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
- 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. - 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 = 0with the sign bit set). Had the suite fetchedmaster, 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
EOFWhich 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.
- Re-fetch from the upstream URL above into a scratch location.
diffagainst 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.- Re-run both implementations over the new bytes (the
procedure and results are in
decisions.mdD-19). If they still agree everywhere, update the file, its SHA-256 and commit pin here, and the case-index tables above. - 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.