wai: every Ed25519 signature verified strictly, by one verifier (SPEC §7) #186
Merged
dcharlot
merged 14 commits from 2026-10-08 01:50:19 -04:00
wai/strict-ed25519 into main 2026-10-07
Merge main (
1cf132ec: audio provenance) into wai/strict-ed25519
Some checks failed
no-vanity-metrics / WAI public text names no company or product (pull_request) Successful in 3s
standards workspaces build + test / arl-rs (pull_request) Successful in 1m13s
standards workspaces build + test / eoc-rs (pull_request) Successful in 1m56s
standards workspaces build + test / jcp-rs (pull_request) Successful in 1m28s
standards workspaces build + test / joule-ui-rs (pull_request) Successful in 1m8s
standards workspaces build + test / joulehook-rs (pull_request) Successful in 1m10s
standards workspaces build + test / proof-rs (pull_request) Successful in 1m12s
standards workspaces build + test / sandbox-rs (pull_request) Successful in 2m15s
standards workspaces build + test / sense-rs (pull_request) Successful in 1m7s
standards workspaces build + test / smart-byte-rs (pull_request) Successful in 3m37s
wai cross-architecture byte-equality / linux-x86_64 (pull_request) Successful in 23s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 6m16s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 13s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 12m3s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 8s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 24s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 46s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 1m37s
WAI HTTP delivery / HTTP corpus · falsification and differential (pull_request) Successful in 1h14m19s
WAI HTTP delivery / cache rules · controls and labelled caches (pull_request) Successful in 1m2s
WAI JPEG-AI whole decode (pinned inputs, required) / fetch pinned JPEG-AI inputs → whole decode + pixel hash, required (pull_request) Successful in 1m1s
WAI neural sink gates / model-free refusals of the neural sink (pull_request) Successful in 1m6s
wai-pack / wai-pack · linux (pull_request) Failing after 12s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Failing after 50s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 18s
wai-pack / wai-pack · dependency licences (pull_request) Failing after 1m9s
WAI section references / cited sections exist (pull_request) Successful in 11s
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Successful in 4m6s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 1m59s
workflow hygiene / every workflow supersedes its own queue (pull_request) Successful in 4s
CHANGELOG: both v0.4.0 sections kept, the signature rule above the audio one. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Merge main (
7ac6deca, nothing under wai/) into wai/strict-ed25519
Some checks failed
no-vanity-metrics / WAI public text names no comparison (pull_request) Has been cancelled
no-vanity-metrics / WAI public text names no company or product (pull_request) Has been cancelled
standards workspaces build + test / arl-rs (pull_request) Has been cancelled
standards workspaces build + test / eoc-rs (pull_request) Has been cancelled
standards workspaces build + test / jcp-rs (pull_request) Has been cancelled
standards workspaces build + test / joule-ui-rs (pull_request) Has been cancelled
standards workspaces build + test / joulehook-rs (pull_request) Has been cancelled
standards workspaces build + test / proof-rs (pull_request) Has been cancelled
standards workspaces build + test / sandbox-rs (pull_request) Has been cancelled
standards workspaces build + test / sense-rs (pull_request) Has been cancelled
standards workspaces build + test / smart-byte-rs (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · linux (pull_request) Has been cancelled
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Has been cancelled
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Has been cancelled
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · linux (pull_request) Has been cancelled
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Has been cancelled
WAI energy receipts / energy receipts · linux (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · falsification and differential (pull_request) Has been cancelled
WAI HTTP delivery / cache rules · controls and labelled caches (pull_request) Has been cancelled
WAI JPEG-AI whole decode (pinned inputs, required) / fetch pinned JPEG-AI inputs → whole decode + pixel hash, required (pull_request) Has been cancelled
WAI neural sink gates / model-free refusals of the neural sink (pull_request) Has been cancelled
wai-pack / wai-pack · linux (pull_request) Has been cancelled
wai-pack / wai-pack · independent verifier (Python) (pull_request) Has been cancelled
wai-pack / wai-pack · the floor through a media prober (pull_request) Has been cancelled
wai-pack / wai-pack · dependency licences (pull_request) Has been cancelled
WAI section references / cited sections exist (pull_request) Has been cancelled
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Has been cancelled
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Has been cancelled
workflow hygiene / every workflow supersedes its own queue (pull_request) Has been cancelled
Co-Authored-By: Claude Opus 5.5 <[email protected]>
wai-pack: the v1 receipts test reads the refused cases as refused
the_receipts_json_round_trips required every receipt of receipts/ to verify; the three marked "signature": "refused" must not, and there are three. Co-Authored-By: Claude Opus 5.5 <[email protected]>
jwp corpus: the weak-key cases go last, so the pre-branch prefix is unchanged
the_pre_branch_vectors_are_an_unchanged_prefix pins the first 83 vectors by digest; the three new group cases sat inside them. They now follow the recovery vectors, and every existing vector keeps its place and its bytes. joule-moq's corpus test passes over the new ones. Co-Authored-By: Claude Opus 5.5 <[email protected]>
SPEC §7 Signatures: the one Ed25519 rule, cited by the receipt extensions
Every Ed25519 signature WAI defines is verified strictly, in order: a canonical key, a key not of small order, S < L, R not of small order, and the cofactorless equation with R compared byte for byte. OER/2's chain signature keeps the rule oer-receipts §5 settles. jwp-receipts §3.1 step 3, http-delivery's playlist receipt and packaging's receipt cite it instead of restating it; the worlds, avatars, feeds, spatial-audio, haptics, volumetric, score, film, provenance, ternary and quantum receipt texts cite it where they name their signature. CHANGELOG records the sweep. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Merge main (#166 merged as
0e6c54dd) into wai/strict-ed25519
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Leave wai-quantum on its own verifier; the rule goes back into wai-rs
wai-quantum belongs to another line of work that is shipping releases, and moving it was not needed to put every signature check written in wai-rs on one strict verifier. This undoesc37c3e9aand the quantum-ops part of9b5854aa: wai/wai-sig is gone, wai-rs/src/sig.rs is the rule again (under the `ed25519` feature), and wai-quantum, its Cargo.toml and lock, every other workspace's lock, the quantum-ops corpus and wai_quantum_ops_vectors are as main has them. wai-quantum's lenient checks are reported to its owner instead. The JWP, signer and packaging weak-key cases stay; they verify through wai::sig. The two HTTP falsify patches are cut against wai-rs/src/sig.rs again, and wai-http and wai-pack fire on claim-conformance/_ed25519.py, which their Python verifiers read. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Corpora: weak-key cases in every receipt family's corpus
Each family whose corpus lacked them gets the three signatures a verifier that checks only the equation accepts and WAI's rule refuses (wai_sig::forgeries): the identity as the key with (R, S) = (B, 1), the identity spelled y = p + 1, and R of small order under the base point. Every case is new; no existing vector's expectation changes. - jwp: group_signer_key_identity_forged, group_signer_key_identity_y_not_reduced (step 1, bad_signer_key), group_signature_r_small_order (step 2). - signer: anchor_root_identity_forged, anchor_root_identity_y_not_reduced, anchor_root_signature_r_small_order (not anchored, key_log_signature_invalid). - pack: mutations receipt-key-identity, receipt-key-identity-y-not-reduced, receipt-r-small-order (receipt-signature); receipts/ v1-key-identity, v1-key-identity-y-not-reduced, v1-r-small-order, marked "signature": "refused", which wai-pack and pack_verify.py now read. - quantum-ops: cal_q3_ and job_bell_ key_identity, key_identity_y_not_reduced and r_small_order, "verifies": false, which wai_quantum_ops_vectors checks are refused and hash as pinned. claim (signer_key_identity, signer_key_identity_y_not_reduced, signature_r_small_order and others) and http (sig-key-identity, -long, sig-r-small-order) had them already. OER/2 keeps its own rule. PackageReceipt::signing_payload (v1) and the calibration and job receipts' signing_payload are public, so a corpus generator can sign over them; wai-quantum re-exports wai_sig beside ed25519_dalek. Co-Authored-By: Claude Opus 5.5 <[email protected]>
http falsify: the two signature patches break wai-sig, where the rule now lives
Co-Authored-By: Claude Opus 5.5 <[email protected]>
wai-sig: the strict Ed25519 rule as its own crate, and wai-quantum on it
wai-quantum's receipts still checked signatures with VerifyingKey::from_bytes and the lenient verify, and could not call wai::sig: wai depends on wai-quantum, not the reverse. The rule moves out of wai-rs/src/sig.rs, unchanged, into the crate wai/wai-sig, which both depend on; `wai::sig` re-exports it, so every call site in wai-rs reads as before. wai-quantum's `_signing` feature brings it. Its twelve verify paths now call wai_sig::verify_strict: the calibration, job, mitigation, decode, compile and rearrange receipts, the QBOM and its labelled form, the phasor, circuit, SPD and resource receipts, and every Labelled<R>. Each module has a test that its receipts refuse the three forgeries a lenient check accepts. assert_refuses_forgeries is public so both crates' tests use it. Lockfiles: the path crate added to every workspace that builds wai or wai-quantum (wai-rs, wai-quantum, wai-pack, wai-quantum-serve, wai-relay, wai-spz-gen, wai-web). wai-pack's lock also moves wai-quantum 0.3.36 to 0.3.37, which #176 left behind. CI: wai-conformance runs wai-sig's tests on every leg; every workflow that builds wai-rs or wai-quantum, and wai-http and wai-pack, which verify signatures through it, fire on wai/wai-sig and on claim-conformance/_ed25519.py. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Merge main (#175 merged as
ade3f855, #176) into wai/strict-ed25519
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Python verifiers: one strict Ed25519 rule, _ed25519.py
claim_verify.py made the key, order and range checks itself and left the equation to `cryptography`; http_verify.py had its own RFC 8032 implementation; and pack_verify.py called `cryptography` alone, which accepts a key of small order, a second spelling of a key, and an R of small order. All three, and the JWP and signer verifiers through claim_verify, now call claim-conformance/_ed25519.py, written from RFC 8032 with the standard library (as http_verify's was): canonical A of large order, S < L, R decoded and not of small order, and the cofactorless equation with R compared byte for byte. It reports a bad key apart from a bad signature, keeps http_verify's --mutate small-order-key and small-order-r, and checks itself against RFC 8032's first two vectors and the three forgeries. Every committed corpus verifies unchanged: no vector's expectation moves. The OER reference keeps its own rule (oer-receipts §5). Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06
sig::forgeries asserts only that a lenient check accepts each forgery
The strict refusal is the caller's to show, so a test of a signing site fails at the site, not inside the constructor, when the rule lets one through. sig's own test checks each forgery's refusal. Co-Authored-By: Claude Opus 5.5 <[email protected]>
wai-rs: every Ed25519 check outside OER/2 goes through sig::verify_strict
Sixteen sites still decoded the key with VerifyingKey::from_bytes and checked with the lenient verify, which accepts a key of small order (under the identity, (R, S) = (B, 1) verifies over any message), a second spelling of a point (y = p + j is reduced), and an R of small order. Each now calls crate::sig::verify_strict: the video, SSAI, key-release, channel and package receipts (v1 and v2), the provenance, ternary and the five media receipts, and the world's receipt, op signature and container seal. A package names a bad key apart from a bad signature under receipt-signature; an op's bad key is still BadActorKey. claim_receipt's own copy of the rule (signer_key, verify_ed25519_strict) is gone: claims, JWP groups and the key log call the same routine, and its key test moved into sig.rs. sig builds under a new `ed25519` feature that is only the dependency, which world and oer enable, so the helper exists exactly when ed25519-dalek does and asks for nothing else. sig::forgeries builds, for any signing payload, the three signatures a lenient verifier accepts and the rule refuses; every moved site has a test that its receipt refuses all three. oer.rs is untouched: OER/2's settled rule refuses the same keys but not R of small order. Co-Authored-By: Claude Opus 5.5 <[email protected]>