canonicalize.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. package dsig
  2. import (
  3. "sort"
  4. "github.com/beevik/etree"
  5. "github.com/russellhaering/goxmldsig/etreeutils"
  6. )
  7. // Canonicalizer is an implementation of a canonicalization algorithm.
  8. type Canonicalizer interface {
  9. Canonicalize(el *etree.Element) ([]byte, error)
  10. Algorithm() AlgorithmID
  11. }
  12. type c14N10ExclusiveCanonicalizer struct {
  13. prefixList string
  14. }
  15. // MakeC14N10ExclusiveCanonicalizerWithPrefixList constructs an exclusive Canonicalizer
  16. // from a PrefixList in NMTOKENS format (a white space separated list).
  17. func MakeC14N10ExclusiveCanonicalizerWithPrefixList(prefixList string) Canonicalizer {
  18. return &c14N10ExclusiveCanonicalizer{
  19. prefixList: prefixList,
  20. }
  21. }
  22. // Canonicalize transforms the input Element into a serialized XML document in canonical form.
  23. func (c *c14N10ExclusiveCanonicalizer) Canonicalize(el *etree.Element) ([]byte, error) {
  24. err := etreeutils.TransformExcC14n(el, c.prefixList)
  25. if err != nil {
  26. return nil, err
  27. }
  28. return canonicalSerialize(el)
  29. }
  30. func (c *c14N10ExclusiveCanonicalizer) Algorithm() AlgorithmID {
  31. return CanonicalXML10ExclusiveAlgorithmId
  32. }
  33. type c14N11Canonicalizer struct{}
  34. // MakeC14N11Canonicalizer constructs an inclusive canonicalizer.
  35. func MakeC14N11Canonicalizer() Canonicalizer {
  36. return &c14N11Canonicalizer{}
  37. }
  38. // Canonicalize transforms the input Element into a serialized XML document in canonical form.
  39. func (c *c14N11Canonicalizer) Canonicalize(el *etree.Element) ([]byte, error) {
  40. scope := make(map[string]struct{})
  41. return canonicalSerialize(canonicalPrep(el, scope))
  42. }
  43. func (c *c14N11Canonicalizer) Algorithm() AlgorithmID {
  44. return CanonicalXML11AlgorithmId
  45. }
  46. type c14N10RecCanonicalizer struct{}
  47. // MakeC14N10RecCanonicalizer constructs an inclusive canonicalizer.
  48. func MakeC14N10RecCanonicalizer() Canonicalizer {
  49. return &c14N10RecCanonicalizer{}
  50. }
  51. // Canonicalize transforms the input Element into a serialized XML document in canonical form.
  52. func (c *c14N10RecCanonicalizer) Canonicalize(el *etree.Element) ([]byte, error) {
  53. scope := make(map[string]struct{})
  54. return canonicalSerialize(canonicalPrep(el, scope))
  55. }
  56. func (c *c14N10RecCanonicalizer) Algorithm() AlgorithmID {
  57. return CanonicalXML10RecAlgorithmId
  58. }
  59. type c14N10CommentCanonicalizer struct{}
  60. // MakeC14N10CommentCanonicalizer constructs an inclusive canonicalizer.
  61. func MakeC14N10CommentCanonicalizer() Canonicalizer {
  62. return &c14N10CommentCanonicalizer{}
  63. }
  64. // Canonicalize transforms the input Element into a serialized XML document in canonical form.
  65. func (c *c14N10CommentCanonicalizer) Canonicalize(el *etree.Element) ([]byte, error) {
  66. scope := make(map[string]struct{})
  67. return canonicalSerialize(canonicalPrep(el, scope))
  68. }
  69. func (c *c14N10CommentCanonicalizer) Algorithm() AlgorithmID {
  70. return CanonicalXML10CommentAlgorithmId
  71. }
  72. func composeAttr(space, key string) string {
  73. if space != "" {
  74. return space + ":" + key
  75. }
  76. return key
  77. }
  78. type c14nSpace struct {
  79. a etree.Attr
  80. used bool
  81. }
  82. const nsSpace = "xmlns"
  83. // canonicalPrep accepts an *etree.Element and transforms it into one which is ready
  84. // for serialization into inclusive canonical form. Specifically this
  85. // entails:
  86. //
  87. // 1. Stripping re-declarations of namespaces
  88. // 2. Sorting attributes into canonical order
  89. //
  90. // Inclusive canonicalization does not strip unused namespaces.
  91. //
  92. // TODO(russell_h): This is very similar to excCanonicalPrep - perhaps they should
  93. // be unified into one parameterized function?
  94. func canonicalPrep(el *etree.Element, seenSoFar map[string]struct{}) *etree.Element {
  95. _seenSoFar := make(map[string]struct{})
  96. for k, v := range seenSoFar {
  97. _seenSoFar[k] = v
  98. }
  99. ne := el.Copy()
  100. sort.Sort(etreeutils.SortedAttrs(ne.Attr))
  101. if len(ne.Attr) != 0 {
  102. for _, attr := range ne.Attr {
  103. if attr.Space != nsSpace {
  104. continue
  105. }
  106. key := attr.Space + ":" + attr.Key
  107. if _, seen := _seenSoFar[key]; seen {
  108. ne.RemoveAttr(attr.Space + ":" + attr.Key)
  109. } else {
  110. _seenSoFar[key] = struct{}{}
  111. }
  112. }
  113. }
  114. for i, token := range ne.Child {
  115. childElement, ok := token.(*etree.Element)
  116. if ok {
  117. ne.Child[i] = canonicalPrep(childElement, _seenSoFar)
  118. }
  119. }
  120. return ne
  121. }
  122. func canonicalSerialize(el *etree.Element) ([]byte, error) {
  123. doc := etree.NewDocument()
  124. doc.SetRoot(el.Copy())
  125. doc.WriteSettings = etree.WriteSettings{
  126. CanonicalAttrVal: true,
  127. CanonicalEndTags: true,
  128. CanonicalText: true,
  129. }
  130. return doc.WriteToBytes()
  131. }