Add ML-DSA-87 variants to Rust wrappers

Pure copy-paste then replace s/65/87/g

Change-Id: I7f5d7e9849612f8c0acc2033ccf1448f61593f98
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/81848
Commit-Queue: Adam Langley <agl@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/rust/bssl-crypto/src/mldsa.rs b/rust/bssl-crypto/src/mldsa.rs
index 309c3da..8c4baa6 100644
--- a/rust/bssl-crypto/src/mldsa.rs
+++ b/rust/bssl-crypto/src/mldsa.rs
@@ -54,6 +54,18 @@
 /// The number of bytes in an encoded ML-DSA-65 signature.
 pub const SIGNATURE_BYTES_65: usize = bssl_sys::MLDSA65_SIGNATURE_BYTES as usize;
 
+/// An ML-DSA-87 private key.
+pub struct PrivateKey87(Box<bssl_sys::MLDSA87_private_key>);
+
+/// An ML-DSA-87 public key.
+pub struct PublicKey87(Box<bssl_sys::MLDSA87_public_key>);
+
+/// The number of bytes in an encoded ML-DSA-87 public key.
+pub const PUBLIC_KEY_BYTES_87: usize = bssl_sys::MLDSA87_PUBLIC_KEY_BYTES as usize;
+
+/// The number of bytes in an encoded ML-DSA-87 signature.
+pub const SIGNATURE_BYTES_87: usize = bssl_sys::MLDSA87_SIGNATURE_BYTES as usize;
+
 /// The number of bytes in an ML-DSA seed value.
 pub const SEED_BYTES: usize = bssl_sys::MLDSA_SEED_BYTES as usize;
 
@@ -341,12 +353,293 @@
     }
 }
 
+impl PrivateKey87 {
+    /// Generates a random public/private key pair returning a serialized public
+    /// key, a private key, and a private seed value that can be used to
+    /// regenerate the same private key in the future.
+    pub fn generate() -> (Vec<u8>, Self, [u8; SEED_BYTES]) {
+        let mut public_key_bytes = Box::new_uninit_slice(PUBLIC_KEY_BYTES_87);
+        let mut seed = MaybeUninit::<[u8; SEED_BYTES]>::uninit();
+
+        let private_key = unsafe {
+            // Safety: the buffers are the sizes that the FFI code requires.
+            initialized_boxed_struct(|priv_key| {
+                let ok = bssl_sys::MLDSA87_generate_key(
+                    public_key_bytes.as_mut_ptr() as *mut u8,
+                    seed.as_mut_ptr() as *mut u8,
+                    priv_key,
+                );
+                // This function can only fail if out of memory, which is not a
+                // case that this crate handles.
+                assert_eq!(ok, 1);
+            })
+        };
+
+        unsafe {
+            (
+                // Safety: the buffers are always fully initialized by
+                // `MLDSA87_generate_key`.
+                public_key_bytes.assume_init().into(),
+                Self(private_key),
+                seed.assume_init(),
+            )
+        }
+    }
+
+    /// Regenerates a private key from a seed value.
+    pub fn from_seed(seed: &[u8; SEED_BYTES]) -> Self {
+        Self(unsafe {
+            // Safety: `priv_key` is the correct size via the type system and
+            // is always fully written.
+            initialized_boxed_struct(|priv_key| {
+                let ok = bssl_sys::MLDSA87_private_key_from_seed(
+                    priv_key,
+                    seed.as_ffi_ptr(),
+                    seed.len(),
+                );
+                // Since the seed value has the correct length, this function can
+                // never fail.
+                assert_eq!(ok, 1);
+            })
+        })
+    }
+
+    /// Derives the public key corresponding to this private key.
+    pub fn to_public_key(&self) -> PublicKey87 {
+        PublicKey87(unsafe {
+            // Safety: `pub_key` is the correct size via the type system and
+            // is always fully written.
+            initialized_boxed_struct(|pub_key| {
+                bssl_sys::MLDSA87_public_from_private(pub_key, &*self.0);
+            })
+        })
+    }
+
+    /// Signs a message using this private key.
+    pub fn sign(&self, msg: &[u8]) -> Vec<u8> {
+        unsafe {
+            // Safety: `signature` is the correct size via the type system and
+            // is always fully written.
+            with_output_vec(SIGNATURE_BYTES_87, |signature| {
+                let ok = bssl_sys::MLDSA87_sign(
+                    signature,
+                    &*self.0,
+                    msg.as_ffi_ptr(),
+                    msg.len(),
+                    core::ptr::null(),
+                    0,
+                );
+                // This function can only fail if out of memory, which is not a
+                // case that this crate handles.
+                assert_eq!(ok, 1);
+                SIGNATURE_BYTES_87
+            })
+        }
+    }
+
+    /// Signs a message using this private key and the given context.
+    ///
+    /// This function returns None if `context` is longer than 255 bytes.
+    pub fn sign_with_context(&self, msg: &[u8], context: &[u8]) -> Option<Vec<u8>> {
+        unsafe {
+            // Safety: `signature` is the correct size via the type system and
+            // is always fully written.
+            with_output_vec_fallible(SIGNATURE_BYTES_87, |signature| {
+                if bssl_sys::MLDSA87_sign(
+                    signature,
+                    &*self.0,
+                    msg.as_ffi_ptr(),
+                    msg.len(),
+                    context.as_ffi_ptr(),
+                    context.len(),
+                ) == 1
+                {
+                    Some(SIGNATURE_BYTES_87)
+                } else {
+                    None
+                }
+            })
+        }
+    }
+
+    /// Sign pre-hashed data.
+    pub fn sign_prehashed(&self, prehash: Prehash87) -> Vec<u8> {
+        let representative = prehash.finalize();
+        unsafe {
+            // Safety: `signature` is the correct size via the type system and
+            // is always fully written; `representative` is an array of the
+            // correct size.
+            with_output_vec(SIGNATURE_BYTES_87, |signature| {
+                let ok = bssl_sys::MLDSA87_sign_message_representative(
+                    signature,
+                    &*self.0,
+                    representative.as_ffi_ptr(),
+                );
+                // This function can only fail if out of memory, which is not a
+                // case that this crate handles.
+                assert_eq!(ok, 1);
+                SIGNATURE_BYTES_87
+            })
+        }
+    }
+}
+
+impl PublicKey87 {
+    /// Parses a public key from a byte slice.
+    pub fn parse(encoded: &[u8]) -> Option<Self> {
+        let mut cbs = as_cbs(encoded);
+        unsafe {
+            // Safety: `pub_key` is the correct size via the type system and
+            // is fully written if this function returns 1.
+            initialized_boxed_struct_fallible(|pub_key| {
+                bssl_sys::MLDSA87_parse_public_key(pub_key, &mut cbs) == 1 && cbs.len == 0
+            })
+        }
+        .map(Self)
+    }
+
+    /// Return the serialization of this public key.
+    pub fn to_bytes(&self) -> Vec<u8> {
+        unsafe {
+            cbb_to_vec(PUBLIC_KEY_BYTES_87, |buf| {
+                let ok = bssl_sys::MLDSA87_marshal_public_key(buf, &*self.0);
+                // `MLKEM768_marshal_public_key` only fails if it cannot
+                // allocate memory, but `cbb_to_vec` allocates a fixed
+                // amount of memory.
+                assert_eq!(ok, 1);
+            })
+        }
+    }
+
+    /// Verifies a signature for a given message using this public key.
+    pub fn verify(&self, msg: &[u8], signature: &[u8]) -> Result<(), InvalidSignatureError> {
+        unsafe {
+            let ok = bssl_sys::MLDSA87_verify(
+                &*self.0,
+                signature.as_ffi_ptr(),
+                signature.len(),
+                msg.as_ffi_ptr(),
+                msg.len(),
+                core::ptr::null(),
+                0,
+            );
+            if ok == 1 {
+                Ok(())
+            } else {
+                Err(InvalidSignatureError)
+            }
+        }
+    }
+
+    /// Verifies a signature for a given message using this public key and the given context.
+    pub fn verify_with_context(
+        &self,
+        msg: &[u8],
+        signature: &[u8],
+        context: &[u8],
+    ) -> Result<(), InvalidSignatureError> {
+        unsafe {
+            let ok = bssl_sys::MLDSA87_verify(
+                &*self.0,
+                signature.as_ffi_ptr(),
+                signature.len(),
+                msg.as_ffi_ptr(),
+                msg.len(),
+                context.as_ffi_ptr(),
+                context.len(),
+            );
+            if ok == 1 {
+                Ok(())
+            } else {
+                Err(InvalidSignatureError)
+            }
+        }
+    }
+
+    /// Verify pre-hashed data.
+    pub fn verify_prehashed(
+        &self,
+        prehash: Prehash87,
+        signature: &[u8],
+    ) -> Result<(), InvalidSignatureError> {
+        let representative = prehash.finalize();
+        unsafe {
+            let ok = bssl_sys::MLDSA87_verify_message_representative(
+                &*self.0,
+                signature.as_ffi_ptr(),
+                signature.len(),
+                representative.as_ffi_ptr(),
+            );
+            if ok == 1 {
+                Ok(())
+            } else {
+                Err(InvalidSignatureError)
+            }
+        }
+    }
+
+    /// Start a pre-hashing operation using this public key.
+    pub fn prehash(&self) -> Prehash87 {
+        unsafe {
+            // Safety: `self.0` is the correct size via the type system and
+            // is fully written if this function returns 1.
+            initialized_struct(|prehash: *mut Prehash87| {
+                let ok = bssl_sys::MLDSA87_prehash_init(
+                    &mut (*prehash).0,
+                    &*self.0,
+                    core::ptr::null(),
+                    0,
+                );
+                // This function can only fail if too much context is provided, but no context is
+                // used here.
+                assert_eq!(ok, 1);
+            })
+        }
+    }
+}
+
+/// An in-progress ML-DSA-87 pre-hashing operation.
+pub struct Prehash87(bssl_sys::MLDSA87_prehash);
+
+impl Prehash87 {
+    /// Add data to the pre-hashing operation.
+    pub fn update(&mut self, data: &[u8]) {
+        unsafe {
+            // Safety: `self.0` is the correct size via the type system and `data`
+            // is a valid Rust slice.
+            bssl_sys::MLDSA87_prehash_update(&mut self.0, data.as_ffi_ptr(), data.len());
+        }
+    }
+
+    /// Complete the pre-hashing operation.
+    fn finalize(mut self) -> [u8; MU_BYTES] {
+        let mut mu = [0u8; MU_BYTES];
+        unsafe {
+            // Safety: `self.0` is the correct size via the type system, as is `mu`.
+            bssl_sys::MLDSA87_prehash_finalize(mu.as_mut_ffi_ptr(), &mut self.0);
+        }
+        mu
+    }
+}
+
+#[cfg(feature = "std")]
+impl std::io::Write for Prehash87 {
+    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+        self.update(buf);
+        Ok(buf.len())
+    }
+
+    fn flush(&mut self) -> std::io::Result<()> {
+        Ok(())
+    }
+}
+
 #[cfg(test)]
 mod test {
     use super::*;
 
     #[test]
-    fn basic() {
+    fn basic65() {
         let (serialized_public_key, private_key, private_seed) = PrivateKey65::generate();
         let public_key = PublicKey65::parse(&serialized_public_key).unwrap();
         let message = &[0u8, 1, 2, 3];
@@ -368,7 +661,7 @@
     }
 
     #[test]
-    fn sign_prehashed() {
+    fn sign_prehashed65() {
         let (serialized_public_key, private_key, _private_seed) = PrivateKey65::generate();
         let public_key = PublicKey65::parse(&serialized_public_key).unwrap();
         let message = &[0u8, 1, 2, 3, 4, 5, 6];
@@ -386,7 +679,7 @@
 
     #[cfg(feature = "std")]
     #[test]
-    fn sign_prehashed_write() {
+    fn sign_prehashed65_write() {
         use std::io::Write;
         let (serialized_public_key, private_key, _private_seed) = PrivateKey65::generate();
         let public_key = PublicKey65::parse(&serialized_public_key).unwrap();
@@ -405,7 +698,7 @@
     }
 
     #[test]
-    fn verify_prehashed() {
+    fn verify_prehashed65() {
         let (serialized_public_key, private_key, _private_seed) = PrivateKey65::generate();
         let public_key = PublicKey65::parse(&serialized_public_key).unwrap();
         let message = &[0u8, 1, 2, 3, 4, 5, 6];
@@ -426,7 +719,7 @@
 
     #[cfg(feature = "std")]
     #[test]
-    fn verify_prehashed_write() {
+    fn verify_prehashed65_write() {
         use std::io::Write;
         let (serialized_public_key, private_key, _private_seed) = PrivateKey65::generate();
         let public_key = PublicKey65::parse(&serialized_public_key).unwrap();
@@ -449,7 +742,7 @@
     }
 
     #[test]
-    fn marshal_public_key() {
+    fn marshal_public_key65() {
         let (serialized_public_key, private_key, _) = PrivateKey65::generate();
         let public_key = PublicKey65::parse(&serialized_public_key).unwrap();
         assert_eq!(serialized_public_key, public_key.to_bytes());
@@ -458,4 +751,118 @@
             private_key.to_public_key().to_bytes()
         );
     }
+
+    #[test]
+    fn basic87() {
+        let (serialized_public_key, private_key, private_seed) = PrivateKey87::generate();
+        let public_key = PublicKey87::parse(&serialized_public_key).unwrap();
+        let message = &[0u8, 1, 2, 3];
+        let signature = private_key.sign(message);
+        let private_key2 = PrivateKey87::from_seed(&private_seed);
+        let mut signature2 = private_key2.sign(message);
+        assert!(public_key.verify(message, &signature).is_ok());
+        assert!(public_key.verify(message, &signature2).is_ok());
+
+        signature2[5] ^= 1;
+        assert!(public_key.verify(message, &signature2).is_err());
+
+        let context = b"context";
+        let signature3 = private_key.sign_with_context(message, context).unwrap();
+        assert!(public_key.verify(message, &signature3).is_err());
+        assert!(public_key
+            .verify_with_context(message, &signature3, context)
+            .is_ok());
+    }
+
+    #[test]
+    fn sign_prehashed87() {
+        let (serialized_public_key, private_key, _private_seed) = PrivateKey87::generate();
+        let public_key = PublicKey87::parse(&serialized_public_key).unwrap();
+        let message = &[0u8, 1, 2, 3, 4, 5, 6];
+
+        let mut prehash = public_key.prehash();
+        prehash.update(&message[0..2]);
+        prehash.update(&message[2..4]);
+        prehash.update(&message[4..]);
+        let mut signature = private_key.sign_prehashed(prehash);
+
+        assert!(public_key.verify(message, &signature).is_ok());
+        signature[5] ^= 1;
+        assert!(public_key.verify(message, &signature).is_err());
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn sign_prehashed87_write() {
+        use std::io::Write;
+        let (serialized_public_key, private_key, _private_seed) = PrivateKey87::generate();
+        let public_key = PublicKey87::parse(&serialized_public_key).unwrap();
+        let message = &[0u8, 1, 2, 3, 4, 5, 6];
+
+        let mut prehash = public_key.prehash();
+        prehash.write(&message[0..2]).unwrap();
+        prehash.write(&message[2..4]).unwrap();
+        prehash.write(&message[4..]).unwrap();
+        prehash.flush().unwrap();
+        let mut signature = private_key.sign_prehashed(prehash);
+
+        assert!(public_key.verify(message, &signature).is_ok());
+        signature[5] ^= 1;
+        assert!(public_key.verify(message, &signature).is_err());
+    }
+
+    #[test]
+    fn verify_prehashed87() {
+        let (serialized_public_key, private_key, _private_seed) = PrivateKey87::generate();
+        let public_key = PublicKey87::parse(&serialized_public_key).unwrap();
+        let message = &[0u8, 1, 2, 3, 4, 5, 6];
+
+        let signature = private_key.sign(message);
+
+        let mut prehash = public_key.prehash();
+        prehash.update(&message[0..2]);
+        prehash.update(&message[2..4]);
+        assert!(public_key.verify_prehashed(prehash, &signature).is_err());
+
+        let mut prehash = public_key.prehash();
+        prehash.update(&message[0..2]);
+        prehash.update(&message[2..4]);
+        prehash.update(&message[4..]);
+        assert!(public_key.verify_prehashed(prehash, &signature).is_ok());
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn verify_prehashed87_write() {
+        use std::io::Write;
+        let (serialized_public_key, private_key, _private_seed) = PrivateKey87::generate();
+        let public_key = PublicKey87::parse(&serialized_public_key).unwrap();
+        let message = &[0u8, 1, 2, 3, 4, 5, 6];
+
+        let signature = private_key.sign(message);
+
+        let mut prehash = public_key.prehash();
+        prehash.write(&message[0..2]).unwrap();
+        prehash.write(&message[2..4]).unwrap();
+        prehash.flush().unwrap();
+        assert!(public_key.verify_prehashed(prehash, &signature).is_err());
+
+        let mut prehash = public_key.prehash();
+        prehash.write(&message[0..2]).unwrap();
+        prehash.write(&message[2..4]).unwrap();
+        prehash.write(&message[4..]).unwrap();
+        prehash.flush().unwrap();
+        assert!(public_key.verify_prehashed(prehash, &signature).is_ok());
+    }
+
+    #[test]
+    fn marshal_public_key87() {
+        let (serialized_public_key, private_key, _) = PrivateKey87::generate();
+        let public_key = PublicKey87::parse(&serialized_public_key).unwrap();
+        assert_eq!(serialized_public_key, public_key.to_bytes());
+        assert_eq!(
+            serialized_public_key,
+            private_key.to_public_key().to_bytes()
+        );
+    }
 }