Beta Pre-1.0 — the file-naming convention may still change. v1.0 will freeze it. ZDDC home · All specifications

ZDDC - Custody Extension ~1.0
Vendored Ed25519 Conformance Corpora
Status: Draft

Third-party known-answer corpora for [M-E10] (Ed25519 verification semantics), vendored verbatim. They are the evidence behind decisions.md D-19 and the inputs [M-R12] requires a conformance runner to execute.

These files are not generated and are not ZDDC vectors. They sit beside vectors/ rather than inside it on purpose: vectors/gen.js --check treats any .json under vectors/ that it did not generate as drift (EXTRA), and that guard is worth more absolute than convenient. Everything under vectors/ is generated; everything here is fetched.

Why vendored rather than fetched

  1. Hard constraint 1 is sneakernet. ZDDC's whole premise is that artifacts verify offline, from a file:// page, with no network. A conformance suite that must reach the internet to run contradicts the contract it is testing. [M-R5] says the runner MUST be runnable offline; a fetch step would have made [M-E10] the one requirement you could not check on an air-gapped machine — precisely the machine most likely to be checking custody.
  2. A fixed contract wants fixed inputs. These corpora gain cases upstream. The Wycheproof file pinned below was last changed two days before it was vendored, by a commit adding cases for exactly the edge this spec had to reason about (x = 0 with the sign bit set). Had the suite fetched master, two runs of "the same" conformance suite months apart would have tested different things and both would have reported "pass" — and a regression introduced upstream would look like a ZDDC failure. A pinned byte string makes "conforming" mean one thing.

Provenance

wycheproof-ed25519_test.json

Upstream https://raw.githubusercontent.com/C2SP/wycheproof/master/testvectors_v1/ed25519_test.json
Project Project Wycheproof — https://github.com/C2SP/wycheproof (formerly google/wycheproof)
Pinned commit 5722833ca004983abd1a91bcb6c24596d50ac0f9 — the last commit touching this path, 2026-08-11, "eddsa: add vectors for x = 0 with the sign bit set"
Corpus version source.version = 0.9rc5; schema = eddsa_verify_schema_v1.json
Retrieved 2026-08-13 (UTC), from master
Licence Apache-2.0
SHA-256 752d2ea7d7c6cf4736381b6cbacb61f8182b126ab7cd9b058f00c50084975536
Size 126 699 bytes — 78 key groups, 151 test cases
Modifications None. Byte-for-byte as retrieved; renamed from ed25519_test.json only to namespace it here.

ed25519-speccheck-cases.json

Upstream https://raw.githubusercontent.com/novifinancial/ed25519-speccheck/main/cases.json
Project ed25519-speccheck, the companion suite to "Taming the many EdDSAs" (Chalkias, Garillot, Nikolaenko) — the paper docs/design/federation-and-custody.md §6.4 cites
Pinned commit 5e4bfc4542293286e9ad3cb2b805badee00503de — the last commit touching this path, 2020-10-12 (the file has been stable since)
Retrieved 2026-08-13 (UTC), from main
Licence Apache-2.0
SHA-256 08e47a36d9aead288664930505584f353fff113ab854f2800db1e4f5b3540450
Size 3 589 bytes — 12 cases
Modifications None. Byte-for-byte as retrieved; renamed from cases.json only to namespace it here.

Both projects are Apache-2.0, which this AGPL-3.0 repository may include. The files carry no in-band notice (they are bare JSON), so this block is the attribution — do not drop it.

Verify the pins at any time:

cd docs/spec/custody/corpora && sha256sum -c <<'EOF'
752d2ea7d7c6cf4736381b6cbacb61f8182b126ab7cd9b058f00c50084975536  wycheproof-ed25519_test.json
08e47a36d9aead288664930505584f353fff113ab854f2800db1e4f5b3540450  ed25519-speccheck-cases.json
EOF

Which cases decide what

The corpora are large and mostly unremarkable; a handful of cases carry the whole [M-E10] argument. They are listed here because a reader can no longer diff against upstream to find them, and because speccheck case files are bare arrays with no comments at all — the index is the identifier.

ed25519-speccheck-cases.json — indices are 0-based array positions:

cases axis required behaviour
0, 1 small-order A ACCEPT. The public key is a small-order point and the equation is otherwise satisfied. A verifier that adds a small-order-A rejection diverges here ([M-E10] rule 6).
4, 5 cofactored vs cofactorless REJECT. [8S]B = [8]R + [8k]A holds while [S]B = R + [k]A does not. This pair is the only discriminator for the cofactor axis in either corpus — Wycheproof does not cover it at all ([M-E10] rule 5).
6, 7 S ≥ L REJECT. The equation is otherwise satisfied, so only the range check refuses them ([M-E10] rule 2).
2, 3 small-order / mixed-order R ACCEPT.
8, 9 non-canonical / undecodable R REJECT.
10, 11 A encoded as y = p − 1 with the x-sign bit set 10 REJECT, 11 ACCEPT — same key, same signature, different message. The split is only possible if the key decoded, which is how D-19 established that neither implementation is RFC 8032 §5.1.3-strict here. Both deviate identically; see [M-E10]'s second consequence note.

wycheproof-ed25519_test.json — tcId, and every case must match its own result field:

cases axis
63–66 (SignatureMalleability) s + L, s + 2L, s + 4L, s + 8L — each unchanged mod L, so only the S < L range check refuses them. REJECT.
67–69 s + 2^253, s + 2^254, s + 2^255 — catch a parser that silently masks high bits off. REJECT.
70 s + p — catches reducing s mod the field prime instead of checking s < L. REJECT.
85 (InvalidKtv) "S just above the bound" — the range-check boundary. REJECT.
42–62, 151 (InvalidEncoding) Non-canonical R, built so that S still matches the modified R; accepted only by verifiers that decode R leniently or compare R as a point. REJECT — which is what forces [M-E10] rule 5's encoding comparison.
30–41 Truncated, over-long, and re-compressed signatures. REJECT (also [M-E9]).

Refreshing these files

Never automatically, and never as a drive-by. The pinned bytes are part of what "conforming" means; changing them changes the contract's evidence base.

  1. Re-fetch from the upstream URL above into a scratch location.
  2. diff against the committed copy, and read every added or changed case. Upstream additions are usually new edge cases — decide whether ZDDC's two reference implementations still agree on them before adopting.
  3. Re-run both implementations over the new bytes (the procedure and results are in decisions.md D-19). If they still agree everywhere, update the file, its SHA-256 and commit pin here, and the case-index tables above.
  4. If they disagree on any new case, do not adopt it silently. A disagreement narrows [M-E10] to the intersection and is a spec change, not a fixture bump — reopen D-19.

Update the pin as a reviewed change, with the diff summarized in the commit message.

Rendered from “Vendored Ed25519 Conformance Corpora” ZDDC