Skip to content

Add public-input fuzzing CI#2478

Draft
tob-scott-a wants to merge 1 commit into
open-quantum-safe:mainfrom
tob-scott-a:deliverable/ci-fuzzing
Draft

Add public-input fuzzing CI#2478
tob-scott-a wants to merge 1 commit into
open-quantum-safe:mainfrom
tob-scott-a:deliverable/ci-fuzzing

Conversation

@tob-scott-a

Copy link
Copy Markdown

This PR replaces the old generic fuzzing setup with a public-input-oriented fuzzing system that is much closer to the real attack surface.

The old fuzzers mostly generated valid keypairs/signatures internally and checked that round trips succeeded. That was useful as a smoke test, but it did not spend much time mutating attacker-controlled bytes that reach decoders and deserializers. This PR shifts the focus to malformed public keys, ciphertexts, signatures, contexts, and serialized stateful key material.

To that end, we added five dedicated fuzz targets:

  • fuzz_test_kem_encaps
    • Exercises OQS_KEM_encaps() and OQS_KEM_encaps_derand()
    • Starts from a valid cached public key for each algorithm
    • Mutates the public key bytes before calling encapsulation
    • Covers both normal and deterministic encapsulation modes
  • fuzz_test_kem_decaps
    • Exercises OQS_KEM_decaps()
    • Starts from a valid cached ciphertext and secret key
    • Mutates only the ciphertext
    • Keeps the secret key valid so malformed ciphertexts reach deep decoder/rejection logic
  • fuzz_test_sig_verify
    • Exercises both OQS_SIG_verify() and OQS_SIG_verify_with_ctx_str()
    • Mutates public keys, signatures, messages, and contexts
    • Covers three context modes:
      • no context API
      • context API with non-null context
      • context API with NULL context
    • Allows variable signature, message, and context lengths to probe length-handling bugs
  • fuzz_test_sig_stfl_verify
    • Exercises OQS_SIG_STFL_verify()
    • Uses KAT-based baselines for XMSS/XMSSMT/LMS
    • Mutates stateful public keys, signatures, and messages
    • Replaces the old family-specific LMS/XMSS fuzzers with one registry-driven generic target
  • fuzz_test_sig_stfl_deserialize
    • Exercises OQS_SIG_STFL_SECRET_KEY_deserialize()
    • Mutates serialized stateful secret-key material and lengths
    • If deserialization succeeds, it also runs lock/unlock callbacks and round-trip serialization
    • This catches bugs that only appear after a malformed key survives initial parsing
    • LMS gets a synthetic minimally valid key baseline so it can reach deeper deserialize logic

The shared fuzzing infrastructure is new as well:

  • tests/fuzz/fuzz_helpers.*

    • Implements a compact 5-byte seed header:

      • version
      • algorithm index
      • mode
      • flags
    • Provides deterministic PRNG seeding

    • Provides cursor-based input parsing and patch helpers

    • Loads STFL KATs from existing tests/KATs/sig_stfl/... files

    • Builds minimal LMS serialized key baselines

  • tests/fuzz/fuzz_kem_cache.*

    • Caches one valid keypair and ciphertext per KEM algorithm
    • Avoids expensive keygen work on every fuzz iteration
  • tests/fuzz/fuzz_sig_cache.*

    • Caches valid keypairs, signatures, and context signatures for each signature algorithm
    • Makes the verifier fuzzers start from valid objects and mutate them incrementally

The corpus system is also new. tests/fuzz/generate_corpus.py walks the algorithm registries in src/kem/kem.c, src/sig/sig.c, and src/sig_stfl/sig_stfl.c, then generates one tiny 5-byte corpus seed per algorithm/mode combination. There are 926 seed files total:

  • 82 KEM encaps seeds
  • 41 KEM decaps seeds
  • 663 signature verify seeds
  • 70 STFL verify seeds
  • 70 STFL deserialize seeds

Those seeds are intentionally tiny. They do not contain real objects; they only select the algorithm and mode. The harness reconstructs valid baselines in memory and then mutates them. That keeps the fuzzers in deep parsing paths instead of spending most iterations on trivially invalid garbage.

The CI changes are substantial:

  • Adds .github/workflows/fuzz.yml
    • Reusable workflow with smoke and full profiles
    • Uses Clang + libFuzzer + ASan
    • Runs all five fuzz targets
    • Uploads crash artifacts on failure
  • Updates .github/workflows/basic.yml
    • Replaces the old single fuzz build check with a real smoke fuzz job
    • Smoke profile runs 30 seconds per target on a representative minimal build
  • Updates .github/workflows/weekly.yml
    • Adds a longer full fuzz run
    • Full profile runs 10 minutes per target across all enabled algorithms

The smoke profile uses:

  • KEM_ml_kem_768
  • KEM_bike_l1
  • SIG_ml_dsa_44
  • SIG_falcon_512
  • SIG_STFL_xmss_shake128_h10
  • SIG_STFL_lms_sha256_h5_w2

There are also build-system changes to support this:

  • tests/CMakeLists.txt
    • Replaces the old four fuzz binaries with five new targets
    • Adds a helper function for consistent fuzz target registration
    • Only builds the STFL deserialize target when experimental STFL key/sign generation is enabled
  • .CMake/alg_support.cmake
    • Re-runs minimal-build filtering after STFL options are defined
    • This lets OQS_MINIMAL_BUILD correctly restrict XMSS/LMS variants in fuzz CI
  • src/sig_stfl/lms/CMakeLists.txt
    • Changes the include path to ${PROJECT_BINARY_DIR}/include
    • This fixes out-of-tree fuzz builds that rely on generated headers
  • docs/FUZZING.md
    • Rewritten to document the new targets, build commands, corpus generation, and workflow behavior

The net effect is that the branch turns fuzzing from a basic “does this algorithm still work” check into a real malformed-input security harness for the generic public APIs and stateful deserialization paths.

  • Does this PR change the input/output behaviour of a cryptographic algorithm (i.e., does it change known answer test values)? (If so, a version bump will be required from x.y.z to x.(y+1).0.)
  • Does this PR change the list of algorithms available -- either adding, removing, or renaming? Does this PR otherwise change an API? (If so, PRs in fully supported downstream projects dependent on these, i.e., oqs-provider will also need to be ready for review and merge by the time this is merged. Also, make sure to update the list of algorithms in the continuous benchmarking files: .github/workflows/kem-bench.yml and sig-bench.yml)

These documents were generated by Codex-5.5-Cyber as part of the Patch the Planet program.

Signed-off-by: Scott Arciszewski <scott.arciszewski@trailofbits.com>
@tob-scott-a tob-scott-a force-pushed the deliverable/ci-fuzzing branch from 3ce1e00 to c0bb5bd Compare June 26, 2026 20:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant