Import upstream version 1.2.0
Debian Janitor
2 years ago
0 | on: [push, pull_request] | |
1 | name: Test | |
2 | jobs: | |
3 | test: | |
4 | name: Test Go ${{ matrix.go }} | |
5 | runs-on: ubuntu-20.04 | |
6 | strategy: | |
7 | matrix: | |
8 | go: ['1.17', '1.16'] | |
9 | steps: | |
10 | - uses: actions/checkout@v2 | |
11 | - name: Setup Go | |
12 | uses: actions/setup-go@v2 | |
13 | with: | |
14 | go-version: ${{ matrix.go }} | |
15 | - name: Test | |
16 | run: go test ./... |
0 | 0 | # goxmldsig |
1 | 1 | |
2 | [![Build Status](https://travis-ci.org/russellhaering/goxmldsig.svg?branch=master)](https://travis-ci.org/russellhaering/goxmldsig) | |
2 | ![Build Status](https://github.com/russellhaering/goxmldsig/actions/workflows/test.yml/badge.svg?branch=main) | |
3 | 3 | [![GoDoc](https://godoc.org/github.com/russellhaering/goxmldsig?status.svg)](https://godoc.org/github.com/russellhaering/goxmldsig) |
4 | 4 | |
5 | 5 | XML Digital Signatures implemented in pure Go. |
25 | 25 | |
26 | 26 | func (c *NullCanonicalizer) Canonicalize(el *etree.Element) ([]byte, error) { |
27 | 27 | scope := make(map[string]struct{}) |
28 | return canonicalSerialize(canonicalPrep(el, scope, false)) | |
28 | return canonicalSerialize(canonicalPrep(el, scope, false, true)) | |
29 | 29 | } |
30 | 30 | |
31 | 31 | type c14N10ExclusiveCanonicalizer struct { |
32 | 32 | prefixList string |
33 | comments bool | |
33 | 34 | } |
34 | 35 | |
35 | 36 | // MakeC14N10ExclusiveCanonicalizerWithPrefixList constructs an exclusive Canonicalizer |
37 | 38 | func MakeC14N10ExclusiveCanonicalizerWithPrefixList(prefixList string) Canonicalizer { |
38 | 39 | return &c14N10ExclusiveCanonicalizer{ |
39 | 40 | prefixList: prefixList, |
41 | comments: false, | |
42 | } | |
43 | } | |
44 | ||
45 | // MakeC14N10ExclusiveWithCommentsCanonicalizerWithPrefixList constructs an exclusive Canonicalizer | |
46 | // from a PrefixList in NMTOKENS format (a white space separated list). | |
47 | func MakeC14N10ExclusiveWithCommentsCanonicalizerWithPrefixList(prefixList string) Canonicalizer { | |
48 | return &c14N10ExclusiveCanonicalizer{ | |
49 | prefixList: prefixList, | |
50 | comments: true, | |
40 | 51 | } |
41 | 52 | } |
42 | 53 | |
43 | 54 | // Canonicalize transforms the input Element into a serialized XML document in canonical form. |
44 | 55 | func (c *c14N10ExclusiveCanonicalizer) Canonicalize(el *etree.Element) ([]byte, error) { |
45 | err := etreeutils.TransformExcC14n(el, c.prefixList) | |
56 | err := etreeutils.TransformExcC14n(el, c.prefixList, c.comments) | |
46 | 57 | if err != nil { |
47 | 58 | return nil, err |
48 | 59 | } |
51 | 62 | } |
52 | 63 | |
53 | 64 | func (c *c14N10ExclusiveCanonicalizer) Algorithm() AlgorithmID { |
65 | if c.comments { | |
66 | return CanonicalXML10ExclusiveWithCommentsAlgorithmId | |
67 | } | |
54 | 68 | return CanonicalXML10ExclusiveAlgorithmId |
55 | 69 | } |
56 | 70 | |
57 | type c14N11Canonicalizer struct{} | |
71 | type c14N11Canonicalizer struct { | |
72 | comments bool | |
73 | } | |
58 | 74 | |
59 | 75 | // MakeC14N11Canonicalizer constructs an inclusive canonicalizer. |
60 | 76 | func MakeC14N11Canonicalizer() Canonicalizer { |
61 | return &c14N11Canonicalizer{} | |
77 | return &c14N11Canonicalizer{ | |
78 | comments: false, | |
79 | } | |
80 | } | |
81 | ||
82 | // MakeC14N11WithCommentsCanonicalizer constructs an inclusive canonicalizer. | |
83 | func MakeC14N11WithCommentsCanonicalizer() Canonicalizer { | |
84 | return &c14N11Canonicalizer{ | |
85 | comments: true, | |
86 | } | |
62 | 87 | } |
63 | 88 | |
64 | 89 | // Canonicalize transforms the input Element into a serialized XML document in canonical form. |
65 | 90 | func (c *c14N11Canonicalizer) Canonicalize(el *etree.Element) ([]byte, error) { |
66 | 91 | scope := make(map[string]struct{}) |
67 | return canonicalSerialize(canonicalPrep(el, scope, true)) | |
92 | return canonicalSerialize(canonicalPrep(el, scope, true, c.comments)) | |
68 | 93 | } |
69 | 94 | |
70 | 95 | func (c *c14N11Canonicalizer) Algorithm() AlgorithmID { |
96 | if c.comments { | |
97 | return CanonicalXML11WithCommentsAlgorithmId | |
98 | } | |
71 | 99 | return CanonicalXML11AlgorithmId |
72 | 100 | } |
73 | 101 | |
74 | type c14N10RecCanonicalizer struct{} | |
102 | type c14N10RecCanonicalizer struct { | |
103 | comments bool | |
104 | } | |
75 | 105 | |
76 | 106 | // MakeC14N10RecCanonicalizer constructs an inclusive canonicalizer. |
77 | 107 | func MakeC14N10RecCanonicalizer() Canonicalizer { |
78 | return &c14N10RecCanonicalizer{} | |
108 | return &c14N10RecCanonicalizer{ | |
109 | comments: false, | |
110 | } | |
111 | } | |
112 | ||
113 | // MakeC14N10WithCommentsCanonicalizer constructs an inclusive canonicalizer. | |
114 | func MakeC14N10WithCommentsCanonicalizer() Canonicalizer { | |
115 | return &c14N10RecCanonicalizer{ | |
116 | comments: true, | |
117 | } | |
79 | 118 | } |
80 | 119 | |
81 | 120 | // Canonicalize transforms the input Element into a serialized XML document in canonical form. |
82 | 121 | func (c *c14N10RecCanonicalizer) Canonicalize(el *etree.Element) ([]byte, error) { |
83 | 122 | scope := make(map[string]struct{}) |
84 | return canonicalSerialize(canonicalPrep(el, scope, true)) | |
123 | return canonicalSerialize(canonicalPrep(el, scope, true, c.comments)) | |
85 | 124 | } |
86 | 125 | |
87 | 126 | func (c *c14N10RecCanonicalizer) Algorithm() AlgorithmID { |
127 | if c.comments { | |
128 | return CanonicalXML10WithCommentsAlgorithmId | |
129 | } | |
88 | 130 | return CanonicalXML10RecAlgorithmId |
89 | } | |
90 | ||
91 | type c14N10CommentCanonicalizer struct{} | |
92 | ||
93 | // MakeC14N10CommentCanonicalizer constructs an inclusive canonicalizer. | |
94 | func MakeC14N10CommentCanonicalizer() Canonicalizer { | |
95 | return &c14N10CommentCanonicalizer{} | |
96 | } | |
97 | ||
98 | // Canonicalize transforms the input Element into a serialized XML document in canonical form. | |
99 | func (c *c14N10CommentCanonicalizer) Canonicalize(el *etree.Element) ([]byte, error) { | |
100 | scope := make(map[string]struct{}) | |
101 | return canonicalSerialize(canonicalPrep(el, scope, true)) | |
102 | } | |
103 | ||
104 | func (c *c14N10CommentCanonicalizer) Algorithm() AlgorithmID { | |
105 | return CanonicalXML10CommentAlgorithmId | |
131 | ||
106 | 132 | } |
107 | 133 | |
108 | 134 | func composeAttr(space, key string) string { |
131 | 157 | // |
132 | 158 | // TODO(russell_h): This is very similar to excCanonicalPrep - perhaps they should |
133 | 159 | // be unified into one parameterized function? |
134 | func canonicalPrep(el *etree.Element, seenSoFar map[string]struct{}, strip bool) *etree.Element { | |
160 | func canonicalPrep(el *etree.Element, seenSoFar map[string]struct{}, strip bool, comments bool) *etree.Element { | |
135 | 161 | _seenSoFar := make(map[string]struct{}) |
136 | 162 | for k, v := range seenSoFar { |
137 | 163 | _seenSoFar[k] = v |
139 | 165 | |
140 | 166 | ne := el.Copy() |
141 | 167 | sort.Sort(etreeutils.SortedAttrs(ne.Attr)) |
142 | if len(ne.Attr) != 0 { | |
143 | for _, attr := range ne.Attr { | |
144 | if attr.Space != nsSpace { | |
145 | continue | |
146 | } | |
147 | key := attr.Space + ":" + attr.Key | |
148 | if _, seen := _seenSoFar[key]; seen { | |
149 | ne.RemoveAttr(attr.Space + ":" + attr.Key) | |
168 | n := 0 | |
169 | for _, attr := range ne.Attr { | |
170 | if attr.Space != nsSpace { | |
171 | ne.Attr[n] = attr | |
172 | n++ | |
173 | continue | |
174 | } | |
175 | key := attr.Space + ":" + attr.Key | |
176 | if _, seen := _seenSoFar[key]; !seen { | |
177 | ne.Attr[n] = attr | |
178 | n++ | |
179 | _seenSoFar[key] = struct{}{} | |
180 | } | |
181 | } | |
182 | ne.Attr = ne.Attr[:n] | |
183 | ||
184 | if !comments { | |
185 | c := 0 | |
186 | for c < len(ne.Child) { | |
187 | if _, ok := ne.Child[c].(*etree.Comment); ok { | |
188 | ne.RemoveChildAt(c) | |
150 | 189 | } else { |
151 | _seenSoFar[key] = struct{}{} | |
190 | c++ | |
152 | 191 | } |
153 | 192 | } |
154 | 193 | } |
156 | 195 | for i, token := range ne.Child { |
157 | 196 | childElement, ok := token.(*etree.Element) |
158 | 197 | if ok { |
159 | ne.Child[i] = canonicalPrep(childElement, _seenSoFar, strip) | |
198 | ne.Child[i] = canonicalPrep(childElement, _seenSoFar, strip, comments) | |
160 | 199 | } |
161 | 200 | } |
162 | 201 |
7 | 7 | ) |
8 | 8 | |
9 | 9 | const ( |
10 | assertion = `<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_88a93ebe-abdf-48cd-9ed0-b0dd1b252909" Version="2.0" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="https://saml2.test.astuart.co/sso/saml2" AssertionConsumerServiceIndex="0" AttributeConsumingServiceIndex="0" IssueInstant="2016-04-28T15:37:17" Destination="http://idp.astuart.co/idp/profile/SAML2/Redirect/SSO"><saml:Issuer>https://saml2.test.astuart.co/sso/saml2</saml:Issuer><samlp:NameIDPolicy AllowCreate="true" Format=""/><samlp:RequestedAuthnContext Comparison="exact"><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef></samlp:RequestedAuthnContext></samlp:AuthnRequest>` | |
11 | c14n11 = `<samlp:AuthnRequest xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" AssertionConsumerServiceIndex="0" AssertionConsumerServiceURL="https://saml2.test.astuart.co/sso/saml2" AttributeConsumingServiceIndex="0" Destination="http://idp.astuart.co/idp/profile/SAML2/Redirect/SSO" ID="_88a93ebe-abdf-48cd-9ed0-b0dd1b252909" IssueInstant="2016-04-28T15:37:17" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Version="2.0"><saml:Issuer>https://saml2.test.astuart.co/sso/saml2</saml:Issuer><samlp:NameIDPolicy AllowCreate="true" Format=""></samlp:NameIDPolicy><samlp:RequestedAuthnContext Comparison="exact"><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef></samlp:RequestedAuthnContext></samlp:AuthnRequest>` | |
12 | assertionC14ned = `<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" AssertionConsumerServiceIndex="0" AssertionConsumerServiceURL="https://saml2.test.astuart.co/sso/saml2" AttributeConsumingServiceIndex="0" Destination="http://idp.astuart.co/idp/profile/SAML2/Redirect/SSO" ID="_88a93ebe-abdf-48cd-9ed0-b0dd1b252909" IssueInstant="2016-04-28T15:37:17" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Version="2.0"><saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">https://saml2.test.astuart.co/sso/saml2</saml:Issuer><samlp:NameIDPolicy AllowCreate="true" Format=""></samlp:NameIDPolicy><samlp:RequestedAuthnContext Comparison="exact"><saml:AuthnContextClassRef xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef></samlp:RequestedAuthnContext></samlp:AuthnRequest>` | |
10 | assertion = `<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_88a93ebe-abdf-48cd-9ed0-b0dd1b252909" Version="2.0" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="https://saml2.test.astuart.co/sso/saml2" AssertionConsumerServiceIndex="0" AttributeConsumingServiceIndex="0" IssueInstant="2016-04-28T15:37:17" Destination="http://idp.astuart.co/idp/profile/SAML2/Redirect/SSO"><!-- Some Comment --><saml:Issuer>https://saml2.test.astuart.co/sso/saml2</saml:Issuer><samlp:NameIDPolicy AllowCreate="true" Format=""/><samlp:RequestedAuthnContext Comparison="exact"><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef></samlp:RequestedAuthnContext></samlp:AuthnRequest>` | |
11 | c14n11 = `<samlp:AuthnRequest xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" AssertionConsumerServiceIndex="0" AssertionConsumerServiceURL="https://saml2.test.astuart.co/sso/saml2" AttributeConsumingServiceIndex="0" Destination="http://idp.astuart.co/idp/profile/SAML2/Redirect/SSO" ID="_88a93ebe-abdf-48cd-9ed0-b0dd1b252909" IssueInstant="2016-04-28T15:37:17" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Version="2.0"><saml:Issuer>https://saml2.test.astuart.co/sso/saml2</saml:Issuer><samlp:NameIDPolicy AllowCreate="true" Format=""></samlp:NameIDPolicy><samlp:RequestedAuthnContext Comparison="exact"><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef></samlp:RequestedAuthnContext></samlp:AuthnRequest>` | |
12 | assertionC14ned = `<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" AssertionConsumerServiceIndex="0" AssertionConsumerServiceURL="https://saml2.test.astuart.co/sso/saml2" AttributeConsumingServiceIndex="0" Destination="http://idp.astuart.co/idp/profile/SAML2/Redirect/SSO" ID="_88a93ebe-abdf-48cd-9ed0-b0dd1b252909" IssueInstant="2016-04-28T15:37:17" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Version="2.0"><saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">https://saml2.test.astuart.co/sso/saml2</saml:Issuer><samlp:NameIDPolicy AllowCreate="true" Format=""></samlp:NameIDPolicy><samlp:RequestedAuthnContext Comparison="exact"><saml:AuthnContextClassRef xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef></samlp:RequestedAuthnContext></samlp:AuthnRequest>` | |
13 | c14n11Comment = `<samlp:AuthnRequest xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" AssertionConsumerServiceIndex="0" AssertionConsumerServiceURL="https://saml2.test.astuart.co/sso/saml2" AttributeConsumingServiceIndex="0" Destination="http://idp.astuart.co/idp/profile/SAML2/Redirect/SSO" ID="_88a93ebe-abdf-48cd-9ed0-b0dd1b252909" IssueInstant="2016-04-28T15:37:17" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Version="2.0"><!-- Some Comment --><saml:Issuer>https://saml2.test.astuart.co/sso/saml2</saml:Issuer><samlp:NameIDPolicy AllowCreate="true" Format=""></samlp:NameIDPolicy><samlp:RequestedAuthnContext Comparison="exact"><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef></samlp:RequestedAuthnContext></samlp:AuthnRequest>` | |
14 | assertionC14nedComment = `<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" AssertionConsumerServiceIndex="0" AssertionConsumerServiceURL="https://saml2.test.astuart.co/sso/saml2" AttributeConsumingServiceIndex="0" Destination="http://idp.astuart.co/idp/profile/SAML2/Redirect/SSO" ID="_88a93ebe-abdf-48cd-9ed0-b0dd1b252909" IssueInstant="2016-04-28T15:37:17" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Version="2.0"><!-- Some Comment --><saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">https://saml2.test.astuart.co/sso/saml2</saml:Issuer><samlp:NameIDPolicy AllowCreate="true" Format=""></samlp:NameIDPolicy><samlp:RequestedAuthnContext Comparison="exact"><saml:AuthnContextClassRef xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef></samlp:RequestedAuthnContext></samlp:AuthnRequest>` | |
13 | 15 | ) |
14 | 16 | |
15 | 17 | const ( |
32 | 34 | runCanonicalizationTest(t, MakeC14N10ExclusiveCanonicalizerWithPrefixList(""), assertion, assertionC14ned) |
33 | 35 | } |
34 | 36 | |
37 | func TestExcC14N10WithComments(t *testing.T) { | |
38 | runCanonicalizationTest(t, MakeC14N10ExclusiveWithCommentsCanonicalizerWithPrefixList(""), assertion, assertionC14nedComment) | |
39 | } | |
40 | ||
35 | 41 | func TestC14N11(t *testing.T) { |
36 | 42 | runCanonicalizationTest(t, MakeC14N11Canonicalizer(), assertion, c14n11) |
43 | } | |
44 | ||
45 | func TestC14N11WithComments(t *testing.T) { | |
46 | runCanonicalizationTest(t, MakeC14N11WithCommentsCanonicalizer(), assertion, c14n11Comment) | |
37 | 47 | } |
38 | 48 | |
39 | 49 | func TestXmldocC14N10Exclusive(t *testing.T) { |
42 | 52 | |
43 | 53 | func TestXmldocC14N11(t *testing.T) { |
44 | 54 | runCanonicalizationTest(t, MakeC14N11Canonicalizer(), xmldoc, xmldocC14N11Canonicalized) |
55 | } | |
56 | ||
57 | func TestNestedExcC14N11(t *testing.T) { | |
58 | input := `<X xmlns:x="x" xmlns:y="y"><Y xmlns:x="x" xmlns:y="y" xmlns:z="z"/></X>` | |
59 | expected := `<X xmlns:x="x" xmlns:y="y"><Y xmlns:z="z"></Y></X>` | |
60 | runCanonicalizationTest(t, MakeC14N11Canonicalizer(), input, expected) | |
45 | 61 | } |
46 | 62 | |
47 | 63 | func TestExcC14nDefaultNamespace(t *testing.T) { |
7 | 7 | ) |
8 | 8 | |
9 | 9 | // TransformExcC14n transforms the passed element into xml-exc-c14n form. |
10 | func TransformExcC14n(el *etree.Element, inclusiveNamespacesPrefixList string) error { | |
10 | func TransformExcC14n(el *etree.Element, inclusiveNamespacesPrefixList string, comments bool) error { | |
11 | 11 | prefixes := strings.Fields(inclusiveNamespacesPrefixList) |
12 | 12 | prefixSet := make(map[string]struct{}, len(prefixes)) |
13 | 13 | |
15 | 15 | prefixSet[prefix] = struct{}{} |
16 | 16 | } |
17 | 17 | |
18 | err := transformExcC14n(DefaultNSContext, DefaultNSContext, el, prefixSet) | |
18 | err := transformExcC14n(DefaultNSContext, DefaultNSContext, el, prefixSet, comments) | |
19 | 19 | if err != nil { |
20 | 20 | return err |
21 | 21 | } |
23 | 23 | return nil |
24 | 24 | } |
25 | 25 | |
26 | func transformExcC14n(ctx, declared NSContext, el *etree.Element, inclusiveNamespaces map[string]struct{}) error { | |
26 | func transformExcC14n(ctx, declared NSContext, el *etree.Element, inclusiveNamespaces map[string]struct{}, comments bool) error { | |
27 | 27 | scope, err := ctx.SubContext(el) |
28 | 28 | if err != nil { |
29 | 29 | return err |
85 | 85 | |
86 | 86 | sort.Sort(SortedAttrs(el.Attr)) |
87 | 87 | |
88 | if !comments { | |
89 | c := 0 | |
90 | for c < len(el.Child) { | |
91 | if _, ok := el.Child[c].(*etree.Comment); ok { | |
92 | el.RemoveChildAt(c) | |
93 | } else { | |
94 | c++ | |
95 | } | |
96 | } | |
97 | } | |
98 | ||
88 | 99 | // Transform child elements |
89 | 100 | for _, child := range el.ChildElements() { |
90 | err := transformExcC14n(scope, declared, child, inclusiveNamespaces) | |
101 | err := transformExcC14n(scope, declared, child, inclusiveNamespaces, comments) | |
91 | 102 | if err != nil { |
92 | 103 | return err |
93 | 104 | } |
20 | 20 | // ErrMissingSignature indicates that no enveloped signature was found referencing |
21 | 21 | // the top level element passed for signature verification. |
22 | 22 | ErrMissingSignature = errors.New("Missing signature referencing the top-level element") |
23 | ErrInvalidSignature = errors.New( "Invalid Signature") | |
23 | ErrInvalidSignature = errors.New("Invalid Signature") | |
24 | 24 | ) |
25 | 25 | |
26 | 26 | type ValidationContext struct { |
69 | 69 | for i, child := range tree.Child { |
70 | 70 | if childElement, ok := child.(*etree.Element); ok { |
71 | 71 | childPath := mapPathToElement(childElement, el) |
72 | if childElement != nil { | |
72 | if childPath != nil { | |
73 | 73 | return append([]int{i}, childPath...) |
74 | 74 | } |
75 | 75 | } |
137 | 137 | |
138 | 138 | canonicalizer = MakeC14N10ExclusiveCanonicalizerWithPrefixList(prefixList) |
139 | 139 | |
140 | case CanonicalXML10ExclusiveWithCommentsAlgorithmId: | |
141 | var prefixList string | |
142 | if transform.InclusiveNamespaces != nil { | |
143 | prefixList = transform.InclusiveNamespaces.PrefixList | |
144 | } | |
145 | ||
146 | canonicalizer = MakeC14N10ExclusiveWithCommentsCanonicalizerWithPrefixList(prefixList) | |
147 | ||
140 | 148 | case CanonicalXML11AlgorithmId: |
141 | 149 | canonicalizer = MakeC14N11Canonicalizer() |
142 | 150 | |
151 | case CanonicalXML11WithCommentsAlgorithmId: | |
152 | canonicalizer = MakeC14N11WithCommentsCanonicalizer() | |
153 | ||
143 | 154 | case CanonicalXML10RecAlgorithmId: |
144 | 155 | canonicalizer = MakeC14N10RecCanonicalizer() |
145 | 156 | |
146 | case CanonicalXML10CommentAlgorithmId: | |
147 | canonicalizer = MakeC14N10CommentCanonicalizer() | |
157 | case CanonicalXML10WithCommentsAlgorithmId: | |
158 | canonicalizer = MakeC14N10WithCommentsCanonicalizer() | |
148 | 159 | |
149 | 160 | default: |
150 | 161 | return nil, nil, errors.New("Unknown Transform Algorithm: " + algo) |
352 | 363 | |
353 | 364 | var canonicalSignedInfo *etree.Element |
354 | 365 | |
355 | switch AlgorithmID(c14NAlgorithm) { | |
356 | case CanonicalXML10ExclusiveAlgorithmId: | |
357 | err := etreeutils.TransformExcC14n(detachedSignedInfo, "") | |
366 | switch alg := AlgorithmID(c14NAlgorithm); alg { | |
367 | case CanonicalXML10ExclusiveAlgorithmId, CanonicalXML10ExclusiveWithCommentsAlgorithmId: | |
368 | err := etreeutils.TransformExcC14n(detachedSignedInfo, "", alg == CanonicalXML10ExclusiveWithCommentsAlgorithmId) | |
358 | 369 | if err != nil { |
359 | 370 | return err |
360 | 371 | } |
365 | 376 | // removing of elements below. |
366 | 377 | canonicalSignedInfo = detachedSignedInfo |
367 | 378 | |
368 | case CanonicalXML11AlgorithmId: | |
369 | canonicalSignedInfo = canonicalPrep(detachedSignedInfo, map[string]struct{}{}, true) | |
370 | ||
371 | case CanonicalXML10RecAlgorithmId: | |
372 | canonicalSignedInfo = canonicalPrep(detachedSignedInfo, map[string]struct{}{}, true) | |
373 | ||
374 | case CanonicalXML10CommentAlgorithmId: | |
375 | canonicalSignedInfo = canonicalPrep(detachedSignedInfo, map[string]struct{}{}, true) | |
379 | case CanonicalXML11AlgorithmId, CanonicalXML10RecAlgorithmId: | |
380 | canonicalSignedInfo = canonicalPrep(detachedSignedInfo, map[string]struct{}{}, true, false) | |
381 | ||
382 | case CanonicalXML11WithCommentsAlgorithmId, CanonicalXML10WithCommentsAlgorithmId: | |
383 | canonicalSignedInfo = canonicalPrep(detachedSignedInfo, map[string]struct{}{}, true, true) | |
376 | 384 | |
377 | 385 | default: |
378 | 386 | return fmt.Errorf("invalid CanonicalizationMethod on Signature: %s", c14NAlgorithm) |
6 | 6 | "testing" |
7 | 7 | |
8 | 8 | "github.com/beevik/etree" |
9 | "github.com/russellhaering/goxmldsig/etreeutils" | |
9 | 10 | "github.com/stretchr/testify/require" |
10 | 11 | ) |
11 | 12 | |
248 | 249 | require.NotEmpty(t, el) |
249 | 250 | } |
250 | 251 | |
251 | ||
252 | 252 | func TestValidateWithModified(t *testing.T) { |
253 | 253 | doc := etree.NewDocument() |
254 | 254 | err := doc.ReadFromBytes([]byte(modifiedToBeTodd)) |
267 | 267 | require.Error(t, err) |
268 | 268 | } |
269 | 269 | |
270 | ||
271 | 270 | func TestValidateWithModifiedAndSignatureEdited(t *testing.T) { |
272 | 271 | doc := etree.NewDocument() |
273 | 272 | err := doc.ReadFromBytes([]byte(spoofedAsTodd)) |
286 | 285 | require.Error(t, err) |
287 | 286 | } |
288 | 287 | |
288 | func TestMapPathAndRemove(t *testing.T) { | |
289 | doc := etree.NewDocument() | |
290 | err := doc.ReadFromString(`<X><Y/><Y><RemoveMe xmlns="x"/></Y></X>`) | |
291 | require.NoError(t, err) | |
292 | ||
293 | el, err := etreeutils.NSFindOne(doc.Root(), "x", "RemoveMe") | |
294 | require.NoError(t, err) | |
295 | require.NotNil(t, el) | |
296 | ||
297 | path := mapPathToElement(doc.Root(), el) | |
298 | removed := removeElementAtPath(doc.Root(), path) | |
299 | require.True(t, removed) | |
300 | ||
301 | el, err = etreeutils.NSFindOne(doc.Root(), "x", "RemoveMe") | |
302 | require.NoError(t, err) | |
303 | require.Nil(t, el) | |
304 | } | |
305 | ||
289 | 306 | const ( |
290 | 307 | validExample = `<?xml version="1.0" encoding="UTF-8"?><saml2p:Response Destination="https://dev.sudo.wtf:8443/v1/_saml_callback" ID="id149481635007085371203272055" InResponseTo="_ffea96b1-44a2-4a86-9683-45807984ab5b" IssueInstant="2020-09-01T17:51:12.176Z" Version="2.0" xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:xs="http://www.w3.org/2001/XMLSchema"><saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">http://www.okta.com/exkrfkzzb7NyB3UeP0h7</saml2:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><ds:Reference URI="#id149481635007085371203272055"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="xs" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>LwRDkrPmsTcUa++BIS5VJIANUlZN7zzdtjLfxfLAWds=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>UyjNRj9ZFbhApPhWEuVG26yACVqd25uyRKalSpp6XCdjrqKjI8Fmx7Q/IFkk5M755cxyFCQGttxThR6IPBk4Kp5OG2qGKXNHt7OQ8mumSLqWZpBJbmzNIKyG3nWlFoLVCoWPtBTd2gZM0aHOQp1JKa1birFBp2NofkEXbLeghZQ2YfCc4m8qgpZW5k/Itc0P/TVIkvPInjdSMyjm/ql4FUDO8cMkExJNR/i+GElW8cfnniWGcDPSiOqfIjLEDvZouXC7F1v5Wa0SmIxg7NJUTB+g6yrDN15VDq3KbHHTMlZXOZTXON2mBZOj5cwyyd4uX3aGSmYQiy/CGqBdqxrW2A==</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIDnjCCAoagAwIBAgIGAXHxS90vMA0GCSqGSIb3DQEBCwUAMIGPMQswCQYDVQQGEwJVUzETMBEG |
291 | 308 | A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEU |
46 | 46 | //Well-known signature algorithms |
47 | 47 | const ( |
48 | 48 | // Supported canonicalization algorithms |
49 | CanonicalXML10ExclusiveAlgorithmId AlgorithmID = "http://www.w3.org/2001/10/xml-exc-c14n#" | |
50 | CanonicalXML11AlgorithmId AlgorithmID = "http://www.w3.org/2006/12/xml-c14n11" | |
49 | CanonicalXML10ExclusiveAlgorithmId AlgorithmID = "http://www.w3.org/2001/10/xml-exc-c14n#" | |
50 | CanonicalXML10ExclusiveWithCommentsAlgorithmId AlgorithmID = "http://www.w3.org/2001/10/xml-exc-c14n#WithComments" | |
51 | 51 | |
52 | CanonicalXML10RecAlgorithmId AlgorithmID = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" | |
53 | CanonicalXML10CommentAlgorithmId AlgorithmID = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments" | |
52 | CanonicalXML11AlgorithmId AlgorithmID = "http://www.w3.org/2006/12/xml-c14n11" | |
53 | CanonicalXML11WithCommentsAlgorithmId AlgorithmID = "http://www.w3.org/2006/12/xml-c14n11#WithComments" | |
54 | ||
55 | CanonicalXML10RecAlgorithmId AlgorithmID = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" | |
56 | CanonicalXML10WithCommentsAlgorithmId AlgorithmID = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments" | |
54 | 57 | |
55 | 58 | EnvelopedSignatureAltorithmId AlgorithmID = "http://www.w3.org/2000/09/xmldsig#enveloped-signature" |
56 | 59 | ) |