Repository navigation
fix(python): handle ALPN edge cases - #322
Conversation
An empty ALPN value (a zero-length protocol name inside the ALPN
extension) made ord(alpn[0]) raise IndexError in to_ja4 and to_ja4s.
to_ja4s is not guarded in main(), so one ServerHello with an empty ALPN
aborted the whole run and every fingerprint from the capture was lost;
in to_ja4 the exception was swallowed and the stream silently dropped
from the output.
A single-character ALPN also rendered as one character where the
fingerprint expects two, and non-ascii was only checked at the front,
so "ex" became "99" while the rust implementation yields "9x".
Mirror first_last() from rust/ja4/src/tls.rs: keep the first and last
character, replace a non-ascii character with '9' per character, pad a
single character with '0', and treat an empty value like a missing
extension ('00').
|
Thanks for fixing the empty-ALPN crash - that part looks good. I’m not sure Rust is the right reference for these edge cases, though. For a one-character ALPN, the current spec treats the character as both first and last ("x" -> "xx"), and that’s also what Wireshark and Zeek do. Rust’s "x0" is the odd one out. The non-alphanumeric handling differs too: the spec and Wireshark use the hex fallback, Rust replaces characters with "9", and Zeek leaves them as-is. So matching Rust here would still leave Python inconsistent with the documented behavior and the other implementations. Could you take another look at these cases? Feel free to open separate issues for inconsistencies in the other implementations if they aren’t already tracked. |
|
I pushed a small follow-up commit to keep this PR focused. Empty/missing ALPN still maps to I left the Rust-style non-ASCII substitution in place for now and created follow-up issue #326. |
An empty ALPN value — a zero-length protocol name inside the ALPN extension — made
ord(alpn[0])raiseIndexErrorinto_ja4()andto_ja4s():tshark -T ekreportstls.handshake.extensions_alpn_stras an empty string when the ALPN extension carries a zero-length name, so one ServerHello like that aborts the whole run into_ja4s()(it isn't guarded inmain()), losing every fingerprint from the capture. Into_ja4()the exception is swallowed and streams with an empty client-side ALPN silently disappear from the output.Two smaller divergences from the rust implementation came from the same block: a single-character ALPN rendered as one character instead of two (
xinstead ofx0), and non-ascii was only checked at position 0 (éxbecame99where rust yields9x).The handling now mirrors
first_last()inrust/ja4/src/tls.rs: first and last character of the value, non-ascii replaced with9per character, a single character padded with0, and an empty value treated like a missing extension (00).to_ja4()andto_ja4s()share one helper now.Update: A follow-up commit changed single-character ALPN handling to match the current spec (
x→xx).