SignCore.XML.Canonicalizer (sign_core v0.1.0)

Copy Markdown View Source

Thin wrapper around SignCore.XML.C14n.XmerlC14n — our vendored copy of xmerl_c14n.

The Phase 4 plan reserved the option to vendor in-tree if the upstream package went unmaintained. The Hex 0.2.0 release crashes on OTP 28 (unprefixed-attribute namespace field shape), so we vendor + patch in lib/pkcs11ex/xml/c14n/. A future pivot to a NIF-wrapped bergshamra is still a one-file replacement.

Scope of this module:

  • Canonicalise an :xmerl document or fragment into the byte sequence that <DigestValue> and <SignatureValue> are computed over.
  • v1 supports only Exclusive XML Canonicalization 1.0 (http://www.w3.org/2001/10/xml-exc-c14n#) — the C14N method XAdES B-B mandates and the SII DTE shape uses. Inclusive C14N and 1.1 are out of scope until a real conformance test argues for them.
  • Errors surface as {:error, {:c14n, reason}} so the verify pipeline can branch on the class atom (per api.md §4.1).

Summary

Types

Canonicalisation method atoms supported by this adapter.

Anything xmerl_c14n accepts: a parsed :xmerl element record ({:xmlElement, ...}) or document ({:xmlDocument, ...}). For binary input use parse/1 first.

Functions

Canonicalise the given :xmerl element under Exclusive XML C14N 1.0.

Canonicalises a sub-tree extracted from a host document where the parent's default namespace would be inherited but is not visibly used anywhere inside the sub-tree.

Parse an XML binary into an :xmerl element. Convenience wrapper around :xmerl_scan.string/2 that returns :ok | :error tuples rather than crashing on malformed input.

Types

method()

@type method() :: :exclusive_c14n_10

Canonicalisation method atoms supported by this adapter.

xmerl_node()

@type xmerl_node() :: tuple()

Anything xmerl_c14n accepts: a parsed :xmerl element record ({:xmlElement, ...}) or document ({:xmlDocument, ...}). For binary input use parse/1 first.

Functions

canonicalize(node, opts \\ [])

@spec canonicalize(
  xmerl_node(),
  keyword()
) :: {:ok, binary()} | {:error, term()}

Canonicalise the given :xmerl element under Exclusive XML C14N 1.0.

Options

  • :method — one of [:exclusive_c14n_10]. v1 rejects anything else with {:c14n, :unsupported_canonicalization}.
  • :inclusive_namespaces — list of namespace prefixes that should NOT be excluded from the canonical form even though they're not visibly used. Maps to the <InclusiveNamespaces PrefixList="..."> transform. The xmerl_c14n API takes this as a charlist list; we accept strings and convert.

canonicalize_subtree(node, opts \\ [])

@spec canonicalize_subtree(
  xmerl_node(),
  keyword()
) :: {:ok, binary()} | {:error, term()}

Canonicalises a sub-tree extracted from a host document where the parent's default namespace would be inherited but is not visibly used anywhere inside the sub-tree.

Why this is needed: exclusive C14N's W3C-spec behaviour drops inherited default namespaces unless an element name in the sub-tree is unprefixed (i.e., actually lives in the inherited default namespace). Our vendored xmerl_c14n over-emits the inherited default — it preserves it whenever it differs from the outer parent — so <ds:SignedInfo> extracted from a SII-DTE document picks up xmlns="http://www.sii.cl/SiiDte" even though none of its descendants use that namespace.

This helper clears the parsed element's :xmlNamespace.default field before delegating to canonicalize/2, producing the canonical bytes a correct exclusive-C14N implementation (xmlsec1, BouncyCastle) would emit.

Caveat: only safe when every element in the sub-tree uses an explicit namespace prefix (ds:, xades:, etc.). If any element name is unprefixed, the default namespace IS visibly used and must be preserved — use canonicalize/2 in that case.

Used by both SignCore.XML.sign/2 (for the <xades:SignedProperties> digest) and SignCore.XML.verify/2 (for re-extracting <ds:SignedInfo> and <xades:SignedProperties> from the host document).

parse(xml)

@spec parse(binary()) :: {:ok, xmerl_node()} | {:error, {:malformed_xml, term()}}

Parse an XML binary into an :xmerl element. Convenience wrapper around :xmerl_scan.string/2 that returns :ok | :error tuples rather than crashing on malformed input.

Returns the root :xmlElement. xmerl_c14n accepts either an element or a document wrapper, but later phases of the sign flow need the root element to splice <Signature> in as a child, so we standardise on returning the element here.