Documentation
¶
Overview ¶
Package xmldsig1 implements W3C XML Digital Signatures 1.1.
Index ¶
- Constants
- Variables
- type DSAKeyValueData
- type ECKeyValueData
- type KeyInfoBuilder
- type KeyInfoData
- type KeySource
- type KeySourceFunc
- type ManifestReference
- type ManifestResult
- type RSAKeyValueData
- type ReferenceConfig
- type ReferenceError
- type ReferenceResolver
- type Signer
- func (s Signer) AllowSHA1(allow bool) Signer
- func (s Signer) CanonicalizationMethod(method string) Signer
- func (s Signer) KeyInfo(builder KeyInfoBuilder) Signer
- func (s Signer) Reference(ref ReferenceConfig) Signer
- func (s Signer) ReferenceParser(p helium.Parser) Signer
- func (s Signer) ReferenceResolver(r ReferenceResolver) Signer
- func (s Signer) SignDetached(ctx context.Context, doc *helium.Document, key any) (*helium.Element, error)
- func (s Signer) SignEnveloped(ctx context.Context, doc *helium.Document, parent *helium.Element, key any) error
- func (s Signer) SignEnveloping(ctx context.Context, doc *helium.Document, content []helium.Node, key any) (*helium.Element, error)
- func (s Signer) SignatureAlgorithm(alg string) Signer
- func (s Signer) SignatureID(id string) Signer
- type Transform
- type VerificationError
- type VerifiedReference
- type Verifier
- func (v Verifier) AllowSHA1(allow bool) Verifier
- func (v Verifier) AllowXPointer(allow bool) Verifier
- func (v Verifier) LenientKeyInfo(lenient bool) Verifier
- func (v Verifier) MaxDecodedBytes(n int) Verifier
- func (v Verifier) MaxKeyInfoEntries(n int) Verifier
- func (v Verifier) MaxReferences(n int) Verifier
- func (v Verifier) ReferenceParser(p helium.Parser) Verifier
- func (v Verifier) ReferenceResolver(r ReferenceResolver) Verifier
- func (v Verifier) ValidateManifests(validate bool) Verifier
- func (v Verifier) Verify(ctx context.Context, doc *helium.Document) (*VerifyResult, error)
- func (v Verifier) VerifyElement(ctx context.Context, doc *helium.Document, sig *helium.Element) (*VerifyResult, error)
- func (v Verifier) XSLTTransformer(t XSLTTransformer) Verifier
- type VerifyResult
- type X509IssuerSerial
- type XSLTTransformer
Constants ¶
const ( // NamespaceDSig is the XML Digital Signatures namespace. NamespaceDSig = "http://www.w3.org/2000/09/xmldsig#" // NamespaceDSig11 is the XML Digital Signatures 1.1 namespace. NamespaceDSig11 = "http://www.w3.org/2009/xmldsig11#" // NamespaceDSigMore is the xmldsig-more namespace. RFC 4050 places its // legacy ECDSAKeyValue (and its DomainParameters/NamedCurve/PublicKey // children) in this namespace, distinct from both the core xmldsig# // namespace and the XML-Signature 1.1 xmldsig11# namespace. NamespaceDSigMore = "http://www.w3.org/2001/04/xmldsig-more#" )
const ( AlgRSASHA1 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1" AlgRSASHA224 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha224" AlgRSASHA256 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" AlgRSASHA384 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384" AlgRSASHA512 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512" AlgECDSASHA1 = "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha1" AlgECDSASHA224 = "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha224" AlgECDSASHA256 = "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256" AlgECDSASHA384 = "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384" AlgECDSASHA512 = "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512" AlgHMACSHA1 = "http://www.w3.org/2000/09/xmldsig#hmac-sha1" AlgHMACSHA224 = "http://www.w3.org/2001/04/xmldsig-more#hmac-sha224" AlgHMACSHA256 = "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256" AlgHMACSHA384 = "http://www.w3.org/2001/04/xmldsig-more#hmac-sha384" AlgHMACSHA512 = "http://www.w3.org/2001/04/xmldsig-more#hmac-sha512" AlgEd25519 = "http://www.w3.org/2021/04/xmldsig-more#eddsa-ed25519" // AlgDSASHA1 is DSA-SHA1. It is verify-only (signing is not supported) and // SHA-1-weak, so it is rejected on verify unless Verifier.AllowSHA1(true). AlgDSASHA1 = "http://www.w3.org/2000/09/xmldsig#dsa-sha1" )
Signature algorithm URIs.
const ( DigestSHA1 = "http://www.w3.org/2000/09/xmldsig#sha1" DigestSHA224 = "http://www.w3.org/2001/04/xmldsig-more#sha224" DigestSHA256 = "http://www.w3.org/2001/04/xmlenc#sha256" DigestSHA384 = "http://www.w3.org/2001/04/xmldsig-more#sha384" DigestSHA512 = "http://www.w3.org/2001/04/xmlenc#sha512" )
Digest algorithm URIs.
const ( C14N10 = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" C14N10Comments = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments" ExcC14N10 = "http://www.w3.org/2001/10/xml-exc-c14n#" ExcC14N10Comments = "http://www.w3.org/2001/10/xml-exc-c14n#WithComments" C14N11URI = "http://www.w3.org/2006/12/xml-c14n11" C14N11Comments = "http://www.w3.org/2006/12/xml-c14n11#WithComments" )
Canonicalization method URIs.
const ( TransformEnvelopedSignature = "http://www.w3.org/2000/09/xmldsig#enveloped-signature" // TransformXPath is the XPath filter transform (XMLDSig core §6.6.3). It is // verify-only: verification evaluates the ds:Transform/XPath expression to // filter the reference node-set, but signing has no typed Transform for it and // the sign preflight rejects it fail-closed (there is no way to author the // required <XPath> child from the signing API). TransformXPath = "http://www.w3.org/TR/1999/REC-xpath-19991116" // TransformXSLT is the XSLT transform (XMLDSig core §6.6.5). Its // ds:Transform/xsl:stylesheet child is applied to the current pipeline octets; // the result feeds the next transform or digest. XSLT is powerful // (document(), unbounded compute), so // it is verify-only and OFF by default: it runs only through an injected // [XSLTTransformer] and fails closed with [ErrUnsupportedTransform] when none is // configured. Signing has no typed Transform for it and the sign preflight // rejects it fail-closed. TransformXSLT = "http://www.w3.org/TR/1999/REC-xslt-19991116" // TransformBase64 is the base64 decode transform (XMLDSig core §6.6.2). Its // node-set input is converted from the remaining text-node string-values, while // an octet input is decoded directly. The decoded octets feed the next declared // transform or the digest. Signing supports it through the Transform interface; // there is no typed constructor. TransformBase64 = "http://www.w3.org/2000/09/xmldsig#base64" )
Transform URIs.
const ( TypeObject = "http://www.w3.org/2000/09/xmldsig#Object" TypeManifest = "http://www.w3.org/2000/09/xmldsig#Manifest" TypeSignProps = "http://www.w3.org/2000/09/xmldsig#SignatureProperties" )
Type URIs for Reference elements.
const ( // TypeRawX509Certificate identifies a resource that is a single raw (DER) // X.509 certificate. TypeRawX509Certificate = "http://www.w3.org/2000/09/xmldsig#rawX509Certificate" // TypeX509Data identifies a resource that is a ds:X509Data element. TypeX509Data = "http://www.w3.org/2000/09/xmldsig#X509Data" )
Type URIs for the ds:RetrievalMethod Type attribute, identifying the kind of key material the referenced resource holds.
Variables ¶
var ( // ErrVerificationFailed is returned when signature verification fails. ErrVerificationFailed = errors.New("xmldsig1: verification failed") // ErrDigestMismatch is returned when a Reference digest does not match. ErrDigestMismatch = errors.New("xmldsig1: digest mismatch") // ErrSignatureNotFound is returned when no Signature element is found. ErrSignatureNotFound = errors.New("xmldsig1: signature element not found") // ErrUnsupportedAlgorithm is returned for unrecognized algorithm URIs. ErrUnsupportedAlgorithm = errors.New("xmldsig1: unsupported algorithm") // ErrWeakAlgorithm is returned when a SHA-1-based signature or digest // algorithm is encountered while SHA-1 is not allowed. SHA-1 is rejected // by default; opt in with Verifier.AllowSHA1(true) (for verification) or // Signer.AllowSHA1(true) (for signing) to accept it for legacy interop. ErrWeakAlgorithm = errors.New("xmldsig1: weak algorithm SHA-1 not allowed") // ErrUnsupportedTransform is returned for unrecognized transform URIs. ErrUnsupportedTransform = errors.New("xmldsig1: unsupported transform") // ErrKeyMismatch is returned when the key type does not match the algorithm. ErrKeyMismatch = errors.New("xmldsig1: key type does not match algorithm") // ErrNoReferences is returned when signing is attempted with no references. ErrNoReferences = errors.New("xmldsig1: no references configured") // ErrReferenceNotFound is returned when a Reference URI cannot be resolved. // It also covers an external Reference that no ReferenceResolver is // configured to dereference (the fail-closed default), and every // FSReferenceResolver rejection short of the size cap (scheme URI, escaping // path, leftover fragment, missing file). ErrReferenceNotFound = errors.New("xmldsig1: reference URI not resolved") // ErrReferenceTooLarge is returned when an external Reference resource // exceeds the resolver's size cap (FSReferenceResolver bounds a single // resource at 64 MiB) so a large or attacker-supplied file cannot exhaust // memory during verification. ErrReferenceTooLarge = errors.New("xmldsig1: external reference exceeds size cap") // ErrResourceLimitExceeded is returned when an attacker-controlled Signature // element exceeds one of the Verifier's parse-time resource caps before the // SignatureValue is checked: too many ds:Reference elements // ([Verifier.MaxReferences]), too many KeyInfo entries // ([Verifier.MaxKeyInfoEntries]), or too many total certificate and signature // octets — the DigestValue/SignatureValue/X509Certificate values plus // whatever a ds:RetrievalMethod in KeyInfo pulls in // ([Verifier.MaxDecodedBytes]). It also covers two fixed pre-verification // caps: a ds:RetrievalMethod transform list past its step cap, and a // ds:Transform/XPath filter expression past its length ceiling. The caps have // conservative defaults and bound the decode/parse/transform work an unsigned // document can force before verification rejects it. A base64 value decoded // off the document is charged against [Verifier.MaxDecodedBytes] before it is // decoded, so a value that is both over the cap and invalid base64 reports // this error, and never the base64 one. ErrResourceLimitExceeded = errors.New("xmldsig1: verification resource limit exceeded") // ErrAmbiguousReference is returned when a Reference URI resolves to more // than one element. This is the primary defense against XML Signature // Wrapping (XSW) attacks where an attacker injects a duplicate-ID element // containing malicious content alongside the legitimately signed element. ErrAmbiguousReference = errors.New("xmldsig1: reference URI matches multiple elements") // ErrAmbiguousSignature is returned when the document contains more than // one Signature element and Verify cannot decide which one to verify. // Callers must use VerifyElement to disambiguate. ErrAmbiguousSignature = errors.New("xmldsig1: document contains multiple Signature elements") // ErrInvalidKeyInfo is returned when KeyInfo content cannot be parsed. ErrInvalidKeyInfo = errors.New("xmldsig1: invalid KeyInfo") // ErrRetrievalMethodLoop is returned when a ds:RetrievalMethod chain is // cyclic or exceeds the maximum follow depth. A RetrievalMethod whose target // is itself a RetrievalMethod is followed, so an unbounded or self-referential // chain is rejected fail-closed, and never dereferenced without limit. ErrRetrievalMethodLoop = errors.New("xmldsig1: RetrievalMethod chain is cyclic or too deep") // ErrInvalidSignature is returned when the Signature element is malformed. ErrInvalidSignature = errors.New("xmldsig1: invalid signature structure") // ErrNoKeySource is returned when a Verifier was created with a nil // KeySource and verification is attempted. Without a KeySource there is no // way to resolve a verification key, so this is rejected before any key // resolution, and never panics on a nil dereference. ErrNoKeySource = errors.New("xmldsig1: no key source configured") // a context that has no bearing node. here() (XMLDSig core §6.6.3.1) resolves // to the element carrying the XPath expression — the ds:XPath element of an // XPath filter transform. It is threaded through only on the XPath filter // transform path; the signing path and a URI-borne XPointer carry no bearing // element, so here() fails closed there, resolving to no wrong node. ErrHereUnavailable = errors.New("xmldsig1: here() has no bearing node in this context") )
Functions ¶
This section is empty.
Types ¶
type DSAKeyValueData ¶ added in v0.7.0
DSAKeyValueData holds parsed DSAKeyValue content (the P, Q, G, Y CryptoBinary parameters). A KeySource builds a *dsa.PublicKey from these.
type ECKeyValueData ¶
ECKeyValueData holds parsed ECKeyValue content.
type KeyInfoBuilder ¶
type KeyInfoBuilder interface {
BuildKeyInfo(ctx context.Context, doc *helium.Document, key any) (*helium.Element, error)
}
KeyInfoBuilder configures how the KeyInfo element is constructed during signing.
func RSAKeyValueKeyInfo ¶
func RSAKeyValueKeyInfo() KeyInfoBuilder
RSAKeyValueKeyInfo returns a KeyInfoBuilder that includes RSAKeyValue derived from the signing key.
func X509DataKeyInfo ¶
func X509DataKeyInfo(certs ...*x509.Certificate) KeyInfoBuilder
X509DataKeyInfo returns a KeyInfoBuilder that includes X509Data containing the given certificates.
type KeyInfoData ¶
type KeyInfoData struct {
KeyNames []string
X509Certificates []*x509.Certificate
X509SKIs [][]byte
X509IssuerSerials []*X509IssuerSerial
X509SubjectNames []string
RSAKeyValue *RSAKeyValueData
ECKeyValue *ECKeyValueData
DSAKeyValue *DSAKeyValueData
}
KeyInfoData holds parsed KeyInfo content for verification.
SECURITY: every field is parsed from the document's ds:KeyInfo, which is attacker-controlled and NOT authenticated by the signature — KeyInfo is resolved before the signature is checked. Treat these values as untrusted hints for selecting a key from trusted material (a trust store, a pinned key, a validated chain), never as the key material to verify with. In particular an embedded X509Certificate is not proof of anything on its own: an attacker can embed a certificate for a key they control. See the KeySource contract.
type KeySource ¶
type KeySource interface {
// ResolveKey returns the verification key for a signature. keyInfo is the
// document's parsed, UNTRUSTED KeyInfo (nil when the Signature carries no
// KeyInfo); alg is the SignatureMethod algorithm URI. See the [KeySource]
// contract: match keyInfo against trusted material, and never trust it.
ResolveKey(ctx context.Context, keyInfo *KeyInfoData, alg string) (any, error)
}
KeySource provides keys for signature verification.
SECURITY: the keyInfo passed to ResolveKey is parsed from the document's ds:KeyInfo BEFORE the signature is verified, so it is entirely attacker-controlled (see KeyInfoData). A KeySource MUST decide trust itself: select the verification key by matching keyInfo against a trust store, a pinned key, or a validated certificate chain. It MUST NOT blindly return an embedded X509Certificate's public key or a KeyValue as the verification key — doing so lets an attacker present a signature made with their own key and have it verify. keyInfo is a selector into trusted key material, never the key material itself. StaticKey and X509CertKeySource ignore keyInfo and return a key the caller already trusts, which is the safe pattern; a custom KeySource that consults keyInfo carries the trust decision.
func KeyByNameSource ¶ added in v0.7.0
KeyByNameSource returns a KeySource that maps a ds:KeyName to a key. The KeyInfo's KeyNames are tried in document order and the first name present in keys wins; a KeyInfo with no matching KeyName (including one that carries no KeyName at all) fails closed with ErrNoKeySource. A ds:KeyName is an opaque, producer-chosen label, so the caller owns the name→key mapping and the trust decision that a named key is acceptable.
func X509CertKeySource ¶
func X509CertKeySource(cert *x509.Certificate) KeySource
X509CertKeySource returns a KeySource that extracts the public key from a trusted X.509 certificate. This is the common SAML pattern.
func X509CertPoolKeySource ¶ added in v0.7.0
func X509CertPoolKeySource(certs ...*x509.Certificate) KeySource
X509CertPoolKeySource returns a KeySource that selects a certificate from the given set by matching the verification-side KeyInfo against it, and returns the matched certificate's PublicKey. Selector strength is applied across the WHOLE pool, strongest first, so a strong match on a later certificate is never masked by a weak match on an earlier one:
- first, an exact raw-DER match against a ds:X509Certificate in the KeyInfo, over every certificate in the pool;
- then a ds:X509SKI match against the certificate's SubjectKeyId;
- then a ds:X509IssuerSerial match against the certificate's Issuer and SerialNumber;
- finally a ds:X509SubjectName match against the certificate's Subject.
Pool order is preserved only within a single selector class. The raw-DER and SubjectKeyId paths are exact and reliable. The IssuerSerial and SubjectName paths compare Go's pkix.Name.String() rendering, which is NOT RFC 2253 canonical, so DName matching is best-effort — prefer supplying the certificate whose SKI or raw bytes the signature carries. Selecting a certificate never establishes trust: the returned public key is still subject to the same out-of-band trust decision as any other verification key.
type KeySourceFunc ¶
KeySourceFunc adapts a function to the KeySource interface.
func (KeySourceFunc) ResolveKey ¶
func (f KeySourceFunc) ResolveKey(ctx context.Context, keyInfo *KeyInfoData, alg string) (any, error)
type ManifestReference ¶ added in v0.7.0
type ManifestReference struct {
// URI is the value of the inner Reference URI attribute.
URI string
// DigestAlgorithm is the algorithm URI declared in the inner Reference's
// DigestMethod element. It is "" when the inner Reference could not be
// parsed.
DigestAlgorithm string
// Element is the element the inner Reference URI resolved to, or nil for an
// external reference or when resolution failed.
Element *helium.Element
// Valid reports whether the inner Reference's recomputed digest matched its
// declared DigestValue.
Valid bool
// Err is the reason the inner Reference could not be validated, including a
// sibling static-preparation failure that prevented execution. It is nil
// only when Valid is true.
Err error
}
ManifestReference reports the outcome of digesting a single inner ds:Reference child of a ds:Manifest. Valid reports whether the inner reference's recomputed digest matched its DigestValue; Err carries the reason it could not be validated (a resolution, transform, digest, or mismatch error). Both are advisory — see ManifestResult.
type ManifestResult ¶ added in v0.7.0
type ManifestResult struct {
// Reference is the top-level VerifiedReference whose Type is TypeManifest —
// the reference the signature actually commits to. It points into
// VerifyResult.References.
Reference *VerifiedReference
// Element is the ds:Manifest element that Reference resolved to.
Element *helium.Element
// References lists the validation result for each inner ds:Reference child
// of the Manifest, in document order. If one child fails static preparation,
// none are executed and otherwise prepared peers carry an error wrapping the
// same cause. Only one level is walked: a Manifest Reference nested inside
// this Manifest is digested but not recursively expanded.
References []ManifestReference
}
ManifestResult reports the outcome of validating the inner ds:Reference children of a ds:Manifest element. It is populated only when Verifier.ValidateManifests(true) is set and the top-level Manifest-typed Reference's own digest verified. Inner-reference results are ADVISORY: per XMLDSig core §5.1 the application decides Manifest policy, so a failed inner reference does not fail Verify — the top-level Manifest Reference's digest is what the signature commits to. Coverage attribution (VerifyResult.Covers / SignedElement) is never made through a Manifest.
type RSAKeyValueData ¶
RSAKeyValueData holds parsed RSAKeyValue content.
type ReferenceConfig ¶
type ReferenceConfig struct {
URI string
DigestAlgorithm string
Transforms []Transform
ID string
Type string
}
ReferenceConfig describes a single Reference element in a signature.
func NewEnvelopedReference ¶
func NewEnvelopedReference() ReferenceConfig
NewEnvelopedReference returns a ReferenceConfig for a WHOLE-DOCUMENT enveloped signature: an empty URI, an enveloped-signature transform + Exclusive C14N, and a SHA-256 digest. The empty URI always resolves to the document element, so the reference covers the entire document regardless of which parent element the Signature is inserted into by Signer.SignEnveloped.
To envelope-sign a specific nested element by its id (for example a SAML Assertion inside a Response), use NewEnvelopedReferenceByID instead — an empty URI does NOT scope coverage to the SignEnveloped parent.
func NewEnvelopedReferenceByID ¶ added in v0.7.0
func NewEnvelopedReferenceByID(id string) ReferenceConfig
NewEnvelopedReferenceByID returns a ReferenceConfig for an enveloped signature that covers the single element carrying the given id (URI="#id"), with an enveloped-signature transform + Exclusive C14N and a SHA-256 digest. This is the correct choice for signing a specific nested element — for example a SAML Assertion by its AssertionID/ID — where NewEnvelopedReference (empty URI) would cover the whole document instead.
The id must be recognized as an ID attribute per the package's ID rules: a DTD/schema-declared ID-typed attribute, xml:id, or the "id" token in the casings "Id", "ID", or "id" (see Verifier.Verify). More than one element matching the id makes the reference ambiguous (ErrAmbiguousReference).
type ReferenceError ¶ added in v0.7.0
type ReferenceError struct {
// Op is the operation during which the failure occurred ("sign").
Op string
// Reference is the 0-based index of the failing Reference.
Reference int
// URI is the Reference URI that failed.
URI string
// Err is the underlying cause.
Err error
}
ReferenceError identifies which Reference failed during a signing operation. A per-reference failure carries the reference's 0-based index and URI so a caller signing over a multi-reference configuration can pinpoint the offending Reference, symmetric with how VerificationError reports a verification-side per-reference failure. The underlying cause stays reachable via errors.Is and errors.As (Unwrap), so a bare sentinel such as ErrReferenceNotFound or ErrUnsupportedTransform remains matchable through the wrapper.
func (*ReferenceError) Error ¶ added in v0.7.0
func (e *ReferenceError) Error() string
func (*ReferenceError) Unwrap ¶ added in v0.7.0
func (e *ReferenceError) Unwrap() error
type ReferenceResolver ¶ added in v0.7.0
type ReferenceResolver interface {
ResolveReference(ctx context.Context, uri string) ([]byte, error)
}
ReferenceResolver supplies the octet stream for a Reference whose URI is NOT one of the four supported same-document forms (see Verifier.Verify for those forms). It is the opt-in seam for verifying detached signatures that reference content outside the signed document.
A resolver is consulted ONLY for a non-same-document (external) Reference URI, after that URI has been joined against the document's base URI. Same-document references never reach it. When no resolver is configured an external reference stays fail-closed with ErrReferenceNotFound, the default.
The interface is public so callers can dereference references over any transport. helium ships only FSReferenceResolver, a filesystem resolver with no network access. No HTTP resolver is provided: anyone implementing network dereferencing owns the resulting SSRF and availability risk (an attacker who controls a Reference URI could otherwise steer requests at internal hosts or stall verification), so that decision is left explicitly to the caller.
ResolveReference must be safe to call from multiple goroutines, and should honor ctx cancellation. The returned octets are the resource's raw bytes; the package then applies every declared transform in order, parsing or canonicalizing only when the next transform requires the other value kind.
func FSReferenceResolver ¶ added in v0.7.0
func FSReferenceResolver(fsys fs.FS) ReferenceResolver
FSReferenceResolver returns a ReferenceResolver that serves external references from fsys, treating the (already base-joined) Reference URI as a slash-separated path inside fsys. It performs NO network access.
It is fail-closed on anything that is not a plain in-tree path:
- a URI carrying a scheme (http:, https:, file:, urn:, or any "scheme:" per RFC 3986, including a Windows drive letter) is refused — the resolver never interprets a scheme, so it cannot be steered into a fetch;
- a path escaping the root (an absolute path, or one with ".." segments that leave the root after cleaning) is refused via an fs.ValidPath containment check;
- a leftover fragment ("#...") is refused.
Reads are bounded: a resource larger than 64 MiB fails with ErrReferenceTooLarge before it can be buffered in full. Every rejection wraps ErrReferenceNotFound (or ErrReferenceTooLarge) so callers can match it with errors.Is.
func LimitReferenceResolver ¶ added in v0.8.0
func LimitReferenceResolver(resolver ReferenceResolver, maxBytes int) ReferenceResolver
LimitReferenceResolver composes a per-resource byte limit around a resolver. A non-positive value or a value above the package maximum selects the 64 MiB default; the cap cannot be disabled through this helper. The built-in FSReferenceResolver applies the limit while reading. Other resolvers are checked after they return their octets, so those implementations must enforce their own transport and aggregate-work limits.
type Signer ¶
type Signer struct {
// contains filtered or unexported fields
}
Signer creates XML Digital Signatures. It uses clone-on-write semantics: each builder method returns a new Signer and the original is never mutated.
func NewSigner ¶
func NewSigner() Signer
NewSigner creates a new Signer with default settings. Defaults: Exclusive C14N for SignedInfo canonicalization.
func (Signer) AllowSHA1 ¶ added in v0.3.0
AllowSHA1 controls whether SHA-1-based signature and digest algorithms (rsa-sha1, hmac-sha1, sha1) may be used when signing. SHA-1 is rejected by default; pass true to opt in for legacy interoperability. SHA-1 is cryptographically weak and should not be used for new signatures.
func (Signer) CanonicalizationMethod ¶
CanonicalizationMethod sets the canonicalization algorithm for SignedInfo.
func (Signer) KeyInfo ¶
func (s Signer) KeyInfo(builder KeyInfoBuilder) Signer
KeyInfo configures KeyInfo element construction.
func (Signer) Reference ¶
func (s Signer) Reference(ref ReferenceConfig) Signer
Reference adds a Reference to be signed. The Reference's Transforms slice is copied at ingress, so a later mutation of the slice the caller passed cannot alter this Signer or race with an in-flight signing operation.
func (Signer) ReferenceParser ¶ added in v0.7.0
ReferenceParser configures the helium.Parser used whenever a Reference transform converts octets to a node-set. This includes external resolver bytes and intermediate Base64 or canonicalization output. It is symmetric with Verifier.ReferenceParser and defaults to the same locked-down parser.
func (Signer) ReferenceResolver ¶ added in v0.7.0
func (s Signer) ReferenceResolver(r ReferenceResolver) Signer
ReferenceResolver configures a ReferenceResolver that dereferences external Reference URIs during signing, so a detached signature can cover content outside the document. It is opt-in and symmetric with Verifier.ReferenceResolver: the default is nil, leaving an external Reference URI fail-closed with ErrReferenceNotFound. When set, an external Reference URI is joined against the document's base URI, passed to r, and the resolved octets are run through the same transform pipeline the verifier applies, so the signed digest is byte-identical to what verification recomputes for the same input.
func (Signer) SignDetached ¶
func (s Signer) SignDetached(ctx context.Context, doc *helium.Document, key any) (*helium.Element, error)
SignDetached creates a detached Signature element referencing URIs specified in the configured References. Returns the Signature element.
Placement (inclusive C14N only): SignedInfo is canonicalized under a proxy carrying the signing document element's inherited canonicalization context. If SignedInfo is canonicalized with inclusive Canonical XML (C14N10 / C14N11) — the SignedInfo CanonicalizationMethod, not the Reference transforms — the caller MUST place the returned Signature directly under the document element, or under an element with the same in-scope namespaces and inherited xml:* attributes. Placing it under an element that contributes extra in-scope namespace declarations or xml:* attributes changes the bytes inclusive C14N canonicalizes for SignedInfo, so verification recomputes a different canonical form and fails. Exclusive Canonical XML (the NewSigner default, ExcC14NTransform) inherits no such context and is unaffected by placement.
Lifetime: the returned Signature is allocated from doc's slab storage (its nodes are created via doc.CreateElement) and is owned by doc, but a successful sign leaves it safe to keep after doc.Free(). Canonicalizing SignedInfo grafts the live Signature into a throwaway document, a cross-document move that marks doc's slab as escaped; doc.Free() then becomes a no-op and never recycles the chunks backing the Signature. So the returned Signature stays valid after doc.Free() — the caller does NOT need to move it into another document first to keep it.
func (Signer) SignEnveloped ¶
func (s Signer) SignEnveloped(ctx context.Context, doc *helium.Document, parent *helium.Element, key any) error
SignEnveloped creates an enveloped signature inside the given parent element of the document. The key is a concrete *rsa.PrivateKey, *ecdsa.PrivateKey, or ed25519.PrivateKey, any crypto.Signer whose public key is one of those types (for example an HSM/KMS/PKCS#11-backed key), or []byte for HMAC.
func (Signer) SignEnveloping ¶
func (s Signer) SignEnveloping(ctx context.Context, doc *helium.Document, content []helium.Node, key any) (*helium.Element, error)
SignEnveloping creates an enveloping signature wrapping the given content nodes in a <ds:Object>. Returns the (detached) Signature element for the caller to place. A configured Reference may point at an element inside the content by its Id (URI="#id") — for example a <ds:Manifest> or <ds:SignatureProperties> — and it is resolved and digested during signing without ever inserting the Signature into the caller's document: an in-Object target is canonicalized on its own, and a target in the document (URI="#root", even the document element) is digested over its unchanged subtree, byte-identical to a signature with no such internal reference. An id that matches in both the document and the Signature's own Object content is rejected as an ambiguous reference (ErrAmbiguousReference).
An in-Object target is canonicalized under a proxy that reproduces the full inherited canonicalization context the target will have once the caller places the Signature under the document element — every in-scope namespace declaration plus the inherited xml:* attributes, copied per the C14N version to match exactly what helium's own canonicalizer inherits to a node-set apex (Canonical XML 1.0 inherits every xml:* attribute including xml:id; Canonical XML 1.1 inherits only xml:lang/xml:space and lexically joins xml:base) — so a reference into the Object verifies under inclusive Canonical XML 1.0 or 1.1. Exclusive Canonical XML inherits no xml:*, so its digests are unaffected.
Placement (inclusive C14N only): the same proxy canonicalizes SignedInfo, and an in-Object Reference is digested under the context of the signing document element. When SignedInfo's CanonicalizationMethod or an in-Object Reference uses inclusive Canonical XML (C14N10 / C14N11), the caller MUST place the returned Signature directly under the document element, or under an element with the same in-scope namespaces and inherited xml:* attributes. Placing it under an element that contributes extra in-scope namespace declarations or xml:* attributes changes the inclusively-canonicalized bytes, so verification recomputes a different canonical form and fails. Exclusive Canonical XML (the NewSigner default, ExcC14NTransform) inherits no such context and is unaffected by placement.
Every content entry must be a movable node (helium.MutableNode); an ordinary DOM element qualifies. A nil, typed-nil, or read-only content entry (e.g. a namespace-node wrapper) is rejected with an indexed error wrapping ErrInvalidSignature before any node is moved, and is never silently dropped from the Object. Moving the content into the Object detaches it from the caller's tree; if signing then fails at any later step, every moved node is restored to its exact original position (parent, siblings, and order), leaving the caller's document byte-identical to before the call.
Lifetime: the returned Signature is allocated from doc's slab storage (its nodes are created via doc.CreateElement) and is owned by doc, but a successful sign leaves it safe to keep after doc.Free(). Canonicalizing SignedInfo grafts the live Signature into a throwaway document, a cross-document move that marks doc's slab as escaped; doc.Free() then becomes a no-op and never recycles the chunks backing the Signature. So the returned Signature stays valid after doc.Free() — the caller does NOT need to move it into another document first to keep it.
func (Signer) SignatureAlgorithm ¶
SignatureAlgorithm sets the signature algorithm URI.
func (Signer) SignatureID ¶
SignatureID sets the Id attribute on the Signature element.
type Transform ¶
type Transform interface {
URI() string
}
Transform represents a single step in a reference transform pipeline.
func C14NTransform ¶
C14NTransform returns a canonicalization transform for the given method URI.
func Enveloped ¶
func Enveloped() Transform
Enveloped returns the enveloped-signature transform. When applied during signing or verification, the ds:Signature element and its descendants are omitted from the canonical input. This is done on a deep copy of the document, so the caller's live DOM is never mutated.
func ExcC14NTransform ¶
ExcC14NTransform returns an Exclusive C14N transform with optional inclusive namespace prefixes. The prefixes are copied, so a later mutation of the caller's slice cannot alter the returned transform.
type VerificationError ¶
type VerificationError struct {
// Reference is the 0-based index of the failing Reference, or -1 for
// a SignatureValue failure.
Reference int
// URI is the Reference URI that failed (empty for SignatureValue).
URI string
// Err is the underlying cause.
Err error
}
VerificationError provides details about which step of verification failed.
func (*VerificationError) Error ¶
func (e *VerificationError) Error() string
func (*VerificationError) Unwrap ¶
func (e *VerificationError) Unwrap() error
type VerifiedReference ¶
type VerifiedReference struct {
// URI is the value of the Reference URI attribute as it appeared in the
// signed document.
URI string
// Element is the element that the URI resolved to at verification time.
// For the enveloped pattern (URI=""), this is the document element.
// For fragment references (URI="#id"), this is the unique element with
// that Id/ID attribute. If duplicate matches existed, verification fails
// with ErrAmbiguousReference before this field is populated.
//
// Element is nil for an External reference: an external resource is a byte
// stream outside the document, not an in-document element.
Element *helium.Element
// External reports whether this Reference was satisfied via a configured
// ReferenceResolver (its URI is not a same-document form). An external
// reference covers content outside the document, so Element is nil and
// neither Covers nor SignedElement ever attributes in-document coverage to
// it — a caller confirming a specific *Element was signed must rely on a
// same-document reference, never an external one.
External bool
// DigestAlgorithm is the algorithm URI declared in the DigestMethod
// element (e.g. DigestSHA256).
DigestAlgorithm string
// Type is the value of the Reference Type attribute as it appeared in the
// signed document, or "" when the attribute was absent. A Reference whose
// Type is TypeManifest points at a ds:Manifest element; when
// Verifier.ValidateManifests(true) is set, that Manifest's inner references
// are reported in VerifyResult.Manifests.
Type string
}
VerifiedReference describes a single Reference that was successfully verified. Callers should use this to confirm the *Element they are about to consume from the document is actually covered by the signature — guarding against XML Signature Wrapping (XSW) attacks.
type Verifier ¶
type Verifier struct {
// contains filtered or unexported fields
}
Verifier verifies XML Digital Signatures. It uses clone-on-write semantics: each builder method returns a new Verifier and the original is never mutated.
func NewVerifier ¶
NewVerifier creates a new Verifier with the given key source.
func (Verifier) AllowSHA1 ¶ added in v0.3.0
AllowSHA1 controls whether SHA-1-based signature and digest algorithms (rsa-sha1, hmac-sha1, sha1) are accepted during verification. SHA-1 is rejected by default; pass true to opt in for verifying legacy signatures. SHA-1 is cryptographically weak and accepting it exposes callers to downgrade and collision risks, so only enable it when interoperating with systems that cannot be upgraded.
func (Verifier) AllowXPointer ¶ added in v0.7.0
AllowXPointer opts into resolving a general XPointer Reference URI — an XPointer framework URI of the form "#xmlns(prefix=uri)...xpointer(<expr>)" (zero or more xmlns() namespace-binding parts followed by one xpointer() XPath expression) — in addition to the four safe same-document forms (see Verifier.Verify). It is opt-in and fail-closed by default: with AllowXPointer off (the default) a general XPointer URI is treated as an external reference and, without a ReferenceResolver, rejected with ErrReferenceNotFound, so default verification is byte-identical.
When enabled, every top-level xpointer() expression is compiled and statically validated during the all-Reference preflight before any Reference resolver or transformer runs. Its prepared bounded XPath 1.0 evaluator is reused during digest execution under the document element's in-scope namespaces overlaid with the xmlns() bindings. An unresolved variable, function, or prefix fails with ErrReferenceNotFound before evaluation. The result MUST identify a single element apex — the XML Signature Wrapping defense: an empty node-set is ErrReferenceNotFound, and a multi-element or non-element node-set is ErrAmbiguousReference. A literal xpointer(id('X')) keeps the duplicate-detecting id resolution (never a last-one-wins id table). The XMLDSig here() function is not available inside a URI-borne XPointer.
func (Verifier) LenientKeyInfo ¶ added in v0.7.0
LenientKeyInfo controls how an UNRESOLVABLE ds:RetrievalMethod in the ds:KeyInfo is treated. A RetrievalMethod is an optional hint pointing at key material held elsewhere; ds:KeyInfo may carry several such hints alongside inline key material. By default (lenient off), a RetrievalMethod that cannot be dereferenced at all — there is no ReferenceResolver configured, or the target cannot be found — fails the whole verification with ErrReferenceNotFound, even when other, usable key material is present. With LenientKeyInfo(true) such an unresolvable RetrievalMethod is skipped instead, so a KeySource still sees the inline certificates/keys and any RetrievalMethod that DID resolve.
Leniency covers ONLY the "could not dereference" case. A RetrievalMethod that resolves but is invalid — an unsupported or absent Type, an unsupported or mis-ordered transform, a cyclic or over-deep chain, an over-cap resource, or an ambiguous same-document target (the XML Signature Wrapping guard) — still fails closed regardless of this setting: a corrupt hint is an error, not a missing one. Neither mode ever trusts retrieved material by itself; the KeySource's out-of-band trust decision still governs, exactly as for an inline certificate.
func (Verifier) MaxDecodedBytes ¶ added in v0.7.0
MaxDecodedBytes caps the running total of certificate and signature octets the verifier produces before the SignatureValue check. Exceeding the cap is rejected with ErrResourceLimitExceeded, bounding the decode allocation an attacker-controlled document can force. n <= 0 has special meaning: 0 (the default) selects the conservative built-in cap, and a negative n disables the cap.
Four of the charged sites are base64 values decoded straight off the document: the Signature's own DigestValue, SignatureValue, and X509Certificate content, plus the rawX509Certificate a same-document ds:RetrievalMethod points at. That last one is not confined to the Signature. A RetrievalMethod URI names any element in the document by ID — any local name, any namespace, inside ds:Signature or outside it — and both the rawX509Certificate branch and the ds:X509Data branch charge whatever element it resolves to.
Each of those four is charged BEFORE it is materialized, so for them the cap bounds what verification BUILDS and not merely what it keeps. That matters because xs:base64Binary permits XML whitespace between characters and a value may be spread over any number of text and CDATA children: the lexical text an attacker wraps around a value is unbounded and unrelated to the bytes it decodes to, so counting it first is what keeps the memory under the cap for a value the cap refuses AND for every value it accepts.
The fifth site is the exception to both halves of that. An EXTERNAL ds:RetrievalMethod is dereferenced through the configured Verifier.ReferenceResolver, which materializes the whole resource under its OWN size cap (FSReferenceResolver bounds one resource at 64 MiB) and runs it through the RetrievalMethod's transforms; only then is the result charged. So those octets are charged AFTER they are materialized, they are bounded by the resolver's cap, and by no cap of this one's on the way in, and they are raw certificate bytes that were never base64-decoded at all.
func (Verifier) MaxKeyInfoEntries ¶ added in v0.7.0
MaxKeyInfoEntries caps the number of KeyInfo entries the verifier parses: the KeyInfo element's own children plus every X509Data child (each X509Certificate is parsed with x509.ParseCertificate, which is not free). A KeyInfo carrying more entries than the cap is rejected with ErrResourceLimitExceeded. n <= 0 has special meaning: 0 (the default) selects the conservative built-in cap, and a negative n disables the cap.
func (Verifier) MaxReferences ¶ added in v0.7.0
MaxReferences caps the number of ds:Reference elements the verifier parses out of SignedInfo. A document whose Signature declares more References than the cap is rejected with ErrResourceLimitExceeded before any Reference is digested, bounding the per-Reference canonicalization work an unsigned, attacker-controlled document can force. n <= 0 has special meaning: 0 (the default) selects the conservative built-in cap, and a negative n disables the cap entirely. The default sits well above any legitimate signature.
func (Verifier) ReferenceParser ¶ added in v0.7.0
ReferenceParser configures the helium.Parser used whenever the ordered transform pipeline converts octets to a node-set. It applies to external resolver bytes and to intermediate Base64, canonicalization, or XSLT output consumed by a later node-set transform. The default is a locked-down parser (helium.NewParser(): XXE blocked, no filesystem access, no network). Override it only to relax those defaults deliberately.
func (Verifier) ReferenceResolver ¶ added in v0.7.0
func (v Verifier) ReferenceResolver(r ReferenceResolver) Verifier
ReferenceResolver configures a ReferenceResolver that dereferences external Reference URIs (those that are not one of the four supported same-document forms). It is opt-in: the default is nil, which keeps external references fail-closed with ErrReferenceNotFound, byte-identical to a Verifier without a resolver. When set, an external Reference URI is joined against the document's base URI and passed to r; the resolved octets are then run through the Reference's transform pipeline before digesting.
A Reference satisfied via the resolver is marked External in the result (see VerifiedReference); VerifyResult.Covers and VerifyResult.SignedElement never report an external reference as covering in-document content, since it resolves to bytes outside the document, and to no element.
func (Verifier) ValidateManifests ¶ added in v0.7.0
ValidateManifests controls whether the inner ds:Reference children of a Manifest-typed Reference are digested and reported (XMLDSig core §5.1). It is opt-in: the default is false, which leaves VerifyResult.Manifests nil and walks no inner references, byte-identical to a Verifier without it.
When enabled, after a top-level Reference whose Type is TypeManifest has itself verified (its own digest over the ds:Manifest subtree is checked exactly as any other Reference), that Manifest's inner references are each resolved, transformed, and digested through the same fail-closed pipeline, with the per-reference outcome recorded in ManifestResult.
Every direct inner Reference is parsed and statically prepared before any resolver or transformer callback runs. If one fails preparation, none are executed: the failing ManifestReference reports that error, and each otherwise prepared peer reports an advisory error wrapping the same cause.
Inner-reference results are ADVISORY: per §5.1 the application decides how to treat a Manifest, so an inner-reference digest mismatch or an unresolved or unsupported inner reference does NOT fail Verify — the top-level Manifest Reference's own digest is what the signature commits to. Inner references never contribute to VerifyResult.Covers or VerifyResult.SignedElement, so coverage is never attributed through a Manifest. Only one level is walked: a Manifest nested inside a Manifest is digested but not recursively expanded.
It is off by default because inner references may pull in transforms or external URIs the top-level policy did not intend, so evaluating them is left to callers who want the report.
func (Verifier) Verify ¶
Verify verifies the Signature element in the document. The document must contain exactly one ds:Signature element; if it contains more than one the function returns ErrAmbiguousSignature and the caller must use VerifyElement to disambiguate.
On success the returned VerifyResult exposes the set of elements actually covered by the signature so callers can confirm — by pointer identity — that the element they intend to consume was signed. This is the primary defense against XML Signature Wrapping (XSW) attacks at the application layer.
Same-document reference resolution (ds:Reference URI="#id") locates the target element by its ID attribute. An attribute is recognized as an ID when it is any of:
- declared ID-typed by a DTD or schema the document was parsed with;
- xml:id (ID-typed by the W3C xml:id Recommendation);
- the "id" attribute token in the casings "Id", "ID", or "id".
This name set is deliberately limited to the "id" token. Other conventions (for example "wsu:Id" or SAML "AssertionID") are ID-typed only by their own schemas, so a document relying on them must carry that typing — via its DTD/schema, or by marking the attribute's type as an ID before verifying. The name alone never infers it. If more than one element matches the referenced ID the reference is refused (ErrAmbiguousReference).
Verification honors ctx: an already-cancelled or already-expired context short-circuits before any work, and cancellation is rechecked between References. Because a SignedInfo may carry arbitrarily many References and each empty-URI enveloped Reference canonicalizes a copy of the whole document, the per-Reference work scales with the number of References; bound it by passing a ctx with a deadline. On cancellation the context error (ctx.Err()) is returned.
func (Verifier) VerifyElement ¶
func (v Verifier) VerifyElement(ctx context.Context, doc *helium.Document, sig *helium.Element) (*VerifyResult, error)
VerifyElement verifies a specific Signature element. Use this when the document contains more than one Signature, or when the caller wants explicit control over which Signature is targeted.
Same-document reference resolution recognizes the same ID attributes as Verifier.Verify.
Verification honors ctx the same way as Verifier.Verify: an already-cancelled or already-expired context short-circuits before any work, cancellation is rechecked between References, and a ctx deadline is the lever for bounding the per-Reference work of a SignedInfo that carries many References.
func (Verifier) XSLTTransformer ¶ added in v0.7.0
func (v Verifier) XSLTTransformer(t XSLTTransformer) Verifier
XSLTTransformer configures the XSLTTransformer that applies the XSLT transform (http://www.w3.org/TR/1999/REC-xslt-19991116) to a Reference that carries one. It is opt-in: the default is nil, which keeps the XSLT transform fail-closed with ErrUnsupportedTransform, byte-identical to a Verifier without one. When set, a Reference's ds:Transform/xsl:stylesheet subtree is serialized and passed to t together with the current pipeline octets. One Reference may invoke t multiple times, and a later node-set transform reparses its output through Verifier.ReferenceParser.
XSLT is a powerful language and both the stylesheet and its input are attacker-controlled on verify, so the transformer owns all resource and XXE policy (see XSLTTransformer). The core package runs no XSLT automatically; the separate xmldsig1/transform package provides an explicit opt-in adapter.
type VerifyResult ¶
type VerifyResult struct {
// Signature is the Signature element that was verified.
Signature *helium.Element
// References lists every Reference inside SignedInfo that was
// successfully verified, in document order.
References []VerifiedReference
// Manifests lists, for each top-level Reference whose Type is TypeManifest,
// the result of digesting that Manifest's inner references. It is populated
// only when Verifier.ValidateManifests(true) is set; otherwise it is nil.
// Inner-reference results are ADVISORY (see ManifestResult): they never
// affect whether Verify succeeds, nor do they contribute to Covers or
// SignedElement coverage attribution.
Manifests []ManifestResult
}
VerifyResult is returned by Verifier.Verify and Verifier.VerifyElement on success. It exposes the set of elements that were actually covered by the signature so callers can correlate signed content with the element they intend to consume.
func (*VerifyResult) Covers ¶
func (r *VerifyResult) Covers(elem *helium.Element) bool
Covers reports whether elem was covered by any verified Reference. An External reference never counts: it covers content outside the document, so it cannot attest that an in-document *Element was signed.
func (*VerifyResult) SignedElement ¶
func (r *VerifyResult) SignedElement(uri string) *helium.Element
SignedElement returns the resolved element for the Reference with the given URI, or nil if no such Reference was verified. This is the preferred way to confirm an element was covered by the signature before consuming it. An External reference is skipped: it resolves to bytes outside the document, not an element, so it can never satisfy an in-document element lookup.
type X509IssuerSerial ¶ added in v0.7.0
X509IssuerSerial holds a parsed ds:X509IssuerSerial: the issuer distinguished name and certificate serial number. The library performs no DName canonicalization or matching; it extracts the values verbatim so a KeySource can select the corresponding certificate out of band.
type XSLTTransformer ¶ added in v0.7.0
type XSLTTransformer interface {
TransformXSLT(ctx context.Context, stylesheet []byte, input []byte) ([]byte, error)
}
XSLTTransformer applies the XMLDSig XSLT transform (XMLDSig core §6.6.5): stylesheet is the serialized xsl:stylesheet/xsl:transform subtree taken from the ds:Transform element, and input is the current pipeline octet stream. It may be raw resolver bytes or output from canonicalization, Base64, or an earlier XSLT step. The result feeds the next declared transform or the DigestValue.
It is the opt-in seam for the XSLT transform, mirroring ReferenceResolver: the XSLT transform is OFF by default and runs only when a transformer is configured via Verifier.XSLTTransformer; without one an XSLT transform fails closed with ErrUnsupportedTransform. XSLT is verify-only — signing rejects it fail-closed and never invokes a transformer.
SECURITY: both stylesheet and input are attacker-controlled on verify (an attacker who controls the signature controls the ds:Transform/xsl:stylesheet subtree, and input derives from the signed document). XSLT is a powerful language — document(), unbounded recursion, and other unbounded computation — so the implementer owns ALL resource and XXE policy (compute/time/memory limits, disabling document()/external access). The core package runs no XSLT automatically; callers may explicitly import the separate xmldsig1/transform adapter.
One Reference may invoke TransformXSLT multiple times. TransformXSLT must be safe to call from multiple goroutines and should honor ctx cancellation.