use std::sync::RwLock;
use aes_gcm::{
aead::{Aead, Payload},
Aes128Gcm, Aes256Gcm, KeyInit,
};
use chacha20poly1305::ChaCha20Poly1305;
use ed25519_dalek::Signer;
use hkdf::Hkdf;
use hpke::Hpke;
use hpke_rs_crypto::types as hpke_types;
use hpke_rs_rust_crypto::HpkeRustCrypto;
#[cfg(feature = "targeted-messages-draft")]
use openmls_traits::crypto::HpkeSealPskResolvedAadError;
use openmls_traits::{
crypto::OpenMlsCrypto,
random::OpenMlsRand,
types::{
self, AeadType, Ciphersuite, CryptoError, ExporterSecret, HashType, HpkeAeadType,
HpkeCiphertext, HpkeConfig, HpkeKdfType, HpkeKemType, HpkeKeyPair, SignatureScheme,
},
};
use p256::{
ecdsa::{signature::Verifier, Signature, SigningKey, VerifyingKey},
EncodedPoint,
};
use rand_core::{RngCore as _, SeedableRng as _};
use sha2::{Digest, Sha256, Sha384, Sha512};
use tls_codec::SecretVLBytes;
use crate::hmac;
#[derive(Debug)]
pub struct RustCrypto {
rng: RwLock<rand_chacha::ChaCha20Rng>,
}
#[cfg(feature = "test-utils")]
impl Clone for RustCrypto {
fn clone(&self) -> Self {
Self::default()
}
}
impl Default for RustCrypto {
fn default() -> Self {
Self {
rng: RwLock::new(rand_chacha::ChaCha20Rng::from_entropy()),
}
}
}
#[inline(always)]
fn kem_mode(kem: HpkeKemType) -> hpke_types::KemAlgorithm {
match kem {
HpkeKemType::DhKemP256 => hpke_types::KemAlgorithm::DhKemP256,
HpkeKemType::DhKemP384 => hpke_types::KemAlgorithm::DhKemP384,
HpkeKemType::DhKemP521 => hpke_types::KemAlgorithm::DhKemP521,
HpkeKemType::DhKem25519 => hpke_types::KemAlgorithm::DhKem25519,
HpkeKemType::DhKem448 => hpke_types::KemAlgorithm::DhKem448,
#[cfg(feature = "draft-ietf-mls-pq-ciphersuites")]
HpkeKemType::XWingKemDraft6 => hpke_types::KemAlgorithm::XWingDraft06,
#[cfg(feature = "draft-ietf-mls-pq-ciphersuites")]
HpkeKemType::MlKem768 => hpke_types::KemAlgorithm::MlKem768,
#[cfg(feature = "draft-ietf-mls-pq-ciphersuites")]
HpkeKemType::MlKem1024 => hpke_types::KemAlgorithm::MlKem1024,
}
}
#[inline(always)]
fn kdf_mode(kdf: HpkeKdfType) -> hpke_types::KdfAlgorithm {
match kdf {
HpkeKdfType::HkdfSha256 => hpke_types::KdfAlgorithm::HkdfSha256,
HpkeKdfType::HkdfSha384 => hpke_types::KdfAlgorithm::HkdfSha384,
HpkeKdfType::HkdfSha512 => hpke_types::KdfAlgorithm::HkdfSha512,
}
}
#[inline(always)]
fn aead_mode(aead: HpkeAeadType) -> hpke_types::AeadAlgorithm {
match aead {
HpkeAeadType::AesGcm128 => hpke_types::AeadAlgorithm::Aes128Gcm,
HpkeAeadType::AesGcm256 => hpke_types::AeadAlgorithm::Aes256Gcm,
HpkeAeadType::ChaCha20Poly1305 => hpke_types::AeadAlgorithm::ChaCha20Poly1305,
HpkeAeadType::Export => hpke_types::AeadAlgorithm::HpkeExport,
}
}
impl OpenMlsCrypto for RustCrypto {
fn supports(&self, ciphersuite: Ciphersuite) -> Result<(), CryptoError> {
match ciphersuite {
Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519
| Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519
| Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256 => Ok(()),
#[cfg(feature = "draft-ietf-mls-pq-ciphersuites")]
Ciphersuite::MLS_192_MLKEM1024_AES256GCM_SHA384_P384
| Ciphersuite::MLS_128_MLKEM768X25519_AES256GCM_SHA384_Ed25519
| Ciphersuite::MLS_128_MLKEM768X25519_AES128GCM_SHA256_Ed25519
| Ciphersuite::MLS_128_MLKEM768_AES256GCM_SHA384_P256
| Ciphersuite::MLS_128_MLKEM768X25519_CHACHA20POLY1305_SHA384_MLDSA44
| Ciphersuite::MLS_192_MLKEM768_AES256GCM_SHA384_MLDSA65
| Ciphersuite::MLS_256_MLKEM1024_AES256GCM_SHA384_MLDSA87
| Ciphersuite::MLS_256_MLKEM1024_AES256GCM_SHA512_MLDSA87
| Ciphersuite::MLS_128_MLKEM768_AES256GCM_SHA384_Ed25519 => Ok(()),
_ => Err(CryptoError::UnsupportedCiphersuite),
}
}
fn supported_ciphersuites(&self) -> Vec<Ciphersuite> {
vec![
Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519,
Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519,
Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256,
#[cfg(feature = "draft-ietf-mls-pq-ciphersuites")]
Ciphersuite::MLS_192_MLKEM1024_AES256GCM_SHA384_P384,
#[cfg(feature = "draft-ietf-mls-pq-ciphersuites")]
Ciphersuite::MLS_256_MLKEM1024_AES256GCM_SHA512_MLDSA87,
#[cfg(feature = "draft-ietf-mls-pq-ciphersuites")]
Ciphersuite::MLS_128_MLKEM768X25519_AES256GCM_SHA384_Ed25519,
#[cfg(feature = "draft-ietf-mls-pq-ciphersuites")]
Ciphersuite::MLS_128_MLKEM768X25519_AES128GCM_SHA256_Ed25519,
#[cfg(feature = "draft-ietf-mls-pq-ciphersuites")]
Ciphersuite::MLS_128_MLKEM768_AES256GCM_SHA384_P256,
#[cfg(feature = "draft-ietf-mls-pq-ciphersuites")]
Ciphersuite::MLS_128_MLKEM768X25519_CHACHA20POLY1305_SHA384_MLDSA44,
#[cfg(feature = "draft-ietf-mls-pq-ciphersuites")]
Ciphersuite::MLS_192_MLKEM768_AES256GCM_SHA384_MLDSA65,
#[cfg(feature = "draft-ietf-mls-pq-ciphersuites")]
Ciphersuite::MLS_256_MLKEM1024_AES256GCM_SHA384_MLDSA87,
#[cfg(feature = "draft-ietf-mls-pq-ciphersuites")]
Ciphersuite::MLS_128_MLKEM768_AES256GCM_SHA384_Ed25519,
]
}
fn hkdf_extract(
&self,
hash_type: openmls_traits::types::HashType,
salt: &[u8],
ikm: &[u8],
) -> Result<SecretVLBytes, openmls_traits::types::CryptoError> {
#[allow(deprecated)]
match hash_type {
HashType::Sha2_256 => Ok(Hkdf::<Sha256>::extract(Some(salt), ikm).0.as_slice().into()),
HashType::Sha2_384 => Ok(Hkdf::<Sha384>::extract(Some(salt), ikm).0.as_slice().into()),
HashType::Sha2_512 => Ok(Hkdf::<Sha512>::extract(Some(salt), ikm).0.as_slice().into()),
}
}
fn hmac(
&self,
hash_type: HashType,
key: &[u8],
message: &[u8],
) -> Result<SecretVLBytes, CryptoError> {
hmac::hmac(hash_type, key, message)
}
fn hkdf_expand(
&self,
hash_type: openmls_traits::types::HashType,
prk: &[u8],
info: &[u8],
okm_len: usize,
) -> Result<SecretVLBytes, openmls_traits::types::CryptoError> {
match hash_type {
HashType::Sha2_256 => {
let hkdf = Hkdf::<Sha256>::from_prk(prk)
.map_err(|_| CryptoError::HkdfOutputLengthInvalid)?;
let mut okm = vec![0u8; okm_len];
hkdf.expand(info, &mut okm)
.map_err(|_| CryptoError::HkdfOutputLengthInvalid)?;
Ok(okm.into())
}
HashType::Sha2_512 => {
let hkdf = Hkdf::<Sha512>::from_prk(prk)
.map_err(|_| CryptoError::HkdfOutputLengthInvalid)?;
let mut okm = vec![0u8; okm_len];
hkdf.expand(info, &mut okm)
.map_err(|_| CryptoError::HkdfOutputLengthInvalid)?;
Ok(okm.into())
}
HashType::Sha2_384 => {
let hkdf = Hkdf::<Sha384>::from_prk(prk)
.map_err(|_| CryptoError::HkdfOutputLengthInvalid)?;
let mut okm = vec![0u8; okm_len];
hkdf.expand(info, &mut okm)
.map_err(|_| CryptoError::HkdfOutputLengthInvalid)?;
Ok(okm.into())
}
}
}
fn hash(
&self,
hash_type: openmls_traits::types::HashType,
data: &[u8],
) -> Result<Vec<u8>, openmls_traits::types::CryptoError> {
#[allow(deprecated)]
match hash_type {
HashType::Sha2_256 => Ok(Sha256::digest(data).as_slice().into()),
HashType::Sha2_384 => Ok(Sha384::digest(data).as_slice().into()),
HashType::Sha2_512 => Ok(Sha512::digest(data).as_slice().into()),
}
}
fn aead_encrypt(
&self,
alg: openmls_traits::types::AeadType,
key: &[u8],
data: &[u8],
nonce: &[u8],
aad: &[u8],
) -> Result<Vec<u8>, openmls_traits::types::CryptoError> {
match alg {
AeadType::Aes128Gcm => {
let aes =
Aes128Gcm::new_from_slice(key).map_err(|_| CryptoError::CryptoLibraryError)?;
aes.encrypt(nonce.into(), Payload { msg: data, aad })
.map(|r| r.as_slice().into())
.map_err(|_| CryptoError::CryptoLibraryError)
}
AeadType::Aes256Gcm => {
let aes =
Aes256Gcm::new_from_slice(key).map_err(|_| CryptoError::CryptoLibraryError)?;
aes.encrypt(nonce.into(), Payload { msg: data, aad })
.map(|r| r.as_slice().into())
.map_err(|_| CryptoError::CryptoLibraryError)
}
AeadType::ChaCha20Poly1305 => {
let chacha_poly = ChaCha20Poly1305::new_from_slice(key)
.map_err(|_| CryptoError::CryptoLibraryError)?;
chacha_poly
.encrypt(nonce.into(), Payload { msg: data, aad })
.map(|r| r.as_slice().into())
.map_err(|_| CryptoError::CryptoLibraryError)
}
}
}
fn aead_decrypt(
&self,
alg: openmls_traits::types::AeadType,
key: &[u8],
ct_tag: &[u8],
nonce: &[u8],
aad: &[u8],
) -> Result<Vec<u8>, openmls_traits::types::CryptoError> {
match alg {
AeadType::Aes128Gcm => {
let aes =
Aes128Gcm::new_from_slice(key).map_err(|_| CryptoError::CryptoLibraryError)?;
aes.decrypt(nonce.into(), Payload { msg: ct_tag, aad })
.map(|r| r.as_slice().into())
.map_err(|_| CryptoError::AeadDecryptionError)
}
AeadType::Aes256Gcm => {
let aes =
Aes256Gcm::new_from_slice(key).map_err(|_| CryptoError::CryptoLibraryError)?;
aes.decrypt(nonce.into(), Payload { msg: ct_tag, aad })
.map(|r| r.as_slice().into())
.map_err(|_| CryptoError::AeadDecryptionError)
}
AeadType::ChaCha20Poly1305 => {
let chacha_poly = ChaCha20Poly1305::new_from_slice(key)
.map_err(|_| CryptoError::CryptoLibraryError)?;
chacha_poly
.decrypt(nonce.into(), Payload { msg: ct_tag, aad })
.map(|r| r.as_slice().into())
.map_err(|_| CryptoError::AeadDecryptionError)
}
}
}
fn signature_key_gen(
&self,
alg: openmls_traits::types::SignatureScheme,
) -> Result<(Vec<u8>, Vec<u8>), openmls_traits::types::CryptoError> {
match alg {
SignatureScheme::ECDSA_SECP256R1_SHA256 => {
let mut rng = self
.rng
.write()
.map_err(|_| CryptoError::InsufficientRandomness)?;
let k = SigningKey::random(&mut *rng);
let pk = k.verifying_key().to_encoded_point(false).as_bytes().into();
#[allow(deprecated)]
Ok((k.to_bytes().as_slice().into(), pk))
}
SignatureScheme::ED25519 => {
let mut rng = self
.rng
.write()
.map_err(|_| CryptoError::InsufficientRandomness)?;
let sk = ed25519_dalek::SigningKey::generate(&mut *rng);
let pk = sk.verifying_key().to_bytes().into();
Ok((sk.to_bytes().into(), pk))
}
SignatureScheme::ECDSA_SECP384R1_SHA384 => {
let mut rng = self
.rng
.write()
.map_err(|_| CryptoError::InsufficientRandomness)?;
let k = p384::ecdsa::SigningKey::random(&mut *rng);
let pk = k.verifying_key().to_encoded_point(false).as_bytes().into();
Ok((k.to_bytes().as_slice().into(), pk))
}
#[cfg(feature = "draft-ietf-mls-pq-ciphersuites")]
SignatureScheme::MLDSA44 => {
use crate::rand_shim::RandCore0_10;
use ml_dsa::{Generate, Keypair};
let sk = {
let mut rng = self
.rng
.write()
.map_err(|_| CryptoError::InsufficientRandomness)?;
ml_dsa::SigningKey::<ml_dsa::MlDsa44>::generate_from_rng(&mut RandCore0_10(
&mut *rng,
))
};
let pk = sk.verifying_key().encode().to_vec();
let sk = sk.to_seed().to_vec();
Ok((sk, pk))
}
#[cfg(feature = "draft-ietf-mls-pq-ciphersuites")]
SignatureScheme::MLDSA65 => {
use crate::rand_shim::RandCore0_10;
use ml_dsa::{Generate, Keypair};
let sk = {
let mut rng = self
.rng
.write()
.map_err(|_| CryptoError::InsufficientRandomness)?;
ml_dsa::SigningKey::<ml_dsa::MlDsa65>::generate_from_rng(&mut RandCore0_10(
&mut *rng,
))
};
let pk = sk.verifying_key().encode().to_vec();
let sk = sk.to_seed().to_vec();
Ok((sk, pk))
}
#[cfg(feature = "draft-ietf-mls-pq-ciphersuites")]
SignatureScheme::MLDSA87 => {
use crate::rand_shim::RandCore0_10;
use ml_dsa::{Generate, Keypair};
let sk = {
let mut rng = self
.rng
.write()
.map_err(|_| CryptoError::InsufficientRandomness)?;
ml_dsa::SigningKey::<ml_dsa::MlDsa87>::generate_from_rng(&mut RandCore0_10(
&mut *rng,
))
};
let pk = sk.verifying_key().encode().to_vec();
let sk = sk.to_seed().to_vec();
Ok((sk, pk))
}
_ => Err(CryptoError::UnsupportedSignatureScheme),
}
}
fn verify_signature(
&self,
alg: openmls_traits::types::SignatureScheme,
data: &[u8],
pk: &[u8],
signature: &[u8],
) -> Result<(), openmls_traits::types::CryptoError> {
match alg {
SignatureScheme::ECDSA_SECP256R1_SHA256 => {
let k = VerifyingKey::from_encoded_point(
&EncodedPoint::from_bytes(pk).map_err(|_| CryptoError::CryptoLibraryError)?,
)
.map_err(|_| CryptoError::CryptoLibraryError)?;
k.verify(
data,
&Signature::from_der(signature).map_err(|_| CryptoError::InvalidSignature)?,
)
.map_err(|_| CryptoError::InvalidSignature)
}
SignatureScheme::ED25519 => {
let k = ed25519_dalek::VerifyingKey::try_from(pk)
.map_err(|_| CryptoError::CryptoLibraryError)?;
if signature.len() != ed25519_dalek::SIGNATURE_LENGTH {
return Err(CryptoError::CryptoLibraryError);
}
let mut sig = [0u8; ed25519_dalek::SIGNATURE_LENGTH];
sig.clone_from_slice(signature);
k.verify_strict(data, &ed25519_dalek::Signature::from(sig))
.map_err(|_| CryptoError::InvalidSignature)
}
SignatureScheme::ECDSA_SECP384R1_SHA384 => {
let k = p384::ecdsa::VerifyingKey::from_encoded_point(
&p384::EncodedPoint::from_bytes(pk)
.map_err(|_| CryptoError::CryptoLibraryError)?,
)
.map_err(|_| CryptoError::CryptoLibraryError)?;
k.verify(
data,
&p384::ecdsa::Signature::from_der(signature)
.map_err(|_| CryptoError::InvalidSignature)?,
)
.map_err(|_| CryptoError::InvalidSignature)
}
#[cfg(feature = "draft-ietf-mls-pq-ciphersuites")]
SignatureScheme::MLDSA44 => {
use ml_dsa::Verifier;
let encoded_key: &ml_dsa::EncodedVerifyingKey<ml_dsa::MlDsa44> =
pk.try_into().map_err(|_| CryptoError::InvalidLength)?;
let encoded_signature: &ml_dsa::EncodedSignature<ml_dsa::MlDsa44> = signature
.try_into()
.map_err(|_| CryptoError::InvalidLength)?;
let key = ml_dsa::VerifyingKey::<ml_dsa::MlDsa44>::decode(encoded_key);
let signature = ml_dsa::Signature::<ml_dsa::MlDsa44>::decode(encoded_signature)
.ok_or(CryptoError::InvalidSignature)?;
key.verify(data, &signature)
.map_err(|_| CryptoError::InvalidSignature)
}
#[cfg(feature = "draft-ietf-mls-pq-ciphersuites")]
SignatureScheme::MLDSA65 => {
use ml_dsa::Verifier;
let encoded_key: &ml_dsa::EncodedVerifyingKey<ml_dsa::MlDsa65> =
pk.try_into().map_err(|_| CryptoError::InvalidLength)?;
let encoded_signature: &ml_dsa::EncodedSignature<ml_dsa::MlDsa65> = signature
.try_into()
.map_err(|_| CryptoError::InvalidLength)?;
let key = ml_dsa::VerifyingKey::<ml_dsa::MlDsa65>::decode(encoded_key);
let signature = ml_dsa::Signature::<ml_dsa::MlDsa65>::decode(encoded_signature)
.ok_or(CryptoError::InvalidSignature)?;
key.verify(data, &signature)
.map_err(|_| CryptoError::InvalidSignature)
}
#[cfg(feature = "draft-ietf-mls-pq-ciphersuites")]
SignatureScheme::MLDSA87 => {
use ml_dsa::Verifier;
let encoded_key: &ml_dsa::EncodedVerifyingKey<ml_dsa::MlDsa87> =
pk.try_into().map_err(|_| CryptoError::InvalidLength)?;
let encoded_signature: &ml_dsa::EncodedSignature<ml_dsa::MlDsa87> = signature
.try_into()
.map_err(|_| CryptoError::InvalidLength)?;
let key = ml_dsa::VerifyingKey::<ml_dsa::MlDsa87>::decode(encoded_key);
let signature = ml_dsa::Signature::<ml_dsa::MlDsa87>::decode(encoded_signature)
.ok_or(CryptoError::InvalidSignature)?;
key.verify(data, &signature)
.map_err(|_| CryptoError::InvalidSignature)
}
_ => Err(CryptoError::UnsupportedSignatureScheme),
}
}
fn sign(
&self,
alg: openmls_traits::types::SignatureScheme,
data: &[u8],
key: &[u8],
) -> Result<Vec<u8>, openmls_traits::types::CryptoError> {
match alg {
SignatureScheme::ECDSA_SECP256R1_SHA256 => {
let k = SigningKey::from_bytes(key.into())
.map_err(|_| CryptoError::CryptoLibraryError)?;
let signature: Signature = k.sign(data);
Ok(signature.to_der().to_bytes().into())
}
SignatureScheme::ECDSA_SECP384R1_SHA384 => {
let k = p384::ecdsa::SigningKey::from_bytes(key.into())
.map_err(|_| CryptoError::CryptoLibraryError)?;
let signature: p384::ecdsa::Signature = k.sign(data);
Ok(signature.to_der().to_bytes().into())
}
SignatureScheme::ED25519 => {
let k = ed25519_dalek::SigningKey::try_from(key)
.map_err(|_| CryptoError::CryptoLibraryError)?;
let signature = k.sign(data);
Ok(signature.to_bytes().into())
}
#[cfg(feature = "draft-ietf-mls-pq-ciphersuites")]
SignatureScheme::MLDSA44 => {
use ml_dsa::Signer;
let seed: &ml_dsa::Seed = key.try_into().map_err(|_| CryptoError::InvalidLength)?;
let k = ml_dsa::SigningKey::<ml_dsa::MlDsa44>::from_seed(seed);
let signature = k.sign(data);
Ok(signature.encode().to_vec())
}
#[cfg(feature = "draft-ietf-mls-pq-ciphersuites")]
SignatureScheme::MLDSA65 => {
use ml_dsa::Signer;
let seed: &ml_dsa::Seed = key.try_into().map_err(|_| CryptoError::InvalidLength)?;
let k = ml_dsa::SigningKey::<ml_dsa::MlDsa65>::from_seed(seed);
let signature = k.sign(data);
Ok(signature.encode().to_vec())
}
#[cfg(feature = "draft-ietf-mls-pq-ciphersuites")]
SignatureScheme::MLDSA87 => {
use ml_dsa::Signer;
let seed: &ml_dsa::Seed = key.try_into().map_err(|_| CryptoError::InvalidLength)?;
let k = ml_dsa::SigningKey::<ml_dsa::MlDsa87>::from_seed(seed);
let signature = k.sign(data);
Ok(signature.encode().to_vec())
}
_ => Err(CryptoError::UnsupportedSignatureScheme),
}
}
fn hpke_seal(
&self,
config: HpkeConfig,
pk_r: &[u8],
info: &[u8],
aad: &[u8],
ptxt: &[u8],
) -> Result<types::HpkeCiphertext, CryptoError> {
let (kem_output, ciphertext) = hpke_from_config(config)
.seal(&pk_r.into(), info, aad, ptxt, None, None, None)
.map_err(|e| match e {
hpke::HpkeError::InvalidInput => CryptoError::InvalidLength,
_ => CryptoError::CryptoLibraryError,
})?;
Ok(HpkeCiphertext {
kem_output: kem_output.into(),
ciphertext: ciphertext.into(),
})
}
fn hpke_open(
&self,
config: HpkeConfig,
input: &types::HpkeCiphertext,
sk_r: &[u8],
info: &[u8],
aad: &[u8],
) -> Result<Vec<u8>, CryptoError> {
hpke_from_config(config)
.open(
input.kem_output.as_slice(),
&sk_r.into(),
info,
aad,
input.ciphertext.as_slice(),
None,
None,
None,
)
.map_err(|_| CryptoError::HpkeDecryptionError)
}
fn hpke_setup_sender_and_export(
&self,
config: HpkeConfig,
pk_r: &[u8],
info: &[u8],
exporter_context: &[u8],
exporter_length: usize,
) -> Result<(Vec<u8>, ExporterSecret), CryptoError> {
let (kem_output, context) = hpke_from_config(config)
.setup_sender(&pk_r.into(), info, None, None, None)
.map_err(|_| CryptoError::SenderSetupError)?;
let exported_secret = context
.export(exporter_context, exporter_length)
.map_err(|_| CryptoError::ExporterError)?;
Ok((kem_output, exported_secret.into()))
}
fn hpke_setup_receiver_and_export(
&self,
config: HpkeConfig,
enc: &[u8],
sk_r: &[u8],
info: &[u8],
exporter_context: &[u8],
exporter_length: usize,
) -> Result<ExporterSecret, CryptoError> {
let context = hpke_from_config(config)
.setup_receiver(enc, &sk_r.into(), info, None, None, None)
.map_err(|_| CryptoError::ReceiverSetupError)?;
let exported_secret = context
.export(exporter_context, exporter_length)
.map_err(|_| CryptoError::ExporterError)?;
Ok(exported_secret.into())
}
fn derive_hpke_keypair(
&self,
config: HpkeConfig,
ikm: &[u8],
) -> Result<types::HpkeKeyPair, CryptoError> {
let kp = hpke_from_config(config)
.derive_key_pair(ikm)
.map_err(|e| match e {
hpke::HpkeError::InvalidInput => CryptoError::InvalidLength,
_ => CryptoError::CryptoLibraryError,
})?
.into_keys();
Ok(HpkeKeyPair {
private: kp.0.as_slice().into(),
public: kp.1.as_slice().into(),
})
}
#[cfg(feature = "targeted-messages-draft")]
fn hpke_open_psk(
&self,
config: HpkeConfig,
input: &types::HpkeCiphertext,
sk_r: &[u8],
info: &[u8],
aad: &[u8],
psk: &[u8],
psk_id: &[u8],
) -> Result<Vec<u8>, CryptoError> {
hpke_psk_from_config(config)
.open(
input.kem_output.as_slice(),
&sk_r.into(),
info,
aad,
input.ciphertext.as_slice(),
Some(psk),
Some(psk_id),
None,
)
.map_err(|_| CryptoError::HpkeDecryptionError)
}
#[cfg(feature = "targeted-messages-draft")]
fn hpke_seal_psk_resolved_aad<F, E>(
&self,
config: HpkeConfig,
pk_r: &[u8],
info: &[u8],
ptxt: &[u8],
psk: &[u8],
psk_id: &[u8],
aad_builder: F,
) -> Result<HpkeCiphertext, HpkeSealPskResolvedAadError<E>>
where
F: FnOnce(&[u8]) -> Result<Vec<u8>, E>,
{
let mut hpke = hpke_psk_from_config(config);
let (kem_output, mut context) = hpke
.setup_sender(&pk_r.into(), info, Some(psk), Some(psk_id), None)
.map_err(|_| HpkeSealPskResolvedAadError::CryptoError(CryptoError::SenderSetupError))?;
let aad = aad_builder(kem_output.as_slice())
.map_err(HpkeSealPskResolvedAadError::AadBuildError)?;
let ciphertext = context.seal(&aad, ptxt).map_err(|e| match e {
hpke::HpkeError::InvalidInput => {
HpkeSealPskResolvedAadError::CryptoError(CryptoError::InvalidLength)
}
hpke::HpkeError::InsufficientRandomness => {
HpkeSealPskResolvedAadError::CryptoError(CryptoError::InsufficientRandomness)
}
_ => HpkeSealPskResolvedAadError::CryptoError(CryptoError::HpkeEncryptionError),
})?;
Ok(HpkeCiphertext {
kem_output: kem_output.into(),
ciphertext: ciphertext.into(),
})
}
#[cfg(feature = "virtual-clients-draft")]
fn ff1_aes128_encrypt(&self, key: &[u8; 16], plaintext: u32) -> Result<u32, CryptoError> {
crate::ff1::encrypt(key, plaintext)
}
#[cfg(feature = "virtual-clients-draft")]
fn ff1_aes128_decrypt(&self, key: &[u8; 16], ciphertext: u32) -> Result<u32, CryptoError> {
crate::ff1::decrypt(key, ciphertext)
}
}
fn hpke_from_config(config: HpkeConfig) -> Hpke<HpkeRustCrypto> {
Hpke::<HpkeRustCrypto>::new(
hpke::Mode::Base,
kem_mode(config.0),
kdf_mode(config.1),
aead_mode(config.2),
)
}
#[cfg(feature = "targeted-messages-draft")]
fn hpke_psk_from_config(config: HpkeConfig) -> Hpke<HpkeRustCrypto> {
Hpke::<HpkeRustCrypto>::new(
hpke::Mode::Psk,
kem_mode(config.0),
kdf_mode(config.1),
aead_mode(config.2),
)
}
impl OpenMlsRand for RustCrypto {
type Error = RandError;
fn random_array<const N: usize>(&self) -> Result<[u8; N], Self::Error> {
let mut rng = self.rng.write().map_err(|_| Self::Error::LockPoisoned)?;
let mut out = [0u8; N];
rng.try_fill_bytes(&mut out)
.map_err(|_| Self::Error::NotEnoughRandomness)?;
Ok(out)
}
fn random_vec(&self, len: usize) -> Result<Vec<u8>, Self::Error> {
let mut rng = self.rng.write().map_err(|_| Self::Error::LockPoisoned)?;
let mut out = vec![0u8; len];
rng.try_fill_bytes(&mut out)
.map_err(|_| Self::Error::NotEnoughRandomness)?;
Ok(out)
}
}
#[derive(thiserror::Error, Debug, Copy, Clone, PartialEq, Eq)]
pub enum RandError {
#[error("Rng lock is poisoned.")]
LockPoisoned,
#[error("Unable to collect enough randomness.")]
NotEnoughRandomness,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn supports_is_consistent_with_supported_ciphersuites() {
let crypto = RustCrypto::default();
for ciphersuite in crypto.supported_ciphersuites() {
assert!(
crypto.supports(ciphersuite).is_ok(),
"{ciphersuite:?} is advertised by supported_ciphersuites() but rejected by supports()"
);
}
}
}