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 let provider = &*DEFAULT_PROVIDER;
128 let named_group = match name {
129 "x25519" | "X25519" => rustls::NamedGroup::X25519,
130 "secp256r1" | "P-256" => rustls::NamedGroup::secp256r1,
131 "secp384r1" | "P-384" => rustls::NamedGroup::secp384r1,
132 "X25519MLKEM768" => rustls::NamedGroup::X25519MLKEM768,
133 _ => return None,
134 };
135 provider
136 .kx_groups
137 .iter()
138 .find(|g| g.name() == named_group)
139 .copied()
140}
141
142pub fn cipher_suite_by_name(name: &str) -> Option<rustls::SupportedCipherSuite> {
157 let candidate = match name {
158 "TLS13_AES_256_GCM_SHA384" => Some(TLS13_AES_256_GCM_SHA384),
159 "TLS13_AES_128_GCM_SHA256" => Some(TLS13_AES_128_GCM_SHA256),
160 "TLS13_CHACHA20_POLY1305_SHA256" => Some(TLS13_CHACHA20_POLY1305_SHA256),
161 "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" => Some(TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384),
162 "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" => Some(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256),
163 "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256" => {
164 Some(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256)
165 }
166 "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" => Some(TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384),
167 "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" => Some(TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256),
168 "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" => {
169 Some(TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256)
170 }
171 _ => None,
172 }?;
173
174 let wanted = candidate.suite();
178 DEFAULT_PROVIDER
179 .cipher_suites
180 .iter()
181 .find(|s| s.suite() == wanted)
182 .copied()
183}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188 use rustls::NamedGroup;
189
190 #[test]
191 fn default_provider_has_tls13_cipher_suites() {
192 let provider = default_provider();
193 let names: Vec<_> = provider.cipher_suites.iter().map(|cs| cs.suite()).collect();
194 assert!(
195 names.contains(&rustls::CipherSuite::TLS13_AES_256_GCM_SHA384),
196 "provider must support TLS13_AES_256_GCM_SHA384"
197 );
198 assert!(
199 names.contains(&rustls::CipherSuite::TLS13_AES_128_GCM_SHA256),
200 "provider must support TLS13_AES_128_GCM_SHA256"
201 );
202 #[cfg(not(feature = "fips"))]
204 assert!(
205 names.contains(&rustls::CipherSuite::TLS13_CHACHA20_POLY1305_SHA256),
206 "provider must support TLS13_CHACHA20_POLY1305_SHA256"
207 );
208 }
209
210 #[test]
211 fn default_provider_has_tls12_cipher_suites() {
212 let provider = default_provider();
213 let names: Vec<_> = provider.cipher_suites.iter().map(|cs| cs.suite()).collect();
214 assert!(
215 names.contains(&rustls::CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384),
216 "provider must support TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
217 );
218 assert!(
219 names.contains(&rustls::CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384),
220 "provider must support TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"
221 );
222 }
223
224 #[test]
225 fn default_provider_has_classical_kx_groups() {
226 let provider = default_provider();
227 let groups: Vec<_> = provider.kx_groups.iter().map(|g| g.name()).collect();
228 #[cfg(not(feature = "fips"))]
230 assert!(
231 groups.contains(&NamedGroup::X25519),
232 "provider must support X25519 key exchange"
233 );
234 assert!(
235 groups.contains(&NamedGroup::secp256r1),
236 "provider must support secp256r1 key exchange"
237 );
238 assert!(
239 groups.contains(&NamedGroup::secp384r1),
240 "provider must support secp384r1 key exchange"
241 );
242 }
243
244 #[cfg(all(feature = "crypto-aws-lc-rs", not(feature = "fips")))]
245 #[test]
246 fn aws_lc_rs_supports_post_quantum_kx() {
247 let provider = default_provider();
248 let groups: Vec<_> = provider.kx_groups.iter().map(|g| g.name()).collect();
249 assert!(
250 groups.contains(&NamedGroup::X25519MLKEM768),
251 "aws-lc-rs provider must support X25519MLKEM768 post-quantum key exchange"
252 );
253 }
254
255 #[cfg(all(feature = "crypto-aws-lc-rs", not(feature = "fips")))]
256 #[test]
257 fn aws_lc_rs_has_more_kx_groups_than_classical() {
258 let provider = default_provider();
259 assert!(
262 provider.kx_groups.len() > 3,
263 "aws-lc-rs should have more than 3 kx groups (has {}), including post-quantum",
264 provider.kx_groups.len()
265 );
266 }
267
268 #[cfg(feature = "fips")]
269 #[test]
270 fn fips_provider_has_nist_kx_groups() {
271 let provider = default_provider();
272 let groups: Vec<_> = provider.kx_groups.iter().map(|g| g.name()).collect();
273 assert!(
274 groups.contains(&NamedGroup::secp256r1),
275 "FIPS provider must support secp256r1"
276 );
277 assert!(
278 groups.contains(&NamedGroup::secp384r1),
279 "FIPS provider must support secp384r1"
280 );
281 }
282
283 #[cfg(feature = "fips")]
284 #[test]
285 fn fips_provider_has_aes_gcm_cipher_suites() {
286 let provider = default_provider();
287 let suites: Vec<_> = provider.cipher_suites.iter().map(|cs| cs.suite()).collect();
288 assert!(
289 suites.contains(&rustls::CipherSuite::TLS13_AES_256_GCM_SHA384),
290 "FIPS provider must support TLS13_AES_256_GCM_SHA384"
291 );
292 assert!(
293 suites.contains(&rustls::CipherSuite::TLS13_AES_128_GCM_SHA256),
294 "FIPS provider must support TLS13_AES_128_GCM_SHA256"
295 );
296 }
297
298 #[cfg(all(feature = "crypto-ring", not(feature = "fips")))]
299 #[test]
300 fn ring_has_no_mlkem() {
301 let provider = default_provider();
302 let groups: Vec<_> = provider.kx_groups.iter().map(|g| g.name()).collect();
303 assert!(
304 !groups.contains(&NamedGroup::X25519MLKEM768),
305 "ring provider should not advertise X25519MLKEM768"
306 );
307 }
308
309 #[cfg(all(
310 feature = "crypto-openssl",
311 not(feature = "fips"),
312 not(feature = "crypto-ring"),
313 not(feature = "crypto-aws-lc-rs")
314 ))]
315 #[test]
316 fn openssl_pq_kx_depends_on_openssl_version() {
317 let provider = default_provider();
318 let groups: Vec<_> = provider.kx_groups.iter().map(|g| g.name()).collect();
319 let has_pq = groups.contains(&NamedGroup::X25519MLKEM768);
320 if has_pq {
323 println!("OpenSSL 3.5+ detected: X25519MLKEM768 is available");
324 } else {
325 println!("OpenSSL < 3.5: X25519MLKEM768 not available, classical groups only");
326 }
327 assert!(groups.contains(&NamedGroup::X25519));
329 assert!(groups.contains(&NamedGroup::secp256r1));
330 }
331
332 #[cfg(not(feature = "fips"))]
333 #[test]
334 fn kx_group_by_name_resolves_x25519() {
335 let group = kx_group_by_name("x25519").expect("x25519 should be supported");
336 assert_eq!(group.name(), NamedGroup::X25519);
337 }
338
339 #[cfg(not(feature = "fips"))]
340 #[test]
341 fn kx_group_by_name_resolves_x25519_uppercase() {
342 let group = kx_group_by_name("X25519").expect("X25519 should be supported");
343 assert_eq!(group.name(), NamedGroup::X25519);
344 }
345
346 #[cfg(feature = "fips")]
347 #[test]
348 fn kx_group_by_name_returns_none_for_x25519_in_fips() {
349 assert!(
350 kx_group_by_name("x25519").is_none(),
351 "X25519 is not FIPS-approved"
352 );
353 assert!(
354 kx_group_by_name("X25519").is_none(),
355 "X25519 is not FIPS-approved"
356 );
357 }
358
359 #[test]
360 fn kx_group_by_name_resolves_p256() {
361 let group = kx_group_by_name("P-256").expect("P-256 should be supported");
362 assert_eq!(group.name(), NamedGroup::secp256r1);
363 }
364
365 #[test]
366 fn kx_group_by_name_resolves_secp256r1() {
367 let group = kx_group_by_name("secp256r1").expect("secp256r1 should be supported");
368 assert_eq!(group.name(), NamedGroup::secp256r1);
369 }
370
371 #[test]
372 fn kx_group_by_name_resolves_p384() {
373 let group = kx_group_by_name("P-384").expect("P-384 should be supported");
374 assert_eq!(group.name(), NamedGroup::secp384r1);
375 }
376
377 #[test]
378 fn kx_group_by_name_returns_none_for_unknown() {
379 assert!(kx_group_by_name("P-521").is_none());
380 assert!(kx_group_by_name("unknown").is_none());
381 assert!(kx_group_by_name("").is_none());
382 }
383
384 #[cfg(all(feature = "crypto-aws-lc-rs", not(feature = "fips")))]
385 #[test]
386 fn kx_group_by_name_resolves_x25519mlkem768() {
387 let group = kx_group_by_name("X25519MLKEM768").expect("X25519MLKEM768 should be supported");
388 assert_eq!(group.name(), NamedGroup::X25519MLKEM768);
389 }
390
391 #[cfg(all(feature = "crypto-ring", not(feature = "fips")))]
392 #[test]
393 fn kx_group_by_name_returns_none_for_mlkem_on_ring() {
394 assert!(
395 kx_group_by_name("X25519MLKEM768").is_none(),
396 "ring does not support X25519MLKEM768"
397 );
398 }
399
400 #[test]
401 fn can_load_rsa_private_key() {
402 use rustls::pki_types::PrivateKeyDer;
403 use rustls::pki_types::pem::PemObject;
404
405 let key_pem = include_str!("../assets/key.pem");
406 let private_key =
407 PrivateKeyDer::from_pem_slice(key_pem.as_bytes()).expect("failed to parse PEM key");
408 any_supported_type(&private_key).expect("provider must be able to load RSA private key");
409 }
410
411 #[test]
412 fn can_build_server_config_with_tls13() {
413 use std::sync::Arc;
414
415 let provider = default_provider();
416 let config = rustls::ServerConfig::builder_with_provider(Arc::new(provider))
417 .with_protocol_versions(&[&rustls::version::TLS13])
418 .expect("failed to build TLS 1.3 config")
419 .with_no_client_auth()
420 .with_cert_resolver(Arc::new(crate::tls::MutexCertificateResolver::default()));
421 assert!(
422 !config.alpn_protocols.contains(&b"h2".to_vec()),
423 "default config should not have ALPN set"
424 );
425 }
426
427 #[test]
428 fn can_build_server_config_with_tls12_and_tls13() {
429 use std::sync::Arc;
430
431 let provider = default_provider();
432 rustls::ServerConfig::builder_with_provider(Arc::new(provider))
433 .with_protocol_versions(&[&rustls::version::TLS12, &rustls::version::TLS13])
434 .expect("failed to build TLS 1.2+1.3 config")
435 .with_no_client_auth()
436 .with_cert_resolver(Arc::new(crate::tls::MutexCertificateResolver::default()));
437 }
438
439 #[cfg(all(feature = "crypto-aws-lc-rs", not(feature = "fips")))]
440 #[test]
441 fn pq_kx_compatible_with_server_config() {
442 use std::sync::Arc;
443
444 let provider = default_provider();
445 let has_pq = provider
447 .kx_groups
448 .iter()
449 .any(|g| g.name() == NamedGroup::X25519MLKEM768);
450 assert!(has_pq, "X25519MLKEM768 must be in the provider");
451
452 let config = rustls::ServerConfig::builder_with_provider(Arc::new(provider))
454 .with_protocol_versions(&[&rustls::version::TLS13])
455 .expect("TLS 1.3 config with PQ kx should build successfully")
456 .with_no_client_auth()
457 .with_cert_resolver(Arc::new(crate::tls::MutexCertificateResolver::default()));
458
459 assert!(
461 !config.alpn_protocols.contains(&b"h2".to_vec()),
462 "default config should not have ALPN set"
463 );
464 }
465
466 #[test]
467 fn default_cipher_list_names_resolve_to_valid_suites() {
468 use sozu_command::config::DEFAULT_CIPHER_LIST;
469
470 let all_suites = [
471 ("TLS13_AES_256_GCM_SHA384", TLS13_AES_256_GCM_SHA384.suite()),
472 ("TLS13_AES_128_GCM_SHA256", TLS13_AES_128_GCM_SHA256.suite()),
473 (
474 "TLS13_CHACHA20_POLY1305_SHA256",
475 TLS13_CHACHA20_POLY1305_SHA256.suite(),
476 ),
477 (
478 "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
479 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384.suite(),
480 ),
481 (
482 "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
483 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256.suite(),
484 ),
485 (
486 "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
487 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256.suite(),
488 ),
489 (
490 "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
491 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384.suite(),
492 ),
493 (
494 "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
495 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256.suite(),
496 ),
497 (
498 "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
499 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256.suite(),
500 ),
501 ];
502
503 for name in DEFAULT_CIPHER_LIST {
505 let found = all_suites.iter().any(|(n, _)| *n == name);
506 assert!(
507 found,
508 "DEFAULT_CIPHER_LIST entry {name:?} does not match any known cipher suite"
509 );
510 }
511
512 assert_eq!(
514 DEFAULT_CIPHER_LIST.len(),
515 all_suites.len(),
516 "DEFAULT_CIPHER_LIST length should match number of known suites"
517 );
518 }
519
520 #[test]
521 fn cipher_suite_by_name_resolves_tls13() {
522 assert!(cipher_suite_by_name("TLS13_AES_256_GCM_SHA384").is_some());
523 assert!(cipher_suite_by_name("TLS13_AES_128_GCM_SHA256").is_some());
524 #[cfg(not(feature = "fips"))]
525 assert!(cipher_suite_by_name("TLS13_CHACHA20_POLY1305_SHA256").is_some());
526 }
527
528 #[test]
529 fn cipher_suite_by_name_resolves_tls12() {
530 assert!(cipher_suite_by_name("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384").is_some());
531 assert!(cipher_suite_by_name("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384").is_some());
532 }
533
534 #[test]
535 fn cipher_suite_by_name_returns_none_for_unknown() {
536 assert!(cipher_suite_by_name("UNKNOWN_CIPHER").is_none());
537 assert!(cipher_suite_by_name("").is_none());
538 assert!(cipher_suite_by_name("TLS_AES_256_GCM_SHA384").is_none());
540 }
541}