Skip to main content

core_crypto/mls_provider/
crypto_provider.rs

1use std::sync::{Arc, LazyLock, RwLock, RwLockWriteGuard};
2
3use aes_gcm::{
4    Aes128Gcm, Aes256Gcm, KeyInit,
5    aead::{Aead, Nonce, Payload},
6};
7use chacha20poly1305::ChaCha20Poly1305;
8use elliptic_curve::{Generate as _, sec1};
9use hkdf::Hkdf;
10use openmls::prelude::HpkeCiphertext;
11use openmls_traits::{
12    crypto::OpenMlsCrypto,
13    random::OpenMlsRand,
14    types::{
15        self, AeadType, Ciphersuite, CryptoError, ExporterSecret, HashType, HpkeAeadType, HpkeConfig, HpkeKdfType,
16        HpkeKemType, SignatureScheme,
17    },
18};
19use rand::Rng as _;
20use rand_core::SeedableRng as _;
21use sha2::{Digest, Sha256, Sha384, Sha512};
22use signature::digest::typenum::Unsigned;
23use tls_codec::SecretVLBytes;
24
25use super::{EntropySeed, Error, RawEntropySeed};
26
27/// Singleton for `RustCrypto`
28/// Because of the reseed feature we have to use this
29pub(crate) static CRYPTO: LazyLock<Arc<RustCrypto>> = LazyLock::new(|| Arc::new(RustCrypto::default()));
30
31/// The type that implements
32/// - key generation
33/// - AEAD encryption & decryption
34/// - signing & signature verification
35/// - HPKE operations
36#[derive(Debug, Clone)]
37pub struct RustCrypto {
38    pub(crate) rng: Arc<RwLock<rand_chacha::ChaCha20Rng>>,
39}
40
41impl Default for RustCrypto {
42    fn default() -> Self {
43        let mut seed = RawEntropySeed::default();
44        getrandom::fill(&mut seed).expect("system RNG has to work");
45        Self::new_with_seed(EntropySeed::from_raw(seed))
46    }
47}
48
49impl RustCrypto {
50    pub(crate) fn new_with_seed(seed: EntropySeed) -> Self {
51        Self {
52            rng: Arc::new(rand_chacha::ChaCha20Rng::from_seed(seed.0).into()),
53        }
54    }
55
56    pub(crate) fn reseed(&self, seed: Option<EntropySeed>) -> Result<(), Error> {
57        let mut val = self.rng.write().map_err(|_| Error::RngLockPoison)?;
58        *val = rand_chacha::ChaCha20Rng::from_seed(seed.unwrap_or_default().0);
59        Ok(())
60    }
61
62    #[expect(clippy::too_many_arguments)]
63    pub(crate) fn hpke_seal_psk(
64        &self,
65        config: HpkeConfig,
66        pk_r: &[u8],
67        info: &[u8],
68        aad: &[u8],
69        psk: &[u8],
70        psk_id: &[u8],
71        ptxt: &[u8],
72    ) -> Result<HpkeCiphertext, CryptoError> {
73        validate_psk(psk, psk_id)?;
74        let mut rng = self.rng.write().map_err(|_| CryptoError::InsufficientRandomness)?;
75
76        match config {
77            HpkeConfig(HpkeKemType::DhKem25519, HpkeKdfType::HkdfSha256, HpkeAeadType::AesGcm128) => {
78                hpke_core::hpke_seal_psk::<hpke::aead::AesGcm128, hpke::kdf::HkdfSha256, hpke::kem::X25519HkdfSha256>(
79                    pk_r, info, aad, psk, psk_id, ptxt, &mut *rng,
80                )
81            }
82            HpkeConfig(HpkeKemType::DhKem25519, HpkeKdfType::HkdfSha256, HpkeAeadType::ChaCha20Poly1305) => {
83                hpke_core::hpke_seal_psk::<
84                    hpke::aead::ChaCha20Poly1305,
85                    hpke::kdf::HkdfSha256,
86                    hpke::kem::X25519HkdfSha256,
87                >(pk_r, info, aad, psk, psk_id, ptxt, &mut *rng)
88            }
89            HpkeConfig(HpkeKemType::DhKemP256, HpkeKdfType::HkdfSha256, HpkeAeadType::AesGcm128) => {
90                hpke_core::hpke_seal_psk::<hpke::aead::AesGcm128, hpke::kdf::HkdfSha256, hpke::kem::DhP256HkdfSha256>(
91                    pk_r, info, aad, psk, psk_id, ptxt, &mut *rng,
92                )
93            }
94            HpkeConfig(HpkeKemType::DhKemP384, HpkeKdfType::HkdfSha384, HpkeAeadType::AesGcm256) => {
95                hpke_core::hpke_seal_psk::<hpke::aead::AesGcm256, hpke::kdf::HkdfSha384, hpke::kem::DhP384HkdfSha384>(
96                    pk_r, info, aad, psk, psk_id, ptxt, &mut *rng,
97                )
98            }
99            HpkeConfig(HpkeKemType::DhKemP521, HpkeKdfType::HkdfSha512, HpkeAeadType::AesGcm256) => {
100                hpke_core::hpke_seal_psk::<hpke::aead::AesGcm256, hpke::kdf::HkdfSha512, hpke::kem::DhP521HkdfSha512>(
101                    pk_r, info, aad, psk, psk_id, ptxt, &mut *rng,
102                )
103            }
104            _ => Err(CryptoError::UnsupportedKem),
105        }
106    }
107
108    #[expect(clippy::too_many_arguments)]
109    pub(crate) fn hpke_open_psk(
110        &self,
111        config: HpkeConfig,
112        input: &HpkeCiphertext,
113        sk_r: &[u8],
114        info: &[u8],
115        aad: &[u8],
116        psk: &[u8],
117        psk_id: &[u8],
118    ) -> Result<Vec<u8>, CryptoError> {
119        validate_psk(psk, psk_id)?;
120        match config {
121            HpkeConfig(HpkeKemType::DhKem25519, HpkeKdfType::HkdfSha256, HpkeAeadType::AesGcm128) => {
122                hpke_core::hpke_open_psk::<hpke::aead::AesGcm128, hpke::kdf::HkdfSha256, hpke::kem::X25519HkdfSha256>(
123                    sk_r,
124                    input.kem_output.as_slice(),
125                    info,
126                    aad,
127                    psk,
128                    psk_id,
129                    input.ciphertext.as_slice(),
130                )
131            }
132            HpkeConfig(HpkeKemType::DhKem25519, HpkeKdfType::HkdfSha256, HpkeAeadType::ChaCha20Poly1305) => {
133                hpke_core::hpke_open_psk::<
134                    hpke::aead::ChaCha20Poly1305,
135                    hpke::kdf::HkdfSha256,
136                    hpke::kem::X25519HkdfSha256,
137                >(
138                    sk_r,
139                    input.kem_output.as_slice(),
140                    info,
141                    aad,
142                    psk,
143                    psk_id,
144                    input.ciphertext.as_slice(),
145                )
146            }
147            HpkeConfig(HpkeKemType::DhKemP256, HpkeKdfType::HkdfSha256, HpkeAeadType::AesGcm128) => {
148                hpke_core::hpke_open_psk::<hpke::aead::AesGcm128, hpke::kdf::HkdfSha256, hpke::kem::DhP256HkdfSha256>(
149                    sk_r,
150                    input.kem_output.as_slice(),
151                    info,
152                    aad,
153                    psk,
154                    psk_id,
155                    input.ciphertext.as_slice(),
156                )
157            }
158            HpkeConfig(HpkeKemType::DhKemP384, HpkeKdfType::HkdfSha384, HpkeAeadType::AesGcm256) => {
159                hpke_core::hpke_open_psk::<hpke::aead::AesGcm256, hpke::kdf::HkdfSha384, hpke::kem::DhP384HkdfSha384>(
160                    sk_r,
161                    input.kem_output.as_slice(),
162                    info,
163                    aad,
164                    psk,
165                    psk_id,
166                    input.ciphertext.as_slice(),
167                )
168            }
169            HpkeConfig(HpkeKemType::DhKemP521, HpkeKdfType::HkdfSha512, HpkeAeadType::AesGcm256) => {
170                hpke_core::hpke_open_psk::<hpke::aead::AesGcm256, hpke::kdf::HkdfSha512, hpke::kem::DhP521HkdfSha512>(
171                    sk_r,
172                    input.kem_output.as_slice(),
173                    info,
174                    aad,
175                    psk,
176                    psk_id,
177                    input.ciphertext.as_slice(),
178                )
179            }
180            _ => Err(CryptoError::UnsupportedKem),
181        }
182    }
183}
184
185/// RFC 9180 section 5.1 requires that a PSK used in `mode_psk` or `mode_auth_psk` "MUST contain at
186/// least 32 bytes of entropy", and that the PSK and its id are either both present or both absent.
187///
188/// `hpke::OpModeS::Psk` enforces only the latter: `PskBundle::new(&[], &[])` is accepted and is
189/// documented as being "equivalent to `Base`".
190fn validate_psk(psk: &[u8], psk_id: &[u8]) -> Result<(), CryptoError> {
191    const MIN_PSK_LEN: usize = 32;
192
193    if psk.len() < MIN_PSK_LEN || psk_id.is_empty() {
194        return Err(CryptoError::InvalidLength);
195    }
196    Ok(())
197}
198
199impl OpenMlsCrypto for RustCrypto {
200    fn signature_public_key_len(&self, signature_scheme: SignatureScheme) -> usize {
201        // `signature_key_gen` emits ECDSA public keys as uncompressed SEC1 points -- a 0x04 tag
202        // byte followed by the two field elements -- so the serialized length is not the field
203        // size. Returning the field size here makes the `OpenMlsCrypto::validate_signature_key`
204        // default reject every key this provider produces.
205        match signature_scheme {
206            SignatureScheme::ECDSA_SECP256R1_SHA256 => sec1::Tag::Uncompressed
207                .message_len(<p256::NistP256 as p256::elliptic_curve::Curve>::FieldBytesSize::to_usize()),
208            SignatureScheme::ECDSA_SECP384R1_SHA384 => sec1::Tag::Uncompressed
209                .message_len(<p384::NistP384 as p384::elliptic_curve::Curve>::FieldBytesSize::to_usize()),
210            SignatureScheme::ECDSA_SECP521R1_SHA512 => sec1::Tag::Uncompressed
211                .message_len(<p521::NistP521 as p521::elliptic_curve::Curve>::FieldBytesSize::to_usize()),
212            SignatureScheme::ED25519 => ed25519_dalek::PUBLIC_KEY_LENGTH,
213            SignatureScheme::ED448 => 57,
214        }
215    }
216
217    fn supports(&self, cipher_suite: Ciphersuite) -> Result<(), CryptoError> {
218        match cipher_suite {
219            Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519
220            | Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519
221            | Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256
222            | Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384
223            | Ciphersuite::MLS_256_DHKEMP521_AES256GCM_SHA512_P521 => Ok(()),
224            _ => Err(CryptoError::UnsupportedCiphersuite),
225        }
226    }
227
228    fn supported_ciphersuites(&self) -> Vec<Ciphersuite> {
229        vec![
230            Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519,
231            Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519,
232            Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256,
233            Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384,
234            Ciphersuite::MLS_256_DHKEMP521_AES256GCM_SHA512_P521,
235        ]
236    }
237
238    fn hkdf_extract(&self, hash_type: HashType, salt: &[u8], ikm: &[u8]) -> Result<SecretVLBytes, CryptoError> {
239        match hash_type {
240            HashType::Sha2_256 => Ok(Hkdf::<Sha256>::extract(Some(salt), ikm).0.as_slice().into()),
241            HashType::Sha2_384 => Ok(Hkdf::<Sha384>::extract(Some(salt), ikm).0.as_slice().into()),
242            HashType::Sha2_512 => Ok(Hkdf::<Sha512>::extract(Some(salt), ikm).0.as_slice().into()),
243        }
244    }
245
246    fn hkdf_expand(
247        &self,
248        hash_type: HashType,
249        prk: &[u8],
250        info: &[u8],
251        okm_len: usize,
252    ) -> Result<SecretVLBytes, CryptoError> {
253        match hash_type {
254            HashType::Sha2_256 => {
255                let hkdf = Hkdf::<Sha256>::from_prk(prk).map_err(|_| CryptoError::HkdfOutputLengthInvalid)?;
256
257                let mut okm = vec![0u8; okm_len];
258                hkdf.expand(info, &mut okm)
259                    .map_err(|_| CryptoError::HkdfOutputLengthInvalid)?;
260
261                Ok(okm.into())
262            }
263            HashType::Sha2_384 => {
264                let hkdf = Hkdf::<Sha384>::from_prk(prk).map_err(|_| CryptoError::HkdfOutputLengthInvalid)?;
265
266                let mut okm = vec![0u8; okm_len];
267                hkdf.expand(info, &mut okm)
268                    .map_err(|_| CryptoError::HkdfOutputLengthInvalid)?;
269
270                Ok(okm.into())
271            }
272            HashType::Sha2_512 => {
273                let hkdf = Hkdf::<Sha512>::from_prk(prk).map_err(|_| CryptoError::HkdfOutputLengthInvalid)?;
274
275                let mut okm = vec![0u8; okm_len];
276                hkdf.expand(info, &mut okm)
277                    .map_err(|_| CryptoError::HkdfOutputLengthInvalid)?;
278
279                Ok(okm.into())
280            }
281        }
282    }
283
284    fn hash(&self, hash_type: HashType, data: &[u8]) -> Result<Vec<u8>, CryptoError> {
285        match hash_type {
286            HashType::Sha2_256 => Ok(Sha256::digest(data).as_slice().into()),
287            HashType::Sha2_384 => Ok(Sha384::digest(data).as_slice().into()),
288            HashType::Sha2_512 => Ok(Sha512::digest(data).as_slice().into()),
289        }
290    }
291
292    fn aead_encrypt(
293        &self,
294        alg: AeadType,
295        key: &[u8],
296        data: &[u8],
297        nonce: &[u8],
298        aad: &[u8],
299    ) -> Result<Vec<u8>, CryptoError> {
300        // All supported algorithms use the same nonce size of 96 bits, so
301        // picking any of them for the generic parameter of Nonce<A> is fine.
302        let nonce = Nonce::<Aes128Gcm>::try_from(nonce).map_err(|_| CryptoError::InvalidLength)?;
303
304        match alg {
305            AeadType::Aes128Gcm => {
306                let aes = Aes128Gcm::new_from_slice(key).map_err(|_| CryptoError::CryptoLibraryError)?;
307
308                aes.encrypt(&nonce, Payload { msg: data, aad })
309                    .map(|r| r.as_slice().into())
310                    .map_err(|_| CryptoError::CryptoLibraryError)
311            }
312            AeadType::Aes256Gcm => {
313                let aes = Aes256Gcm::new_from_slice(key).map_err(|_| CryptoError::CryptoLibraryError)?;
314
315                aes.encrypt(&nonce, Payload { msg: data, aad })
316                    .map(|r| r.as_slice().into())
317                    .map_err(|_| CryptoError::CryptoLibraryError)
318            }
319            AeadType::ChaCha20Poly1305 => {
320                let chacha_poly = ChaCha20Poly1305::new_from_slice(key).map_err(|_| CryptoError::CryptoLibraryError)?;
321
322                chacha_poly
323                    .encrypt(&nonce, Payload { msg: data, aad })
324                    .map(|r| r.as_slice().into())
325                    .map_err(|_| CryptoError::CryptoLibraryError)
326            }
327        }
328    }
329
330    fn aead_decrypt(
331        &self,
332        alg: AeadType,
333        key: &[u8],
334        ct_tag: &[u8],
335        nonce: &[u8],
336        aad: &[u8],
337    ) -> Result<Vec<u8>, CryptoError> {
338        // All supported algorithms use the same nonce size of 96 bits, so
339        // picking any of them for the generic parameter of Nonce<A> is fine.
340        let nonce = Nonce::<Aes128Gcm>::try_from(nonce).map_err(|_| CryptoError::InvalidLength)?;
341
342        match alg {
343            AeadType::Aes128Gcm => {
344                let aes = Aes128Gcm::new_from_slice(key).map_err(|_| CryptoError::CryptoLibraryError)?;
345                aes.decrypt(&nonce, Payload { msg: ct_tag, aad })
346                    .map(|r| r.as_slice().into())
347                    .map_err(|_| CryptoError::AeadDecryptionError)
348            }
349            AeadType::Aes256Gcm => {
350                let aes = Aes256Gcm::new_from_slice(key).map_err(|_| CryptoError::CryptoLibraryError)?;
351                aes.decrypt(&nonce, Payload { msg: ct_tag, aad })
352                    .map(|r| r.as_slice().into())
353                    .map_err(|_| CryptoError::AeadDecryptionError)
354            }
355            AeadType::ChaCha20Poly1305 => {
356                let chacha_poly = ChaCha20Poly1305::new_from_slice(key).map_err(|_| CryptoError::CryptoLibraryError)?;
357                chacha_poly
358                    .decrypt(&nonce, Payload { msg: ct_tag, aad })
359                    .map(|r| r.as_slice().into())
360                    .map_err(|_| CryptoError::AeadDecryptionError)
361            }
362        }
363    }
364
365    /// Generate a `(secret key, public key)` pair from a signature scheme.
366    fn signature_key_gen(&self, alg: SignatureScheme) -> Result<(Vec<u8>, Vec<u8>), CryptoError> {
367        let mut rng = self.rng.write().map_err(|_| CryptoError::InsufficientRandomness)?;
368
369        match alg {
370            SignatureScheme::ECDSA_SECP256R1_SHA256 => {
371                let sk = p256::ecdsa::SigningKey::generate_from_rng(&mut *rng);
372                let pk = sk.verifying_key().to_sec1_bytes().to_vec();
373                Ok((sk.to_bytes().to_vec(), pk))
374            }
375            SignatureScheme::ECDSA_SECP384R1_SHA384 => {
376                let sk = p384::ecdsa::SigningKey::generate_from_rng(&mut *rng);
377                let pk = sk.verifying_key().to_sec1_bytes().to_vec();
378                Ok((sk.to_bytes().to_vec(), pk))
379            }
380            SignatureScheme::ECDSA_SECP521R1_SHA512 => {
381                let sk = p521::ecdsa::SigningKey::generate_from_rng(&mut *rng);
382                let pk = p521::ecdsa::VerifyingKey::from(&sk)
383                    .to_sec1_point(false)
384                    .to_bytes()
385                    .into();
386                Ok((sk.to_bytes().to_vec(), pk))
387            }
388            SignatureScheme::ED25519 => {
389                let k = ed25519_dalek::SigningKey::generate(&mut *rng);
390                let pk = k.verifying_key();
391                Ok((k.to_bytes().into(), pk.to_bytes().into()))
392            }
393            _ => Err(CryptoError::UnsupportedSignatureScheme),
394        }
395    }
396
397    fn validate_signature_key(&self, alg: SignatureScheme, key: &[u8]) -> Result<(), CryptoError> {
398        match alg {
399            SignatureScheme::ED25519 => {
400                ed25519_dalek::VerifyingKey::try_from(key).map_err(|_| CryptoError::InvalidKey)?;
401            }
402            SignatureScheme::ECDSA_SECP256R1_SHA256 => {
403                p256::ecdsa::VerifyingKey::try_from(key).map_err(|_| CryptoError::InvalidKey)?;
404            }
405            SignatureScheme::ECDSA_SECP384R1_SHA384 => {
406                p384::ecdsa::VerifyingKey::try_from(key).map_err(|_| CryptoError::InvalidKey)?;
407            }
408            SignatureScheme::ECDSA_SECP521R1_SHA512 => {
409                p521::ecdsa::VerifyingKey::from_sec1_bytes(key).map_err(|_| CryptoError::InvalidKey)?;
410            }
411            SignatureScheme::ED448 => {
412                return Err(CryptoError::UnsupportedSignatureScheme);
413            }
414        }
415        Ok(())
416    }
417
418    fn verify_signature(
419        &self,
420        alg: SignatureScheme,
421        data: &[u8],
422        pk: &[u8],
423        signature: &[u8],
424    ) -> Result<(), CryptoError> {
425        use signature::Verifier as _;
426        match alg {
427            SignatureScheme::ECDSA_SECP256R1_SHA256 => {
428                let k = p256::ecdsa::VerifyingKey::from_sec1_bytes(pk).map_err(|_| CryptoError::CryptoLibraryError)?;
429
430                let signature =
431                    p256::ecdsa::DerSignature::from_bytes(signature).map_err(|_| CryptoError::InvalidSignature)?;
432
433                k.verify(data, &signature).map_err(|_| CryptoError::InvalidSignature)
434            }
435            SignatureScheme::ECDSA_SECP384R1_SHA384 => {
436                let k = p384::ecdsa::VerifyingKey::from_sec1_bytes(pk).map_err(|_| CryptoError::CryptoLibraryError)?;
437
438                let signature =
439                    p384::ecdsa::DerSignature::from_bytes(signature).map_err(|_| CryptoError::InvalidSignature)?;
440
441                k.verify(data, &signature).map_err(|_| CryptoError::InvalidSignature)
442            }
443            SignatureScheme::ECDSA_SECP521R1_SHA512 => {
444                let k = p521::ecdsa::VerifyingKey::from_sec1_bytes(pk).map_err(|_| CryptoError::CryptoLibraryError)?;
445
446                let signature =
447                    p521::ecdsa::Signature::from_der(signature).map_err(|_| CryptoError::InvalidSignature)?;
448
449                k.verify(data, &signature).map_err(|_| CryptoError::InvalidSignature)
450            }
451            SignatureScheme::ED25519 => {
452                let k = ed25519_dalek::VerifyingKey::try_from(pk).map_err(|_| CryptoError::CryptoLibraryError)?;
453
454                let sig = ed25519_dalek::Signature::from_slice(signature).map_err(|_| CryptoError::InvalidSignature)?;
455
456                k.verify_strict(data, &sig).map_err(|_| CryptoError::InvalidSignature)
457            }
458            _ => Err(CryptoError::UnsupportedSignatureScheme),
459        }
460    }
461
462    fn sign(&self, _alg: SignatureScheme, _data: &[u8], _key: &[u8]) -> Result<Vec<u8>, CryptoError> {
463        unimplemented!("This is never used by openmls, so we don't make the effort of implementing it.");
464    }
465
466    fn hpke_seal(
467        &self,
468        config: HpkeConfig,
469        pk_r: &[u8],
470        info: &[u8],
471        aad: &[u8],
472        ptxt: &[u8],
473    ) -> Result<types::HpkeCiphertext, CryptoError> {
474        let mut rng = self.rng.write().map_err(|_| CryptoError::InsufficientRandomness)?;
475
476        match config {
477            HpkeConfig(HpkeKemType::DhKem25519, HpkeKdfType::HkdfSha256, HpkeAeadType::AesGcm128) => {
478                hpke_core::hpke_seal::<hpke::aead::AesGcm128, hpke::kdf::HkdfSha256, hpke::kem::X25519HkdfSha256>(
479                    pk_r, info, aad, ptxt, &mut *rng,
480                )
481            }
482            HpkeConfig(HpkeKemType::DhKem25519, HpkeKdfType::HkdfSha256, HpkeAeadType::ChaCha20Poly1305) => {
483                hpke_core::hpke_seal::<hpke::aead::ChaCha20Poly1305, hpke::kdf::HkdfSha256, hpke::kem::X25519HkdfSha256>(
484                    pk_r, info, aad, ptxt, &mut *rng,
485                )
486            }
487            HpkeConfig(HpkeKemType::DhKemP256, HpkeKdfType::HkdfSha256, HpkeAeadType::AesGcm128) => {
488                hpke_core::hpke_seal::<hpke::aead::AesGcm128, hpke::kdf::HkdfSha256, hpke::kem::DhP256HkdfSha256>(
489                    pk_r, info, aad, ptxt, &mut *rng,
490                )
491            }
492            HpkeConfig(HpkeKemType::DhKemP384, HpkeKdfType::HkdfSha384, HpkeAeadType::AesGcm256) => {
493                hpke_core::hpke_seal::<hpke::aead::AesGcm256, hpke::kdf::HkdfSha384, hpke::kem::DhP384HkdfSha384>(
494                    pk_r, info, aad, ptxt, &mut *rng,
495                )
496            }
497            HpkeConfig(HpkeKemType::DhKemP521, HpkeKdfType::HkdfSha512, HpkeAeadType::AesGcm256) => {
498                hpke_core::hpke_seal::<hpke::aead::AesGcm256, hpke::kdf::HkdfSha512, hpke::kem::DhP521HkdfSha512>(
499                    pk_r, info, aad, ptxt, &mut *rng,
500                )
501            }
502            _ => Err(CryptoError::UnsupportedKem),
503        }
504    }
505
506    fn hpke_open(
507        &self,
508        config: HpkeConfig,
509        input: &types::HpkeCiphertext,
510        sk_r: &[u8],
511        info: &[u8],
512        aad: &[u8],
513    ) -> Result<Vec<u8>, CryptoError> {
514        let plaintext = match config {
515            HpkeConfig(HpkeKemType::DhKem25519, HpkeKdfType::HkdfSha256, HpkeAeadType::AesGcm128) => {
516                hpke_core::hpke_open::<hpke::aead::AesGcm128, hpke::kdf::HkdfSha256, hpke::kem::X25519HkdfSha256>(
517                    sk_r,
518                    input.kem_output.as_slice(),
519                    info,
520                    aad,
521                    input.ciphertext.as_slice(),
522                )?
523            }
524            HpkeConfig(HpkeKemType::DhKem25519, HpkeKdfType::HkdfSha256, HpkeAeadType::ChaCha20Poly1305) => {
525                hpke_core::hpke_open::<hpke::aead::ChaCha20Poly1305, hpke::kdf::HkdfSha256, hpke::kem::X25519HkdfSha256>(
526                    sk_r,
527                    input.kem_output.as_slice(),
528                    info,
529                    aad,
530                    input.ciphertext.as_slice(),
531                )?
532            }
533            HpkeConfig(HpkeKemType::DhKemP256, HpkeKdfType::HkdfSha256, HpkeAeadType::AesGcm128) => {
534                hpke_core::hpke_open::<hpke::aead::AesGcm128, hpke::kdf::HkdfSha256, hpke::kem::DhP256HkdfSha256>(
535                    sk_r,
536                    input.kem_output.as_slice(),
537                    info,
538                    aad,
539                    input.ciphertext.as_slice(),
540                )?
541            }
542            HpkeConfig(HpkeKemType::DhKemP384, HpkeKdfType::HkdfSha384, HpkeAeadType::AesGcm256) => {
543                hpke_core::hpke_open::<hpke::aead::AesGcm256, hpke::kdf::HkdfSha384, hpke::kem::DhP384HkdfSha384>(
544                    sk_r,
545                    input.kem_output.as_slice(),
546                    info,
547                    aad,
548                    input.ciphertext.as_slice(),
549                )?
550            }
551            HpkeConfig(HpkeKemType::DhKemP521, HpkeKdfType::HkdfSha512, HpkeAeadType::AesGcm256) => {
552                hpke_core::hpke_open::<hpke::aead::AesGcm256, hpke::kdf::HkdfSha512, hpke::kem::DhP521HkdfSha512>(
553                    sk_r,
554                    input.kem_output.as_slice(),
555                    info,
556                    aad,
557                    input.ciphertext.as_slice(),
558                )?
559            }
560            _ => return Err(CryptoError::UnsupportedKem),
561        };
562
563        Ok(plaintext)
564    }
565
566    fn hpke_setup_sender_and_export(
567        &self,
568        config: HpkeConfig,
569        pk_r: &[u8],
570        info: &[u8],
571        exporter_context: &[u8],
572        exporter_length: usize,
573    ) -> Result<(Vec<u8>, ExporterSecret), CryptoError> {
574        let mut rng = self.rng.write().map_err(|_| CryptoError::InsufficientRandomness)?;
575
576        let (kem_output, export) =
577            match config {
578                HpkeConfig(HpkeKemType::DhKem25519, HpkeKdfType::HkdfSha256, HpkeAeadType::AesGcm128) => {
579                    hpke_core::hpke_export_tx::<
580                        hpke::aead::AesGcm128,
581                        hpke::kdf::HkdfSha256,
582                        hpke::kem::X25519HkdfSha256,
583                    >(pk_r, info, exporter_context, exporter_length, &mut *rng)?
584                }
585                HpkeConfig(HpkeKemType::DhKem25519, HpkeKdfType::HkdfSha256, HpkeAeadType::ChaCha20Poly1305) => {
586                    hpke_core::hpke_export_tx::<
587                        hpke::aead::ChaCha20Poly1305,
588                        hpke::kdf::HkdfSha256,
589                        hpke::kem::X25519HkdfSha256,
590                    >(pk_r, info, exporter_context, exporter_length, &mut *rng)?
591                }
592                HpkeConfig(HpkeKemType::DhKemP256, HpkeKdfType::HkdfSha256, HpkeAeadType::AesGcm128) => {
593                    hpke_core::hpke_export_tx::<
594                        hpke::aead::AesGcm128,
595                        hpke::kdf::HkdfSha256,
596                        hpke::kem::DhP256HkdfSha256,
597                    >(pk_r, info, exporter_context, exporter_length, &mut *rng)?
598                }
599                HpkeConfig(HpkeKemType::DhKemP384, HpkeKdfType::HkdfSha384, HpkeAeadType::AesGcm256) => {
600                    hpke_core::hpke_export_tx::<
601                        hpke::aead::AesGcm256,
602                        hpke::kdf::HkdfSha384,
603                        hpke::kem::DhP384HkdfSha384,
604                    >(pk_r, info, exporter_context, exporter_length, &mut *rng)?
605                }
606                HpkeConfig(HpkeKemType::DhKemP521, HpkeKdfType::HkdfSha512, HpkeAeadType::AesGcm256) => {
607                    hpke_core::hpke_export_tx::<
608                        hpke::aead::AesGcm256,
609                        hpke::kdf::HkdfSha512,
610                        hpke::kem::DhP521HkdfSha512,
611                    >(pk_r, info, exporter_context, exporter_length, &mut *rng)?
612                }
613                _ => return Err(CryptoError::UnsupportedKem),
614            };
615
616        debug_assert_eq!(export.len(), exporter_length);
617
618        Ok((kem_output, export.into()))
619    }
620
621    fn hpke_setup_receiver_and_export(
622        &self,
623        config: HpkeConfig,
624        enc: &[u8],
625        sk_r: &[u8],
626        info: &[u8],
627        exporter_context: &[u8],
628        exporter_length: usize,
629    ) -> Result<ExporterSecret, CryptoError> {
630        let export =
631            match config {
632                HpkeConfig(HpkeKemType::DhKem25519, HpkeKdfType::HkdfSha256, HpkeAeadType::AesGcm128) => {
633                    hpke_core::hpke_export_rx::<
634                        hpke::aead::AesGcm128,
635                        hpke::kdf::HkdfSha256,
636                        hpke::kem::X25519HkdfSha256,
637                    >(enc, sk_r, info, exporter_context, exporter_length)?
638                }
639                HpkeConfig(HpkeKemType::DhKem25519, HpkeKdfType::HkdfSha256, HpkeAeadType::ChaCha20Poly1305) => {
640                    hpke_core::hpke_export_rx::<
641                        hpke::aead::ChaCha20Poly1305,
642                        hpke::kdf::HkdfSha256,
643                        hpke::kem::X25519HkdfSha256,
644                    >(enc, sk_r, info, exporter_context, exporter_length)?
645                }
646                HpkeConfig(HpkeKemType::DhKemP256, HpkeKdfType::HkdfSha256, HpkeAeadType::AesGcm128) => {
647                    hpke_core::hpke_export_rx::<
648                        hpke::aead::AesGcm128,
649                        hpke::kdf::HkdfSha256,
650                        hpke::kem::DhP256HkdfSha256,
651                    >(enc, sk_r, info, exporter_context, exporter_length)?
652                }
653                HpkeConfig(HpkeKemType::DhKemP384, HpkeKdfType::HkdfSha384, HpkeAeadType::AesGcm256) => {
654                    hpke_core::hpke_export_rx::<
655                        hpke::aead::AesGcm256,
656                        hpke::kdf::HkdfSha384,
657                        hpke::kem::DhP384HkdfSha384,
658                    >(enc, sk_r, info, exporter_context, exporter_length)?
659                }
660                HpkeConfig(HpkeKemType::DhKemP521, HpkeKdfType::HkdfSha512, HpkeAeadType::AesGcm256) => {
661                    hpke_core::hpke_export_rx::<
662                        hpke::aead::AesGcm256,
663                        hpke::kdf::HkdfSha512,
664                        hpke::kem::DhP521HkdfSha512,
665                    >(enc, sk_r, info, exporter_context, exporter_length)?
666                }
667                _ => return Err(CryptoError::UnsupportedKem),
668            };
669
670        debug_assert_eq!(export.len(), exporter_length);
671
672        Ok(export.into())
673    }
674
675    fn derive_hpke_keypair(&self, config: HpkeConfig, ikm: &[u8]) -> Result<types::HpkeKeyPair, CryptoError> {
676        match config.0 {
677            HpkeKemType::DhKemP256 => hpke_core::hpke_derive_keypair::<hpke::kem::DhP256HkdfSha256>(ikm),
678            HpkeKemType::DhKemP384 => hpke_core::hpke_derive_keypair::<hpke::kem::DhP384HkdfSha384>(ikm),
679            HpkeKemType::DhKemP521 => hpke_core::hpke_derive_keypair::<hpke::kem::DhP521HkdfSha512>(ikm),
680            HpkeKemType::DhKem25519 => hpke_core::hpke_derive_keypair::<hpke::kem::X25519HkdfSha256>(ikm),
681            _ => Err(CryptoError::UnsupportedKem),
682        }
683    }
684}
685
686mod hpke_core {
687    use hpke::PskBundle;
688    use openmls_traits::types::{CryptoError, HpkeCiphertext, HpkeKeyPair};
689
690    pub(crate) fn hpke_open<Aead: hpke::aead::Aead, Kdf: hpke::kdf::Kdf, Kem: hpke::Kem>(
691        private_key: &[u8],
692        kem_output: &[u8],
693        info: &[u8],
694        aad: &[u8],
695        ciphertext: &[u8],
696    ) -> Result<Vec<u8>, CryptoError> {
697        use hpke::Deserializable as _;
698        let encapped_key = Kem::EncappedKey::from_bytes(kem_output).map_err(|_| CryptoError::HpkeDecryptionError)?;
699        let key = Kem::PrivateKey::from_bytes(private_key).map_err(|_| CryptoError::HpkeDecryptionError)?;
700        let plaintext =
701            hpke::single_shot_open::<Aead, Kdf, Kem>(&hpke::OpModeR::Base, &key, &encapped_key, info, ciphertext, aad)
702                .map_err(|_| CryptoError::HpkeDecryptionError)?;
703
704        Ok(plaintext)
705    }
706
707    pub(crate) fn hpke_open_psk<Aead: hpke::aead::Aead, Kdf: hpke::kdf::Kdf, Kem: hpke::Kem>(
708        private_key: &[u8],
709        kem_output: &[u8],
710        info: &[u8],
711        aad: &[u8],
712        psk: &[u8],
713        psk_id: &[u8],
714        ciphertext: &[u8],
715    ) -> Result<Vec<u8>, CryptoError> {
716        use hpke::Deserializable as _;
717        let encapped_key = Kem::EncappedKey::from_bytes(kem_output).map_err(|_| CryptoError::HpkeDecryptionError)?;
718        let key = Kem::PrivateKey::from_bytes(private_key).map_err(|_| CryptoError::HpkeDecryptionError)?;
719        let psk_bundle = PskBundle::new(psk, psk_id).map_err(|_| CryptoError::HpkeDecryptionError)?;
720        let plaintext = hpke::single_shot_open::<Aead, Kdf, Kem>(
721            &hpke::OpModeR::Psk(psk_bundle),
722            &key,
723            &encapped_key,
724            info,
725            ciphertext,
726            aad,
727        )
728        .map_err(|_| CryptoError::HpkeDecryptionError)?;
729
730        Ok(plaintext)
731    }
732
733    pub(crate) fn hpke_seal<Aead: hpke::aead::Aead, Kdf: hpke::kdf::Kdf, Kem: hpke::Kem>(
734        public_key: &[u8],
735        info: &[u8],
736        aad: &[u8],
737        plaintext: &[u8],
738        csprng: &mut impl rand_core::CryptoRng,
739    ) -> Result<HpkeCiphertext, CryptoError> {
740        use hpke::{Deserializable as _, Serializable as _};
741        let key = Kem::PublicKey::from_bytes(public_key).map_err(|_| CryptoError::HpkeEncryptionError)?;
742        let (encapped, ciphertext) =
743            hpke::single_shot_seal_with_rng::<Aead, Kdf, Kem>(&hpke::OpModeS::Base, &key, info, plaintext, aad, csprng)
744                .map_err(|_| CryptoError::HpkeEncryptionError)?;
745
746        Ok(HpkeCiphertext {
747            kem_output: encapped.to_bytes().to_vec().into(),
748            ciphertext: ciphertext.into(),
749        })
750    }
751
752    pub(crate) fn hpke_seal_psk<Aead: hpke::aead::Aead, Kdf: hpke::kdf::Kdf, Kem: hpke::Kem>(
753        public_key: &[u8],
754        info: &[u8],
755        aad: &[u8],
756        psk: &[u8],
757        psk_id: &[u8],
758        plaintext: &[u8],
759        csprng: &mut impl rand_core::CryptoRng,
760    ) -> Result<HpkeCiphertext, CryptoError> {
761        use hpke::{Deserializable as _, Serializable as _};
762        let key = Kem::PublicKey::from_bytes(public_key).map_err(|_| CryptoError::HpkeEncryptionError)?;
763        let psk_bundle = PskBundle::new(psk, psk_id).map_err(|_| CryptoError::HpkeEncryptionError)?;
764        let (encapped, ciphertext) = hpke::single_shot_seal_with_rng::<Aead, Kdf, Kem>(
765            &hpke::OpModeS::Psk(psk_bundle),
766            &key,
767            info,
768            plaintext,
769            aad,
770            csprng,
771        )
772        .map_err(|_| CryptoError::HpkeEncryptionError)?;
773
774        Ok(HpkeCiphertext {
775            kem_output: encapped.to_bytes().to_vec().into(),
776            ciphertext: ciphertext.into(),
777        })
778    }
779
780    #[allow(dead_code)]
781    pub(crate) fn hpke_gen_keypair<Kem: hpke::Kem>(
782        csprng: &mut impl rand_core::CryptoRng,
783    ) -> Result<HpkeKeyPair, CryptoError> {
784        use hpke::Serializable as _;
785        let (sk, pk) = Kem::gen_keypair_with_rng(csprng);
786        let (private, public) = (sk.to_bytes().to_vec().into(), pk.to_bytes().to_vec());
787
788        Ok(HpkeKeyPair { private, public })
789    }
790
791    pub(crate) fn hpke_derive_keypair<Kem: hpke::Kem>(ikm: &[u8]) -> Result<HpkeKeyPair, CryptoError> {
792        use hpke::Serializable as _;
793        let (sk, pk) = Kem::derive_keypair(ikm);
794        let (private, public) = (sk.to_bytes().to_vec().into(), pk.to_bytes().to_vec());
795
796        Ok(HpkeKeyPair { private, public })
797    }
798
799    pub(crate) fn hpke_export_rx<Aead: hpke::aead::Aead, Kdf: hpke::kdf::Kdf, Kem: hpke::Kem>(
800        encapped_key: &[u8],
801        rx_private_key: &[u8],
802        info: &[u8],
803        export_info: &[u8],
804        export_len: usize,
805    ) -> Result<Vec<u8>, CryptoError> {
806        use hpke::Deserializable as _;
807        let key = Kem::PrivateKey::from_bytes(rx_private_key).map_err(|_| CryptoError::ReceiverSetupError)?;
808        let encapped_key = Kem::EncappedKey::from_bytes(encapped_key).map_err(|_| CryptoError::ReceiverSetupError)?;
809        let ctx = hpke::setup_receiver::<Aead, Kdf, Kem>(&hpke::OpModeR::Base, &key, &encapped_key, info)
810            .map_err(|_| CryptoError::ReceiverSetupError)?;
811
812        let mut export = vec![0u8; export_len];
813
814        ctx.export(export_info, &mut export)
815            .map_err(|_| CryptoError::ExporterError)?;
816
817        Ok(export)
818    }
819
820    pub(crate) fn hpke_export_tx<Aead: hpke::aead::Aead, Kdf: hpke::kdf::Kdf, Kem: hpke::Kem>(
821        tx_public_key: &[u8],
822        info: &[u8],
823        export_info: &[u8],
824        export_len: usize,
825        csprng: &mut impl rand_core::CryptoRng,
826    ) -> Result<(Vec<u8>, Vec<u8>), CryptoError> {
827        use hpke::{Deserializable as _, Serializable as _};
828        let key = Kem::PublicKey::from_bytes(tx_public_key).map_err(|_| CryptoError::SenderSetupError)?;
829        let (kem_output, ctx) = hpke::setup_sender_with_rng::<Aead, Kdf, Kem>(&hpke::OpModeS::Base, &key, info, csprng)
830            .map_err(|_| CryptoError::SenderSetupError)?;
831
832        let mut export = vec![0u8; export_len];
833
834        ctx.export(export_info, &mut export)
835            .map_err(|_| CryptoError::ExporterError)?;
836
837        Ok((kem_output.to_bytes().to_vec(), export))
838    }
839}
840
841impl OpenMlsRand for RustCrypto {
842    type Error = Error;
843
844    type RandImpl = rand_chacha::ChaCha20Rng;
845    type BorrowTarget<'a> = RwLockWriteGuard<'a, Self::RandImpl>;
846
847    fn borrow_rand(&self) -> Result<Self::BorrowTarget<'_>, Self::Error> {
848        self.rng.write().map_err(|_| Error::RngLockPoison)
849    }
850
851    fn random_array<const N: usize>(&self) -> Result<[u8; N], Self::Error> {
852        let mut rng = self.borrow_rand()?;
853        let mut out = [0u8; N];
854        rng.fill_bytes(&mut out);
855        Ok(out)
856    }
857
858    fn random_vec(&self, len: usize) -> Result<Vec<u8>, Self::Error> {
859        let mut rng = self.borrow_rand()?;
860        let mut out = vec![0u8; len];
861        rng.fill_bytes(&mut out);
862        Ok(out)
863    }
864}
865
866#[cfg(test)]
867mod tests {
868    use openmls_traits::{
869        crypto::OpenMlsCrypto as _,
870        types::{CryptoError, HpkeAeadType, HpkeCiphertext, HpkeConfig, HpkeKdfType, HpkeKemType, SignatureScheme},
871    };
872
873    use super::RustCrypto;
874
875    /// `signature_public_key_len` exists so that callers can size a buffer for, or validate the
876    /// length of, a public key this provider produced. It is only meaningful if it agrees with
877    /// what `signature_key_gen` actually emits.
878    #[test]
879    fn signature_public_key_len_matches_generated_keys() {
880        let crypto = RustCrypto::default();
881
882        for scheme in [
883            SignatureScheme::ED25519,
884            SignatureScheme::ECDSA_SECP256R1_SHA256,
885            SignatureScheme::ECDSA_SECP384R1_SHA384,
886            SignatureScheme::ECDSA_SECP521R1_SHA512,
887        ] {
888            let (_sk, pk) = crypto
889                .signature_key_gen(scheme)
890                .expect("this provider supports all four of these schemes");
891
892            assert_eq!(
893                pk.len(),
894                crypto.signature_public_key_len(scheme),
895                "reported public key length for {scheme:?} must match the generated key"
896            );
897
898            // The generated key must also pass this provider's own validation, which is what the
899            // trait's default implementation would have used the length above to decide.
900            crypto
901                .validate_signature_key(scheme, &pk)
902                .unwrap_or_else(|err| panic!("generated {scheme:?} key must validate: {err:?}"));
903        }
904    }
905
906    /// An empty PSK provides no binding, and `hpke` treats such a bundle as plain `Base` mode, so
907    /// the `_psk` entry points must not silently accept one.
908    #[test]
909    fn hpke_psk_mode_rejects_a_psk_without_entropy() {
910        let crypto = RustCrypto::default();
911        // `HpkeConfig` is neither `Copy` nor `Clone`, so build a fresh one per call.
912        let config = || {
913            HpkeConfig(
914                HpkeKemType::DhKem25519,
915                HpkeKdfType::HkdfSha256,
916                HpkeAeadType::AesGcm128,
917            )
918        };
919        // Deliberately not a real recipient key or ciphertext: the PSK is checked before any of
920        // that is parsed, and asserting on `InvalidLength` specifically keeps this test from
921        // passing for the wrong reason.
922        let pk_r = [0u8; 32];
923        let ciphertext = HpkeCiphertext {
924            kem_output: Vec::new().into(),
925            ciphertext: Vec::new().into(),
926        };
927
928        for (psk, psk_id, case) in [
929            (&[][..], &[][..], "empty psk and psk id"),
930            (&[0u8; 32][..], &[][..], "empty psk id"),
931            (&[0u8; 31][..], &b"id"[..], "psk one byte short of 32"),
932        ] {
933            assert_eq!(
934                crypto.hpke_seal_psk(config(), &pk_r, b"info", b"aad", psk, psk_id, b"ptxt"),
935                Err(CryptoError::InvalidLength),
936                "hpke_seal_psk must reject: {case}"
937            );
938            assert_eq!(
939                crypto.hpke_open_psk(config(), &ciphertext, &pk_r, b"info", b"aad", psk, psk_id),
940                Err(CryptoError::InvalidLength),
941                "hpke_open_psk must reject: {case}"
942            );
943        }
944    }
945}