Fix for TLS-F, ALPN и SNI/ALPN helpers

Co-Authored-By: brekotis <93345790+brekotis@users.noreply.github.com>
This commit is contained in:
Alexey
2026-06-11 19:17:06 +03:00
parent 0f8aca56d9
commit c36eb81808
7 changed files with 339 additions and 46 deletions

View File

@@ -1719,6 +1719,9 @@ fn make_valid_tls_client_hello_with_alpn(
timestamp: u32,
alpn_protocols: &[&[u8]],
) -> Vec<u8> {
const TLS_EXTENSION_KEY_SHARE: u16 = 0x0033;
const X25519_KEY_SHARE_LEN: usize = 32;
let mut body = Vec::new();
body.extend_from_slice(&TLS_VERSION);
body.extend_from_slice(&[0u8; 32]);
@@ -1730,6 +1733,19 @@ fn make_valid_tls_client_hello_with_alpn(
body.push(0);
let mut ext_blob = Vec::new();
let mut key_share = Vec::new();
key_share.extend_from_slice(&tls::TLS_NAMED_GROUP_X25519.to_be_bytes());
key_share.extend_from_slice(&(X25519_KEY_SHARE_LEN as u16).to_be_bytes());
key_share.push(9);
key_share.resize(key_share.len() + X25519_KEY_SHARE_LEN - 1, 0);
let mut key_share_extension = Vec::new();
key_share_extension.extend_from_slice(&(key_share.len() as u16).to_be_bytes());
key_share_extension.extend_from_slice(&key_share);
ext_blob.extend_from_slice(&TLS_EXTENSION_KEY_SHARE.to_be_bytes());
ext_blob.extend_from_slice(&(key_share_extension.len() as u16).to_be_bytes());
ext_blob.extend_from_slice(&key_share_extension);
if !alpn_protocols.is_empty() {
let mut alpn_list = Vec::new();
for proto in alpn_protocols {

View File

@@ -21,11 +21,52 @@ fn test_config_with_secret_hex(secret_hex: &str) -> ProxyConfig {
}
fn make_valid_tls_handshake(secret: &[u8], timestamp: u32) -> Vec<u8> {
const TLS_AES_128_GCM_SHA256: [u8; 2] = [0x13, 0x01];
const TLS_EXTENSION_KEY_SHARE: u16 = 0x0033;
const X25519_KEY_SHARE_LEN: usize = 32;
let session_id_len: usize = 32;
let len = tls::TLS_DIGEST_POS + tls::TLS_DIGEST_LEN + 1 + session_id_len;
let mut handshake = vec![0x42u8; len];
let fill = 0x42u8;
handshake[tls::TLS_DIGEST_POS + tls::TLS_DIGEST_LEN] = session_id_len as u8;
let mut extensions = Vec::new();
let mut key_share = Vec::new();
key_share.extend_from_slice(&tls::TLS_NAMED_GROUP_X25519.to_be_bytes());
key_share.extend_from_slice(&(X25519_KEY_SHARE_LEN as u16).to_be_bytes());
key_share.push(9);
key_share.resize(key_share.len() + X25519_KEY_SHARE_LEN - 1, 0);
let mut key_share_extension = Vec::new();
key_share_extension.extend_from_slice(&(key_share.len() as u16).to_be_bytes());
key_share_extension.extend_from_slice(&key_share);
extensions.extend_from_slice(&TLS_EXTENSION_KEY_SHARE.to_be_bytes());
extensions.extend_from_slice(&(key_share_extension.len() as u16).to_be_bytes());
extensions.extend_from_slice(&key_share_extension);
let body_len =
2 + 32 + 1 + session_id_len + 2 + TLS_AES_128_GCM_SHA256.len() + 1 + 1 + 2
+ extensions.len();
let mut body = Vec::with_capacity(body_len);
body.extend_from_slice(&TLS_VERSION);
body.extend_from_slice(&[fill; 32]);
body.push(session_id_len as u8);
body.extend_from_slice(&[fill; 32]);
body.extend_from_slice(&(TLS_AES_128_GCM_SHA256.len() as u16).to_be_bytes());
body.extend_from_slice(&TLS_AES_128_GCM_SHA256);
body.push(1);
body.push(0);
body.extend_from_slice(&(extensions.len() as u16).to_be_bytes());
body.extend_from_slice(&extensions);
assert_eq!(body.len(), body_len);
let mut handshake = Vec::with_capacity(5 + 4 + body_len);
handshake.push(TLS_RECORD_HANDSHAKE);
handshake.extend_from_slice(&[0x03, 0x01]);
handshake.extend_from_slice(&((4 + body_len) as u16).to_be_bytes());
handshake.push(0x01);
let body_len_bytes = (body_len as u32).to_be_bytes();
handshake.extend_from_slice(&body_len_bytes[1..4]);
handshake.extend_from_slice(&body);
// The proxy authenticates TLS-fronted clients through the random field.
handshake[tls::TLS_DIGEST_POS..tls::TLS_DIGEST_POS + tls::TLS_DIGEST_LEN].fill(0);
let computed = sha256_hmac(secret, &handshake);
@@ -85,6 +126,9 @@ fn make_valid_tls_client_hello_with_alpn(
timestamp: u32,
alpn_protocols: &[&[u8]],
) -> Vec<u8> {
const TLS_EXTENSION_KEY_SHARE: u16 = 0x0033;
const X25519_KEY_SHARE_LEN: usize = 32;
let mut body = Vec::new();
body.extend_from_slice(&TLS_VERSION);
body.extend_from_slice(&[0u8; 32]);
@@ -96,6 +140,19 @@ fn make_valid_tls_client_hello_with_alpn(
body.push(0);
let mut ext_blob = Vec::new();
let mut key_share = Vec::new();
key_share.extend_from_slice(&tls::TLS_NAMED_GROUP_X25519.to_be_bytes());
key_share.extend_from_slice(&(X25519_KEY_SHARE_LEN as u16).to_be_bytes());
key_share.push(9);
key_share.resize(key_share.len() + X25519_KEY_SHARE_LEN - 1, 0);
let mut key_share_extension = Vec::new();
key_share_extension.extend_from_slice(&(key_share.len() as u16).to_be_bytes());
key_share_extension.extend_from_slice(&key_share);
ext_blob.extend_from_slice(&TLS_EXTENSION_KEY_SHARE.to_be_bytes());
ext_blob.extend_from_slice(&(key_share_extension.len() as u16).to_be_bytes());
ext_blob.extend_from_slice(&key_share_extension);
if !alpn_protocols.is_empty() {
let mut alpn_list = Vec::new();
for proto in alpn_protocols {
@@ -150,13 +207,7 @@ async fn tls_minimum_viable_length_boundary() {
let rng = SecureRandom::new();
let peer: SocketAddr = "192.0.2.1:12345".parse().unwrap();
let min_len = tls::TLS_DIGEST_POS + tls::TLS_DIGEST_LEN + 1;
let mut exact_min_handshake = vec![0x42u8; min_len];
exact_min_handshake[min_len - 1] = 0;
exact_min_handshake[tls::TLS_DIGEST_POS..tls::TLS_DIGEST_POS + tls::TLS_DIGEST_LEN].fill(0);
let digest = sha256_hmac(&secret, &exact_min_handshake);
exact_min_handshake[tls::TLS_DIGEST_POS..tls::TLS_DIGEST_POS + tls::TLS_DIGEST_LEN]
.copy_from_slice(&digest);
let exact_min_handshake = make_valid_tls_handshake(&secret, 0);
let res = handle_tls_handshake(
&exact_min_handshake,
@@ -171,12 +222,12 @@ async fn tls_minimum_viable_length_boundary() {
.await;
assert!(
matches!(res, HandshakeResult::Success(_)),
"Exact minimum length TLS handshake must succeed"
"Minimum valid TLS ClientHello must succeed"
);
let short_handshake = vec![0x42u8; min_len - 1];
let short_handshake = &exact_min_handshake[..exact_min_handshake.len() - 1];
let res_short = handle_tls_handshake(
&short_handshake,
short_handshake,
tokio::io::empty(),
tokio::io::sink(),
peer,
@@ -188,7 +239,7 @@ async fn tls_minimum_viable_length_boundary() {
.await;
assert!(
matches!(res_short, HandshakeResult::BadClient { .. }),
"Handshake 1 byte shorter than minimum must fail closed"
"Handshake 1 byte shorter than minimum valid ClientHello must fail closed"
);
}

View File

@@ -1,5 +1,6 @@
use super::*;
use crate::crypto::sha256_hmac;
use crate::protocol::constants::{TLS_RECORD_HANDSHAKE, TLS_VERSION};
use crate::stats::ReplayChecker;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::time::{Duration, Instant};
@@ -17,11 +18,52 @@ fn test_config_with_secret_hex(secret_hex: &str) -> ProxyConfig {
}
fn make_valid_tls_handshake(secret: &[u8], timestamp: u32) -> Vec<u8> {
const TLS_AES_128_GCM_SHA256: [u8; 2] = [0x13, 0x01];
const TLS_EXTENSION_KEY_SHARE: u16 = 0x0033;
const X25519_KEY_SHARE_LEN: usize = 32;
let session_id_len: usize = 32;
let len = tls::TLS_DIGEST_POS + tls::TLS_DIGEST_LEN + 1 + session_id_len;
let mut handshake = vec![0x42u8; len];
let fill = 0x42u8;
handshake[tls::TLS_DIGEST_POS + tls::TLS_DIGEST_LEN] = session_id_len as u8;
let mut extensions = Vec::new();
let mut key_share = Vec::new();
key_share.extend_from_slice(&tls::TLS_NAMED_GROUP_X25519.to_be_bytes());
key_share.extend_from_slice(&(X25519_KEY_SHARE_LEN as u16).to_be_bytes());
key_share.push(9);
key_share.resize(key_share.len() + X25519_KEY_SHARE_LEN - 1, 0);
let mut key_share_extension = Vec::new();
key_share_extension.extend_from_slice(&(key_share.len() as u16).to_be_bytes());
key_share_extension.extend_from_slice(&key_share);
extensions.extend_from_slice(&TLS_EXTENSION_KEY_SHARE.to_be_bytes());
extensions.extend_from_slice(&(key_share_extension.len() as u16).to_be_bytes());
extensions.extend_from_slice(&key_share_extension);
let body_len =
2 + 32 + 1 + session_id_len + 2 + TLS_AES_128_GCM_SHA256.len() + 1 + 1 + 2
+ extensions.len();
let mut body = Vec::with_capacity(body_len);
body.extend_from_slice(&TLS_VERSION);
body.extend_from_slice(&[fill; 32]);
body.push(session_id_len as u8);
body.extend_from_slice(&[fill; 32]);
body.extend_from_slice(&(TLS_AES_128_GCM_SHA256.len() as u16).to_be_bytes());
body.extend_from_slice(&TLS_AES_128_GCM_SHA256);
body.push(1);
body.push(0);
body.extend_from_slice(&(extensions.len() as u16).to_be_bytes());
body.extend_from_slice(&extensions);
assert_eq!(body.len(), body_len);
let mut handshake = Vec::with_capacity(5 + 4 + body_len);
handshake.push(TLS_RECORD_HANDSHAKE);
handshake.extend_from_slice(&[0x03, 0x01]);
handshake.extend_from_slice(&((4 + body_len) as u16).to_be_bytes());
handshake.push(0x01);
let body_len_bytes = (body_len as u32).to_be_bytes();
handshake.extend_from_slice(&body_len_bytes[1..4]);
handshake.extend_from_slice(&body);
// The proxy authenticates TLS-fronted clients through the random field.
handshake[tls::TLS_DIGEST_POS..tls::TLS_DIGEST_POS + tls::TLS_DIGEST_LEN].fill(0);
let computed = sha256_hmac(secret, &handshake);

View File

@@ -25,11 +25,52 @@ fn test_config_with_secret_hex(secret_hex: &str) -> ProxyConfig {
}
fn make_valid_tls_handshake(secret: &[u8], timestamp: u32) -> Vec<u8> {
const TLS_AES_128_GCM_SHA256: [u8; 2] = [0x13, 0x01];
const TLS_EXTENSION_KEY_SHARE: u16 = 0x0033;
const X25519_KEY_SHARE_LEN: usize = 32;
let session_id_len: usize = 32;
let len = tls::TLS_DIGEST_POS + tls::TLS_DIGEST_LEN + 1 + session_id_len;
let mut handshake = vec![0x42u8; len];
let fill = 0x42u8;
handshake[tls::TLS_DIGEST_POS + tls::TLS_DIGEST_LEN] = session_id_len as u8;
let mut extensions = Vec::new();
let mut key_share = Vec::new();
key_share.extend_from_slice(&tls::TLS_NAMED_GROUP_X25519.to_be_bytes());
key_share.extend_from_slice(&(X25519_KEY_SHARE_LEN as u16).to_be_bytes());
key_share.push(9);
key_share.resize(key_share.len() + X25519_KEY_SHARE_LEN - 1, 0);
let mut key_share_extension = Vec::new();
key_share_extension.extend_from_slice(&(key_share.len() as u16).to_be_bytes());
key_share_extension.extend_from_slice(&key_share);
extensions.extend_from_slice(&TLS_EXTENSION_KEY_SHARE.to_be_bytes());
extensions.extend_from_slice(&(key_share_extension.len() as u16).to_be_bytes());
extensions.extend_from_slice(&key_share_extension);
let body_len =
2 + 32 + 1 + session_id_len + 2 + TLS_AES_128_GCM_SHA256.len() + 1 + 1 + 2
+ extensions.len();
let mut body = Vec::with_capacity(body_len);
body.extend_from_slice(&TLS_VERSION);
body.extend_from_slice(&[fill; 32]);
body.push(session_id_len as u8);
body.extend_from_slice(&[fill; 32]);
body.extend_from_slice(&(TLS_AES_128_GCM_SHA256.len() as u16).to_be_bytes());
body.extend_from_slice(&TLS_AES_128_GCM_SHA256);
body.push(1);
body.push(0);
body.extend_from_slice(&(extensions.len() as u16).to_be_bytes());
body.extend_from_slice(&extensions);
assert_eq!(body.len(), body_len);
let mut handshake = Vec::with_capacity(5 + 4 + body_len);
handshake.push(TLS_RECORD_HANDSHAKE);
handshake.extend_from_slice(&[0x03, 0x01]);
handshake.extend_from_slice(&((4 + body_len) as u16).to_be_bytes());
handshake.push(0x01);
let body_len_bytes = (body_len as u32).to_be_bytes();
handshake.extend_from_slice(&body_len_bytes[1..4]);
handshake.extend_from_slice(&body);
// The proxy authenticates TLS-fronted clients through the random field.
handshake[tls::TLS_DIGEST_POS..tls::TLS_DIGEST_POS + tls::TLS_DIGEST_LEN].fill(0);
let computed = sha256_hmac(secret, &handshake);
@@ -90,6 +131,9 @@ fn make_valid_tls_client_hello_with_sni_and_alpn(
sni_host: &str,
alpn_protocols: &[&[u8]],
) -> Vec<u8> {
const TLS_EXTENSION_KEY_SHARE: u16 = 0x0033;
const X25519_KEY_SHARE_LEN: usize = 32;
let mut body = Vec::new();
body.extend_from_slice(&TLS_VERSION);
body.extend_from_slice(&[0u8; 32]);
@@ -112,6 +156,19 @@ fn make_valid_tls_client_hello_with_sni_and_alpn(
ext_blob.extend_from_slice(&(sni_payload.len() as u16).to_be_bytes());
ext_blob.extend_from_slice(&sni_payload);
let mut key_share = Vec::new();
key_share.extend_from_slice(&tls::TLS_NAMED_GROUP_X25519.to_be_bytes());
key_share.extend_from_slice(&(X25519_KEY_SHARE_LEN as u16).to_be_bytes());
key_share.push(9);
key_share.resize(key_share.len() + X25519_KEY_SHARE_LEN - 1, 0);
let mut key_share_extension = Vec::new();
key_share_extension.extend_from_slice(&(key_share.len() as u16).to_be_bytes());
key_share_extension.extend_from_slice(&key_share);
ext_blob.extend_from_slice(&TLS_EXTENSION_KEY_SHARE.to_be_bytes());
ext_blob.extend_from_slice(&(key_share_extension.len() as u16).to_be_bytes());
ext_blob.extend_from_slice(&key_share_extension);
if !alpn_protocols.is_empty() {
let mut alpn_list = Vec::new();
for proto in alpn_protocols {

View File

@@ -24,6 +24,9 @@ fn make_valid_tls_client_hello_with_alpn(
timestamp: u32,
alpn_protocols: &[&[u8]],
) -> Vec<u8> {
const TLS_EXTENSION_KEY_SHARE: u16 = 0x0033;
const X25519_KEY_SHARE_LEN: usize = 32;
let mut body = Vec::new();
body.extend_from_slice(&TLS_VERSION);
body.extend_from_slice(&[0u8; 32]);
@@ -35,6 +38,19 @@ fn make_valid_tls_client_hello_with_alpn(
body.push(0);
let mut ext_blob = Vec::new();
let mut key_share = Vec::new();
key_share.extend_from_slice(&tls::TLS_NAMED_GROUP_X25519.to_be_bytes());
key_share.extend_from_slice(&(X25519_KEY_SHARE_LEN as u16).to_be_bytes());
key_share.push(9);
key_share.resize(key_share.len() + X25519_KEY_SHARE_LEN - 1, 0);
let mut key_share_extension = Vec::new();
key_share_extension.extend_from_slice(&(key_share.len() as u16).to_be_bytes());
key_share_extension.extend_from_slice(&key_share);
ext_blob.extend_from_slice(&TLS_EXTENSION_KEY_SHARE.to_be_bytes());
ext_blob.extend_from_slice(&(key_share_extension.len() as u16).to_be_bytes());
ext_blob.extend_from_slice(&key_share_extension);
if !alpn_protocols.is_empty() {
let mut alpn_list = Vec::new();
for proto in alpn_protocols {

View File

@@ -10,11 +10,52 @@ use std::time::{Duration, Instant};
use tokio::sync::Barrier;
fn make_valid_tls_handshake(secret: &[u8], timestamp: u32) -> Vec<u8> {
const TLS_AES_128_GCM_SHA256: [u8; 2] = [0x13, 0x01];
const TLS_EXTENSION_KEY_SHARE: u16 = 0x0033;
const X25519_KEY_SHARE_LEN: usize = 32;
let session_id_len: usize = 32;
let len = tls::TLS_DIGEST_POS + tls::TLS_DIGEST_LEN + 1 + session_id_len;
let mut handshake = vec![0x42u8; len];
let fill = 0x42u8;
handshake[tls::TLS_DIGEST_POS + tls::TLS_DIGEST_LEN] = session_id_len as u8;
let mut extensions = Vec::new();
let mut key_share = Vec::new();
key_share.extend_from_slice(&tls::TLS_NAMED_GROUP_X25519.to_be_bytes());
key_share.extend_from_slice(&(X25519_KEY_SHARE_LEN as u16).to_be_bytes());
key_share.push(9);
key_share.resize(key_share.len() + X25519_KEY_SHARE_LEN - 1, 0);
let mut key_share_extension = Vec::new();
key_share_extension.extend_from_slice(&(key_share.len() as u16).to_be_bytes());
key_share_extension.extend_from_slice(&key_share);
extensions.extend_from_slice(&TLS_EXTENSION_KEY_SHARE.to_be_bytes());
extensions.extend_from_slice(&(key_share_extension.len() as u16).to_be_bytes());
extensions.extend_from_slice(&key_share_extension);
let body_len =
2 + 32 + 1 + session_id_len + 2 + TLS_AES_128_GCM_SHA256.len() + 1 + 1 + 2
+ extensions.len();
let mut body = Vec::with_capacity(body_len);
body.extend_from_slice(&TLS_VERSION);
body.extend_from_slice(&[fill; 32]);
body.push(session_id_len as u8);
body.extend_from_slice(&[fill; 32]);
body.extend_from_slice(&(TLS_AES_128_GCM_SHA256.len() as u16).to_be_bytes());
body.extend_from_slice(&TLS_AES_128_GCM_SHA256);
body.push(1);
body.push(0);
body.extend_from_slice(&(extensions.len() as u16).to_be_bytes());
body.extend_from_slice(&extensions);
assert_eq!(body.len(), body_len);
let mut handshake = Vec::with_capacity(5 + 4 + body_len);
handshake.push(TLS_RECORD_HANDSHAKE);
handshake.extend_from_slice(&[0x03, 0x01]);
handshake.extend_from_slice(&((4 + body_len) as u16).to_be_bytes());
handshake.push(0x01);
let body_len_bytes = (body_len as u32).to_be_bytes();
handshake.extend_from_slice(&body_len_bytes[1..4]);
handshake.extend_from_slice(&body);
// The proxy authenticates TLS-fronted clients through the random field.
handshake[tls::TLS_DIGEST_POS..tls::TLS_DIGEST_POS + tls::TLS_DIGEST_LEN].fill(0);
let computed = sha256_hmac(secret, &handshake);
@@ -34,6 +75,9 @@ fn make_valid_tls_client_hello_with_alpn(
timestamp: u32,
alpn_protocols: &[&[u8]],
) -> Vec<u8> {
const TLS_EXTENSION_KEY_SHARE: u16 = 0x0033;
const X25519_KEY_SHARE_LEN: usize = 32;
let mut body = Vec::new();
body.extend_from_slice(&TLS_VERSION);
body.extend_from_slice(&[0u8; 32]);
@@ -45,6 +89,19 @@ fn make_valid_tls_client_hello_with_alpn(
body.push(0);
let mut ext_blob = Vec::new();
let mut key_share = Vec::new();
key_share.extend_from_slice(&tls::TLS_NAMED_GROUP_X25519.to_be_bytes());
key_share.extend_from_slice(&(X25519_KEY_SHARE_LEN as u16).to_be_bytes());
key_share.push(9);
key_share.resize(key_share.len() + X25519_KEY_SHARE_LEN - 1, 0);
let mut key_share_extension = Vec::new();
key_share_extension.extend_from_slice(&(key_share.len() as u16).to_be_bytes());
key_share_extension.extend_from_slice(&key_share);
ext_blob.extend_from_slice(&TLS_EXTENSION_KEY_SHARE.to_be_bytes());
ext_blob.extend_from_slice(&(key_share_extension.len() as u16).to_be_bytes());
ext_blob.extend_from_slice(&key_share_extension);
if !alpn_protocols.is_empty() {
let mut alpn_list = Vec::new();
for proto in alpn_protocols {
@@ -92,6 +149,9 @@ fn make_valid_tls_client_hello_with_sni_and_alpn(
sni_host: &str,
alpn_protocols: &[&[u8]],
) -> Vec<u8> {
const TLS_EXTENSION_KEY_SHARE: u16 = 0x0033;
const X25519_KEY_SHARE_LEN: usize = 32;
let mut body = Vec::new();
body.extend_from_slice(&TLS_VERSION);
body.extend_from_slice(&[0u8; 32]);
@@ -114,6 +174,19 @@ fn make_valid_tls_client_hello_with_sni_and_alpn(
ext_blob.extend_from_slice(&(sni_payload.len() as u16).to_be_bytes());
ext_blob.extend_from_slice(&sni_payload);
let mut key_share = Vec::new();
key_share.extend_from_slice(&tls::TLS_NAMED_GROUP_X25519.to_be_bytes());
key_share.extend_from_slice(&(X25519_KEY_SHARE_LEN as u16).to_be_bytes());
key_share.push(9);
key_share.resize(key_share.len() + X25519_KEY_SHARE_LEN - 1, 0);
let mut key_share_extension = Vec::new();
key_share_extension.extend_from_slice(&(key_share.len() as u16).to_be_bytes());
key_share_extension.extend_from_slice(&key_share);
ext_blob.extend_from_slice(&TLS_EXTENSION_KEY_SHARE.to_be_bytes());
ext_blob.extend_from_slice(&(key_share_extension.len() as u16).to_be_bytes());
ext_blob.extend_from_slice(&key_share_extension);
if !alpn_protocols.is_empty() {
let mut alpn_list = Vec::new();
for proto in alpn_protocols {
@@ -549,25 +622,6 @@ async fn adversarial_tls_replay_churn_allows_only_unique_digests() {
let replay_checker = Arc::new(ReplayChecker::new(8192, Duration::from_secs(60)));
let rng = Arc::new(SecureRandom::new());
let make_tagged_handshake = |timestamp: u32, tag: u8| {
let session_id_len: usize = 32;
let len = tls::TLS_DIGEST_POS + tls::TLS_DIGEST_LEN + 1 + session_id_len;
let mut handshake = vec![tag; len];
handshake[tls::TLS_DIGEST_POS + tls::TLS_DIGEST_LEN] = session_id_len as u8;
handshake[tls::TLS_DIGEST_POS..tls::TLS_DIGEST_POS + tls::TLS_DIGEST_LEN].fill(0);
let computed = sha256_hmac(&secret, &handshake);
let mut digest = computed;
let ts = timestamp.to_le_bytes();
for i in 0..4 {
digest[28 + i] ^= ts[i];
}
handshake[tls::TLS_DIGEST_POS..tls::TLS_DIGEST_POS + tls::TLS_DIGEST_LEN]
.copy_from_slice(&digest);
handshake
};
let mut tasks = Vec::new();
// 128 exact duplicates: only one should pass.
@@ -601,7 +655,7 @@ async fn adversarial_tls_replay_churn_allows_only_unique_digests() {
let config = Arc::clone(&config);
let replay_checker = Arc::clone(&replay_checker);
let rng = Arc::clone(&rng);
let handshake = make_tagged_handshake(10_000 + i as u32, (i as u8).wrapping_add(0x80));
let handshake = make_valid_tls_handshake(&secret, 10_000 + i as u32);
tasks.push(tokio::spawn(async move {
let peer = SocketAddr::new(
IpAddr::V4(Ipv4Addr::new(198, 18, 0, ((i % 250) + 1) as u8)),

View File

@@ -47,11 +47,52 @@ fn make_valid_mtproto_handshake(
}
fn make_valid_tls_handshake(secret: &[u8], timestamp: u32) -> Vec<u8> {
const TLS_AES_128_GCM_SHA256: [u8; 2] = [0x13, 0x01];
const TLS_EXTENSION_KEY_SHARE: u16 = 0x0033;
const X25519_KEY_SHARE_LEN: usize = 32;
let session_id_len: usize = 32;
let len = tls::TLS_DIGEST_POS + tls::TLS_DIGEST_LEN + 1 + session_id_len;
let mut handshake = vec![0x42u8; len];
let fill = 0x42u8;
handshake[tls::TLS_DIGEST_POS + tls::TLS_DIGEST_LEN] = session_id_len as u8;
let mut extensions = Vec::new();
let mut key_share = Vec::new();
key_share.extend_from_slice(&tls::TLS_NAMED_GROUP_X25519.to_be_bytes());
key_share.extend_from_slice(&(X25519_KEY_SHARE_LEN as u16).to_be_bytes());
key_share.push(9);
key_share.resize(key_share.len() + X25519_KEY_SHARE_LEN - 1, 0);
let mut key_share_extension = Vec::new();
key_share_extension.extend_from_slice(&(key_share.len() as u16).to_be_bytes());
key_share_extension.extend_from_slice(&key_share);
extensions.extend_from_slice(&TLS_EXTENSION_KEY_SHARE.to_be_bytes());
extensions.extend_from_slice(&(key_share_extension.len() as u16).to_be_bytes());
extensions.extend_from_slice(&key_share_extension);
let body_len =
2 + 32 + 1 + session_id_len + 2 + TLS_AES_128_GCM_SHA256.len() + 1 + 1 + 2
+ extensions.len();
let mut body = Vec::with_capacity(body_len);
body.extend_from_slice(&TLS_VERSION);
body.extend_from_slice(&[fill; 32]);
body.push(session_id_len as u8);
body.extend_from_slice(&[fill; 32]);
body.extend_from_slice(&(TLS_AES_128_GCM_SHA256.len() as u16).to_be_bytes());
body.extend_from_slice(&TLS_AES_128_GCM_SHA256);
body.push(1);
body.push(0);
body.extend_from_slice(&(extensions.len() as u16).to_be_bytes());
body.extend_from_slice(&extensions);
assert_eq!(body.len(), body_len);
let mut handshake = Vec::with_capacity(5 + 4 + body_len);
handshake.push(TLS_RECORD_HANDSHAKE);
handshake.extend_from_slice(&[0x03, 0x01]);
handshake.extend_from_slice(&((4 + body_len) as u16).to_be_bytes());
handshake.push(0x01);
let body_len_bytes = (body_len as u32).to_be_bytes();
handshake.extend_from_slice(&body_len_bytes[1..4]);
handshake.extend_from_slice(&body);
// The proxy authenticates TLS-fronted clients through the random field.
handshake[tls::TLS_DIGEST_POS..tls::TLS_DIGEST_POS + tls::TLS_DIGEST_LEN].fill(0);
let computed = sha256_hmac(secret, &handshake);
@@ -72,6 +113,9 @@ fn make_valid_tls_client_hello_with_sni_and_alpn(
sni_host: &str,
alpn_protocols: &[&[u8]],
) -> Vec<u8> {
const TLS_EXTENSION_KEY_SHARE: u16 = 0x0033;
const X25519_KEY_SHARE_LEN: usize = 32;
let mut body = Vec::new();
body.extend_from_slice(&TLS_VERSION);
body.extend_from_slice(&[0u8; 32]);
@@ -93,6 +137,19 @@ fn make_valid_tls_client_hello_with_sni_and_alpn(
ext_blob.extend_from_slice(&(sni_payload.len() as u16).to_be_bytes());
ext_blob.extend_from_slice(&sni_payload);
let mut key_share = Vec::new();
key_share.extend_from_slice(&tls::TLS_NAMED_GROUP_X25519.to_be_bytes());
key_share.extend_from_slice(&(X25519_KEY_SHARE_LEN as u16).to_be_bytes());
key_share.push(9);
key_share.resize(key_share.len() + X25519_KEY_SHARE_LEN - 1, 0);
let mut key_share_extension = Vec::new();
key_share_extension.extend_from_slice(&(key_share.len() as u16).to_be_bytes());
key_share_extension.extend_from_slice(&key_share);
ext_blob.extend_from_slice(&TLS_EXTENSION_KEY_SHARE.to_be_bytes());
ext_blob.extend_from_slice(&(key_share_extension.len() as u16).to_be_bytes());
ext_blob.extend_from_slice(&key_share_extension);
if !alpn_protocols.is_empty() {
let mut alpn_list = Vec::new();
for proto in alpn_protocols {