129 lines
3.2 KiB
Go
129 lines
3.2 KiB
Go
package dsig
|
|
|
|
import (
|
|
"sort"
|
|
|
|
"github.com/beevik/etree"
|
|
"github.com/russellhaering/goxmldsig/etreeutils"
|
|
)
|
|
|
|
// Canonicalizer is an implementation of a canonicalization algorithm.
|
|
type Canonicalizer interface {
|
|
Canonicalize(el *etree.Element) ([]byte, error)
|
|
Algorithm() AlgorithmID
|
|
}
|
|
|
|
type c14N10ExclusiveCanonicalizer struct {
|
|
prefixList string
|
|
}
|
|
|
|
// MakeC14N10ExclusiveCanonicalizerWithPrefixList constructs an exclusive Canonicalizer
|
|
// from a PrefixList in NMTOKENS format (a white space separated list).
|
|
func MakeC14N10ExclusiveCanonicalizerWithPrefixList(prefixList string) Canonicalizer {
|
|
return &c14N10ExclusiveCanonicalizer{
|
|
prefixList: prefixList,
|
|
}
|
|
}
|
|
|
|
// Canonicalize transforms the input Element into a serialized XML document in canonical form.
|
|
func (c *c14N10ExclusiveCanonicalizer) Canonicalize(el *etree.Element) ([]byte, error) {
|
|
err := etreeutils.TransformExcC14n(el, c.prefixList)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return canonicalSerialize(el)
|
|
}
|
|
|
|
func (c *c14N10ExclusiveCanonicalizer) Algorithm() AlgorithmID {
|
|
return CanonicalXML10ExclusiveAlgorithmId
|
|
}
|
|
|
|
type c14N11Canonicalizer struct{}
|
|
|
|
// MakeC14N11Canonicalizer constructs an inclusive canonicalizer.
|
|
func MakeC14N11Canonicalizer() Canonicalizer {
|
|
return &c14N11Canonicalizer{}
|
|
}
|
|
|
|
// Canonicalize transforms the input Element into a serialized XML document in canonical form.
|
|
func (c *c14N11Canonicalizer) Canonicalize(el *etree.Element) ([]byte, error) {
|
|
scope := make(map[string]struct{})
|
|
return canonicalSerialize(canonicalPrep(el, scope))
|
|
}
|
|
|
|
func (c *c14N11Canonicalizer) Algorithm() AlgorithmID {
|
|
return CanonicalXML11AlgorithmId
|
|
}
|
|
|
|
func composeAttr(space, key string) string {
|
|
if space != "" {
|
|
return space + ":" + key
|
|
}
|
|
|
|
return key
|
|
}
|
|
|
|
type c14nSpace struct {
|
|
a etree.Attr
|
|
used bool
|
|
}
|
|
|
|
const nsSpace = "xmlns"
|
|
|
|
// canonicalPrep accepts an *etree.Element and transforms it into one which is ready
|
|
// for serialization into inclusive canonical form. Specifically this
|
|
// entails:
|
|
//
|
|
// 1. Stripping re-declarations of namespaces
|
|
// 2. Sorting attributes into canonical order
|
|
//
|
|
// Inclusive canonicalization does not strip unused namespaces.
|
|
//
|
|
// TODO(russell_h): This is very similar to excCanonicalPrep - perhaps they should
|
|
// be unified into one parameterized function?
|
|
func canonicalPrep(el *etree.Element, seenSoFar map[string]struct{}) *etree.Element {
|
|
_seenSoFar := make(map[string]struct{})
|
|
for k, v := range seenSoFar {
|
|
_seenSoFar[k] = v
|
|
}
|
|
|
|
ne := el.Copy()
|
|
sort.Sort(etreeutils.SortedAttrs(ne.Attr))
|
|
if len(ne.Attr) != 0 {
|
|
for _, attr := range ne.Attr {
|
|
if attr.Space != nsSpace {
|
|
continue
|
|
}
|
|
key := attr.Space + ":" + attr.Key
|
|
if _, seen := _seenSoFar[key]; seen {
|
|
ne.RemoveAttr(attr.Space + ":" + attr.Key)
|
|
} else {
|
|
_seenSoFar[key] = struct{}{}
|
|
}
|
|
}
|
|
}
|
|
|
|
for i, token := range ne.Child {
|
|
childElement, ok := token.(*etree.Element)
|
|
if ok {
|
|
ne.Child[i] = canonicalPrep(childElement, _seenSoFar)
|
|
}
|
|
}
|
|
|
|
return ne
|
|
}
|
|
|
|
func canonicalSerialize(el *etree.Element) ([]byte, error) {
|
|
doc := etree.NewDocument()
|
|
doc.SetRoot(el.Copy())
|
|
|
|
doc.WriteSettings = etree.WriteSettings{
|
|
CanonicalAttrVal: true,
|
|
CanonicalEndTags: true,
|
|
CanonicalText: true,
|
|
}
|
|
|
|
return doc.WriteToBytes()
|
|
}
|