Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,55 @@ All notable changes to **openvc** are documented here. The format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project aims for
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.22.0] — unreleased

### Added

- **ETSI TS 119 602 Lists of Trusted Entities (LoTE) — the JSON trusted-list
lane** ([#135](https://github.com/luisgf/openvc/issues/135)). TS 119 602 is
the successor data model to the TS 119 612 XML Trusted Lists, and its EU
profiles are the EUDI wallet anchor lists: **Annex F** (WRPAC providers —
who may issue relying-party *access* certificates) and **Annex G** (WRPRC
providers — the registrar anchors `verify_rp_registration_certificate`
consumes). One interface, two encodings: `openvc.trustlist` grows
`parse_lote` / `consume_lote` / `walk_lote` plus the
`EU_WRPAC_PROVIDERS_PROFILE` / `EU_WRPRC_PROVIDERS_PROFILE` conformance
gates (`LoteProfile`, `LoteType`, `LoteServiceType`,
`TrustListProfileError`), all distilling into the same `TrustAnchorSet`
as `walk_lotl` — `.certificates` feeds the existing X.509 path unchanged.

A JSON LoTE travels as a **compact JAdES baseline-B** JWS (clause 6.8 /
Annex G.4), so verification runs on the library's own JOSE primitives — the
`{ES256, ES384, EdDSA, Ed25519}` allow-list before any crypto, the WRPRC
lane's allow-listed `crit`, the signer from `x5c` authenticated against
**caller-pinned** certificates (byte-for-byte or by path validation), plus
clause 6.8's DN binding (signing-certificate `organizationName` ↔
`SchemeOperatorName`, `countryName` ↔ `SchemeTerritory`). Parsing is strict
and fail-closed on every field that feeds a trust decision: unknown
structural members reject (the official schema is
`additionalProperties: false` throughout), date-times must be the UTC `Z`
form, an unrecognised critical extension rejects the list, a malformed
certificate blob is skipped rather than trusted, and a **closed** list
(`NextUpdate` null) contributes zero anchors. The EU profiles enforce
Tables F.1–G.3 — registered URIs (including the spec's literal
`WRPRCrovidersList` StatusDetn typo, accepted alongside the corrected
spelling), territory `EU`, the exclusive service-type pairs,
`ServiceStatus`/`StatusStartingTime`/`HistoricalInformationPeriod` absent,
and the ≤ 6-month update window. Self-made signed vectors pin the behaviour;
the Commission's real EU lists become golden fixtures when published.

The adversarial review hardened the lane before merge: a profiled
`walk_lote` now defaults its selection to the profile's **issuance** service
type (a provider's *revocation*-service certificates no longer anchor
credential verification unless explicitly selected — the review proved a
WRPRC signed under a revocation-service CA validated through the documented
flow), only follows pointers whose `LoTEType` matches the profile (and
consumes the pointed list under the same profile), fails closed instead of
raising an uncaught `ValueError` on a far-future `ListIssueDateTime`,
rejects a present-but-empty `ServiceStatus` under the profiles
(presence is the violation), and pins date-times to the exact
`YYYY-MM-DDThh:mm:ssZ` form clause 6.1.3 mandates.

## [1.21.0] — 2026-07-19

### Added
Expand Down
49 changes: 39 additions & 10 deletions src/openvc/trustlist/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
"""
openvc.trustlist — consume EU Trusted Lists (LOTL → national TL) as a verifier
X.509 trust-anchor source (eIDAS 2.0 / EUDI, ETSI TS 119 612).
openvc.trustlist — consume EU trusted lists as a verifier X.509 trust-anchor
source (eIDAS 2.0 / EUDI): **one interface, two encodings**.

A Trusted List is, for a verifier, a **source of EU-recognised X.509 anchors**.
:func:`walk_lotl` turns the Commission's List of Trusted Lists and the national
lists it points at into a :class:`TrustAnchorSet`; its ``.certificates`` feed the
existing X.509 path directly:
* **ETSI TS 119 612** XML Trusted Lists (LOTL → national TL): :func:`walk_lotl`.
* **ETSI TS 119 602** JSON Lists of Trusted Entities (LoTE), the successor data
model whose EU profiles carry the EUDI wallet anchor lists — Annex F (WRPAC
providers) and Annex G (WRPRC providers): :func:`walk_lote`.

Both distil into the same :class:`TrustAnchorSet`; its ``.certificates`` feed
the existing X.509 path directly:

from openvc import verify_credential
from openvc.trustlist import walk_lotl
Expand All @@ -19,10 +22,12 @@
verify_credential(vc, x5c_trust_anchors=anchors.certificates)

This adds no verification surface — :mod:`openvc.x5c` remains the path validator;
trust lists only tell it *which roots are EU-recognised*. Parsing is hardened
stdlib XML (no DTD/XXE, bounded); XML-signature verification is an **injected
callback** (the ``[trustlist]`` extra ships a reference XAdES one). See
``docs/adr/ADR-0003-eu-trusted-lists.md``.
trust lists only tell it *which roots are EU-recognised*. XML parsing is hardened
stdlib (no DTD/XXE, bounded) with XML-signature verification an **injected
callback** (the ``[trustlist]`` extra ships a reference XAdES one); the LoTE lane
is signed as compact JAdES and verifies on the library's own JOSE primitives, no
extra needed. See ``docs/adr/ADR-0003-eu-trusted-lists.md`` and
:mod:`openvc.trustlist.lote`.
"""
from __future__ import annotations

Expand All @@ -40,10 +45,23 @@
from .errors import (
TrustListError,
TrustListParseError,
TrustListProfileError,
TrustListSignatureBackendUnavailable,
TrustListSignatureError,
TrustListSignatureUnavailable,
)
from .lote import (
EU_WRPAC_PROVIDERS_PROFILE,
EU_WRPRC_PROVIDERS_PROFILE,
FetchLote,
LoteProfile,
LoteServiceType,
LoteType,
consume_lote,
default_lote_fetch,
parse_lote,
walk_lote,
)
from .model import (
TrustAnchorSet,
TrustList,
Expand All @@ -57,7 +75,13 @@

__all__ = [
"DEFAULT_SELECT",
"EU_WRPAC_PROVIDERS_PROFILE",
"EU_WRPRC_PROVIDERS_PROFILE",
"FetchLote",
"FetchTrustList",
"LoteProfile",
"LoteServiceType",
"LoteType",
"Select",
"ServiceStatus",
"ServiceType",
Expand All @@ -66,16 +90,21 @@
"TrustListError",
"TrustListParseError",
"TrustListProblem",
"TrustListProfileError",
"TrustListSignatureBackendUnavailable",
"TrustListSignatureError",
"TrustListSignatureUnavailable",
"TrustServiceAnchor",
"TrustServiceProvider",
"TslPointer",
"VerifySignature",
"consume_lote",
"consume_trust_list",
"default_lote_fetch",
"default_trust_list_fetch",
"parse_lote",
"parse_trust_list",
"verify_xades_enveloped",
"walk_lote",
"walk_lotl",
]
14 changes: 12 additions & 2 deletions src/openvc/trustlist/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,17 @@ class TrustListSignatureUnavailable(TrustListError):


class TrustListSignatureError(TrustListError):
"""The Trusted List's XML signature did not verify against the expected signer
certificate(s) — the list is not authentic."""
"""The Trusted List's signature (XML XAdES, or a LoTE's compact JAdES) did
not verify against the expected signer certificate(s) — the list is not
authentic."""


class TrustListProfileError(TrustListError):
"""A verified, well-formed LoTE does not conform to the requested profile
(ETSI TS 119 602 clause 4.7 — e.g. the Annex F/G EU WRPAC/WRPRC providers
lists): wrong ``LoTEType``, a forbidden component present, a service type
outside the profile's exclusive set, or an update window over the ceiling.
Fail-closed: a non-conformant list contributes no anchors."""


class TrustListSignatureBackendUnavailable(TrustListSignatureUnavailable):
Expand All @@ -36,6 +45,7 @@ class TrustListSignatureBackendUnavailable(TrustListSignatureUnavailable):
__all__ = [
"TrustListError",
"TrustListParseError",
"TrustListProfileError",
"TrustListSignatureBackendUnavailable",
"TrustListSignatureError",
"TrustListSignatureUnavailable",
Expand Down
Loading
Loading