117 lines
4.8 KiB
Markdown
117 lines
4.8 KiB
Markdown
# 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` 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
|
||
```
|