Initial QSfera import
This commit is contained in:
+270
@@ -0,0 +1,270 @@
|
||||
package dsig
|
||||
|
||||
import (
|
||||
"maps"
|
||||
"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 NullCanonicalizer struct {
|
||||
}
|
||||
|
||||
func MakeNullCanonicalizer() Canonicalizer {
|
||||
return &NullCanonicalizer{}
|
||||
}
|
||||
|
||||
func (c *NullCanonicalizer) Algorithm() AlgorithmID {
|
||||
return AlgorithmID("NULL")
|
||||
}
|
||||
|
||||
func (c *NullCanonicalizer) Canonicalize(el *etree.Element) ([]byte, error) {
|
||||
return canonicalSerialize(canonicalPrep(el, false, true))
|
||||
}
|
||||
|
||||
type c14N10ExclusiveCanonicalizer struct {
|
||||
prefixList string
|
||||
comments bool
|
||||
}
|
||||
|
||||
// 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,
|
||||
comments: false,
|
||||
}
|
||||
}
|
||||
|
||||
// MakeC14N10ExclusiveWithCommentsCanonicalizerWithPrefixList constructs an exclusive Canonicalizer
|
||||
// from a PrefixList in NMTOKENS format (a white space separated list).
|
||||
func MakeC14N10ExclusiveWithCommentsCanonicalizerWithPrefixList(prefixList string) Canonicalizer {
|
||||
return &c14N10ExclusiveCanonicalizer{
|
||||
prefixList: prefixList,
|
||||
comments: true,
|
||||
}
|
||||
}
|
||||
|
||||
// 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, c.comments)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return canonicalSerialize(el)
|
||||
}
|
||||
|
||||
func (c *c14N10ExclusiveCanonicalizer) Algorithm() AlgorithmID {
|
||||
if c.comments {
|
||||
return CanonicalXML10ExclusiveWithCommentsAlgorithmId
|
||||
}
|
||||
return CanonicalXML10ExclusiveAlgorithmId
|
||||
}
|
||||
|
||||
type c14N11Canonicalizer struct {
|
||||
comments bool
|
||||
}
|
||||
|
||||
// MakeC14N11Canonicalizer constructs an inclusive canonicalizer.
|
||||
func MakeC14N11Canonicalizer() Canonicalizer {
|
||||
return &c14N11Canonicalizer{
|
||||
comments: false,
|
||||
}
|
||||
}
|
||||
|
||||
// MakeC14N11WithCommentsCanonicalizer constructs an inclusive canonicalizer.
|
||||
func MakeC14N11WithCommentsCanonicalizer() Canonicalizer {
|
||||
return &c14N11Canonicalizer{
|
||||
comments: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Canonicalize transforms the input Element into a serialized XML document in canonical form.
|
||||
func (c *c14N11Canonicalizer) Canonicalize(el *etree.Element) ([]byte, error) {
|
||||
return canonicalSerialize(canonicalPrep(el, true, c.comments))
|
||||
}
|
||||
|
||||
func (c *c14N11Canonicalizer) Algorithm() AlgorithmID {
|
||||
if c.comments {
|
||||
return CanonicalXML11WithCommentsAlgorithmId
|
||||
}
|
||||
return CanonicalXML11AlgorithmId
|
||||
}
|
||||
|
||||
type c14N10RecCanonicalizer struct {
|
||||
comments bool
|
||||
}
|
||||
|
||||
// MakeC14N10RecCanonicalizer constructs an inclusive canonicalizer.
|
||||
func MakeC14N10RecCanonicalizer() Canonicalizer {
|
||||
return &c14N10RecCanonicalizer{
|
||||
comments: false,
|
||||
}
|
||||
}
|
||||
|
||||
// MakeC14N10WithCommentsCanonicalizer constructs an inclusive canonicalizer.
|
||||
func MakeC14N10WithCommentsCanonicalizer() Canonicalizer {
|
||||
return &c14N10RecCanonicalizer{
|
||||
comments: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Canonicalize transforms the input Element into a serialized XML document in canonical form.
|
||||
func (c *c14N10RecCanonicalizer) Canonicalize(inputXML *etree.Element) ([]byte, error) {
|
||||
parentNamespaceAttributes, parentXmlAttributes := getParentNamespaceAndXmlAttributes(inputXML)
|
||||
inputXMLCopy := inputXML.Copy()
|
||||
enhanceNamespaceAttributes(inputXMLCopy, parentNamespaceAttributes, parentXmlAttributes)
|
||||
return canonicalSerialize(canonicalPrep(inputXMLCopy, true, c.comments))
|
||||
}
|
||||
|
||||
func (c *c14N10RecCanonicalizer) Algorithm() AlgorithmID {
|
||||
if c.comments {
|
||||
return CanonicalXML10WithCommentsAlgorithmId
|
||||
}
|
||||
return CanonicalXML10RecAlgorithmId
|
||||
|
||||
}
|
||||
|
||||
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, strip bool, comments bool) *etree.Element {
|
||||
return canonicalPrepInner(el, make(map[string]string), strip, comments)
|
||||
}
|
||||
|
||||
func canonicalPrepInner(el *etree.Element, seenSoFar map[string]string, strip bool, comments bool) *etree.Element {
|
||||
_seenSoFar := make(map[string]string)
|
||||
maps.Copy(_seenSoFar, seenSoFar)
|
||||
|
||||
ne := el.Copy()
|
||||
sort.Sort(etreeutils.SortedAttrs(ne.Attr))
|
||||
n := 0
|
||||
for _, attr := range ne.Attr {
|
||||
if attr.Space != nsSpace && !(attr.Space == "" && attr.Key == nsSpace) {
|
||||
ne.Attr[n] = attr
|
||||
n++
|
||||
continue
|
||||
}
|
||||
|
||||
if attr.Space == nsSpace {
|
||||
key := attr.Space + ":" + attr.Key
|
||||
if uri, seen := _seenSoFar[key]; !seen || attr.Value != uri {
|
||||
ne.Attr[n] = attr
|
||||
n++
|
||||
_seenSoFar[key] = attr.Value
|
||||
}
|
||||
} else {
|
||||
if uri, seen := _seenSoFar[nsSpace]; (!seen && attr.Value != "") || attr.Value != uri {
|
||||
ne.Attr[n] = attr
|
||||
n++
|
||||
_seenSoFar[nsSpace] = attr.Value
|
||||
}
|
||||
}
|
||||
}
|
||||
ne.Attr = ne.Attr[:n]
|
||||
|
||||
if !comments {
|
||||
c := 0
|
||||
for c < len(ne.Child) {
|
||||
if _, ok := ne.Child[c].(*etree.Comment); ok {
|
||||
ne.RemoveChildAt(c)
|
||||
} else {
|
||||
c++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i, token := range ne.Child {
|
||||
childElement, ok := token.(*etree.Element)
|
||||
if ok {
|
||||
ne.Child[i] = canonicalPrepInner(childElement, _seenSoFar, strip, comments)
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
func getParentNamespaceAndXmlAttributes(el *etree.Element) (map[string]string, map[string]string) {
|
||||
namespaceMap := make(map[string]string, 23)
|
||||
xmlMap := make(map[string]string, 5)
|
||||
parents := make([]*etree.Element, 0, 23)
|
||||
n1 := el.Parent()
|
||||
if n1 == nil {
|
||||
return namespaceMap, xmlMap
|
||||
}
|
||||
parent := n1
|
||||
for parent != nil {
|
||||
parents = append(parents, parent)
|
||||
parent = parent.Parent()
|
||||
}
|
||||
for i := len(parents) - 1; i > -1; i-- {
|
||||
elementPos := parents[i]
|
||||
for _, attr := range elementPos.Attr {
|
||||
if attr.Space == "xmlns" && (attr.Key != "xml" || attr.Value != "http://www.w3.org/XML/1998/namespace") {
|
||||
namespaceMap[attr.Key] = attr.Value
|
||||
} else if attr.Space == "" && attr.Key == "xmlns" {
|
||||
namespaceMap[attr.Key] = attr.Value
|
||||
} else if attr.Space == "xml" {
|
||||
xmlMap[attr.Key] = attr.Value
|
||||
}
|
||||
}
|
||||
}
|
||||
return namespaceMap, xmlMap
|
||||
}
|
||||
|
||||
func enhanceNamespaceAttributes(el *etree.Element, parentNamespaces map[string]string, parentXmlAttributes map[string]string) {
|
||||
for prefix, uri := range parentNamespaces {
|
||||
if prefix == "xmlns" {
|
||||
el.CreateAttr("xmlns", uri)
|
||||
} else {
|
||||
el.CreateAttr("xmlns:"+prefix, uri)
|
||||
}
|
||||
}
|
||||
for attr, value := range parentXmlAttributes {
|
||||
el.CreateAttr("xml:"+attr, value)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user