0.34.9
This commit is contained in:
1 parent
70388578cb
commit
2b7c67d755
22 files changed
+369
-59
No files matched your search
+64
-1
@@ -186,6 +186,7 @@ mod tests {
|
||||
tls12: true,
|
||||
tls13: false,
|
||||
cipher_suites: Vec::new(),
|
||||
post_quantum: false,
|
||||
},
|
||||
..Settings::default()
|
||||
};
|
||||
@@ -199,6 +200,7 @@ mod tests {
|
||||
tls12: false,
|
||||
tls13: false,
|
||||
cipher_suites: vec![],
|
||||
post_quantum: false,
|
||||
}
|
||||
.validate()
|
||||
.is_err());
|
||||
@@ -214,6 +216,7 @@ mod tests {
|
||||
tls12: true,
|
||||
tls13: false,
|
||||
cipher_suites: vec![tls13.clone()],
|
||||
post_quantum: false,
|
||||
}
|
||||
.validate()
|
||||
.is_err());
|
||||
@@ -223,6 +226,7 @@ mod tests {
|
||||
tls12: false,
|
||||
tls13: true,
|
||||
cipher_suites: vec![tls12.clone()],
|
||||
post_quantum: false,
|
||||
}
|
||||
.validate()
|
||||
.is_err());
|
||||
@@ -231,11 +235,13 @@ mod tests {
|
||||
tls12: true,
|
||||
tls13: false,
|
||||
cipher_suites: vec![],
|
||||
post_quantum: false,
|
||||
};
|
||||
assert!(ProtocolPolicy {
|
||||
tls12: true,
|
||||
tls13: false,
|
||||
cipher_suites: vec![]
|
||||
cipher_suites: vec![],
|
||||
post_quantum: false,
|
||||
}
|
||||
.validate_narrower_than(&global)
|
||||
.is_ok());
|
||||
@@ -243,6 +249,63 @@ mod tests {
|
||||
.validate_narrower_than(&global)
|
||||
.is_err());
|
||||
}
|
||||
#[test]
|
||||
fn pqc_is_opt_in_and_not_a_cipher_suite() {
|
||||
let legacy: ProtocolPolicy = serde_json::from_value(serde_json::json!({
|
||||
"tls12": true, "tls13": true, "cipher_suites": []
|
||||
}))
|
||||
.unwrap();
|
||||
assert!(!legacy.post_quantum);
|
||||
assert!(serde_json::to_value(&legacy)
|
||||
.unwrap()
|
||||
.get("post_quantum")
|
||||
.is_none());
|
||||
let mut pqc = legacy.clone();
|
||||
pqc.post_quantum = true;
|
||||
assert!(pqc.validate().is_ok());
|
||||
let groups = pqc.key_exchange_groups();
|
||||
assert_eq!(groups.first().map(String::as_str), Some("X25519MLKEM768"));
|
||||
assert!(groups.iter().any(|name| name == "X25519"));
|
||||
assert!(!legacy
|
||||
.key_exchange_groups()
|
||||
.contains(&"X25519MLKEM768".into()));
|
||||
assert!(!pqc
|
||||
.effective_cipher_suites()
|
||||
.iter()
|
||||
.any(|suite| suite.contains("MLKEM")));
|
||||
assert!(provider_for(&pqc).unwrap().kx_groups.len() > 1);
|
||||
assert!(serde_json::to_value(&pqc).unwrap()["post_quantum"] == true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pqc_policy_requires_tls13_and_global_permission() {
|
||||
let legacy = ProtocolPolicy::default();
|
||||
let mut pqc = legacy.clone();
|
||||
pqc.post_quantum = true;
|
||||
assert!(pqc.validate_narrower_than(&legacy).is_err());
|
||||
assert!(legacy.validate_narrower_than(&pqc).is_ok());
|
||||
pqc.tls13 = false;
|
||||
assert!(pqc.validate().is_err());
|
||||
pqc.tls13 = true;
|
||||
pqc.cipher_suites = vec!["X25519MLKEM768".into()];
|
||||
assert!(pqc.validate().is_err()); // KEX name is not a cipher suite
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pqc_builds_both_tcp_tls_and_quic_configs() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
initialize_default(dir.path()).unwrap();
|
||||
let mut settings = Settings::default();
|
||||
settings.policy.post_quantum = true;
|
||||
let tls = compile(&settings, Some(dir.path())).unwrap().unwrap();
|
||||
assert!(tls.alpn_protocols.contains(&b"h2".to_vec()));
|
||||
let h3 = crate::http3::crypto(&tls, &settings.policy);
|
||||
assert!(h3.is_ok(), "hybrid HTTP/3 TLS config must build");
|
||||
let mut tls12_only = settings.policy.clone();
|
||||
tls12_only.tls13 = false;
|
||||
assert!(crate::http3::crypto(&tls, &tls12_only).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn administration_tls_supports_default_and_selected_certificate() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
|
||||
+54
-5
@@ -52,6 +52,12 @@ pub struct ProtocolPolicy {
|
||||
pub tls12: bool,
|
||||
pub tls13: bool,
|
||||
pub cipher_suites: Vec<String>,
|
||||
#[serde(default, skip_serializing_if = "is_false")]
|
||||
pub post_quantum: bool,
|
||||
}
|
||||
|
||||
fn is_false(enabled: &bool) -> bool {
|
||||
!*enabled
|
||||
}
|
||||
impl Default for ProtocolPolicy {
|
||||
fn default() -> Self {
|
||||
@@ -59,6 +65,7 @@ impl Default for ProtocolPolicy {
|
||||
tls12: true,
|
||||
tls13: true,
|
||||
cipher_suites: Vec::new(),
|
||||
post_quantum: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,11 +75,15 @@ impl ProtocolPolicy {
|
||||
self.tls12 || self.tls13,
|
||||
"at least one TLS protocol version must be enabled"
|
||||
);
|
||||
ensure!(
|
||||
!self.post_quantum || self.tls13,
|
||||
"post-quantum key exchange requires TLS 1.3"
|
||||
);
|
||||
ensure!(
|
||||
self.cipher_suites.len() <= 16,
|
||||
"at most 16 TLS cipher suites may be selected"
|
||||
);
|
||||
let available = cipher_suite_names();
|
||||
let available = cipher_suite_names_for(self.post_quantum);
|
||||
let mut seen = HashSet::new();
|
||||
for suite in &self.cipher_suites {
|
||||
ensure!(
|
||||
@@ -97,7 +108,7 @@ impl ProtocolPolicy {
|
||||
}
|
||||
pub fn effective_cipher_suites(&self) -> Vec<String> {
|
||||
let selected = if self.cipher_suites.is_empty() {
|
||||
cipher_suite_names()
|
||||
cipher_suite_names_for(self.post_quantum)
|
||||
} else {
|
||||
self.cipher_suites.clone()
|
||||
};
|
||||
@@ -120,6 +131,10 @@ impl ProtocolPolicy {
|
||||
pub fn validate_narrower_than(&self, global: &Self) -> Result<()> {
|
||||
self.validate()?;
|
||||
global.validate()?;
|
||||
ensure!(
|
||||
!self.post_quantum || global.post_quantum,
|
||||
"per-vhost post-quantum key exchange must be enabled globally"
|
||||
);
|
||||
ensure!(
|
||||
!self.tls12 || global.tls12,
|
||||
"per-vhost TLS policy cannot enable TLS 1.2 when it is disabled globally"
|
||||
@@ -144,17 +159,51 @@ impl ProtocolPolicy {
|
||||
}
|
||||
}
|
||||
|
||||
fn base_provider(post_quantum: bool) -> rustls::crypto::CryptoProvider {
|
||||
if !post_quantum {
|
||||
return rustls::crypto::ring::default_provider();
|
||||
}
|
||||
let mut provider = rustls::crypto::aws_lc_rs::default_provider();
|
||||
let hybrid = rustls::crypto::aws_lc_rs::kx_group::X25519MLKEM768;
|
||||
provider
|
||||
.kx_groups
|
||||
.retain(|group| group.name() != hybrid.name());
|
||||
provider.kx_groups.insert(0, hybrid);
|
||||
provider
|
||||
}
|
||||
|
||||
pub fn cipher_suite_names() -> Vec<String> {
|
||||
rustls::crypto::ring::default_provider()
|
||||
base_provider(false)
|
||||
.cipher_suites
|
||||
.iter()
|
||||
.map(|suite| format!("{:?}", suite.suite()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn provider_for(policy: &ProtocolPolicy) -> Result<rustls::crypto::CryptoProvider> {
|
||||
pub fn cipher_suite_names_for(post_quantum: bool) -> Vec<String> {
|
||||
if !post_quantum {
|
||||
return cipher_suite_names();
|
||||
}
|
||||
base_provider(post_quantum)
|
||||
.cipher_suites
|
||||
.iter()
|
||||
.map(|suite| format!("{:?}", suite.suite()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
impl ProtocolPolicy {
|
||||
pub fn key_exchange_groups(&self) -> Vec<String> {
|
||||
base_provider(self.post_quantum)
|
||||
.kx_groups
|
||||
.iter()
|
||||
.map(|group| format!("{:?}", group.name()))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn provider_for(policy: &ProtocolPolicy) -> Result<rustls::crypto::CryptoProvider> {
|
||||
policy.validate()?;
|
||||
let mut provider = rustls::crypto::ring::default_provider();
|
||||
let mut provider = base_provider(policy.post_quantum);
|
||||
if !policy.cipher_suites.is_empty() {
|
||||
provider.cipher_suites.retain(|suite| {
|
||||
let name = format!("{:?}", suite.suite());
|
||||
|
||||
Reference in new issue
Block a user