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
27pub(crate) static CRYPTO: LazyLock<Arc<RustCrypto>> = LazyLock::new(|| Arc::new(RustCrypto::default()));
30
31#[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
185fn 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 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 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 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 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 #[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 crypto
901 .validate_signature_key(scheme, &pk)
902 .unwrap_or_else(|err| panic!("generated {scheme:?} key must validate: {err:?}"));
903 }
904 }
905
906 #[test]
909 fn hpke_psk_mode_rejects_a_psk_without_entropy() {
910 let crypto = RustCrypto::default();
911 let config = || {
913 HpkeConfig(
914 HpkeKemType::DhKem25519,
915 HpkeKdfType::HkdfSha256,
916 HpkeAeadType::AesGcm128,
917 )
918 };
919 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}