Repository navigation
Conversation
…ry one Both trust policies matched an accreditation by intersecting the credential's whole type list with accreditedFor.types, and on real EBSI data both lists start with the generic base types (VerifiableCredential, VerifiableAttestation), so the intersection was never empty: an issuer accredited only for OtherType was accepted for every credential, and a chain whose leaf accreditation listed only generic types closed through an accreditor scoped to another type. The generic base types are ignored on both sides and every specific type of the credential must be listed by the leaf accreditation (rule (b), the maintainer's decision of 2026-10-30); the delegated scope is the credential's specific types; a credential with no specific type, or an accreditation listing only generic types, is refused. Closes #367
…he anchor The adversarial review of PR #373 (no wrong-accept beyond the intended relaxation above the leaf) found that the leaf-is-anchor shortcut in verify_trust_chain returned before the "no specific type" refusal, so a generic-only credential issued by a pinned anchor was trusted with an empty scope, against what the CHANGELOG and threat-model I26 promise. The refusal runs first now; tests pin it and the shortcut's scope (a mutant returning the whole type list survived the suite). The CHANGELOG, assurance and test docstrings now say exactly which negatives main already refused, and name the intended relaxation. Refs #367
Owner
Author
Adversarial review (before merge)Reviewer: a subagent on the session model, instructed to break the change with runnable repros (kept locally under the session's scratchpad, What was tried and held
Fixed after the review
Left for follow-ups (not regressions; identical or stricter than
|
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #367. Milestone: Conformance & production readiness. Security fix, under
[1.38.0] — unreleased; third of the trust-policy trio after #371 (#358) and #372 (#368). Rule (b), the maintainer's decision of 2026-10-30: every non-generic type of the credential must be listed by the leaf accreditation.Step 0 — the recordings
Every recorded
typelist and every recordedaccreditedFor.typeslist starts with the generic base types: the four recorded RootTAO accreditations (v5 pilot, Core v1 preprod GRNET / INCM, prod ITI) areaccreditedFor[VerifiableCredential, VerifiableAttestation, VerifiableAuthorisationForTrustChain](one[…, TrustedNodesList]), and their ownvc.typeis[VC, VA, VerifiableAccreditation, VerifiableAccreditationToAttest]. The tests that walk them ask for["VerifiableAuthorisationForTrustChain"]or[VC, VA, VerifiableAuthorisationForTrustChain]: under rule (b) the specific set{VerifiableAuthorisationForTrustChain}is listed, so they keep verifying.VerifiableAccreditationnever appears in a recordedaccreditedFor, which is why it is treated as a generic base type (an accreditation listing[VC, VA, VerifiableAccreditation]was trusted for anything onmain, row 7 below).Measurement on
mainat 2ec271c (#358 and #368 merged, CPython 3.14.10)Real P-256 signatures, LEAF accredited by ROOT (the anchor), through
evaluate_issuer_trust(single) andverify_trust_chain(chain), identical on v4, v5 and Core v1:So on real-shaped data the type rule never refused anything but two lists with no type in common at all.
What changed
models.py:GENERIC_TYPES = {VerifiableCredential, VerifiableAttestation, VerifiableAccreditation},specific_types(),Accreditation.specific_credential_types.trust.py:accreditation_ineligibilityadds "lists only generic types" after "lists no type";_pick_accreditationapplies ONE rule at every position: the accreditation's specific types must be a superset of the needed specific types (at the leaf, the credential's; above it, the delegated scope);verify_trust_chaincomputes the credential's specific types once, refuses a credential with none before any registry request, and sets the delegated scope to them (TrustChain.scopeno longer carries the generics; the leaf-is-anchor shortcut keeps returning without a request, with the specific types as scope).verify.py:evaluate_issuer_trustuses the same superset rule over specific types (so the two policies stay identical by construction); theIssuerNotTrustedmessage says when the credential names no specific type.schemaId/limitJurisdictionmatching (out of scope); the anchor's own record is still not read.maincompared whole lists there).Tests
tests/test_ebsi_trust_specific_types.py(every lane, both policies,verify_ebsi_badgein both trust modes andrequire_trust=False): the headline (five accreditedFor lists that share only generics with the credential), the generic-only / typeless credential (refused before any request, with a message that says why),[MyBadge, Extra]against[MyBadge], a record picking the accreditation that covers every type, five two-hop chains that cannot close on generics, the accreditor covering the whole delegated scope, the model helpers, the message for a generic-only accreditation. The headline, generic-only and one-of-two-types negatives are TRUSTED onmain(table above) and refused here; the[]credential, three two-hop cases and the["Unknown"]unit row were refused onmainalready (corrected after the review). Mutation check on a scratch copy: superset → intersection in_pick_accreditation(12 tests fail), the same inevaluate_issuer_trust(7), the generic-only accreditation rule removed (3),VerifiableAccreditationnot treated as generic (4).Two existing ES256K golden tests flip on purpose (
tests/test_es256k_ebsi_chain.py): they handed the recorded RootTAO's own…ToAttestaccreditation toverify_ebsi_badgeas the badge, and it was trusted only through the generic types (its specific typeVerifiableAccreditationToAttestis not what itsaccreditedForlists,VerifiableAuthorisationForTrustChain). They now pinIssuerNotTrusted/NoTrustedAnchor(andtrusted=Falseunderrequire_trust=False); the signature checks they also pin are unchanged. Said in the CHANGELOG migration note.Docs
CHANGELOG
### Securityunder[1.38.0] — unreleased(with the migration note:[VC, VA, A, B]now needs an accreditation listing bothAandB);wiki/Trust.md(a new bullet and the "delegation scoping" sentence);docs/audit/threat-model.mdrow I26 and the narrative that listed this as open;docs/audit/assurance.md§4 bullet#367; module docstrings.Review
Trust evaluation: an adversarial review runs before merge (posted below). Merge waits for the maintainer's OK.