xmldsig

package
v0.0.0-...-7f5ad21 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 22, 2026 License: LGPL-2.1 Imports: 20 Imported by: 0

Documentation

Overview

Ported from org.apache.xml.security.signature.XMLSignatureInput (Apache Santuario xmlsec 3.0.6).

Package xmldsig implements the XML-DSig reference-processing core that upstream DSS obtains from Apache Santuario (xmlsec 3.0.6): URI resolution, the transform pipeline, canonicalization of the result, digest computation, ds:Manifest validation, and verification of ds:SignatureValue over the canonicalized ds:SignedInfo.

Provenance

Per PORTING.md, machinery that upstream gets from a third party has no Java class to mirror one-to-one and therefore lives under internal/. This package replaces exactly the part of org.apache.xml.security that dss-xades imports - the 25 import sites under dss-xades/src/main, enumerated in the table below - and nothing else. It is layered on internal/xmldom, internal/xmlc14n and internal/xpath10, and on model/enumerations for the DSSDocument and DigestAlgorithm types that its detached-content resolver and its digests are expressed in.

The Go names below are the entry points; the Java column is the Santuario member each one replaces, and every exported identifier repeats its own mapping in its doc comment.

Go                                    Apache Santuario 3.0.6
------------------------------------------------------------------------------------
Data                                  signature.XMLSignatureInput
Data.Bytes                            XMLSignatureInput#getBytes
NodeFilter                            signature.NodeFilter (re-exported from xmlc14n)
Transform                             transforms.TransformSpi
Registry / DefaultRegistry            transforms.Transform's algorithm registry
Registry.Perform                      transforms.Transform#performTransform
PerformTransforms                     transforms.Transforms#performTransforms
URIResolver                           utils.resolver.ResourceResolverSpi
ResolverContext                       utils.resolver.ResourceResolverContext
Resolve                               utils.resolver.ResourceResolver#resolve
ResolverFragment                      utils.resolver.implementations.ResolverFragment
EnforcedResolverFragment              dss-xades EnforcedResolverFragment
ResolverXPointer                      utils.resolver.implementations.ResolverXPointer
DetachedSignatureResolver             dss-xades DetachedSignatureResolver
Reference                             signature.Reference
Reference.ContentsBeforeTransformation  Reference#getContentsBeforeTransformation
Reference.ContentsAfterTransformation   Reference#getContentsAfterTransformation
Reference.ReferencedBytes             Reference#getReferencedBytes
Reference.CalculateDigest             Reference#calculateDigest
Reference.Verify                      Reference#verify
Manifest                              signature.Manifest
Manifest.VerifyReferences             Manifest#verifyReferences
SignedInfo                            signature.SignedInfo
SignedInfo.CanonicalizedOctets        SignedInfo#getCanonicalizedOctetStream
XMLSignature                          signature.XMLSignature
XMLSignature.CheckSignatureValue      XMLSignature#checkSignatureValue
IsDescendantOrSelf, NodeSetOf         utils.XMLUtils#isDescendantOrSelf, #getSet

Two deliberate structural deviations

No streaming. Santuario threads an OutputStream through the last transform of a chain so that the digest can be computed without materializing the transform output (Reference#calculateDigest passes a DigesterOutputStream; Transforms#performTransforms hands it to the last Transform only). Every transform that accepts the stream writes to it exactly the octets it would otherwise have returned - TransformC14N and TransformBase64Decode are the only two that look at it, and both branches are the same bytes - and a chain whose last transform ignores it ends in XMLSignatureInput#updateOutputStream, which canonicalizes with Canonicalizer20010315OmitComments, the same canonicalizer getBytes uses. So the streamed and the buffered paths are byte-identical, and this package implements only the buffered one - which is also the path DSS itself reads through Reference#getReferencedBytes and getContentsAfterTransformation().getBytes(). Reference.CalculateDigest digests ReferencedBytes.

Signature verification is not routed through spi.ContentVerifier. XML-DSig encodes an ECDSA signature as the raw IEEE P1363 pair r||s, not as the DER SEQUENCE crypto/x509 expects (Santuario converts in SignatureECDSA), and XMLSignature#checkSignatureValue must distinguish "the signature does not verify" (false) from "this algorithm or key cannot be used" (an exception), which spi.ContentVerifier collapses into one error. verifySignature therefore mirrors Santuario's SignatureBaseRSA/SignatureECDSA/SignatureEDDSA engineVerify directly on crypto/rsa, crypto/ecdsa and crypto/ed25519.

What is out of scope, and where it went instead

Building a signature (Santuario's Transform/Transforms/Reference construction side) belongs to the XAdES port: this package reads a signature that exists. dss-xades's CounterSignatureResolver is likewise not here - it serializes a node with DomUtils.serializeNode and queries it with XPathUtils, both of which live in DSS packages that internal/ may not import - but URIResolver is an interface precisely so the XAdES port can register it. The XSLT transform is refused, exactly as Santuario refuses it under secure validation and as DSS never enables it.

Ported from org.apache.xml.security.signature.Manifest and VerifiedReference (Apache Santuario xmlsec 3.0.6).

Ported from org.apache.xml.security.signature.Reference (Apache Santuario xmlsec 3.0.6).

Ported from org.apache.xml.security.utils.resolver.ResourceResolver, ResourceResolverSpi, ResourceResolverContext and the two same-document implementations ResolverFragment and ResolverXPointer (Apache Santuario xmlsec 3.0.6), plus eu.europa.esig.dss.xades.EnforcedResolverFragment (DSS 6.5.RC1).

Ported from eu.europa.esig.dss.xades.validation.DetachedSignatureResolver, DSSDocumentXMLSignatureInput and DigestDocumentXMLSignatureInput (DSS 6.5.RC1).

Ported from org.apache.xml.security.algorithms.SignatureAlgorithm and its implementations SignatureBaseRSA, SignatureBaseRSAPSS, SignatureECDSA, SignatureDSA and SignatureEDDSA (Apache Santuario xmlsec 3.0.6), plus the JCEMapper entries DSS installs.

Ported from org.apache.xml.security.signature.XMLSignature (Apache Santuario xmlsec 3.0.6), as eu.europa.esig.dss.xades.validation.XAdESSignature and XAdESSignatureIntegrityValidator drive it.

Ported from org.apache.xml.security.signature.SignedInfo (Apache Santuario xmlsec 3.0.6).

Ported from org.apache.xml.security.transforms.Transform, TransformSpi and Transforms (Apache Santuario xmlsec 3.0.6), together with the algorithm registry that org.apache.xml.security.Init populates and DSSXMLUtils re-declares.

Ported from org.apache.xml.security.transforms.implementations.TransformBase64Decode (Apache Santuario xmlsec 3.0.6).

Ported from org.apache.xml.security.transforms.implementations.TransformC14N, TransformC14NWithComments, TransformC14N11, TransformC14N11_WithComments, TransformC14NExclusive and TransformC14NExclusiveWithComments (Apache Santuario xmlsec 3.0.6).

Ported from org.apache.xml.security.transforms.implementations.TransformEnvelopedSignature and its EnvelopedNodeFilter (Apache Santuario xmlsec 3.0.6).

Ported from org.apache.xml.security.transforms.implementations.TransformXPath and its XPathNodeFilter (Apache Santuario xmlsec 3.0.6).

Ported from org.apache.xml.security.transforms.implementations.TransformXPath2Filter and its XPath2NodeFilter, plus org.apache.xml.security.transforms.params.XPath2FilterContainer (Apache Santuario xmlsec 3.0.6).

Ported from org.apache.xml.security.utils.XMLUtils and org.apache.xml.security.utils.Constants (Apache Santuario xmlsec 3.0.6): the handful of DOM helpers the reference pipeline needs.

Index

Constants

View Source
const (
	MaximumTransformCount = 5
	MaximumReferenceCount = 30
)

MaximumTransformCount is Reference.MAXIMUM_TRANSFORM_COUNT and MaximumReferenceCount is Manifest.MAXIMUM_REFERENCE_COUNT, the two secure-validation caps. Upstream lets a system property (org.apache.xml.security.maxReferences) raise the second one; that knob is not ported, because a global mutable limit is exactly the sort of ambient state this port avoids - a caller who needs a different cap can count the references itself.

DSS validates with secure validation OFF, so neither cap is enforced by default; they are exported so a caller that wants upstream's hardened behaviour can ask for it through ManifestOptions.

View Source
const (
	ReferenceTypeObject   = NamespaceDSig + "Object"
	ReferenceTypeManifest = NamespaceDSig + "Manifest"
)

Reference type URIs. Port of Reference.OBJECT_URI and Reference.MANIFEST_URI.

View Source
const (
	TransformC14N                 = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
	TransformC14NWithComments     = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"
	TransformC14N11               = "http://www.w3.org/2006/12/xml-c14n11"
	TransformC14N11WithComments   = "http://www.w3.org/2006/12/xml-c14n11#WithComments"
	TransformC14NExcl             = "http://www.w3.org/2001/10/xml-exc-c14n#"
	TransformC14NExclWithComments = "http://www.w3.org/2001/10/xml-exc-c14n#WithComments"
	TransformBase64Decode         = NamespaceDSig + "base64"
	TransformEnvelopedSignature   = NamespaceDSig + "enveloped-signature"
	TransformXPath                = "http://www.w3.org/TR/1999/REC-xpath-19991116"
	TransformXPath2Filter         = NamespaceXPathFilter2
	TransformXSLT                 = "http://www.w3.org/TR/1999/REC-xslt-19991116"

	// TransformXPointer is declared because DSSXMLUtils.registerDefaultTransforms names it, but
	// there is no implementation to register: Santuario has an XPointer RESOLVER
	// (ResolverXPointer, which is what a "#xpointer(...)" reference URI goes through) and no
	// XPointer TransformSpi at all, so a ds:Transform naming this URI is an unknown transform
	// in upstream exactly as it is here.
	TransformXPointer = "http://www.w3.org/TR/2001/WD-xptr-20010108"
)

Transform algorithm URIs. Port of the TRANSFORM_* constants of org.apache.xml.security.transforms.Transforms; DSSXMLUtils.registerDefaultTransforms names the same set, minus the canonicalization methods it takes from XMLCanonicalizer.

View Source
const (
	// NamespaceDSig is Constants.SignatureSpecNS.
	NamespaceDSig = "http://www.w3.org/2000/09/xmldsig#"
	// NamespaceExcC14N is InclusiveNamespaces.ExclusiveCanonicalizationNamespace.
	NamespaceExcC14N = "http://www.w3.org/2001/10/xml-exc-c14n#"
	// NamespaceXPathFilter2 is XPath2FilterContainer.XPathFilter2NS.
	NamespaceXPathFilter2 = "http://www.w3.org/2002/06/xmldsig-filter2"
)

Namespace URIs. Port of Constants.SignatureSpecNS and the XPath Filter 2.0 namespace of transforms.params.XPath2FilterContainer.

Variables

View Source
var ErrDetachedDocumentNotFound = errors.New("xmldsig: unable to find the detached document")

ErrDetachedDocumentNotFound is the ResourceResolverException DetachedSignatureResolver raises when no candidate matches ("Unable to find document '%s' (detached signature)").

View Source
var ErrForbiddenTransform = errors.New("xmldsig: forbidden transform algorithm")

ErrForbiddenTransform is Santuario's TransformationException("signature.Transform.ForbiddenTransform"), raised by Transforms#checkSecureValidation for the XSLT transform.

View Source
var ErrKeyMismatch = errors.New("xmldsig: the public key does not match the signature algorithm")

ErrKeyMismatch reports a public key whose type does not match the signature method - an RSA key under an ECDSA method, say. Santuario surfaces it as an InvalidKeyException wrapped in XMLSignatureException, i.e. as a failure, never as "invalid signature".

View Source
var ErrMalformedSignatureValue = errors.New("xmldsig: malformed ds:SignatureValue")

ErrMalformedSignatureValue reports a ds:SignatureValue that cannot be a signature under the key at all - the wrong length for the RSA modulus. The JCE throws SignatureException for it rather than answering false, and so must this: "this is not a signature" and "this signature is forged" are different findings, and a validator that reports the second for the first has silently decided the document was signed and merely tampered with.

View Source
var ErrMissingID = errors.New("xmldsig: no element carries the referenced Id")

ErrMissingID is ResourceResolverException("signature.Verification.MissingID").

View Source
var ErrMultipleIDs = errors.New("xmldsig: more than one element carries the referenced Id")

ErrMultipleIDs is ResourceResolverException("signature.Verification.MultipleIDs"), the signature-wrapping guard.

View Source
var ErrNoReferences = errors.New("xmldsig: the manifest contains no ds:Reference")

ErrNoReferences is Santuario's XMLSecurityException("empty", "References are empty").

View Source
var ErrNoResolver = errors.New("xmldsig: no resolver can dereference the reference URI")

ErrNoResolver is Santuario's ResourceResolverException("utils.resolver.noClass").

View Source
var ErrNotAManifest = errors.New("xmldsig: not a ds:Manifest or ds:SignedInfo element")

ErrNotAManifest reports an element that is neither ds:Manifest nor ds:SignedInfo.

View Source
var ErrNotAReference = errors.New("xmldsig: not a ds:Reference element")

ErrNotAReference reports an element that is not a ds:Reference.

View Source
var ErrNotASignature = errors.New("xmldsig: not a ds:Signature element")

ErrNotASignature reports an element that is not a ds:Signature.

View Source
var ErrNotASignedInfo = errors.New("xmldsig: not a ds:SignedInfo element")

ErrNotASignedInfo reports an element that is not a ds:SignedInfo.

View Source
var ErrUninitializedData = errors.New("xmldsig: XMLSignatureInput is in no usable state")

ErrUninitializedData is Santuario's "getNodeSet() called but no input data present" and the TransformationException("Unrecognized XMLSignatureInput state") its transforms raise.

View Source
var ErrUnknownDigestAlgorithm = errors.New("xmldsig: unknown digest algorithm")

ErrUnknownDigestAlgorithm reports a ds:DigestMethod naming an algorithm this build cannot compute. Santuario raises XMLSignatureException("signature.signatureAlgorithm").

View Source
var ErrUnknownTransform = errors.New("xmldsig: unknown transform algorithm")

ErrUnknownTransform is Santuario's InvalidTransformException("signature.Transform.UnknownTransform").

View Source
var ErrUnsupportedSignatureAlgorithm = errors.New("xmldsig: unsupported signature algorithm")

ErrUnsupportedSignatureAlgorithm reports a ds:SignatureMethod this build cannot verify. It is the counterpart of Santuario's XMLSignatureException("algorithms.NoSuchAlgorithm"), and it is deliberately distinct from "the signature does not verify".

Functions

func IsDescendantOrSelf

func IsDescendantOrSelf(ctx, descendantOrSelf *xmldom.Node) bool

IsDescendantOrSelf reports whether descendantOrSelf is ctx or lies inside it. Port of XMLUtils#isDescendantOrSelf, including its one subtlety: an attribute's parent is the element that owns it, so an attribute of a ds:Signature counts as inside that signature even though it is not among its children.

func NodeSetOf

func NodeSetOf(root, exclude *xmldom.Node, comments bool) []*xmldom.Node

NodeSetOf collects the nodes of a subtree, in document order, skipping exclude and its subtree. Port of XMLUtils#getSet/getSetRec: an element contributes itself and all of its attributes (namespace declarations included, which is why the exclusive canonicalizer can ask whether a declaration is in the set), a comment contributes itself only when comments are kept, and a DOCTYPE contributes nothing.

Types

type Data

type Data struct {
	// contains filtered or unexported fields
}

Data is one value flowing through the reference-processing pipeline: the result of dereferencing a ds:Reference URI, and then the result of each ds:Transform in turn. It is the port of XMLSignatureInput.

Santuario's class is a union with an unusually load-bearing discriminator, because the canonicalizer that ends a transform chain dispatches on it (CanonicalizerBase#engineCanonicalize tests isOctetStream, then isElement, then isNodeSet, in that order) and the three branches canonicalize differently. The fields are kept private and the predicates exported so that the discriminator can only ever be in a state Santuario can produce:

state        built by                                    canonicalized as
-----------------------------------------------------------------------------------------
octets       NewOctetData; base64 and c14n transforms    parse, then whole document
element      NewNodeData; ResolverFragment/XPointer      subtree rooted at Node, minus Exclude
node set     SetNodeSet(true); NewNodeSetData            document subset under the filters

Exclude and Filters are what the enveloped-signature transform leaves behind (XMLSignatureInput#setExcludeNode plus a NodeFilter), and Filters is also where the ds:XPath and XPath Filter 2.0 transforms put themselves. ExcludeComments is XMLDSIG 4.4.3.3 step 4, set by the same-document resolvers.

func NewNodeData

func NewNodeData(n *xmldom.Node) *Data

NewNodeData wraps a document or element node: the subtree rooted at it, all descendants included. Port of XMLSignatureInput(Node).

func NewNodeSetData

func NewNodeSetData(nodes []*xmldom.Node) *Data

NewNodeSetData wraps an explicit node set. Port of XMLSignatureInput(Set<Node>).

func NewOctetData

func NewOctetData(octets []byte) *Data

NewOctetData wraps an octet stream. Port of XMLSignatureInput(byte[]).

A nil slice becomes an empty one: Java distinguishes "no octets" from "zero octets" through null, and this constructor is only ever reached for the second, so the nil is normalized away here rather than left to trip a digest routine downstream.

func NewPreCalculatedDigestData

func NewPreCalculatedDigestData(base64Digest string) *Data

NewPreCalculatedDigestData wraps a base64-encoded digest that stands in for content that is never streamed. Port of XMLSignatureInput(String), which DSS reaches through DigestDocumentXMLSignatureInput.

func PerformTransforms

func PerformTransforms(in *Data, transforms *xmldom.Node, baseURI string, secureValidation bool, r *Registry) (*Data, error)

PerformTransforms runs a ds:Transforms element's chain over in, left to right. Port of Transforms#performTransforms.

transforms may be nil, which is a ds:Reference with no ds:Transforms: the input passes through untouched and the digest is taken over Data.Bytes, which is XMLDSIG 4.4.3.2's "if the data is a node-set, apply Canonical XML".

func Resolve

func Resolve(perManifest, global []URIResolver, ctx *ResolverContext) (*Data, error)

Resolve walks perManifest first and then global, returning the first resolver's answer. Port of ResourceResolver#resolve(List<ResourceResolverSpi>, ResourceResolverContext).

func (*Data) AddNodeFilter

func (d *Data) AddNodeFilter(f xmlc14n.NodeFilter) error

AddNodeFilter ports addNodeFilter, including its side effect: a filter cannot be applied to octets, so an octet input is parsed into a document first (Santuario's convertToNodes).

func (*Data) Bytes

func (d *Data) Bytes() ([]byte, error)

Bytes returns the octets this value denotes. Port of XMLSignatureInput#getBytes: octets are returned as they are, and anything else is canonicalized with Canonical XML 1.0 omitting comments - the default the XMLDSIG reference processing model prescribes for a node set that reaches the digest without an explicit canonicalization transform (4.4.3.2), and the hard-wired canonicalizer of both getBytes and updateOutputStream.

func (*Data) ExcludeComments

func (d *Data) ExcludeComments() bool

ExcludeComments ports isExcludeComments.

func (*Data) ExcludeNode

func (d *Data) ExcludeNode() *xmldom.Node

ExcludeNode ports getExcludeNode.

func (*Data) Filters

func (d *Data) Filters() []xmlc14n.NodeFilter

Filters ports getNodeFilters.

func (*Data) IsElement

func (d *Data) IsElement() bool

IsElement ports isElement: a subtree, not yet turned into a node set.

FIX (integrator, Phase 4d): Santuario's isElement()/isNodeSet() test inputOctetStreamProxy (i.e. whether this value was ever constructed as an octet stream), never the cached bytes getBytes() leaves behind after canonicalizing a subtree - getBytes() caches into `bytes` at XMLSignatureInput.java:279 and upstream still re-canonicalizes the subtree on every call after that. This predicate used to also require !hasOctets, which meant a Data that had already been read once (Bytes() caches its result the same way) reported "no usable state" to the next transform - breaking any identity transform (EnvelopedSignatureTransform, Base64Transform) followed by another one, exactly the chain EnvelopedSignatureTransform's own javadoc prescribes. IsOctetStream's node==nil && nodeSet==nil guard already keeps the three branches disjoint, so dropping the hasOctets term here is safe. See the dss-xades REFS chunk's porter notes for the original diagnosis (55/57 -> 56/57 KAT cases verified against the Java oracle in a scratch harness).

func (*Data) IsNodeSet

func (d *Data) IsNodeSet() bool

IsNodeSet ports isNodeSet. See IsElement's FIX note above; the same hasOctets term was dropped here for the same reason.

func (*Data) IsOctetStream

func (d *Data) IsOctetStream() bool

IsOctetStream ports isOctetStream.

func (*Data) IsPreCalculatedDigest

func (d *Data) IsPreCalculatedDigest() bool

IsPreCalculatedDigest ports isPreCalculatedDigest.

func (*Data) MIMEType

func (d *Data) MIMEType() string

MIMEType ports getMIMEType.

func (*Data) Node

func (d *Data) Node() *xmldom.Node

Node ports getSubNode.

func (*Data) NodeSet

func (d *Data) NodeSet() []*xmldom.Node

NodeSet ports getInputNodeSet.

func (*Data) Nodes

func (d *Data) Nodes() ([]*xmldom.Node, error)

Nodes materializes the node set this value denotes. Port of getNodeSet(): the explicit set when there is one, the subtree under Node minus the exclude node otherwise, and the whole parsed document for octets.

Only Manifest.VerifyReferences needs it, to find the ds:Manifest inside a reference's output when following nested manifests; the canonicalizers never call it, because materializing a filtered set would lose the -1 "skip this subtree" answers that make the filters exact.

func (*Data) PreCalculatedDigest

func (d *Data) PreCalculatedDigest() string

PreCalculatedDigest ports getPreCalculatedDigest.

func (*Data) SetExcludeComments

func (d *Data) SetExcludeComments(b bool)

SetExcludeComments ports setExcludeComments.

func (*Data) SetExcludeNode

func (d *Data) SetExcludeNode(n *xmldom.Node)

SetExcludeNode ports setExcludeNode: the enveloped-signature transform names the ds:Signature element that the subtree canonicalization must skip.

func (*Data) SetMIMEType

func (d *Data) SetMIMEType(s string)

SetMIMEType ports setMIMEType.

func (*Data) SetNodeSet

func (d *Data) SetNodeSet(b bool)

SetNodeSet ports setNodeSet: the ds:XPath and XPath Filter 2.0 transforms flip the discriminator so that the canonicalizer takes the document-subset path.

func (*Data) SetSourceURI

func (d *Data) SetSourceURI(s string)

SetSourceURI ports setSourceURI.

func (*Data) SourceURI

func (d *Data) SourceURI() string

SourceURI ports getSourceURI.

type DetachedSignatureResolver

type DetachedSignatureResolver struct {
	Documents       []model.DSSDocument
	DigestAlgorithm enumerations.DigestAlgorithm
}

DetachedSignatureResolver resolves a ds:Reference whose URI names a detached document, or carries no URI at all, against a list of documents supplied by the caller. Port of DetachedSignatureResolver.

DigestAlgorithm is the fallback used when the reference does not say which digest it uses; upstream registers one resolver per distinct digest algorithm found in the ds:SignedInfo, which is why the field is a single algorithm and not a set.

Candidate selection is deliberately not "match the URI to a file name". Upstream tries, in order:

  1. exactly one document and exactly one detached reference in the ds:SignedInfo: that document, whatever the URI says;
  2. the document whose digest equals the ds:DigestValue the reference states, which lets a renamed file still validate;
  3. the document whose name equals the percent-decoded URI.

Rule 2 before rule 3 is the surprising one and it is load-bearing: DSS prefers the document that actually hashes right, and only falls back to the name.

func (*DetachedSignatureResolver) CanResolve

func (r *DetachedSignatureResolver) CanResolve(ctx *ResolverContext) bool

CanResolve ports engineCanResolveURI: a reference with no URI attribute at all, or one whose URI is a non-blank value that does not start with '#'.

func (*DetachedSignatureResolver) Resolve

func (r *DetachedSignatureResolver) Resolve(ctx *ResolverContext) (*Data, error)

Resolve ports engineResolveURI.

A DigestDocument - a document that carries a digest instead of content - yields a pre-calculated-digest input, which Reference.CalculateDigest returns verbatim without running any transform. That is how DSS validates a reference over content it was never given, and it is also why a DigestDocument silently "passes" a reference that carries transforms: there is nothing to transform. Upstream has the same hole.

type EnforcedResolverFragment

type EnforcedResolverFragment struct{ ResolverFragment }

EnforcedResolverFragment is ResolverFragment with DSS's XPath-injection guard in front. Port of eu.europa.esig.dss.xades.EnforcedResolverFragment.

The guard percent-decodes the URI and refuses it if it contains any of "()='[]:,*/ ". Its purpose is to stop a URI that has been crafted to be read as an XPath expression by some downstream resolver, and its effect on ordinary XAdES is nil: a "#id" fragment can carry none of those characters. A URI it refuses simply falls through to the next resolver, and then to "no resolver can dereference this", which is why DSS registers it BEFORE ResolverXPointer rather than instead of ResolverFragment.

func (EnforcedResolverFragment) CanResolve

func (r EnforcedResolverFragment) CanResolve(ctx *ResolverContext) bool

CanResolve ports EnforcedResolverFragment#engineCanResolveURI.

type Manifest

type Manifest struct {
	// contains filtered or unexported fields
}

Manifest is a ds:Manifest, and the shared base of ds:SignedInfo - which is what it is in Santuario too: SignedInfo extends Manifest, and "verify the signature's references" is literally Manifest#verifyReferences on the ds:SignedInfo element.

func NewManifest

func NewManifest(element *xmldom.Node, opts *ManifestOptions) (*Manifest, error)

NewManifest wraps a ds:Manifest element. Port of Manifest(Element, String, boolean).

func (*Manifest) AddResourceResolver

func (m *Manifest) AddResourceResolver(r URIResolver)

AddResourceResolver appends a per-manifest resolver. Port of addResourceResolver, which DSSXMLUtils#initManifestDetachedContent calls once per distinct reference digest algorithm.

func (*Manifest) Element

func (m *Manifest) Element() *xmldom.Node

Element returns the wrapped element.

func (*Manifest) Item

func (m *Manifest) Item(i int) (*Reference, error)

Item returns the i'th ds:Reference. Port of item(int).

func (*Manifest) Length

func (m *Manifest) Length() int

Length returns the number of ds:Reference children. Port of getLength().

func (*Manifest) References

func (m *Manifest) References() ([]*Reference, error)

References returns every ds:Reference of this manifest, in document order.

func (*Manifest) VerificationResults

func (m *Manifest) VerificationResults() []VerifiedReference

VerificationResults returns the per-reference outcome of the last VerifyReferences. Port of getVerificationResults().

func (*Manifest) VerifyReferences

func (m *Manifest) VerifyReferences(followManifests bool) (bool, error)

VerifyReferences re-digests every reference and reports whether all of them match. Port of verifyReferences(boolean).

It is all-or-nothing in its return value but not in its work: every reference is verified even after one has failed, so VerificationResults tells the caller which ones did. A reference that cannot be dereferenced at all is an error, not a false - that is Santuario's MissingResourceFailureException, and DSS distinguishes the two as "reference data not found" versus "reference data not intact".

followManifests walks into a ds:Manifest a reference of type ...#Manifest points at. DSS leaves it off here and validates manifests through its own ManifestValidator instead, which is why the flag exists but the default caller passes false.

type ManifestOptions

type ManifestOptions struct {
	BaseURI          string
	SecureValidation bool

	// Resolvers replaces the global resolver list. Nil selects DefaultResolvers().
	Resolvers []URIResolver
	// Registry replaces the transform registry. Nil selects DefaultRegistry().
	Registry *Registry
}

ManifestOptions carries the knobs that are constructor arguments or setters upstream. The zero value is what DSS uses: secure validation off, the DSS resolver set, the default transform registry.

type Reference

type Reference struct {
	// contains filtered or unexported fields
}

Reference is one ds:Reference. Port of org.apache.xml.security.signature.Reference.

It reads the element it was built from every time rather than caching, exactly as the Java does, so a caller that mutates the DOM between calls sees the mutation - which matters, because XAdES extension does mutate the signature between validations.

func NewReference

func NewReference(element *xmldom.Node, baseURI string, manifest *Manifest, secureValidation bool) (*Reference, error)

NewReference wraps a ds:Reference element. Port of Reference(Element, String, Manifest, boolean).

func (*Reference) CalculateDigest

func (r *Reference) CalculateDigest() ([]byte, error)

CalculateDigest computes the reference's digest. Port of calculateDigest(boolean).

The pre-calculated shortcut comes first and skips everything else: an input that carries a digest instead of content is returned as it is, without dereferencing, transforming or hashing anything.

func (*Reference) ContentsAfterTransformation

func (r *Reference) ContentsAfterTransformation() (*Data, error)

ContentsAfterTransformation runs the transform chain. Port of getContentsAfterTransformation().

func (*Reference) ContentsBeforeTransformation

func (r *Reference) ContentsBeforeTransformation() (*Data, error)

ContentsBeforeTransformation dereferences the URI. Port of getContentsBeforeTransformation().

func (*Reference) DigestAlgorithm

func (r *Reference) DigestAlgorithm() (enumerations.DigestAlgorithm, error)

DigestAlgorithm returns the ds:DigestMethod algorithm. Port of getMessageDigestAlgorithm().

Santuario additionally refuses MD5 under secure validation; DSS runs with secure validation off, so that guard never fires there and is not reproduced here. A digest algorithm the enumerations do not know is an error either way.

func (*Reference) DigestValue

func (r *Reference) DigestValue() ([]byte, error)

DigestValue returns the decoded ds:DigestValue. Port of getDigestValue().

func (*Reference) Element

func (r *Reference) Element() *xmldom.Node

Element returns the ds:Reference element. Port of ElementProxy#getElement.

func (*Reference) HasURI

func (r *Reference) HasURI() bool

HasURI reports whether the URI attribute is present at all - not the same as an empty URI, which means "this document".

func (*Reference) ID

func (r *Reference) ID() string

ID returns the Id attribute. Port of getId().

func (*Reference) ReferencedBytes

func (r *Reference) ReferencedBytes() ([]byte, error)

ReferencedBytes returns the octets that will be digested. Port of getReferencedBytes().

This is the byte array DSSXMLUtils#getReferenceOriginalContentBytes hands back for a reference that carries an enveloped-signature transform, and the one the XAdES validation report shows as the signed content.

func (*Reference) TransformsElement

func (r *Reference) TransformsElement() *xmldom.Node

TransformsElement returns the ds:Transforms child, or nil. Port of getTransforms(), whose null result is what "this reference has no transforms" means downstream.

func (*Reference) TransformsOutput

func (r *Reference) TransformsOutput() *Data

TransformsOutput returns the pipeline result of the last CalculateDigest or ContentsAfterTransformation, or nil. Port of getTransformsOutput(), which upstream documents as "only works after a call to verify".

func (*Reference) Type

func (r *Reference) Type() string

Type returns the Type attribute. Port of getType().

func (*Reference) TypeIsReferenceToManifest

func (r *Reference) TypeIsReferenceToManifest() bool

TypeIsReferenceToManifest ports typeIsReferenceToManifest().

func (*Reference) TypeIsReferenceToObject

func (r *Reference) TypeIsReferenceToObject() bool

TypeIsReferenceToObject ports typeIsReferenceToObject().

func (*Reference) URI

func (r *Reference) URI() string

URI returns the URI attribute, empty when absent. Port of getURI().

func (*Reference) Verify

func (r *Reference) Verify() (bool, error)

Verify recomputes the digest and compares it with ds:DigestValue. Port of verify().

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

Registry maps algorithm URIs to implementations. Port of the static registry org.apache.xml.security.transforms.Transform keeps, which Init.init() fills.

func DefaultRegistry

func DefaultRegistry() *Registry

DefaultRegistry returns a registry holding the eleven transforms Transform#registerDefaultAlgorithms registers: the six canonicalization methods, base64, enveloped-signature, ds:XPath, XPath Filter 2.0 and XSLT.

Two entries are not what upstream has, and both are deliberate:

  • Santuario's "physical" canonicalization method is NOT a transform. It is registered as a canonicalizer (XMLCanonicalizer offers it, so a ds:CanonicalizationMethod may name it) but never as a TransformSpi, so a ds:Transform naming it is an unknown algorithm. That is reproduced: no entry here.
  • XSLT is registered as a transform that always fails. Santuario refuses it only when secure validation is on, and DSS turns secure validation OFF (new XMLSignature(element, "", false)), so upstream would actually run an XSLT stylesheet out of a signature it is validating. Running attacker-supplied XSLT is not a behaviour worth reproducing, and no XAdES profile uses the transform; a reference that carries it fails to validate here instead of being digested over transformed output. Recorded as an accepted divergence.

A fresh registry per call: the transforms themselves are stateless values, but a caller that registers its own algorithm must not perturb anyone else's pipeline, which is exactly the global-mutable-registry hazard upstream lives with.

func NewRegistry

func NewRegistry() *Registry

NewRegistry returns an empty registry.

func (*Registry) Lookup

func (r *Registry) Lookup(alg string) (Transform, bool)

Lookup returns the transform registered for alg.

func (*Registry) Perform

func (r *Registry) Perform(in *Data, element *xmldom.Node, baseURI string, secureValidation bool) (*Data, error)

Perform runs the transform named by the ds:Transform element's Algorithm attribute. Port of Transform#performTransform plus Transforms#checkSecureValidation.

func (*Registry) Register

func (r *Registry) Register(t Transform)

Register adds t, replacing any transform already registered for its URI. Port of Transform#register.

type ResolverContext

type ResolverContext struct {
	Attr             *xmldom.Node
	URIToResolve     string
	BaseURI          string
	SecureValidation bool
}

ResolverContext is what a resolver is asked about. Port of ResourceResolverContext.

Attr is the ds:Reference URI attribute node, or nil when the reference carries no URI at all - a distinction the detached resolver depends on, since "no URI" means "the application knows what is meant" while URI="" means "this document".

type ResolverFragment

type ResolverFragment struct{}

ResolverFragment resolves "" (the whole document) and "#id" (the element with that Id). Port of ResolverFragment.

Both answers exclude comments - result.setExcludeComments(true) - which is XMLDSIG 4.4.3.3 step 4: a same-document reference is a node-set that omits comment nodes unless a #WithComments canonicalization is asked for explicitly.

func (ResolverFragment) CanResolve

func (ResolverFragment) CanResolve(ctx *ResolverContext) bool

CanResolve ports engineCanResolveURI: the empty URI, or a "#" URI that is not an XPointer.

func (ResolverFragment) Resolve

func (ResolverFragment) Resolve(ctx *ResolverContext) (*Data, error)

Resolve ports engineResolveURI.

type ResolverXPointer

type ResolverXPointer struct{}

ResolverXPointer resolves the two XPointer forms XMLDSIG requires support for. Port of ResolverXPointer.

Only two shapes are recognised, and they are recognised by string matching, not by parsing XPointer: exactly "#xpointer(/)" for the whole document, and "#xpointer(id('x'))" or "#xpointer(id(\"x\"))" for one element. Anything else - a bare-name XPointer, a scheme other than the implicit one, whitespace inside the parentheses - is not resolvable here, as upstream.

Unlike ResolverFragment this one does NOT exclude comments: an XPointer node-set keeps them, which is XMLDSIG 4.4.3.3's distinction between a bare-name and an XPointer same-document reference, and it is observable whenever the reference is canonicalized #WithComments.

func (ResolverXPointer) CanResolve

func (ResolverXPointer) CanResolve(ctx *ResolverContext) bool

CanResolve ports engineCanResolveURI.

func (ResolverXPointer) Resolve

func (ResolverXPointer) Resolve(ctx *ResolverContext) (*Data, error)

Resolve ports engineResolveURI.

type SignedInfo

type SignedInfo struct {
	*Manifest
	// contains filtered or unexported fields
}

SignedInfo is a ds:SignedInfo. Port of SignedInfo, which extends Manifest in Java too: the reference verification is entirely inherited, and only the canonicalization and the signature method are its own.

func NewSignedInfo

func NewSignedInfo(element *xmldom.Node, opts *ManifestOptions) (*SignedInfo, error)

NewSignedInfo wraps a ds:SignedInfo element. Port of SignedInfo(Element, String, boolean).

func (*SignedInfo) CanonicalizationMethodURI

func (si *SignedInfo) CanonicalizationMethodURI() string

CanonicalizationMethodURI returns the ds:CanonicalizationMethod Algorithm. Port of getCanonicalizationMethodURI().

func (*SignedInfo) CanonicalizedOctets

func (si *SignedInfo) CanonicalizedOctets() ([]byte, error)

CanonicalizedOctets returns the octets the ds:SignatureValue is computed over. Port of getCanonicalizedOctetStream().

Note what it is NOT: it is a plain SUBTREE canonicalization of the ds:SignedInfo element, with no reference processing, no node filtering and no comment exclusion - the comment behaviour is entirely the algorithm's, so a ds:CanonicalizationMethod naming a #WithComments variant really does sign the comments inside ds:SignedInfo. The result is cached, as upstream caches c14nizedBytes, because both the verification and DSS's getDataToBeSignedRepresentation ask for it.

func (*SignedInfo) SignatureMethodURI

func (si *SignedInfo) SignatureMethodURI() string

SignatureMethodURI returns the ds:SignatureMethod Algorithm. Port of getSignatureMethodURI().

func (*SignedInfo) Verify

func (si *SignedInfo) Verify(followManifests bool) (bool, error)

Verify re-digests every reference. Port of SignedInfo#verify(boolean), which is Manifest#verifyReferences under another name.

type Transform

type Transform interface {
	Algorithm() string
	Perform(in *Data, element *xmldom.Node, baseURI string, secureValidation bool) (*Data, error)
}

Transform is one transform algorithm. Port of org.apache.xml.security.transforms.TransformSpi#enginePerformTransform.

element is the ds:Transform element itself, so a transform can read its own parameters - the ds:XPath child, the ec:InclusiveNamespaces PrefixList - and, crucially, resolve the namespace prefixes its expression uses against the declarations in scope there. baseURI is the reference's base URI. secureValidation is XMLSignatureInput#isSecureValidation, which DSS sets to false so that every signature algorithm stays reachable.

A transform returns a new Data or, for the filtering transforms, the same one with a filter attached; both are what Santuario does.

type URIResolver

type URIResolver interface {
	CanResolve(ctx *ResolverContext) bool
	Resolve(ctx *ResolverContext) (*Data, error)
}

URIResolver dereferences a ds:Reference URI. Port of ResourceResolverSpi.

The two methods are asked in that order and the first resolver that says it can resolve is the one that must: ResourceResolver#resolve does not fall through to the next resolver when the chosen one fails.

func DefaultResolvers

func DefaultResolvers() []URIResolver

DefaultResolvers returns the same-document resolvers, in the order XAdESSignature.initDefaultResolvers registers them: the XPath-injection-guarded fragment resolver, then the XPointer resolver.

This is deliberately NOT Santuario's registerDefaultResolvers(), which also installs ResolverDirectHTTP and ResolverLocalFilesystem. DSS replaces the default set precisely to drop those two - "Ignore references which point to a file (file://) or external http urls" - so a signature can never make the validator fetch anything.

type VerifiedReference

type VerifiedReference struct {
	Valid bool
	URI   string
	// ManifestReferences holds the results of a nested ds:Manifest's own references, and is
	// non-empty only when VerifyReferences was asked to follow manifests.
	ManifestReferences []VerifiedReference
}

VerifiedReference is one entry of the verification result. Port of org.apache.xml.security.signature.VerifiedReference.

type XMLSignature

type XMLSignature struct {
	// contains filtered or unexported fields
}

XMLSignature is a ds:Signature. Port of XMLSignature(Element, String, boolean), the constructor XAdESSignature.getSantuarioSignature calls with secure validation off.

func NewXMLSignature

func NewXMLSignature(element *xmldom.Node, opts *ManifestOptions) (*XMLSignature, error)

NewXMLSignature wraps a ds:Signature element.

The document's ID attributes must already be registered - xmldom's RegisterIDs, which is DSS's XAdESDOMDocument.recursiveIdBrowse - or every "#id" reference will fail to resolve. Upstream has the same precondition and satisfies it in getSantuarioSignature, one line before constructing the XMLSignature.

func (*XMLSignature) AddResourceResolver

func (s *XMLSignature) AddResourceResolver(r URIResolver)

AddResourceResolver appends a resolver to the signature's ds:SignedInfo, which is the manifest whose references are being validated. Port of XMLSignature#addResourceResolver, which delegates to signedInfo.addResourceResolver.

func (*XMLSignature) CheckSignatureValue

func (s *XMLSignature) CheckSignatureValue(pub crypto.PublicKey) (bool, error)

CheckSignatureValue verifies the whole signature against pub: first the cryptographic signature over the canonicalized ds:SignedInfo, then every ds:Reference. Port of checkSignatureValue(Key).

The order is Santuario's and is not an implementation detail: the references are re-digested ONLY if the signature over ds:SignedInfo verified, so a forged signature never causes the validator to dereference the URIs it names.

func (*XMLSignature) CheckSignatureValueOnly

func (s *XMLSignature) CheckSignatureValueOnly(pub crypto.PublicKey) (bool, error)

CheckSignatureValueOnly verifies the cryptographic signature over the canonicalized ds:SignedInfo and nothing else.

It has no Santuario counterpart because Santuario never separates the two halves, but DSS does: XAdESSignature.checkSignatureIntegrity reports "signature intact" and "reference data intact" as two independent flags of SignatureCryptographicVerification, and it can only fill them in separately if the two checks can be run separately. Upstream gets there by calling checkSignatureValue (which does both) and then reading the reference validations back out of its own ReferenceValidation list; splitting the call is the same information without re-deriving it.

func (*XMLSignature) Element

func (s *XMLSignature) Element() *xmldom.Node

Element returns the ds:Signature element.

func (*XMLSignature) KeyInfoElement

func (s *XMLSignature) KeyInfoElement() *xmldom.Node

KeyInfoElement returns the ds:KeyInfo child, or nil.

func (*XMLSignature) SetFollowNestedManifests

func (s *XMLSignature) SetFollowNestedManifests(b bool)

SetFollowNestedManifests ports setFollowNestedManifests: whether CheckSignatureValue also validates the references of a ds:Manifest that a reference points at. Default false, as upstream.

func (*XMLSignature) SignatureValue

func (s *XMLSignature) SignatureValue() ([]byte, error)

SignatureValue returns the decoded ds:SignatureValue. Port of getSignatureValue().

func (*XMLSignature) SignedInfo

func (s *XMLSignature) SignedInfo() *SignedInfo

SignedInfo returns the ds:SignedInfo. Port of getSignedInfo().

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL