Skip to content

feat(seal): assert the mandated trusted-list signature profile - #317

Open
LKSNDRTMLKV wants to merge 8 commits into
mainfrom
feat/trusted-list-signature-profile
Open

LKSNDRTMLKV wants to merge 8 commits into
mainfrom
feat/trusted-list-signature-profile

Conversation

@LKSNDRTMLKV

Copy link
Copy Markdown
Member

Addresses asks 1–3 of #297. Stacked on #316 (which revives the trusted-list reader); retarget as those merge.

just check is green (1125/1125).

What it asserts

The ds:Reference with URI="" must carry exactly one ds:Transforms, holding exactly two ds:Transform — enveloped-signature then exclusive canonicalization, in that order. Anything else is rejected with a new LotlRejected::NonConformantProfile, and the same variant on TrustedListRejected.

✅ 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-sec already enforces

Established rather than assumed, which was the point of that ask. It enforces nothing here by default:

  • TransformPolicy::allowed_algorithms is documented as "Allowed transform and canonicalization URIs; None accepts every implemented algorithm", and it defaults to None.
  • The implemented set includes XPath (REC-xpath-19991116) and XPath Filter 2.0 — the two most expressive ways to change what a reference covers.
  • max_transforms_per_reference defaults 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 URI that no longer covers the document.

Each asserts NonConformantProfile specifically, 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 only is_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 says ds:SignedInfo shall contain a ds:Reference with URI="" and that that reference shall have the one-Transforms/two-Transform shape. 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 SignedProperties with 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_algorithms to 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.

@LKSNDRTMLKV
LKSNDRTMLKV force-pushed the feat/trusted-list-revived branch from 7503b7e to ad247db Compare September 15, 2026 06:25
@LKSNDRTMLKV
LKSNDRTMLKV force-pushed the feat/trusted-list-signature-profile branch from 1078dd0 to 3e03920 Compare September 15, 2026 06:30
@LKSNDRTMLKV
LKSNDRTMLKV changed the base branch from feat/trusted-list-revived to main September 15, 2026 10:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review-ready Opt this PR into a CodeRabbit review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant