# diffFuzz — differential-fuzz harness _(no submodules)_ ## Purpose Run N iterations of random input against two implementations of the same primitive and report the first diverging reproducer, plus the total number of mismatches. Designed specifically for the "noble vs native" gating in `crypto-primitive-replaceability` Phase 1+: before a native implementation is allowed to flip to `'native'` in `crypto.config.ts`, it must run through `diffFuzz` against the reference `noble` impl without a single mismatch across millions of random inputs. ## Features - Deterministic `byteSource` injection for reproducible fuzz campaigns. - Generic over input and output types — works for hashes, ciphers, MACs, curve ops. - Captures first reproducer with the exact input, both outputs, and iteration index for CI log forensics. - Continues running after the first mismatch so the `mismatches` count approximates the rate, not just the existence of a bug. ## Usage: Basic Example ```ts import { diffFuzz } from "@suaveplan/crypto-utils/testing"; const result = diffFuzz({ iterations: 1000, sample: (src) => src(32), implA: (buf: Uint8Array) => buf.reduce((a, b) => a + b, 0), implB: (buf: Uint8Array) => buf.reduce((a, b) => a + b, 0), equals: (a, b) => a === b, }); console.log(result.mismatches); // 0 ``` ## Usage: Advanced Example ```ts import { diffFuzz } from "@suaveplan/crypto-utils/testing"; import { constantTimeEqual } from "@suaveplan/crypto-utils/constant-time"; // Differential fuzz SHA-256 noble vs native declare function sha256Noble(msg: Uint8Array): Uint8Array; declare function sha256Native(msg: Uint8Array): Uint8Array; const result = diffFuzz({ iterations: 100_000, sample: (src, iter) => src(iter % 4096), // variable-length messages implA: sha256Noble, implB: sha256Native, equals: constantTimeEqual, }); if (result.firstMismatch !== undefined) { const { iter, input, outputA, outputB } = result.firstMismatch; throw new Error(`SHA-256 diff at iter=${iter} len=${input.length} noble=${outputA.length}B native=${outputB.length}B`); } ``` ## API Reference - `diffFuzz(opts: DiffFuzzOptions): DiffFuzzResult` - `opts.iterations: number` — how many trials to run. - `opts.sample: (byteSource, iter) => V` — builds a fresh input each iteration. Receives the iteration index so callers can vary input shape by iteration. - `opts.implA`, `opts.implB: (input: V) => E` - `opts.equals: (a: E, b: E) => boolean` - `opts.byteSource?: ByteSource` — optional deterministic source; defaults to `defaultByteSource`. - Returns `{ iterations, mismatches, firstMismatch? }`. ## Implementation Notes The loop is intentionally unrolled to a single pair of calls per iteration — no async, no microtasks, no chunking. For 100 000 iterations of SHA-256 this completes in a few hundred milliseconds in Node 20+. Callers that need structured output (e.g. `DifferentialFuzzCorpus` for CI regression pinning) should compose on top of this primitive rather than complicate its API. The `byteSource` injection is how reproducibility is achieved: when a CI job reports a fail, the caller can replay the run with a fixed source seeded from the failing iteration's context.