1use std::sync::LazyLock;
19
20use rustls::crypto::CryptoProvider;
21
22static DEFAULT_PROVIDER: LazyLock<CryptoProvider> = LazyLock::new(default_provider);
23
24#[cfg(not(any(
25 feature = "crypto-ring",
26 feature = "crypto-aws-lc-rs",
27 feature = "crypto-openssl"
28)))]
29compile_error!(
30 "No crypto provider selected. Enable one of: `crypto-ring`, `crypto-aws-lc-rs`, or `crypto-openssl`."
31);
32
33#[cfg(feature = "fips")]
45pub use rustls::crypto::aws_lc_rs::{
46 cipher_suite::{
47 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
48 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
49 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
50 TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384, TLS13_CHACHA20_POLY1305_SHA256,
51 },
52 default_provider,
53 sign::any_supported_type,
54};
55
56#[cfg(all(feature = "crypto-ring", not(feature = "fips")))]
57pub use rustls::crypto::ring::{
58 cipher_suite::{
59 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
60 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
61 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
62 TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384, TLS13_CHACHA20_POLY1305_SHA256,
63 },
64 default_provider,
65 sign::any_supported_type,
66};
67
68#[cfg(all(
69 feature = "crypto-aws-lc-rs",
70 not(feature = "fips"),
71 not(feature = "crypto-ring")
72))]
73pub use rustls::crypto::aws_lc_rs::{
74 cipher_suite::{
75 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
76 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
77 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
78 TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384, TLS13_CHACHA20_POLY1305_SHA256,
79 },
80 default_provider,
81 sign::any_supported_type,
82};
83
84#[cfg(all(
85 feature = "crypto-openssl",
86 not(feature = "fips"),
87 not(feature = "crypto-ring"),
88 not(feature = "crypto-aws-lc-rs")
89))]
90pub use rustls_openssl::{
91 cipher_suite::{
92 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
93 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
94 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
95 TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384, TLS13_CHACHA20_POLY1305_SHA256,
96 },
97 default_provider,
98};
99
100#[cfg(all(
105 feature = "crypto-openssl",
106 not(feature = "fips"),
107 not(feature = "crypto-ring"),
108 not(feature = "crypto-aws-lc-rs")
109))]
110pub fn any_supported_type(
111 der: &rustls::pki_types::PrivateKeyDer<'_>,
112) -> Result<std::sync::Arc<dyn rustls::sign::SigningKey>, rustls::Error> {
113 use rustls::crypto::KeyProvider;
114 rustls_openssl::KeyProvider.load_private_key(der.clone_key())
115}
116
117pub fn kx_group_by_name(name: &str) -> Option<&'static dyn rustls::crypto::SupportedKxGroup> {
127 debug_assert_provider_precedence();
128 let provider = &*DEFAULT_PROVIDER;
129 debug_assert!(
133 !provider.kx_groups.is_empty(),
134 "the active crypto provider must advertise at least one kx group"
135 );
136 let named_group = match name {
137 "x25519" | "X25519" => rustls::NamedGroup::X25519,
138 "secp256r1" | "P-256" => rustls::NamedGroup::secp256r1,
139 "secp384r1" | "P-384" => rustls::NamedGroup::secp384r1,
140 "X25519MLKEM768" => rustls::NamedGroup::X25519MLKEM768,
141 _ => return None,
142 };
143 let resolved = provider
144 .kx_groups
145 .iter()
146 .find(|g| g.name() == named_group)
147 .copied();
148 debug_assert!(
151 resolved.is_none_or(|g| g.name() == named_group),
152 "resolved kx group must match the requested named group"
153 );
154 resolved
155}
156
157pub fn cipher_suite_by_name(name: &str) -> Option<rustls::SupportedCipherSuite> {
172 debug_assert_provider_precedence();
173 debug_assert!(
176 !DEFAULT_PROVIDER.cipher_suites.is_empty(),
177 "the active crypto provider must advertise at least one cipher suite"
178 );
179 let candidate = match name {
180 "TLS13_AES_256_GCM_SHA384" => Some(TLS13_AES_256_GCM_SHA384),
181 "TLS13_AES_128_GCM_SHA256" => Some(TLS13_AES_128_GCM_SHA256),
182 "TLS13_CHACHA20_POLY1305_SHA256" => Some(TLS13_CHACHA20_POLY1305_SHA256),
183 "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" => Some(TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384),
184 "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" => Some(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256),
185 "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256" => {
186 Some(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256)
187 }
188 "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" => Some(TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384),
189 "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" => Some(TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256),
190 "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" => {
191 Some(TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256)
192 }
193 _ => None,
194 }?;
195
196 let wanted = candidate.suite();
200 let resolved = DEFAULT_PROVIDER
201 .cipher_suites
202 .iter()
203 .find(|s| s.suite() == wanted)
204 .copied();
205 debug_assert!(
208 resolved.is_none_or(|s| s.suite() == wanted),
209 "resolved cipher suite must match the suite the name mapped to"
210 );
211 resolved
212}
213
214#[inline]
225fn debug_assert_provider_precedence() {
226 debug_assert!(
229 cfg!(feature = "crypto-ring")
230 || cfg!(feature = "crypto-aws-lc-rs")
231 || cfg!(feature = "crypto-openssl"),
232 "at least one crypto provider feature must be enabled"
233 );
234 debug_assert!(
237 !cfg!(feature = "fips") || cfg!(feature = "crypto-aws-lc-rs"),
238 "the `fips` feature must imply `crypto-aws-lc-rs`"
239 );
240 debug_assert!(
245 !cfg!(all(
246 feature = "crypto-openssl",
247 not(feature = "fips"),
248 not(feature = "crypto-ring"),
249 not(feature = "crypto-aws-lc-rs")
250 )) || cfg!(feature = "crypto-openssl"),
251 "the openssl provider arm is only reachable with openssl enabled"
252 );
253}
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258 use rustls::NamedGroup;
259
260 #[test]
261 fn default_provider_has_tls13_cipher_suites() {
262 let provider = default_provider();
263 let names: Vec<_> = provider.cipher_suites.iter().map(|cs| cs.suite()).collect();
264 assert!(
265 names.contains(&rustls::CipherSuite::TLS13_AES_256_GCM_SHA384),
266 "provider must support TLS13_AES_256_GCM_SHA384"
267 );
268 assert!(
269 names.contains(&rustls::CipherSuite::TLS13_AES_128_GCM_SHA256),
270 "provider must support TLS13_AES_128_GCM_SHA256"
271 );
272 #[cfg(not(feature = "fips"))]
274 assert!(
275 names.contains(&rustls::CipherSuite::TLS13_CHACHA20_POLY1305_SHA256),
276 "provider must support TLS13_CHACHA20_POLY1305_SHA256"
277 );
278 }
279
280 #[test]
281 fn default_provider_has_tls12_cipher_suites() {
282 let provider = default_provider();
283 let names: Vec<_> = provider.cipher_suites.iter().map(|cs| cs.suite()).collect();
284 assert!(
285 names.contains(&rustls::CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384),
286 "provider must support TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
287 );
288 assert!(
289 names.contains(&rustls::CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384),
290 "provider must support TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"
291 );
292 }
293
294 #[test]
295 fn default_provider_has_classical_kx_groups() {
296 let provider = default_provider();
297 let groups: Vec<_> = provider.kx_groups.iter().map(|g| g.name()).collect();
298 #[cfg(not(feature = "fips"))]
300 assert!(
301 groups.contains(&NamedGroup::X25519),
302 "provider must support X25519 key exchange"
303 );
304 assert!(
305 groups.contains(&NamedGroup::secp256r1),
306 "provider must support secp256r1 key exchange"
307 );
308 assert!(
309 groups.contains(&NamedGroup::secp384r1),
310 "provider must support secp384r1 key exchange"
311 );
312 }
313
314 #[cfg(all(feature = "crypto-aws-lc-rs", not(feature = "fips")))]
315 #[test]
316 fn aws_lc_rs_supports_post_quantum_kx() {
317 let provider = default_provider();
318 let groups: Vec<_> = provider.kx_groups.iter().map(|g| g.name()).collect();
319 assert!(
320 groups.contains(&NamedGroup::X25519MLKEM768),
321 "aws-lc-rs provider must support X25519MLKEM768 post-quantum key exchange"
322 );
323 }
324
325 #[cfg(all(feature = "crypto-aws-lc-rs", not(feature = "fips")))]
326 #[test]
327 fn aws_lc_rs_has_more_kx_groups_than_classical() {
328 let provider = default_provider();
329 assert!(
332 provider.kx_groups.len() > 3,
333 "aws-lc-rs should have more than 3 kx groups (has {}), including post-quantum",
334 provider.kx_groups.len()
335 );
336 }
337
338 #[cfg(feature = "fips")]
339 #[test]
340 fn fips_provider_has_nist_kx_groups() {
341 let provider = default_provider();
342 let groups: Vec<_> = provider.kx_groups.iter().map(|g| g.name()).collect();
343 assert!(
344 groups.contains(&NamedGroup::secp256r1),
345 "FIPS provider must support secp256r1"
346 );
347 assert!(
348 groups.contains(&NamedGroup::secp384r1),
349 "FIPS provider must support secp384r1"
350 );
351 }
352
353 #[cfg(feature = "fips")]
354 #[test]
355 fn fips_provider_has_aes_gcm_cipher_suites() {
356 let provider = default_provider();
357 let suites: Vec<_> = provider.cipher_suites.iter().map(|cs| cs.suite()).collect();
358 assert!(
359 suites.contains(&rustls::CipherSuite::TLS13_AES_256_GCM_SHA384),
360 "FIPS provider must support TLS13_AES_256_GCM_SHA384"
361 );
362 assert!(
363 suites.contains(&rustls::CipherSuite::TLS13_AES_128_GCM_SHA256),
364 "FIPS provider must support TLS13_AES_128_GCM_SHA256"
365 );
366 }
367
368 #[cfg(all(feature = "crypto-ring", not(feature = "fips")))]
369 #[test]
370 fn ring_has_no_mlkem() {
371 let provider = default_provider();
372 let groups: Vec<_> = provider.kx_groups.iter().map(|g| g.name()).collect();
373 assert!(
374 !groups.contains(&NamedGroup::X25519MLKEM768),
375 "ring provider should not advertise X25519MLKEM768"
376 );
377 }
378
379 #[cfg(all(
380 feature = "crypto-openssl",
381 not(feature = "fips"),
382 not(feature = "crypto-ring"),
383 not(feature = "crypto-aws-lc-rs")
384 ))]
385 #[test]
386 fn openssl_pq_kx_depends_on_openssl_version() {
387 let provider = default_provider();
388 let groups: Vec<_> = provider.kx_groups.iter().map(|g| g.name()).collect();
389 let has_pq = groups.contains(&NamedGroup::X25519MLKEM768);
390 if has_pq {
393 println!("OpenSSL 3.5+ detected: X25519MLKEM768 is available");
394 } else {
395 println!("OpenSSL < 3.5: X25519MLKEM768 not available, classical groups only");
396 }
397 assert!(groups.contains(&NamedGroup::X25519));
399 assert!(groups.contains(&NamedGroup::secp256r1));
400 }
401
402 #[cfg(not(feature = "fips"))]
403 #[test]
404 fn kx_group_by_name_resolves_x25519() {
405 let group = kx_group_by_name("x25519").expect("x25519 should be supported");
406 assert_eq!(group.name(), NamedGroup::X25519);
407 }
408
409 #[cfg(not(feature = "fips"))]
410 #[test]
411 fn kx_group_by_name_resolves_x25519_uppercase() {
412 let group = kx_group_by_name("X25519").expect("X25519 should be supported");
413 assert_eq!(group.name(), NamedGroup::X25519);
414 }
415
416 #[cfg(feature = "fips")]
417 #[test]
418 fn kx_group_by_name_returns_none_for_x25519_in_fips() {
419 assert!(
420 kx_group_by_name("x25519").is_none(),
421 "X25519 is not FIPS-approved"
422 );
423 assert!(
424 kx_group_by_name("X25519").is_none(),
425 "X25519 is not FIPS-approved"
426 );
427 }
428
429 #[test]
430 fn kx_group_by_name_resolves_p256() {
431 let group = kx_group_by_name("P-256").expect("P-256 should be supported");
432 assert_eq!(group.name(), NamedGroup::secp256r1);
433 }
434
435 #[test]
436 fn kx_group_by_name_resolves_secp256r1() {
437 let group = kx_group_by_name("secp256r1").expect("secp256r1 should be supported");
438 assert_eq!(group.name(), NamedGroup::secp256r1);
439 }
440
441 #[test]
442 fn kx_group_by_name_resolves_p384() {
443 let group = kx_group_by_name("P-384").expect("P-384 should be supported");
444 assert_eq!(group.name(), NamedGroup::secp384r1);
445 }
446
447 #[test]
448 fn kx_group_by_name_returns_none_for_unknown() {
449 assert!(kx_group_by_name("P-521").is_none());
450 assert!(kx_group_by_name("unknown").is_none());
451 assert!(kx_group_by_name("").is_none());
452 }
453
454 #[cfg(all(feature = "crypto-aws-lc-rs", not(feature = "fips")))]
455 #[test]
456 fn kx_group_by_name_resolves_x25519mlkem768() {
457 let group = kx_group_by_name("X25519MLKEM768").expect("X25519MLKEM768 should be supported");
458 assert_eq!(group.name(), NamedGroup::X25519MLKEM768);
459 }
460
461 #[cfg(all(feature = "crypto-ring", not(feature = "fips")))]
462 #[test]
463 fn kx_group_by_name_returns_none_for_mlkem_on_ring() {
464 assert!(
465 kx_group_by_name("X25519MLKEM768").is_none(),
466 "ring does not support X25519MLKEM768"
467 );
468 }
469
470 #[test]
471 fn can_load_rsa_private_key() {
472 use rustls::pki_types::PrivateKeyDer;
473 use rustls::pki_types::pem::PemObject;
474
475 let key_pem = include_str!("../assets/key.pem");
476 let private_key =
477 PrivateKeyDer::from_pem_slice(key_pem.as_bytes()).expect("failed to parse PEM key");
478 any_supported_type(&private_key).expect("provider must be able to load RSA private key");
479 }
480
481 #[test]
482 fn can_build_server_config_with_tls13() {
483 use std::sync::Arc;
484
485 let provider = default_provider();
486 let config = rustls::ServerConfig::builder_with_provider(Arc::new(provider))
487 .with_protocol_versions(&[&rustls::version::TLS13])
488 .expect("failed to build TLS 1.3 config")
489 .with_no_client_auth()
490 .with_cert_resolver(Arc::new(crate::tls::MutexCertificateResolver::default()));
491 assert!(
492 !config.alpn_protocols.contains(&b"h2".to_vec()),
493 "default config should not have ALPN set"
494 );
495 }
496
497 #[test]
498 fn can_build_server_config_with_tls12_and_tls13() {
499 use std::sync::Arc;
500
501 let provider = default_provider();
502 rustls::ServerConfig::builder_with_provider(Arc::new(provider))
503 .with_protocol_versions(&[&rustls::version::TLS12, &rustls::version::TLS13])
504 .expect("failed to build TLS 1.2+1.3 config")
505 .with_no_client_auth()
506 .with_cert_resolver(Arc::new(crate::tls::MutexCertificateResolver::default()));
507 }
508
509 #[cfg(all(feature = "crypto-aws-lc-rs", not(feature = "fips")))]
510 #[test]
511 fn pq_kx_compatible_with_server_config() {
512 use std::sync::Arc;
513
514 let provider = default_provider();
515 let has_pq = provider
517 .kx_groups
518 .iter()
519 .any(|g| g.name() == NamedGroup::X25519MLKEM768);
520 assert!(has_pq, "X25519MLKEM768 must be in the provider");
521
522 let config = rustls::ServerConfig::builder_with_provider(Arc::new(provider))
524 .with_protocol_versions(&[&rustls::version::TLS13])
525 .expect("TLS 1.3 config with PQ kx should build successfully")
526 .with_no_client_auth()
527 .with_cert_resolver(Arc::new(crate::tls::MutexCertificateResolver::default()));
528
529 assert!(
531 !config.alpn_protocols.contains(&b"h2".to_vec()),
532 "default config should not have ALPN set"
533 );
534 }
535
536 #[test]
537 fn default_cipher_list_names_resolve_to_valid_suites() {
538 use sozu_command::config::DEFAULT_CIPHER_LIST;
539
540 let all_suites = [
541 ("TLS13_AES_256_GCM_SHA384", TLS13_AES_256_GCM_SHA384.suite()),
542 ("TLS13_AES_128_GCM_SHA256", TLS13_AES_128_GCM_SHA256.suite()),
543 (
544 "TLS13_CHACHA20_POLY1305_SHA256",
545 TLS13_CHACHA20_POLY1305_SHA256.suite(),
546 ),
547 (
548 "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
549 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384.suite(),
550 ),
551 (
552 "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
553 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256.suite(),
554 ),
555 (
556 "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
557 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256.suite(),
558 ),
559 (
560 "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
561 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384.suite(),
562 ),
563 (
564 "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
565 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256.suite(),
566 ),
567 (
568 "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
569 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256.suite(),
570 ),
571 ];
572
573 for name in DEFAULT_CIPHER_LIST {
575 let found = all_suites.iter().any(|(n, _)| *n == name);
576 assert!(
577 found,
578 "DEFAULT_CIPHER_LIST entry {name:?} does not match any known cipher suite"
579 );
580 }
581
582 assert_eq!(
584 DEFAULT_CIPHER_LIST.len(),
585 all_suites.len(),
586 "DEFAULT_CIPHER_LIST length should match number of known suites"
587 );
588 }
589
590 #[test]
591 fn cipher_suite_by_name_resolves_tls13() {
592 assert!(cipher_suite_by_name("TLS13_AES_256_GCM_SHA384").is_some());
593 assert!(cipher_suite_by_name("TLS13_AES_128_GCM_SHA256").is_some());
594 #[cfg(not(feature = "fips"))]
595 assert!(cipher_suite_by_name("TLS13_CHACHA20_POLY1305_SHA256").is_some());
596 }
597
598 #[test]
599 fn cipher_suite_by_name_resolves_tls12() {
600 assert!(cipher_suite_by_name("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384").is_some());
601 assert!(cipher_suite_by_name("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384").is_some());
602 }
603
604 #[test]
605 fn cipher_suite_by_name_returns_none_for_unknown() {
606 assert!(cipher_suite_by_name("UNKNOWN_CIPHER").is_none());
607 assert!(cipher_suite_by_name("").is_none());
608 assert!(cipher_suite_by_name("TLS_AES_256_GCM_SHA384").is_none());
610 }
611}