first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,68 @@
# diffFuzz — differential-fuzz harness
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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<V, E>(opts: DiffFuzzOptions<V, E>): DiffFuzzResult<V, E>`
- `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.