feat(seal): assert the mandated trusted-list signature profile - #317
Open
LKSNDRTMLKV wants to merge 8 commits into
Open
LKSNDRTMLKV wants to merge 8 commits into
LKSNDRTMLKV wants to merge 8 commits into
Conversation
LKSNDRTMLKV
force-pushed
the
feat/trusted-list-revived
branch
from
September 15, 2026 05:06
79d0082 to
7503b7e
Compare
LKSNDRTMLKV
force-pushed
the
feat/trusted-list-signature-profile
branch
from
September 15, 2026 05:06
f03d585 to
1078dd0
Compare
LKSNDRTMLKV
force-pushed
the
feat/trusted-list-revived
branch
from
September 15, 2026 06:25
7503b7e to
ad247db
Compare
LKSNDRTMLKV
force-pushed
the
feat/trusted-list-signature-profile
branch
from
September 15, 2026 06:30
1078dd0 to
3e03920
Compare
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.
Addresses asks 1–3 of #297. Stacked on #316 (which revives the trusted-list reader); retarget as those merge.
just checkis green (1125/1125).What it asserts
The
ds:ReferencewithURI=""must carry exactly oneds:Transforms, holding exactly twods:Transform— enveloped-signature then exclusive canonicalization, in that order. Anything else is rejected with a newLotlRejected::NonConformantProfile, and the same variant onTrustedListRejected.✅ COMPLIANCE-PIN: CID (EU) 2015/1505 Annex I, Chapter II, the "Signature element (clause B.1), General (clause B.1.0)" section inserted by CID (EU) 2025/2164, Annex, point (3). Applicable since 29 April 2026 under its Art. 2.
Checked before the signature, for the same reason the anchor is: a valid signature must not be able to excuse a transform chain the law does not permit, and handing an arbitrary chain to the canonicaliser is the thing the profile exists to prevent.
Ask 2 — what
xml-secalready enforcesEstablished rather than assumed, which was the point of that ask. It enforces nothing here by default:
TransformPolicy::allowed_algorithmsis documented as "Allowed transform and canonicalization URIs;Noneaccepts every implemented algorithm", and it defaults toNone.REC-xpath-19991116) and XPath Filter 2.0 — the two most expressive ways to change what a reference covers.max_transforms_per_referencedefaults to 64.So the assertion is known-necessary, not known-redundant.
The negative test makes that concrete rather than theoretical. The injected XPath transform is not merely permitted in principle — it is evaluated, running 5800 context evaluations before an unrelated resource ceiling stops it. A cheaper XPath would not have hit that ceiling at all.
Ask 3 — the negative test, and why it asserts the variant
Four cases: a third transform, the two in the wrong order, inclusive canonicalization in place of exclusive, and a
URIthat no longer covers the document.Each asserts
NonConformantProfilespecifically, and that matters. With the check removed, all four still fail — for four unrelated, incidental reasons: a dangling ID reference, a digest mismatch, a signature mismatch, and the XPath resource ceiling. A test asserting onlyis_err()would have passed against a verifier doing none of this.Documents are tampered in memory. These carry CRLF terminators, so a line-oriented edit rewrites every line and a rejection then proves nothing about the change intended.
The positive control is the existing
the_published_lotl_verifies: the real Commission document passes its own mandated profile, so these cases test the check rather than a fixture that never conformed.What is deliberately not constrained
Only the
URI=""reference. Reading the clause closely, it saysds:SignedInfoshall contain ads:ReferencewithURI=""and that that reference shall have the one-Transforms/two-Transformshape. It says nothing about other references.That is not a technicality. Both published documents — the LOTL and Finland's — carry a second reference to the XAdES
SignedPropertieswith its own single exclusive-c14n transform. Constraining every reference would refuse every real trusted list, including the Commission's own.The stronger fix, and why it is not here
Tightening
TransformPolicy::allowed_algorithmsto those two URIs would refuse a non-conformant chain during verification rather than beside it. It is deliberately not done: the acceptance bar for that is the full set of lists from the LOTL's own pointers, not the two fixtures in this repository, because one Member State using a different canonicalization for its XAdES reference would silently stop verifying. That is recorded in the code beside the check.Still open on #297
Part 1 — the trusted-list template now being based on ETSI TS 119 612 v2.4.1 rather than an unversioned reference. That needs v2.4.1 obtained and diffed against v2.3.1 for anything the parser reads, and the version recorded in the parser rather than only in an external register. I cannot close that half from here; the issue stays open for it.