0b881e1a74cdac1b2434703afa01ea3a273c8930
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
496d622671 |
feat(attesto3): make every SDK check every vector it is able to check
Twelve of twenty-five provenance vectors were consumed by no SDK. From outside the repository that looked exactly like full coverage, which is the problem: a corpus proves nothing about an implementation that never loads it. Vectors now declare what they require. `sha256` vectors use SHA-256 and canonical JSON, which all three SDKs have, so an unconsumed one is a gap and fails a contract. `ristretto255` vectors need curve scalar arithmetic no SDK carries; those are a declared boundary with a stated reason rather than a silent skip, so the exemption cannot spread by habit. Coverage went from 13/17 reachable in each SDK to 17/17. Closing the four gaps surfaced a real verifier weakness: `capsule_root` receives digests, so by then a role is no longer visible, and a tree carrying `evidence_root` twice with `vault_identity_commitment` missing folds to a root all three SDKs accepted. Each gains `ordered_top_leaf_digests`, which requires each of the six roles exactly once, and the safe path is now the easy one. Two findings of my own drift: * The Go corpus-typing test accepted only `valid` and `invalid`, so it had been failing since the Sprint 1 recovery added vectors carrying `differs` and `rejected`. I updated Python's typing test then and not Go's, and no gate caught it because the SDK parity suites are not in the sprint gates. Fixed, and both Go and TypeScript now also require the capability declaration. * TypeScript's strict indexing caught that a missing randomizer would have reached the hash as the string "undefined". Both halves are now checked. Python 88, Go ok, TypeScript 107, Local Vault 375, edge 116, backend 1404. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
f80b28c3b5 |
feat(attesto3): register the REVIEW-02 disclosure v2 and ZK range domains
Sprint 1 recovery, first item. §5.2 of REVIEW-02 adds three domains the registry did not carry: attesto.disclosure.v2, attesto.zk.range.v1.statement and attesto.zk.range.v1.transcript. They are not in the attesto.provenance.v1. namespace, and that is deliberate: disclosure v2 and the ZK range protocol are separate protocols with their own versions, so a preimage space is named after the protocol that owns it rather than the one it happens to travel with. That namespace difference meant the parity contract could not see them at all — its pattern matched attesto.provenance.v1.* only, so three new domains would have been silently unguarded. The pattern now names each protocol explicitly rather than loosening to a prefix wildcard: a looser first attempt also matched prose that mentions a namespace without a terminal segment and reported it as an unknown domain. All four registry locations updated together with the golden vector, and all three SDK parity suites plus the contract are green. The Go failure message was also corrected: it printed "rust=21 go=21" while failing on a third hardcoded expectation it never named. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
9e6ae6277a |
feat(attesto3): Sprint 1 — pinned attesto-edge core + 3-language parity
Establishes the single normative cryptographic authority for the provenance lane, and freezes the boundary and Merkle semantics before any ingestion path exists to depend on them. New crate edge/ (attesto-edge) - domains.rs — the closed 18-domain v1 registry. Unknown domains are errors, never a fallback: a generic attesto.provenance.v1.commitment would let two unrelated objects share a preimage space, which is what domain separation exists to prevent. - canonical.rs — conforming ATTESTO-CANONICAL-JSON-001, not a second serializer. Floats and integers past 2^53-1 are refused with their JSON path. - commitment.rs — randomized, domain-separated commitments. Legacy Proofstream commitments stay deterministic; provenance values are low-entropy, so claim_type = "c2pa_manifest_valid" hashed deterministically is a dictionary lookup and a deterministic asset digest links a file across events. Debug for Randomizer prints <redacted>: it is C1 and Debug output reaches logs. - merkle.rs — the two-level capsule forest. A claim leaf cannot verify against evidence_root on two independent grounds: subtrees fold under different node domains, and the top leaf binds leaf_role. Odd nodes are promoted, never duplicated, matching the rule inclusion.json already pins for Proofstream. - boundary.rs — derives nothing. It shapes a request for attesto-nova, reusing the existing event-payload 16 KiB size class so Nova's closed boundary_max_len() allowlist needs no new entry. On-wire artifact is N10.R redacted. - main.rs — NDJSON surface (handshake, canonicalize, commit, capsule-root, boundary-derive, self-test), the transport the backend already speaks. Poseidon is deliberately absent. It stays in proofs/nova, reached through that crate's public boundary API, so there remains exactly one Poseidon authority. The only Nova change is making CIRCUIT_ID and PROVER_VERSION pub so the edge handshake can report the prover it wraps; its 40 tests are unchanged. Test-only randomizers are gated behind the `test-vectors` cargo feature and compiled out of release builds. A caller who can choose the randomizer can make production commitments deterministic — that is not a debug convenience, it is the vulnerability. A release build refuses one and reports accepts_caller_randomizers: false in its handshake. Conformance - golden-vectors/provenance-v0.1-dev/ — 14 Rust-authored vectors, 9 valid and 5 invalid. CI regenerates them and requires git diff --exit-code, so the committed corpus cannot drift from what the normative core produces. - Python (sdk/python/src/attesto/provenance.py, 19 tests) and Go (sdk/go/provenance.go, 14 tests) reproduce every valid vector and refuse every invalid one. Both reuse their existing canonical-JSON primitives rather than forking a second implementation. - provenance_domain_registry_contract.py pins Rust = spec = Python = Go = vector, and that no registry declares the forbidden fallback. It reads each declaration block rather than whole files, so the negative test cases that must name the fallback do not trip it. TypeScript parity is still owed and Sprint 1's DoD is not fully closed: the sdk/typescript build break recorded in the Sprint 0 baseline makes its whole suite unrunnable. Also fixes a Sprint 0 guard found by the guard itself: the naming lint scanned only tracked files, so new work read green until it was committed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |