Implement mls-rs-crypto-traits backed by BoringSSL.

Fix: 302021139
Test: Presubmit
Change-Id: Iaefa21d3fb69f92d735875778f3f96e1878d0876
diff --git a/mls/mls-rs-crypto-boringssl/src/eddsa.rs b/mls/mls-rs-crypto-boringssl/src/eddsa.rs
new file mode 100644
index 0000000..473b756
--- /dev/null
+++ b/mls/mls-rs-crypto-boringssl/src/eddsa.rs
@@ -0,0 +1,284 @@
+// Copyright 2024, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Edwards-curve digital signature algorithm.
+
+use bssl_crypto::{ed25519, InvalidSignatureError};
+use mls_rs_core::crypto::{CipherSuite, SignaturePublicKey, SignatureSecretKey};
+use mls_rs_crypto_traits::Curve;
+
+use core::array::TryFromSliceError;
+use thiserror::Error;
+
+/// Errors returned from EdDSA.
+#[derive(Debug, Error)]
+pub enum EdDsaError {
+    /// Error returned when conversion from slice to array fails.
+    #[error(transparent)]
+    TryFromSliceError(#[from] TryFromSliceError),
+    /// Error returned on an invalid signature.
+    #[error("invalid signature")]
+    InvalidSig(InvalidSignatureError),
+    /// Error returned when the private key length is invalid.
+    #[error("EdDSA private key of invalid length {len}, expected length {expected_len}")]
+    InvalidPrivKeyLen {
+        /// Invalid key length.
+        len: usize,
+        /// Expected key length.
+        expected_len: usize,
+    },
+    /// Error returned when the public key length is invalid.
+    #[error("EdDSA public key of invalid length {len}, expected length {expected_len}")]
+    InvalidPubKeyLen {
+        /// Invalid key length.
+        len: usize,
+        /// Expected key length.
+        expected_len: usize,
+    },
+    /// Error returned when the signature length is invalid.
+    #[error("EdDSA signature of invalid length {len}, expected length {expected_len}")]
+    InvalidSigLen {
+        /// Invalid signature length.
+        len: usize,
+        /// Expected signature length.
+        expected_len: usize,
+    },
+    /// Error returned when unsupported cipher suite is requested.
+    #[error("unsupported cipher suite")]
+    UnsupportedCipherSuite,
+}
+
+// Explicitly implemented as InvalidSignatureError's as_dyn_error does not satisfy trait bounds.
+impl From<InvalidSignatureError> for EdDsaError {
+    fn from(e: InvalidSignatureError) -> Self {
+        EdDsaError::InvalidSig(e)
+    }
+}
+
+/// EdDSA implementation backed by BoringSSL.
+#[derive(Clone, Debug, Copy, PartialEq, Eq)]
+pub struct EdDsa(Curve);
+
+impl EdDsa {
+    /// Creates a new EdDsa.
+    pub fn new(cipher_suite: CipherSuite) -> Option<Self> {
+        Curve::from_ciphersuite(cipher_suite, /*for_sig=*/ true).map(Self)
+    }
+
+    /// Generates a key pair.
+    pub fn signature_key_generate(
+        &self,
+    ) -> Result<(SignatureSecretKey, SignaturePublicKey), EdDsaError> {
+        if self.0 != Curve::Ed25519 {
+            return Err(EdDsaError::UnsupportedCipherSuite);
+        }
+
+        let private_key = ed25519::PrivateKey::generate();
+        let public_key = private_key.to_public();
+        Ok((private_key.to_seed().to_vec().into(), public_key.as_bytes().to_vec().into()))
+    }
+
+    /// Derives the public key from the private key.
+    pub fn signature_key_derive_public(
+        &self,
+        secret_key: &SignatureSecretKey,
+    ) -> Result<SignaturePublicKey, EdDsaError> {
+        if self.0 != Curve::Ed25519 {
+            return Err(EdDsaError::UnsupportedCipherSuite);
+        }
+        if secret_key.len() != ed25519::SEED_LEN {
+            return Err(EdDsaError::InvalidPrivKeyLen {
+                len: secret_key.len(),
+                expected_len: ed25519::SEED_LEN,
+            });
+        }
+
+        let private_key =
+            ed25519::PrivateKey::from_seed(secret_key[..ed25519::SEED_LEN].try_into()?);
+        Ok(private_key.to_public().as_bytes().to_vec().into())
+    }
+
+    /// Signs `data` using `secret_key`.
+    pub fn sign(
+        &self,
+        secret_key: &SignatureSecretKey,
+        data: &[u8],
+    ) -> Result<Vec<u8>, EdDsaError> {
+        if self.0 != Curve::Ed25519 {
+            return Err(EdDsaError::UnsupportedCipherSuite);
+        }
+        if secret_key.len() != ed25519::SEED_LEN {
+            return Err(EdDsaError::InvalidPrivKeyLen {
+                len: secret_key.len(),
+                expected_len: ed25519::SEED_LEN,
+            });
+        }
+
+        let private_key =
+            ed25519::PrivateKey::from_seed(secret_key[..ed25519::SEED_LEN].try_into()?);
+        Ok(private_key.sign(data).to_vec())
+    }
+
+    /// Verifies `signature` is a valid signature of `data` using `public_key`.
+    pub fn verify(
+        &self,
+        public_key: &SignaturePublicKey,
+        signature: &[u8],
+        data: &[u8],
+    ) -> Result<(), EdDsaError> {
+        if self.0 != Curve::Ed25519 {
+            return Err(EdDsaError::UnsupportedCipherSuite);
+        }
+        if public_key.len() != ed25519::PUBLIC_KEY_LEN {
+            return Err(EdDsaError::InvalidPubKeyLen {
+                len: public_key.len(),
+                expected_len: ed25519::PUBLIC_KEY_LEN,
+            });
+        }
+        if signature.len() != ed25519::SIGNATURE_LEN {
+            return Err(EdDsaError::InvalidSigLen {
+                len: signature.len(),
+                expected_len: ed25519::SIGNATURE_LEN,
+            });
+        }
+
+        let public_key = ed25519::PublicKey::from_bytes(
+            public_key.as_bytes()[..ed25519::PUBLIC_KEY_LEN].try_into()?,
+        );
+        match public_key.verify(data, signature[..ed25519::SIGNATURE_LEN].try_into()?) {
+            Ok(_) => Ok(()),
+            Err(e) => Err(EdDsaError::InvalidSig(e)),
+        }
+    }
+}
+
+#[cfg(all(not(mls_build_async), test))]
+mod test {
+    use super::{EdDsa, EdDsaError};
+    use crate::test_helpers::decode_hex;
+    use assert_matches::assert_matches;
+    use mls_rs_core::crypto::{CipherSuite, SignaturePublicKey, SignatureSecretKey};
+
+    #[test]
+    fn signature_key_generate() {
+        let ed25519 = EdDsa::new(CipherSuite::CURVE25519_AES128).unwrap();
+        assert!(ed25519.signature_key_generate().is_ok());
+    }
+
+    #[test]
+    fn signature_key_derive_public() {
+        // Test 1 from https://www.rfc-editor.org/rfc/rfc8032#section-7.1
+        let private_key = SignatureSecretKey::from(
+            decode_hex::<32>("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60")
+                .to_vec(),
+        );
+        let expected_public_key = SignaturePublicKey::from(
+            decode_hex::<32>("d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a")
+                .to_vec(),
+        );
+
+        let ed25519 = EdDsa::new(CipherSuite::CURVE25519_CHACHA).unwrap();
+        assert_eq!(ed25519.signature_key_derive_public(&private_key).unwrap(), expected_public_key);
+    }
+
+    #[test]
+    fn signature_key_derive_public_invalid_key() {
+        let private_key_short =
+            SignatureSecretKey::from(decode_hex::<16>("9d61b19deffd5a60ba844af492ec2cc4").to_vec());
+
+        let ed25519 = EdDsa::new(CipherSuite::CURVE25519_CHACHA).unwrap();
+        assert_matches!(
+            ed25519.signature_key_derive_public(&private_key_short),
+            Err(EdDsaError::InvalidPrivKeyLen { .. })
+        );
+    }
+
+    #[test]
+    fn sign_verify() {
+        // Test 3 from https://www.rfc-editor.org/rfc/rfc8032#section-7.1
+        let private_key = SignatureSecretKey::from(
+            decode_hex::<32>("c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7")
+                .to_vec(),
+        );
+        let data: [u8; 2] = decode_hex("af82");
+        let expected_sig = decode_hex::<64>("6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a").to_vec();
+
+        let ed25519 = EdDsa::new(CipherSuite::CURVE25519_AES128).unwrap();
+        let sig = ed25519.sign(&private_key, &data).unwrap();
+        assert_eq!(sig, expected_sig);
+
+        let public_key = ed25519.signature_key_derive_public(&private_key).unwrap();
+        assert!(ed25519.verify(&public_key, &sig, &data).is_ok());
+    }
+
+    #[test]
+    fn sign_invalid_key() {
+        let private_key_short =
+            SignatureSecretKey::from(decode_hex::<16>("c5aa8df43f9f837bedb7442f31dcb7b1").to_vec());
+
+        let ed25519 = EdDsa::new(CipherSuite::CURVE25519_AES128).unwrap();
+        assert_matches!(
+            ed25519.sign(&private_key_short, &decode_hex::<2>("af82")),
+            Err(EdDsaError::InvalidPrivKeyLen { .. })
+        );
+    }
+
+    #[test]
+    fn verify_invalid_key() {
+        let public_key_short =
+            SignaturePublicKey::from(decode_hex::<16>("fc51cd8e6218a1a38da47ed00230f058").to_vec());
+        let sig = decode_hex::<64>("6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a").to_vec();
+        let data: [u8; 2] = decode_hex("af82");
+
+        let ed25519 = EdDsa::new(CipherSuite::CURVE25519_AES128).unwrap();
+        assert_matches!(
+            ed25519.verify(&public_key_short, &sig, &data),
+            Err(EdDsaError::InvalidPubKeyLen { .. })
+        );
+    }
+
+    #[test]
+    fn verify_invalid_sig() {
+        let public_key = SignaturePublicKey::from(
+            decode_hex::<32>("fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025")
+                .to_vec(),
+        );
+        let sig_short =
+            decode_hex::<32>("6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac")
+                .to_vec();
+        let data: [u8; 2] = decode_hex("af82");
+
+        let ed25519 = EdDsa::new(CipherSuite::CURVE25519_AES128).unwrap();
+        assert_matches!(
+            ed25519.verify(&public_key, &sig_short, &data),
+            Err(EdDsaError::InvalidSigLen { .. })
+        );
+    }
+
+    #[test]
+    fn unsupported_cipher_suites() {
+        for suite in vec![
+            CipherSuite::P256_AES128,
+            CipherSuite::P384_AES256,
+            CipherSuite::P521_AES256,
+            CipherSuite::CURVE448_CHACHA,
+            CipherSuite::CURVE448_AES256,
+        ] {
+            assert_matches!(
+                EdDsa::new(suite).unwrap().signature_key_generate(),
+                Err(EdDsaError::UnsupportedCipherSuite)
+            );
+        }
+    }
+}