Skip to main content

fors_sign_into_redundant

Function fors_sign_into_redundant 

Source
pub fn fors_sign_into_redundant<P: Params>(
    md: &[u8],
    sk_seed: &[u8],
    pk_seed: &[u8],
    adrs_template: &Adrs,
    out: &mut [u8],
) -> Result<Vec<u8>, SlhDsaError>
Expand description

Recompute-and-compare FORS signing — T1-C redundancy against single-fault grafting-tree forgery.

Signs the FORS component twice into independent scratch buffers, derives the FORS public key from each signature via the constant-time fors_pk_from_sig, then compares both signatures and both derived public keys under silentops::ct_eq. On any mismatch the function returns SlhDsaError::FaultDetected without writing anything into out — the faulted signature never leaves the device. On a clean run, the validated signature is copied into the caller’s out buffer and the FORS public key (reusable by the hypertree signing step) is returned.

§Threat addressed

Castelnovi-Martinelli-Prest Grafting Trees (PQCrypto 2018, ePrint 2018/102) showed that a single random fault during any FORS hash chain produces a valid-looking signature under a different FORS root; after collecting a handful of such signatures an attacker forges arbitrary messages. Genêt On Protecting SPHINCS+ Against Fault Attacks (TCHES 2023(3), ePrint 2023/042) recommends recompute-and-compare at signing time as the canonical defence. The threat is no longer hypothetical-physical: Adiletta et al. SLasH-DSA (arXiv 2509.13048, Aug 2025) realised it on stock OpenSSL with software-only Rowhammer in 1–8 h.

§Design rationale

Comparing both the signature bytes and the derived public keys is intentional defence-in-depth. A fault that corrupts auth-path bytes might round-trip to the same FORS root under the verifier path; the byte-level ct_eq catches that case. Symmetrically, a fault that lands inside the second fors_pk_from_sig derivation would not be caught by a signature-bytes-only check; the pk ct_eq catches that case. Both checks together cost a single extra ct_eq and are paid only on the slow path that already runs the FORS signer twice.

§Abort posture (vs ML-KEM’s CT-substitute)

Unlike ML-KEM’s double-decaps + branchless fault-fallback (quantica::ml_kem::kem::decaps), this routine aborts rather than substituting a fault-derived key. The asymmetry is deliberate: a KEM must always return a shared secret (or the caller cannot proceed at all), while a signer that detects a fault is allowed — required, per Genêt 2023 — to refuse to emit, so the faulted signature does not propagate.

§Memory

One SecretBytes scratch of length `fors_sig_len = K * (1 + A)

  • N` (≈ 10 KiB for SLH-DSA-SHAKE-256s). Heap-allocated so the stack budget on the M0 baseline stays honest. The scratch is drop-zeroized on both the success and the abort path.