crates/dpp-seal/src/trustlist/anchor.rs:59-60 correctly cites "Implementing Decision (EU) 2015/1505 as amended by Commission Implementing Decision (EU) 2025/2164". 2025/2164 had not been read. Reading it turns up two things that bear directly on the trusted-list verifier.
It applies from 29 April 2026 (its Art. 2) — live for over four months.
1. The trusted-list template is now based on ETSI TS 119 612 v2.4.1
CID (EU) 2025/2164, Annex, point (1), verbatim — amending Annex I to CID 2015/1505:
"In Chapter II, the first paragraph is replaced by the following: 'The present specifications leverage the specifications and requirements set in ETSI TS 119 612 v2.4.1 (hereinafter referred to as ETSI TS 119 612).'"
crates/dpp-seal/src/trustlist/parse.rs is written against TS 119 612 without a version, and the version on hand is V2.3.1 (2024-11).
⚠️ This is not automatically a defect. A v2.3.1 → v2.4.1 bump in a trusted-list schema is more likely additive than breaking, and the parser matches on local names — which its own comment argues is "what TS 119 612 actually constrains" and is the choice most likely to survive a minor revision.
But we cannot currently say that, because v2.4.1 is not on hand and has not been diffed against v2.3.1. The honest position is unknown, and the current one reads as verified.
Ask: obtain TS 119 612 v2.4.1 (free from ETSI, like the rest of the set), diff it against v2.3.1 for anything the parser reads, and record the version the parser targets in the parser, not only in an external register. An unversioned "TS 119 612" in a doc comment cannot go stale visibly.
2. 🚨 The Signature element is now mandatory, with a pinned transform profile — and we do not assert it
Annex, point (3), verbatim — a new section added to Chapter II after "Service current status (clause 5.5.4)":
"The Signature element (clause B.1), General (clause B.1.0) This clause shall be mandatory and shall comply with the specifications from TS 119 612 clause B.1.0, where point (2) is replaced by the following:
'2) Its ds:SignedInfo element shall contain a ds:Reference element with the URI attribute set to an empty string (i.e. URI=""), so as to refer to the entire document. This ds:Reference element shall satisfy the following requirements:
(a) It shall contain only one ds:Transforms element;
(b) This ds:Transforms element shall contain two ds:Transform elements. The first one will be one whose Algorithm attribute indicates the enveloped transformation with the value: http://www.w3.org/2000/09/xmldsig#enveloped-signature. The second one will be one whose Algorithm attribute instructs to perform the exclusive canonicalization http://www.w3.org/2001/10/xml-exc-c14n#.'"
That is a closed profile: one reference over the whole document, exactly one ds:Transforms, exactly two ds:Transform, both algorithms named.
What verify.rs does (verify_lotl_with, and the same shape in verify_trusted_list):
let outcome = VerifyContext::new()
…
.verify(xml)
…
if !matches!(outcome.status, DsigStatus::Valid) { … }
It delegates to a general XML-DSig verifier and accepts any signature that validates. Nothing checks the reference URI, the number of ds:Transforms, or the two algorithms.
Why this is worth more than a conformance tick
A general XML-DSig verifier accepting arbitrary transforms is the classic signature-wrapping surface: transforms decide what was actually signed, and a permissive transform chain lets a valid signature cover something other than the document you are reading. The narrowness of the profile above is the point of it.
⚠️ I am not asserting an exploitable bug. Whether xml_sec itself constrains the transform set is unknown to me and should be checked before anything stronger is said. But the act names a mandatory profile, our verifier does not assert it, and that gap is ours either way — a library's defaults are not a compliance argument.
Ask
- Assert the profile explicitly after (or during) verification: one
ds:Reference with URI="", exactly one ds:Transforms, exactly two ds:Transform with those two Algorithm values. Reject otherwise, with its own LotlRejected variant — the existing enum's reasoning ("a log line has to make that obvious", "three different people") applies directly.
- Establish what
xml_sec already enforces, and record it, so the assertion is known-redundant or known-necessary rather than assumed.
- A negative test: a trusted list whose signature validates cryptographically but carries a non-conformant transform chain must be rejected. A gate that has never been seen to fail is not known to work.
3. What did not change
Annex point (2) rewrites "Scheme type/community/rules (clause 5.3.9)" — it makes the field mandatory, requires UK English URIs, and pins the common http://uri.etsi.org/TrstSvc/TrustedList/schemerules/EUcommon URI plus its descriptive text. It is scheme-level prose; nothing in the pointer-filtering path reads it.
The pointer-selection logic — filtering on TSLType and MimeType, both of which sit inside AdditionalInformation — is untouched by this amendment.
crates/dpp-seal/src/trustlist/anchor.rs:59-60correctly cites "Implementing Decision (EU) 2015/1505 as amended by Commission Implementing Decision (EU) 2025/2164". 2025/2164 had not been read. Reading it turns up two things that bear directly on the trusted-list verifier.It applies from 29 April 2026 (its Art. 2) — live for over four months.
1. The trusted-list template is now based on ETSI TS 119 612 v2.4.1
CID (EU) 2025/2164, Annex, point (1), verbatim — amending Annex I to CID 2015/1505:
crates/dpp-seal/src/trustlist/parse.rsis written against TS 119 612 without a version, and the version on hand is V2.3.1 (2024-11).But we cannot currently say that, because v2.4.1 is not on hand and has not been diffed against v2.3.1. The honest position is unknown, and the current one reads as verified.
Ask: obtain TS 119 612 v2.4.1 (free from ETSI, like the rest of the set), diff it against v2.3.1 for anything the parser reads, and record the version the parser targets in the parser, not only in an external register. An unversioned "TS 119 612" in a doc comment cannot go stale visibly.
2. 🚨 The Signature element is now mandatory, with a pinned transform profile — and we do not assert it
Annex, point (3), verbatim — a new section added to Chapter II after "Service current status (clause 5.5.4)":
That is a closed profile: one reference over the whole document, exactly one
ds:Transforms, exactly twods:Transform, both algorithms named.What
verify.rsdoes (verify_lotl_with, and the same shape inverify_trusted_list):It delegates to a general XML-DSig verifier and accepts any signature that validates. Nothing checks the reference URI, the number of
ds:Transforms, or the two algorithms.Why this is worth more than a conformance tick
A general XML-DSig verifier accepting arbitrary transforms is the classic signature-wrapping surface: transforms decide what was actually signed, and a permissive transform chain lets a valid signature cover something other than the document you are reading. The narrowness of the profile above is the point of it.
xml_secitself constrains the transform set is unknown to me and should be checked before anything stronger is said. But the act names a mandatory profile, our verifier does not assert it, and that gap is ours either way — a library's defaults are not a compliance argument.Ask
ds:ReferencewithURI="", exactly oneds:Transforms, exactly twods:Transformwith those two Algorithm values. Reject otherwise, with its ownLotlRejectedvariant — the existing enum's reasoning ("a log line has to make that obvious", "three different people") applies directly.xml_secalready enforces, and record it, so the assertion is known-redundant or known-necessary rather than assumed.3. What did not change
Annex point (2) rewrites "Scheme type/community/rules (clause 5.3.9)" — it makes the field mandatory, requires UK English URIs, and pins the common
http://uri.etsi.org/TrstSvc/TrustedList/schemerules/EUcommonURI plus its descriptive text. It is scheme-level prose; nothing in the pointer-filtering path reads it.The pointer-selection logic — filtering on
TSLTypeandMimeType, both of which sit insideAdditionalInformation— is untouched by this amendment.