Skip to content

Hand-rolled TLS 1.3 stacks lack cross-client test vectors and parser fuzzing #190

Description

@davvd

We maintain three independent hand-rolled TLS 1.3 implementations that must agree byte-for-byte — the pinned Chrome JA4, cipher 0x1301, X25519, the HKDF key schedule, the ClientToken/ServerToken construction, and the record framing: the Go node (node/internal/server/serverhandshake.go, reality.go, record.go, clienthello.go), the Swift client (ios/APNExtension/Handshake.swift, Record.swift, ClientToken.swift, ServerToken.swift), and the Kotlin client (android/.../vpn/Handshake.kt, Record.kt, ClientHello.kt). The only interop test is node/internal/server/handshake_interop_test.go:15, which merely checks that Go's crypto/tls stdlib client completes a handshake against our server.

That test proves nothing about the two clients that actually ship. Nothing asserts the three stacks derive identical ClientToken, ServerToken, ServerHello.random, session keys, and sealed records from the same inputs, so a silent divergence in the handshake or record layer would either break a client or open a security hole with no test to catch it — and cross-language drift is already a live problem here (the proto copies drift and break the iOS build). There is also no fuzzing of the ClientHello/record parsers (reality.Inspect, fromBytes, readRecord) even though they parse attacker-controlled bytes on every connection. WireGuard sidesteps this entirely with a single reference implementation whose Noise handshake is formally verified (Tamarin, CryptoVerif, ProVerif); we cannot formally verify three hand-rolled stacks, but we currently have no substitute assurance either.

Two concrete steps: add a shared, checked-in set of handshake/AEAD test vectors — a fixed PSK plus a fixed ephemeral key mapping to the expected ClientToken, ServerToken, ServerHello.random, derived app keys, and one sealed application_data record — asserted by unit tests in all three repos so any drifting stack fails; and add a Go fuzz target (testing.F) over reality.Inspect, fromBytes, and readRecord. Filing per a WireGuard comparison review.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions