-
Notifications
You must be signed in to change notification settings - Fork 0
Trust
A verified signature only proves someone signed — trust is deciding who may vouch. openvc keeps that decision caller-pinned: you hand it the anchors; it never ships an implicit root.
For tokens that carry their certificate chain, pass the roots you trust;
openvc validates the path and binds the leaf's SAN to the iss, so a
valid-but-unrelated certificate cannot vouch for an arbitrary issuer:
result = verify_credential(token, x5c_trust_anchors=my_trusted_roots)openvc.trustlist turns the European Commission's List of Trusted Lists
and the national Trusted Lists it points at (ETSI TS 119 612) into X.509
anchors for that same x5c path — closing the gap between "the chain is
internally valid" and "the chain roots in an EU-recognised anchor,
granted now, for the right service type":
from openvc import verify_credential
from openvc.trustlist import verify_xades_enveloped, walk_lotl # [trustlist] extra
anchors = walk_lotl(
LOTL_URL,
lotl_signer_certs=commission_certs, # caller-pinned: the Commission's keys
verify_signature=verify_xades_enveloped, # XAdES check on every list (fail-closed)
)
result = verify_credential(token, x5c_trust_anchors=anchors.certificates)Properties worth knowing:
- Trust is caller-pinned — you supply the LOTL signer certificates; there is no implicit root.
-
Fail-closed — a national TL that cannot be fetched, verified, or is
expired contributes zero anchors and is recorded in
anchors.problems, never silently trusted. -
Selective — the default selection keeps
grantedqualified-CA services (the ones that issue EUDI issuer certs); passselect=Nonefor everything, or aSelect(service_types=…)overServiceType. BeyondCA_QC, the named types cover the qualified trust services TLv6 national lists carry (EDS_Q,PSES_Q,QES_VALIDATION_Q,REMOTE_QSIGCD_MANAGEMENT_Q,REMOTE_QSEALCD_MANAGEMENT_Q,TSA_QTST, …).Selectmatches theServiceTypeIdentifierverbatim, so the EUDI-wallet trust services v2.4.1 adds (issuance of QEAA / EAA / PuB-EAA, qualified electronic ledgers) are selectable by their URI as national lists start carrying them. -
TLv6 — since 29 Apr 2026 the LOTL and every national TL are ETSI TS 119 612
v2.4.1 (TLv6) only. The parser reads
TSLVersionIdentifier(TrustList.version—6for TLv6) and tolerates the new optional elements (e.g.ServiceSupplyPoints); the[trustlist]XAdES verifier accepts the mandated XAdES-BASELINE-B signatures (document +SignedPropertiesreferences) and is pinned by golden fixtures recorded from the real Commission-signed EU LOTL and the Spanish national TL. -
Hardened XML — stdlib parsing with DTD/DOCTYPE rejected (no XXE, no
entity-expansion bombs), size-bounded input; XAdES verification lives behind
the
[trustlist]extra (signxml) and pins the signer to the certs the parent list vouched for.
Design rationale: ADR-0003.
openvc_ebsi resolves did:ebsi and reads the Trusted Issuers Registry,
then verifies the full accreditation chain — TI → TAO → RootTAO — recursively,
with per-hop delegation scoping and revocation of the accreditations
themselves:
from openvc.proof.vc_jwt import VcJwtProofSuite
from openvc_ebsi.http import for_ebsi
from openvc_ebsi.versioning import DidEbsiResolver
from openvc_ebsi.verify import verify_ebsi_badge
suite = VcJwtProofSuite()
with for_ebsi("pilot") as http:
resolver = DidEbsiResolver(http.get_json, decode_jwt=suite.peek_claims)
result = verify_ebsi_badge(token, resolver=resolver, proof_suite=suite,
expected_types=["VerifiableAttestation"])
print(result.trusted, result.issuer)- Read-only by design: resolve DIDs, read the registries. Onboarding / writing (JSON-RPC + OID4VP) is out of scope.
-
SSRF-guarded: the EBSI HTTP client is https-only with a host
allow-list, short client-side TTL caching, and bounded retries
(ADR-0001).
Never resolve
did:webthrough it —did:webhas its own guarded fetch (see Resolving issuer keys). -
Environment-aware:
for_ebsi("pilot" | "conformance" | "production")seeds the allow-list from the chosen EBSI environment —production(api.ebsi.eu) is registered for EBSI's Q4 2026 business launch. Passextra_hoststo also permit an issuer's status-list origin. The v5 Trusted Issuers Registry/attributeslisting is paginated, and openvc walks every page (bounded, and immune to EBSI's self-referentialnextcursor) so an issuer with many accreditations is read in full. -
Version drift, contained: every EBSI API version specific lives behind
one adapter in
openvc_ebsi.versioning; the trust logic never sees wire formats. Conformance is pinned by recorded pilot fixtures, plus an opt-in live smoke test (OPENVC_EBSI_LIVE=1 pytest).
📖 Published automatically from the wiki/ directory of the main repository — edits made directly on the wiki are overwritten by the next sync. To change a page, open a PR against wiki/. Every python block on these pages is executed by CI. · API reference · LGPL-3.0-or-later
Start
Proof formats
Verifying in practice
- Presentations & OpenID4VP
- Resolving issuer keys
- Relying-party certificates
- Credential schemas
- Status lists
- Trust anchors: EU TL & EBSI
- Async verification
Walkthroughs
Operating
Reference