qbit-libbitcoinpqc v0.3.1
Release notes
Patch release of qbit-libbitcoinpqc, cut from v0.3.0. It wipes SLH-DSA secret-key material from the stack before key generation and signing return. Keys, signatures and verification results are byte-identical to v0.3.0.
Changes
- Secret-key scratch is zeroed before return (#5).
crypto_sign_seed_keypairandcrypto_sign_signaturereturn through one cleanup path that wipes the stackspx_ctx(an exact SK_SEED copy) andoptrand.prf_addr,gen_message_random, the SHA-2 finalize helpers,sha256()/sha512()and the AVX2 x8 helpers wipe their local scratch. FORS+C salt-counter exhaustion, which cannot be reached under the signing cap, now also resets*siglen. - The released stack is burned once per secret-key call (#6).
slh_dsa_keygen,slh_dsa_sign/slh_dsa_sign_with_stats,slh_dsa_secret_key_validateandslh_dsa_derandomizeoverwriteBITCOIN_PQC_BURN_STACK_BYTESof the stack they released, on success and failure paths. This clears SHA-2 message schedules that compiler spills leave behind, which can be inverted to SK_SEED, SK_PRF and derived seeds. Threaded FORS workers (test/benchmark knobSPX_FORS_THREADS) burn their own stacks. - Sanitizer-clean wipe loop (from #14). The
secure_zero_localhelpers count down without wrappinglen, so-fsanitize=unsigned-integer-overflowbuilds do not trap. - crossbeam-epoch 0.9.20 (RUSTSEC-2026-0204, from #4). Lockfile only. The crate reaches the lockfile only through the
criteriondev-dependency. - Version metadata is
0.3.1.docs/vendored-crypto-provenance.mdlists the vendored SPHINCS+ files this release changes.
Not included: #10 (new PqcError::OutOfMemory variant and changed C error codes; this needs a minor release), #4's backend-selection test helpers, #13, and the CI and ruleset changes on main.
Compatibility
- API and ABI unchanged. No public header declares a new or changed function. The only header change is a comment documenting the stack requirement. The Rust sources (
*.rs, includingbuild.rs) are byte-identical tov0.3.0. The library objects export two new internal symbols,slh_dsa_secret_key_validate_unburnedandslh_dsa_derandomize_unburned, which no header declares. - Outputs unchanged. The golden vectors pass unmodified. A C harness linked against the
v0.3.0and0.3.1static libraries produces byte-identical keys and signatures (4 keypairs and 16 signatures, 59,264 bytes, sha2565f0a2ba45b04406b…).
Stack Requirements
Each secret-key call now overwrites BITCOIN_PQC_BURN_STACK_BYTES of stack below its caller before returning:
- 16 KiB in optimized native builds (
__OPTIMIZE__). Optimized keygen and sign use about 13 KiB below the entry point, so a call needs about 17 KiB of free stack. - 208 KiB in unoptimized GCC/Clang builds (Cargo
devprofile, CMakeDebug), where the-O0AVX2 transform frame alone is about 190 KiB. - 4 KiB under Emscripten.
- Defining
BITCOIN_PQC_BURN_STACK_BYTESat build time overrides the size.
Optimized keygen, validation and signing run on a 64 KiB thread stack (checked with a pthread_attr_setstacksize(65536) thread on x86-64 Linux, clang -O3). Unoptimized builds need more than 208 KiB. Verification does not burn.
Performance
Measured with clang -O3 (CMake Release) on a shared x86-64 Linux host: 3 interleaved runs of 20 iterations, v0.3.0 against 0.3.1.
| Call | 0.3.0 | 0.3.1 |
|---|---|---|
| Sign | 144–147 ms | 158–164 ms (about +10%) |
| Keygen | 9.5–9.8 ms | 10.6–11.1 ms (about +12%) |
| Secret-key validation | 9.6–9.7 ms | 10.7–11.1 ms (about +12%) |
| Verify | 1.80–1.86 ms | 1.82–1.88 ms (about +1%) |
Sign-time key validation. slh_dsa_sign and slh_dsa_sign_with_stats validate the secret key before signing by re-deriving its public root. That costs about 10.7 ms, about 6.7% of a sign. The check was already in 0.3.0. 0.3.1 only routes it through an internal variant that does not burn, so a sign burns the stack once. Callers who validate a key on import and then sign pay for validation twice.
Verification gains only the SHA-2 helper wipes, because it shares those helpers with signing. It does not burn the stack.
Security And Signing Posture
The wipes and the stack burn reduce accidental disclosure of secret-key material through released stack memory. They are not side-channel hardening.
Signing remains deterministic, and deterministic signing is not side-channel hardening. Randomized or hedged signing is not available. Production signing should be limited to trusted local wallet processes, dedicated signing hosts, or comparably isolated key-custody infrastructure. This release does not claim signing support on hostile shared hardware, or resistance to physical side-channel attackers or fault injection. See docs/side-channel-policy.md.
Validation
See the evidence table and the independent review in the release PR (Qbit-Org/qbit-libbitcoinpqc#15). Upstream CI ran on the release commit on a temporary develop branch.