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