PRF nonce reuse between lifting and r
Holds · Argued · Cause: code
Property: IND-CCA security
Cost against claim: The lifting and the encryption randomness r consume overlapping PRF nonces, giving identical bytes where the design calls for disjoint streams
In every encapsulation (and in decapsulation's internal re-encryption), Weaver's reference code draws the message lifting and the ephemeral randomness r from overlapping PRF nonces instead of the disjoint stream the specification assigns to each. Nonces 1..k-1 are consumed by both uses, so the PRF, which is prefix-consistent, returns identical bytes for the shared nonces, contradicting the independence the IND-CPA proof assumes between the two quantities.
| Parameter set | Claim | Attack cost | Verdict |
|---|---|---|---|
| WeaverKEM-128, -192, -256 (reference code) | independent PRF streams for the lifting and for r | identical PRF bytes on overlapping nonces; no distinguisher built | Holds |
What causes it
Algorithm 2 (spec p. 11) fixes one PRF stream at nonce 0 for the lifting and nonces 1..k for r. In the code, indcpa.c passes the lifting's nonce by value into polyvec_invq, so the caller's own counter only ever advances to 1, while polyvec_invq increments a separate, internal copy of the nonce across nonces 0..k-1 for its k polynomials. r is then drawn from prf(coins, 1+i) (indcpa.c:364-365). Nonces 1..k-1 are therefore consumed by both the lifting and r.
Evidence
Traced with a PRF-wrapping harness (prf_trace.c, built with -Wl,--wrap) linked against an unmodified copy of the reference sources whose three KAT files were first reproduced byte for byte. Across 300 encapsulations per parameter set, every run shows the same reused nonces with byte-identical PRF output: 256 bytes shared per encryption at Weaver-640 (nonces 1-4 of 5), 408 bytes at Weaver-1024 (nonces 1-3 of 4), and all four r streams (1088 bytes) at Weaver-2048; the pattern also appears in the re-encryption inside decapsulation. This is the overlap already reported for Loom's embedded core, confirmed independently in Weaver's own submitted code; no exploitation was attempted.
Cause in the submitted code; the specification is not affected.
Limits
No distinguishing or recovery attack was run; this confirms the code-only byte overlap, not a demonstrated break.
Credit
First public report: M.-J. Saarinen (ngcc.dev kem-39-1, 2026-09-21). Found independently by PQC-X.