XML-DSig + XAdES B-B format adapter.
Sign
SignCore.XML.sign(xml_bytes,
module: pkcs11_module,
slot_id: slot_id,
pin: "1234",
key_label: "platform-signing-key",
alg: :PS256,
x5c: [leaf_der, intermediate_der]
)Returns the original XML with an enveloped <ds:Signature>
element spliced in before the root's closing tag. The output is a
XAdES B-B signature with <xades:SigningCertificateV2>,
<xades:SigningTime>, and the canonical
Exclusive XML Canonicalization 1.0 C14N method on every
<ds:Reference> and the <ds:SignedInfo> element itself.
Pipeline:
- Parse the input XML (xmerl).
- Generate fresh
signature_idandsigned_properties_id. - Build the XAdES
<xades:QualifyingProperties>block viaSignCore.XML.XAdES. SHA-256 the leaf cert into<xades:CertDigest>; DER-encode the RFC 5035 IssuerSerial into<xades:IssuerSerialV2>. - Canonicalise
<xades:SignedProperties>(subtree only) with exc-c14n → SHA-256 → second<ds:Reference>digest. - Canonicalise the input document with exc-c14n → SHA-256 →
data
<ds:Reference>digest. The enveloped-signature transform is conceptually applied; at sign time there is no Signature in the document yet so the transform is a no-op, leaving the canonical form unchanged. - Build
<ds:SignedInfo>with both references. - Canonicalise
<ds:SignedInfo>with exc-c14n. Those bytes are the to-be-signed input. - Sign via
Pkcs11ex.sign_bytes/2(PKCS#11 → HSM). Software signing never enters the path. - Build the full
<ds:Signature>element and splice it into the source XML before the root's closing tag.
v1 limitations
- Enveloped signatures only. Detached and enveloping XML-DSig modes are post-v1.
:PS256and:RS256. PS256 emits thesha256-rsa-MGF1signature method URI per RFC 4051; receivers should use SHA-256 / MGF1-SHA-256 / sLen=32 to match the HSM-produced signature.- Single
<ds:Reference>URI is empty (whole-document). The:reference_uriopt is reserved for a future fragment-signing mode. verify/2lands in step 4b.1.6.
Architectural invariants
- x5c is untrusted input until verify runs the
configured
SignCore.Policy(allowlist gate). - Signature math always runs in the HSM via
Pkcs11ex.sign_bytes/2. Software signing is never invoked.
Summary
Types
Functions
@spec sign( binary() | iodata(), keyword() ) :: sign_result()
Sign an XML document with XML-DSig + XAdES B-B.
@spec verify( binary() | iodata(), keyword() ) :: verify_result()
Verify a XAdES B-B-signed XML document.
Returns {:ok, subject_id} where subject_id is whatever the
configured SignCore.Policy.validate/3 returned. The verify
pipeline runs in this order — every step is a checkpoint that
can refuse the signature with the documented error class:
- Parse the signed XML. Locate the (single)
<ds:Signature>element. v1 refuses multi-signature documents. - Extract the embedded
<ds:KeyInfo>x5c chain. - Allowlist gate (architectural invariant). Synthesise a
JOSE-style header and route it through the configured
SignCore.Policy—resolve/2thenvalidate/3. The chain is untrusted input until both succeed. No cryptographic check has happened yet. - Verify the XAdES
<xades:SigningCertificateV2>actually binds the leaf cert from<ds:KeyInfo>:SHA-256(leaf_der)must match<xades:CertDigest>, and<xades:IssuerSerialV2>must match the leaf's issuer + serial. - Recompute the data
<ds:Reference>digest: apply the enveloped-signature transform (remove<Signature>from the doc), exc-c14n the result, SHA-256. - Recompute the SignedProperties
<ds:Reference>digest: canonicalise the<xades:SignedProperties>subtree, SHA-256. - Canonicalise
<ds:SignedInfo>(subtree extraction with inherited-default-namespace clear) and verify the math against the leaf's SPKI.
Failures from step 3 short-circuit before any signature math, so
callers cannot use verify/2 as a CPU-bound oracle on
attacker-supplied certificates.