Falcon input framing

The exact byte layout the Falcon precompiles expect. The message sits in the middle.

Docs / Post-quantum / Falcon input framing

This page is normative and it is the single most common integration mistake against chain 2800. The message goes in the middle of the input, not at the end.

The layout

input = pk || sm

pk    = (0x00 + logn) || packed_h
        897 bytes for Falcon-512   (logn = 9,  header byte 0x09)
        1793 bytes for Falcon-1024 (logn = 10, header byte 0x0A)

sm    = sigLen(2 bytes, big-endian) || nonce(40) || message || esig

esig  = (0x20 + logn) || compressedSignature
        header 0x29 for Falcon-512, 0x2A for Falcon-1024

sigLen == len(esig)

This is the NIST reference signed-message convention. If you pass a detached signature with the message appended at the end, the way most non-Falcon APIs work, the precompile returns a clean zero for a perfectly valid signature and looks dead.

Two things that will bite you

Falcon signatures are variable length. They are compressed. Two signatures produced by the same key measured 614 and 616 bytes. sigLen must be read per call and written into the two-byte prefix each time. A hard-coded length works today and fails tomorrow, on a signature that is perfectly valid.

Zero is ambiguous. The precompile does not revert on malformed input; it returns the zero word. So a wrong layout and a bad signature are indistinguishable from the outside. Put a positive control in the same run: a signature you know is good, framed the way you believe is right. If that comes back zero, nothing else in that run tells you anything.

ML-DSA and SLH-DSA

For 0x0AE3 and 0x0AE4 the convention is the ordinary NIST sm = signature || message, with the fixed signature lengths from the precompile table. Everything after pk || sig is the message.

SHAKE256

0x0AE5 reads a 32-byte big-endian output length first, then hashes the remaining bytes. Values above 65,536 are capped to 65,536, a non-zero high 28 bytes also caps, and an input shorter than 32 bytes returns empty output.

Where this is written down normatively

Section 4.2 of the protocol specification, and the source it cites, precompiles/AereFalconSupport.java in the published node package. The specification is the authority; this page is the readable form of it.