ja3

package
v0.783.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 29, 2026 License: AGPL-3.0 Imports: 8 Imported by: 0

Documentation

Overview

Package ja3 computes the JA3 TLS-client fingerprint from a captured ClientHello — the fingerprint format used across IDS / threat-intel tooling (Suricata, Zeek, every major EDR) to identify a TLS client (and the malware family behind it) independent of IP or SNI.

JA3 (Althouse et al., Salesforce) concatenates five ClientHello fields —

SSLVersion,Cipher,SSLExtension,EllipticCurve,EllipticCurvePointFormat

— values dash-joined, fields comma-joined, then takes the MD5. GREASE values (RFC 8701) are removed from the cipher, extension, and curve lists so a client that randomises GREASE still fingerprints to one hash. This package implements that algorithm over the raw ClientHello wire bytes.

Wrap-vs-native: native — a bounds-checked walk of the TLS record / handshake / ClientHello structure (RFC 5246 §7.4.1.2) + stdlib crypto/md5. No new go.mod dependency. The field extraction and GREASE filtering are pinned against the Salesforce reference implementation (pyja3) on a real openssl ClientHello and a hand-built GREASE-bearing one, and the string→MD5 step against the two worked examples in the JA3 spec.

Scope: JA3 (client). JA3S (the server-side ServerHello variant) is detected and reported as not-yet-supported rather than mis-fingerprinted.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Hello added in v0.651.0

type Hello struct {
	LegacyVersion     int
	CiphersRaw        []uint16 // in offer order, GREASE retained
	ExtOrder          []uint16 // extension types in appearance order, GREASE retained
	Curves            []int    // supported_groups (ext 10), GREASE removed
	PointFormats      []int    // ec_point_formats (ext 11)
	SNI               string
	SNIPresent        bool
	ALPN              []string // application_layer_protocol_negotiation (ext 16)
	SupportedVersions []uint16 // supported_versions (ext 43), GREASE retained
	SigAlgs           []uint16 // signature_algorithms (ext 13), in order, GREASE retained
}

Hello is the parsed view of a ClientHello that both JA3 and JA4 derive from. Cipher and extension lists retain GREASE (each fingerprint filters as its own spec dictates).

type JA4Result added in v0.651.0

type JA4Result struct {
	// JA4 is the fingerprint: ja4_a_ja4_b_ja4_c.
	JA4 string `json:"ja4"`
	// JA4R is the raw (un-hashed) form: a_<ciphers>_<exts>_<sigalgs>, useful
	// for debugging and exact cross-checking against reference tooling.
	JA4R string `json:"ja4_r"`
	// A, B, C are the three sections.
	A string `json:"ja4_a"`
	B string `json:"ja4_b"`
	C string `json:"ja4_c"`
	// TLSVersion is the negotiated TLS version label (e.g. "13", "12", "10").
	TLSVersion string `json:"tls_version"`
	// SNI / ALPN are surfaced for context.
	SNI  string `json:"sni,omitempty"`
	ALPN string `json:"alpn,omitempty"`
	// Note carries interpretation guidance.
	Note string `json:"note,omitempty"`
}

JA4Result is the outcome of a JA4 computation.

func JA4Decode added in v0.651.0

func JA4Decode(hexInput string) (*JA4Result, error)

JA4Decode accepts a ClientHello as hex (a full TLS record or a bare handshake) and returns its JA4 fingerprint.

func JA4FromClientHello added in v0.651.0

func JA4FromClientHello(b []byte) (*JA4Result, error)

JA4FromClientHello computes the JA4 (FoxIO JA4.md) from raw ClientHello bytes. Scope: TLS-over-TCP (protocol "t"); QUIC/DTLS framing is not unwrapped here.

type JA4SResult added in v0.652.0

type JA4SResult struct {
	// JA4S is the server fingerprint: ja4s_a_ja4s_b_ja4s_c.
	JA4S string `json:"ja4s"`
	// JA4SR is the raw (un-hashed) form a_<cipher>_<extensions in order>.
	JA4SR string `json:"ja4s_r"`
	A     string `json:"ja4s_a"`
	B     string `json:"ja4s_b"`
	C     string `json:"ja4s_c"`
	// TLSVersion is the negotiated TLS version label.
	TLSVersion string `json:"tls_version"`
	// Cipher is the server's single chosen cipher suite (4-hex).
	Cipher string `json:"cipher"`
	// ALPN is the server's chosen ALPN protocol, if any.
	ALPN string `json:"alpn,omitempty"`
	Note string `json:"note,omitempty"`
}

JA4SResult is the outcome of a JA4S (server) computation.

func JA4SDecode added in v0.652.0

func JA4SDecode(hexInput string) (*JA4SResult, error)

JA4SDecode accepts a ServerHello as hex (a full TLS record or a bare handshake) and returns its JA4S fingerprint.

func JA4SFromServerHello added in v0.652.0

func JA4SFromServerHello(b []byte) (*JA4SResult, error)

JA4SFromServerHello computes the JA4S (FoxIO) from raw ServerHello bytes. Unlike client JA4, JA4S hashes the extension list in ServerHello order and retains GREASE (per FoxIO's reference to_ja4s).

type Result

type Result struct {
	// JA3 is the fingerprint string (the pre-hash field concatenation).
	JA3 string `json:"ja3"`
	// JA3Digest is the MD5 of JA3, lowercase hex.
	JA3Digest string `json:"ja3_digest"`
	// TLSVersion is the ClientHello legacy_version as a decimal (e.g. 771 =
	// 0x0303).
	TLSVersion int `json:"tls_version"`
	// Ciphers is the offered cipher-suite list, GREASE removed.
	Ciphers []int `json:"ciphers"`
	// Extensions is the extension-type list in appearance order, GREASE removed.
	Extensions []int `json:"extensions"`
	// Curves is the supported_groups (ext 10) list, GREASE removed.
	Curves []int `json:"curves"`
	// PointFormats is the ec_point_formats (ext 11) list.
	PointFormats []int `json:"point_formats"`
	// SNI is the server_name (ext 0), informational — not part of the JA3.
	SNI string `json:"sni,omitempty"`
	// Note carries interpretation guidance.
	Note string `json:"note,omitempty"`
}

Result is the outcome of a Decode / FromClientHello call.

func Decode

func Decode(hexInput string) (*Result, error)

Decode accepts a ClientHello as a hex string — either a full TLS record (starting 0x16) or a bare handshake message (starting 0x01) — and returns its JA3. Whitespace and colons in the hex are ignored.

func FromClientHello

func FromClientHello(b []byte) (*Result, error)

FromClientHello computes the JA3 from raw ClientHello bytes. The input may be a full TLS record (record-layer header stripped) or a bare handshake message.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL