Skip to content

Key-schedule KAT: external-anchor non-circularity via an RFC-8448-validated Expand-Label oracle

Context and Problem Statement

Spec/10 §8 requires a non-circular key-schedule KAT (handshake_secret ladder + traffic key/iv + finished_key). Unlike the KEM-wire KAT (ADR-0007), which anchored to published NIST/RFC values, the key schedule is built on HKDF-Expand-Label with the N-PAMP-original prefix "n-pamp " (draft-00 §7.4, an RFC 8446 §7.1 construction with the prefix changed from "tls13 "). No external standard publishes output bytes for "n-pamp " labels, so the KAT cannot anchor its expected outputs to a published vector directly. How is it made non-circular?

Decision Drivers

  • Non-circularity (E8): expected values must not be produced by an N-PAMP implementation.
  • The expected outputs must trace to a standard despite the protocol-original label prefix.
  • Reusable for the remaining handshake KATs (transcript, Finished, CertVerify).

Considered Options

  • (A) External-anchor oracle. Build an independent HKDF-Expand-Label oracle in the test; PROVE it correct against RFC 8448 (TLS 1.3, "tls13 " prefix) and RFC 5869 (raw HKDF); then apply the same proven oracle with "n-pamp " to check the schedule. Store the RFC anchors + fixed inputs, not impl-produced outputs.
  • (B) Store goldens generated by the reference impl (circular — rejected).
  • (C) Re-target RFC 8448 inputs through N-PAMP's function with "tls13 " (rejected — the function hardcodes "n-pamp ", so RFC 8448 values are not directly reproducible through it).

Decision Outcome

Chosen option: (A). The vector file (test-vectors/v1/key-schedule-kat.json; consumed by the Go reference implementation) carries: RFC 5869 TC1 (Extract/Expand), the RFC 8448 §3 (client_handshake_traffic_secret → write_key/write_iv/finished_key) tuple, and fixed N-PAMP inputs (ML-KEM/X25519 shared secrets reused from the KEM-wire KAT; synthetic transcript-hash points TH_kem/TH_cCV). The test:

  1. Validates raw HKDF-Extract/Expand vs RFC 5869 TC1.
  2. PROVES the in-test Expand-Label oracle vs RFC 8448 ("tls13 ", empty context): key/iv/finished.
  3. Applies the proven oracle with "n-pamp " and asserts NewSchedule / HandshakeAEAD / ApplicationAEAD / FinishedKey reproduce handshake_secret, c hs, s hs, master, traffic (key, iv), and finished_key — and that application-phase keys differ from handshake-phase keys for the same (dir, epoch, suite, channel) tuple (cross-phase separation).

The vector file stores RFC ANCHORS + INPUTS, not N-PAMP output goldens — so the shared cross-impl artifact is an anchor set each implementation grounds itself against, not one impl's numbers.

Consequences

  • Good: the key schedule (labels, transcript-point wiring, traffic context encoding, key/iv/finished) is anchored to RFC 8446/8448/5869 despite the protocol-original prefix; mutation-proven (swapping the c hs/s hs labels fails the KAT while the RFC anchor legs still pass).
  • Good: the oracle + anchor pattern is reusable for the transcript and CertVerify KATs.
  • Neutral: the "n-pamp "-prefixed outputs are not independently published anywhere; their correctness rests on the RFC-8448-validated oracle, which is the strongest available grounding.

Confirmation

The key-schedule KAT test passes; the RFC 5869 + RFC 8448 anchor assertions pass (oracle proven); mutation (swap c hs/s hs) fails the schedule legs while anchors still pass. The full verification gate passes.

More Information

test-vectors/v1/key-schedule-kat.json; spec/10 §5/§8; draft-00 §7.4/§7.5; ADR-0005, ADR-0007. Sources: RFC 8446 §7.1/§7.3, RFC 8448 §3, RFC 5869 App A.1.