ja4 0.1.0

A fast, pure-rust, no_std implementation of the JA4 TLS client fingerprinting algorithm.
Documentation
  • Coverage
  • 100%
    5 out of 5 items documented1 out of 4 items with examples
  • Size
  • Source code size: 70.04 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 274.97 kB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 5s Average build duration of successful builds.
  • all releases: 5s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • Homepage
  • Repository
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • hacer-bark

ja4

Crates.io Docs.rs License no_std Safety

A high-performance, production-ready, #![no_std] Rust implementation of the JA4 TLS Client Fingerprinting algorithm.

JA4 is a modular, human-readable TLS client fingerprinting standard designed to replace the legacy JA3 method. It normalizes TLS GREASE values, sorts ciphers and extensions, and formats the output into three structured, searchable segments (a_b_c) representing:

  • Part a: Connection characteristics (protocol, version, SNI, ciphers/extensions counts, first ALPN chars).
  • Part b: A truncated hash of the sorted list of cipher suites.
  • Part c: A truncated hash of the sorted list of extensions and signature algorithms.

Features

  • Zero-Allocation: No heap allocations (std::vec, std::string, etc.) on the parser's critical path.
  • Pure Rust: 100% Rust implementation utilizing the pure-Rust sha2 crate.
  • Unsafe-Free: Enforces #![forbid(unsafe_code)] for high-assurance environments.
  • no_std Support: Compiles out-of-the-box in bare-metal, embedded, and kernel-level (e.g., eBPF/Wasm) environments.
  • Robust & Safe: Extensively bounds-checked and panic-free parser.

Installation

Add the following to your Cargo.toml:

[dependencies]
ja4 = "0.1"

Usage Example

TCP (with TLS Record Header)

use ja4::parse_ja4;

fn main() {
    // A raw TLS ClientHello packet captured from a TCP stream
    let packet: &[u8] = &[
        0x16, 0x03, 0x01, 0x00, 0x2f, // TLS Record Header
        0x01, 0x00, 0x00, 0x2b,       // Handshake Header (ClientHello)
        0x03, 0x03,                   // Version TLS 1.2
        // ... (remaining payload / random / ciphers / extensions)
    ];

    match parse_ja4(packet, false) {
        Some(result) => {
            println!("JA4 Fingerprint: {}", result.as_str());
            // Outputs: t12i010000_8daaf6152771_000000000000 (example)
        }
        None => {
            eprintln!("Failed to parse ClientHello packet.");
        }
    }
}

QUIC (No Record Header)

When analyzing QUIC-based TLS (e.g., HTTP/3), the TLS payload does not start with a 5-byte TCP TLS record header. Pass true for the is_quic parameter:

use ja4::parse_ja4;

fn parse_quic_payload(quic_tls_payload: &[u8]) {
    if let Some(result) = parse_ja4(quic_tls_payload, true) {
        println!("QUIC JA4 Fingerprint: {}", result);
    }
}

Anatomy of a JA4 Fingerprint

A JA4 fingerprint consists of three parts separated by underscores (e.g., t13d1516h2_8daaf6152771_e5627efa2ab1):

Part A: Connection Details (t13d1516h2)

  • Protocol (t or q): t for TCP, q for QUIC.
  • Version (13, 12, etc.): The highest TLS version supported by the client.
  • SNI (d or i): d if SNI (Server Name Indication) is present, i if not.
  • Ciphers Count (2 digits): Number of cipher suites offered (capped at 99).
  • Extensions Count (2 digits): Number of TLS extensions offered (capped at 99).
  • ALPN (2 chars): First and last alphanumeric characters of the first ALPN protocol (e.g., h2), or 00 if not present.

Part B: Ciphers Hash (8daaf6152771)

  • The first 12 hex characters of the SHA-256 hash of the sorted list of cipher suites.

Part C: Extensions & Signature Algorithms Hash (e5627efa2ab1)

  • The first 12 hex characters of the SHA-256 hash of the sorted list of extensions, followed by the sorted signature algorithms list.

Security & Safety

This crate is designed for use in critical traffic inspection infrastructures.

  • No Unsafe Code: Enforced by #![forbid(unsafe_code)].
  • Panic Protection: Every index, slice, and offset lookup uses safe bounds-checking methods, avoiding out-of-bounds panics even on highly corrupted or malicious packets.

License

Licensed under either of:

at your option.