package attesto // Go parity on offline disclosure verification. // // The fixture is a presentation the Local Vault really built through the real // edge core. Three implementations agreeing with each other proves less than // three agreeing with the producer, which is why it is not written to satisfy // the verifiers. import ( "encoding/json" "os" "path/filepath" "strings" "testing" "time" ) func loadPresentation(t *testing.T) map[string]any { t.Helper() path := filepath.Join( "..", "..", "golden-vectors", "provenance-v0.1-dev", "provenance-disclosure-presentation-valid.json", ) raw, err := os.ReadFile(path) if err != nil { t.Fatalf("read fixture: %v", err) } var vector map[string]any if err := json.Unmarshal(raw, &vector); err != nil { t.Fatalf("parse fixture: %v", err) } return vector } func presentationCopy(t *testing.T, vector map[string]any) map[string]any { t.Helper() raw, err := json.Marshal(vector["presentation"]) if err != nil { t.Fatalf("copy: %v", err) } var copied map[string]any if err := json.Unmarshal(raw, &copied); err != nil { t.Fatalf("copy: %v", err) } return copied } func TestARealDisclosureVerifiesAcrossLanguages(t *testing.T) { vector := loadPresentation(t) report := VerifyDisclosure( vector["presentation"].(map[string]any), WithExpectedNonce(vector["nonce"].(string)), ) if !report.Ok { t.Fatalf("a real disclosure did not verify: %v", report.Problems) } expected := vector["expected"].(map[string]any) if float64(len(report.VerifiedLeaves)) != expected["verified_leaf_count"].(float64) { t.Fatalf("verified %d leaves, expected %v", len(report.VerifiedLeaves), expected["verified_leaf_count"]) } if report.Freshness != "challenge" { t.Fatalf("freshness: got %q want challenge", report.Freshness) } } func TestWithoutAChallengeTheReportSaysSo(t *testing.T) { // Weaker evidence, reported as weaker rather than presented as the same. vector := loadPresentation(t) report := VerifyDisclosure(vector["presentation"].(map[string]any)) if !report.Ok { t.Fatalf("expiry-bounded verification failed: %v", report.Problems) } if report.Freshness != "bounded_lifetime" { t.Fatalf("freshness: got %q want bounded_lifetime", report.Freshness) } } func TestASwappedValueDoesNotOpenItsLeaf(t *testing.T) { vector := loadPresentation(t) tampered := presentationCopy(t, vector) revealed := tampered["revealed"].([]any) entry := revealed[0].(map[string]any) entry["value"].(map[string]any)["value"] = map[string]any{"manifest_count": "9"} report := VerifyDisclosure(tampered, WithExpectedNonce(vector["nonce"].(string))) if report.Ok { t.Fatal("a swapped value still verified") } if !hasDisclosureProblem(report.Problems, "does not open") { t.Fatalf("expected an opening failure, got %v", report.Problems) } } func TestALeafThatDoesNotFoldToTheRootIsRefused(t *testing.T) { // A leaf can open its own commitment perfectly and still belong to a // different capsule. Corrupting a sibling leaves the value and randomizer // untouched, so only the two-hop fold can catch it — which is what // distinguishes a verifier from a value checker. vector := loadPresentation(t) tampered := presentationCopy(t, vector) proofs := tampered["inclusion_proofs"].([]any) steps := proofs[0].(map[string]any)["subtree_steps"].([]any) steps[0].(map[string]any)["sibling"] = strings.Repeat("0", 64) report := VerifyDisclosure(tampered, WithExpectedNonce(vector["nonce"].(string))) if report.Ok || !hasDisclosureProblem(report.Problems, "not included") { t.Fatalf("a leaf outside the capsule was accepted: %v", report.Problems) } } func TestACorruptedTopPathIsRefused(t *testing.T) { vector := loadPresentation(t) tampered := presentationCopy(t, vector) proofs := tampered["inclusion_proofs"].([]any) steps := proofs[0].(map[string]any)["top_steps"].([]any) steps[0].(map[string]any)["sibling"] = strings.Repeat("0", 64) report := VerifyDisclosure(tampered, WithExpectedNonce(vector["nonce"].(string))) if report.Ok || !hasDisclosureProblem(report.Problems, "not included") { t.Fatalf("a corrupted top path was accepted: %v", report.Problems) } } func TestAReplayedNonceIsRefused(t *testing.T) { vector := loadPresentation(t) report := VerifyDisclosure( vector["presentation"].(map[string]any), WithExpectedNonce(strings.Repeat("ff", 32)), ) if report.Ok || !hasDisclosureProblem(report.Problems, "nonce") { t.Fatalf("a replayed nonce was accepted: %v", report.Problems) } } func TestAnExpiredDisclosureIsRefused(t *testing.T) { vector := loadPresentation(t) report := VerifyDisclosure( vector["presentation"].(map[string]any), WithVerificationTime(time.Date(2100, 1, 1, 0, 0, 0, 0, time.UTC)), ) if report.Ok || !hasDisclosureProblem(report.Problems, "expired") { t.Fatalf("an expired disclosure was accepted: %v", report.Problems) } } func TestADisclosureForAnotherAssetIsRefused(t *testing.T) { vector := loadPresentation(t) report := VerifyDisclosure( vector["presentation"].(map[string]any), WithSubjectCommitment(strings.Repeat("aa", 32)), ) if report.Ok || report.SubjectChecked { t.Fatalf("a foreign subject was accepted: %v", report.Problems) } } func TestTheMatchingSubjectIsReportedAsChecked(t *testing.T) { vector := loadPresentation(t) report := VerifyDisclosure( vector["presentation"].(map[string]any), WithSubjectCommitment(vector["subject_commitment"].(string)), ) if !report.Ok || !report.SubjectChecked { t.Fatalf("the matching subject was not reported: %v", report.Problems) } } func TestATamperedSignatureIsCaught(t *testing.T) { vector := loadPresentation(t) tampered := presentationCopy(t, vector) tampered["nonce"] = strings.Repeat("cd", 32) report := VerifyDisclosure(tampered) if report.Ok || !hasDisclosureProblem(report.Problems, "signature") { t.Fatalf("a tampered presentation was accepted: %v", report.Problems) } } func TestEveryProblemIsCollected(t *testing.T) { // A caller should see everything wrong with a presentation at once. vector := loadPresentation(t) tampered := presentationCopy(t, vector) revealed := tampered["revealed"].([]any) revealed[0].(map[string]any)["value"].(map[string]any)["value"] = map[string]any{"x": "y"} report := VerifyDisclosure( tampered, WithExpectedNonce(strings.Repeat("ff", 32)), WithVerificationTime(time.Date(2100, 1, 1, 0, 0, 0, 0, time.UTC)), ) if len(report.Problems) < 3 { t.Fatalf("expected several problems, got %v", report.Problems) } } func TestTheReportSaysWhatItDoesNotClaim(t *testing.T) { vector := loadPresentation(t) report := VerifyDisclosure(vector["presentation"].(map[string]any)) if len(report.NotClaimed) != 1 || report.NotClaimed[0]["id"] != "undisclosed_facts_absent" { t.Fatalf("the non-claim is missing: %v", report.NotClaimed) } } func TestAnUnknownProtocolIsRefusedFirst(t *testing.T) { report := VerifyDisclosure(map[string]any{"protocol": "SOMETHING-ELSE"}) if report.Ok || len(report.Problems) != 1 { t.Fatalf("expected a single protocol refusal, got %v", report.Problems) } } func hasDisclosureProblem(problems []string, needle string) bool { for _, problem := range problems { if strings.Contains(problem, needle) { return true } } return false }