rustls_mbedcrypto_provider/
kx.rs1use std::sync::Mutex;
9use std::sync::OnceLock;
10
11use super::agreement;
12use crate::error::mbedtls_err_to_rustls_error;
13use crate::rng::MbedRng;
14
15use alloc::boxed::Box;
16use alloc::fmt;
17use alloc::format;
18use alloc::vec::Vec;
19use crypto::SupportedKxGroup;
20use mbedtls::bignum::Mpi;
21use mbedtls::rng::Random;
22use mbedtls::rng::RngCallback;
23use mbedtls::{
24 ecp::EcPoint,
25 pk::{EcGroup, Pk as PkMbed},
26};
27use rustls::crypto;
28use rustls::crypto::ActiveKeyExchange;
29use rustls::ffdhe_groups;
30use rustls::ffdhe_groups::FfdheGroup;
31use rustls::Error;
32use rustls::NamedGroup;
33pub struct EcdhKxGroupWrapper<T: RngCallback> {
39 kx_group: EcdhKxGroup,
41
42 rng_provider: fn() -> Option<T>,
44}
45
46#[derive(Debug, Clone, Copy)]
48struct EcdhKxGroup {
49 agreement_algorithm: &'static agreement::Algorithm,
51
52 name: NamedGroup,
54}
55
56impl<T: RngCallback> EcdhKxGroupWrapper<T> {
57 pub const fn with_rng_provider<F: RngCallback>(&self, rng_provider: fn() -> Option<F>) -> EcdhKxGroupWrapper<F> {
59 EcdhKxGroupWrapper { rng_provider, kx_group: self.kx_group }
60 }
61}
62
63impl<T: RngCallback> fmt::Debug for EcdhKxGroupWrapper<T> {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 f.debug_struct("EcdhKxGroupWrapper")
66 .field("kx_group", &self.kx_group)
67 .finish()
68 }
69}
70
71impl<T: RngCallback + 'static> SupportedKxGroup for EcdhKxGroupWrapper<T> {
72 fn start(&self) -> Result<Box<dyn ActiveKeyExchange>, Error> {
73 let mut rng = (self.rng_provider)().ok_or(Error::FailedToGetRandomBytes)?;
74
75 #[allow(unused_mut)]
76 let mut priv_key = generate_ec_key(
77 self.kx_group
78 .agreement_algorithm
79 .group_id,
80 &mut rng,
81 )?;
82
83 #[cfg(feature = "fips")]
85 match self.name() {
86 NamedGroup::secp256r1 | NamedGroup::secp384r1 | NamedGroup::secp521r1 => {
87 crate::fips_utils::fips_ec_pct(
88 &mut priv_key,
89 self.kx_group
90 .agreement_algorithm
91 .group_id,
92 &mut rng,
93 )?;
94 }
95 _ => (),
96 }
97
98 Ok(Box::new(EcdhKeyExchangeImpl {
99 name: self.kx_group.name,
100 agreement_algorithm: self.kx_group.agreement_algorithm,
101 priv_key,
102 pub_key: OnceLock::new(),
103 rng_provider: self.rng_provider,
104 }))
105 }
106
107 fn name(&self) -> NamedGroup {
108 self.kx_group.name
109 }
110}
111
112#[inline]
113fn generate_ec_key<F: Random>(group_id: mbedtls::pk::EcGroupId, rng: &mut F) -> Result<PkMbed, Error> {
114 PkMbed::generate_ec(rng, group_id)
115 .map_err(|err| Error::General(format!("Got error when generating ec key, mbedtls error: {err}")))
116}
117
118pub static X25519: &dyn SupportedKxGroup = X25519_KX_GROUP;
120pub static X25519_KX_GROUP: &EcdhKxGroupWrapper<MbedRng> = &EcdhKxGroupWrapper {
122 kx_group: EcdhKxGroup { name: NamedGroup::X25519, agreement_algorithm: &agreement::X25519 },
123 rng_provider: crate::rng::rng_new,
124};
125
126pub static SECP256R1: &dyn SupportedKxGroup = SECP256R1_KX_GROUP;
128pub static SECP256R1_KX_GROUP: &EcdhKxGroupWrapper<MbedRng> = &EcdhKxGroupWrapper {
130 kx_group: EcdhKxGroup { name: NamedGroup::secp256r1, agreement_algorithm: &agreement::ECDH_P256 },
131 rng_provider: crate::rng::rng_new,
132};
133
134pub static SECP384R1: &dyn SupportedKxGroup = SECP384R1_KX_GROUP;
136pub static SECP384R1_KX_GROUP: &EcdhKxGroupWrapper<MbedRng> = &EcdhKxGroupWrapper {
138 kx_group: EcdhKxGroup { name: NamedGroup::secp384r1, agreement_algorithm: &agreement::ECDH_P384 },
139 rng_provider: crate::rng::rng_new,
140};
141
142pub static SECP521R1: &dyn SupportedKxGroup = SECP521R1_KX_GROUP;
144pub static SECP521R1_KX_GROUP: &EcdhKxGroupWrapper<MbedRng> = &EcdhKxGroupWrapper {
146 kx_group: EcdhKxGroup { name: NamedGroup::secp521r1, agreement_algorithm: &agreement::ECDH_P521 },
147 rng_provider: crate::rng::rng_new,
148};
149
150pub static FFDHE2048: &dyn SupportedKxGroup = FFDHE2048_KX_GROUP;
152pub static FFDHE2048_KX_GROUP: &FfdheKxGroupWrapper<MbedRng> = &FfdheKxGroupWrapper {
154 dhe_kx_group: FfdheKxGroup {
155 named_group: NamedGroup::FFDHE2048,
156 group: ffdhe_groups::FFDHE2048,
157 priv_key_len: 36,
158 },
159 rng_provider: crate::rng::rng_new,
160};
161
162pub static FFDHE3072: &dyn SupportedKxGroup = FFDHE3072_KX_GROUP;
164pub static FFDHE3072_KX_GROUP: &FfdheKxGroupWrapper<MbedRng> = &FfdheKxGroupWrapper {
166 dhe_kx_group: FfdheKxGroup {
167 named_group: NamedGroup::FFDHE3072,
168 group: ffdhe_groups::FFDHE3072,
169 priv_key_len: 40,
170 },
171 rng_provider: crate::rng::rng_new,
172};
173
174pub static FFDHE4096: &dyn SupportedKxGroup = FFDHE4096_KX_GROUP;
176pub static FFDHE4096_KX_GROUP: &FfdheKxGroupWrapper<MbedRng> = &FfdheKxGroupWrapper {
178 dhe_kx_group: FfdheKxGroup {
179 named_group: NamedGroup::FFDHE4096,
180 group: ffdhe_groups::FFDHE4096,
181 priv_key_len: 48,
182 },
183 rng_provider: crate::rng::rng_new,
184};
185
186pub static FFDHE6144: &dyn SupportedKxGroup = FFDHE6144_KX_GROUP;
188pub static FFDHE6144_KX_GROUP: &FfdheKxGroupWrapper<MbedRng> = &FfdheKxGroupWrapper {
190 dhe_kx_group: FfdheKxGroup {
191 named_group: NamedGroup::FFDHE6144,
192 group: ffdhe_groups::FFDHE6144,
193 priv_key_len: 56,
194 },
195 rng_provider: crate::rng::rng_new,
196};
197
198pub static FFDHE8192: &dyn SupportedKxGroup = FFDHE8192_KX_GROUP;
200pub static FFDHE8192_KX_GROUP: &FfdheKxGroupWrapper<MbedRng> = &FfdheKxGroupWrapper {
202 dhe_kx_group: FfdheKxGroup {
203 named_group: NamedGroup::FFDHE8192,
204 group: ffdhe_groups::FFDHE8192,
205 priv_key_len: 64,
206 },
207 rng_provider: crate::rng::rng_new,
208};
209
210pub static ALL_KX_GROUPS: &[&dyn SupportedKxGroup] = &[
212 X25519, SECP256R1, SECP384R1, SECP521R1, FFDHE2048, FFDHE3072, FFDHE4096, FFDHE6144, FFDHE8192,
216];
217
218struct EcdhKeyExchangeImpl<T: RngCallback> {
221 agreement_algorithm: &'static agreement::Algorithm,
223 name: NamedGroup,
225 priv_key: PkMbed,
227 pub_key: OnceLock<Vec<u8>>,
229 rng_provider: fn() -> Option<T>,
231}
232
233impl<T: RngCallback> EcdhKeyExchangeImpl<T> {
234 fn get_pub_key(&self) -> mbedtls::Result<Vec<u8>> {
235 let group = EcGroup::new(self.agreement_algorithm.group_id)?;
236 self.priv_key
237 .ec_public()?
238 .to_binary(&group, false)
239 }
240}
241
242impl<T: RngCallback> ActiveKeyExchange for EcdhKeyExchangeImpl<T> {
243 fn complete(mut self: Box<Self>, peer_public_key: &[u8]) -> Result<crypto::SharedSecret, Error> {
245 let group_id = self.agreement_algorithm.group_id;
246
247 if peer_public_key.len() != self.agreement_algorithm.public_key_len {
248 return Err(rustls::PeerMisbehaved::InvalidKeyShare.into());
249 }
250
251 let peer_pk = parse_peer_public_key(group_id, peer_public_key).map_err(mbedtls_err_to_rustls_error)?;
252
253 let mut rng = (self.rng_provider)().ok_or(crypto::GetRandomFailed)?;
254
255 #[cfg(feature = "fips")]
257 match self.name {
258 NamedGroup::secp256r1 | NamedGroup::secp384r1 | NamedGroup::secp521r1 => {
259 crate::fips_utils::fips_check_ec_pub_key(&peer_pk, &mut rng)?
260 }
261 _ => (),
262 }
263
264 let mut shared_key = [0u8; mbedtls::pk::ECDSA_MAX_LEN];
265 let shared_key = &mut shared_key[..self
266 .agreement_algorithm
267 .max_signature_len];
268 let len = self
269 .priv_key
270 .agree(&peer_pk, shared_key, &mut rng)
271 .map_err(mbedtls_err_to_rustls_error)?;
272 Ok(crypto::SharedSecret::from(&shared_key[..len]))
273 }
274
275 fn pub_key(&self) -> &[u8] {
277 self.pub_key
278 .get_or_init(|| self.get_pub_key().unwrap_or_default())
279 }
280
281 fn group(&self) -> NamedGroup {
283 self.name
284 }
285}
286
287pub struct FfdheKxGroupWrapper<T: RngCallback> {
293 pub(crate) dhe_kx_group: FfdheKxGroup,
295 rng_provider: fn() -> Option<T>,
297}
298
299#[derive(Debug, Clone, Copy)]
301pub(crate) struct FfdheKxGroup {
302 pub(crate) group: FfdheGroup<'static>,
304 pub(crate) named_group: NamedGroup,
306 pub(crate) priv_key_len: usize,
308}
309
310impl<T: RngCallback> FfdheKxGroupWrapper<T> {
311 pub const fn with_rng_provider<F: RngCallback>(&self, rng_provider: fn() -> Option<F>) -> FfdheKxGroupWrapper<F> {
313 FfdheKxGroupWrapper { dhe_kx_group: self.dhe_kx_group, rng_provider }
314 }
315}
316
317impl<T: RngCallback> fmt::Debug for FfdheKxGroupWrapper<T> {
318 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
319 f.debug_struct("FfdheKxGroupWrapper")
320 .field("dhe_kx_group", &self.dhe_kx_group)
321 .finish()
322 }
323}
324
325impl<T: RngCallback> SupportedKxGroup for FfdheKxGroupWrapper<T> {
326 fn start(&self) -> Result<Box<dyn ActiveKeyExchange>, Error> {
327 let g = Mpi::from_binary(self.dhe_kx_group.group.g).map_err(mbedtls_err_to_rustls_error)?;
328 let p = Mpi::from_binary(self.dhe_kx_group.group.p).map_err(mbedtls_err_to_rustls_error)?;
329
330 let mut rng = (self.rng_provider)().ok_or(crypto::GetRandomFailed)?;
331 let mut x = vec![0; self.dhe_kx_group.priv_key_len];
332 rng.random(&mut x)
333 .map_err(|_| crypto::GetRandomFailed)?;
334 let x = Mpi::from_binary(&x).map_err(|e| Error::General(format!("failed to make Bignum from random bytes: {e}")))?;
335 let x_pub = g
336 .mod_exp(&x, &p)
337 .map_err(mbedtls_err_to_rustls_error)?;
338
339 #[cfg(feature = "fips")]
340 crate::fips_utils::ffdhe_pct(self, &x, &x_pub)?;
341
342 Ok(Box::new(DheActiveKeyExchangeImpl::new(
343 self.dhe_kx_group.named_group,
344 self.dhe_kx_group.group,
345 Mutex::new(p),
346 Mutex::new(x),
347 x_pub
348 .to_binary_padded(self.dhe_kx_group.group.p.len())
349 .map_err(mbedtls_err_to_rustls_error)?,
350 )))
351 }
352
353 fn name(&self) -> NamedGroup {
354 self.dhe_kx_group.named_group
355 }
356}
357
358pub(crate) struct DheActiveKeyExchangeImpl {
359 named_group: NamedGroup,
360 group: FfdheGroup<'static>,
361 p: Mutex<Mpi>,
364 x: Mutex<Mpi>,
365 x_pub: Vec<u8>,
366}
367
368impl DheActiveKeyExchangeImpl {
369 pub(crate) fn new(
370 named_group: NamedGroup,
371 group: FfdheGroup<'static>,
372 p: Mutex<Mpi>,
373 x: Mutex<Mpi>,
374 x_pub: Vec<u8>,
375 ) -> Self {
376 Self { named_group, group, p, x, x_pub }
377 }
378}
379
380impl ActiveKeyExchange for DheActiveKeyExchangeImpl {
381 fn complete(self: Box<Self>, peer_pub_key: &[u8]) -> Result<crypto::SharedSecret, Error> {
382 let y_pub = Mpi::from_binary(peer_pub_key).map_err(mbedtls_err_to_rustls_error)?;
383
384 let x = self
385 .x
386 .into_inner()
387 .expect("Mpi Mutex poisoned");
388 let p = self
389 .p
390 .into_inner()
391 .expect("Mpi Mutex poisoned");
392
393 let one = Mpi::new(1).map_err(mbedtls_err_to_rustls_error)?;
394
395 let mut p_minus_one = p;
396 p_minus_one -= &one;
397
398 if !(one < y_pub && y_pub < p_minus_one) {
401 return Err(Error::General(
402 "Invalid DHE key exchange public key received; pub key must be in range (1, p-1)".into(),
403 ));
404 }
405
406 p_minus_one += &one;
407 let p = p_minus_one;
408
409 #[cfg(feature = "fips")]
410 crate::fips_utils::ffdhe_pub_key_check(&self.group, self.named_group, &y_pub)?;
411
412 let secret = y_pub
413 .mod_exp(&x, &p)
414 .map_err(mbedtls_err_to_rustls_error)?;
415
416 Ok(crypto::SharedSecret::from(
417 secret
418 .to_binary_padded(self.group.p.len())
419 .map_err(mbedtls_err_to_rustls_error)?
420 .as_ref(),
421 ))
422 }
423
424 fn pub_key(&self) -> &[u8] {
425 &self.x_pub
426 }
427
428 fn group(&self) -> NamedGroup {
429 self.named_group
430 }
431}
432
433#[inline]
434fn parse_peer_public_key(group_id: mbedtls::pk::EcGroupId, peer_public_key: &[u8]) -> Result<PkMbed, mbedtls::Error> {
435 let ec_group = EcGroup::new(group_id)?;
436 let public_point = EcPoint::from_binary_no_compress(&ec_group, peer_public_key)?;
437 PkMbed::public_from_ec_components(ec_group, public_point)
438}
439
440#[cfg(test)]
441mod tests {
442 use super::*;
443
444 #[test]
445 fn test_kx_group_fmt_debug() {
446 let debug_str = format!("{X25519_KX_GROUP:?}");
447 assert_eq!(
448 debug_str,
449 "EcdhKxGroupWrapper { kx_group: EcdhKxGroup { agreement_algorithm: Algorithm { group_id: Curve25519 }, name: X25519 } }",
450 )
451 }
452
453 #[test]
454 fn test_dhe_kx_group_fmt_debug() {
455 let debug_str = format!("{FFDHE2048_KX_GROUP:?}");
456 assert!(debug_str.contains("FfdheKxGroup"), "debug_str: {debug_str}");
457 assert!(debug_str.contains("FFDHE2048"), "debug_str: {debug_str}");
458 assert!(debug_str.contains("FfdheGroup"), "debug_str: {debug_str}");
459 assert!(!debug_str.contains("rng_provider"), "debug_str: {debug_str}");
460 }
461
462 #[test]
463 fn test_static_with_rng_provider() {
464 fn get_ftx_rng() -> Option<MbedRng> {
465 None
466 }
467 assert!((X25519_KX_GROUP
469 .with_rng_provider(get_ftx_rng)
470 .rng_provider)()
471 .is_none());
472 assert!((FFDHE2048_KX_GROUP
473 .with_rng_provider(get_ftx_rng)
474 .rng_provider)()
475 .is_none());
476 static _X25519: &dyn SupportedKxGroup = &X25519_KX_GROUP.with_rng_provider(get_ftx_rng);
478 static _FFDHE2048: &dyn SupportedKxGroup = &FFDHE2048_KX_GROUP.with_rng_provider(get_ftx_rng);
479 }
480}
481
482#[cfg(bench)]
483mod benchmarks {
484
485 #[bench]
486 fn bench_ecdh_p256(b: &mut test::Bencher) {
487 bench_any(b, super::SECP256R1);
488 }
489
490 #[bench]
491 fn bench_ecdh_p384(b: &mut test::Bencher) {
492 bench_any(b, super::SECP384R1);
493 }
494
495 #[bench]
496 fn bench_ecdh_p521(b: &mut test::Bencher) {
497 bench_any(b, super::SECP521R1);
498 }
499
500 #[bench]
501 fn bench_x25519(b: &mut test::Bencher) {
502 bench_any(b, super::X25519);
503 }
504
505 fn bench_any(b: &mut test::Bencher, kxg: &dyn super::SupportedKxGroup) {
506 b.iter(|| {
507 let akx = kxg.start().unwrap();
508 let pub_key = akx.pub_key().to_vec();
509 test::black_box(akx.complete(&pub_key).unwrap());
510 });
511 }
512
513 #[bench]
514 fn bench_ecdh_p256_start(b: &mut test::Bencher) {
515 let kxg = super::SECP256R1;
516 b.iter(|| {
517 test::black_box(kxg.start().unwrap());
518 });
519 }
520
521 #[bench]
522 fn bench_ecdh_p256_gen_private_key(b: &mut test::Bencher) {
523 let mut rng = crate::rng::rng_new().unwrap();
524 b.iter(|| {
525 test::black_box(super::generate_ec_key(mbedtls::pk::EcGroupId::SecP256R1, &mut rng).unwrap());
526 });
527 }
528
529 #[bench]
530 fn bench_ecdh_p256_parse_peer_pub_key(b: &mut test::Bencher) {
531 let kxg = super::SECP256R1;
532 let akx = kxg.start().unwrap();
533 let pub_key = akx.pub_key().to_vec();
534 b.iter(|| {
535 test::black_box(super::parse_peer_public_key(mbedtls::pk::EcGroupId::SecP256R1, &pub_key).unwrap());
536 });
537 }
538}