LibCrypto: Use ASN1 macros for RSA key parsing

Improve error handling in `RSA::parse_rsa_key` by using ASN1 macros and
generalizing the parsing to both private and public keys.
This commit is contained in:
devgianlu 2024-11-29 18:28:18 +01:00 committed by Andreas Kling
parent ee50a8c0f7
commit 1f7586ce14
Notes: github-actions[bot] 2024-11-30 10:18:46 +00:00
5 changed files with 104 additions and 101 deletions

View file

@ -325,12 +325,12 @@ ErrorOr<SubjectPublicKey> parse_subject_public_key_info(Crypto::ASN1::Decoder& d
public_key.raw_key = TRY(ByteBuffer::copy(TRY(value.raw_bytes())));
if (public_key.algorithm.identifier.span() == ASN1::rsa_encryption_oid.span()) {
auto key = Crypto::PK::RSA::parse_rsa_key(TRY(value.raw_bytes()));
if (!key.public_key.length()) {
return Error::from_string_literal("Invalid RSA key");
auto maybe_key = Crypto::PK::RSA::parse_rsa_key(public_key.raw_key, false, current_scope);
if (maybe_key.is_error()) {
ERROR_WITH_SCOPE(maybe_key.release_error());
}
public_key.rsa = move(key.public_key);
public_key.rsa = move(maybe_key.release_value().public_key);
EXIT_SCOPE();
return public_key;
@ -384,12 +384,12 @@ ErrorOr<PrivateKey> parse_private_key_info(Crypto::ASN1::Decoder& decoder, Vecto
private_key.raw_key = TRY(ByteBuffer::copy(value.bytes()));
if (private_key.algorithm.identifier.span() == ASN1::rsa_encryption_oid.span()) {
auto key = Crypto::PK::RSA::parse_rsa_key(value.bytes());
if (key.private_key.length() == 0) {
ERROR_WITH_SCOPE(TRY(String::formatted("Invalid RSA key at {}", current_scope)));
auto maybe_key = Crypto::PK::RSA::parse_rsa_key(value.bytes(), true, current_scope);
if (maybe_key.is_error()) {
ERROR_WITH_SCOPE(maybe_key.release_error());
}
private_key.rsa = move(key.private_key);
private_key.rsa = move(maybe_key.release_value().private_key);
EXIT_SCOPE();
return private_key;

View file

@ -13,107 +13,109 @@
#include <LibCrypto/Certificate/Certificate.h>
#include <LibCrypto/PK/RSA.h>
namespace {
// Used by ASN1 macros
static String s_error_string;
}
namespace Crypto::PK {
RSA::KeyPairType RSA::parse_rsa_key(ReadonlyBytes der)
ErrorOr<RSA::KeyPairType> RSA::parse_rsa_key(ReadonlyBytes der, bool is_private, Vector<StringView> current_scope)
{
// we are going to assign to at least one of these
KeyPairType keypair;
ASN1::Decoder decoder(der);
// There are two possible (supported) formats:
// PKCS#1 private key
// PKCS#1 public key
// They're all a single sequence, so let's check that first
{
auto result = decoder.peek();
if (result.is_error()) {
// Bad data.
dbgln_if(RSA_PARSE_DEBUG, "RSA key parse failed: {}", result.error());
return keypair;
if (is_private) {
// RSAPrivateKey ::= SEQUENCE {
// version Version,
// modulus INTEGER,
// publicExponent INTEGER,
// privateExponent INTEGER,
// prime1 INTEGER,
// prime2 INTEGER,
// exponent1 INTEGER,
// exponent2 INTEGER,
// coefficient INTEGER,
// otherPrimeInfos OtherPrimeInfos OPTIONAL
// }
ENTER_TYPED_SCOPE(Sequence, "RSAPrivateKey"sv);
PUSH_SCOPE("version");
READ_OBJECT(Integer, Crypto::UnsignedBigInteger, version);
POP_SCOPE();
if (version != 0) {
ERROR_WITH_SCOPE(TRY(String::formatted("Invalid version value at {}", current_scope)));
}
auto tag = result.value();
if (tag.kind != ASN1::Kind::Sequence) {
dbgln_if(RSA_PARSE_DEBUG, "RSA key parse failed: Expected a Sequence but got {}", ASN1::kind_name(tag.kind));
return keypair;
}
}
// Then enter the sequence
{
auto error = decoder.enter();
if (error.is_error()) {
// Something was weird with the input.
dbgln_if(RSA_PARSE_DEBUG, "RSA key parse failed: {}", error.error());
return keypair;
}
}
PUSH_SCOPE("modulus");
READ_OBJECT(Integer, Crypto::UnsignedBigInteger, modulus);
POP_SCOPE();
auto integer_result = decoder.read<UnsignedBigInteger>();
if (integer_result.is_error()) {
dbgln_if(RSA_PARSE_DEBUG, "RSA key parse failed: {}", integer_result.error());
return keypair;
}
PUSH_SCOPE("publicExponent");
READ_OBJECT(Integer, Crypto::UnsignedBigInteger, public_exponent);
POP_SCOPE();
auto first_integer = integer_result.release_value();
PUSH_SCOPE("privateExponent");
READ_OBJECT(Integer, Crypto::UnsignedBigInteger, private_exponent);
POP_SCOPE();
// It's a PKCS#1 key (or something we don't support)
// if the first integer is zero or one, it's a private key.
if (first_integer == 0) {
// This is a private key, parse the rest.
auto modulus_result = decoder.read<UnsignedBigInteger>();
auto public_exponent_result = decoder.read<UnsignedBigInteger>();
auto private_exponent_result = decoder.read<UnsignedBigInteger>();
auto prime1_result = decoder.read<UnsignedBigInteger>();
auto prime2_result = decoder.read<UnsignedBigInteger>();
auto exponent1_result = decoder.read<UnsignedBigInteger>();
auto exponent2_result = decoder.read<UnsignedBigInteger>();
auto coefficient_result = decoder.read<UnsignedBigInteger>();
PUSH_SCOPE("prime1");
READ_OBJECT(Integer, Crypto::UnsignedBigInteger, prime1);
POP_SCOPE();
Array results = { &modulus_result, &public_exponent_result, &private_exponent_result, &prime1_result, &prime2_result, &exponent1_result, &exponent2_result, &coefficient_result };
for (auto& result : results) {
if (result->is_error()) {
dbgln_if(RSA_PARSE_DEBUG, "RSA PKCS#1 private key parse failed: {}", result->error());
return keypair;
}
}
PUSH_SCOPE("prime2");
READ_OBJECT(Integer, Crypto::UnsignedBigInteger, prime2);
POP_SCOPE();
PUSH_SCOPE("exponent1");
READ_OBJECT(Integer, Crypto::UnsignedBigInteger, exponent1);
POP_SCOPE();
PUSH_SCOPE("exponent2");
READ_OBJECT(Integer, Crypto::UnsignedBigInteger, exponent2);
POP_SCOPE();
PUSH_SCOPE("coefficient");
READ_OBJECT(Integer, Crypto::UnsignedBigInteger, coefficient);
POP_SCOPE();
keypair.private_key = {
modulus_result.value(),
private_exponent_result.release_value(),
public_exponent_result.value(),
prime1_result.release_value(),
prime2_result.release_value(),
exponent1_result.release_value(),
exponent2_result.release_value(),
coefficient_result.release_value(),
modulus,
private_exponent,
public_exponent,
prime1,
prime2,
exponent1,
exponent2,
coefficient,
};
keypair.public_key = { modulus_result.release_value(), public_exponent_result.release_value() };
keypair.public_key = { modulus, public_exponent };
EXIT_SCOPE();
return keypair;
} else {
// RSAPublicKey ::= SEQUENCE {
// modulus INTEGER,
// publicExponent INTEGER
// }
ENTER_TYPED_SCOPE(Sequence, "RSAPublicKey"sv);
PUSH_SCOPE("modulus");
READ_OBJECT(Integer, Crypto::UnsignedBigInteger, modulus);
POP_SCOPE();
PUSH_SCOPE("publicExponent");
READ_OBJECT(Integer, Crypto::UnsignedBigInteger, public_exponent);
POP_SCOPE();
keypair.public_key = { move(modulus), move(public_exponent) };
EXIT_SCOPE();
return keypair;
}
if (first_integer == 1) {
// This is a multi-prime key, we don't support that.
dbgln_if(RSA_PARSE_DEBUG, "RSA PKCS#1 private key parse failed: Multi-prime key not supported");
return keypair;
}
auto&& modulus = move(first_integer);
// Try reading a public key, `first_integer` is the modulus.
auto public_exponent_result = decoder.read<UnsignedBigInteger>();
if (public_exponent_result.is_error()) {
// Bad public key.
dbgln_if(RSA_PARSE_DEBUG, "RSA PKCS#1 public key parse failed: {}", public_exponent_result.error());
return keypair;
}
auto public_exponent = public_exponent_result.release_value();
keypair.public_key.set(move(modulus), move(public_exponent));
return keypair;
}
void RSA::encrypt(ReadonlyBytes in, Bytes& out)
@ -189,12 +191,12 @@ void RSA::import_private_key(ReadonlyBytes bytes, bool pem)
}
}
auto key = parse_rsa_key(decoded_bytes);
if (key.private_key.length() == 0) {
dbgln("Failed to parse RSA private key");
auto maybe_key = parse_rsa_key(decoded_bytes, true, {});
if (maybe_key.is_error()) {
dbgln("Failed to parse RSA private key: {}", maybe_key.error());
VERIFY_NOT_REACHED();
}
m_private_key = key.private_key;
m_private_key = maybe_key.release_value().private_key;
}
void RSA::import_public_key(ReadonlyBytes bytes, bool pem)
@ -220,12 +222,12 @@ void RSA::import_public_key(ReadonlyBytes bytes, bool pem)
}
}
auto key = parse_rsa_key(decoded_bytes);
if (key.public_key.length() == 0) {
dbgln("Failed to parse RSA public key");
auto maybe_key = parse_rsa_key(decoded_bytes, false, {});
if (maybe_key.is_error()) {
dbgln("Failed to parse RSA public key: {}", maybe_key.error());
VERIFY_NOT_REACHED();
}
m_public_key = key.public_key;
m_public_key = maybe_key.release_value().public_key;
}
void RSA_PKCS1_EME::encrypt(ReadonlyBytes in, Bytes& out)

View file

@ -152,7 +152,7 @@ class RSA : public PKSystem<RSAPrivateKey<IntegerType>, RSAPublicKey<IntegerType
public:
using KeyPairType = RSAKeyPair<PublicKeyType, PrivateKeyType>;
static KeyPairType parse_rsa_key(ReadonlyBytes der);
static ErrorOr<KeyPairType> parse_rsa_key(ReadonlyBytes der, bool is_private, Vector<StringView> current_scope);
static KeyPairType generate_key_pair(size_t bits = 256, IntegerType e = 65537)
{
IntegerType p;

View file

@ -11,6 +11,7 @@
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
{
AK::set_debug_enabled(false);
Crypto::PK::RSA::parse_rsa_key({ data, size });
(void)Crypto::PK::RSA::parse_rsa_key({ data, size }, true, {});
(void)Crypto::PK::RSA::parse_rsa_key({ data, size }, false, {});
return 0;
}

View file

@ -127,7 +127,7 @@ c8yGzl89pYST
EXPECT_EQ(decoded.type, Crypto::PEMType::PrivateKey);
auto decoder = Crypto::ASN1::Decoder { decoded.data };
auto priv_key_info = MUST(Crypto::Certificate::parse_private_key_info(decoder, {}));
auto keypair = Crypto::PK::RSA::parse_rsa_key(priv_key_info.raw_key);
auto keypair = MUST(Crypto::PK::RSA::parse_rsa_key(priv_key_info.raw_key, true, {}));
auto priv_der = MUST(priv_key_info.rsa.export_as_der());
auto rsa_encryption_oid = Array<int, 7> { 1, 2, 840, 113549, 1, 1, 1 };
auto wrapped_priv_der = MUST(Crypto::PK::wrap_in_private_key_info(priv_key_info.raw_key, rsa_encryption_oid, nullptr));