Conversation
Records the Veridise finding that the verifier accepts both 0x29 and 0x39 for the same detached layout, and the agreed remediation (require 0x39, fix the two inverted comment blocks, rewrite the KAT conversion). This finding overturns AUD-002, which closed the identical issue as 'Accepted -- required for interop' on the premise that falcon-wasm emits 0x29 for padded signatures. Verified against the vendored signer: it emits 0x39 for both sign() and signPadded(), and never 0x29. Tracking stub only; no verifier change lands here.
Severity Medium, likelihood Likely, impact Bad (Veridise issue #1292).
…natures The header gate accepted both 0x2X and 0x3X high nibbles, so flipping a valid signature's 0x39 header to 0x29 kept it valid (audit finding, PR #2). Per the Falcon Round-3 spec and every conforming signer (reference C, PQClean, falcon.py, falcon-wasm), detached compressed signatures — natural and 666-byte padded alike — use 0x39; 0x29 only labels the nonce-less tail inside the NIST crypto_sign envelope, and 0x59 (CT) needs a different decoder. The earlier interop rationale for accepting 0x29 was wrong: the project's own e2e receipts record 0x39 for falcon-wasm's padded output. - pin the header to exactly 0x39 = 0x30 | logn in verify_512; natural vs padded form is still decided by decoder consumption + total length - KAT suites now convert the NIST envelope to detached form by replacing the envelope's 0x29 with 0x39 (asserting the envelope header) instead of preserving it, and pin sig[0] == 0x39 - add test_envelope_header_0x29_rejected regression tests (core unit + KAT-based: header flipped to 0x29/0x59/0x31/0x38 must be rejected) - correct the format story in module docs, web-demo comments, threat model, and the CT-analysis standalone copy - remediation log: register audit finding as EXT-001 (Fixed); close AUD-002 as superseded (its 'required for interop' acceptance was factually wrong)
Brings the auditor's VER-001 report (non-standard 0x29 detached signatures) onto the fix branch so the finding and its remediation travel together.
… Veridise report Tick the VER-001 remediation checklist and record the developers' response in the finding doc; rename the log row EXT-001 -> VER-001, adopt the auditor's Medium severity and 2026-08-19 report date, and cross-link the finding write-up.
a587ccb to
ec54f5c
Compare
…natures The header gate accepted both 0x2X and 0x3X high nibbles, so flipping a valid signature's 0x39 header to 0x29 kept it valid (audit finding, PR #2). Per the Falcon Round-3 spec and every conforming signer (reference C, PQClean, falcon.py, falcon-wasm), detached compressed signatures — natural and 666-byte padded alike — use 0x39; 0x29 only labels the nonce-less tail inside the NIST crypto_sign envelope, and 0x59 (CT) needs a different decoder. The earlier interop rationale for accepting 0x29 was wrong: the project's own e2e receipts record 0x39 for falcon-wasm's padded output. - pin the header to exactly 0x39 = 0x30 | logn in verify_512; natural vs padded form is still decided by decoder consumption + total length - KAT suites now convert the NIST envelope to detached form by replacing the envelope's 0x29 with 0x39 (asserting the envelope header) instead of preserving it, and pin sig[0] == 0x39 - add test_envelope_header_0x29_rejected regression tests (core unit + KAT-based: header flipped to 0x29/0x59/0x31/0x38 must be rejected) - correct the format story in module docs, web-demo comments, threat model, and the CT-analysis standalone copy - remediation log: register audit finding as EXT-001 (Fixed); close AUD-002 as superseded (its 'required for interop' acceptance was factually wrong)
Brings the auditor's VER-001 report (non-standard 0x29 detached signatures) onto the fix branch so the finding and its remediation travel together.
6e176e9 to
ec54f5c
Compare
|
Confirmed the core of VER-001 against let fmt = sig_header & 0xF0;
if fmt != 0x20 && fmt != 0x30 {
return false;
}followed unconditionally by the fixed I also checked the test at One thing I would check while you are in here, since it is adjacent to the framing change and cheap to get wrong. I am not asking for anything to be added to the PR. I read the code, confirmed the finding, and this is a heads-up on a test that your change will quietly make a misnomer. For the record on my side: I looked for an unclaimed issue to pick up here and there is nothing in this repo. The only open issue in the org is |
Description
The verifier accepts both
0x29and0x39as headers for the same detached signature layout:After checking that the high nibble is either
0x2Xor0x3X, the verifier always extracts bytes 1–40 as the nonce and invokes the compressed polynomial decoder. The selected header family therefore has no effect on framing or decoding.This conflates two distinct formats defined by Falcon:
0x39identifies a Falcon-512 compressed signature containing the nonce. Both natural-length and 666-byte padded signatures use this header.0x59identifies the alternate fixed-width/constant-time encoding and requires a different decoder.0x29is used only for the nonce-less signature tail inside the NISTcrypto_signsigned-message envelope.Consequently, the local representation
0x29 || nonce || compressed(s₂)is a non-standard hybrid format.The comments claiming that signers disagree about the meanings of these nibbles are inaccurate. The Falcon specification, reference C implementation, PQClean, and the Python signer agree on the relevant conventions:
0x39.0x59.0x29belongs to the nonce-less tail of the NIST signed-message envelope.The KAT tests currently create the non-standard hybrid by extracting the nonce from the NIST envelope while preserving its
0x29nonce-less header. A correct conversion to detached form would replace0x29with0x39.Impact
An attacker can change the first byte of a valid
0x39signature to0x29without invalidating it. This produces multiple accepted byte encodings for the same signature and message.This does not appear to enable a signature over a new message or public key. However, it has the following consequences:
0x29.0x59fixed-width encoding is added.Recommendation
Require
0x39for every compressed detached Falcon-512 signature:Continue determining natural versus padded compressed form from decoder consumption and length:
0x29to identify padded signatures; padded signatures also use0x39.Developers Response
The developers have been notified of the issue, but not yet provided a response.