use std::sync::RwLock;
use aes_gcm::{
aead::{Aead, Payload},
Aes128Gcm, Aes256Gcm, NewAead,
};
use chacha20poly1305::ChaCha20Poly1305;
use ed25519_dalek::Signer as DalekSigner;
use hkdf::Hkdf;
use hpke::Hpke;
use hpke_rs_crypto::types as hpke_types;
use hpke_rs_rust_crypto::HpkeRustCrypto;
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::{Signer, Verifier},
Signature, SigningKey, VerifyingKey,
},
EncodedPoint,
};
use rand::{RngCore, SeedableRng};
use sha2::{Digest, Sha256, Sha384, Sha512};
use tls_codec::SecretVLBytes;
#[derive(Debug)]
pub struct RustCrypto {
rng: RwLock<rand_chacha::ChaCha20Rng>,
}
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,
}
}
#[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(()),
_ => 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,
]
}
fn hkdf_extract(
&self,
hash_type: openmls_traits::types::HashType,
salt: &[u8],
ikm: &[u8],
) -> Result<SecretVLBytes, openmls_traits::types::CryptoError> {
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 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> {
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();
Ok((k.to_bytes().as_slice().into(), pk))
}
SignatureScheme::ED25519 => {
let k = ed25519_dalek::Keypair::generate(&mut rand_07::rngs::OsRng).to_bytes();
let pk = k[ed25519_dalek::SECRET_KEY_LENGTH..].to_vec();
let sk_pk = k.into();
Ok((sk_pk, 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::PublicKey::from_bytes(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)
}
_ => 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::ED25519 => {
let k = ed25519_dalek::Keypair::from_bytes(key)
.map_err(|_| CryptoError::CryptoLibraryError)?;
let signature = k.sign(data);
Ok(signature.to_bytes().into())
}
_ => Err(CryptoError::UnsupportedSignatureScheme),
}
}
fn hpke_seal(
&self,
config: HpkeConfig,
pk_r: &[u8],
info: &[u8],
aad: &[u8],
ptxt: &[u8],
) -> types::HpkeCiphertext {
let (kem_output, ciphertext) = hpke_from_config(config)
.seal(&pk_r.into(), info, aad, ptxt, None, None, None)
.unwrap();
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]) -> types::HpkeKeyPair {
let kp = hpke_from_config(config)
.derive_key_pair(ikm)
.unwrap()
.into_keys();
HpkeKeyPair {
private: kp.0.as_slice().into(),
public: kp.1.as_slice().into(),
}
}
}
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),
)
}
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,
}