Skip to content

SphincsStandardVerifier

Standard SPHINCS verifier for SphincsAccount

src/Verifiers/SphincsStandardVerifier.sol is the verifier behind the SPHINCS-only account. Like the other two verifiers it is stateless and pure, deployed once and shared. It exposes the same ISphincsVerifier.verify interface as the backup verifier but implements a different, standard parameter set: plain FORS under plain WOTS+, with no +C tricks on either layer.

Interface

uint256 constant SPHINCS_STANDARD_SIG_LEN = 6176;
 
function verify(bytes32 pkSeed, bytes32 pkRoot, bytes32 message, bytes calldata sig)
    external pure returns (bool valid);

Same contract as the backup verifier: reverts Invalid sig length on any length other than 6,176 and Invalid public key on a non-canonical key (low 128 bits nonzero); returns false for any well-formed but invalid signature. SphincsAccount checks the length before calling and rejects non-canonical keys at initialize, so in the account path it always returns a boolean.

Parameters

ParameterValueNotes
n16 bytes128-bit, Keccak-256
h / d20 / 5five XMSS layers of height 4
k / a19 / 9all 19 trees opened, each with a full 9-node auth path
w / l16 / 3532 message chains + 3 checksum chains (log w = 4)
signature6,176 bytes
verify gasnot yet measured for this profile
budget~10^6 signatures2^20 FORS instances

What differs from the backup verifier

SphincsVerifier (backup)SphincsStandardVerifier
FORS layerFORS+C: last tree's index forced to zero by grinding R, its auth path omittedstandard: every index used as drawn from the digest, including the last at bits [162, 171)
WOTS layerWOTS+C: 43 chains, per-layer 4-byte counter ground until digits sum to 208standard WOTS+: 32 message chains plus a 3-digit base-16 checksum csum = 480 − digitSum
Signer workgrinding on both layersnone; a standard SLH-DSA-style signer with Keccak swapped in
Signature3,688 B6,176 B
Gas~105knot yet measured

Forgery resistance in the WOTS layer comes from the checksum: raising any message digit lowers csum, and a lower csum cannot be signed because some checksum chain would have to be walked backwards.

Layout

R (16)
‖ 19 FORS secrets            (19 × 16  = 304)
‖ 19 FORS auth paths          (19 × 9 × 16 = 2,736)
‖ 5 × [ 35 WOTS chains (35 × 16 = 560) ‖ subtree auth path (4 × 16 = 64) ]   (5 × 624 = 3,120)
= 6,176 bytes

Hmsg is the same 160-byte keccak256(pkSeed ‖ pkRoot ‖ R ‖ message ‖ 0xFF…FF) as the backup verifier. The hypertree index is (digest >> 171) & 0xFFFFF (k · a = 171, 2^h − 1). ADRS is the FIPS 205 §4.2 uncompressed layout shared by all three NiceTry verifiers.

Tests

test/SphincsStandardVerifier.t.sol checks the length arithmetic (including that the blob is exactly one FORS auth path longer than a FORS+C equivalent), that 3 checksum digits suffice, that the 6,176 class is disjoint from every other length in the repo, the two revert guards, and a fuzz test that random input returns false. A positive test will be added once scripts/sphincs_reference.py can drive a signer for this parameter set.