T-001 — Ed25519 test vectors for HERALD v0.2
Output path declared in TODO.md: test-vectors/v0.2/
Submission location (manthan sandbox): learning/herald-protocol/manthan/state/findings/T-001/
These are the canonical test vectors a HERALD v0.2 implementation MUST be
able to round-trip: same canonical CBOR-deterministic bytes, same id,
same Ed25519 signatures, and the same verifier verdict for each scenario.
Files
| File | Purpose |
|---|---|
generate.py | Deterministic vector generator. Re-running it produces a byte-identical vectors.json. |
vectors.json | The vectors themselves (keypairs + 7 envelope scenarios). |
verify.py | Loads vectors.json and verifies each vector against its declared expected result. Exits non-zero on any mismatch. |
RECEIPT.md | Quotes the SPEC sections each vector exercises. |
INTERPRETATION.md | Documents the spec ambiguities resolved (genesis trace fixed-point, deterministic nonce, dom-sep tag version). |
Canonical seed values
The three keypairs are derived deterministically from fixed 32-byte seeds:
| Sigil name | Seed (hex) | Public key (hex) | Sigil (128-bit, base32 lower, no padding) |
|---|---|---|---|
| alpha | 0x01 × 32 | 8a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5c | sigil:gr2q7gf5lh6pzfdnurnkvputhm |
| beta | 0x02 × 32 | 8139770ea87d175f56a35466c34c7ecccb8d8a91b4ee37a25df60f5b8fc9b394 | sigil:ni4ahvpqlgicuhdnv66jxjdssi |
| gamma | 0x03 × 32 | ed4928c628d1c2c6eae90338905995612959273a5c63f93636c14614ac8737d1 | sigil:wyxim75c6m5p4ywv22ywilqwee |
Sigil derivation per SPEC §2.1:
sigil:<base32(sha256(pubkey)[0..16])>128-bit truncation, RFC 4648 §6 alphabet, lower-case, no padding.
The vectors
| # | Name | Expected verifier result | Spec sections |
|---|---|---|---|
| 1 | genesis-alpha-valid | valid | §3.1, §3.2, §3.5, §3.6 |
| 2 | reply-beta-valid | valid | §3.1, §3.2, §3.3a, §3.5, §3.6 |
| 3 | tampered-genesis | tampered | §3.5, §6 |
| 4 | replay-genesis | replayed-nonce | §3.7, §6 |
| 5 | cross-trace-replay | parent-trace-mismatch | §3.3a, §6 |
| 6 | wrong-parent | orphaned | §6 |
| 7 | lineage-cycle-birth | rejected-lineage-cycle | §2.3 |
How to regenerate
Requires Python 3.9+ with cryptography and cbor2 installed.
pip install --user cryptography cbor2 # (or --break-system-packages on macOS Homebrew)
cd manthan/state/findings/T-001
python3 generate.pyRe-running produces a byte-identical vectors.json. Verified:
python3 generate.py /tmp/v.json && diff vectors.json /tmp/v.json && echo "deterministic"How to verify
cd manthan/state/findings/T-001
python3 verify.pyOutput is per-vector pass/fail with details. Exits 0 if every vector
matches its declared expected result, non-zero otherwise. Current run:
✓ genesis-alpha-valid expected=valid observed=valid
✓ reply-beta-valid expected=valid observed=valid
✓ tampered-genesis expected=tampered observed=tampered
✓ replay-genesis expected=replayed-nonce observed=replayed-nonce
✓ cross-trace-replay expected=parent-trace-mismatch observed=parent-trace-mismatch
✓ wrong-parent expected=orphaned observed=orphaned
✓ lineage-cycle-birth expected=rejected-lineage-cycle observed=rejected-lineage-cycle
OK: all 7 vectors verified against expected resultsCanonicalisation
Per SPEC §3.5 (RFC 8949 §4.2.1 + HERALD profile):
- CBOR-deterministic encoding via
cbor2.dumps(value, canonical=True). - No indefinite-length items.
- No NaN, ±Infinity, or negative-zero floats (in fact, no floats at all
in any vector).
- No tags.
- Map keys are text strings only.
id = sha256(canonical_body)wherecanonical_bodyis the CBOR
encoding of the envelope *with id omitted and signatures
replaced by an empty array*.
Domain separation
Per SPEC §3.6: origin-scope signatures are computed over
"herald.envelope.v2" || canonical_body(The task brief mentioned herald.envelope.v1; we follow the SPEC as
currently written. See INTERPRETATION.md.)
Nonces
Per SPEC §3.7 production senders MUST use a CSPRNG. For test-vector
reproducibility we derive nonces deterministically from a label:
nonce = sha256(b"herald.testvector.nonce/" + label)[:16]This is a test-vector accommodation only and is documented inINTERPRETATION.md. Implementations consuming these vectors should
treat the nonces as opaque 16-byte values.