rust: add SLH-DSA support.

Change-Id: I3e21ef033b2681e203be232ecd61dc6a0c9360c2
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/71470
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: Bob Beck <bbe@google.com>
diff --git a/gen/sources.bzl b/gen/sources.bzl
index 5bd088e..296a04c 100644
--- a/gen/sources.bzl
+++ b/gen/sources.bzl
@@ -2678,6 +2678,7 @@
     "rust/bssl-crypto/src/rand.rs",
     "rust/bssl-crypto/src/rsa.rs",
     "rust/bssl-crypto/src/scoped.rs",
+    "rust/bssl-crypto/src/slhdsa.rs",
     "rust/bssl-crypto/src/test_helpers.rs",
     "rust/bssl-crypto/src/x25519.rs",
 ]
diff --git a/gen/sources.cmake b/gen/sources.cmake
index 594b417..494f167 100644
--- a/gen/sources.cmake
+++ b/gen/sources.cmake
@@ -2724,6 +2724,7 @@
   rust/bssl-crypto/src/rand.rs
   rust/bssl-crypto/src/rsa.rs
   rust/bssl-crypto/src/scoped.rs
+  rust/bssl-crypto/src/slhdsa.rs
   rust/bssl-crypto/src/test_helpers.rs
   rust/bssl-crypto/src/x25519.rs
 )
diff --git a/gen/sources.gni b/gen/sources.gni
index 10a0c01..be9dd0a 100644
--- a/gen/sources.gni
+++ b/gen/sources.gni
@@ -2678,6 +2678,7 @@
   "rust/bssl-crypto/src/rand.rs",
   "rust/bssl-crypto/src/rsa.rs",
   "rust/bssl-crypto/src/scoped.rs",
+  "rust/bssl-crypto/src/slhdsa.rs",
   "rust/bssl-crypto/src/test_helpers.rs",
   "rust/bssl-crypto/src/x25519.rs",
 ]
diff --git a/gen/sources.json b/gen/sources.json
index 7f57e6e..9585d46 100644
--- a/gen/sources.json
+++ b/gen/sources.json
@@ -2660,6 +2660,7 @@
       "rust/bssl-crypto/src/rand.rs",
       "rust/bssl-crypto/src/rsa.rs",
       "rust/bssl-crypto/src/scoped.rs",
+      "rust/bssl-crypto/src/slhdsa.rs",
       "rust/bssl-crypto/src/test_helpers.rs",
       "rust/bssl-crypto/src/x25519.rs"
     ]
diff --git a/rust/bssl-crypto/src/lib.rs b/rust/bssl-crypto/src/lib.rs
index 070895e..b6abfb5 100644
--- a/rust/bssl-crypto/src/lib.rs
+++ b/rust/bssl-crypto/src/lib.rs
@@ -51,6 +51,7 @@
 pub mod mldsa;
 pub mod mlkem;
 pub mod rsa;
+pub mod slhdsa;
 pub mod x25519;
 
 mod scoped;
diff --git a/rust/bssl-crypto/src/slhdsa.rs b/rust/bssl-crypto/src/slhdsa.rs
new file mode 100644
index 0000000..da86e64
--- /dev/null
+++ b/rust/bssl-crypto/src/slhdsa.rs
@@ -0,0 +1,257 @@
+/* Copyright (c) 2024, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+//! SLH-DSA-SHA2-128s.
+//!
+//! SLH-DSA-SHA2-128s is a post-quantum signature scheme. Its has a security
+//! reduction to the underlying hash function (SHA-256), giving an extremely high
+//! level of cryptoanalytic security. However, it has very large signatures (7856
+//! bytes) and signing is very slow (only a few operations per core second on
+//! high-powered cores). A given private key may only be used to sign
+//! 2<sup>64</sup> different messages.
+//!
+//! ```
+//! use bssl_crypto::slhdsa;
+//!
+//! // Generate a key pair.
+//! let (public_key, private_key) = slhdsa::PrivateKey::generate();
+//!
+//! // Sign a message.
+//! let message = b"test message";
+//! let signature = private_key.sign(message);
+//!
+//! // Verify the signature.
+//! assert!(public_key.verify(message, &signature).is_ok());
+//!
+//! // Signing with a context value.
+//! let context = b"context";
+//! let signature2 = private_key.sign_with_context(message, context)
+//!    .expect("signing will always work if the context is less than 256 bytes");
+//!
+//! // The signature isn't value without a matching context.
+//! assert!(public_key.verify(message, &signature2).is_err());
+//!
+//! // ... but works with the correct context.
+//! assert!(public_key.verify_with_context(message, &signature2, context).is_ok());
+//! ```
+
+use crate::{with_output_vec_fallible, FfiSlice, InvalidSignatureError};
+use alloc::vec::Vec;
+
+/// The number of bytes in an SLH-DSA-SHA2-128s public key.
+pub const PUBLIC_KEY_BYTES: usize = bssl_sys::SLHDSA_SHA2_128S_PUBLIC_KEY_BYTES as usize;
+
+/// The number of bytes in an SLH-DSA-SHA2-128s private key.
+pub const PRIVATE_KEY_BYTES: usize = bssl_sys::SLHDSA_SHA2_128S_PRIVATE_KEY_BYTES as usize;
+
+/// The number of bytes in an SLH-DSA-SHA2-128s signature.
+pub const SIGNATURE_BYTES: usize = bssl_sys::SLHDSA_SHA2_128S_SIGNATURE_BYTES as usize;
+
+/// An SLH-DSA-SHA2-128s private key.
+#[derive(Clone, PartialEq, Eq)]
+pub struct PrivateKey([u8; PRIVATE_KEY_BYTES]);
+
+/// An SLH-DSA-SHA2-128s public key.
+#[derive(Clone, PartialEq, Eq)]
+pub struct PublicKey([u8; PUBLIC_KEY_BYTES]);
+
+impl PrivateKey {
+    /// Generates a random public/private key pair.
+    pub fn generate() -> (PublicKey, Self) {
+        let mut public_key = [0u8; PUBLIC_KEY_BYTES];
+        let mut private_key = [0u8; PRIVATE_KEY_BYTES];
+
+        // Safety: the sizes of the arrays are correct.
+        unsafe {
+            bssl_sys::SLHDSA_SHA2_128S_generate_key(
+                public_key.as_mut_ptr(),
+                private_key.as_mut_ptr(),
+            );
+        }
+
+        (PublicKey(public_key), Self(private_key))
+    }
+
+    /// Derives the public key corresponding to this private key.
+    pub fn to_public_key(&self) -> PublicKey {
+        let mut public_key = [0u8; PUBLIC_KEY_BYTES];
+
+        // Safety: the sizes of the arrays are correct.
+        unsafe {
+            bssl_sys::SLHDSA_SHA2_128S_public_from_private(
+                public_key.as_mut_ptr(),
+                self.0.as_ptr(),
+            );
+        }
+
+        PublicKey(public_key)
+    }
+
+    /// Signs a message using this private key.
+    pub fn sign(&self, msg: &[u8]) -> Vec<u8> {
+        #[allow(clippy::expect_used)]
+        self.sign_with_context(msg, &[])
+            .expect("Empty context should always succeed")
+    }
+
+    /// 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>> {
+	// Safety: the FFI function always writes exactly `SIGNATURE_BYTES` to
+	// the first argument if it suceeds. The size of the array passed as
+	// the second argument is correct.
+        unsafe {
+            with_output_vec_fallible(SIGNATURE_BYTES, |signature| {
+                if bssl_sys::SLHDSA_SHA2_128S_sign(
+                    signature,
+                    self.0.as_ptr(),
+                    msg.as_ffi_ptr(),
+                    msg.len(),
+                    context.as_ffi_ptr(),
+                    context.len(),
+                ) == 1
+                {
+                    Some(SIGNATURE_BYTES)
+                } else {
+                    None
+                }
+            })
+        }
+    }
+}
+
+impl AsRef<[u8]> for PrivateKey {
+    /// Returns the bytes of the public key.
+    fn as_ref(&self) -> &[u8] {
+        &self.0
+    }
+}
+
+impl From<&[u8; PRIVATE_KEY_BYTES]> for PrivateKey {
+    fn from(bytes: &[u8; PRIVATE_KEY_BYTES]) -> Self {
+        Self(*bytes)
+    }
+}
+
+impl PublicKey {
+    /// Verifies a signature for a given message using this public key.
+    pub fn verify(&self, msg: &[u8], signature: &[u8]) -> Result<(), InvalidSignatureError> {
+        self.verify_with_context(msg, signature, &[])
+    }
+
+    /// 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> {
+        // Safety: the size of the array passed as the third argument is correct.
+        let ok = unsafe {
+            bssl_sys::SLHDSA_SHA2_128S_verify(
+                signature.as_ffi_ptr(),
+                signature.len(),
+                self.0.as_ptr(),
+                msg.as_ffi_ptr(),
+                msg.len(),
+                context.as_ffi_ptr(),
+                context.len(),
+            )
+        };
+
+        if ok == 1 {
+            Ok(())
+        } else {
+            Err(InvalidSignatureError)
+        }
+    }
+}
+
+impl AsRef<[u8]> for PublicKey {
+    /// Returns the bytes of the public key.
+    fn as_ref(&self) -> &[u8] {
+        &self.0
+    }
+}
+
+impl From<&[u8; PUBLIC_KEY_BYTES]> for PublicKey {
+    fn from(bytes: &[u8; PUBLIC_KEY_BYTES]) -> Self {
+        Self(*bytes)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_key_generation_and_signature() {
+        let (public_key, private_key) = PrivateKey::generate();
+        let msg = b"test message";
+        let sig = private_key.sign(msg);
+
+        assert!(public_key.verify(msg, &sig).is_ok());
+
+        let mut invalid_sig = sig.clone();
+        invalid_sig[0] ^= 1;
+        assert!(public_key.verify(msg, &invalid_sig).is_err());
+    }
+
+    #[test]
+    fn test_sign_and_verify_with_context() {
+        let (public_key, private_key) = PrivateKey::generate();
+        let msg = b"test message";
+        let context = b"test context";
+
+        let sig = private_key.sign_with_context(msg, context).unwrap();
+        assert!(public_key.verify_with_context(msg, &sig, context).is_ok());
+        assert!(public_key
+            .verify_with_context(msg, &sig, b"wrong context")
+            .is_err());
+    }
+
+    #[test]
+    fn test_public_key_from_private() {
+        let (public_key, private_key) = PrivateKey::generate();
+        let derived_public_key = private_key.to_public_key();
+
+        assert_eq!(public_key.0, derived_public_key.0);
+    }
+
+    #[test]
+    fn test_empty_message_and_context() {
+        let (public_key, private_key) = PrivateKey::generate();
+        let msg = b"";
+        let context = b"";
+
+        let sig = private_key.sign_with_context(msg, context).unwrap();
+        assert!(public_key.verify_with_context(msg, &sig, context).is_ok());
+    }
+
+    #[test]
+    fn test_max_context_length() {
+        let (public_key, private_key) = PrivateKey::generate();
+        let msg = b"test message";
+        let context = vec![0u8; 255]; // Maximum allowed context length
+
+        let sig = private_key.sign_with_context(msg, &context).unwrap();
+        assert!(public_key.verify_with_context(msg, &sig, &context).is_ok());
+
+        let too_long_context = vec![0u8; 256];
+        assert!(private_key
+            .sign_with_context(msg, &too_long_context)
+            .is_none());
+    }
+}
diff --git a/rust/bssl-sys/wrapper.h b/rust/bssl-sys/wrapper.h
index 3a578c9..8413a3e 100644
--- a/rust/bssl-sys/wrapper.h
+++ b/rust/bssl-sys/wrapper.h
@@ -66,6 +66,7 @@
 #include <openssl/safestack.h>
 #include <openssl/sha.h>
 #include <openssl/siphash.h>
+#include <openssl/slhdsa.h>
 #include <openssl/span.h>
 #include <openssl/srtp.h>
 #include <openssl/ssl.h>