Documentation
¶
Overview ¶
Package capture is the exchange format between a client that connects and the report describing what it sent.
One shape serves both sides on purpose. The echo server answers every client — a real browser or this library — with the same JSON, so comparing the two is comparing two values of one type rather than reconciling two formats. The alternative, a browser-shaped struct and a client-shaped struct, is where "these fields look equivalent" quietly becomes "these fields are equivalent".
The authoritative field is RawClientHello: the bytes as they arrived. Every list below is derived from it and exists to be read by a human. A comparison re-parses the raw bytes instead of trusting the rendering, so a bug in the rendering can never make two different handshakes look identical.
Index ¶
Constants ¶
const ( SourceBrowser = "browser" SourceTLSFetch = "tlsforge" )
Source labels which side produced a capture.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Capture ¶
type Capture struct {
Source string `json:"source,omitempty"`
Profile string `json:"profile,omitempty"`
Negotiated string `json:"alpn_negotiated,omitempty"`
// RawClientHello is the ClientHello exactly as it arrived, record layer
// included. Everything in TLS below is derived from it.
RawClientHello []byte `json:"raw_client_hello"`
TLS TLS `json:"tls"`
HTTP2 *HTTP2 `json:"http2,omitempty"`
HTTP1 *HTTP1 `json:"http1,omitempty"`
}
Capture is one client's complete self-description.
func (*Capture) Hello ¶
func (c *Capture) Hello() (*fingerprint.ClientHello, error)
Hello re-parses the authoritative bytes.
Comparison goes through here rather than through the rendered lists so that a mistake in the rendering cannot make two different handshakes compare equal.
type HTTP1 ¶
type HTTP1 struct {
Method string `json:"method"`
Path string `json:"path"`
Proto string `json:"proto"`
// HeaderOrder stays lower-case so an HTTP/1.1 set can still be compared
// directly with HTTP/2, where HPACK requires lower-case names. HeaderNames
// is the same sequence as it appeared on the HTTP/1.1 wire.
HeaderOrder []string `json:"header_order"`
HeaderNames []string `json:"header_names,omitempty"`
Headers []HeaderField `json:"headers"`
}
HTTP1 is what an HTTP/1.1 client sent.
func (*HTTP1) Fingerprint ¶
func (h *HTTP1) Fingerprint() *fingerprint.HTTP1
Fingerprint converts the rendered HTTP/1.1 view into its comparable form.
type HTTP2 ¶
type HTTP2 struct {
Akamai string `json:"akamai_fingerprint"`
Settings []Setting `json:"settings"`
WindowUpdate uint32 `json:"window_update"`
Priorities []Priority `json:"priorities,omitempty"`
HeaderPriority *Priority `json:"header_priority,omitempty"`
PseudoHeaderOrder []string `json:"pseudo_header_order"`
HeaderOrder []string `json:"header_order"`
Headers []HeaderField `json:"headers"`
}
HTTP2 is the readable rendering of the connection preamble and first request.
func FromHTTP2 ¶
func FromHTTP2(h *fingerprint.HTTP2) *HTTP2
FromHTTP2 renders observed HTTP/2 traffic for humans.
func (*HTTP2) Fingerprint ¶
func (h *HTTP2) Fingerprint() *fingerprint.HTTP2
Fingerprint converts the rendered HTTP/2 view back into the comparable form.
type HeaderField ¶
HeaderField is one header in wire order.
type Navigator ¶
type Navigator struct {
}
Navigator is what the browser says about itself in JavaScript.
It is collected because the HTTP layer has to agree with it: a request whose user-agent claims Chrome 151 while sec-ch-ua-platform-version claims a macOS that shipped with Chrome 120 is a contradiction no real browser produces. The high-entropy hints are stored verbatim rather than re-derived for the same reason — inventing one would be inventing a machine.
type Priority ¶
type Priority struct {
StreamID uint32 `json:"stream_id"`
Exclusive bool `json:"exclusive"`
DependsOn uint32 `json:"depends_on"`
Weight uint8 `json:"weight"`
}
Priority is a standalone PRIORITY frame.
type TLS ¶
type TLS struct {
JA3 string `json:"ja3"`
JA3Hash string `json:"ja3_hash"`
JA4 string `json:"ja4"`
JA4Raw string `json:"ja4_r"`
LegacyVersion Value `json:"legacy_version"`
ServerName string `json:"server_name,omitempty"`
CipherSuites []Value `json:"cipher_suites"`
Extensions []Value `json:"extensions"`
SupportedVersions []Value `json:"supported_versions,omitempty"`
SupportedGroups []Value `json:"supported_groups,omitempty"`
SignatureAlgorithms []Value `json:"signature_algorithms,omitempty"`
CertCompression []Value `json:"compress_certificate,omitempty"`
ECPointFormats []uint8 `json:"ec_point_formats,omitempty"`
PSKKeyExchangeModes []uint8 `json:"psk_key_exchange_modes,omitempty"`
ALPN []string `json:"alpn,omitempty"`
ApplicationSettings []string `json:"application_settings,omitempty"`
// Resumed reports a pre_shared_key extension, which changes the extension
// count and therefore the JA4. A resumed hello and a cold one from the SAME
// browser have different JA4s; comparing across that line reports a
// difference that is not one.
Resumed bool `json:"session_resumed"`
}
TLS is the readable rendering of a ClientHello.
func FromClientHello ¶
func FromClientHello(hello *fingerprint.ClientHello) TLS
FromClientHello renders a parsed hello for humans.