# Fuzzing ZKAC This repository ships a **libFuzzer** harness via [`cargo-fuzz`](https://github.com/rust-fuzz/cargo-fuzz). You can reuse `zkac_fuzz` from other fuzzers (e.g. AFL++, Honggfuzz) with a small custom binary. ## Prerequisites - **Rust** toolchain (`rustc`, `cargo`). - **`cargo-fuzz`:** `cargo install cargo-fuzz` - **AddressSanitizer / default `cargo fuzz`:** requires **nightly** Rust (e.g. `rustup toolchain install nightly`). If you use a distro Rust **without** `rustup`, use sanitizer `none` (see below). ## Quick start From the repository root: ```bash chmod +x scripts/fuzz-libfuzzer.sh ./scripts/fuzz-libfuzzer.sh ``` Fuzz a single target for 5 minutes: ```bash FUZZ_TIME=300 ./scripts/fuzz-libfuzzer.sh handshake_respond ``` ### Stable Rust (no nightly) Default `cargo fuzz` enables AddressSanitizer and needs nightly. To fuzz on **stable**, build and run with sanitizer `none`: ```bash cargo fuzz build -s none handshake_respond cargo fuzz run -s none handshake_respond -- -max_total_time=60 ``` The helper script sets `SANITIZER=none` by default for stable-toolchain usage. It also prepends `~/.cargo/bin` to `PATH` so `cargo-fuzz` is found after `cargo install cargo-fuzz`. ### Did anything crash? After a run: 1. **Exit code** — `0` means every target finished its time/run budget **without** libFuzzer treating the process as a crash. Non-zero means at least one target failed (crash, abort, or libFuzzer error). 2. **Stdout** — On failure, libFuzzer prints `ERROR: libFuzzer: deadly signal` (or ASan messages if you use a sanitizer), the panic/backtrace, and a **reproduce** command. 3. **Artifacts** — Crashes are written under `fuzz/artifacts//`, typically `crash-` (and sometimes `leak-*` with leak sanitizer). Re-run with: ```bash cargo fuzz run -s none fuzz/artifacts//crash- ``` 4. **Minimize** — Shrink a crashing input with `cargo fuzz tmin -s none `. If the script stops early, scroll up to the last `=== cargo-fuzz: ===` block: the lines after it show which target failed. ### Nightly + AddressSanitizer ```bash SANITIZER=address FUZZ_TIME=120 ./scripts/fuzz-libfuzzer.sh session_decrypt ``` ## Targets (`fuzz/fuzz_targets/`) | Target | Exercises | |--------|-----------| | `handshake_respond` | X25519 responder (`handshake::respond`) | | `handshake_initiator_complete` | Initiator `complete` with arbitrary response bytes | | `session_decrypt` | ChaCha20-Poly1305 decrypt + replay guard | | `replay_sequence` | Sliding-window replay logic | | `crypto_deserialize` | Ristretto public key, Schnorr signature, BBS+ issuer key parsing | | `bbs_verify_presentation` | BBS+ proof parse + verify (heavy; keep corpora small) | Shared logic lives in `fuzz/src/lib.rs` (`zkac_fuzz`). ## Crate feature `fuzz-expose` Fuzz builds enable `zkac`’s **`fuzz-expose`** feature so harnesses can call `Session::new_fuzz`. Do not enable this in production binaries. ## corpora / artifacts - **Inputs:** seed with `mkdir -p fuzz/corpus/` and pass `-artifact_prefix=fuzz/artifacts/` if you want crash dumps. - **Gitignored:** `fuzz/corpus/`, `fuzz/artifacts/`, crash files (see `.gitignore`). ## AFL++ (optional) 1. Install [AFL++](https://github.com/AFLplusplus/AFLplusplus) and `cargo install cargo-afl`. 2. Add a binary in `fuzz/` (or a separate crate) that calls into `zkac_fuzz::dispatch_all` or individual `zkac_fuzz::*` functions. 3. Build with the `cargo-afl` wrapper (not plain `cargo build`): ```bash cd fuzz cargo afl build --release # then point cargo-afl at your harness binary, e.g.: # cargo afl fuzz -i in -o out target/release/your_afl_bin ``` (`fuzz/Cargo.toml` already enables `zkac`’s `fuzz-expose` for the dependency.) The `afl` crate’s `fuzz!` macro must be linked with AFL’s runtime; use `cargo afl build` as documented in [cargo-afl](https://github.com/rust-fuzz/afl.rs). ## Honggfuzz (optional) Install the `honggfuzz` binary (distro package or source). Point it at a binary that exercises `zkac_fuzz` (similar to AFL). Honggfuzz does not require the `cargo-fuzz` project layout; you can compile a small `main` that loops over inputs. ## CI smoke GitHub Actions (`.github/workflows/fuzz-smoke.yml`) builds all targets with sanitizer `none`, then runs **`scripts/fuzz-libfuzzer.sh`** with **`FUZZ_RUNS=2000`** so every registered fuzz target gets a short fixed-iteration smoke (same idea as below). `scripts/fuzz-libfuzzer.sh` discovers targets dynamically from `cargo fuzz list`, so adding a new `[[bin]]` fuzz target in `fuzz/Cargo.toml` automatically includes it in local and CI smoke runs. Locally you can match that with: ```bash FUZZ_RUNS=2000 ./scripts/fuzz-libfuzzer.sh ``` Or smoke a single target with a run budget instead of time: ```bash cargo fuzz run -s none handshake_respond -- -runs=2000 -print_final_stats=1 ```