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