ZKAC/docs/FUZZING.md
2026-05-06 17:42:51 +02:00

117 lines
4.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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/<target_name>/`, typically `crash-<hash>` (and sometimes `leak-*` with leak sanitizer). Re-run with:
```bash
cargo fuzz run -s none <target> fuzz/artifacts/<target>/crash-<hash>
```
4. **Minimize** — Shrink a crashing input with `cargo fuzz tmin -s none <target> <crash-file>`.
If the script stops early, scroll up to the last `=== cargo-fuzz: <name> ===` 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/<target>` 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` crates `fuzz!` macro must be linked with AFLs 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
```