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
30pub(crate) static CRYPTO: LazyLock<Arc<RustCrypto>> = LazyLock::new(|| Arc::new(RustCrypto::default()));
33
34#[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 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
167fn 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 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 | 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 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 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 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 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 #[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 crypto
784 .validate_signature_key(scheme, &pk)
785 .unwrap_or_else(|err| panic!("generated {scheme:?} key must validate: {err:?}"));
786 }
787 }
788
789 #[test]
792 fn hpke_psk_mode_rejects_a_psk_without_entropy() {
793 let crypto = RustCrypto::default();
794 let config = || {
796 HpkeConfig(
797 HpkeKemType::DhKem25519,
798 HpkeKdfType::HkdfSha256,
799 HpkeAeadType::AesGcm128,
800 )
801 };
802 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 #[test]
835 fn reseeding_without_a_seed_does_not_use_a_fixed_key() {
836 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}