mirror of
https://github.com/telemt/telemt.git
synced 2026-06-19 02:00:08 +07:00
Hardened TLS-F ServerHello selection
Co-Authored-By: brekotis <93345790+brekotis@users.noreply.github.com>
This commit is contained in:
@@ -1457,7 +1457,6 @@ fn emulated_server_hello_never_places_alpn_in_server_hello_extensions() {
|
|||||||
true,
|
true,
|
||||||
ClientHelloTlsVersion::Tls13,
|
ClientHelloTlsVersion::Tls13,
|
||||||
[0x13, 0x01],
|
[0x13, 0x01],
|
||||||
TLS_NAMED_GROUP_X25519MLKEM768,
|
|
||||||
&rng,
|
&rng,
|
||||||
Some(b"h2".to_vec()),
|
Some(b"h2".to_vec()),
|
||||||
0,
|
0,
|
||||||
@@ -1467,14 +1466,21 @@ fn emulated_server_hello_never_places_alpn_in_server_hello_extensions() {
|
|||||||
!exts.contains(&0x0010),
|
!exts.contains(&0x0010),
|
||||||
"ALPN extension must not appear in emulated ServerHello"
|
"ALPN extension must not appear in emulated ServerHello"
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
server_hello_key_share(&response),
|
||||||
|
Some((
|
||||||
|
TLS_NAMED_GROUP_X25519MLKEM768,
|
||||||
|
X25519MLKEM768_SERVER_KEY_SHARE_LEN
|
||||||
|
))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tls_extension_builder() {
|
fn test_tls_extension_builder() {
|
||||||
let key = [0x42u8; 32];
|
let key = vec![0x42u8; X25519MLKEM768_SERVER_KEY_SHARE_LEN];
|
||||||
|
|
||||||
let mut builder = TlsExtensionBuilder::new();
|
let mut builder = TlsExtensionBuilder::new();
|
||||||
builder.add_key_share(TLS_NAMED_GROUP_X25519, &key);
|
builder.add_key_share(TLS_NAMED_GROUP_X25519MLKEM768, &key);
|
||||||
builder.add_supported_versions(0x0304);
|
builder.add_supported_versions(0x0304);
|
||||||
|
|
||||||
let result = builder.build();
|
let result = builder.build();
|
||||||
@@ -1487,10 +1493,10 @@ fn test_tls_extension_builder() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_server_hello_builder() {
|
fn test_server_hello_builder() {
|
||||||
let session_id = vec![0x01, 0x02, 0x03, 0x04];
|
let session_id = vec![0x01, 0x02, 0x03, 0x04];
|
||||||
let key = [0x55u8; 32];
|
let key = vec![0x55u8; X25519MLKEM768_SERVER_KEY_SHARE_LEN];
|
||||||
|
|
||||||
let builder = ServerHelloBuilder::new(session_id.clone())
|
let builder = ServerHelloBuilder::new(session_id.clone())
|
||||||
.with_key_share(TLS_NAMED_GROUP_X25519, &key)
|
.with_key_share(TLS_NAMED_GROUP_X25519MLKEM768, &key)
|
||||||
.with_tls13_version();
|
.with_tls13_version();
|
||||||
|
|
||||||
let record = builder.build_record();
|
let record = builder.build_record();
|
||||||
@@ -1535,7 +1541,7 @@ fn test_build_server_hello_structure() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_build_server_hello_with_cipher_can_keep_x25519_key_share() {
|
fn test_build_server_hello_with_cipher_always_uses_hybrid_key_share() {
|
||||||
let secret = b"test secret";
|
let secret = b"test secret";
|
||||||
let client_digest = [0x42u8; 32];
|
let client_digest = [0x42u8; 32];
|
||||||
let session_id = vec![0xAA; 32];
|
let session_id = vec![0xAA; 32];
|
||||||
@@ -1548,14 +1554,16 @@ fn test_build_server_hello_with_cipher_can_keep_x25519_key_share() {
|
|||||||
2048,
|
2048,
|
||||||
&rng,
|
&rng,
|
||||||
[0x13, 0x01],
|
[0x13, 0x01],
|
||||||
TLS_NAMED_GROUP_X25519,
|
|
||||||
None,
|
None,
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
server_hello_key_share(&response),
|
server_hello_key_share(&response),
|
||||||
Some((TLS_NAMED_GROUP_X25519, X25519_KEY_SHARE_LEN))
|
Some((
|
||||||
|
TLS_NAMED_GROUP_X25519MLKEM768,
|
||||||
|
X25519MLKEM768_SERVER_KEY_SHARE_LEN
|
||||||
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1579,10 +1587,10 @@ fn test_build_server_hello_digest() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_server_hello_extensions_length() {
|
fn test_server_hello_extensions_length() {
|
||||||
let session_id = vec![0x01; 32];
|
let session_id = vec![0x01; 32];
|
||||||
let key = [0x55u8; 32];
|
let key = vec![0x55u8; X25519MLKEM768_SERVER_KEY_SHARE_LEN];
|
||||||
|
|
||||||
let builder = ServerHelloBuilder::new(session_id)
|
let builder = ServerHelloBuilder::new(session_id)
|
||||||
.with_key_share(TLS_NAMED_GROUP_X25519, &key)
|
.with_key_share(TLS_NAMED_GROUP_X25519MLKEM768, &key)
|
||||||
.with_tls13_version();
|
.with_tls13_version();
|
||||||
|
|
||||||
let record = builder.build_record();
|
let record = builder.build_record();
|
||||||
@@ -1796,7 +1804,7 @@ fn select_server_hello_cipher_suite_keeps_profile_cipher_when_offered() {
|
|||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
select_server_hello_cipher_suite(&ch, [0x13, 0x03]),
|
select_server_hello_cipher_suite(&ch, [0x13, 0x03]),
|
||||||
[0x13, 0x03]
|
Some([0x13, 0x03])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1809,7 +1817,16 @@ fn select_server_hello_cipher_suite_ignores_profile_tls12_cipher() {
|
|||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
select_server_hello_cipher_suite(&ch, [0xc0, 0x2f]),
|
select_server_hello_cipher_suite(&ch, [0xc0, 0x2f]),
|
||||||
[0x13, 0x03]
|
Some([0x13, 0x03])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn select_server_hello_cipher_suite_rejects_without_offered_tls13_suite() {
|
||||||
|
let ch = build_client_hello_with_ciphers_and_exts(&[[0xc0, 0x2f]], Vec::new(), "example.com");
|
||||||
|
assert_eq!(
|
||||||
|
select_server_hello_cipher_suite(&ch, [0x13, 0x01]),
|
||||||
|
None
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1818,18 +1835,18 @@ fn select_server_hello_cipher_suite_falls_back_to_offered_tls13_suite() {
|
|||||||
let ch = build_client_hello_with_ciphers_and_exts(&[[0x13, 0x03]], Vec::new(), "example.com");
|
let ch = build_client_hello_with_ciphers_and_exts(&[[0x13, 0x03]], Vec::new(), "example.com");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
select_server_hello_cipher_suite(&ch, [0x13, 0x01]),
|
select_server_hello_cipher_suite(&ch, [0x13, 0x01]),
|
||||||
[0x13, 0x03]
|
Some([0x13, 0x03])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn select_server_hello_cipher_suite_keeps_preferred_for_malformed_clienthello() {
|
fn select_server_hello_cipher_suite_rejects_malformed_clienthello() {
|
||||||
let mut ch =
|
let mut ch =
|
||||||
build_client_hello_with_ciphers_and_exts(&[[0x13, 0x03]], Vec::new(), "example.com");
|
build_client_hello_with_ciphers_and_exts(&[[0x13, 0x03]], Vec::new(), "example.com");
|
||||||
ch.truncate(12);
|
ch.truncate(12);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
select_server_hello_cipher_suite(&ch, [0x13, 0x01]),
|
select_server_hello_cipher_suite(&ch, [0x13, 0x01]),
|
||||||
[0x13, 0x01]
|
None
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1847,38 +1864,32 @@ fn select_server_hello_key_share_group_prefers_hybrid_when_valid_share_is_offere
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
select_server_hello_key_share_group(&ch),
|
select_server_hello_key_share_group(&ch),
|
||||||
TLS_NAMED_GROUP_X25519MLKEM768
|
Some(TLS_NAMED_GROUP_X25519MLKEM768)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn select_server_hello_key_share_group_falls_back_without_hybrid_share() {
|
fn select_server_hello_key_share_group_rejects_without_hybrid_share() {
|
||||||
let key_share =
|
let key_share =
|
||||||
client_key_share_extension(&[(TLS_NAMED_GROUP_X25519, X25519_KEY_SHARE_LEN)]);
|
client_key_share_extension(&[(TLS_NAMED_GROUP_X25519, X25519_KEY_SHARE_LEN)]);
|
||||||
let ch = build_client_hello_with_exts(vec![(0x0033, key_share)], "example.com");
|
let ch = build_client_hello_with_exts(vec![(0x0033, key_share)], "example.com");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(select_server_hello_key_share_group(&ch), None);
|
||||||
select_server_hello_key_share_group(&ch),
|
|
||||||
TLS_NAMED_GROUP_X25519
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn select_server_hello_key_share_group_falls_back_for_malformed_hybrid_len() {
|
fn select_server_hello_key_share_group_rejects_malformed_hybrid_len() {
|
||||||
let key_share = client_key_share_extension(&[(
|
let key_share = client_key_share_extension(&[(
|
||||||
TLS_NAMED_GROUP_X25519MLKEM768,
|
TLS_NAMED_GROUP_X25519MLKEM768,
|
||||||
X25519MLKEM768_CLIENT_KEY_SHARE_LEN - 1,
|
X25519MLKEM768_CLIENT_KEY_SHARE_LEN - 1,
|
||||||
)]);
|
)]);
|
||||||
let ch = build_client_hello_with_exts(vec![(0x0033, key_share)], "example.com");
|
let ch = build_client_hello_with_exts(vec![(0x0033, key_share)], "example.com");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(select_server_hello_key_share_group(&ch), None);
|
||||||
select_server_hello_key_share_group(&ch),
|
|
||||||
TLS_NAMED_GROUP_X25519
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn select_server_hello_key_share_group_falls_back_for_malformed_key_share_tail() {
|
fn select_server_hello_key_share_group_rejects_malformed_key_share_tail() {
|
||||||
let mut key_share = client_key_share_extension(&[(
|
let mut key_share = client_key_share_extension(&[(
|
||||||
TLS_NAMED_GROUP_X25519MLKEM768,
|
TLS_NAMED_GROUP_X25519MLKEM768,
|
||||||
X25519MLKEM768_CLIENT_KEY_SHARE_LEN,
|
X25519MLKEM768_CLIENT_KEY_SHARE_LEN,
|
||||||
@@ -1888,10 +1899,7 @@ fn select_server_hello_key_share_group_falls_back_for_malformed_key_share_tail()
|
|||||||
key_share.push(0);
|
key_share.push(0);
|
||||||
let ch = build_client_hello_with_exts(vec![(0x0033, key_share)], "example.com");
|
let ch = build_client_hello_with_exts(vec![(0x0033, key_share)], "example.com");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(select_server_hello_key_share_group(&ch), None);
|
||||||
select_server_hello_key_share_group(&ch),
|
|
||||||
TLS_NAMED_GROUP_X25519
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
+22
-30
@@ -561,7 +561,6 @@ pub fn build_server_hello(
|
|||||||
fake_cert_len,
|
fake_cert_len,
|
||||||
rng,
|
rng,
|
||||||
cipher_suite::TLS_AES_128_GCM_SHA256,
|
cipher_suite::TLS_AES_128_GCM_SHA256,
|
||||||
TLS_NAMED_GROUP_X25519MLKEM768,
|
|
||||||
alpn,
|
alpn,
|
||||||
new_session_tickets,
|
new_session_tickets,
|
||||||
)
|
)
|
||||||
@@ -579,7 +578,6 @@ pub(crate) fn build_server_hello_with_cipher(
|
|||||||
fake_cert_len: usize,
|
fake_cert_len: usize,
|
||||||
rng: &SecureRandom,
|
rng: &SecureRandom,
|
||||||
selected_cipher_suite: [u8; 2],
|
selected_cipher_suite: [u8; 2],
|
||||||
selected_key_share_group: u16,
|
|
||||||
alpn: Option<Vec<u8>>,
|
alpn: Option<Vec<u8>>,
|
||||||
new_session_tickets: u8,
|
new_session_tickets: u8,
|
||||||
) -> Vec<u8> {
|
) -> Vec<u8> {
|
||||||
@@ -588,21 +586,12 @@ pub(crate) fn build_server_hello_with_cipher(
|
|||||||
let fake_cert_len = fake_cert_len.clamp(MIN_APP_DATA, MAX_APP_DATA);
|
let fake_cert_len = fake_cert_len.clamp(MIN_APP_DATA, MAX_APP_DATA);
|
||||||
|
|
||||||
// Build ServerHello
|
// Build ServerHello
|
||||||
let server_hello = if selected_key_share_group == TLS_NAMED_GROUP_X25519MLKEM768 {
|
let key_share = gen_fake_x25519mlkem768_server_key_share(rng);
|
||||||
let key_share = gen_fake_x25519mlkem768_server_key_share(rng);
|
let server_hello = ServerHelloBuilder::new(session_id.to_vec())
|
||||||
ServerHelloBuilder::new(session_id.to_vec())
|
.with_cipher_suite(selected_cipher_suite)
|
||||||
.with_cipher_suite(selected_cipher_suite)
|
.with_key_share(TLS_NAMED_GROUP_X25519MLKEM768, &key_share)
|
||||||
.with_key_share(TLS_NAMED_GROUP_X25519MLKEM768, &key_share)
|
.with_tls13_version()
|
||||||
.with_tls13_version()
|
.build_record();
|
||||||
.build_record()
|
|
||||||
} else {
|
|
||||||
let key_share = gen_fake_x25519_key(rng);
|
|
||||||
ServerHelloBuilder::new(session_id.to_vec())
|
|
||||||
.with_cipher_suite(selected_cipher_suite)
|
|
||||||
.with_key_share(TLS_NAMED_GROUP_X25519, &key_share)
|
|
||||||
.with_tls13_version()
|
|
||||||
.build_record()
|
|
||||||
};
|
|
||||||
|
|
||||||
// Build Change Cipher Spec record
|
// Build Change Cipher Spec record
|
||||||
let change_cipher_spec = [
|
let change_cipher_spec = [
|
||||||
@@ -1201,20 +1190,23 @@ fn is_tls13_cipher_suite(suite: [u8; 2]) -> bool {
|
|||||||
/// Select the ServerHello cipher suite from the already-received ClientHello.
|
/// Select the ServerHello cipher suite from the already-received ClientHello.
|
||||||
///
|
///
|
||||||
/// This is intentionally a borrowed, zero-allocation scan. It runs only for an
|
/// This is intentionally a borrowed, zero-allocation scan. It runs only for an
|
||||||
/// authenticated success response and keeps malformed or unexpected ClientHello
|
/// authenticated success response and fails closed for malformed or unsupported
|
||||||
/// shapes on the previous fallback behavior.
|
/// ClientHello shapes that cannot produce a DPI-consistent ServerHello.
|
||||||
pub(crate) fn select_server_hello_cipher_suite(handshake: &[u8], preferred: [u8; 2]) -> [u8; 2] {
|
pub(crate) fn select_server_hello_cipher_suite(
|
||||||
|
handshake: &[u8],
|
||||||
|
preferred: [u8; 2],
|
||||||
|
) -> Option<[u8; 2]> {
|
||||||
let preferred = if is_tls13_cipher_suite(preferred) {
|
let preferred = if is_tls13_cipher_suite(preferred) {
|
||||||
preferred
|
preferred
|
||||||
} else {
|
} else {
|
||||||
cipher_suite::TLS_AES_128_GCM_SHA256
|
cipher_suite::TLS_AES_128_GCM_SHA256
|
||||||
};
|
};
|
||||||
let Some(range) = client_hello_cipher_suites_range(handshake) else {
|
let Some(range) = client_hello_cipher_suites_range(handshake) else {
|
||||||
return preferred;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
if client_hello_offers_cipher_suite(handshake, range, preferred) {
|
if client_hello_offers_cipher_suite(handshake, range, preferred) {
|
||||||
return preferred;
|
return Some(preferred);
|
||||||
}
|
}
|
||||||
|
|
||||||
for fallback in [
|
for fallback in [
|
||||||
@@ -1223,26 +1215,26 @@ pub(crate) fn select_server_hello_cipher_suite(handshake: &[u8], preferred: [u8;
|
|||||||
cipher_suite::TLS_AES_256_GCM_SHA384,
|
cipher_suite::TLS_AES_256_GCM_SHA384,
|
||||||
] {
|
] {
|
||||||
if client_hello_offers_cipher_suite(handshake, range, fallback) {
|
if client_hello_offers_cipher_suite(handshake, range, fallback) {
|
||||||
return fallback;
|
return Some(fallback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
preferred
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Select the ServerHello key_share named group from the authenticated ClientHello.
|
/// Select the hybrid ServerHello key_share named group from the authenticated ClientHello.
|
||||||
///
|
///
|
||||||
/// Malformed key_share structures intentionally keep the legacy X25519 response
|
/// Malformed or non-hybrid key_share structures fail closed so authenticated
|
||||||
/// to avoid breaking older clients that do not advertise the hybrid group.
|
/// but DPI-inconsistent ClientHellos take the ordinary masking fallback path.
|
||||||
pub(crate) fn select_server_hello_key_share_group(handshake: &[u8]) -> u16 {
|
pub(crate) fn select_server_hello_key_share_group(handshake: &[u8]) -> Option<u16> {
|
||||||
if client_hello_offers_key_share_group(
|
if client_hello_offers_key_share_group(
|
||||||
handshake,
|
handshake,
|
||||||
TLS_NAMED_GROUP_X25519MLKEM768,
|
TLS_NAMED_GROUP_X25519MLKEM768,
|
||||||
X25519MLKEM768_CLIENT_KEY_SHARE_LEN,
|
X25519MLKEM768_CLIENT_KEY_SHARE_LEN,
|
||||||
) {
|
) {
|
||||||
TLS_NAMED_GROUP_X25519MLKEM768
|
Some(TLS_NAMED_GROUP_X25519MLKEM768)
|
||||||
} else {
|
} else {
|
||||||
TLS_NAMED_GROUP_X25519
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+54
-25
@@ -1473,24 +1473,22 @@ where
|
|||||||
return HandshakeResult::BadClient { reader, writer };
|
return HandshakeResult::BadClient { reader, writer };
|
||||||
}
|
}
|
||||||
|
|
||||||
let cached = if config.censorship.tls_emulation {
|
if tls::select_server_hello_key_share_group(handshake).is_none() {
|
||||||
|
auth_probe_record_failure_in(shared, peer.ip(), Instant::now());
|
||||||
|
maybe_apply_server_hello_delay(config).await;
|
||||||
|
debug!(
|
||||||
|
peer = %peer,
|
||||||
|
"TLS handshake rejected: ClientHello did not offer a valid X25519MLKEM768 key_share"
|
||||||
|
);
|
||||||
|
return HandshakeResult::BadClient { reader, writer };
|
||||||
|
}
|
||||||
|
|
||||||
|
let cached_entry = if config.censorship.tls_emulation {
|
||||||
if let Some(cache) = tls_cache.as_ref() {
|
if let Some(cache) = tls_cache.as_ref() {
|
||||||
let selected_domain =
|
let selected_domain =
|
||||||
matched_tls_domain.unwrap_or(config.censorship.tls_domain.as_str());
|
matched_tls_domain.unwrap_or(config.censorship.tls_domain.as_str());
|
||||||
let cached_entry = cache.get(selected_domain).await;
|
let cached_entry = cache.get(selected_domain).await;
|
||||||
let use_full_cert_payload = if config.censorship.serverhello_compact
|
Some(cached_entry)
|
||||||
&& matches!(client_tls_version, tls::ClientHelloTlsVersion::Tls12)
|
|
||||||
{
|
|
||||||
cache
|
|
||||||
.take_full_cert_budget_for_ip(
|
|
||||||
peer.ip(),
|
|
||||||
Duration::from_secs(config.censorship.tls_full_cert_ttl_secs),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
};
|
|
||||||
Some((cached_entry, use_full_cert_payload))
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -1498,20 +1496,54 @@ where
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let preferred_cipher_suite = if let Some(cached_entry) = cached_entry.as_ref() {
|
||||||
|
if cached_entry.server_hello_template.cipher_suite == [0, 0] {
|
||||||
|
[0x13, 0x01]
|
||||||
|
} else {
|
||||||
|
cached_entry.server_hello_template.cipher_suite
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
[0x13, 0x01]
|
||||||
|
};
|
||||||
|
let Some(selected_cipher_suite) =
|
||||||
|
tls::select_server_hello_cipher_suite(handshake, preferred_cipher_suite)
|
||||||
|
else {
|
||||||
|
auth_probe_record_failure_in(shared, peer.ip(), Instant::now());
|
||||||
|
maybe_apply_server_hello_delay(config).await;
|
||||||
|
debug!(
|
||||||
|
peer = %peer,
|
||||||
|
"TLS handshake rejected: ClientHello did not offer a supported TLS 1.3 cipher suite"
|
||||||
|
);
|
||||||
|
return HandshakeResult::BadClient { reader, writer };
|
||||||
|
};
|
||||||
|
|
||||||
|
let cached = if let Some(cached_entry) = cached_entry {
|
||||||
|
let use_full_cert_payload = if config.censorship.serverhello_compact
|
||||||
|
&& matches!(client_tls_version, tls::ClientHelloTlsVersion::Tls12)
|
||||||
|
{
|
||||||
|
if let Some(cache) = tls_cache.as_ref() {
|
||||||
|
cache.take_full_cert_budget_for_ip(
|
||||||
|
peer.ip(),
|
||||||
|
Duration::from_secs(config.censorship.tls_full_cert_ttl_secs),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
};
|
||||||
|
Some((cached_entry, use_full_cert_payload))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
// Add replay digest only for policy-valid handshakes.
|
// Add replay digest only for policy-valid handshakes.
|
||||||
replay_checker.add_tls_digest(digest_half);
|
replay_checker.add_tls_digest(digest_half);
|
||||||
|
|
||||||
let validation_session_id_slice = &validation_session_id[..validation_session_id_len];
|
let validation_session_id_slice = &validation_session_id[..validation_session_id_len];
|
||||||
let selected_key_share_group = tls::select_server_hello_key_share_group(handshake);
|
|
||||||
|
|
||||||
let response = if let Some((cached_entry, use_full_cert_payload)) = cached {
|
let response = if let Some((cached_entry, use_full_cert_payload)) = cached {
|
||||||
let preferred_cipher_suite = if cached_entry.server_hello_template.cipher_suite == [0, 0] {
|
|
||||||
[0x13, 0x01]
|
|
||||||
} else {
|
|
||||||
cached_entry.server_hello_template.cipher_suite
|
|
||||||
};
|
|
||||||
let selected_cipher_suite =
|
|
||||||
tls::select_server_hello_cipher_suite(handshake, preferred_cipher_suite);
|
|
||||||
emulator::build_emulated_server_hello(
|
emulator::build_emulated_server_hello(
|
||||||
&validated_secret,
|
&validated_secret,
|
||||||
&validation_digest,
|
&validation_digest,
|
||||||
@@ -1521,13 +1553,11 @@ where
|
|||||||
config.censorship.serverhello_compact,
|
config.censorship.serverhello_compact,
|
||||||
client_tls_version,
|
client_tls_version,
|
||||||
selected_cipher_suite,
|
selected_cipher_suite,
|
||||||
selected_key_share_group,
|
|
||||||
rng,
|
rng,
|
||||||
selected_alpn.clone(),
|
selected_alpn.clone(),
|
||||||
config.censorship.tls_new_session_tickets,
|
config.censorship.tls_new_session_tickets,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let selected_cipher_suite = tls::select_server_hello_cipher_suite(handshake, [0x13, 0x01]);
|
|
||||||
tls::build_server_hello_with_cipher(
|
tls::build_server_hello_with_cipher(
|
||||||
&validated_secret,
|
&validated_secret,
|
||||||
&validation_digest,
|
&validation_digest,
|
||||||
@@ -1535,7 +1565,6 @@ where
|
|||||||
config.censorship.fake_cert_len,
|
config.censorship.fake_cert_len,
|
||||||
rng,
|
rng,
|
||||||
selected_cipher_suite,
|
selected_cipher_suite,
|
||||||
selected_key_share_group,
|
|
||||||
selected_alpn.clone(),
|
selected_alpn.clone(),
|
||||||
config.censorship.tls_new_session_tickets,
|
config.censorship.tls_new_session_tickets,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ use crate::protocol::constants::{
|
|||||||
TLS_RECORD_HANDSHAKE, TLS_VERSION,
|
TLS_RECORD_HANDSHAKE, TLS_VERSION,
|
||||||
};
|
};
|
||||||
use crate::protocol::tls::{
|
use crate::protocol::tls::{
|
||||||
ClientHelloTlsVersion, TLS_DIGEST_LEN, TLS_DIGEST_POS, TLS_NAMED_GROUP_X25519,
|
ClientHelloTlsVersion, TLS_DIGEST_LEN, TLS_DIGEST_POS, TLS_NAMED_GROUP_X25519MLKEM768,
|
||||||
TLS_NAMED_GROUP_X25519MLKEM768, gen_fake_x25519_key,
|
|
||||||
gen_fake_x25519mlkem768_server_key_share,
|
gen_fake_x25519mlkem768_server_key_share,
|
||||||
};
|
};
|
||||||
use crate::tls_front::types::{
|
use crate::tls_front::types::{
|
||||||
@@ -216,25 +215,15 @@ fn push_key_share_entry(extensions: &mut Vec<u8>, group: u16, key_exchange: &[u8
|
|||||||
extensions.extend_from_slice(key_exchange);
|
extensions.extend_from_slice(key_exchange);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_key_share_extension(
|
fn push_key_share_extension(extensions: &mut Vec<u8>, rng: &SecureRandom) {
|
||||||
extensions: &mut Vec<u8>,
|
let key = gen_fake_x25519mlkem768_server_key_share(rng);
|
||||||
rng: &SecureRandom,
|
push_key_share_entry(extensions, TLS_NAMED_GROUP_X25519MLKEM768, &key);
|
||||||
selected_key_share_group: u16,
|
|
||||||
) {
|
|
||||||
if selected_key_share_group == TLS_NAMED_GROUP_X25519MLKEM768 {
|
|
||||||
let key = gen_fake_x25519mlkem768_server_key_share(rng);
|
|
||||||
push_key_share_entry(extensions, TLS_NAMED_GROUP_X25519MLKEM768, &key);
|
|
||||||
} else {
|
|
||||||
let key = gen_fake_x25519_key(rng);
|
|
||||||
push_key_share_entry(extensions, TLS_NAMED_GROUP_X25519, &key);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replay_profiled_server_hello_extension(
|
fn replay_profiled_server_hello_extension(
|
||||||
ext: &TlsExtension,
|
ext: &TlsExtension,
|
||||||
extensions: &mut Vec<u8>,
|
extensions: &mut Vec<u8>,
|
||||||
rng: &SecureRandom,
|
rng: &SecureRandom,
|
||||||
selected_key_share_group: u16,
|
|
||||||
saw_supported_versions: &mut bool,
|
saw_supported_versions: &mut bool,
|
||||||
saw_key_share: &mut bool,
|
saw_key_share: &mut bool,
|
||||||
) {
|
) {
|
||||||
@@ -244,7 +233,7 @@ fn replay_profiled_server_hello_extension(
|
|||||||
*saw_supported_versions = true;
|
*saw_supported_versions = true;
|
||||||
}
|
}
|
||||||
EXT_KEY_SHARE if !*saw_key_share => {
|
EXT_KEY_SHARE if !*saw_key_share => {
|
||||||
push_key_share_extension(extensions, rng, selected_key_share_group);
|
push_key_share_extension(extensions, rng);
|
||||||
*saw_key_share = true;
|
*saw_key_share = true;
|
||||||
}
|
}
|
||||||
EXT_ALPN => {}
|
EXT_ALPN => {}
|
||||||
@@ -252,11 +241,7 @@ fn replay_profiled_server_hello_extension(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_profiled_server_hello_extensions(
|
fn build_profiled_server_hello_extensions(cached: &CachedTlsData, rng: &SecureRandom) -> Vec<u8> {
|
||||||
cached: &CachedTlsData,
|
|
||||||
rng: &SecureRandom,
|
|
||||||
selected_key_share_group: u16,
|
|
||||||
) -> Vec<u8> {
|
|
||||||
let capacity = cached
|
let capacity = cached
|
||||||
.server_hello_template
|
.server_hello_template
|
||||||
.extensions
|
.extensions
|
||||||
@@ -273,14 +258,13 @@ fn build_profiled_server_hello_extensions(
|
|||||||
ext,
|
ext,
|
||||||
&mut extensions,
|
&mut extensions,
|
||||||
rng,
|
rng,
|
||||||
selected_key_share_group,
|
|
||||||
&mut saw_supported_versions,
|
&mut saw_supported_versions,
|
||||||
&mut saw_key_share,
|
&mut saw_key_share,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !saw_key_share {
|
if !saw_key_share {
|
||||||
push_key_share_extension(&mut extensions, rng, selected_key_share_group);
|
push_key_share_extension(&mut extensions, rng);
|
||||||
}
|
}
|
||||||
if !saw_supported_versions {
|
if !saw_supported_versions {
|
||||||
push_supported_versions_extension(&mut extensions);
|
push_supported_versions_extension(&mut extensions);
|
||||||
@@ -299,13 +283,12 @@ pub fn build_emulated_server_hello(
|
|||||||
serverhello_compact: bool,
|
serverhello_compact: bool,
|
||||||
client_tls_version: ClientHelloTlsVersion,
|
client_tls_version: ClientHelloTlsVersion,
|
||||||
selected_cipher_suite: [u8; 2],
|
selected_cipher_suite: [u8; 2],
|
||||||
selected_key_share_group: u16,
|
|
||||||
rng: &SecureRandom,
|
rng: &SecureRandom,
|
||||||
alpn: Option<Vec<u8>>,
|
alpn: Option<Vec<u8>>,
|
||||||
new_session_tickets: u8,
|
new_session_tickets: u8,
|
||||||
) -> Vec<u8> {
|
) -> Vec<u8> {
|
||||||
// --- ServerHello ---
|
// --- ServerHello ---
|
||||||
let extensions = build_profiled_server_hello_extensions(cached, rng, selected_key_share_group);
|
let extensions = build_profiled_server_hello_extensions(cached, rng);
|
||||||
let extensions_len = extensions.len() as u16;
|
let extensions_len = extensions.len() as u16;
|
||||||
|
|
||||||
let body_len = 2 + 32 + 1 + session_id.len() + 2 + 1 + 2 + extensions.len();
|
let body_len = 2 + 32 + 1 + session_id.len() + 2 + 1 + 2 + extensions.len();
|
||||||
@@ -490,7 +473,7 @@ mod tests {
|
|||||||
use crate::protocol::constants::{
|
use crate::protocol::constants::{
|
||||||
TLS_RECORD_APPLICATION, TLS_RECORD_CHANGE_CIPHER, TLS_RECORD_HANDSHAKE,
|
TLS_RECORD_APPLICATION, TLS_RECORD_CHANGE_CIPHER, TLS_RECORD_HANDSHAKE,
|
||||||
};
|
};
|
||||||
use crate::protocol::tls::{ClientHelloTlsVersion, TLS_NAMED_GROUP_X25519MLKEM768};
|
use crate::protocol::tls::ClientHelloTlsVersion;
|
||||||
|
|
||||||
fn first_app_data_payload(response: &[u8]) -> &[u8] {
|
fn first_app_data_payload(response: &[u8]) -> &[u8] {
|
||||||
let hello_len = u16::from_be_bytes([response[3], response[4]]) as usize;
|
let hello_len = u16::from_be_bytes([response[3], response[4]]) as usize;
|
||||||
@@ -572,7 +555,6 @@ mod tests {
|
|||||||
true,
|
true,
|
||||||
ClientHelloTlsVersion::Tls12,
|
ClientHelloTlsVersion::Tls12,
|
||||||
[0x13, 0x01],
|
[0x13, 0x01],
|
||||||
TLS_NAMED_GROUP_X25519MLKEM768,
|
|
||||||
&rng,
|
&rng,
|
||||||
None,
|
None,
|
||||||
0,
|
0,
|
||||||
@@ -602,7 +584,6 @@ mod tests {
|
|||||||
true,
|
true,
|
||||||
ClientHelloTlsVersion::Tls13,
|
ClientHelloTlsVersion::Tls13,
|
||||||
[0x13, 0x03],
|
[0x13, 0x03],
|
||||||
TLS_NAMED_GROUP_X25519MLKEM768,
|
|
||||||
&rng,
|
&rng,
|
||||||
None,
|
None,
|
||||||
0,
|
0,
|
||||||
@@ -638,7 +619,6 @@ mod tests {
|
|||||||
true,
|
true,
|
||||||
ClientHelloTlsVersion::Tls13,
|
ClientHelloTlsVersion::Tls13,
|
||||||
[0x13, 0x01],
|
[0x13, 0x01],
|
||||||
TLS_NAMED_GROUP_X25519MLKEM768,
|
|
||||||
&rng,
|
&rng,
|
||||||
Some(b"h2".to_vec()),
|
Some(b"h2".to_vec()),
|
||||||
0,
|
0,
|
||||||
@@ -663,7 +643,6 @@ mod tests {
|
|||||||
true,
|
true,
|
||||||
ClientHelloTlsVersion::Tls12,
|
ClientHelloTlsVersion::Tls12,
|
||||||
[0x13, 0x01],
|
[0x13, 0x01],
|
||||||
TLS_NAMED_GROUP_X25519MLKEM768,
|
|
||||||
&rng,
|
&rng,
|
||||||
None,
|
None,
|
||||||
0,
|
0,
|
||||||
@@ -699,7 +678,6 @@ mod tests {
|
|||||||
true,
|
true,
|
||||||
ClientHelloTlsVersion::Tls12,
|
ClientHelloTlsVersion::Tls12,
|
||||||
[0x13, 0x01],
|
[0x13, 0x01],
|
||||||
TLS_NAMED_GROUP_X25519MLKEM768,
|
|
||||||
&rng,
|
&rng,
|
||||||
None,
|
None,
|
||||||
0,
|
0,
|
||||||
@@ -741,7 +719,6 @@ mod tests {
|
|||||||
true,
|
true,
|
||||||
ClientHelloTlsVersion::Tls13,
|
ClientHelloTlsVersion::Tls13,
|
||||||
[0x13, 0x01],
|
[0x13, 0x01],
|
||||||
TLS_NAMED_GROUP_X25519MLKEM768,
|
|
||||||
&rng,
|
&rng,
|
||||||
None,
|
None,
|
||||||
0,
|
0,
|
||||||
@@ -775,7 +752,6 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
ClientHelloTlsVersion::Tls12,
|
ClientHelloTlsVersion::Tls12,
|
||||||
[0x13, 0x01],
|
[0x13, 0x01],
|
||||||
TLS_NAMED_GROUP_X25519MLKEM768,
|
|
||||||
&rng,
|
&rng,
|
||||||
Some(b"h2".to_vec()),
|
Some(b"h2".to_vec()),
|
||||||
0,
|
0,
|
||||||
@@ -808,7 +784,6 @@ mod tests {
|
|||||||
true,
|
true,
|
||||||
ClientHelloTlsVersion::Tls13,
|
ClientHelloTlsVersion::Tls13,
|
||||||
[0x13, 0x01],
|
[0x13, 0x01],
|
||||||
TLS_NAMED_GROUP_X25519MLKEM768,
|
|
||||||
&rng,
|
&rng,
|
||||||
None,
|
None,
|
||||||
0,
|
0,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use crate::crypto::SecureRandom;
|
|||||||
use crate::protocol::constants::{
|
use crate::protocol::constants::{
|
||||||
TLS_RECORD_APPLICATION, TLS_RECORD_CHANGE_CIPHER, TLS_RECORD_HANDSHAKE,
|
TLS_RECORD_APPLICATION, TLS_RECORD_CHANGE_CIPHER, TLS_RECORD_HANDSHAKE,
|
||||||
};
|
};
|
||||||
use crate::protocol::tls::{ClientHelloTlsVersion, TLS_NAMED_GROUP_X25519MLKEM768};
|
use crate::protocol::tls::ClientHelloTlsVersion;
|
||||||
use crate::tls_front::emulator::build_emulated_server_hello;
|
use crate::tls_front::emulator::build_emulated_server_hello;
|
||||||
use crate::tls_front::types::{
|
use crate::tls_front::types::{
|
||||||
CachedTlsData, ParsedServerHello, TlsBehaviorProfile, TlsProfileSource,
|
CachedTlsData, ParsedServerHello, TlsBehaviorProfile, TlsProfileSource,
|
||||||
@@ -66,7 +66,6 @@ fn emulated_server_hello_keeps_single_change_cipher_spec_for_client_compatibilit
|
|||||||
true,
|
true,
|
||||||
ClientHelloTlsVersion::Tls13,
|
ClientHelloTlsVersion::Tls13,
|
||||||
[0x13, 0x01],
|
[0x13, 0x01],
|
||||||
TLS_NAMED_GROUP_X25519MLKEM768,
|
|
||||||
&rng,
|
&rng,
|
||||||
None,
|
None,
|
||||||
0,
|
0,
|
||||||
@@ -92,7 +91,6 @@ fn emulated_server_hello_does_not_emit_profile_ticket_tail_when_disabled() {
|
|||||||
true,
|
true,
|
||||||
ClientHelloTlsVersion::Tls13,
|
ClientHelloTlsVersion::Tls13,
|
||||||
[0x13, 0x01],
|
[0x13, 0x01],
|
||||||
TLS_NAMED_GROUP_X25519MLKEM768,
|
|
||||||
&rng,
|
&rng,
|
||||||
None,
|
None,
|
||||||
0,
|
0,
|
||||||
@@ -116,7 +114,6 @@ fn emulated_server_hello_uses_profile_ticket_lengths_when_enabled() {
|
|||||||
true,
|
true,
|
||||||
ClientHelloTlsVersion::Tls13,
|
ClientHelloTlsVersion::Tls13,
|
||||||
[0x13, 0x01],
|
[0x13, 0x01],
|
||||||
TLS_NAMED_GROUP_X25519MLKEM768,
|
|
||||||
&rng,
|
&rng,
|
||||||
None,
|
None,
|
||||||
2,
|
2,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use crate::crypto::SecureRandom;
|
|||||||
use crate::protocol::constants::{
|
use crate::protocol::constants::{
|
||||||
TLS_RECORD_APPLICATION, TLS_RECORD_CHANGE_CIPHER, TLS_RECORD_HANDSHAKE,
|
TLS_RECORD_APPLICATION, TLS_RECORD_CHANGE_CIPHER, TLS_RECORD_HANDSHAKE,
|
||||||
};
|
};
|
||||||
use crate::protocol::tls::{ClientHelloTlsVersion, TLS_NAMED_GROUP_X25519MLKEM768};
|
use crate::protocol::tls::ClientHelloTlsVersion;
|
||||||
use crate::tls_front::emulator::build_emulated_server_hello;
|
use crate::tls_front::emulator::build_emulated_server_hello;
|
||||||
use crate::tls_front::types::{
|
use crate::tls_front::types::{
|
||||||
CachedTlsData, ParsedServerHello, TlsBehaviorProfile, TlsCertPayload, TlsProfileSource,
|
CachedTlsData, ParsedServerHello, TlsBehaviorProfile, TlsCertPayload, TlsProfileSource,
|
||||||
@@ -59,7 +59,6 @@ fn emulated_server_hello_ignores_oversized_alpn_when_marker_would_not_fit() {
|
|||||||
true,
|
true,
|
||||||
ClientHelloTlsVersion::Tls13,
|
ClientHelloTlsVersion::Tls13,
|
||||||
[0x13, 0x01],
|
[0x13, 0x01],
|
||||||
TLS_NAMED_GROUP_X25519MLKEM768,
|
|
||||||
&rng,
|
&rng,
|
||||||
Some(oversized_alpn),
|
Some(oversized_alpn),
|
||||||
0,
|
0,
|
||||||
@@ -99,7 +98,6 @@ fn emulated_server_hello_keeps_alpn_marker_out_of_appdata() {
|
|||||||
true,
|
true,
|
||||||
ClientHelloTlsVersion::Tls13,
|
ClientHelloTlsVersion::Tls13,
|
||||||
[0x13, 0x01],
|
[0x13, 0x01],
|
||||||
TLS_NAMED_GROUP_X25519MLKEM768,
|
|
||||||
&rng,
|
&rng,
|
||||||
Some(b"h2".to_vec()),
|
Some(b"h2".to_vec()),
|
||||||
0,
|
0,
|
||||||
@@ -131,7 +129,6 @@ fn emulated_server_hello_prefers_cert_payload_over_alpn_marker() {
|
|||||||
true,
|
true,
|
||||||
ClientHelloTlsVersion::Tls12,
|
ClientHelloTlsVersion::Tls12,
|
||||||
[0x13, 0x01],
|
[0x13, 0x01],
|
||||||
TLS_NAMED_GROUP_X25519MLKEM768,
|
|
||||||
&rng,
|
&rng,
|
||||||
Some(b"h2".to_vec()),
|
Some(b"h2".to_vec()),
|
||||||
0,
|
0,
|
||||||
|
|||||||
Reference in New Issue
Block a user