mirror of
https://github.com/Runnin4ik/dpi-detector.git
synced 2026-10-19 09:23:17 +00:00
fingerprint: the shape data says a thing once, and the docs stop contradicting it
The records were already one `TlsShape` per variant with the big lists named, but three things were repeated inside them and two comments contradicted their own record. Repeated data, now named once: `drop12`/`drop13` arrays were written inline 8–12 times per family (`CHROME_TLS_DROP13` alone replaced twelve copies), the zlib cert-compression list was the literal `&[1]` where brotli was `BROTLI`, and two whole records were byte-identical to others — `EDGE101_H2` to `CHROME99_ANDROID_H2` and `GO_TLS_GROUPS` to `SAFARI_TLS_PQ_GROUPS`, both now aliases with the reason stated. Contradictions fixed, data untouched: `tor.rs` said ECH was omitted while the record's order and `ech: true` send it, and Safari 26's iOS row claimed its cipher order matched macOS while its fields and its own JA3 pin say it is 15.5's. The ECH counts now agree with the table — nine of thirty, which is what `the_ech_shapes_carry_the_grease_extension_and_the_others_do_not` asserts. Two self-comparisons in `tests.rs` left the `chrome133`↔`chrome146` shared-hello pin unable to fail: the pair was `(Chrome146, Chrome146)` and the other side of the second assertion was the same record it was comparing. Both now compare two independently built hellos, so an edit to either record's TLS lists fails the pin. No wire byte moves: every list's contents, every record's fields and every extension order are unchanged, which is what the JA3/JA4 pins and `tls_fingerprint dump` confirm.
This commit is contained in:
parent
f89a66582a
commit
653f225df0
9 changed files with 196 additions and 187 deletions
|
|
@ -168,20 +168,14 @@ pub(crate) const CHROME99_ANDROID_H2: H2Fingerprint = H2Fingerprint {
|
|||
/// `1:65536;3:1000;4:6291456;6:262144`, window 15663105,
|
||||
/// `--http2-stream-weight 256 --http2-stream-exclusive 1`, pseudo-headers `masp`.
|
||||
/// Edge leaves out `SETTINGS_ENABLE_PUSH`, which Chrome sends.
|
||||
pub(crate) const EDGE101_H2: H2Fingerprint = H2Fingerprint {
|
||||
header_table_size: Some(65_536),
|
||||
max_concurrent_streams: Some(1000),
|
||||
initial_window_size: 6_291_456,
|
||||
max_frame_size: None,
|
||||
max_header_list_size: Some(262_144),
|
||||
enable_push: None,
|
||||
enable_connect_protocol: None,
|
||||
no_rfc7540_priorities: None,
|
||||
settings_order: &[],
|
||||
connection_window: 15_663_105 + 65_535,
|
||||
pseudo_order: PseudoOrder::MethodAuthoritySchemePath,
|
||||
priority: Some((256, true)),
|
||||
};
|
||||
///
|
||||
/// An alias of [`CHROME99_ANDROID_H2`], the record it is byte-identical to: the
|
||||
/// two clients send the same preface, so the list is written once there. Both
|
||||
/// keep Chrome's Android stream cap of 1000, both send no
|
||||
/// `SETTINGS_ENABLE_PUSH` (which desktop Chrome does), and both use the same
|
||||
/// window and request priority — the differences between the records are their
|
||||
/// TLS shape and their identity, not the preface.
|
||||
pub(crate) const EDGE101_H2: H2Fingerprint = CHROME99_ANDROID_H2;
|
||||
|
||||
/// `2:0;3:100;4:2097152;8:1;9:1`, window 10420225,
|
||||
/// `--http2-pseudo-headers-order "msap" --http2-stream-weight 256
|
||||
|
|
|
|||
|
|
@ -103,6 +103,39 @@ pub(crate) const CHROME_TLS_RAW_EXTS: &[(u16, &[u8])] = &[
|
|||
(EXT_PADDING, &[]),
|
||||
];
|
||||
|
||||
/// The four extensions every Chrome drops from a hello pinned to TLS 1.3
|
||||
/// alone: `ec_point_formats` (a 1.2-era extension), and `extended_master_secret`,
|
||||
/// `renegotiation_info` and `session_ticket`, none of which a Chrome 1.3 hello
|
||||
/// carries. Measured on `curl_chrome99…146`, whose pinned 1.3 hellos all drop
|
||||
/// these four (see `super::super::pinned_drop`).
|
||||
pub(crate) const CHROME_TLS_DROP13: &[u16] = &[
|
||||
EXT_EXTENDED_MASTER_SECRET,
|
||||
EXT_RENEGOTIATION_INFO,
|
||||
EXT_EC_POINT_FORMATS,
|
||||
EXT_SESSION_TICKET,
|
||||
];
|
||||
|
||||
/// Chrome 133 and 146's 1.2 drop: the 1.3-only `supported_versions` and ALPS,
|
||||
/// at the code point those releases moved ALPS to.
|
||||
pub(crate) const CHROME_TLS_DROP12_ALPS_NEW: &[u16] =
|
||||
&[EXT_SUPPORTED_VERSIONS, EXT_APPLICATION_SETTINGS_NEW];
|
||||
|
||||
/// Chrome 131 and 115 PQ's 1.2 drop: the 1.3-only `supported_versions` and
|
||||
/// ALPS at its old code point, which those releases still send.
|
||||
pub(crate) const CHROME_TLS_DROP12_ALPS: &[u16] =
|
||||
&[EXT_SUPPORTED_VERSIONS, EXT_APPLICATION_SETTINGS];
|
||||
|
||||
/// The 1.2 drop of the Chrome records whose hello can carry the padding slot:
|
||||
/// `supported_versions`, ALPS, and the padding — a 1.2 hello is already under
|
||||
/// the 256-byte floor where BoringSSL stops padding. Used by Chrome 99 Android
|
||||
/// through 131 Android and Edge 101.
|
||||
pub(crate) const CHROME_TLS_DROP12_ALPS_PADDING: &[u16] =
|
||||
&[EXT_SUPPORTED_VERSIONS, EXT_APPLICATION_SETTINGS, EXT_PADDING];
|
||||
|
||||
/// The 1.2 drop of the older Chrome records, which send no ALPS: the 1.3-only
|
||||
/// `supported_versions` and the padding slot.
|
||||
pub(crate) const CHROME_TLS_DROP12_PADDING: &[u16] = &[EXT_SUPPORTED_VERSIONS, EXT_PADDING];
|
||||
|
||||
/// Chrome 110–131's extension order: Chrome 99–107's list without the padding
|
||||
/// slot, which those releases no longer send on their own — nothing pads a
|
||||
/// 300-byte hello.
|
||||
|
|
@ -408,13 +441,8 @@ pub(crate) const CHROME146: TlsShape = TlsShape {
|
|||
ext_order: CHROME_ALPS_NEW_EXT_ORDER,
|
||||
raw_exts: CHROME_ALPS_NEW_RAW_EXTS,
|
||||
suppress: &[],
|
||||
drop13: &[
|
||||
EXT_EXTENDED_MASTER_SECRET,
|
||||
EXT_RENEGOTIATION_INFO,
|
||||
EXT_EC_POINT_FORMATS,
|
||||
EXT_SESSION_TICKET,
|
||||
],
|
||||
drop12: &[EXT_SUPPORTED_VERSIONS, EXT_APPLICATION_SETTINGS_NEW],
|
||||
drop13: CHROME_TLS_DROP13,
|
||||
drop12: CHROME_TLS_DROP12_ALPS_NEW,
|
||||
alpn: H2_AND_HTTP11,
|
||||
padding_to: None,
|
||||
grease: true,
|
||||
|
|
@ -453,13 +481,8 @@ pub(crate) const CHROME133: TlsShape = TlsShape {
|
|||
ext_order: CHROME_ALPS_NEW_EXT_ORDER,
|
||||
raw_exts: CHROME_ALPS_NEW_RAW_EXTS,
|
||||
suppress: &[],
|
||||
drop13: &[
|
||||
EXT_EXTENDED_MASTER_SECRET,
|
||||
EXT_RENEGOTIATION_INFO,
|
||||
EXT_EC_POINT_FORMATS,
|
||||
EXT_SESSION_TICKET,
|
||||
],
|
||||
drop12: &[EXT_SUPPORTED_VERSIONS, EXT_APPLICATION_SETTINGS_NEW],
|
||||
drop13: CHROME_TLS_DROP13,
|
||||
drop12: CHROME_TLS_DROP12_ALPS_NEW,
|
||||
alpn: H2_AND_HTTP11,
|
||||
padding_to: None,
|
||||
grease: true,
|
||||
|
|
@ -495,13 +518,8 @@ pub(crate) const CHROME131: TlsShape = TlsShape {
|
|||
ext_order: CHROME_NO_PADDING_EXT_ORDER,
|
||||
raw_exts: CHROME_TLS_RAW_EXTS,
|
||||
suppress: &[],
|
||||
drop13: &[
|
||||
EXT_EXTENDED_MASTER_SECRET,
|
||||
EXT_RENEGOTIATION_INFO,
|
||||
EXT_EC_POINT_FORMATS,
|
||||
EXT_SESSION_TICKET,
|
||||
],
|
||||
drop12: &[EXT_SUPPORTED_VERSIONS, EXT_APPLICATION_SETTINGS],
|
||||
drop13: CHROME_TLS_DROP13,
|
||||
drop12: CHROME_TLS_DROP12_ALPS,
|
||||
alpn: H2_AND_HTTP11,
|
||||
padding_to: None,
|
||||
grease: true,
|
||||
|
|
@ -536,13 +554,8 @@ pub(crate) const CHROME131_ANDROID: TlsShape = TlsShape {
|
|||
ext_order: CHROME_PADDING_AND_ECH_EXT_ORDER,
|
||||
raw_exts: CHROME_TLS_RAW_EXTS,
|
||||
suppress: &[],
|
||||
drop13: &[
|
||||
EXT_EXTENDED_MASTER_SECRET,
|
||||
EXT_RENEGOTIATION_INFO,
|
||||
EXT_EC_POINT_FORMATS,
|
||||
EXT_SESSION_TICKET,
|
||||
],
|
||||
drop12: &[EXT_SUPPORTED_VERSIONS, EXT_APPLICATION_SETTINGS, EXT_PADDING],
|
||||
drop13: CHROME_TLS_DROP13,
|
||||
drop12: CHROME_TLS_DROP12_ALPS_PADDING,
|
||||
alpn: H2_AND_HTTP11,
|
||||
padding_to: Some(512),
|
||||
grease: true,
|
||||
|
|
@ -595,15 +608,10 @@ pub(crate) const CHROME123: TlsShape = TlsShape {
|
|||
ext_order: CHROME_PADDING_AND_ECH_EXT_ORDER,
|
||||
raw_exts: CHROME_TLS_RAW_EXTS,
|
||||
suppress: &[],
|
||||
drop13: &[
|
||||
EXT_EXTENDED_MASTER_SECRET,
|
||||
EXT_RENEGOTIATION_INFO,
|
||||
EXT_EC_POINT_FORMATS,
|
||||
EXT_SESSION_TICKET,
|
||||
],
|
||||
drop13: CHROME_TLS_DROP13,
|
||||
// The padding extension goes with the pinned 1.2 hello, ALPS and the
|
||||
// version list with it.
|
||||
drop12: &[EXT_SUPPORTED_VERSIONS, EXT_APPLICATION_SETTINGS, EXT_PADDING],
|
||||
drop12: CHROME_TLS_DROP12_ALPS_PADDING,
|
||||
alpn: H2_AND_HTTP11,
|
||||
padding_to: Some(512),
|
||||
grease: true,
|
||||
|
|
@ -647,13 +655,8 @@ pub(crate) const CHROME116: TlsShape = TlsShape {
|
|||
ext_order: CHROME_TLS_EXT_ORDER,
|
||||
raw_exts: CHROME_TLS_RAW_EXTS,
|
||||
suppress: &[],
|
||||
drop13: &[
|
||||
EXT_EXTENDED_MASTER_SECRET,
|
||||
EXT_RENEGOTIATION_INFO,
|
||||
EXT_EC_POINT_FORMATS,
|
||||
EXT_SESSION_TICKET,
|
||||
],
|
||||
drop12: &[EXT_SUPPORTED_VERSIONS, EXT_APPLICATION_SETTINGS, EXT_PADDING],
|
||||
drop13: CHROME_TLS_DROP13,
|
||||
drop12: CHROME_TLS_DROP12_ALPS_PADDING,
|
||||
alpn: H2_AND_HTTP11,
|
||||
padding_to: Some(512),
|
||||
grease: true,
|
||||
|
|
@ -711,13 +714,8 @@ pub(crate) const CHROME115_PQ: TlsShape = TlsShape {
|
|||
ext_order: CHROME115_PQ_TLS_EXT_ORDER,
|
||||
raw_exts: CHROME_TLS_RAW_EXTS,
|
||||
suppress: &[],
|
||||
drop13: &[
|
||||
EXT_EXTENDED_MASTER_SECRET,
|
||||
EXT_RENEGOTIATION_INFO,
|
||||
EXT_EC_POINT_FORMATS,
|
||||
EXT_SESSION_TICKET,
|
||||
],
|
||||
drop12: &[EXT_SUPPORTED_VERSIONS, EXT_APPLICATION_SETTINGS],
|
||||
drop13: CHROME_TLS_DROP13,
|
||||
drop12: CHROME_TLS_DROP12_ALPS,
|
||||
alpn: H2_AND_HTTP11,
|
||||
// No padding: the spec's 1524-byte hello never takes extension 21, and
|
||||
// this build's shorter one must not either (see
|
||||
|
|
@ -774,13 +772,8 @@ pub(crate) const CHROME107: TlsShape = TlsShape {
|
|||
// Chrome sends session_ticket and psk_key_exchange_modes, so nothing is
|
||||
// suppressed — the difference from rustls' defaults is additive.
|
||||
suppress: &[],
|
||||
drop13: &[
|
||||
EXT_EXTENDED_MASTER_SECRET,
|
||||
EXT_RENEGOTIATION_INFO,
|
||||
EXT_EC_POINT_FORMATS,
|
||||
EXT_SESSION_TICKET,
|
||||
],
|
||||
drop12: &[EXT_SUPPORTED_VERSIONS, EXT_APPLICATION_SETTINGS, EXT_PADDING],
|
||||
drop13: CHROME_TLS_DROP13,
|
||||
drop12: CHROME_TLS_DROP12_ALPS_PADDING,
|
||||
alpn: H2_AND_HTTP11,
|
||||
padding_to: Some(512),
|
||||
grease: true,
|
||||
|
|
@ -824,13 +817,8 @@ pub(crate) const CHROME99_ANDROID: TlsShape = TlsShape {
|
|||
ext_order: CHROME_TLS_EXT_ORDER,
|
||||
raw_exts: CHROME_TLS_RAW_EXTS,
|
||||
suppress: &[],
|
||||
drop13: &[
|
||||
EXT_EXTENDED_MASTER_SECRET,
|
||||
EXT_RENEGOTIATION_INFO,
|
||||
EXT_EC_POINT_FORMATS,
|
||||
EXT_SESSION_TICKET,
|
||||
],
|
||||
drop12: &[EXT_SUPPORTED_VERSIONS, EXT_APPLICATION_SETTINGS, EXT_PADDING],
|
||||
drop13: CHROME_TLS_DROP13,
|
||||
drop12: CHROME_TLS_DROP12_ALPS_PADDING,
|
||||
alpn: H2_AND_HTTP11,
|
||||
padding_to: Some(512),
|
||||
grease: true,
|
||||
|
|
@ -879,13 +867,8 @@ pub(crate) const CHROME87: TlsShape = TlsShape {
|
|||
ext_order: CHROME72_TLS_EXT_ORDER,
|
||||
raw_exts: CHROME_TLS_RAW_EXTS,
|
||||
suppress: &[],
|
||||
drop13: &[
|
||||
EXT_EXTENDED_MASTER_SECRET,
|
||||
EXT_RENEGOTIATION_INFO,
|
||||
EXT_EC_POINT_FORMATS,
|
||||
EXT_SESSION_TICKET,
|
||||
],
|
||||
drop12: &[EXT_SUPPORTED_VERSIONS, EXT_PADDING],
|
||||
drop13: CHROME_TLS_DROP13,
|
||||
drop12: CHROME_TLS_DROP12_PADDING,
|
||||
alpn: H2_AND_HTTP11,
|
||||
padding_to: Some(512),
|
||||
grease: true,
|
||||
|
|
@ -928,13 +911,8 @@ pub(crate) const CHROME72: TlsShape = TlsShape {
|
|||
ext_order: CHROME72_TLS_EXT_ORDER,
|
||||
raw_exts: CHROME_TLS_RAW_EXTS,
|
||||
suppress: &[],
|
||||
drop13: &[
|
||||
EXT_EXTENDED_MASTER_SECRET,
|
||||
EXT_RENEGOTIATION_INFO,
|
||||
EXT_EC_POINT_FORMATS,
|
||||
EXT_SESSION_TICKET,
|
||||
],
|
||||
drop12: &[EXT_SUPPORTED_VERSIONS, EXT_PADDING],
|
||||
drop13: CHROME_TLS_DROP13,
|
||||
drop12: CHROME_TLS_DROP12_PADDING,
|
||||
alpn: H2_AND_HTTP11,
|
||||
padding_to: Some(512),
|
||||
grease: true,
|
||||
|
|
@ -976,13 +954,8 @@ pub(crate) const CHROME70: TlsShape = TlsShape {
|
|||
ext_order: CHROME70_TLS_EXT_ORDER,
|
||||
raw_exts: CHROME70_TLS_RAW_EXTS,
|
||||
suppress: &[],
|
||||
drop13: &[
|
||||
EXT_EXTENDED_MASTER_SECRET,
|
||||
EXT_RENEGOTIATION_INFO,
|
||||
EXT_EC_POINT_FORMATS,
|
||||
EXT_SESSION_TICKET,
|
||||
],
|
||||
drop12: &[EXT_SUPPORTED_VERSIONS, EXT_PADDING],
|
||||
drop13: CHROME_TLS_DROP13,
|
||||
drop12: CHROME_TLS_DROP12_PADDING,
|
||||
alpn: H2_AND_HTTP11,
|
||||
padding_to: Some(512),
|
||||
grease: true,
|
||||
|
|
|
|||
|
|
@ -3,16 +3,12 @@
|
|||
|
||||
use super::super::TlsFingerprint;
|
||||
use super::chrome::{
|
||||
CHROME_TLS_CIPHERS, CHROME_TLS_EXT_ORDER, CHROME_TLS_GROUPS, CHROME_TLS_RAW_EXTS,
|
||||
CHROME_TLS_SIG_ALGS,
|
||||
CHROME_TLS_CIPHERS, CHROME_TLS_DROP12_ALPS_PADDING, CHROME_TLS_DROP13, CHROME_TLS_EXT_ORDER,
|
||||
CHROME_TLS_GROUPS, CHROME_TLS_RAW_EXTS, CHROME_TLS_SIG_ALGS,
|
||||
};
|
||||
use super::super::h2::EDGE101_H2;
|
||||
use super::super::identity::EDGE101_HEADERS;
|
||||
use super::{
|
||||
BROTLI, EXT_APPLICATION_SETTINGS, EXT_EC_POINT_FORMATS, EXT_EXTENDED_MASTER_SECRET,
|
||||
EXT_PADDING, EXT_RENEGOTIATION_INFO, EXT_SESSION_TICKET, EXT_SUPPORTED_VERSIONS,
|
||||
H2_AND_HTTP11,
|
||||
};
|
||||
use super::{BROTLI, H2_AND_HTTP11};
|
||||
use super::TlsShape;
|
||||
|
||||
// Edge 101, as `curl_edge101` of curl-impersonate v2.2.3 sends it, with
|
||||
|
|
@ -36,13 +32,9 @@ pub(crate) const EDGE101: TlsShape = TlsShape {
|
|||
ext_order: CHROME_TLS_EXT_ORDER,
|
||||
raw_exts: CHROME_TLS_RAW_EXTS,
|
||||
suppress: &[],
|
||||
drop13: &[
|
||||
EXT_EXTENDED_MASTER_SECRET,
|
||||
EXT_RENEGOTIATION_INFO,
|
||||
EXT_EC_POINT_FORMATS,
|
||||
EXT_SESSION_TICKET,
|
||||
],
|
||||
drop12: &[EXT_SUPPORTED_VERSIONS, EXT_APPLICATION_SETTINGS, EXT_PADDING],
|
||||
// Chromium's drop lists, the same ones the Chrome 99–107 records use.
|
||||
drop13: CHROME_TLS_DROP13,
|
||||
drop12: CHROME_TLS_DROP12_ALPS_PADDING,
|
||||
alpn: H2_AND_HTTP11,
|
||||
padding_to: Some(512),
|
||||
grease: true,
|
||||
|
|
|
|||
|
|
@ -133,6 +133,21 @@ const FIREFOX_PRE_HYBRID_KEY_SHARE_GROUPS: &[u16] = &[
|
|||
23, // secp256r1
|
||||
];
|
||||
|
||||
/// The two extensions every Firefox drops from a hello pinned to TLS 1.3
|
||||
/// alone: `ec_point_formats`, and `session_ticket`, which Firefox sends on the
|
||||
/// 1.2 hello but not at 1.3. Unlike Chrome and Safari it keeps
|
||||
/// `extended_master_secret` and `renegotiation_info` there (see
|
||||
/// `super::super::pinned_drop`).
|
||||
const FIREFOX_TLS_DROP13: &[u16] = &[EXT_EC_POINT_FORMATS, EXT_SESSION_TICKET];
|
||||
|
||||
/// Firefox 147, 133 and 120's 1.2 drop: the 1.3-only `supported_versions`
|
||||
/// alone, because those hellos send no padding.
|
||||
const FIREFOX_TLS_DROP12: &[u16] = &[EXT_SUPPORTED_VERSIONS];
|
||||
|
||||
/// Firefox 105, 99 and 65's 1.2 drop: `supported_versions` and the 512-byte
|
||||
/// padding slot those older hellos carry.
|
||||
const FIREFOX_TLS_DROP12_PADDING: &[u16] = &[EXT_SUPPORTED_VERSIONS, EXT_PADDING];
|
||||
|
||||
/// Firefox 133–144's signature schemes. `curl_tor145` sends the same eleven in
|
||||
/// the same order.
|
||||
pub(crate) const FIREFOX_TLS_SIG_ALGS: &[u16] = &[
|
||||
|
|
@ -308,8 +323,8 @@ pub(crate) const FIREFOX147: TlsShape = TlsShape {
|
|||
ext_order: FIREFOX147_TLS_EXT_ORDER,
|
||||
raw_exts: FIREFOX_TLS_RAW_EXTS,
|
||||
suppress: &[],
|
||||
drop13: &[EXT_EC_POINT_FORMATS, EXT_SESSION_TICKET],
|
||||
drop12: &[EXT_SUPPORTED_VERSIONS],
|
||||
drop13: FIREFOX_TLS_DROP13,
|
||||
drop12: FIREFOX_TLS_DROP12,
|
||||
alpn: H2_AND_HTTP11,
|
||||
padding_to: None,
|
||||
grease: false,
|
||||
|
|
@ -374,8 +389,8 @@ pub(crate) const FIREFOX133: TlsShape = TlsShape {
|
|||
suppress: &[],
|
||||
// Firefox 133 keeps `extended_master_secret` and `renegotiation_info` at
|
||||
// 1.3, and drops `session_ticket` there.
|
||||
drop13: &[EXT_EC_POINT_FORMATS, EXT_SESSION_TICKET],
|
||||
drop12: &[EXT_SUPPORTED_VERSIONS],
|
||||
drop13: FIREFOX_TLS_DROP13,
|
||||
drop12: FIREFOX_TLS_DROP12,
|
||||
alpn: H2_AND_HTTP11,
|
||||
// Firefox sends no padding; the JA3 this profile is pinned to has none.
|
||||
padding_to: None,
|
||||
|
|
@ -429,8 +444,8 @@ pub(crate) const FIREFOX120: TlsShape = TlsShape {
|
|||
ext_order: FIREFOX120_TLS_EXT_ORDER,
|
||||
raw_exts: FIREFOX_TLS_RAW_EXTS,
|
||||
suppress: &[],
|
||||
drop13: &[EXT_EC_POINT_FORMATS, EXT_SESSION_TICKET],
|
||||
drop12: &[EXT_SUPPORTED_VERSIONS],
|
||||
drop13: FIREFOX_TLS_DROP13,
|
||||
drop12: FIREFOX_TLS_DROP12,
|
||||
alpn: H2_AND_HTTP11,
|
||||
padding_to: None,
|
||||
grease: false,
|
||||
|
|
@ -481,8 +496,8 @@ pub(crate) const FIREFOX105: TlsShape = TlsShape {
|
|||
ext_order: FIREFOX99_TLS_EXT_ORDER,
|
||||
raw_exts: FIREFOX_TLS_RAW_EXTS,
|
||||
suppress: &[],
|
||||
drop13: &[EXT_EC_POINT_FORMATS, EXT_SESSION_TICKET],
|
||||
drop12: &[EXT_SUPPORTED_VERSIONS, EXT_PADDING],
|
||||
drop13: FIREFOX_TLS_DROP13,
|
||||
drop12: FIREFOX_TLS_DROP12_PADDING,
|
||||
alpn: H2_AND_HTTP11,
|
||||
padding_to: Some(512),
|
||||
grease: false,
|
||||
|
|
@ -522,8 +537,8 @@ pub(crate) const FIREFOX99: TlsShape = TlsShape {
|
|||
ext_order: FIREFOX99_TLS_EXT_ORDER,
|
||||
raw_exts: FIREFOX_TLS_RAW_EXTS,
|
||||
suppress: &[],
|
||||
drop13: &[EXT_EC_POINT_FORMATS, EXT_SESSION_TICKET],
|
||||
drop12: &[EXT_SUPPORTED_VERSIONS, EXT_PADDING],
|
||||
drop13: FIREFOX_TLS_DROP13,
|
||||
drop12: FIREFOX_TLS_DROP12_PADDING,
|
||||
alpn: H2_AND_HTTP11,
|
||||
padding_to: Some(512),
|
||||
grease: false,
|
||||
|
|
@ -565,8 +580,8 @@ pub(crate) const FIREFOX65: TlsShape = TlsShape {
|
|||
ext_order: FIREFOX65_TLS_EXT_ORDER,
|
||||
raw_exts: FIREFOX_TLS_RAW_EXTS,
|
||||
suppress: &[],
|
||||
drop13: &[EXT_EC_POINT_FORMATS, EXT_SESSION_TICKET],
|
||||
drop12: &[EXT_SUPPORTED_VERSIONS, EXT_PADDING],
|
||||
drop13: FIREFOX_TLS_DROP13,
|
||||
drop12: FIREFOX_TLS_DROP12_PADDING,
|
||||
alpn: H2_AND_HTTP11,
|
||||
padding_to: Some(512),
|
||||
grease: false,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use super::super::TlsFingerprint;
|
||||
use super::super::identity::GO127_HEADERS;
|
||||
use super::safari::SAFARI_TLS_PQ_GROUPS;
|
||||
use super::{
|
||||
EXT_ALPN, EXT_EC_POINT_FORMATS, EXT_EXTENDED_MASTER_SECRET, EXT_KEY_SHARE,
|
||||
EXT_PSK_KEY_EXCHANGE_MODES, EXT_RENEGOTIATION_INFO, EXT_SCT, EXT_SERVER_NAME,
|
||||
|
|
@ -44,13 +45,13 @@ const GO_TLS_CIPHERS: &[u16] = &[
|
|||
/// Go's curves: the hybrid group Go 1.24 added in front of Go's classical four,
|
||||
/// all of them shared-classical in this build's provider. The key share follows
|
||||
/// from them (see the record).
|
||||
const GO_TLS_GROUPS: &[u16] = &[
|
||||
4588, // X25519MLKEM768
|
||||
29, // X25519
|
||||
23, // secp256r1
|
||||
24, // secp384r1
|
||||
25, // secp521r1
|
||||
];
|
||||
///
|
||||
/// Defined once as [`SAFARI_TLS_PQ_GROUPS`], the list it is byte-identical to:
|
||||
/// Go 1.27's `defaultCurvePreferences` and Safari 26.0 both put X25519MLKEM768
|
||||
/// in front of X25519, secp256r1, secp384r1 and secp521r1 — an accident of two
|
||||
/// unrelated clients, so the five groups are written there and both records
|
||||
/// reference them.
|
||||
const GO_TLS_GROUPS: &[u16] = SAFARI_TLS_PQ_GROUPS;
|
||||
|
||||
/// Go's signature schemes, in the order `defaultSupportedSignatureAlgorithms`
|
||||
/// lists them: RSA-PSS first, Ed25519 between the P-256 and the P-384 ECDSA
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ pub(crate) struct TlsShape {
|
|||
pub(crate) grease: bool,
|
||||
/// Whether the impersonated client carries `encrypted_client_hello` (65037).
|
||||
///
|
||||
/// Nine of the nineteen shapes do, and every one of them does it as GREASE:
|
||||
/// Nine of the thirty shapes do, and every one of them does it as GREASE:
|
||||
/// their wrappers say `--ech true`, and curl can only fetch an ECHConfigList
|
||||
/// through DoH or `--ecl:`, neither of which the wrappers pass — so what
|
||||
/// reaches the wire is a grease extension. Measured on the bundle's own
|
||||
|
|
@ -157,6 +157,12 @@ const H2_AND_HTTP11: &[&[u8]] = &[b"h2", b"http/1.1"];
|
|||
/// brotli, exactly what Chrome 107, Chrome 133 and Edge 101 advertise.
|
||||
const BROTLI: &[u16] = &[2];
|
||||
|
||||
/// zlib (RFC 8879 code point 1), the algorithm every Safari record from 15.5 on
|
||||
/// advertises for certificate compression. Safari 15.3 is the exception its
|
||||
/// empty list records: `curl_safari153` advertises no `compress_certificate` at
|
||||
/// all.
|
||||
const ZLIB: &[u16] = &[1];
|
||||
|
||||
/// Every selectable shape, in report order: the baseline first, then Go, then one
|
||||
/// browser at a time alphabetically with the newest version of each first, and
|
||||
/// the phone shape of a version right after its desktop sibling (`go127`,
|
||||
|
|
@ -252,7 +258,7 @@ pub(crate) const EXT_SUPPORTED_VERSIONS: u16 = 43;
|
|||
pub(crate) const EXT_PSK_KEY_EXCHANGE_MODES: u16 = 45;
|
||||
pub(crate) const EXT_KEY_SHARE: u16 = 51;
|
||||
pub(crate) const EXT_RENEGOTIATION_INFO: u16 = 65281;
|
||||
/// `encrypted_client_hello` (draft-ietf-tls-esni), the extension seven shapes
|
||||
/// `encrypted_client_hello` (draft-ietf-tls-esni), the extension nine shapes
|
||||
/// carry as GREASE.
|
||||
pub(crate) const EXT_ENCRYPTED_CLIENT_HELLO: u16 = 65037;
|
||||
/// Chrome's ALPS (draft-vvv-tls-alps), as Chrome 107 and Safari send it.
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use super::{
|
|||
EXT_ALPN, EXT_COMPRESS_CERTIFICATE, EXT_EC_POINT_FORMATS, EXT_EXTENDED_MASTER_SECRET,
|
||||
EXT_KEY_SHARE, EXT_PADDING, EXT_PSK_KEY_EXCHANGE_MODES, EXT_RENEGOTIATION_INFO, EXT_SCT,
|
||||
EXT_SERVER_NAME, EXT_SESSION_TICKET, EXT_SIGNATURE_ALGORITHMS, EXT_STATUS_REQUEST,
|
||||
EXT_SUPPORTED_GROUPS, EXT_SUPPORTED_VERSIONS, H2_AND_HTTP11,
|
||||
EXT_SUPPORTED_GROUPS, EXT_SUPPORTED_VERSIONS, H2_AND_HTTP11, ZLIB,
|
||||
};
|
||||
use super::TlsShape;
|
||||
|
||||
|
|
@ -55,7 +55,12 @@ const SAFARI_TLS_GROUPS: &[u16] = &[
|
|||
|
||||
/// Safari 26.0's groups: the four above with the hybrid group in front, which
|
||||
/// is the whole TLS difference between 26.0 on macOS and 26.0 on iOS.
|
||||
const SAFARI_TLS_PQ_GROUPS: &[u16] = &[
|
||||
///
|
||||
/// Byte-identical to Go 1.27's `defaultCurvePreferences` — the list is written
|
||||
/// once here and `go.rs`'s `GO_TLS_GROUPS` aliases it, because two unrelated
|
||||
/// clients arrived at the same five groups. `pub(crate)` for that second
|
||||
/// reader.
|
||||
pub(crate) const SAFARI_TLS_PQ_GROUPS: &[u16] = &[
|
||||
4588, // X25519MLKEM768
|
||||
29, // X25519
|
||||
23, // secp256r1
|
||||
|
|
@ -269,6 +274,26 @@ const SAFARI260_TLS_RAW_EXTS: &[(u16, &[u8])] = &[
|
|||
(EXT_PADDING, &[]),
|
||||
];
|
||||
|
||||
/// The three extensions every Safari drops from a hello pinned to TLS 1.3
|
||||
/// alone: `ec_point_formats`, plus `extended_master_secret` and
|
||||
/// `renegotiation_info`, which a Safari 1.3 hello does not carry. One list for
|
||||
/// all eight records — measured on `curl_safari155` through `curl_safari260_ios`
|
||||
/// (see `super::super::pinned_drop`).
|
||||
const SAFARI_TLS_DROP13: &[u16] = &[
|
||||
EXT_EXTENDED_MASTER_SECRET,
|
||||
EXT_RENEGOTIATION_INFO,
|
||||
EXT_EC_POINT_FORMATS,
|
||||
];
|
||||
|
||||
/// What every Safari but 26.0 on macOS drops from a hello pinned to TLS 1.2
|
||||
/// alone: the 1.3-only `supported_versions`, and the padding — a 1.2 hello is
|
||||
/// already under the 256-byte floor where BoringSSL stops padding.
|
||||
const SAFARI_TLS_DROP12: &[u16] = &[EXT_SUPPORTED_VERSIONS, EXT_PADDING];
|
||||
|
||||
/// Safari 26.0 on macOS's 1.2 drop: `supported_versions` alone, because that
|
||||
/// hello sends no padding at all, so there is no slot to take out.
|
||||
const SAFARI_TLS_DROP12_NO_PADDING: &[u16] = &[EXT_SUPPORTED_VERSIONS];
|
||||
|
||||
// Safari 26.0 on macOS, as `curl_safari260` sends it: the first Safari in
|
||||
// the bundle that offers the hybrid group, and the first client of any
|
||||
// family whose hello carries no padding *and* a session ticket.
|
||||
|
|
@ -292,8 +317,8 @@ pub(crate) const SAFARI260: TlsShape = TlsShape {
|
|||
raw_exts: SAFARI260_TLS_RAW_EXTS,
|
||||
// The session ticket is part of this shape, so nothing is suppressed.
|
||||
suppress: &[],
|
||||
drop13: &[EXT_EXTENDED_MASTER_SECRET, EXT_RENEGOTIATION_INFO, EXT_EC_POINT_FORMATS],
|
||||
drop12: &[EXT_SUPPORTED_VERSIONS],
|
||||
drop13: SAFARI_TLS_DROP13,
|
||||
drop12: SAFARI_TLS_DROP12_NO_PADDING,
|
||||
alpn: H2_AND_HTTP11,
|
||||
// No padding either, and the hello says so.
|
||||
padding_to: None,
|
||||
|
|
@ -301,7 +326,7 @@ pub(crate) const SAFARI260: TlsShape = TlsShape {
|
|||
permute_extensions: false,
|
||||
ech: false,
|
||||
priority_on_h1: false,
|
||||
cert_compression: &[1],
|
||||
cert_compression: ZLIB,
|
||||
key_share_groups: None,
|
||||
pq: true,
|
||||
// Safari 26 offers 1.3 and 1.2 only, where 15.5–18.4 still listed 1.1
|
||||
|
|
@ -312,11 +337,13 @@ pub(crate) const SAFARI260: TlsShape = TlsShape {
|
|||
};
|
||||
// Safari 26.0 on iOS, as `curl_safari260_ios` sends it.
|
||||
//
|
||||
// The same release as the macOS row with three differences a middlebox can
|
||||
// read: no hybrid group (iOS keeps X25519 first), the padding extension back
|
||||
// (its hello measures 512 bytes where macOS 26's does not), and the iOS UA.
|
||||
// Everything else, the cipher order and the preface included, matches 26.0
|
||||
// on macOS.
|
||||
// The same release as the macOS row with four differences a middlebox can
|
||||
// read: no hybrid group (iOS keeps X25519 first), the older TLS 1.3 suite
|
||||
// order — `4865-4866-4867`, where macOS 26 reorders them to `4866-4867-4865`,
|
||||
// which is the cipher list of 15.5 rather than the macOS row's own — the
|
||||
// padding extension back (its hello measures 512 bytes where macOS 26's does
|
||||
// not), and the iOS UA. The preface is the only thing that matches 26.0 on
|
||||
// macOS.
|
||||
pub(crate) const SAFARI260_IOS: TlsShape = TlsShape {
|
||||
variant: TlsFingerprint::Safari260Ios,
|
||||
code: "safari260ios",
|
||||
|
|
@ -330,15 +357,15 @@ pub(crate) const SAFARI260_IOS: TlsShape = TlsShape {
|
|||
ext_order: SAFARI260_IOS_TLS_EXT_ORDER,
|
||||
raw_exts: SAFARI260_TLS_RAW_EXTS,
|
||||
suppress: &[],
|
||||
drop13: &[EXT_EXTENDED_MASTER_SECRET, EXT_RENEGOTIATION_INFO, EXT_EC_POINT_FORMATS],
|
||||
drop12: &[EXT_SUPPORTED_VERSIONS, EXT_PADDING],
|
||||
drop13: SAFARI_TLS_DROP13,
|
||||
drop12: SAFARI_TLS_DROP12,
|
||||
alpn: H2_AND_HTTP11,
|
||||
padding_to: Some(512),
|
||||
grease: true,
|
||||
permute_extensions: false,
|
||||
ech: false,
|
||||
priority_on_h1: false,
|
||||
cert_compression: &[1],
|
||||
cert_compression: ZLIB,
|
||||
key_share_groups: None,
|
||||
pq: false,
|
||||
legacy_versions: &[],
|
||||
|
|
@ -365,15 +392,15 @@ pub(crate) const SAFARI184_IOS: TlsShape = TlsShape {
|
|||
ext_order: SAFARI_TLS_EXT_ORDER,
|
||||
raw_exts: SAFARI_TLS_RAW_EXTS,
|
||||
suppress: &[EXT_SESSION_TICKET],
|
||||
drop13: &[EXT_EXTENDED_MASTER_SECRET, EXT_RENEGOTIATION_INFO, EXT_EC_POINT_FORMATS],
|
||||
drop12: &[EXT_SUPPORTED_VERSIONS, EXT_PADDING],
|
||||
drop13: SAFARI_TLS_DROP13,
|
||||
drop12: SAFARI_TLS_DROP12,
|
||||
alpn: H2_AND_HTTP11,
|
||||
padding_to: Some(512),
|
||||
grease: true,
|
||||
permute_extensions: false,
|
||||
ech: false,
|
||||
priority_on_h1: false,
|
||||
cert_compression: &[1],
|
||||
cert_compression: ZLIB,
|
||||
key_share_groups: None,
|
||||
pq: false,
|
||||
legacy_versions: &[0x0302, 0x0301],
|
||||
|
|
@ -413,15 +440,15 @@ pub(crate) const SAFARI180: TlsShape = TlsShape {
|
|||
ext_order: SAFARI_TLS_EXT_ORDER,
|
||||
raw_exts: SAFARI_TLS_RAW_EXTS,
|
||||
suppress: &[EXT_SESSION_TICKET],
|
||||
drop13: &[EXT_EXTENDED_MASTER_SECRET, EXT_RENEGOTIATION_INFO, EXT_EC_POINT_FORMATS],
|
||||
drop12: &[EXT_SUPPORTED_VERSIONS, EXT_PADDING],
|
||||
drop13: SAFARI_TLS_DROP13,
|
||||
drop12: SAFARI_TLS_DROP12,
|
||||
alpn: H2_AND_HTTP11,
|
||||
padding_to: Some(512),
|
||||
grease: true,
|
||||
permute_extensions: false,
|
||||
ech: false,
|
||||
priority_on_h1: false,
|
||||
cert_compression: &[1],
|
||||
cert_compression: ZLIB,
|
||||
key_share_groups: None,
|
||||
pq: false,
|
||||
legacy_versions: &[0x0302, 0x0301],
|
||||
|
|
@ -444,15 +471,15 @@ pub(crate) const SAFARI172_IOS: TlsShape = TlsShape {
|
|||
ext_order: SAFARI_TLS_EXT_ORDER,
|
||||
raw_exts: SAFARI_TLS_RAW_EXTS,
|
||||
suppress: &[EXT_SESSION_TICKET],
|
||||
drop13: &[EXT_EXTENDED_MASTER_SECRET, EXT_RENEGOTIATION_INFO, EXT_EC_POINT_FORMATS],
|
||||
drop12: &[EXT_SUPPORTED_VERSIONS, EXT_PADDING],
|
||||
drop13: SAFARI_TLS_DROP13,
|
||||
drop12: SAFARI_TLS_DROP12,
|
||||
alpn: H2_AND_HTTP11,
|
||||
padding_to: Some(512),
|
||||
grease: true,
|
||||
permute_extensions: false,
|
||||
ech: false,
|
||||
priority_on_h1: false,
|
||||
cert_compression: &[1],
|
||||
cert_compression: ZLIB,
|
||||
key_share_groups: None,
|
||||
pq: false,
|
||||
legacy_versions: &[0x0302, 0x0301],
|
||||
|
|
@ -480,15 +507,15 @@ pub(crate) const SAFARI170: TlsShape = TlsShape {
|
|||
ext_order: SAFARI_TLS_EXT_ORDER,
|
||||
raw_exts: SAFARI_TLS_RAW_EXTS,
|
||||
suppress: &[EXT_SESSION_TICKET],
|
||||
drop13: &[EXT_EXTENDED_MASTER_SECRET, EXT_RENEGOTIATION_INFO, EXT_EC_POINT_FORMATS],
|
||||
drop12: &[EXT_SUPPORTED_VERSIONS, EXT_PADDING],
|
||||
drop13: SAFARI_TLS_DROP13,
|
||||
drop12: SAFARI_TLS_DROP12,
|
||||
alpn: H2_AND_HTTP11,
|
||||
padding_to: Some(512),
|
||||
grease: true,
|
||||
permute_extensions: false,
|
||||
ech: false,
|
||||
priority_on_h1: false,
|
||||
cert_compression: &[1],
|
||||
cert_compression: ZLIB,
|
||||
key_share_groups: None,
|
||||
pq: false,
|
||||
legacy_versions: &[0x0302, 0x0301],
|
||||
|
|
@ -525,16 +552,15 @@ pub(crate) const SAFARI155: TlsShape = TlsShape {
|
|||
raw_exts: SAFARI_TLS_RAW_EXTS,
|
||||
// rustls sends session_ticket; Safari does not.
|
||||
suppress: &[EXT_SESSION_TICKET],
|
||||
drop13: &[EXT_EXTENDED_MASTER_SECRET, EXT_RENEGOTIATION_INFO, EXT_EC_POINT_FORMATS],
|
||||
drop12: &[EXT_SUPPORTED_VERSIONS, EXT_PADDING],
|
||||
drop13: SAFARI_TLS_DROP13,
|
||||
drop12: SAFARI_TLS_DROP12,
|
||||
alpn: H2_AND_HTTP11,
|
||||
padding_to: Some(512),
|
||||
grease: true,
|
||||
permute_extensions: false,
|
||||
ech: false,
|
||||
priority_on_h1: false,
|
||||
// zlib, exactly what Safari advertises.
|
||||
cert_compression: &[1],
|
||||
cert_compression: ZLIB,
|
||||
key_share_groups: None,
|
||||
pq: false,
|
||||
// Safari 15.5 keeps offering TLS 1.1 and 1.0 behind 1.2, and
|
||||
|
|
@ -567,8 +593,8 @@ pub(crate) const SAFARI153: TlsShape = TlsShape {
|
|||
ext_order: SAFARI153_TLS_EXT_ORDER,
|
||||
raw_exts: SAFARI_TLS_RAW_EXTS,
|
||||
suppress: &[EXT_SESSION_TICKET],
|
||||
drop13: &[EXT_EXTENDED_MASTER_SECRET, EXT_RENEGOTIATION_INFO, EXT_EC_POINT_FORMATS],
|
||||
drop12: &[EXT_SUPPORTED_VERSIONS, EXT_PADDING],
|
||||
drop13: SAFARI_TLS_DROP13,
|
||||
drop12: SAFARI_TLS_DROP12,
|
||||
alpn: H2_AND_HTTP11,
|
||||
padding_to: Some(512),
|
||||
grease: true,
|
||||
|
|
|
|||
|
|
@ -55,9 +55,9 @@ const TOR_KEY_SHARE_GROUPS: &[u16] = &[
|
|||
];
|
||||
|
||||
/// Tor Browser 14.5's extension order, exactly the wrapper's
|
||||
/// `--tls-extension-order`: no GREASE (Tor does not grease), no
|
||||
/// `compress_certificate`, no SCT, and ECH last — which this build omits, so
|
||||
/// the order stops at `record_size_limit`.
|
||||
/// `--tls-extension-order`: no GREASE slots (Tor does not grease), no
|
||||
/// `compress_certificate`, no SCT, and `encrypted_client_hello` last, sent as
|
||||
/// GREASE (see the record).
|
||||
const TOR_TLS_EXT_ORDER: &[u16] = &[
|
||||
EXT_SERVER_NAME,
|
||||
EXT_EXTENDED_MASTER_SECRET,
|
||||
|
|
@ -92,8 +92,10 @@ const TOR_TLS_EXT_ORDER: &[u16] = &[
|
|||
// with, and it drops `compress_certificate` — so a hello-shaped block that
|
||||
// reacts to extension 27 alone cannot be what passes Tor.
|
||||
//
|
||||
// One deviation: `encrypted_client_hello` (last in the wrapper's order) is
|
||||
// omitted for the reason in the Firefox record.
|
||||
// The wrapper's `--ech true` is sent as GREASE, the same as every other ECH
|
||||
// wrapper: curl fetches no real ECHConfigList, for the reason in the Firefox
|
||||
// record, so what reaches the wire is the grease body and the extension is
|
||||
// present, not omitted.
|
||||
pub(crate) const TOR145: TlsShape = TlsShape {
|
||||
variant: TlsFingerprint::Tor145,
|
||||
code: "tor145",
|
||||
|
|
|
|||
|
|
@ -311,17 +311,17 @@ fn the_shuffling_shapes_are_the_chromium_ones_from_110_on() {
|
|||
);
|
||||
}
|
||||
|
||||
/// The eight shapes whose client carries `encrypted_client_hello`, and the body
|
||||
/// The nine shapes whose client carries `encrypted_client_hello`, and the body
|
||||
/// they carry.
|
||||
///
|
||||
/// Seven of the list are the wrappers that name `--ech true` — `curl_chrome123`,
|
||||
/// `curl_chrome131`, `curl_chrome131_android`, `curl_chrome146`,
|
||||
/// `curl_firefox133`, `curl_firefox147` and `curl_tor145` — and every one of
|
||||
/// them is GREASE: curl needs DoH or an explicit `--ecl:` to have a real
|
||||
/// config, and no wrapper passes either. The eighth is `firefox120`, whose uTLS
|
||||
/// spec carries the library's own `GREASEEncryptedClientHelloExtension`: the
|
||||
/// extension is in the shape its JA3 and JA4 were read from, and both hashes
|
||||
/// see only its presence.
|
||||
/// Eight of the list are the wrappers that name `--ech true` —
|
||||
/// `curl_chrome123`, `curl_chrome131`, `curl_chrome131_android`,
|
||||
/// `curl_chrome133a`, `curl_chrome146`, `curl_firefox133`, `curl_firefox147`
|
||||
/// and `curl_tor145` — and every one of them is GREASE: curl needs DoH or an
|
||||
/// explicit `--ecl:` to have a real config, and no wrapper passes either. The
|
||||
/// ninth is `firefox120`, whose uTLS spec carries the library's own
|
||||
/// `GREASEEncryptedClientHelloExtension`: the extension is in the shape its JA3
|
||||
/// and JA4 were read from, and both hashes see only its presence.
|
||||
///
|
||||
/// The body is the GREASE form of draft-ietf-tls-esni §6.2 — outer, a cipher
|
||||
/// suite, a random `config_id`, an `enc` of the KEM's public-key length, and a
|
||||
|
|
@ -1352,11 +1352,11 @@ fn profiles_that_share_a_tls_shape_send_the_same_hello() {
|
|||
for version in [TlsVersion::Any, TlsVersion::Tls13, TlsVersion::Tls12] {
|
||||
same(TlsFingerprint::Edge101, TlsFingerprint::Chrome107, version);
|
||||
// The M4 rows that are an identity rather than a hello: Chrome 99's
|
||||
// Android build, Chrome 136 (which is Chrome 133's shape), Safari 18.4
|
||||
// on iOS and Firefox 144.
|
||||
// Android build, Chrome 133's identity over the same hello Chrome 146
|
||||
// sends, and Safari 18.4 on iOS.
|
||||
for (left, right) in [
|
||||
(TlsFingerprint::Chrome99Android, TlsFingerprint::Chrome107),
|
||||
(TlsFingerprint::Chrome146, TlsFingerprint::Chrome146),
|
||||
(TlsFingerprint::Chrome133, TlsFingerprint::Chrome146),
|
||||
(TlsFingerprint::Safari184Ios, TlsFingerprint::Safari180),
|
||||
] {
|
||||
same(left, right, version);
|
||||
|
|
@ -1441,16 +1441,16 @@ fn added_shapes_match_the_captures_they_were_read_from() {
|
|||
assert_eq!(got.split('_').nth(1), Some("8daaf6152771"), "{fingerprint}: cipher hash");
|
||||
}
|
||||
|
||||
// Chrome 133's hello, which 136 shares: the pin is the one that test
|
||||
// carries. Both shuffle a GREASE ECH body whose length is drawn per
|
||||
// connection, so the comparison is the sorted extension list and the two
|
||||
// hashes — the length is not a property of the shape.
|
||||
let ours = client_hello_full(TlsFingerprint::Chrome146, TlsVersion::Any);
|
||||
// Chrome 133's record is Chrome 146's hello under the 133 identity: the pin
|
||||
// is the one that test carries. Both shuffle a GREASE ECH body whose length
|
||||
// is drawn per connection, so the comparison is the sorted extension list
|
||||
// and the two hashes — the length is not a property of the shape.
|
||||
let ours = client_hello_full(TlsFingerprint::Chrome133, TlsVersion::Any);
|
||||
let theirs = client_hello_full(TlsFingerprint::Chrome146, TlsVersion::Any);
|
||||
assert_eq!(
|
||||
(order_independent_ja3(&ours.0), ours.2),
|
||||
(order_independent_ja3(&theirs.0), theirs.2),
|
||||
"chrome136 sends Chrome 133's hello"
|
||||
"chrome133 and chrome146 send one hello"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue