Skip to content

JA4T: no text specification, and Rust diverges from Wireshark/Zeek #304

Description

@elpy1

Summary

The technical specification for JA4T is severely lacking in the repo -
technical_details/ contains only JA4T.png, and the only textual
definitions are a one-line table entry in technical_details/README.md, a
single example string in the root README, external blogs, and a format
comment in rust/ja4/src/tcp.rs:121-126.

Without a spec, the three implementations that do exist (Rust, Wireshark,
Zeek) have drifted apart on four edge cases. In each case Wireshark and Zeek
agree with each other and Rust differs (as at commit 0e54bc8).

More detail

1. Empty TCP options placeholder

For a SYN carrying zero TCP options:

  • Rust joins an empty options vector, leaving the field empty - e.g.
    64240__0_0 (rust/ja4/src/tcp.rs:128-134).
  • Wireshark emits 00 (wireshark/source/packet-ja4.c:666-680).
  • Zeek emits 00 (zeek/ja4t/main.zeek:198-202).

2. Absent MSS and window-scale formatting

  • Rust prints unwrap_or(0) with no padding -> 0
    (rust/ja4/src/tcp.rs:139-140).
  • Wireshark and Zeek print %02d -> 00 (packet-ja4.c:668-677;
    zeek/ja4t/main.zeek:204-210).

A SYN with no MSS and no window-scale option is 65535_00_00_00 in
Wireshark/Zeek but 65535__0_0 in Rust (combined with 1 above).

3. Duplicated MSS / window-scale options

If MSS or window-scale appears more than once in the options list:

  • Rust takes the first occurrence (rust/ja4/src/tcp.rs:76-85).
  • Wireshark and Zeek overwrite on each occurrence, so the last wins
    (packet-ja4.c:1461-1466; zeek/ja4t/main.zeek:109-114).

(The ordered option-kind list itself is handled identically everywhere - no
implementation deduplicates it.)

4. ECN-flagged SYNs

A SYN negotiating ECN sets CWR and ECE (flags 0xC2):

  • Rust detects an initial SYN via bitmask (SYN set, ACK clear), explicitly
    ignoring CWR/ECE; a unit test asserts 0xC2 is accepted
    (rust/ja4/src/tcp.rs:14-15,145-159).
  • Wireshark requires tcp_flags == 0x02 exactly (packet-ja4.c:1266) - an
    ECN-setup SYN produces no JA4T.
  • Zeek requires flags equal to TH_SYN exactly (zeek/ja4t/main.zeek:126)
    • skipped also.

So ECN-capable clients are fingerprinted by Rust and silently invisible to
Wireshark and Zeek.

Ask

  1. Publish a technical_details/JA4T.md defining the field layout
    (window_size_options_mss_windowscale) and the canonical behavior for:
    empty options, absent MSS/window-scale, duplicated options, and which TCP
    flag combinations qualify as a fingerprintable SYN.
  2. Align implementations.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions