Codebase list golang-github-russellhaering-goxmldsig / 9ae03d0
New upstream version 0.0~git20180430.7acd5e4 Christian Barcenas 4 years ago
12 changed file(s) with 268 addition(s) and 47 deletion(s). Raw diff Collapse all Expand all
0 *.test
00 language: go
11
22 go:
3 - 1.5
4 - 1.6
5 - tip
3 - "1.6.x"
4 - "1.7.x"
5 - "1.8.x"
6 - "1.9.x"
7 - "1.10.x"
8 - master
5353
5454 func (c *c14N11Canonicalizer) Algorithm() AlgorithmID {
5555 return CanonicalXML11AlgorithmId
56 }
57
58 type c14N10RecCanonicalizer struct{}
59
60 // MakeC14N10RecCanonicalizer constructs an inclusive canonicalizer.
61 func MakeC14N10RecCanonicalizer() Canonicalizer {
62 return &c14N10RecCanonicalizer{}
63 }
64
65 // Canonicalize transforms the input Element into a serialized XML document in canonical form.
66 func (c *c14N10RecCanonicalizer) Canonicalize(el *etree.Element) ([]byte, error) {
67 scope := make(map[string]struct{})
68 return canonicalSerialize(canonicalPrep(el, scope))
69 }
70
71 func (c *c14N10RecCanonicalizer) Algorithm() AlgorithmID {
72 return CanonicalXML10RecAlgorithmId
73 }
74
75 type c14N10CommentCanonicalizer struct{}
76
77 // MakeC14N10CommentCanonicalizer constructs an inclusive canonicalizer.
78 func MakeC14N10CommentCanonicalizer() Canonicalizer {
79 return &c14N10CommentCanonicalizer{}
80 }
81
82 // Canonicalize transforms the input Element into a serialized XML document in canonical form.
83 func (c *c14N10CommentCanonicalizer) Canonicalize(el *etree.Element) ([]byte, error) {
84 scope := make(map[string]struct{})
85 return canonicalSerialize(canonicalPrep(el, scope))
86 }
87
88 func (c *c14N10CommentCanonicalizer) Algorithm() AlgorithmID {
89 return CanonicalXML10CommentAlgorithmId
5690 }
5791
5892 func composeAttr(space, key string) string {
1515 prefixSet[prefix] = struct{}{}
1616 }
1717
18 err := transformExcC14n(DefaultNSContext, EmptyNSContext, el, prefixSet)
18 err := transformExcC14n(DefaultNSContext, DefaultNSContext, el, prefixSet)
1919 if err != nil {
2020 return err
2121 }
265265 // returned by NSFindIterate.
266266 func NSFindIterateCtx(ctx NSContext, el *etree.Element, namespace, tag string, handle NSIterHandler) error {
267267 err := NSTraverse(ctx, el, func(ctx NSContext, el *etree.Element) error {
268 currentNS, err := ctx.LookupPrefix(el.Space)
268 _ctx, err := ctx.SubContext(el)
269 if err != nil {
270 return err
271 }
272
273 currentNS, err := _ctx.LookupPrefix(el.Space)
269274 if err != nil {
270275 return err
271276 }
302307 })
303308
304309 if err != nil {
310 return nil, err
311 }
312
313 return found, nil
314 }
315
316 // NSIterateChildren iterates the children of an element, invoking the passed
317 // handler with each direct child of the element, and the context surrounding
318 // that child.
319 func NSIterateChildren(ctx NSContext, el *etree.Element, handle NSIterHandler) error {
320 ctx, err := ctx.SubContext(el)
321 if err != nil {
322 return err
323 }
324
325 // Iterate the child elements.
326 for _, child := range el.ChildElements() {
327 err = handle(ctx, child)
328 if err != nil {
329 return err
330 }
331 }
332
333 return nil
334 }
335
336 // NSFindIterateChildrenCtx takes an element and its surrounding context, and iterates
337 // the children of that element searching for an element matching the passed namespace
338 // and tag. For each such element that is found, handle is invoked with the matched
339 // element and its own surrounding context.
340 func NSFindChildrenIterateCtx(ctx NSContext, el *etree.Element, namespace, tag string, handle NSIterHandler) error {
341 err := NSIterateChildren(ctx, el, func(ctx NSContext, el *etree.Element) error {
342 _ctx, err := ctx.SubContext(el)
343 if err != nil {
344 return err
345 }
346
347 currentNS, err := _ctx.LookupPrefix(el.Space)
348 if err != nil {
349 return err
350 }
351
352 // Base case, el is the sought after element.
353 if currentNS == namespace && el.Tag == tag {
354 return handle(ctx, el)
355 }
356
357 return nil
358 })
359
360 if err != nil && err != ErrTraversalHalted {
361 return err
362 }
363
364 return nil
365 }
366
367 // NSFindOneChild behaves identically to NSFindOneChildCtx, but uses
368 // DefaultNSContext for context.
369 func NSFindOneChild(el *etree.Element, namespace, tag string) (*etree.Element, error) {
370 return NSFindOneChildCtx(DefaultNSContext, el, namespace, tag)
371 }
372
373 // NSFindOneCtx conducts a depth-first search for the specified element. If such an
374 // element is found a reference to it is returned.
375 func NSFindOneChildCtx(ctx NSContext, el *etree.Element, namespace, tag string) (*etree.Element, error) {
376 var found *etree.Element
377
378 err := NSFindChildrenIterateCtx(ctx, el, namespace, tag, func(ctx NSContext, el *etree.Element) error {
379 found = el
380 return ErrTraversalHalted
381 })
382
383 if err != nil && err != ErrTraversalHalted {
305384 return nil, err
306385 }
307386
99
1010 type X509KeyStore interface {
1111 GetKeyPair() (privateKey *rsa.PrivateKey, cert []byte, err error)
12 }
13
14 type X509ChainStore interface {
15 GetChain() (certs [][]byte, err error)
1216 }
1317
1418 type X509CertificateStore interface {
5858 }
5959
6060 func (ctx *SigningContext) constructSignedInfo(el *etree.Element, enveloped bool) (*etree.Element, error) {
61 digestAlgorithmIdentifier, ok := digestAlgorithmIdentifiers[ctx.Hash]
62 if !ok {
61 digestAlgorithmIdentifier := ctx.GetDigestAlgorithmIdentifier()
62 if digestAlgorithmIdentifier == "" {
6363 return nil, errors.New("unsupported hash mechanism")
6464 }
6565
66 signatureMethodIdentifier, ok := signatureMethodIdentifiers[ctx.Hash]
67 if !ok {
66 signatureMethodIdentifier := ctx.GetSignatureMethodIdentifier()
67 if signatureMethodIdentifier == "" {
6868 return nil, errors.New("unsupported signature method")
6969 }
7070
175175 return nil, err
176176 }
177177
178 certs := [][]byte{cert}
179 if cs, ok := ctx.KeyStore.(X509ChainStore); ok {
180 certs, err = cs.GetChain()
181 if err != nil {
182 return nil, err
183 }
184 }
185
178186 rawSignature, err := rsa.SignPKCS1v15(rand.Reader, key, ctx.Hash, digest)
179187 if err != nil {
180188 return nil, err
185193
186194 keyInfo := ctx.createNamespacedElement(sig, KeyInfoTag)
187195 x509Data := ctx.createNamespacedElement(keyInfo, X509DataTag)
188 x509Certificate := ctx.createNamespacedElement(x509Data, X509CertificateTag)
189 x509Certificate.SetText(base64.StdEncoding.EncodeToString(cert))
196 for _, cert := range certs {
197 x509Certificate := ctx.createNamespacedElement(x509Data, X509CertificateTag)
198 x509Certificate.SetText(base64.StdEncoding.EncodeToString(cert))
199 }
190200
191201 return sig, nil
192202 }
208218
209219 return ret, nil
210220 }
221
222 func (ctx *SigningContext) GetSignatureMethodIdentifier() string {
223 if ident, ok := signatureMethodIdentifiers[ctx.Hash]; ok {
224 return ident
225 }
226 return ""
227 }
228
229 func (ctx *SigningContext) GetDigestAlgorithmIdentifier() string {
230 if ident, ok := digestAlgorithmIdentifiers[ctx.Hash]; ok {
231 return ident
232 }
233 return ""
234 }
235
236 // Useful for signing query string (including DEFLATED AuthnRequest) when
237 // using HTTP-Redirect to make a signed request.
238 // See 3.4.4.1 DEFLATE Encoding of https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf
239 func (ctx *SigningContext) SignString(content string) ([]byte, error) {
240 hash := ctx.Hash.New()
241 if ln, err := hash.Write([]byte(content)); err != nil {
242 return nil, fmt.Errorf("error calculating hash: %v", err)
243 } else if ln < 1 {
244 return nil, fmt.Errorf("zero length hash")
245 }
246 digest := hash.Sum(nil)
247
248 var signature []byte
249 if key, _, err := ctx.KeyStore.GetKeyPair(); err != nil {
250 return nil, fmt.Errorf("unable to fetch key for signing: %v", err)
251 } else if signature, err = rsa.SignPKCS1v15(rand.Reader, key, ctx.Hash, digest); err != nil {
252 return nil, fmt.Errorf("error signing: %v", err)
253 }
254 return signature, nil
255 }
55 "testing"
66
77 "github.com/beevik/etree"
8 "github.com/satori/go.uuid"
98 "github.com/stretchr/testify/require"
109 )
1110
1716 Space: "samlp",
1817 Tag: "AuthnRequest",
1918 }
20 id := "_" + uuid.NewV4().String()
19 id := "_97e34c50-65ec-4132-8b39-02933960a96a"
2120 authnRequest.CreateAttr("ID", id)
2221 hash := crypto.SHA256.New()
2322 canonicalized, err := ctx.Canonicalizer.Canonicalize(authnRequest)
126125 Tag: "Bar",
127126 }
128127
129 id := "_" + uuid.NewV4().String()
128 id := "_97e34c50-65ec-4132-8b39-02933960a96b"
130129
131130 signable.CreateAttr("OtherID", id)
132131 signed, err := ctx.SignEnveloped(signable)
3131
3232 return pk, crt, nil
3333 }
34
35 //GetChain impliments X509ChainStore using the underlying tls.Certificate
36 func (d TLSCertKeyStore) GetChain() ([][]byte, error) {
37 return d.Certificate, nil
38 }
6262 }
6363
6464 type X509Data struct {
65 XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# X509Data"`
66 X509Certificate X509Certificate `xml:"X509Certificate"`
65 XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# X509Data"`
66 X509Certificates []X509Certificate `xml:"X509Certificate"`
6767 }
6868
6969 type X509Certificate struct {
5858 }
5959 }
6060
61 // The RemoveElement method on etree.Element isn't recursive...
62 func recursivelyRemoveElement(tree, el *etree.Element) bool {
63 if tree.RemoveChild(el) != nil {
61 func mapPathToElement(tree, el *etree.Element) []int {
62 for i, child := range tree.Child {
63 if child == el {
64 return []int{i}
65 }
66 }
67
68 for i, child := range tree.Child {
69 if childElement, ok := child.(*etree.Element); ok {
70 childPath := mapPathToElement(childElement, el)
71 if childElement != nil {
72 return append([]int{i}, childPath...)
73 }
74 }
75 }
76
77 return nil
78 }
79
80 func removeElementAtPath(el *etree.Element, path []int) bool {
81 if len(path) == 0 {
82 return false
83 }
84
85 if len(el.Child) <= path[0] {
86 return false
87 }
88
89 childElement, ok := el.Child[path[0]].(*etree.Element)
90 if !ok {
91 return false
92 }
93
94 if len(path) == 1 {
95 el.RemoveChild(childElement)
6496 return true
6597 }
6698
67 for _, child := range tree.Child {
68 if childElement, ok := child.(*etree.Element); ok {
69 if recursivelyRemoveElement(childElement, el) {
70 return true
71 }
72 }
73 }
74
75 return false
76 }
77
78 // transform applies the passed set of transforms to the specified root element.
99 return removeElementAtPath(childElement, path[1:])
100 }
101
102 // Transform returns a new element equivalent to the passed root el, but with
103 // the set of transformations described by the ref applied.
79104 //
80105 // The functionality of transform is currently very limited and purpose-specific.
81 //
82 // NOTE(russell_h): Ideally this wouldn't mutate the root passed to it, and would
83 // instead return a copy. Unfortunately copying the tree makes it difficult to
84 // correctly locate the signature. I'm opting, for now, to simply mutate the root
85 // parameter.
86106 func (ctx *ValidationContext) transform(
87107 el *etree.Element,
88108 sig *types.Signature,
93113 return nil, nil, errors.New("Expected Enveloped and C14N transforms")
94114 }
95115
116 // map the path to the passed signature relative to the passed root, in
117 // order to enable removal of the signature by an enveloped signature
118 // transform
119 signaturePath := mapPathToElement(el, sig.UnderlyingElement())
120
121 // make a copy of the passed root
122 el = el.Copy()
123
96124 var canonicalizer Canonicalizer
97125
98126 for _, transform := range transforms {
100128
101129 switch AlgorithmID(algo) {
102130 case EnvelopedSignatureAltorithmId:
103 if !recursivelyRemoveElement(el, sig.UnderlyingElement()) {
131 if !removeElementAtPath(el, signaturePath) {
104132 return nil, nil, errors.New("Error applying canonicalization transform: Signature not found")
105133 }
106134
115143 case CanonicalXML11AlgorithmId:
116144 canonicalizer = MakeC14N11Canonicalizer()
117145
146 case CanonicalXML10RecAlgorithmId:
147 canonicalizer = MakeC14N10RecCanonicalizer()
148
149 case CanonicalXML10CommentAlgorithmId:
150 canonicalizer = MakeC14N10CommentCanonicalizer()
151
118152 default:
119153 return nil, nil, errors.New("Unknown Transform Algorithm: " + algo)
120154 }
150184 func (ctx *ValidationContext) verifySignedInfo(sig *types.Signature, canonicalizer Canonicalizer, signatureMethodId string, cert *x509.Certificate, decodedSignature []byte) error {
151185 signatureElement := sig.UnderlyingElement()
152186
153 signedInfo := signatureElement.FindElement(childPath(signatureElement.Space, SignedInfoTag))
187 nsCtx, err := etreeutils.NSBuildParentContext(signatureElement)
188 if err != nil {
189 return err
190 }
191
192 signedInfo, err := etreeutils.NSFindOneChildCtx(nsCtx, signatureElement, Namespace, SignedInfoTag)
193 if err != nil {
194 return err
195 }
196
154197 if signedInfo == nil {
155198 return errors.New("Missing SignedInfo")
156199 }
265308 err := etreeutils.NSFindIterate(el, Namespace, SignatureTag, func(ctx etreeutils.NSContext, el *etree.Element) error {
266309
267310 found := false
268 err := etreeutils.NSFindIterateCtx(ctx, el, Namespace, SignedInfoTag,
311 err := etreeutils.NSFindChildrenIterateCtx(ctx, el, Namespace, SignedInfoTag,
269312 func(ctx etreeutils.NSContext, signedInfo *etree.Element) error {
270 // Ignore any SignedInfo that isn't an immediate descendent of Signature.
271 if signedInfo.Parent() != el {
272 return nil
273 }
274
275313 detachedSignedInfo, err := etreeutils.NSDetatch(ctx, signedInfo)
276314 if err != nil {
277315 return err
278316 }
279317
280 c14NMethod := detachedSignedInfo.FindElement(childPath(detachedSignedInfo.Space, CanonicalizationMethodTag))
318 c14NMethod, err := etreeutils.NSFindOneChildCtx(ctx, detachedSignedInfo, Namespace, CanonicalizationMethodTag)
319 if err != nil {
320 return err
321 }
322
281323 if c14NMethod == nil {
282324 return errors.New("missing CanonicalizationMethod on Signature")
283325 }
300342 canonicalSignedInfo = detachedSignedInfo
301343
302344 case CanonicalXML11AlgorithmId:
345 canonicalSignedInfo = canonicalPrep(detachedSignedInfo, map[string]struct{}{})
346
347 case CanonicalXML10RecAlgorithmId:
348 canonicalSignedInfo = canonicalPrep(detachedSignedInfo, map[string]struct{}{})
349
350 case CanonicalXML10CommentAlgorithmId:
303351 canonicalSignedInfo = canonicalPrep(detachedSignedInfo, map[string]struct{}{})
304352
305353 default:
363411
364412 if sig.KeyInfo != nil {
365413 // If the Signature includes KeyInfo, extract the certificate from there
366 if sig.KeyInfo.X509Data.X509Certificate.Data == "" {
414 if len(sig.KeyInfo.X509Data.X509Certificates) == 0 || sig.KeyInfo.X509Data.X509Certificates[0].Data == "" {
367415 return nil, errors.New("missing X509Certificate within KeyInfo")
368416 }
369417
370418 certData, err := base64.StdEncoding.DecodeString(
371 whiteSpace.ReplaceAllString(sig.KeyInfo.X509Data.X509Certificate.Data, ""))
419 whiteSpace.ReplaceAllString(sig.KeyInfo.X509Data.X509Certificates[0].Data, ""))
372420 if err != nil {
373421 return nil, errors.New("Failed to parse certificate")
374422 }
4949 CanonicalXML10ExclusiveAlgorithmId AlgorithmID = "http://www.w3.org/2001/10/xml-exc-c14n#"
5050 CanonicalXML11AlgorithmId AlgorithmID = "http://www.w3.org/2006/12/xml-c14n11"
5151
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"
54
5255 EnvelopedSignatureAltorithmId AlgorithmID = "http://www.w3.org/2000/09/xmldsig#enveloped-signature"
5356 )
5457