// 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, 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, } 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) -> Self { Self { enabled_cipher_suites } } /// Returns all available cipher suites. pub fn all_supported_cipher_suites() -> Vec { 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, Kdf, AeadWrapper>; fn supported_cipher_suites(&self) -> Vec { self.enabled_cipher_suites.clone() } fn cipher_suite_provider( &self, cipher_suite: CipherSuite, ) -> Option { 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 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 BoringsslCipherSuite 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 { 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 CipherSuiteProvider for BoringsslCipherSuite 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, Self::Error> { Ok(self.hash.hash(data)) } async fn mac(&self, key: &[u8], data: &[u8]) -> Result, 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>, 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>, 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, 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>, 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, 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 { 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 { 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, 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 { Ok(self.eddsa.signature_key_derive_public(secret_key)?) } async fn sign( &self, secret_key: &SignatureSecretKey, data: &[u8], ) -> Result, 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 { 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()); } } }