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>
This commit is contained in:
+37
-3
@@ -48,9 +48,9 @@ var ProvenanceDomains = map[string]struct{}{
|
||||
"attesto.provenance.v1.evidence_tree": {},
|
||||
"attesto.provenance.v1.policy_tree": {},
|
||||
"attesto.provenance.v1.attestation": {},
|
||||
"attesto.disclosure.v2": {},
|
||||
"attesto.zk.range.v1.statement": {},
|
||||
"attesto.zk.range.v1.transcript": {},
|
||||
"attesto.disclosure.v2": {},
|
||||
"attesto.zk.range.v1.statement": {},
|
||||
"attesto.zk.range.v1.transcript": {},
|
||||
}
|
||||
|
||||
// TopLeafRoles is the canonical order of the six typed top-tree leaves. No
|
||||
@@ -263,6 +263,40 @@ func TopLeafDigest(leafRole, commitment, randomizer string) (string, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// OrderedTopLeafDigests builds the six typed top leaves, requiring each role
|
||||
// exactly once.
|
||||
//
|
||||
// CapsuleRoot 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 it will accept. The check has to happen here, where the roles still
|
||||
// exist, which is also why callers should reach for this rather than assembling
|
||||
// the slice themselves.
|
||||
func OrderedTopLeafDigests(commitments, randomizers map[string]string) ([]string, error) {
|
||||
for _, role := range TopLeafRoles {
|
||||
if _, ok := commitments[role]; !ok {
|
||||
return nil, fmt.Errorf("capsule tree is missing top leaf %s", role)
|
||||
}
|
||||
}
|
||||
known := make(map[string]struct{}, len(TopLeafRoles))
|
||||
for _, role := range TopLeafRoles {
|
||||
known[role] = struct{}{}
|
||||
}
|
||||
for role := range commitments {
|
||||
if _, ok := known[role]; !ok {
|
||||
return nil, fmt.Errorf("unknown top leaf role: %s", role)
|
||||
}
|
||||
}
|
||||
digests := make([]string, 0, len(TopLeafRoles))
|
||||
for _, role := range TopLeafRoles {
|
||||
digest, err := TopLeafDigest(role, commitments[role], randomizers[role])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
digests = append(digests, digest)
|
||||
}
|
||||
return digests, nil
|
||||
}
|
||||
|
||||
// CapsuleRoot folds the six typed top leaves into the capsule root.
|
||||
func CapsuleRoot(orderedTopLeafDigests []string) (string, error) {
|
||||
if len(orderedTopLeafDigests) != len(TopLeafRoles) {
|
||||
|
||||
Reference in New Issue
Block a user