LibCrypto: Support the parsing of certs and keys for ml-kem keys
This commit is contained in:
parent
df0796bdf2
commit
dac1952e47
4 changed files with 143 additions and 0 deletions
|
|
@ -355,6 +355,36 @@ ErrorOr<PrivateKey> parse_private_key_info(ASN1::Decoder& decoder, Vector<String
|
|||
EXIT_SCOPE();
|
||||
return private_key;
|
||||
}
|
||||
if (private_key.algorithm.identifier.span() == ASN1::ml_kem_512_oid.span()) {
|
||||
auto maybe_key = Crypto::PK::MLKEM::parse_mlkem_key(PK::MLKEMSize::MLKEM512, value.bytes(), current_scope);
|
||||
if (maybe_key.is_error()) {
|
||||
ERROR_WITH_SCOPE(maybe_key.release_error());
|
||||
}
|
||||
|
||||
private_key.mlkem = move(maybe_key.release_value().private_key);
|
||||
EXIT_SCOPE();
|
||||
return private_key;
|
||||
}
|
||||
if (private_key.algorithm.identifier.span() == ASN1::ml_kem_768_oid.span()) {
|
||||
auto maybe_key = Crypto::PK::MLKEM::parse_mlkem_key(PK::MLKEMSize::MLKEM768, value.bytes(), current_scope);
|
||||
if (maybe_key.is_error()) {
|
||||
ERROR_WITH_SCOPE(maybe_key.release_error());
|
||||
}
|
||||
|
||||
private_key.mlkem = move(maybe_key.release_value().private_key);
|
||||
EXIT_SCOPE();
|
||||
return private_key;
|
||||
}
|
||||
if (private_key.algorithm.identifier.span() == ASN1::ml_kem_1024_oid.span()) {
|
||||
auto maybe_key = Crypto::PK::MLKEM::parse_mlkem_key(PK::MLKEMSize::MLKEM1024, value.bytes(), current_scope);
|
||||
if (maybe_key.is_error()) {
|
||||
ERROR_WITH_SCOPE(maybe_key.release_error());
|
||||
}
|
||||
|
||||
private_key.mlkem = move(maybe_key.release_value().private_key);
|
||||
EXIT_SCOPE();
|
||||
return private_key;
|
||||
}
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/rfc8410#section-9
|
||||
// For all of the OIDs, the parameters MUST be absent.
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <LibCrypto/ASN1/DER.h>
|
||||
#include <LibCrypto/PK/EC.h>
|
||||
#include <LibCrypto/PK/MLDSA.h>
|
||||
#include <LibCrypto/PK/MLKEM.h>
|
||||
#include <LibCrypto/PK/RSA.h>
|
||||
|
||||
namespace Crypto::Certificate {
|
||||
|
|
@ -46,6 +47,7 @@ public:
|
|||
PK::RSAPrivateKey rsa;
|
||||
PK::ECPrivateKey ec;
|
||||
PK::MLDSAPrivateKey mldsa;
|
||||
PK::MLKEMPrivateKey mlkem;
|
||||
|
||||
AlgorithmIdentifier algorithm;
|
||||
ByteBuffer raw_key;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#include <LibCrypto/PK/MLKEM.h>
|
||||
|
||||
#include <LibCrypto/ASN1/DER.h>
|
||||
#include <LibCrypto/Curves/SECPxxxr1.h>
|
||||
#include <LibCrypto/OpenSSL.h>
|
||||
|
||||
#include <openssl/core_names.h>
|
||||
|
|
@ -28,6 +30,110 @@ static char const* mlkem_size_to_openssl_name(MLKEMSize size)
|
|||
}
|
||||
}
|
||||
|
||||
static ErrorOr<ByteBuffer> read_mlkem_seed(ASN1::Decoder& decoder, Vector<StringView>& current_scope)
|
||||
{
|
||||
// seed ::= OCTET STRING (SIZE (64))
|
||||
READ_OBJECT(OctetString, StringView, seed_bits);
|
||||
|
||||
auto const seed = seed_bits.bytes();
|
||||
if (seed.size() != 64) {
|
||||
ERROR_WITH_SCOPE("Invalid seed length");
|
||||
}
|
||||
POP_SCOPE();
|
||||
|
||||
return ByteBuffer::copy(seed);
|
||||
}
|
||||
|
||||
static ErrorOr<ByteBuffer> read_mlkem_private_key(MLKEMSize size, ASN1::Decoder& decoder, Vector<StringView>& current_scope)
|
||||
{
|
||||
// expandedKey ::= OCTET STRING (SIZE (1632 | 2400 | 3168))
|
||||
|
||||
ENTER_TYPED_SCOPE(OctetString, "expandedKey");
|
||||
READ_OBJECT(OctetString, StringView, expanded_key_bits);
|
||||
|
||||
auto const expanded_key = expanded_key_bits.bytes();
|
||||
switch (size) {
|
||||
case MLKEMSize::MLKEM512:
|
||||
if (expanded_key.size() != 1632) {
|
||||
ERROR_WITH_SCOPE("Invalid expandedKey size");
|
||||
}
|
||||
break;
|
||||
case MLKEMSize::MLKEM768:
|
||||
if (expanded_key.size() != 2400) {
|
||||
ERROR_WITH_SCOPE("Invalid expandedKey size");
|
||||
}
|
||||
break;
|
||||
case MLKEMSize::MLKEM1024:
|
||||
if (expanded_key.size() != 3168) {
|
||||
ERROR_WITH_SCOPE("Invalid expandedKey size");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
POP_SCOPE();
|
||||
|
||||
return ByteBuffer::copy(expanded_key);
|
||||
}
|
||||
|
||||
ErrorOr<ByteBuffer> MLKEMPrivateKey::export_as_der() const
|
||||
{
|
||||
ASN1::Encoder encoder;
|
||||
|
||||
TRY(encoder.write<ReadonlyBytes>(m_seed, ASN1::Class::Context, static_cast<ASN1::Kind>(0)));
|
||||
|
||||
return encoder.finish();
|
||||
}
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/draft-ietf-lamps-kyber-certificates-11#autoid-7
|
||||
ErrorOr<MLKEM::KeyPairType> MLKEM::parse_mlkem_key(MLKEMSize size, ReadonlyBytes der, Vector<StringView> current_scope)
|
||||
{
|
||||
ASN1::Decoder decoder(der);
|
||||
|
||||
// ML-KEM-PrivateKey ::= CHOICE {
|
||||
// seed [0] IMPLICIT OCTET STRING (SIZE (64)),
|
||||
// expandedKey OCTET STRING (SIZE (1632 | 2400 | 3168)),
|
||||
// both SEQUENCE {
|
||||
// seed OCTET STRING (SIZE (64)),
|
||||
// expandedKey OCTET STRING (SIZE (1632 | 2400 | 3168))
|
||||
// }
|
||||
// }
|
||||
|
||||
if (decoder.eof()) {
|
||||
return Error::from_string_literal("Input key is empty");
|
||||
}
|
||||
|
||||
auto const tag = TRY(decoder.peek());
|
||||
if (static_cast<u8>(tag.kind) == 0) {
|
||||
REWRITE_TAG(OctetString);
|
||||
return generate_key_pair(size, TRY(read_mlkem_seed(decoder, current_scope)));
|
||||
}
|
||||
if (tag.kind == ASN1::Kind::OctetString) {
|
||||
return KeyPairType {
|
||||
{},
|
||||
{ {}, {}, TRY(read_mlkem_private_key(size, decoder, current_scope)) }
|
||||
};
|
||||
}
|
||||
if (tag.kind == ASN1::Kind::Sequence) {
|
||||
ENTER_TYPED_SCOPE(Sequence, "both");
|
||||
ENTER_TYPED_SCOPE(OctetString, "seed");
|
||||
auto key_pair = TRY(generate_key_pair(size, TRY(read_mlkem_seed(decoder, current_scope))));
|
||||
POP_SCOPE()
|
||||
|
||||
ENTER_TYPED_SCOPE(OctetString, "expandedKey");
|
||||
if (auto const expanded_key = TRY(read_mlkem_private_key(size, decoder, current_scope));
|
||||
key_pair.private_key.private_key() != expanded_key) {
|
||||
ERROR_WITH_SCOPE("Invalid expanded_key");
|
||||
}
|
||||
POP_SCOPE();
|
||||
|
||||
POP_SCOPE();
|
||||
return key_pair;
|
||||
}
|
||||
|
||||
return Error::from_string_literal("Invalid key format");
|
||||
}
|
||||
|
||||
ErrorOr<MLKEMEncapsulation> MLKEM::encapsulate(MLKEMSize size, MLKEMPublicKey const& key)
|
||||
{
|
||||
auto public_key = TRY(OpenSSL_PKEY::wrap(EVP_PKEY_new_raw_public_key_ex(nullptr, mlkem_size_to_openssl_name(size), nullptr, key.public_key().data(), key.public_key().size())));
|
||||
|
|
|
|||
|
|
@ -44,6 +44,9 @@ public:
|
|||
MLKEMPrivateKey() = default;
|
||||
|
||||
ByteBuffer const& seed() const { return m_seed; }
|
||||
ByteBuffer const& private_key() const { return m_private_key; }
|
||||
|
||||
ErrorOr<ByteBuffer> export_as_der() const;
|
||||
|
||||
private:
|
||||
ByteBuffer m_seed;
|
||||
|
|
@ -69,6 +72,8 @@ class MLKEM {
|
|||
public:
|
||||
using KeyPairType = MLKEMKeyPair<PublicKeyType, PrivateKeyType>;
|
||||
|
||||
static ErrorOr<KeyPairType> parse_mlkem_key(MLKEMSize, ReadonlyBytes der, Vector<StringView> current_scope);
|
||||
|
||||
static ErrorOr<MLKEMEncapsulation> encapsulate(MLKEMSize size, MLKEMPublicKey const& key);
|
||||
static ErrorOr<KeyPairType> generate_key_pair(MLKEMSize size, ByteBuffer seed = {});
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue