Summary
technical_details/JA4.md says GREASE values should be ignored anywhere JA4
sees them. The Python implementation filters GREASE from the
signature_algorithms extension (this was fixed in #60), but the Rust, Zeek,
and Wireshark implementations still include GREASE values in the JA4_c
input. A ClientHello with GREASE values within its signature_algorithms
extension (RFC 8701) therefore produces a different JA4_c hash in Python
than in the other three implementations.
All references are against commit 0e54bc8.
Details
What makes this look like an oversight: each affected implementation already
applies its GREASE filter to ciphers, extensions, and supported_versions -
signature_algorithms is the lone exception.
- Rust:
rust/ja4/src/tls.rs:276-285 (sig_hash_algs) collects
tls.handshake.sig_hash_alg values with only a 0x strip and no GREASE
check. Contrast ciphers (tls.rs:192), client extensions (tls.rs:590),
and supported_versions (tls.rs:560), which all use
TLS_GREASE_VALUES_*.
- Zeek:
zeek/ja4/helpers.zeek:125-136
(ssl_extension_signature_algorithm) appends every algorithm with no
TLS_GREASE_TYPES test. Contrast ciphers (helpers.zeek:59), extensions
(:83), and supported_versions (:113), which all check
TLS_GREASE_TYPES.
- Wireshark:
wireshark/source/packet-ja4.c:1004-1011 appends
sig_hash_alg values with no IS_GREASE_TLS guard. Contrast ciphers
(:789), extensions (:757), and supported_version (:1017), which all
call IS_GREASE_TLS.
For reference, Python filters correctly: python/common.py:211
(get_signature_algorithms filters against GREASE_TABLE).
Impact
Any client with GREASE in signature_algorithms fingerprints differently (JA4_c)
across the official implementations.
Suggested resolution
Align implementations and apply each implementation's existing GREASE filter
to the signature_algorithms collection, matching the spec's
"ignore GREASE values anywhere it sees them."
Summary
technical_details/JA4.mdsays GREASE values should be ignored anywhere JA4sees them. The Python implementation filters GREASE from the
signature_algorithms extension (this was fixed in #60), but the Rust, Zeek,
and Wireshark implementations still include GREASE values in the
JA4_cinput. A ClientHello with GREASE values within its signature_algorithms
extension (RFC 8701) therefore produces a different
JA4_chash in Pythonthan in the other three implementations.
All references are against commit
0e54bc8.Details
What makes this look like an oversight: each affected implementation already
applies its GREASE filter to ciphers, extensions, and supported_versions -
signature_algorithms is the lone exception.
rust/ja4/src/tls.rs:276-285(sig_hash_algs) collectstls.handshake.sig_hash_algvalues with only a0xstrip and no GREASEcheck. Contrast ciphers (
tls.rs:192), client extensions (tls.rs:590),and supported_versions (
tls.rs:560), which all useTLS_GREASE_VALUES_*.zeek/ja4/helpers.zeek:125-136(
ssl_extension_signature_algorithm) appends every algorithm with noTLS_GREASE_TYPEStest. Contrast ciphers (helpers.zeek:59), extensions(
:83), and supported_versions (:113), which all checkTLS_GREASE_TYPES.wireshark/source/packet-ja4.c:1004-1011appendssig_hash_algvalues with noIS_GREASE_TLSguard. Contrast ciphers(
:789), extensions (:757), and supported_version (:1017), which allcall
IS_GREASE_TLS.For reference, Python filters correctly:
python/common.py:211(
get_signature_algorithmsfilters againstGREASE_TABLE).Impact
Any client with GREASE in signature_algorithms fingerprints differently (
JA4_c)across the official implementations.
Suggested resolution
Align implementations and apply each implementation's existing GREASE filter
to the signature_algorithms collection, matching the spec's
"ignore GREASE values anywhere it sees them."