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