quic

package
v0.4.1 Latest Latest
Warning

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

Go to latest
Published: Jul 26, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package quic decrypts QUIC Initial packets far enough to recover the TLS ClientHello inside them.

This exists because roughly a third of web traffic is now HTTP/3, and a sensor that only fingerprints TLS over TCP is blind to all of it. QUIC encrypts its Initial packets, but not secretly: the keys are derived from the Destination Connection ID, which travels in the clear precisely so that middleboxes, load balancers, and observers can do this. Decrypting one is a key schedule and an AEAD open, not an attack.

Index

Constants

View Source
const DefaultMaxPending = 8192

DefaultMaxPending is how many in-progress QUIC handshakes are tracked at once.

View Source
const MaxCryptoBytes = 16384

MaxCryptoBytes bounds how much handshake data will be buffered for one connection. A ClientHello with a hybrid post-quantum key share runs to a few kilobytes; anything past this is not a handshake we need to see.

View Source
const MinInitialDatagram = 1200

MinInitialDatagram is the padding floor RFC 9000 section 14.1 imposes on a client's Initial datagram, so that a server never amplifies traffic toward an unvalidated address.

View Source
const Version1 uint32 = 0x00000001

Version1 is the only QUIC version handled here (RFC 9000). Draft versions and QUIC v2 use different initial salts, so treating them as v1 would derive wrong keys and produce garbage rather than a clean failure.

Variables

View Source
var (
	// ErrNotInitial means the datagram is not a QUIC v1 client Initial packet.
	// This is the overwhelmingly common outcome on a real network, where most
	// UDP is DNS, QUIC short-header packets, or something else entirely, so it
	// carries no diagnostic weight.
	ErrNotInitial = errors.New("quic: not a v1 Initial packet")
	// ErrMalformed means the packet claims to be an Initial but does not parse.
	ErrMalformed = errors.New("quic: malformed Initial packet")
)

Errors reported by ParseInitial.

View Source
var ErrTruncated = errors.New("quic: truncated")

ErrTruncated means the buffer ended in the middle of a field.

Functions

func BuildClientInitial

func BuildClientInitial(dcid, scid []byte, cryptoOffset uint64, crypto []byte, size int) ([]byte, error)

BuildClientInitial constructs a client Initial packet carrying handshake bytes in a CRYPTO frame.

This is the exact inverse of ParseInitial: seal with the AEAD, then apply header protection. It exists so the synthetic capture generator can produce genuine QUIC traffic rather than a plausible-looking imitation, and so the tests can round-trip through both directions instead of only agreeing with themselves.

size is the target datagram length; the payload is padded to reach it.

Types

type CryptoFrame

type CryptoFrame struct {
	Offset uint64
	Data   []byte
}

CryptoFrame is one fragment of the TLS handshake stream.

type InitialPacket

type InitialPacket struct {
	Version uint32
	DCID    []byte
	SCID    []byte
	Frames  []CryptoFrame
}

InitialPacket is a decrypted client Initial.

func ParseInitial

func ParseInitial(datagram []byte) (*InitialPacket, error)

ParseInitial decrypts every client Initial packet in a datagram and returns the CRYPTO frames they carry.

A datagram can coalesce several QUIC packets, so this walks the whole thing rather than stopping after the first. Non-Initial packets and packets that fail authentication are skipped, because a passive observer sees plenty of both and neither is an error worth reporting.

type Reassembler

type Reassembler struct {
	// contains filtered or unexported fields
}

Reassembler turns a stream of UDP datagrams into TLS client fingerprints.

It mirrors fingerprint.Reassembler, which handles the same job over TCP, and deliberately so: a client that speaks both HTTP/2 and HTTP/3 should produce fingerprints that can be compared, and the only difference in the output is the transport character at the front of the JA4.

func NewReassembler

func NewReassembler(max int) *Reassembler

NewReassembler returns a ready Reassembler. max <= 0 selects the default.

func (*Reassembler) Feed

func (r *Reassembler) Feed(key model.FlowKey, datagram []byte) *fingerprint.Result

Feed supplies one UDP datagram.

It returns a non-nil result on the datagram that completes the ClientHello. Callers may feed every UDP payload unconditionally: anything that is not a QUIC Initial is rejected by a length check and a header bit before any cryptography happens.

func (*Reassembler) Forget

func (r *Reassembler) Forget(key model.FlowKey)

Forget drops any partial state for a flow.

func (*Reassembler) Pending

func (r *Reassembler) Pending() int

Pending reports how many partial handshakes are buffered.

Jump to

Keyboard shortcuts

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