carlos / contra Public

Clone
git clone https://amadan.net/carlos/contra

Plain git — no account needed to clone.

Download

Download this file

πŸ€– 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.