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

Fix: 302021139
Test: Presubmit
Change-Id: Iaefa21d3fb69f92d735875778f3f96e1878d0876
diff --git a/mls/mls-rs-crypto-boringssl/src/lib.rs b/mls/mls-rs-crypto-boringssl/src/lib.rs
new file mode 100644
index 0000000..806bd87
--- /dev/null
+++ b/mls/mls-rs-crypto-boringssl/src/lib.rs
@@ -0,0 +1,676 @@
+// 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.
+
+//! Implements mls_rs_core's CryptoProvider and CipherSuiteProvider backed by BoringSSL.
+
+pub mod aead;
+pub mod ecdh;
+pub mod eddsa;
+pub mod hash;
+pub mod hpke;
+pub mod kdf;
+
+#[cfg(test)]
+mod test_helpers;
+
+use mls_rs_core::crypto::{
+    CipherSuite, CipherSuiteProvider, CryptoProvider, HpkeCiphertext, HpkePublicKey, HpkeSecretKey,
+    SignaturePublicKey, SignatureSecretKey,
+};
+use mls_rs_core::error::{AnyError, IntoAnyError};
+use mls_rs_crypto_traits::{AeadType, KdfType, KemType};
+use thiserror::Error;
+use zeroize::Zeroizing;
+
+use aead::AeadWrapper;
+use ecdh::Ecdh;
+use eddsa::{EdDsa, EdDsaError};
+use hash::{Hash, HashError};
+use hpke::{ContextR, ContextS, DhKem, Hpke, HpkeError};
+use kdf::Kdf;
+
+/// Errors returned from BoringsslCryptoProvider.
+#[derive(Debug, Error)]
+pub enum BoringsslCryptoError {
+    /// Error returned from hash functions and HMACs.
+    #[error(transparent)]
+    HashError(#[from] HashError),
+    /// Error returned from KEMs.
+    #[error(transparent)]
+    KemError(AnyError),
+    /// Error returned from KDFs.
+    #[error(transparent)]
+    KdfError(AnyError),
+    /// Error returned from AEADs.
+    #[error(transparent)]
+    AeadError(AnyError),
+    /// Error returned from HPKE.
+    #[error(transparent)]
+    HpkeError(#[from] HpkeError),
+    /// Error returned from EdDSA.
+    #[error(transparent)]
+    EdDsaError(#[from] EdDsaError),
+}
+
+impl IntoAnyError for BoringsslCryptoError {
+    fn into_dyn_error(self) -> Result<Box<dyn std::error::Error + Send + Sync>, Self> {
+        Ok(self.into())
+    }
+}
+
+/// CryptoProvider trait implementation backed by BoringSSL.
+#[derive(Debug, Clone)]
+#[non_exhaustive]
+pub struct BoringsslCryptoProvider {
+    /// Available cipher suites.
+    pub enabled_cipher_suites: Vec<CipherSuite>,
+}
+
+impl BoringsslCryptoProvider {
+    /// Creates a new BoringsslCryptoProvider.
+    pub fn new() -> Self {
+        Default::default()
+    }
+
+    /// Sets the enabled cipher suites.
+    pub fn with_enabled_cipher_suites(enabled_cipher_suites: Vec<CipherSuite>) -> Self {
+        Self { enabled_cipher_suites }
+    }
+
+    /// Returns all available cipher suites.
+    pub fn all_supported_cipher_suites() -> Vec<CipherSuite> {
+        vec![CipherSuite::CURVE25519_AES128, CipherSuite::CURVE25519_CHACHA]
+    }
+}
+
+impl Default for BoringsslCryptoProvider {
+    fn default() -> Self {
+        Self { enabled_cipher_suites: Self::all_supported_cipher_suites() }
+    }
+}
+
+impl CryptoProvider for BoringsslCryptoProvider {
+    type CipherSuiteProvider = BoringsslCipherSuite<DhKem<Ecdh, Kdf>, Kdf, AeadWrapper>;
+
+    fn supported_cipher_suites(&self) -> Vec<CipherSuite> {
+        self.enabled_cipher_suites.clone()
+    }
+
+    fn cipher_suite_provider(
+        &self,
+        cipher_suite: CipherSuite,
+    ) -> Option<Self::CipherSuiteProvider> {
+        if !self.enabled_cipher_suites.contains(&cipher_suite) {
+            return None;
+        }
+
+        let ecdh = Ecdh::new(cipher_suite)?;
+        let kdf = Kdf::new(cipher_suite)?;
+        let kem = DhKem::new(cipher_suite, ecdh, kdf.clone())?;
+        let aead = AeadWrapper::new(cipher_suite)?;
+
+        BoringsslCipherSuite::new(cipher_suite, kem, kdf, aead)
+    }
+}
+
+/// CipherSuiteProvider trait implementation backed by BoringSSL.
+#[derive(Clone)]
+pub struct BoringsslCipherSuite<KEM, KDF, AEAD>
+where
+    KEM: KemType + Clone,
+    KDF: KdfType + Clone,
+    AEAD: AeadType + Clone,
+{
+    cipher_suite: CipherSuite,
+    hash: Hash,
+    kem: KEM,
+    kdf: KDF,
+    aead: AEAD,
+    hpke: Hpke,
+    eddsa: EdDsa,
+}
+
+impl<KEM, KDF, AEAD> BoringsslCipherSuite<KEM, KDF, AEAD>
+where
+    KEM: KemType + Clone,
+    KDF: KdfType + Clone,
+    AEAD: AeadType + Clone,
+{
+    /// Creates a new BoringsslCipherSuite.
+    pub fn new(cipher_suite: CipherSuite, kem: KEM, kdf: KDF, aead: AEAD) -> Option<Self> {
+        Some(Self {
+            cipher_suite,
+            hash: Hash::new(cipher_suite).ok()?,
+            kem,
+            kdf,
+            aead,
+            hpke: Hpke::new(cipher_suite),
+            eddsa: EdDsa::new(cipher_suite)?,
+        })
+    }
+
+    /// Returns random bytes generated via BoringSSL.
+    pub fn random_bytes(&self, out: &mut [u8]) -> Result<(), BoringsslCryptoError> {
+        bssl_crypto::rand_bytes(out);
+        Ok(())
+    }
+}
+
+#[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
+#[cfg_attr(all(target_arch = "wasm32", mls_build_async), maybe_async::must_be_async(?Send))]
+#[cfg_attr(all(not(target_arch = "wasm32"), mls_build_async), maybe_async::must_be_async)]
+impl<KEM, KDF, AEAD> CipherSuiteProvider for BoringsslCipherSuite<KEM, KDF, AEAD>
+where
+    KEM: KemType + Clone + Send + Sync,
+    KDF: KdfType + Clone + Send + Sync,
+    AEAD: AeadType + Clone + Send + Sync,
+{
+    type Error = BoringsslCryptoError;
+    type HpkeContextS = ContextS;
+    type HpkeContextR = ContextR;
+
+    fn cipher_suite(&self) -> CipherSuite {
+        self.cipher_suite
+    }
+
+    fn random_bytes(&self, out: &mut [u8]) -> Result<(), Self::Error> {
+        self.random_bytes(out)
+    }
+
+    async fn hash(&self, data: &[u8]) -> Result<Vec<u8>, Self::Error> {
+        Ok(self.hash.hash(data))
+    }
+
+    async fn mac(&self, key: &[u8], data: &[u8]) -> Result<Vec<u8>, Self::Error> {
+        Ok(self.hash.mac(key, data)?)
+    }
+
+    async fn kem_generate(&self) -> Result<(HpkeSecretKey, HpkePublicKey), Self::Error> {
+        self.kem.generate().await.map_err(|e| BoringsslCryptoError::KemError(e.into_any_error()))
+    }
+
+    async fn kem_derive(&self, ikm: &[u8]) -> Result<(HpkeSecretKey, HpkePublicKey), Self::Error> {
+        self.kem.derive(ikm).await.map_err(|e| BoringsslCryptoError::KemError(e.into_any_error()))
+    }
+
+    fn kem_public_key_validate(&self, key: &HpkePublicKey) -> Result<(), Self::Error> {
+        self.kem
+            .public_key_validate(key)
+            .map_err(|e| BoringsslCryptoError::KemError(e.into_any_error()))
+    }
+
+    async fn kdf_extract(
+        &self,
+        salt: &[u8],
+        ikm: &[u8],
+    ) -> Result<Zeroizing<Vec<u8>>, Self::Error> {
+        self.kdf
+            .extract(salt, ikm)
+            .await
+            .map_err(|e| BoringsslCryptoError::KdfError(e.into_any_error()))
+            .map(Zeroizing::new)
+    }
+
+    async fn kdf_expand(
+        &self,
+        prk: &[u8],
+        info: &[u8],
+        len: usize,
+    ) -> Result<Zeroizing<Vec<u8>>, Self::Error> {
+        self.kdf
+            .expand(prk, info, len)
+            .await
+            .map_err(|e| BoringsslCryptoError::KdfError(e.into_any_error()))
+            .map(Zeroizing::new)
+    }
+
+    fn kdf_extract_size(&self) -> usize {
+        self.kdf.extract_size()
+    }
+
+    async fn aead_seal(
+        &self,
+        key: &[u8],
+        data: &[u8],
+        aad: Option<&[u8]>,
+        nonce: &[u8],
+    ) -> Result<Vec<u8>, Self::Error> {
+        self.aead
+            .seal(key, data, aad, nonce)
+            .await
+            .map_err(|e| BoringsslCryptoError::AeadError(e.into_any_error()))
+    }
+
+    async fn aead_open(
+        &self,
+        key: &[u8],
+        cipher_text: &[u8],
+        aad: Option<&[u8]>,
+        nonce: &[u8],
+    ) -> Result<Zeroizing<Vec<u8>>, Self::Error> {
+        self.aead
+            .open(key, cipher_text, aad, nonce)
+            .await
+            .map_err(|e| BoringsslCryptoError::AeadError(e.into_any_error()))
+            .map(Zeroizing::new)
+    }
+
+    fn aead_key_size(&self) -> usize {
+        self.aead.key_size()
+    }
+
+    fn aead_nonce_size(&self) -> usize {
+        self.aead.nonce_size()
+    }
+
+    async fn hpke_setup_s(
+        &self,
+        remote_key: &HpkePublicKey,
+        info: &[u8],
+    ) -> Result<(Vec<u8>, Self::HpkeContextS), Self::Error> {
+        Ok(self.hpke.setup_sender(remote_key, info).await?)
+    }
+
+    async fn hpke_seal(
+        &self,
+        remote_key: &HpkePublicKey,
+        info: &[u8],
+        aad: Option<&[u8]>,
+        pt: &[u8],
+    ) -> Result<HpkeCiphertext, Self::Error> {
+        Ok(self.hpke.seal(remote_key, info, aad, pt).await?)
+    }
+
+    async fn hpke_setup_r(
+        &self,
+        enc: &[u8],
+        local_secret: &HpkeSecretKey,
+        // Other implementations use `_local_public` to skip derivation of the public from the
+        // private key for the KEM decapsulation step, but BoringSSL's API does not accept a public
+        // key and instead derives it under the hood.
+        _local_public: &HpkePublicKey,
+        info: &[u8],
+    ) -> Result<Self::HpkeContextR, Self::Error> {
+        Ok(self.hpke.setup_receiver(enc, local_secret, info).await?)
+    }
+
+    async fn hpke_open(
+        &self,
+        ciphertext: &HpkeCiphertext,
+        local_secret: &HpkeSecretKey,
+        // Other implementations use `_local_public` to skip derivation of the public from the
+        // private key for hpke_setup_r()'s KEM decapsulation step, but BoringSSL's API does not
+        // accept a public key and instead derives it under the hood.
+        _local_public: &HpkePublicKey,
+        info: &[u8],
+        aad: Option<&[u8]>,
+    ) -> Result<Vec<u8>, Self::Error> {
+        Ok(self.hpke.open(ciphertext, local_secret, info, aad).await?)
+    }
+
+    async fn signature_key_generate(
+        &self,
+    ) -> Result<(SignatureSecretKey, SignaturePublicKey), Self::Error> {
+        Ok(self.eddsa.signature_key_generate()?)
+    }
+
+    async fn signature_key_derive_public(
+        &self,
+        secret_key: &SignatureSecretKey,
+    ) -> Result<SignaturePublicKey, Self::Error> {
+        Ok(self.eddsa.signature_key_derive_public(secret_key)?)
+    }
+
+    async fn sign(
+        &self,
+        secret_key: &SignatureSecretKey,
+        data: &[u8],
+    ) -> Result<Vec<u8>, Self::Error> {
+        Ok(self.eddsa.sign(secret_key, data)?)
+    }
+
+    async fn verify(
+        &self,
+        public_key: &SignaturePublicKey,
+        signature: &[u8],
+        data: &[u8],
+    ) -> Result<(), Self::Error> {
+        Ok(self.eddsa.verify(public_key, signature, data)?)
+    }
+}
+
+#[cfg(all(not(mls_build_async), test))]
+mod test {
+    use super::BoringsslCryptoProvider;
+    use crate::test_helpers::decode_hex;
+    use mls_rs_core::crypto::{
+        CipherSuite, CipherSuiteProvider, CryptoProvider, HpkeContextR, HpkeContextS,
+        HpkePublicKey, HpkeSecretKey, SignaturePublicKey, SignatureSecretKey,
+    };
+
+    fn get_cipher_suites() -> Vec<CipherSuite> {
+        vec![CipherSuite::CURVE25519_AES128, CipherSuite::CURVE25519_CHACHA]
+    }
+
+    #[test]
+    fn supported_cipher_suites() {
+        let bssl = BoringsslCryptoProvider::new();
+        assert_eq!(bssl.supported_cipher_suites().len(), 2);
+    }
+
+    #[test]
+    fn unsupported_cipher_suites() {
+        let bssl = BoringsslCryptoProvider::new();
+        for suite in vec![
+            CipherSuite::P256_AES128,
+            CipherSuite::CURVE448_AES256,
+            CipherSuite::P521_AES256,
+            CipherSuite::CURVE448_CHACHA,
+            CipherSuite::P384_AES256,
+        ] {
+            assert!(bssl.cipher_suite_provider(suite).is_none());
+        }
+    }
+
+    #[test]
+    fn cipher_suite() {
+        let bssl = BoringsslCryptoProvider::new();
+        for suite in get_cipher_suites() {
+            let crypto = bssl.cipher_suite_provider(suite).unwrap();
+            assert_eq!(crypto.cipher_suite(), suite);
+        }
+    }
+
+    #[test]
+    fn random_bytes() {
+        let bssl = BoringsslCryptoProvider::new();
+        for suite in get_cipher_suites() {
+            let crypto = bssl.cipher_suite_provider(suite).unwrap();
+            let mut buf = [0; 32];
+            let _ = crypto.random_bytes(&mut buf);
+        }
+    }
+
+    #[test]
+    fn hash() {
+        let bssl = BoringsslCryptoProvider::new();
+        for suite in get_cipher_suites() {
+            let crypto = bssl.cipher_suite_provider(suite).unwrap();
+            assert_eq!(
+                crypto.hash(&decode_hex::<4>("74ba2521")).unwrap(),
+                // bssl_crypto::hmac test vector.
+                decode_hex::<32>(
+                    "b16aa56be3880d18cd41e68384cf1ec8c17680c45a02b1575dc1518923ae8b0e"
+                )
+            );
+        }
+    }
+
+    #[test]
+    fn mac() {
+        let bssl = BoringsslCryptoProvider::new();
+        for suite in get_cipher_suites() {
+            let crypto = bssl.cipher_suite_provider(suite).unwrap();
+            // bssl_crypto::hmac test vector.
+            let expected = vec![
+                0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb,
+                0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
+                0x2e, 0x32, 0xcf, 0xf7,
+            ];
+            let key: [u8; 20] = [0x0b; 20];
+            let data = b"Hi There";
+
+            assert_eq!(crypto.mac(&key, data).unwrap(), expected);
+        }
+    }
+
+    #[test]
+    fn kem_generate() {
+        let bssl = BoringsslCryptoProvider::new();
+        for suite in get_cipher_suites() {
+            let crypto = bssl.cipher_suite_provider(suite).unwrap();
+            assert!(crypto.kem_generate().is_ok());
+        }
+    }
+
+    #[test]
+    fn kem_derive() {
+        let bssl = BoringsslCryptoProvider::new();
+        for suite in get_cipher_suites() {
+            let crypto = bssl.cipher_suite_provider(suite).unwrap();
+            // https://www.rfc-editor.org/rfc/rfc9180.html#appendix-A.1.1
+            let ikm: [u8; 32] =
+                decode_hex("7268600d403fce431561aef583ee1613527cff655c1343f29812e66706df3234");
+            let expected_sk = HpkeSecretKey::from(
+                decode_hex::<32>(
+                    "52c4a758a802cd8b936eceea314432798d5baf2d7e9235dc084ab1b9cfa2f736",
+                )
+                .to_vec(),
+            );
+            let expected_pk = HpkePublicKey::from(
+                decode_hex::<32>(
+                    "37fda3567bdbd628e88668c3c8d7e97d1d1253b6d4ea6d44c150f741f1bf4431",
+                )
+                .to_vec(),
+            );
+
+            let (sk, pk) = crypto.kem_derive(&ikm).unwrap();
+            assert_eq!(sk, expected_sk);
+            assert_eq!(pk, expected_pk);
+        }
+    }
+
+    #[test]
+    fn kem_public_key_validate() {
+        let bssl = BoringsslCryptoProvider::new();
+        for suite in get_cipher_suites() {
+            let crypto = bssl.cipher_suite_provider(suite).unwrap();
+            // https://www.rfc-editor.org/rfc/rfc7748.html#section-6.1
+            let public_key = HpkePublicKey::from(
+                decode_hex::<32>(
+                    "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a",
+                )
+                .to_vec(),
+            );
+            assert!(crypto.kem_public_key_validate(&public_key).is_ok());
+        }
+    }
+
+    #[test]
+    fn kdf_extract_and_expand() {
+        let bssl = BoringsslCryptoProvider::new();
+        for suite in get_cipher_suites() {
+            let crypto = bssl.cipher_suite_provider(suite).unwrap();
+            // https://www.rfc-editor.org/rfc/rfc5869.html#appendix-A.1
+            let ikm: [u8; 22] = decode_hex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
+            let salt: [u8; 13] = decode_hex("000102030405060708090a0b0c");
+            let info: [u8; 10] = decode_hex("f0f1f2f3f4f5f6f7f8f9");
+            let expected_prk: [u8; 32] =
+                decode_hex("077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5");
+            let expected_okm : [u8; 42] = decode_hex(
+                    "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865"
+                );
+
+            let prk = crypto.kdf_extract(&salt, &ikm).unwrap();
+            assert_eq!(prk.as_ref(), expected_prk);
+            assert_eq!(crypto.kdf_expand(&prk.as_ref(), &info, 42).unwrap().as_ref(), expected_okm);
+        }
+    }
+
+    #[test]
+    fn kdf_extract_size() {
+        let bssl = BoringsslCryptoProvider::new();
+        for suite in get_cipher_suites() {
+            let crypto = bssl.cipher_suite_provider(suite).unwrap();
+            assert_eq!(crypto.kdf_extract_size(), 32);
+        }
+    }
+
+    #[test]
+    fn aead() {
+        let bssl = BoringsslCryptoProvider::new();
+        for suite in get_cipher_suites() {
+            let crypto = bssl.cipher_suite_provider(suite).unwrap();
+            let key = vec![42u8; crypto.aead_key_size()];
+            let associated_data = vec![42u8, 12];
+            let nonce = vec![42u8; crypto.aead_nonce_size()];
+            let plaintext = b"message";
+
+            let ciphertext =
+                crypto.aead_seal(&key, plaintext, Some(&associated_data), &nonce).unwrap();
+            assert_eq!(
+                plaintext,
+                crypto
+                    .aead_open(&key, ciphertext.as_slice(), Some(&associated_data), &nonce)
+                    .unwrap()
+                    .as_slice()
+            );
+        }
+    }
+
+    #[test]
+    fn hpke_setup_seal_open_export() {
+        let bssl = BoringsslCryptoProvider::new();
+        for suite in get_cipher_suites() {
+            let crypto = bssl.cipher_suite_provider(suite).unwrap();
+            // https://www.rfc-editor.org/rfc/rfc9180.html#appendix-A.1.1
+            let receiver_pub_key = HpkePublicKey::from(
+                decode_hex::<32>(
+                    "3948cfe0ad1ddb695d780e59077195da6c56506b027329794ab02bca80815c4d",
+                )
+                .to_vec(),
+            );
+            let receiver_priv_key = HpkeSecretKey::from(
+                decode_hex::<32>(
+                    "4612c550263fc8ad58375df3f557aac531d26850903e55a9f23f21d8534e8ac8",
+                )
+                .to_vec(),
+            );
+
+            let info = b"some_info";
+            let plaintext = b"plaintext";
+            let associated_data = b"some_ad";
+            let exporter_ctx = b"export_ctx";
+
+            let (enc, mut sender_ctx) = crypto.hpke_setup_s(&receiver_pub_key, info).unwrap();
+            let mut receiver_ctx =
+                crypto.hpke_setup_r(&enc, &receiver_priv_key, &receiver_pub_key, info).unwrap();
+            let ct = sender_ctx.seal(Some(associated_data), plaintext).unwrap();
+            assert_eq!(plaintext.as_ref(), receiver_ctx.open(Some(associated_data), &ct).unwrap(),);
+            assert_eq!(
+                sender_ctx.export(exporter_ctx, 32).unwrap(),
+                receiver_ctx.export(exporter_ctx, 32).unwrap(),
+            );
+        }
+    }
+
+    #[test]
+    fn hpke_seal_open() {
+        let bssl = BoringsslCryptoProvider::new();
+        for suite in get_cipher_suites() {
+            let crypto = bssl.cipher_suite_provider(suite).unwrap();
+            // https://www.rfc-editor.org/rfc/rfc9180.html#appendix-A.1.1
+            let receiver_pub_key = HpkePublicKey::from(
+                decode_hex::<32>(
+                    "3948cfe0ad1ddb695d780e59077195da6c56506b027329794ab02bca80815c4d",
+                )
+                .to_vec(),
+            );
+            let receiver_priv_key = HpkeSecretKey::from(
+                decode_hex::<32>(
+                    "4612c550263fc8ad58375df3f557aac531d26850903e55a9f23f21d8534e8ac8",
+                )
+                .to_vec(),
+            );
+
+            let info = b"some_info";
+            let plaintext = b"plaintext";
+            let associated_data = b"some_ad";
+
+            let ct = crypto
+                .hpke_seal(&receiver_pub_key, info, Some(associated_data), plaintext)
+                .unwrap();
+            assert_eq!(
+                plaintext.as_ref(),
+                crypto
+                    .hpke_open(
+                        &ct,
+                        &receiver_priv_key,
+                        &receiver_pub_key,
+                        info,
+                        Some(associated_data)
+                    )
+                    .unwrap(),
+            );
+        }
+    }
+
+    #[test]
+    fn signature_key_generate() {
+        let bssl = BoringsslCryptoProvider::new();
+        for suite in get_cipher_suites() {
+            let crypto = bssl.cipher_suite_provider(suite).unwrap();
+            assert!(crypto.signature_key_generate().is_ok());
+        }
+    }
+
+    #[test]
+    fn signature_key_derive_public() {
+        let bssl = BoringsslCryptoProvider::new();
+        for suite in get_cipher_suites() {
+            let crypto = bssl.cipher_suite_provider(suite).unwrap();
+            // 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(),
+            );
+
+            assert_eq!(
+                crypto.signature_key_derive_public(&private_key).unwrap(),
+                expected_public_key
+            );
+        }
+    }
+
+    #[test]
+    fn sign_verify() {
+        let bssl = BoringsslCryptoProvider::new();
+        for suite in get_cipher_suites() {
+            let crypto = bssl.cipher_suite_provider(suite).unwrap();
+            // 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 sig = crypto.sign(&private_key, &data).unwrap();
+            assert_eq!(sig, expected_sig);
+
+            let public_key = crypto.signature_key_derive_public(&private_key).unwrap();
+            assert!(crypto.verify(&public_key, &sig, &data).is_ok());
+        }
+    }
+}