# 🤖 Golden vectors — the cross-language crypto contract

`contra-v1.json` is the reference for every derivation in `PROTOCOL.md`.

CONTRA implements the same crypto three times: Go on the instance, JavaScript
in the console, Swift on the Mac. They must produce identical bytes. **All
three suites pass this one file, or the contract is not being kept** — a second
implementation that passes vectors of its own has proved nothing, and cross-
platform KDF drift is the bug class behind the 1Password PRF mismatch that
shaped our whole recovery design.

Changing a value in this file is changing the protocol.

## What it pins

Argon2id at `m=64MiB, t=3, p=1`; all five `contra/v1/*` HKDF labels; the KCV
construction; P-256 derivation from a seed, including a seed that forces the
rejection branch; Crockford Base32 with the O→0 and I/L→1 mappings; the 20-bit
checksum and a single-substitution repair; AES-256-GCM with its AAD binding.
`account_chain` is the whole hierarchy end to end, so a new implementation can
bisect where it diverges rather than staring at one wrong key.

The wire formats `PROTOCOL.md` leaves open — HKDF mode, the AAD layout,
Crockford packing, the rejection loop — are pinned in **discussion 19**. Read
that before implementing against this file.

## Where the numbers come from

The generator is `gen/gen.c`, **C against OpenSSL** — deliberately not Go.

Generating vectors from an implementation and then testing that implementation
against them proves only that the code agrees with itself. So the vectors come
from an implementation nobody here wrote, and the Go package is only ever the
thing under test. If you are ever tempted to regenerate this file to make a
test pass, stop: that is the exact failure the arrangement exists to prevent.
Fix the code, or argue on the record that the spec is wrong.

`gen.c` also re-computes published third-party vectors — the PHC Argon2id
reference outputs, RFC 5869, RFC 4231, NIST CAVP AES-GCM — and aborts if any
disagrees. Otherwise "independent" would only mean the two implementations are
not the same, not that either is right.

Every input is generated from its own published label:

    fixture(label, n) = SHA-256("contra/v1/vectors/" || label)[0..n]

so the file regenerates from the source alone, and nothing in it is, or may be
reused as, a real secret (`AGENTS.md` decision 8).

## Regenerating

    cd gen && make vectors

`REJECT_K` in the `Makefile` is the seed witness that forces a P-256 rejection.
It is pinned rather than searched each time because finding one costs a few
billion HKDF evaluations — a rejection is a ~2^-32 event. If the
`contra/v1/ec-p256` label ever changes, the witness dies with it: run
`make search` (minutes, all cores) and pin the new `k`.

## Verifying the Go side

    go test ./internal/contra/crypto/

The suite has been mutation-checked: lowering the Argon2 time cost, typoing an
HKDF label, truncating the KCV, dropping the AAD length prefixes or its domain
tag, skipping the rejected EC candidate, accepting non-zero Base32 pad bits,
widening the checksum, and dropping the O/I/L mappings each turn it red. A
vector nobody has watched fail is a vector nobody should trust.
