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:
- Validates raw HKDF-Extract/Expand vs RFC 5869 TC1.
- PROVES the in-test Expand-Label oracle vs RFC 8448 (
"tls13 ", empty context): key/iv/finished. - 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 hslabels 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.