mirror of
https://github.com/telemt/telemt.git
synced 2026-06-19 02:00:08 +07:00
Select ServerHello key share from TLS Fetcher Profile
Co-Authored-By: brekotis <93345790+brekotis@users.noreply.github.com>
This commit is contained in:
@@ -1396,6 +1396,10 @@ fn server_hello_key_share(record: &[u8]) -> Option<(u16, usize)> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn test_server_key_share(group: u16, len: usize) -> ServerHelloKeyShare {
|
||||||
|
ServerHelloKeyShare::new(group, vec![0x42; len])
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn build_server_hello_never_places_alpn_in_server_hello_extensions() {
|
fn build_server_hello_never_places_alpn_in_server_hello_extensions() {
|
||||||
let secret = b"alpn_sh_forbidden";
|
let secret = b"alpn_sh_forbidden";
|
||||||
@@ -1457,7 +1461,10 @@ fn emulated_server_hello_never_places_alpn_in_server_hello_extensions() {
|
|||||||
true,
|
true,
|
||||||
ClientHelloTlsVersion::Tls13,
|
ClientHelloTlsVersion::Tls13,
|
||||||
[0x13, 0x01],
|
[0x13, 0x01],
|
||||||
&vec![0x42; X25519MLKEM768_SERVER_KEY_SHARE_LEN],
|
&test_server_key_share(
|
||||||
|
TLS_NAMED_GROUP_X25519MLKEM768,
|
||||||
|
X25519MLKEM768_SERVER_KEY_SHARE_LEN,
|
||||||
|
),
|
||||||
&rng,
|
&rng,
|
||||||
Some(b"h2".to_vec()),
|
Some(b"h2".to_vec()),
|
||||||
0,
|
0,
|
||||||
@@ -1542,11 +1549,12 @@ fn test_build_server_hello_structure() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_build_server_hello_with_cipher_always_uses_hybrid_key_share() {
|
fn test_build_server_hello_with_cipher_uses_selected_key_share_group() {
|
||||||
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];
|
||||||
let key_share = vec![0x55u8; X25519MLKEM768_SERVER_KEY_SHARE_LEN];
|
let key_share =
|
||||||
|
ServerHelloKeyShare::new(TLS_NAMED_GROUP_X25519, vec![0x55u8; X25519_KEY_SHARE_LEN]);
|
||||||
|
|
||||||
let rng = crate::crypto::SecureRandom::new();
|
let rng = crate::crypto::SecureRandom::new();
|
||||||
let response = build_server_hello_with_cipher(
|
let response = build_server_hello_with_cipher(
|
||||||
@@ -1563,10 +1571,7 @@ fn test_build_server_hello_with_cipher_always_uses_hybrid_key_share() {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
server_hello_key_share(&response),
|
server_hello_key_share(&response),
|
||||||
Some((
|
Some((TLS_NAMED_GROUP_X25519, X25519_KEY_SHARE_LEN))
|
||||||
TLS_NAMED_GROUP_X25519MLKEM768,
|
|
||||||
X25519MLKEM768_SERVER_KEY_SHARE_LEN
|
|
||||||
))
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1887,6 +1892,23 @@ fn select_server_hello_key_share_group_prefers_hybrid_when_valid_share_is_offere
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn select_server_hello_key_share_group_prefers_profiled_x25519_when_valid_share_is_offered() {
|
||||||
|
let key_share = client_key_share_extension(&[
|
||||||
|
(
|
||||||
|
TLS_NAMED_GROUP_X25519MLKEM768,
|
||||||
|
X25519MLKEM768_CLIENT_KEY_SHARE_LEN,
|
||||||
|
),
|
||||||
|
(TLS_NAMED_GROUP_X25519, X25519_KEY_SHARE_LEN),
|
||||||
|
]);
|
||||||
|
let ch = build_client_hello_with_exts(vec![(0x0033, key_share)], "example.com");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
select_server_hello_key_share_group_with_preference(&ch, Some(TLS_NAMED_GROUP_X25519)),
|
||||||
|
Some(TLS_NAMED_GROUP_X25519)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn build_x25519mlkem768_server_key_share_accepts_tdesktop_canonical_share() {
|
fn build_x25519mlkem768_server_key_share_accepts_tdesktop_canonical_share() {
|
||||||
let key_share = client_key_share_extension(&[
|
let key_share = client_key_share_extension(&[
|
||||||
@@ -1917,6 +1939,68 @@ fn build_x25519mlkem768_server_key_share_accepts_tdesktop_canonical_share() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn build_x25519_server_key_share_accepts_tdesktop_fallback_share() {
|
||||||
|
let key_share = client_key_share_extension(&[
|
||||||
|
(
|
||||||
|
TLS_NAMED_GROUP_X25519MLKEM768,
|
||||||
|
X25519MLKEM768_CLIENT_KEY_SHARE_LEN,
|
||||||
|
),
|
||||||
|
(TLS_NAMED_GROUP_X25519, X25519_KEY_SHARE_LEN),
|
||||||
|
]);
|
||||||
|
let ch = build_client_hello_with_exts(vec![(0x0033, key_share)], "example.com");
|
||||||
|
let rng = crate::crypto::SecureRandom::new();
|
||||||
|
|
||||||
|
let server_key_share = build_x25519_server_key_share(&ch, &rng)
|
||||||
|
.expect("tdesktop-like X25519 share must build a ServerHello share");
|
||||||
|
|
||||||
|
assert_eq!(server_key_share.len(), X25519_KEY_SHARE_LEN);
|
||||||
|
assert!(
|
||||||
|
server_key_share.iter().any(|byte| *byte != 0),
|
||||||
|
"X25519 server share must not be all zero"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn build_server_hello_key_share_prefers_profiled_x25519() {
|
||||||
|
let key_share = client_key_share_extension(&[
|
||||||
|
(
|
||||||
|
TLS_NAMED_GROUP_X25519MLKEM768,
|
||||||
|
X25519MLKEM768_CLIENT_KEY_SHARE_LEN,
|
||||||
|
),
|
||||||
|
(TLS_NAMED_GROUP_X25519, X25519_KEY_SHARE_LEN),
|
||||||
|
]);
|
||||||
|
let ch = build_client_hello_with_exts(vec![(0x0033, key_share)], "example.com");
|
||||||
|
let rng = crate::crypto::SecureRandom::new();
|
||||||
|
|
||||||
|
let server_key_share =
|
||||||
|
build_server_hello_key_share(&ch, Some(TLS_NAMED_GROUP_X25519), &rng)
|
||||||
|
.expect("profiled X25519 share must be selected when client offers it");
|
||||||
|
|
||||||
|
assert_eq!(server_key_share.group(), TLS_NAMED_GROUP_X25519);
|
||||||
|
assert_eq!(server_key_share.key_exchange().len(), X25519_KEY_SHARE_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn build_server_hello_key_share_falls_back_from_bad_profiled_x25519_to_hybrid() {
|
||||||
|
let key_share = client_key_share_extension(&[(
|
||||||
|
TLS_NAMED_GROUP_X25519MLKEM768,
|
||||||
|
X25519MLKEM768_CLIENT_KEY_SHARE_LEN,
|
||||||
|
)]);
|
||||||
|
let ch = build_client_hello_with_exts(vec![(0x0033, key_share)], "example.com");
|
||||||
|
let rng = crate::crypto::SecureRandom::new();
|
||||||
|
|
||||||
|
let server_key_share =
|
||||||
|
build_server_hello_key_share(&ch, Some(TLS_NAMED_GROUP_X25519), &rng)
|
||||||
|
.expect("hybrid share must be selected when profiled X25519 is unavailable");
|
||||||
|
|
||||||
|
assert_eq!(server_key_share.group(), TLS_NAMED_GROUP_X25519MLKEM768);
|
||||||
|
assert_eq!(
|
||||||
|
server_key_share.key_exchange().len(),
|
||||||
|
X25519MLKEM768_SERVER_KEY_SHARE_LEN
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn build_x25519mlkem768_server_key_share_rejects_noncanonical_mlkem_key() {
|
fn build_x25519mlkem768_server_key_share_rejects_noncanonical_mlkem_key() {
|
||||||
let mut key_exchange = vec![0x42; X25519MLKEM768_CLIENT_KEY_SHARE_LEN];
|
let mut key_exchange = vec![0x42; X25519MLKEM768_CLIENT_KEY_SHARE_LEN];
|
||||||
@@ -1946,12 +2030,15 @@ fn build_x25519mlkem768_server_key_share_rejects_all_zero_x25519_share() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn select_server_hello_key_share_group_rejects_without_hybrid_share() {
|
fn select_server_hello_key_share_group_accepts_x25519_when_hybrid_is_absent() {
|
||||||
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!(select_server_hello_key_share_group(&ch), None);
|
assert_eq!(
|
||||||
|
select_server_hello_key_share_group(&ch),
|
||||||
|
Some(TLS_NAMED_GROUP_X25519)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
+119
-18
@@ -127,6 +127,30 @@ const X25519MLKEM768_SERVER_KEY_SHARE_LEN: usize = 1120;
|
|||||||
const MLKEM768_CLIENT_ENCAPSULATION_KEY_LEN: usize = 1184;
|
const MLKEM768_CLIENT_ENCAPSULATION_KEY_LEN: usize = 1184;
|
||||||
const MLKEM768_SERVER_CIPHERTEXT_LEN: usize = 1088;
|
const MLKEM768_SERVER_CIPHERTEXT_LEN: usize = 1088;
|
||||||
|
|
||||||
|
/// ServerHello key_share selected for the authenticated ClientHello.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub(crate) struct ServerHelloKeyShare {
|
||||||
|
group: u16,
|
||||||
|
key_exchange: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServerHelloKeyShare {
|
||||||
|
pub(crate) fn new(group: u16, key_exchange: Vec<u8>) -> Self {
|
||||||
|
Self {
|
||||||
|
group,
|
||||||
|
key_exchange,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn group(&self) -> u16 {
|
||||||
|
self.group
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn key_exchange(&self) -> &[u8] {
|
||||||
|
&self.key_exchange
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ============= TLS Validation Result =============
|
// ============= TLS Validation Result =============
|
||||||
|
|
||||||
/// Result of validating TLS handshake
|
/// Result of validating TLS handshake
|
||||||
@@ -588,6 +612,65 @@ pub(crate) fn build_x25519mlkem768_server_key_share(
|
|||||||
Some(key_share)
|
Some(key_share)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Build a valid X25519 ServerHello key_share for the authenticated ClientHello.
|
||||||
|
pub(crate) fn build_x25519_server_key_share(
|
||||||
|
handshake: &[u8],
|
||||||
|
rng: &SecureRandom,
|
||||||
|
) -> Option<Vec<u8>> {
|
||||||
|
let client_key_exchange =
|
||||||
|
client_hello_key_share_group_entry(handshake, TLS_NAMED_GROUP_X25519, X25519_KEY_SHARE_LEN)?;
|
||||||
|
let mut client_x25519 = [0u8; X25519_KEY_SHARE_LEN];
|
||||||
|
client_x25519.copy_from_slice(client_key_exchange);
|
||||||
|
let (server_x25519_scalar, server_x25519_key) = gen_x25519_key_pair(rng);
|
||||||
|
let x25519_shared = x25519(server_x25519_scalar, client_x25519);
|
||||||
|
if bool::from(x25519_shared.ct_eq(&[0u8; X25519_KEY_SHARE_LEN])) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(server_x25519_key.to_vec())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_server_hello_key_share_for_group(
|
||||||
|
handshake: &[u8],
|
||||||
|
group: u16,
|
||||||
|
rng: &SecureRandom,
|
||||||
|
) -> Option<ServerHelloKeyShare> {
|
||||||
|
match group {
|
||||||
|
TLS_NAMED_GROUP_X25519MLKEM768 => {
|
||||||
|
let key_exchange = build_x25519mlkem768_server_key_share(handshake, rng)?;
|
||||||
|
Some(ServerHelloKeyShare::new(group, key_exchange))
|
||||||
|
}
|
||||||
|
TLS_NAMED_GROUP_X25519 => {
|
||||||
|
let key_exchange = build_x25519_server_key_share(handshake, rng)?;
|
||||||
|
Some(ServerHelloKeyShare::new(group, key_exchange))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn server_hello_key_share_candidate_order(preferred_group: Option<u16>) -> [u16; 2] {
|
||||||
|
if preferred_group == Some(TLS_NAMED_GROUP_X25519) {
|
||||||
|
[TLS_NAMED_GROUP_X25519, TLS_NAMED_GROUP_X25519MLKEM768]
|
||||||
|
} else {
|
||||||
|
[TLS_NAMED_GROUP_X25519MLKEM768, TLS_NAMED_GROUP_X25519]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build a ServerHello key_share using a profile-preferred group when possible.
|
||||||
|
pub(crate) fn build_server_hello_key_share(
|
||||||
|
handshake: &[u8],
|
||||||
|
preferred_group: Option<u16>,
|
||||||
|
rng: &SecureRandom,
|
||||||
|
) -> Option<ServerHelloKeyShare> {
|
||||||
|
for group in server_hello_key_share_candidate_order(preferred_group) {
|
||||||
|
if let Some(key_share) = build_server_hello_key_share_for_group(handshake, group, rng) {
|
||||||
|
return Some(key_share);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// Build TLS ServerHello response
|
/// Build TLS ServerHello response
|
||||||
///
|
///
|
||||||
/// This builds a complete TLS 1.3-like response including:
|
/// This builds a complete TLS 1.3-like response including:
|
||||||
@@ -605,6 +688,10 @@ pub fn build_server_hello(
|
|||||||
alpn: Option<Vec<u8>>,
|
alpn: Option<Vec<u8>>,
|
||||||
new_session_tickets: u8,
|
new_session_tickets: u8,
|
||||||
) -> Vec<u8> {
|
) -> Vec<u8> {
|
||||||
|
let server_key_share = ServerHelloKeyShare::new(
|
||||||
|
TLS_NAMED_GROUP_X25519MLKEM768,
|
||||||
|
gen_fake_x25519mlkem768_server_key_share(rng),
|
||||||
|
);
|
||||||
build_server_hello_with_cipher(
|
build_server_hello_with_cipher(
|
||||||
secret,
|
secret,
|
||||||
client_digest,
|
client_digest,
|
||||||
@@ -612,7 +699,7 @@ 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,
|
||||||
&gen_fake_x25519mlkem768_server_key_share(rng),
|
&server_key_share,
|
||||||
alpn,
|
alpn,
|
||||||
new_session_tickets,
|
new_session_tickets,
|
||||||
)
|
)
|
||||||
@@ -630,7 +717,7 @@ 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],
|
||||||
server_key_share: &[u8],
|
server_key_share: &ServerHelloKeyShare,
|
||||||
alpn: Option<Vec<u8>>,
|
alpn: Option<Vec<u8>>,
|
||||||
new_session_tickets: u8,
|
new_session_tickets: u8,
|
||||||
) -> Vec<u8> {
|
) -> Vec<u8> {
|
||||||
@@ -641,7 +728,7 @@ pub(crate) fn build_server_hello_with_cipher(
|
|||||||
// Build ServerHello
|
// Build ServerHello
|
||||||
let server_hello = ServerHelloBuilder::new(session_id.to_vec())
|
let server_hello = 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, server_key_share)
|
.with_key_share(server_key_share.group(), server_key_share.key_exchange())
|
||||||
.with_tls13_version()
|
.with_tls13_version()
|
||||||
.build_record();
|
.build_record();
|
||||||
|
|
||||||
@@ -1281,24 +1368,38 @@ pub(crate) fn select_server_hello_cipher_suite(
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Select the hybrid ServerHello key_share named group from the authenticated ClientHello.
|
fn client_hello_key_share_group_len(group: u16) -> Option<usize> {
|
||||||
///
|
match group {
|
||||||
/// Malformed or non-hybrid key_share structures fail closed so authenticated
|
TLS_NAMED_GROUP_X25519MLKEM768 => Some(X25519MLKEM768_CLIENT_KEY_SHARE_LEN),
|
||||||
/// but DPI-inconsistent ClientHellos take the ordinary masking fallback path.
|
TLS_NAMED_GROUP_X25519 => Some(X25519_KEY_SHARE_LEN),
|
||||||
pub(crate) fn select_server_hello_key_share_group(handshake: &[u8]) -> Option<u16> {
|
_ => None,
|
||||||
if client_hello_key_share_group_entry(
|
|
||||||
handshake,
|
|
||||||
TLS_NAMED_GROUP_X25519MLKEM768,
|
|
||||||
X25519MLKEM768_CLIENT_KEY_SHARE_LEN,
|
|
||||||
)
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
Some(TLS_NAMED_GROUP_X25519MLKEM768)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Select the ServerHello key_share named group from the authenticated ClientHello.
|
||||||
|
///
|
||||||
|
/// Malformed key_share structures fail closed so authenticated but
|
||||||
|
/// DPI-inconsistent ClientHellos take the ordinary masking fallback path.
|
||||||
|
pub(crate) fn select_server_hello_key_share_group(handshake: &[u8]) -> Option<u16> {
|
||||||
|
select_server_hello_key_share_group_with_preference(handshake, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Select the ServerHello key_share named group with an origin-profile preference.
|
||||||
|
pub(crate) fn select_server_hello_key_share_group_with_preference(
|
||||||
|
handshake: &[u8],
|
||||||
|
preferred_group: Option<u16>,
|
||||||
|
) -> Option<u16> {
|
||||||
|
for group in server_hello_key_share_candidate_order(preferred_group) {
|
||||||
|
let expected_key_exchange_len = client_hello_key_share_group_len(group)?;
|
||||||
|
if client_hello_key_share_group_entry(handshake, group, expected_key_exchange_len).is_some()
|
||||||
|
{
|
||||||
|
return Some(group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// Check if bytes look like a TLS ClientHello
|
/// Check if bytes look like a TLS ClientHello
|
||||||
pub fn is_tls_handshake(first_bytes: &[u8]) -> bool {
|
pub fn is_tls_handshake(first_bytes: &[u8]) -> bool {
|
||||||
if first_bytes.len() < 3 {
|
if first_bytes.len() < 3 {
|
||||||
|
|||||||
+15
-10
@@ -1473,16 +1473,6 @@ where
|
|||||||
return HandshakeResult::BadClient { reader, writer };
|
return HandshakeResult::BadClient { reader, writer };
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(server_key_share) = tls::build_x25519mlkem768_server_key_share(handshake, rng) 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 usable X25519MLKEM768 key_share"
|
|
||||||
);
|
|
||||||
return HandshakeResult::BadClient { reader, writer };
|
|
||||||
};
|
|
||||||
|
|
||||||
let cached_entry = if config.censorship.tls_emulation {
|
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 =
|
||||||
@@ -1496,6 +1486,21 @@ where
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let preferred_key_share_group = cached_entry
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|cached_entry| emulator::profiled_server_hello_key_share_group(cached_entry));
|
||||||
|
let Some(server_key_share) =
|
||||||
|
tls::build_server_hello_key_share(handshake, preferred_key_share_group, rng)
|
||||||
|
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 usable TLS 1.3 key_share"
|
||||||
|
);
|
||||||
|
return HandshakeResult::BadClient { reader, writer };
|
||||||
|
};
|
||||||
|
|
||||||
let preferred_cipher_suite = if let Some(cached_entry) = cached_entry.as_ref() {
|
let preferred_cipher_suite = if let Some(cached_entry) = cached_entry.as_ref() {
|
||||||
if cached_entry.server_hello_template.cipher_suite == [0, 0] {
|
if cached_entry.server_hello_template.cipher_suite == [0, 0] {
|
||||||
[0x13, 0x01]
|
[0x13, 0x01]
|
||||||
|
|||||||
+83
-11
@@ -6,7 +6,8 @@ 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_X25519MLKEM768,
|
ClientHelloTlsVersion, ServerHelloKeyShare, TLS_DIGEST_LEN, TLS_DIGEST_POS,
|
||||||
|
TLS_NAMED_GROUP_X25519, TLS_NAMED_GROUP_X25519MLKEM768,
|
||||||
};
|
};
|
||||||
use crate::tls_front::types::{
|
use crate::tls_front::types::{
|
||||||
CachedTlsData, ParsedCertificateInfo, TlsExtension, TlsProfileSource,
|
CachedTlsData, ParsedCertificateInfo, TlsExtension, TlsProfileSource,
|
||||||
@@ -20,6 +21,40 @@ const EXT_SUPPORTED_VERSIONS: u16 = 0x002b;
|
|||||||
const EXT_KEY_SHARE: u16 = 0x0033;
|
const EXT_KEY_SHARE: u16 = 0x0033;
|
||||||
const EXT_ALPN: u16 = 0x0010;
|
const EXT_ALPN: u16 = 0x0010;
|
||||||
|
|
||||||
|
fn parse_profiled_key_share_group(data: &[u8]) -> Option<u16> {
|
||||||
|
if data.len() < 4 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let group = u16::from_be_bytes([data[0], data[1]]);
|
||||||
|
let key_exchange_len = u16::from_be_bytes([data[2], data[3]]) as usize;
|
||||||
|
if data.len() != 4 + key_exchange_len {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
match group {
|
||||||
|
TLS_NAMED_GROUP_X25519 | TLS_NAMED_GROUP_X25519MLKEM768 => Some(group),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the origin-profiled ServerHello key_share group when it is replay-safe.
|
||||||
|
pub(crate) fn profiled_server_hello_key_share_group(cached: &CachedTlsData) -> Option<u16> {
|
||||||
|
if !matches!(
|
||||||
|
cached.behavior_profile.source,
|
||||||
|
TlsProfileSource::Raw | TlsProfileSource::Merged
|
||||||
|
) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
cached
|
||||||
|
.server_hello_template
|
||||||
|
.extensions
|
||||||
|
.iter()
|
||||||
|
.find(|ext| ext.ext_type == EXT_KEY_SHARE)
|
||||||
|
.and_then(|ext| parse_profiled_key_share_group(&ext.data))
|
||||||
|
}
|
||||||
|
|
||||||
fn jitter_and_clamp_sizes(sizes: &[usize], rng: &SecureRandom) -> Vec<usize> {
|
fn jitter_and_clamp_sizes(sizes: &[usize], rng: &SecureRandom) -> Vec<usize> {
|
||||||
sizes
|
sizes
|
||||||
.iter()
|
.iter()
|
||||||
@@ -208,18 +243,18 @@ 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(extensions: &mut Vec<u8>, server_key_share: &[u8]) {
|
fn push_key_share_extension(extensions: &mut Vec<u8>, server_key_share: &ServerHelloKeyShare) {
|
||||||
push_key_share_entry(
|
push_key_share_entry(
|
||||||
extensions,
|
extensions,
|
||||||
TLS_NAMED_GROUP_X25519MLKEM768,
|
server_key_share.group(),
|
||||||
server_key_share,
|
server_key_share.key_exchange(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replay_profiled_server_hello_extension(
|
fn replay_profiled_server_hello_extension(
|
||||||
ext: &TlsExtension,
|
ext: &TlsExtension,
|
||||||
extensions: &mut Vec<u8>,
|
extensions: &mut Vec<u8>,
|
||||||
server_key_share: &[u8],
|
server_key_share: &ServerHelloKeyShare,
|
||||||
saw_supported_versions: &mut bool,
|
saw_supported_versions: &mut bool,
|
||||||
saw_key_share: &mut bool,
|
saw_key_share: &mut bool,
|
||||||
) {
|
) {
|
||||||
@@ -239,7 +274,7 @@ fn replay_profiled_server_hello_extension(
|
|||||||
|
|
||||||
fn build_profiled_server_hello_extensions(
|
fn build_profiled_server_hello_extensions(
|
||||||
cached: &CachedTlsData,
|
cached: &CachedTlsData,
|
||||||
server_key_share: &[u8],
|
server_key_share: &ServerHelloKeyShare,
|
||||||
) -> Vec<u8> {
|
) -> Vec<u8> {
|
||||||
let capacity = cached
|
let capacity = cached
|
||||||
.server_hello_template
|
.server_hello_template
|
||||||
@@ -282,7 +317,7 @@ 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],
|
||||||
server_key_share: &[u8],
|
server_key_share: &ServerHelloKeyShare,
|
||||||
rng: &SecureRandom,
|
rng: &SecureRandom,
|
||||||
alpn: Option<Vec<u8>>,
|
alpn: Option<Vec<u8>>,
|
||||||
new_session_tickets: u8,
|
new_session_tickets: u8,
|
||||||
@@ -469,13 +504,16 @@ mod tests {
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
build_compact_cert_info_payload, build_emulated_server_hello,
|
build_compact_cert_info_payload, build_emulated_server_hello,
|
||||||
hash_compact_cert_info_payload,
|
hash_compact_cert_info_payload, profiled_server_hello_key_share_group,
|
||||||
};
|
};
|
||||||
use crate::crypto::SecureRandom;
|
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;
|
use crate::protocol::tls::{
|
||||||
|
ClientHelloTlsVersion, ServerHelloKeyShare, TLS_NAMED_GROUP_X25519,
|
||||||
|
TLS_NAMED_GROUP_X25519MLKEM768,
|
||||||
|
};
|
||||||
|
|
||||||
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;
|
||||||
@@ -540,8 +578,42 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_server_key_share() -> Vec<u8> {
|
fn test_server_key_share() -> ServerHelloKeyShare {
|
||||||
vec![0x42; 1120]
|
ServerHelloKeyShare::new(TLS_NAMED_GROUP_X25519MLKEM768, vec![0x42; 1120])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn server_key_share_extension_data(group: u16, len: usize) -> Vec<u8> {
|
||||||
|
let mut data = Vec::new();
|
||||||
|
data.extend_from_slice(&group.to_be_bytes());
|
||||||
|
data.extend_from_slice(&(len as u16).to_be_bytes());
|
||||||
|
data.resize(4 + len, 0x42);
|
||||||
|
data
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn profiled_server_hello_key_share_group_reads_raw_x25519_profile() {
|
||||||
|
let mut cached = make_cached(None);
|
||||||
|
cached.behavior_profile.source = TlsProfileSource::Raw;
|
||||||
|
cached.server_hello_template.extensions = vec![TlsExtension {
|
||||||
|
ext_type: 0x0033,
|
||||||
|
data: server_key_share_extension_data(TLS_NAMED_GROUP_X25519, 32),
|
||||||
|
}];
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
profiled_server_hello_key_share_group(&cached),
|
||||||
|
Some(TLS_NAMED_GROUP_X25519)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn profiled_server_hello_key_share_group_ignores_default_profile() {
|
||||||
|
let mut cached = make_cached(None);
|
||||||
|
cached.server_hello_template.extensions = vec![TlsExtension {
|
||||||
|
ext_type: 0x0033,
|
||||||
|
data: server_key_share_extension_data(TLS_NAMED_GROUP_X25519, 32),
|
||||||
|
}];
|
||||||
|
|
||||||
|
assert_eq!(profiled_server_hello_key_share_group(&cached), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ 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;
|
use crate::protocol::tls::{
|
||||||
|
ClientHelloTlsVersion, ServerHelloKeyShare, TLS_NAMED_GROUP_X25519MLKEM768,
|
||||||
|
};
|
||||||
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,
|
||||||
@@ -52,8 +54,8 @@ fn record_lengths_by_type(response: &[u8], wanted_type: u8) -> Vec<usize> {
|
|||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_server_key_share() -> Vec<u8> {
|
fn test_server_key_share() -> ServerHelloKeyShare {
|
||||||
vec![0x42; 1120]
|
ServerHelloKeyShare::new(TLS_NAMED_GROUP_X25519MLKEM768, vec![0x42; 1120])
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ 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;
|
use crate::protocol::tls::{
|
||||||
|
ClientHelloTlsVersion, ServerHelloKeyShare, TLS_NAMED_GROUP_X25519MLKEM768,
|
||||||
|
};
|
||||||
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,
|
||||||
@@ -44,8 +46,8 @@ fn first_app_data_payload(response: &[u8]) -> &[u8] {
|
|||||||
&response[app_start + 5..app_start + 5 + app_len]
|
&response[app_start + 5..app_start + 5 + app_len]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_server_key_share() -> Vec<u8> {
|
fn test_server_key_share() -> ServerHelloKeyShare {
|
||||||
vec![0x42; 1120]
|
ServerHelloKeyShare::new(TLS_NAMED_GROUP_X25519MLKEM768, vec![0x42; 1120])
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user