Hardened TLS-F ServerHello selection

Co-Authored-By: brekotis <93345790+brekotis@users.noreply.github.com>
This commit is contained in:
Alexey
2026-06-11 13:07:40 +03:00
parent db7ff8737c
commit c4b58ad374
6 changed files with 126 additions and 128 deletions
+54 -25
View File
@@ -1473,24 +1473,22 @@ where
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() {
let selected_domain =
matched_tls_domain.unwrap_or(config.censorship.tls_domain.as_str());
let cached_entry = cache.get(selected_domain).await;
let use_full_cert_payload = if config.censorship.serverhello_compact
&& 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))
Some(cached_entry)
} else {
None
}
@@ -1498,20 +1496,54 @@ where
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.
replay_checker.add_tls_digest(digest_half);
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 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(
&validated_secret,
&validation_digest,
@@ -1521,13 +1553,11 @@ where
config.censorship.serverhello_compact,
client_tls_version,
selected_cipher_suite,
selected_key_share_group,
rng,
selected_alpn.clone(),
config.censorship.tls_new_session_tickets,
)
} else {
let selected_cipher_suite = tls::select_server_hello_cipher_suite(handshake, [0x13, 0x01]);
tls::build_server_hello_with_cipher(
&validated_secret,
&validation_digest,
@@ -1535,7 +1565,6 @@ where
config.censorship.fake_cert_len,
rng,
selected_cipher_suite,
selected_key_share_group,
selected_alpn.clone(),
config.censorship.tls_new_session_tickets,
)