Expose ML-DSA-87 in the public headers. Change-Id: I2307056254ac0910b5a462da896c7bc265429545 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/79228 Reviewed-by: David Benjamin <davidben@google.com> Commit-Queue: David Benjamin <davidben@google.com> Auto-Submit: Adam Langley <agl@google.com>
diff --git a/crypto/mldsa/mldsa.cc b/crypto/mldsa/mldsa.cc index 418e3f6..8219642 100644 --- a/crypto/mldsa/mldsa.cc +++ b/crypto/mldsa/mldsa.cc
@@ -23,10 +23,20 @@ static_assert(sizeof(BCM_mldsa65_public_key) == sizeof(MLDSA65_public_key), ""); static_assert(alignof(BCM_mldsa65_public_key) == alignof(MLDSA65_public_key), ""); +static_assert(sizeof(BCM_mldsa87_private_key) == sizeof(MLDSA87_private_key), + ""); +static_assert(alignof(BCM_mldsa87_private_key) == alignof(MLDSA87_private_key), + ""); +static_assert(sizeof(BCM_mldsa87_public_key) == sizeof(MLDSA87_public_key), ""); +static_assert(alignof(BCM_mldsa87_public_key) == alignof(MLDSA87_public_key), + ""); static_assert(MLDSA_SEED_BYTES == BCM_MLDSA_SEED_BYTES, ""); static_assert(MLDSA65_PRIVATE_KEY_BYTES == BCM_MLDSA65_PRIVATE_KEY_BYTES, ""); static_assert(MLDSA65_PUBLIC_KEY_BYTES == BCM_MLDSA65_PUBLIC_KEY_BYTES, ""); static_assert(MLDSA65_SIGNATURE_BYTES == BCM_MLDSA65_SIGNATURE_BYTES, ""); +static_assert(MLDSA87_PRIVATE_KEY_BYTES == BCM_MLDSA87_PRIVATE_KEY_BYTES, ""); +static_assert(MLDSA87_PUBLIC_KEY_BYTES == BCM_MLDSA87_PUBLIC_KEY_BYTES, ""); +static_assert(MLDSA87_SIGNATURE_BYTES == BCM_MLDSA87_SIGNATURE_BYTES, ""); int MLDSA65_generate_key( uint8_t out_encoded_public_key[MLDSA65_PUBLIC_KEY_BYTES], @@ -88,3 +98,64 @@ return bcm_success(BCM_mldsa65_parse_public_key( reinterpret_cast<BCM_mldsa65_public_key *>(public_key), in)); } + +int MLDSA87_generate_key( + uint8_t out_encoded_public_key[MLDSA87_PUBLIC_KEY_BYTES], + uint8_t out_seed[MLDSA_SEED_BYTES], + struct MLDSA87_private_key *out_private_key) { + return bcm_success(BCM_mldsa87_generate_key( + out_encoded_public_key, out_seed, + reinterpret_cast<BCM_mldsa87_private_key *>(out_private_key))); +} + +int MLDSA87_private_key_from_seed(struct MLDSA87_private_key *out_private_key, + const uint8_t *seed, size_t seed_len) { + if (seed_len != BCM_MLDSA_SEED_BYTES) { + return 0; + } + return bcm_success(BCM_mldsa87_private_key_from_seed( + reinterpret_cast<BCM_mldsa87_private_key *>(out_private_key), seed)); +} + +int MLDSA87_public_from_private(struct MLDSA87_public_key *out_public_key, + const struct MLDSA87_private_key *private_key) { + return bcm_success(BCM_mldsa87_public_from_private( + reinterpret_cast<BCM_mldsa87_public_key *>(out_public_key), + reinterpret_cast<const BCM_mldsa87_private_key *>(private_key))); +} + +int MLDSA87_sign(uint8_t out_encoded_signature[MLDSA87_SIGNATURE_BYTES], + const struct MLDSA87_private_key *private_key, + const uint8_t *msg, size_t msg_len, const uint8_t *context, + size_t context_len) { + if (context_len > 255) { + return 0; + } + return bcm_success(BCM_mldsa87_sign( + out_encoded_signature, + reinterpret_cast<const BCM_mldsa87_private_key *>(private_key), msg, + msg_len, context, context_len)); +} + +int MLDSA87_verify(const struct MLDSA87_public_key *public_key, + const uint8_t *signature, size_t signature_len, + const uint8_t *msg, size_t msg_len, const uint8_t *context, + size_t context_len) { + if (context_len > 255 || signature_len != BCM_MLDSA87_SIGNATURE_BYTES) { + return 0; + } + return bcm_success(BCM_mldsa87_verify( + reinterpret_cast<const BCM_mldsa87_public_key *>(public_key), signature, + msg, msg_len, context, context_len)); +} + +int MLDSA87_marshal_public_key(CBB *out, + const struct MLDSA87_public_key *public_key) { + return bcm_success(BCM_mldsa87_marshal_public_key( + out, reinterpret_cast<const BCM_mldsa87_public_key *>(public_key))); +} + +int MLDSA87_parse_public_key(struct MLDSA87_public_key *public_key, CBS *in) { + return bcm_success(BCM_mldsa87_parse_public_key( + reinterpret_cast<BCM_mldsa87_public_key *>(public_key), in)); +}
diff --git a/crypto/mldsa/mldsa_test.cc b/crypto/mldsa/mldsa_test.cc index 112b444..9f1da28 100644 --- a/crypto/mldsa/mldsa_test.cc +++ b/crypto/mldsa/mldsa_test.cc
@@ -143,66 +143,6 @@ BCM_mldsa65_marshal_private_key>(); } -// These are the wrapper functions needed for `MLDSABasicTest`. ML-DSA-87 isn't -// publicly exposed yet, so they are included here. It's good to exercise the -// ML-DSA-65 wrapper functions so that they aren't untested (even if they are -// quite trivial) thus `MLDSABasicTest` is done this way around. - -struct MLDSA87_private_key { - BCM_mldsa87_private_key priv; -}; - -struct MLDSA87_public_key { - BCM_mldsa87_public_key pub; -}; - -static int MLDSA87_generate_key( - uint8_t out_encoded_public_key[BCM_MLDSA87_PUBLIC_KEY_BYTES], - uint8_t out_seed[MLDSA_SEED_BYTES], - struct MLDSA87_private_key *out_private_key) { - return bcm_success(BCM_mldsa87_generate_key( - out_encoded_public_key, out_seed, - reinterpret_cast<BCM_mldsa87_private_key *>(out_private_key))); -} - -static int MLDSA87_private_key_from_seed( - struct MLDSA87_private_key *out_private_key, const uint8_t *seed, - size_t seed_len) { - if (seed_len != BCM_MLDSA_SEED_BYTES) { - return 0; - } - return bcm_success(BCM_mldsa87_private_key_from_seed( - reinterpret_cast<BCM_mldsa87_private_key *>(out_private_key), seed)); -} - -static int MLDSA87_sign( - uint8_t out_encoded_signature[BCM_MLDSA87_SIGNATURE_BYTES], - const struct MLDSA87_private_key *private_key, const uint8_t *msg, - size_t msg_len, const uint8_t *context, size_t context_len) { - return bcm_success(BCM_mldsa87_sign( - out_encoded_signature, - reinterpret_cast<const BCM_mldsa87_private_key *>(private_key), msg, - msg_len, context, context_len)); -} - -static int MLDSA87_verify(const struct MLDSA87_public_key *public_key, - const uint8_t *signature, size_t signature_len, - const uint8_t *msg, size_t msg_len, - const uint8_t *context, size_t context_len) { - if (context_len > 255 || signature_len != BCM_MLDSA87_SIGNATURE_BYTES) { - return 0; - } - return bcm_success(BCM_mldsa87_verify( - reinterpret_cast<const BCM_mldsa87_public_key *>(public_key), signature, - msg, msg_len, context, context_len)); -} - -static int MLDSA87_parse_public_key(struct MLDSA87_public_key *public_key, - CBS *in) { - return bcm_success(BCM_mldsa87_parse_public_key( - reinterpret_cast<BCM_mldsa87_public_key *>(public_key), in)); -} - TEST(MLDSATest, Basic87) { MLDSABasicTest<MLDSA87_private_key, MLDSA87_public_key, BCM_MLDSA87_PUBLIC_KEY_BYTES, BCM_MLDSA87_SIGNATURE_BYTES,
diff --git a/include/openssl/mldsa.h b/include/openssl/mldsa.h index cc0f850..569c89c 100644 --- a/include/openssl/mldsa.h +++ b/include/openssl/mldsa.h
@@ -15,7 +15,7 @@ #ifndef OPENSSL_HEADER_MLDSA_H_ #define OPENSSL_HEADER_MLDSA_H_ -#include <openssl/base.h> // IWYU pragma: export +#include <openssl/base.h> // IWYU pragma: export #if defined(__cplusplus) extern "C" { @@ -123,6 +123,100 @@ struct MLDSA65_public_key *public_key, CBS *in); +// ML-DSA-87. +// +// ML-DSA-87 is excessively and unnecessarily large. Use -65 unless you are +// specifically attempting to achieve CNSA 2.0 compliance. + +// MLDSA87_private_key contains an ML-DSA-87 private key. The contents of this +// object should never leave the address space since the format is unstable. +struct MLDSA87_private_key { + union { + uint8_t bytes[32 + 32 + 64 + 256 * 4 * (7 + 8 + 8)]; + uint32_t alignment; + } opaque; +}; + +// MLDSA87_public_key contains an ML-DSA-87 public key. The contents of this +// object should never leave the address space since the format is unstable. +struct MLDSA87_public_key { + union { + uint8_t bytes[32 + 64 + 256 * 4 * 8]; + uint32_t alignment; + } opaque; +}; + +// MLDSA87_PRIVATE_KEY_BYTES is the number of bytes in an encoded ML-DSA-87 +// private key. +#define MLDSA87_PRIVATE_KEY_BYTES 4896 + +// MLDSA87_PUBLIC_KEY_BYTES is the number of bytes in an encoded ML-DSA-87 +// public key. +#define MLDSA87_PUBLIC_KEY_BYTES 2592 + +// MLDSA87_SIGNATURE_BYTES is the number of bytes in an encoded ML-DSA-87 +// signature. +#define MLDSA87_SIGNATURE_BYTES 4627 + +// MLDSA87_generate_key generates a random public/private key pair, writes the +// encoded public key to |out_encoded_public_key|, writes the seed to +// |out_seed|, and sets |out_private_key| to the private key. Returns 1 on +// success and 0 on allocation failure. +OPENSSL_EXPORT int MLDSA87_generate_key( + uint8_t out_encoded_public_key[MLDSA87_PUBLIC_KEY_BYTES], + uint8_t out_seed[MLDSA_SEED_BYTES], + struct MLDSA87_private_key *out_private_key); + +// MLDSA87_private_key_from_seed regenerates a private key from a seed value +// that was generated by |MLDSA87_generate_key|. Returns 1 on success and 0 on +// allocation failure or if |seed_len| is incorrect. +OPENSSL_EXPORT int MLDSA87_private_key_from_seed( + struct MLDSA87_private_key *out_private_key, const uint8_t *seed, + size_t seed_len); + +// MLDSA87_public_from_private sets |*out_public_key| to the public key that +// corresponds to |private_key|. Returns 1 on success and 0 on failure. +OPENSSL_EXPORT int MLDSA87_public_from_private( + struct MLDSA87_public_key *out_public_key, + const struct MLDSA87_private_key *private_key); + +// MLDSA87_sign generates a signature for the message |msg| of length +// |msg_len| using |private_key| (following the randomized algorithm), and +// writes the encoded signature to |out_encoded_signature|. The |context| +// argument is also signed over and can be used to include implicit contextual +// information that isn't included in |msg|. The same value of |context| must be +// presented to |MLDSA87_verify| in order for the generated signature to be +// considered valid. |context| and |context_len| may be |NULL| and 0 to use an +// empty context (this is common). Returns 1 on success and 0 on failure. +OPENSSL_EXPORT int MLDSA87_sign( + uint8_t out_encoded_signature[MLDSA87_SIGNATURE_BYTES], + const struct MLDSA87_private_key *private_key, const uint8_t *msg, + size_t msg_len, const uint8_t *context, size_t context_len); + +// MLDSA87_verify verifies that |signature| constitutes a valid +// signature for the message |msg| of length |msg_len| using |public_key|. The +// value of |context| must equal the value that was passed to |MLDSA87_sign| +// when the signature was generated. Returns 1 on success or 0 on error. +OPENSSL_EXPORT int MLDSA87_verify(const struct MLDSA87_public_key *public_key, + const uint8_t *signature, + size_t signature_len, const uint8_t *msg, + size_t msg_len, const uint8_t *context, + size_t context_len); + +// MLDSA87_marshal_public_key serializes |public_key| to |out| in the standard +// format for ML-DSA-87 public keys. It returns 1 on success or 0 on +// allocation error. +OPENSSL_EXPORT int MLDSA87_marshal_public_key( + CBB *out, const struct MLDSA87_public_key *public_key); + +// MLDSA87_parse_public_key parses a public key, in the format generated by +// |MLDSA87_marshal_public_key|, from |in| and writes the result to +// |out_public_key|. It returns 1 on success or 0 on parse error or if +// there are trailing bytes in |in|. +OPENSSL_EXPORT int MLDSA87_parse_public_key( + struct MLDSA87_public_key *public_key, CBS *in); + + #if defined(__cplusplus) } // extern C #endif