WO-11 — SLH-DSA keys and KMAC derivation¶
8 October 2026 · Branch wo-11-slh-dsa, starting at 2c8dd32.
Status: DONE¶
Decision #19 option A is implemented for testnet. CMake requires exactly
OpenSSL 3.6.5 and fails configuration on any other version. The key-generation
entry point runs the NIST ACVP SHAKE-128s seeded-keygen known-answer test once
per process before generating a requested key; a mismatch throws and refuses
key generation. startup_self_test() is exposed for wallet startup, and the
same check also gates every key-generation entry point. The NIST KAT is also
run by the CTest suite. A unit test substitutes a one-bit-wrong expected
public key and verifies refusal. Revisit the provider choice before mainnet,
as decision #19 directs. No library was installed.
Implemented¶
e46/src/keys.cppande46/include/e46/keys.hppprovide KMAC wallet-seed derivation, seeded key generation, randomized signing, deterministic test signing, and verification for SLH-DSA-SHAKE-128s.Seed derivation follows decision #18 exactly: 32-byte master key; message
ASCII("E46-key") || u32le(account) || u32le(index); empty customization; 48-byte KMAC256 output. The output is passed in FIPS 205 order: bytes 0–15SK.seed, 16–31SK.prf, 32–47PK.seed.Key and signature sizes are 32-byte public key, 64-byte private key, and 7,856-byte signature. Signing and verification reject contexts longer than 255 bytes. Keygen/sign/verify are backed by OpenSSL; no custom SLH-DSA code was written.
Added
e46/golden/wo11-golden.jsonand its deterministic generator, plus the official NIST ACVP SLH-DSA-SHAKE-128s keygen and signature fixtures ine46/golden/wo11-nist-kat.json.Added the
keyslibrary, CLI, unit/differential/golden tests, and fuzz harness to CMake.
Evidence¶
NIST ACVP SHAKE-128s keygen tcId 11 matches the expected 64-byte private key and 32-byte public key. The fixture’s first 48 private-key bytes are the exact input seed in the approved FIPS 205 order.
Startup KAT refusal test: replacing the expected KAT public key with a one-bit-different value causes the shared self-test gate to throw before returning a generated key.
Fixed empty-context signing and verification: OpenSSL now receives a non-null placeholder address with a zero-length context parameter. Added a randomized empty-context sign/verify round trip and a context mismatch rejection check.
Added official NIST ACVP empty-context vectors: deterministic sigGen tcId 215 matches all 7,856 signature bytes; sigVer tcId 343 accepts. Flipping a bit in that NIST sigVer signature is rejected. These are SHAKE-128s, external-interface, pure-message cases with an explicitly empty context.
NIST ACVP deterministic signature tcId 214 matches all 7,856 bytes and verifies. Source files are linked in the golden fixture and originate from NIST ACVP keygen, signature-generation, and signature-verification vectors.
Differential check: 24 frozen wallet-seed cases, 1 keygen KAT, 1 signature KAT, empty-context sigGen/sigVer KATs, and 1,000 fresh KMAC inputs against the independent pure-Python SP 800-185 reference; 0 mismatches. Invalid lengths, malformed input, and the changed empty-context signature were rejected as expected.
Full repository release and ASan/UBSan suites passed 28/28 before this review correction. After the empty-context fix, all three WO-11 tests pass in both release and ASan/UBSan builds (3/3 each).
LibFuzzer with Homebrew LLVM Clang 22 and ASan/UBSan: 2,782,145 runs in 31 seconds, no crash or sanitizer finding. Apple system Clang lacked its libFuzzer runtime, so the dedicated fuzz build used Homebrew LLVM.
WO-11 invokes no SHA-2 primitive; it uses KMAC256 and the SHAKE-based SLH-DSA parameter set. Earlier golden files were not edited.
Limits¶
Only the installed OpenSSL 3.6.5 default provider was exercised. Its deterministic seeded-keygen API is documented for testing; option A constrains this work to the pinned provider and startup KAT, with review required before mainnet.
sign_deterministic_for_testexists solely to reproduce the NIST ACVP signature vector; the productionsignfunction uses the provider’s randomized signing mode.No FIPS provider configuration, cross-platform build, transaction/wallet integration, or network consensus behavior was tested.
No changes were made to owner files in
MAPS/ordocs/; no commits, pushes, merges, history rewrites, or file deletions were performed.