forked from mystiq/dex
Merge pull request #870 from Calpicow/fix_assertion_fallback
Fix assertion fallback
This commit is contained in:
commit
2a6ae0a6ea
14 changed files with 562 additions and 268 deletions
|
@ -17,6 +17,7 @@ import (
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/beevik/etree"
|
"github.com/beevik/etree"
|
||||||
dsig "github.com/russellhaering/goxmldsig"
|
dsig "github.com/russellhaering/goxmldsig"
|
||||||
|
"github.com/russellhaering/goxmldsig/etreeutils"
|
||||||
|
|
||||||
"github.com/coreos/dex/connector"
|
"github.com/coreos/dex/connector"
|
||||||
)
|
)
|
||||||
|
@ -500,8 +501,9 @@ func verify(validator *dsig.ValidationContext, data []byte) (signed []byte, err
|
||||||
verified = true
|
verified = true
|
||||||
doc.SetRoot(transformedResponse)
|
doc.SetRoot(transformedResponse)
|
||||||
}
|
}
|
||||||
assertion := response.SelectElement("Assertion")
|
// Ensures xmlns are copied down to the assertion element when they are defined in the root
|
||||||
if assertion == nil {
|
assertion, err := etreeutils.NSSelectOne(response, "urn:oasis:names:tc:SAML:2.0:assertion", "Assertion")
|
||||||
|
if err != nil {
|
||||||
return nil, fmt.Errorf("response does not contain an Assertion element")
|
return nil, fmt.Errorf("response does not contain an Assertion element")
|
||||||
}
|
}
|
||||||
transformedAssertion, err := validator.Validate(assertion)
|
transformedAssertion, err := validator.Validate(assertion)
|
||||||
|
|
|
@ -86,6 +86,10 @@ func TestVerify(t *testing.T) {
|
||||||
runVerify(t, "testdata/okta-ca.pem", "testdata/okta-resp.xml", true)
|
runVerify(t, "testdata/okta-ca.pem", "testdata/okta-resp.xml", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestVerifyUnsignedMessageAndSignedAssertionWithRootXmlNs(t *testing.T) {
|
||||||
|
runVerify(t, "testdata/oam-ca.pem", "testdata/oam-resp.xml", true)
|
||||||
|
}
|
||||||
|
|
||||||
func TestVerifySignedMessageAndUnsignedAssertion(t *testing.T) {
|
func TestVerifySignedMessageAndUnsignedAssertion(t *testing.T) {
|
||||||
runVerify(t, "testdata/idp-cert.pem", "testdata/idp-resp-signed-message.xml", true)
|
runVerify(t, "testdata/idp-cert.pem", "testdata/idp-resp-signed-message.xml", true)
|
||||||
}
|
}
|
||||||
|
|
13
connector/saml/testdata/oam-ca.pem
vendored
Normal file
13
connector/saml/testdata/oam-ca.pem
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIB/jCCAWegAwIBAgIBCjANBgkqhkiG9w0BAQQFADAkMSIwIAYDVQQDExlkZWFv
|
||||||
|
YW0tZGV2MDIuanBsLm5hc2EuZ292MB4XDTE2MDYzMDA0NTQxNloXDTI2MDYyODA0
|
||||||
|
NTQxNlowJDEiMCAGA1UEAxMZZGVhb2FtLWRldjAyLmpwbC5uYXNhLmdvdjCBnzAN
|
||||||
|
BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAht1N4lGdwUbl7YRyHwSCrnep6/e2I3+V
|
||||||
|
eue0pSA/DGn8OuR/udM8UCja5utqlqJdq200ox4b4Mpz0Jg9kMckALtKe+1DgeES
|
||||||
|
EIx9FpeuBdHlitYQNSbEr30HIG2nmeTOy4Vi5unBO54um3tNazcUTMA0/LJ6KQL8
|
||||||
|
LeZSlB/IxwUCAwEAAaNAMD4wDAYDVR0TAQH/BAIwADAPBgNVHQ8BAf8EBQMDB9gA
|
||||||
|
MB0GA1UdDgQWBBRYo1YjfrNonauLzj6/AsueWFGSszANBgkqhkiG9w0BAQQFAAOB
|
||||||
|
gQACq7GHK/Zsg0+qC0WWa2ZjmOXE6Dqk/xuooG49QT7ihABs7k9U27Fw3xKF6MkC
|
||||||
|
7pca1FwT82eZK1N3XKKpZe7Flu1fMKt2o/XSiBkDjWwUcChVnwGsUBe8hJFwFqg7
|
||||||
|
olNJn1kaVBJUqZIiXF9kS0d+1H55rStOd0CNXAzp9utr2A==
|
||||||
|
-----END CERTIFICATE-----
|
1
connector/saml/testdata/oam-resp.xml
vendored
Normal file
1
connector/saml/testdata/oam-resp.xml
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:enc="http://www.w3.org/2001/04/xmlenc#" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:x500="urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Destination="http://127.0.0.1:5556/callback" ID="id-IWlPTptSB-PlR80dwt8ZhVeG70mrz7nPvTVrhduK" InResponseTo="_e66b3a98-831c-4c96-5706-b63fe0549624" IssueInstant="2016-12-12T16:54:35Z" Version="2.0"><saml:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://deaoam-dev02.jpl.nasa.gov:14101/oam/fed</saml:Issuer><samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status><saml:Assertion ID="id-rT9rTqxdQC9j34YhVeNayUWC9EbIBgym6gp-MZt-" IssueInstant="2016-12-12T16:54:35Z" Version="2.0"><saml:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://deaoam-dev02.jpl.nasa.gov:14101/oam/fed</saml:Issuer><dsig:Signature><dsig:SignedInfo><dsig:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><dsig:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><dsig:Reference URI="#id-rT9rTqxdQC9j34YhVeNayUWC9EbIBgym6gp-MZt-"><dsig:Transforms><dsig:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><dsig:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><dsig:DigestValue>z1HD/59hv6UOd5+jeG+ihaFWLgI=</dsig:DigestValue></dsig:Reference></dsig:SignedInfo><dsig:SignatureValue>I99oG5kiOfIgbXYa21z/TOmzftTkFnXe9ObhBNSKit9kAhT93apYROqqXv4Ax96P144Ld7ERX1hgJsytK8LC2874Pk7QrSNm4zvW3x0D4GR4lM06CvJK/EhIur3TrCUJDPigvyP7TJitheCyBejwt0x0lqNP/OzR3tMbAIMRoho=</dsig:SignatureValue></dsig:Signature><saml:Subject><saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" NameQualifier="https://deaoam-dev02.jpl.nasa.gov:14101/oam/fed" SPNameQualifier="JSAuth">pkieu</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData InResponseTo="_e66b3a98-831c-4c96-5706-b63fe0549624" NotOnOrAfter="2016-12-12T16:59:35Z" Recipient="http://127.0.0.1:5556/callback"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="2016-12-12T16:54:35Z" NotOnOrAfter="2016-12-12T16:59:35Z"><saml:AudienceRestriction><saml:Audience>JSAuth</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AuthnStatement AuthnInstant="2016-12-12T16:54:10Z" SessionIndex="id-l3NCbxKoBfUZcuKhlotMuIF3ydgYJgGGG6BGTTU6" SessionNotOnOrAfter="2016-12-12T17:54:35Z"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement></saml:Assertion></samlp:Response>
|
7
glide.lock
generated
7
glide.lock
generated
|
@ -1,5 +1,5 @@
|
||||||
hash: 4a3ac3a2a2b39357dc5de7ba296dbe20a97dd2fe13bc15a68e904e69afae2adb
|
hash: ba77a77f03b1750479aa4a61de59d7a545dc8bd5c71be10e1c2e9afed37e1925
|
||||||
updated: 2017-03-21T09:24:08.089284929-07:00
|
updated: 2017-03-24T11:03:21.332291207-07:00
|
||||||
imports:
|
imports:
|
||||||
- name: github.com/beevik/etree
|
- name: github.com/beevik/etree
|
||||||
version: 4cd0dd976db869f817248477718071a28e978df0
|
version: 4cd0dd976db869f817248477718071a28e978df0
|
||||||
|
@ -46,9 +46,10 @@ imports:
|
||||||
subpackages:
|
subpackages:
|
||||||
- cacheobject
|
- cacheobject
|
||||||
- name: github.com/russellhaering/goxmldsig
|
- name: github.com/russellhaering/goxmldsig
|
||||||
version: 51810e925e5fc495822fbddda8202f70a6e4a3f3
|
version: eaac44c63fe007124f8f6255b09febc906784981
|
||||||
subpackages:
|
subpackages:
|
||||||
- etreeutils
|
- etreeutils
|
||||||
|
- types
|
||||||
- name: github.com/Sirupsen/logrus
|
- name: github.com/Sirupsen/logrus
|
||||||
version: d26492970760ca5d33129d2d799e34be5c4782eb
|
version: d26492970760ca5d33129d2d799e34be5c4782eb
|
||||||
- name: github.com/spf13/cobra
|
- name: github.com/spf13/cobra
|
||||||
|
|
|
@ -133,7 +133,7 @@ import:
|
||||||
|
|
||||||
# XML signature validation for SAML connector
|
# XML signature validation for SAML connector
|
||||||
- package: github.com/russellhaering/goxmldsig
|
- package: github.com/russellhaering/goxmldsig
|
||||||
version: 51810e925e5fc495822fbddda8202f70a6e4a3f3
|
version: eaac44c63fe007124f8f6255b09febc906784981
|
||||||
- package: github.com/beevik/etree
|
- package: github.com/beevik/etree
|
||||||
version: 4cd0dd976db869f817248477718071a28e978df0
|
version: 4cd0dd976db869f817248477718071a28e978df0
|
||||||
- package: github.com/jonboulle/clockwork
|
- package: github.com/jonboulle/clockwork
|
||||||
|
|
110
vendor/github.com/russellhaering/goxmldsig/canonicalize.go
generated
vendored
110
vendor/github.com/russellhaering/goxmldsig/canonicalize.go
generated
vendored
|
@ -2,7 +2,6 @@ package dsig
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/beevik/etree"
|
"github.com/beevik/etree"
|
||||||
"github.com/russellhaering/goxmldsig/etreeutils"
|
"github.com/russellhaering/goxmldsig/etreeutils"
|
||||||
|
@ -15,28 +14,25 @@ type Canonicalizer interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type c14N10ExclusiveCanonicalizer struct {
|
type c14N10ExclusiveCanonicalizer struct {
|
||||||
InclusiveNamespaces map[string]struct{}
|
prefixList string
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeC14N10ExclusiveCanonicalizerWithPrefixList constructs an exclusive Canonicalizer
|
// MakeC14N10ExclusiveCanonicalizerWithPrefixList constructs an exclusive Canonicalizer
|
||||||
// from a PrefixList in NMTOKENS format (a white space separated list).
|
// from a PrefixList in NMTOKENS format (a white space separated list).
|
||||||
func MakeC14N10ExclusiveCanonicalizerWithPrefixList(prefixList string) Canonicalizer {
|
func MakeC14N10ExclusiveCanonicalizerWithPrefixList(prefixList string) Canonicalizer {
|
||||||
prefixes := strings.Fields(prefixList)
|
|
||||||
prefixSet := make(map[string]struct{}, len(prefixes))
|
|
||||||
|
|
||||||
for _, prefix := range prefixes {
|
|
||||||
prefixSet[prefix] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &c14N10ExclusiveCanonicalizer{
|
return &c14N10ExclusiveCanonicalizer{
|
||||||
InclusiveNamespaces: prefixSet,
|
prefixList: prefixList,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Canonicalize transforms the input Element into a serialized XML document in canonical form.
|
// Canonicalize transforms the input Element into a serialized XML document in canonical form.
|
||||||
func (c *c14N10ExclusiveCanonicalizer) Canonicalize(el *etree.Element) ([]byte, error) {
|
func (c *c14N10ExclusiveCanonicalizer) Canonicalize(el *etree.Element) ([]byte, error) {
|
||||||
scope := make(map[string]c14nSpace)
|
err := etreeutils.TransformExcC14n(el, c.prefixList)
|
||||||
return canonicalSerialize(excCanonicalPrep(el, scope, c.InclusiveNamespaces))
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return canonicalSerialize(el)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *c14N10ExclusiveCanonicalizer) Algorithm() AlgorithmID {
|
func (c *c14N10ExclusiveCanonicalizer) Algorithm() AlgorithmID {
|
||||||
|
@ -75,94 +71,6 @@ type c14nSpace struct {
|
||||||
|
|
||||||
const nsSpace = "xmlns"
|
const nsSpace = "xmlns"
|
||||||
|
|
||||||
// excCanonicalPrep accepts an *etree.Element and recursively transforms it into one
|
|
||||||
// which is ready for serialization to exclusive canonical form. Specifically this
|
|
||||||
// entails:
|
|
||||||
//
|
|
||||||
// 1. Stripping re-declarations of namespaces
|
|
||||||
// 2. Stripping unused namespaces
|
|
||||||
// 3. Sorting attributes into canonical order.
|
|
||||||
//
|
|
||||||
// NOTE(russell_h): Currently this function modifies the passed element.
|
|
||||||
func excCanonicalPrep(el *etree.Element, _nsAlreadyDeclared map[string]c14nSpace, inclusiveNamespaces map[string]struct{}) *etree.Element {
|
|
||||||
//Copy alreadyDeclared map (only contains namespaces)
|
|
||||||
nsAlreadyDeclared := make(map[string]c14nSpace, len(_nsAlreadyDeclared))
|
|
||||||
for k := range _nsAlreadyDeclared {
|
|
||||||
nsAlreadyDeclared[k] = _nsAlreadyDeclared[k]
|
|
||||||
}
|
|
||||||
|
|
||||||
//Track the namespaces used on the current element
|
|
||||||
nsUsedHere := make(map[string]struct{})
|
|
||||||
|
|
||||||
//Make sure to track the element namespace for the case:
|
|
||||||
//<foo:bar xmlns:foo="..."/>
|
|
||||||
if el.Space != "" {
|
|
||||||
nsUsedHere[el.Space] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
toRemove := make([]string, 0, 0)
|
|
||||||
|
|
||||||
for _, a := range el.Attr {
|
|
||||||
switch a.Space {
|
|
||||||
case nsSpace:
|
|
||||||
|
|
||||||
//For simplicity, remove all xmlns attribues; to be added in one pass
|
|
||||||
//later. Otherwise, we need another map/set to track xmlns attributes
|
|
||||||
//that we left alone.
|
|
||||||
toRemove = append(toRemove, a.Space+":"+a.Key)
|
|
||||||
if _, ok := nsAlreadyDeclared[a.Key]; !ok {
|
|
||||||
//If we're not tracking ancestor state already for this namespace, add
|
|
||||||
//it to the map
|
|
||||||
nsAlreadyDeclared[a.Key] = c14nSpace{a: a, used: false}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This algorithm accepts a set of namespaces which should be treated
|
|
||||||
// in an inclusive fashion. Specifically that means we should keep the
|
|
||||||
// declaration of that namespace closest to the root of the tree. We can
|
|
||||||
// accomplish that be pretending it was used by this element.
|
|
||||||
_, inclusive := inclusiveNamespaces[a.Key]
|
|
||||||
if inclusive {
|
|
||||||
nsUsedHere[a.Key] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
//We only track namespaces, so ignore attributes without one.
|
|
||||||
if a.Space != "" {
|
|
||||||
nsUsedHere[a.Space] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Remove all attributes so that we can add them with much-simpler logic
|
|
||||||
for _, attrK := range toRemove {
|
|
||||||
el.RemoveAttr(attrK)
|
|
||||||
}
|
|
||||||
|
|
||||||
//For all namespaces used on the current element, declare them if they were
|
|
||||||
//not declared (and used) in an ancestor.
|
|
||||||
for k := range nsUsedHere {
|
|
||||||
spc := nsAlreadyDeclared[k]
|
|
||||||
//If previously unused, mark as used
|
|
||||||
if !spc.used {
|
|
||||||
el.Attr = append(el.Attr, spc.a)
|
|
||||||
spc.used = true
|
|
||||||
|
|
||||||
//Assignment here is only to update the pre-existing `used` tracking value
|
|
||||||
nsAlreadyDeclared[k] = spc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Canonicalize all children, passing down the ancestor tracking map
|
|
||||||
for _, child := range el.ChildElements() {
|
|
||||||
excCanonicalPrep(child, nsAlreadyDeclared, inclusiveNamespaces)
|
|
||||||
}
|
|
||||||
|
|
||||||
//Sort attributes lexicographically
|
|
||||||
sort.Sort(etreeutils.SortedAttrs(el.Attr))
|
|
||||||
|
|
||||||
return el.Copy()
|
|
||||||
}
|
|
||||||
|
|
||||||
// canonicalPrep accepts an *etree.Element and transforms it into one which is ready
|
// canonicalPrep accepts an *etree.Element and transforms it into one which is ready
|
||||||
// for serialization into inclusive canonical form. Specifically this
|
// for serialization into inclusive canonical form. Specifically this
|
||||||
// entails:
|
// entails:
|
||||||
|
@ -208,7 +116,7 @@ func canonicalPrep(el *etree.Element, seenSoFar map[string]struct{}) *etree.Elem
|
||||||
|
|
||||||
func canonicalSerialize(el *etree.Element) ([]byte, error) {
|
func canonicalSerialize(el *etree.Element) ([]byte, error) {
|
||||||
doc := etree.NewDocument()
|
doc := etree.NewDocument()
|
||||||
doc.SetRoot(el)
|
doc.SetRoot(el.Copy())
|
||||||
|
|
||||||
doc.WriteSettings = etree.WriteSettings{
|
doc.WriteSettings = etree.WriteSettings{
|
||||||
CanonicalAttrVal: true,
|
CanonicalAttrVal: true,
|
||||||
|
|
98
vendor/github.com/russellhaering/goxmldsig/etreeutils/canonicalize.go
generated
vendored
Normal file
98
vendor/github.com/russellhaering/goxmldsig/etreeutils/canonicalize.go
generated
vendored
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
package etreeutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/beevik/etree"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TransformExcC14n transforms the passed element into xml-exc-c14n form.
|
||||||
|
func TransformExcC14n(el *etree.Element, inclusiveNamespacesPrefixList string) error {
|
||||||
|
prefixes := strings.Fields(inclusiveNamespacesPrefixList)
|
||||||
|
prefixSet := make(map[string]struct{}, len(prefixes))
|
||||||
|
|
||||||
|
for _, prefix := range prefixes {
|
||||||
|
prefixSet[prefix] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := transformExcC14n(DefaultNSContext, EmptyNSContext, el, prefixSet)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func transformExcC14n(ctx, declared NSContext, el *etree.Element, inclusiveNamespaces map[string]struct{}) error {
|
||||||
|
scope, err := ctx.SubContext(el)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
visiblyUtilizedPrefixes := map[string]struct{}{
|
||||||
|
el.Space: struct{}{},
|
||||||
|
}
|
||||||
|
|
||||||
|
filteredAttrs := []etree.Attr{}
|
||||||
|
|
||||||
|
// Filter out all namespace declarations
|
||||||
|
for _, attr := range el.Attr {
|
||||||
|
switch {
|
||||||
|
case attr.Space == xmlnsPrefix:
|
||||||
|
if _, ok := inclusiveNamespaces[attr.Key]; ok {
|
||||||
|
visiblyUtilizedPrefixes[attr.Key] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
case attr.Space == defaultPrefix && attr.Key == xmlnsPrefix:
|
||||||
|
if _, ok := inclusiveNamespaces[defaultPrefix]; ok {
|
||||||
|
visiblyUtilizedPrefixes[defaultPrefix] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
if attr.Space != defaultPrefix {
|
||||||
|
visiblyUtilizedPrefixes[attr.Space] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
filteredAttrs = append(filteredAttrs, attr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
el.Attr = filteredAttrs
|
||||||
|
|
||||||
|
declared = declared.Copy()
|
||||||
|
|
||||||
|
// Declare all visibly utilized prefixes that are in-scope but haven't
|
||||||
|
// been declared in the canonicalized form yet. These might have been
|
||||||
|
// declared on this element but then filtered out above, or they might
|
||||||
|
// have been declared on an ancestor (before canonicalization) which
|
||||||
|
// didn't visibly utilize and thus had them removed.
|
||||||
|
for prefix := range visiblyUtilizedPrefixes {
|
||||||
|
// Skip redundant declarations - they have to already have the same
|
||||||
|
// value.
|
||||||
|
if declaredNamespace, ok := declared.prefixes[prefix]; ok {
|
||||||
|
if value, ok := scope.prefixes[prefix]; ok && declaredNamespace == value {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace, err := scope.LookupPrefix(prefix)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
el.Attr = append(el.Attr, declared.declare(prefix, namespace))
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(SortedAttrs(el.Attr))
|
||||||
|
|
||||||
|
// Transform child elements
|
||||||
|
for _, child := range el.ChildElements() {
|
||||||
|
err := transformExcC14n(scope, declared, child, inclusiveNamespaces)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
138
vendor/github.com/russellhaering/goxmldsig/etreeutils/namespace.go
generated
vendored
138
vendor/github.com/russellhaering/goxmldsig/etreeutils/namespace.go
generated
vendored
|
@ -47,13 +47,38 @@ type NSContext struct {
|
||||||
prefixes map[string]string
|
prefixes map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx NSContext) SubContext(el *etree.Element) (NSContext, error) {
|
func (ctx NSContext) Copy() NSContext {
|
||||||
// The subcontext should inherit existing declared prefixes
|
|
||||||
prefixes := make(map[string]string, len(ctx.prefixes)+4)
|
prefixes := make(map[string]string, len(ctx.prefixes)+4)
|
||||||
for k, v := range ctx.prefixes {
|
for k, v := range ctx.prefixes {
|
||||||
prefixes[k] = v
|
prefixes[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return NSContext{prefixes: prefixes}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx NSContext) declare(prefix, namespace string) etree.Attr {
|
||||||
|
ctx.prefixes[prefix] = namespace
|
||||||
|
|
||||||
|
switch prefix {
|
||||||
|
case defaultPrefix:
|
||||||
|
return etree.Attr{
|
||||||
|
Key: xmlnsPrefix,
|
||||||
|
Value: namespace,
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return etree.Attr{
|
||||||
|
Space: xmlnsPrefix,
|
||||||
|
Key: prefix,
|
||||||
|
Value: namespace,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx NSContext) SubContext(el *etree.Element) (NSContext, error) {
|
||||||
|
// The subcontext should inherit existing declared prefixes
|
||||||
|
newCtx := ctx.Copy()
|
||||||
|
|
||||||
// Merge new namespace declarations on top of existing ones.
|
// Merge new namespace declarations on top of existing ones.
|
||||||
for _, attr := range el.Attr {
|
for _, attr := range el.Attr {
|
||||||
if attr.Space == xmlnsPrefix {
|
if attr.Space == xmlnsPrefix {
|
||||||
|
@ -69,7 +94,7 @@ func (ctx NSContext) SubContext(el *etree.Element) (NSContext, error) {
|
||||||
return ctx, ErrReservedNamespace
|
return ctx, ErrReservedNamespace
|
||||||
}
|
}
|
||||||
|
|
||||||
prefixes[attr.Key] = attr.Value
|
newCtx.declare(attr.Key, attr.Value)
|
||||||
} else if attr.Space == defaultPrefix && attr.Key == xmlnsPrefix {
|
} else if attr.Space == defaultPrefix && attr.Key == xmlnsPrefix {
|
||||||
// This attribute is a default namespace declaration
|
// This attribute is a default namespace declaration
|
||||||
|
|
||||||
|
@ -78,11 +103,21 @@ func (ctx NSContext) SubContext(el *etree.Element) (NSContext, error) {
|
||||||
return ctx, ErrInvalidDefaultNamespace
|
return ctx, ErrInvalidDefaultNamespace
|
||||||
}
|
}
|
||||||
|
|
||||||
prefixes[defaultPrefix] = attr.Value
|
newCtx.declare(defaultPrefix, attr.Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NSContext{prefixes: prefixes}, nil
|
return newCtx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefixes returns a copy of this context's prefix map.
|
||||||
|
func (ctx NSContext) Prefixes() map[string]string {
|
||||||
|
prefixes := make(map[string]string, len(ctx.prefixes))
|
||||||
|
for k, v := range ctx.prefixes {
|
||||||
|
prefixes[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
return prefixes
|
||||||
}
|
}
|
||||||
|
|
||||||
// LookupPrefix attempts to find a declared namespace for the specified prefix. If the prefix
|
// LookupPrefix attempts to find a declared namespace for the specified prefix. If the prefix
|
||||||
|
@ -98,7 +133,13 @@ func (ctx NSContext) LookupPrefix(prefix string) (string, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func nsTraverse(ctx NSContext, el *etree.Element, handle func(NSContext, *etree.Element) error) error {
|
// NSIterHandler is a function which is invoked with a element and its surrounding
|
||||||
|
// NSContext during traversals.
|
||||||
|
type NSIterHandler func(NSContext, *etree.Element) error
|
||||||
|
|
||||||
|
// NSTraverse traverses an element tree, invoking the passed handler for each element
|
||||||
|
// in the tree.
|
||||||
|
func NSTraverse(ctx NSContext, el *etree.Element, handle NSIterHandler) error {
|
||||||
ctx, err := ctx.SubContext(el)
|
ctx, err := ctx.SubContext(el)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -111,7 +152,7 @@ func nsTraverse(ctx NSContext, el *etree.Element, handle func(NSContext, *etree.
|
||||||
|
|
||||||
// Recursively traverse child elements.
|
// Recursively traverse child elements.
|
||||||
for _, child := range el.ChildElements() {
|
for _, child := range el.ChildElements() {
|
||||||
err := nsTraverse(ctx, child, handle)
|
err := NSTraverse(ctx, child, handle)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -120,7 +161,9 @@ func nsTraverse(ctx NSContext, el *etree.Element, handle func(NSContext, *etree.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func detachWithNamespaces(ctx NSContext, el *etree.Element) (*etree.Element, error) {
|
// NSDetatch makes a copy of the passed element, and declares any namespaces in
|
||||||
|
// the passed context onto the new element before returning it.
|
||||||
|
func NSDetatch(ctx NSContext, el *etree.Element) (*etree.Element, error) {
|
||||||
ctx, err := ctx.SubContext(el)
|
ctx, err := ctx.SubContext(el)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -177,43 +220,52 @@ func detachWithNamespaces(ctx NSContext, el *etree.Element) (*etree.Element, err
|
||||||
return el, nil
|
return el, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NSSelectOne conducts a depth-first search for an element with the specified namespace
|
// NSSelectOne behaves identically to NSSelectOneCtx, but uses DefaultNSContext as the
|
||||||
|
// surrounding context.
|
||||||
|
func NSSelectOne(el *etree.Element, namespace, tag string) (*etree.Element, error) {
|
||||||
|
return NSSelectOneCtx(DefaultNSContext, el, namespace, tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NSSelectOneCtx conducts a depth-first search for an element with the specified namespace
|
||||||
// and tag. If such an element is found, a new *etree.Element is returned which is a
|
// and tag. If such an element is found, a new *etree.Element is returned which is a
|
||||||
// copy of the found element, but with all in-context namespace declarations attached
|
// copy of the found element, but with all in-context namespace declarations attached
|
||||||
// to the element as attributes.
|
// to the element as attributes.
|
||||||
func NSSelectOne(el *etree.Element, namespace, tag string) (*etree.Element, error) {
|
func NSSelectOneCtx(ctx NSContext, el *etree.Element, namespace, tag string) (*etree.Element, error) {
|
||||||
var found *etree.Element
|
var found *etree.Element
|
||||||
|
|
||||||
err := nsTraverse(DefaultNSContext, el, func(ctx NSContext, el *etree.Element) error {
|
err := NSFindIterateCtx(ctx, el, namespace, tag, func(ctx NSContext, el *etree.Element) error {
|
||||||
currentNS, err := ctx.LookupPrefix(el.Space)
|
var err error
|
||||||
|
|
||||||
|
found, err = NSDetatch(ctx, el)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Base case, el is the sought after element.
|
|
||||||
if currentNS == namespace && el.Tag == tag {
|
|
||||||
found, err = detachWithNamespaces(ctx, el)
|
|
||||||
return ErrTraversalHalted
|
return ErrTraversalHalted
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil && err != ErrTraversalHalted {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return found, nil
|
return found, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NSFindIterate conducts a depth-first traversal searching for elements with the
|
// NSFindIterate behaves identically to NSFindIterateCtx, but uses DefaultNSContext
|
||||||
// specified tag in the specified namespace. For each such element, the passed
|
// as the surrounding context.
|
||||||
// handler function is invoked. If the handler function returns an error
|
func NSFindIterate(el *etree.Element, namespace, tag string, handle NSIterHandler) error {
|
||||||
// traversal is immediately halted. If the error returned by the handler is
|
return NSFindIterateCtx(DefaultNSContext, el, namespace, tag, handle)
|
||||||
// ErrTraversalHalted then nil will be returned by NSFindIterate. If any other
|
}
|
||||||
// error is returned by the handler, that error will be returned by NSFindIterate.
|
|
||||||
func NSFindIterate(el *etree.Element, namespace, tag string, handle func(*etree.Element) error) error {
|
// NSFindIterateCtx conducts a depth-first traversal searching for elements with the
|
||||||
err := nsTraverse(DefaultNSContext, el, func(ctx NSContext, el *etree.Element) error {
|
// specified tag in the specified namespace. It uses the passed NSContext for prefix
|
||||||
|
// lookups. For each such element, the passed handler function is invoked. If the
|
||||||
|
// handler function returns an error traversal is immediately halted. If the error
|
||||||
|
// returned by the handler is ErrTraversalHalted then nil will be returned by
|
||||||
|
// NSFindIterate. If any other error is returned by the handler, that error will be
|
||||||
|
// returned by NSFindIterate.
|
||||||
|
func NSFindIterateCtx(ctx NSContext, el *etree.Element, namespace, tag string, handle NSIterHandler) error {
|
||||||
|
err := NSTraverse(ctx, el, func(ctx NSContext, el *etree.Element) error {
|
||||||
currentNS, err := ctx.LookupPrefix(el.Space)
|
currentNS, err := ctx.LookupPrefix(el.Space)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -221,7 +273,7 @@ func NSFindIterate(el *etree.Element, namespace, tag string, handle func(*etree.
|
||||||
|
|
||||||
// Base case, el is the sought after element.
|
// Base case, el is the sought after element.
|
||||||
if currentNS == namespace && el.Tag == tag {
|
if currentNS == namespace && el.Tag == tag {
|
||||||
return handle(el)
|
return handle(ctx, el)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -234,12 +286,18 @@ func NSFindIterate(el *etree.Element, namespace, tag string, handle func(*etree.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NSFindOne conducts a depth-first search for the specified element. If such an element
|
// NSFindOne behaves identically to NSFindOneCtx, but uses DefaultNSContext for
|
||||||
// is found a reference to it is returned.
|
// context.
|
||||||
func NSFindOne(el *etree.Element, namespace, tag string) (*etree.Element, error) {
|
func NSFindOne(el *etree.Element, namespace, tag string) (*etree.Element, error) {
|
||||||
|
return NSFindOneCtx(DefaultNSContext, el, namespace, tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NSFindOneCtx conducts a depth-first search for the specified element. If such an element
|
||||||
|
// is found a reference to it is returned.
|
||||||
|
func NSFindOneCtx(ctx NSContext, el *etree.Element, namespace, tag string) (*etree.Element, error) {
|
||||||
var found *etree.Element
|
var found *etree.Element
|
||||||
|
|
||||||
err := NSFindIterate(el, namespace, tag, func(el *etree.Element) error {
|
err := NSFindIterateCtx(ctx, el, namespace, tag, func(ctx NSContext, el *etree.Element) error {
|
||||||
found = el
|
found = el
|
||||||
return ErrTraversalHalted
|
return ErrTraversalHalted
|
||||||
})
|
})
|
||||||
|
@ -250,3 +308,21 @@ func NSFindOne(el *etree.Element, namespace, tag string) (*etree.Element, error)
|
||||||
|
|
||||||
return found, nil
|
return found, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NSBuildParentContext recurses upward from an element in order to build an NSContext
|
||||||
|
// for its immediate parent. If the element has no parent DefaultNSContext
|
||||||
|
// is returned.
|
||||||
|
func NSBuildParentContext(el *etree.Element) (NSContext, error) {
|
||||||
|
parent := el.Parent()
|
||||||
|
if parent == nil {
|
||||||
|
return DefaultNSContext, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, err := NSBuildParentContext(parent)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return ctx, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.SubContext(parent)
|
||||||
|
}
|
||||||
|
|
35
vendor/github.com/russellhaering/goxmldsig/etreeutils/sort.go
generated
vendored
35
vendor/github.com/russellhaering/goxmldsig/etreeutils/sort.go
generated
vendored
|
@ -2,6 +2,8 @@ package etreeutils
|
||||||
|
|
||||||
import "github.com/beevik/etree"
|
import "github.com/beevik/etree"
|
||||||
|
|
||||||
|
// SortedAttrs provides sorting capabilities, compatible with XML C14N, on top
|
||||||
|
// of an []etree.Attr
|
||||||
type SortedAttrs []etree.Attr
|
type SortedAttrs []etree.Attr
|
||||||
|
|
||||||
func (a SortedAttrs) Len() int {
|
func (a SortedAttrs) Len() int {
|
||||||
|
@ -13,17 +15,23 @@ func (a SortedAttrs) Swap(i, j int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a SortedAttrs) Less(i, j int) bool {
|
func (a SortedAttrs) Less(i, j int) bool {
|
||||||
// As I understand it: any "xmlns" attribute should come first, followed by any
|
// This is the best reference I've found on sort order:
|
||||||
// any "xmlns:prefix" attributes, presumably ordered by prefix. Lastly any other
|
// http://dst.lbl.gov/~ksb/Scratch/XMLC14N.html
|
||||||
// attributes in lexicographical order.
|
|
||||||
if a[i].Space == defaultPrefix && a[i].Key == xmlnsPrefix {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// If attr j is a default namespace declaration, attr i may
|
||||||
|
// not be strictly "less" than it.
|
||||||
if a[j].Space == defaultPrefix && a[j].Key == xmlnsPrefix {
|
if a[j].Space == defaultPrefix && a[j].Key == xmlnsPrefix {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Otherwise, if attr i is a default namespace declaration, it
|
||||||
|
// must be less than anything else.
|
||||||
|
if a[i].Space == defaultPrefix && a[i].Key == xmlnsPrefix {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next, namespace prefix declarations, sorted by prefix, come before
|
||||||
|
// anythign else.
|
||||||
if a[i].Space == xmlnsPrefix {
|
if a[i].Space == xmlnsPrefix {
|
||||||
if a[j].Space == xmlnsPrefix {
|
if a[j].Space == xmlnsPrefix {
|
||||||
return a[i].Key < a[j].Key
|
return a[i].Key < a[j].Key
|
||||||
|
@ -35,6 +43,21 @@ func (a SortedAttrs) Less(i, j int) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Then come unprefixed attributes, sorted by key.
|
||||||
|
if a[i].Space == defaultPrefix {
|
||||||
|
if a[j].Space == defaultPrefix {
|
||||||
|
return a[i].Key < a[j].Key
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if a[j].Space == defaultPrefix {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wow. We're still going. Finally, attributes in the same namespace should be
|
||||||
|
// sorted by key. Attributes in different namespaces should be sorted by the
|
||||||
|
// actual namespace (_not_ the prefix). For now just use the prefix.
|
||||||
if a[i].Space == a[j].Space {
|
if a[i].Space == a[j].Space {
|
||||||
return a[i].Key < a[j].Key
|
return a[i].Key < a[j].Key
|
||||||
}
|
}
|
||||||
|
|
43
vendor/github.com/russellhaering/goxmldsig/etreeutils/unmarshal.go
generated
vendored
Normal file
43
vendor/github.com/russellhaering/goxmldsig/etreeutils/unmarshal.go
generated
vendored
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package etreeutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
|
||||||
|
"github.com/beevik/etree"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NSUnmarshalElement unmarshals the passed etree Element into the value pointed to by
|
||||||
|
// v using encoding/xml in the context of the passed NSContext. If v implements
|
||||||
|
// ElementKeeper, SetUnderlyingElement will be called on v with a reference to el.
|
||||||
|
func NSUnmarshalElement(ctx NSContext, el *etree.Element, v interface{}) error {
|
||||||
|
detatched, err := NSDetatch(ctx, el)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
doc := etree.NewDocument()
|
||||||
|
doc.AddChild(detatched)
|
||||||
|
data, err := doc.WriteToBytes()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = xml.Unmarshal(data, v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v := v.(type) {
|
||||||
|
case ElementKeeper:
|
||||||
|
v.SetUnderlyingElement(el)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ElementKeeper should be implemented by types which will be passed to
|
||||||
|
// UnmarshalElement, but wish to keep a reference
|
||||||
|
type ElementKeeper interface {
|
||||||
|
SetUnderlyingElement(*etree.Element)
|
||||||
|
UnderlyingElement() *etree.Element
|
||||||
|
}
|
35
vendor/github.com/russellhaering/goxmldsig/sign.go
generated
vendored
35
vendor/github.com/russellhaering/goxmldsig/sign.go
generated
vendored
|
@ -11,6 +11,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/beevik/etree"
|
"github.com/beevik/etree"
|
||||||
|
"github.com/russellhaering/goxmldsig/etreeutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SigningContext struct {
|
type SigningContext struct {
|
||||||
|
@ -133,15 +134,39 @@ func (ctx *SigningContext) constructSignature(el *etree.Element, enveloped bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
sig.CreateAttr(xmlns, Namespace)
|
sig.CreateAttr(xmlns, Namespace)
|
||||||
|
sig.AddChild(signedInfo)
|
||||||
|
|
||||||
sig.Child = append(sig.Child, signedInfo)
|
// When using xml-c14n11 (ie, non-exclusive canonicalization) the canonical form
|
||||||
|
// of the SignedInfo must declare all namespaces that are in scope at it's final
|
||||||
|
// enveloped location in the document. In order to do that, we're going to construct
|
||||||
|
// a series of cascading NSContexts to capture namespace declarations:
|
||||||
|
|
||||||
// Must propagate down the attributes to the 'SignedInfo' before digesting
|
// First get the context surrounding the element we are signing.
|
||||||
for _, attr := range sig.Attr {
|
rootNSCtx, err := etreeutils.NSBuildParentContext(el)
|
||||||
signedInfo.CreateAttr(attr.Space+":"+attr.Key, attr.Value)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
digest, err := ctx.digest(signedInfo)
|
// Then capture any declarations on the element itself.
|
||||||
|
elNSCtx, err := rootNSCtx.SubContext(el)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Followed by declarations on the Signature (which we just added above)
|
||||||
|
sigNSCtx, err := elNSCtx.SubContext(sig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally detatch the SignedInfo in order to capture all of the namespace
|
||||||
|
// declarations in the scope we've constructed.
|
||||||
|
detatchedSignedInfo, err := etreeutils.NSDetatch(sigNSCtx, signedInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
digest, err := ctx.digest(detatchedSignedInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
93
vendor/github.com/russellhaering/goxmldsig/types/signature.go
generated
vendored
Normal file
93
vendor/github.com/russellhaering/goxmldsig/types/signature.go
generated
vendored
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
|
||||||
|
"github.com/beevik/etree"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InclusiveNamespaces struct {
|
||||||
|
XMLName xml.Name `xml:"http://www.w3.org/2001/10/xml-exc-c14n# InclusiveNamespaces"`
|
||||||
|
PrefixList string `xml:"PrefixList,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Transform struct {
|
||||||
|
XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# Transform"`
|
||||||
|
Algorithm string `xml:"Algorithm,attr"`
|
||||||
|
InclusiveNamespaces *InclusiveNamespaces `xml:"InclusiveNamespaces"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Transforms struct {
|
||||||
|
XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# Transforms"`
|
||||||
|
Transforms []Transform `xml:"Transform"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DigestMethod struct {
|
||||||
|
XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# DigestMethod"`
|
||||||
|
Algorithm string `xml:"Algorithm,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Reference struct {
|
||||||
|
XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# Reference"`
|
||||||
|
URI string `xml:"URI,attr"`
|
||||||
|
DigestValue string `xml:"DigestValue"`
|
||||||
|
DigestAlgo DigestMethod `xml:"DigestMethod"`
|
||||||
|
Transforms Transforms `xml:"Transforms"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CanonicalizationMethod struct {
|
||||||
|
XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# CanonicalizationMethod"`
|
||||||
|
Algorithm string `xml:"Algorithm,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SignatureMethod struct {
|
||||||
|
XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# SignatureMethod"`
|
||||||
|
Algorithm string `xml:"Algorithm,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SignedInfo struct {
|
||||||
|
XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# SignedInfo"`
|
||||||
|
CanonicalizationMethod CanonicalizationMethod `xml:"CanonicalizationMethod"`
|
||||||
|
SignatureMethod SignatureMethod `xml:"SignatureMethod"`
|
||||||
|
References []Reference `xml:"Reference"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SignatureValue struct {
|
||||||
|
XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# SignatureValue"`
|
||||||
|
Data string `xml:",chardata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type KeyInfo struct {
|
||||||
|
XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# KeyInfo"`
|
||||||
|
X509Data X509Data `xml:"X509Data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type X509Data struct {
|
||||||
|
XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# X509Data"`
|
||||||
|
X509Certificate X509Certificate `xml:"X509Certificate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type X509Certificate struct {
|
||||||
|
XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# X509Certificate"`
|
||||||
|
Data string `xml:",chardata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Signature struct {
|
||||||
|
XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# Signature"`
|
||||||
|
SignedInfo *SignedInfo `xml:"SignedInfo"`
|
||||||
|
SignatureValue *SignatureValue `xml:"SignatureValue"`
|
||||||
|
KeyInfo *KeyInfo `xml:"KeyInfo"`
|
||||||
|
el *etree.Element
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUnderlyingElement will be called with a reference to the Element this Signature
|
||||||
|
// was unmarshaled from.
|
||||||
|
func (s *Signature) SetUnderlyingElement(el *etree.Element) {
|
||||||
|
s.el = el
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnderlyingElement returns a reference to the Element this signature was unmarshaled
|
||||||
|
// from, where applicable.
|
||||||
|
func (s *Signature) UnderlyingElement() *etree.Element {
|
||||||
|
return s.el
|
||||||
|
}
|
237
vendor/github.com/russellhaering/goxmldsig/validate.go
generated
vendored
237
vendor/github.com/russellhaering/goxmldsig/validate.go
generated
vendored
|
@ -11,6 +11,7 @@ import (
|
||||||
|
|
||||||
"github.com/beevik/etree"
|
"github.com/beevik/etree"
|
||||||
"github.com/russellhaering/goxmldsig/etreeutils"
|
"github.com/russellhaering/goxmldsig/etreeutils"
|
||||||
|
"github.com/russellhaering/goxmldsig/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
var uriRegexp = regexp.MustCompile("^#[a-zA-Z_][\\w.-]*$")
|
var uriRegexp = regexp.MustCompile("^#[a-zA-Z_][\\w.-]*$")
|
||||||
|
@ -82,7 +83,12 @@ func recursivelyRemoveElement(tree, el *etree.Element) bool {
|
||||||
// instead return a copy. Unfortunately copying the tree makes it difficult to
|
// instead return a copy. Unfortunately copying the tree makes it difficult to
|
||||||
// correctly locate the signature. I'm opting, for now, to simply mutate the root
|
// correctly locate the signature. I'm opting, for now, to simply mutate the root
|
||||||
// parameter.
|
// parameter.
|
||||||
func (ctx *ValidationContext) transform(root, sig *etree.Element, transforms []*etree.Element) (*etree.Element, Canonicalizer, error) {
|
func (ctx *ValidationContext) transform(
|
||||||
|
el *etree.Element,
|
||||||
|
sig *types.Signature,
|
||||||
|
ref *types.Reference) (*etree.Element, Canonicalizer, error) {
|
||||||
|
transforms := ref.Transforms.Transforms
|
||||||
|
|
||||||
if len(transforms) != 2 {
|
if len(transforms) != 2 {
|
||||||
return nil, nil, errors.New("Expected Enveloped and C14N transforms")
|
return nil, nil, errors.New("Expected Enveloped and C14N transforms")
|
||||||
}
|
}
|
||||||
|
@ -90,25 +96,18 @@ func (ctx *ValidationContext) transform(root, sig *etree.Element, transforms []*
|
||||||
var canonicalizer Canonicalizer
|
var canonicalizer Canonicalizer
|
||||||
|
|
||||||
for _, transform := range transforms {
|
for _, transform := range transforms {
|
||||||
algo := transform.SelectAttr(AlgorithmAttr)
|
algo := transform.Algorithm
|
||||||
if algo == nil {
|
|
||||||
return nil, nil, errors.New("Missing Algorithm attribute")
|
|
||||||
}
|
|
||||||
|
|
||||||
switch AlgorithmID(algo.Value) {
|
switch AlgorithmID(algo) {
|
||||||
case EnvelopedSignatureAltorithmId:
|
case EnvelopedSignatureAltorithmId:
|
||||||
if !recursivelyRemoveElement(root, sig) {
|
if !recursivelyRemoveElement(el, sig.UnderlyingElement()) {
|
||||||
return nil, nil, errors.New("Error applying canonicalization transform: Signature not found")
|
return nil, nil, errors.New("Error applying canonicalization transform: Signature not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
case CanonicalXML10ExclusiveAlgorithmId:
|
case CanonicalXML10ExclusiveAlgorithmId:
|
||||||
var prefixList string
|
var prefixList string
|
||||||
ins := transform.FindElement(childPath("", InclusiveNamespacesTag))
|
if transform.InclusiveNamespaces != nil {
|
||||||
if ins != nil {
|
prefixList = transform.InclusiveNamespaces.PrefixList
|
||||||
prefixListEl := ins.SelectAttr(PrefixListAttr)
|
|
||||||
if prefixListEl != nil {
|
|
||||||
prefixList = prefixListEl.Value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
canonicalizer = MakeC14N10ExclusiveCanonicalizerWithPrefixList(prefixList)
|
canonicalizer = MakeC14N10ExclusiveCanonicalizerWithPrefixList(prefixList)
|
||||||
|
@ -117,7 +116,7 @@ func (ctx *ValidationContext) transform(root, sig *etree.Element, transforms []*
|
||||||
canonicalizer = MakeC14N11Canonicalizer()
|
canonicalizer = MakeC14N11Canonicalizer()
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, nil, errors.New("Unknown Transform Algorithm: " + algo.Value)
|
return nil, nil, errors.New("Unknown Transform Algorithm: " + algo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,7 +124,7 @@ func (ctx *ValidationContext) transform(root, sig *etree.Element, transforms []*
|
||||||
return nil, nil, errors.New("Expected canonicalization transform")
|
return nil, nil, errors.New("Expected canonicalization transform")
|
||||||
}
|
}
|
||||||
|
|
||||||
return root, canonicalizer, nil
|
return el, canonicalizer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *ValidationContext) digest(el *etree.Element, digestAlgorithmId string, canonicalizer Canonicalizer) ([]byte, error) {
|
func (ctx *ValidationContext) digest(el *etree.Element, digestAlgorithmId string, canonicalizer Canonicalizer) ([]byte, error) {
|
||||||
|
@ -148,19 +147,16 @@ func (ctx *ValidationContext) digest(el *etree.Element, digestAlgorithmId string
|
||||||
return hash.Sum(nil), nil
|
return hash.Sum(nil), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *ValidationContext) verifySignedInfo(signatureElement *etree.Element, canonicalizer Canonicalizer, signatureMethodId string, cert *x509.Certificate, sig []byte) error {
|
func (ctx *ValidationContext) verifySignedInfo(sig *types.Signature, canonicalizer Canonicalizer, signatureMethodId string, cert *x509.Certificate, decodedSignature []byte) error {
|
||||||
|
signatureElement := sig.UnderlyingElement()
|
||||||
|
|
||||||
signedInfo := signatureElement.FindElement(childPath(signatureElement.Space, SignedInfoTag))
|
signedInfo := signatureElement.FindElement(childPath(signatureElement.Space, SignedInfoTag))
|
||||||
if signedInfo == nil {
|
if signedInfo == nil {
|
||||||
return errors.New("Missing SignedInfo")
|
return errors.New("Missing SignedInfo")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Any attributes from the 'Signature' element must be pushed down into the 'SignedInfo' element before it is canonicalized
|
|
||||||
for _, attr := range signatureElement.Attr {
|
|
||||||
signedInfo.CreateAttr(attr.Space+":"+attr.Key, attr.Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Canonicalize the xml
|
// Canonicalize the xml
|
||||||
canonical, err := canonicalizer.Canonicalize(signedInfo)
|
canonical, err := canonicalSerialize(signedInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -184,7 +180,7 @@ func (ctx *ValidationContext) verifySignedInfo(signatureElement *etree.Element,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that the private key matching the public key from the cert was what was used to sign the 'SignedInfo' and produce the 'SignatureValue'
|
// Verify that the private key matching the public key from the cert was what was used to sign the 'SignedInfo' and produce the 'SignatureValue'
|
||||||
err = rsa.VerifyPKCS1v15(pubKey, signatureAlgorithm, hashed[:], sig)
|
err = rsa.VerifyPKCS1v15(pubKey, signatureAlgorithm, hashed[:], decodedSignature)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -192,65 +188,37 @@ func (ctx *ValidationContext) verifySignedInfo(signatureElement *etree.Element,
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *ValidationContext) validateSignature(el, sig *etree.Element, cert *x509.Certificate) (*etree.Element, error) {
|
func (ctx *ValidationContext) validateSignature(el *etree.Element, sig *types.Signature, cert *x509.Certificate) (*etree.Element, error) {
|
||||||
// Get the 'SignedInfo' element
|
idAttr := el.SelectAttr(DefaultIdAttr)
|
||||||
signedInfo := sig.FindElement(childPath(sig.Space, SignedInfoTag))
|
if idAttr == nil || idAttr.Value == "" {
|
||||||
if signedInfo == nil {
|
return nil, errors.New("Missing ID attribute")
|
||||||
return nil, errors.New("Missing SignedInfo")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reference := signedInfo.FindElement(childPath(sig.Space, ReferenceTag))
|
var ref *types.Reference
|
||||||
if reference == nil {
|
|
||||||
return nil, errors.New("Missing Reference")
|
|
||||||
}
|
|
||||||
|
|
||||||
transforms := reference.FindElement(childPath(sig.Space, TransformsTag))
|
// Find the first reference which references the top-level element
|
||||||
if transforms == nil {
|
for _, _ref := range sig.SignedInfo.References {
|
||||||
return nil, errors.New("Missing Transforms")
|
if _ref.URI == "" || _ref.URI[1:] == idAttr.Value {
|
||||||
|
ref = &_ref
|
||||||
}
|
}
|
||||||
|
|
||||||
uri := reference.SelectAttr("URI")
|
|
||||||
if uri == nil {
|
|
||||||
// TODO(russell_h): It is permissible to leave this out. We should be
|
|
||||||
// able to fall back to finding the referenced element some other way.
|
|
||||||
return nil, errors.New("Reference is missing URI attribute")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the element referenced in the 'SignedInfo'
|
|
||||||
referencedElement := el.FindElement(fmt.Sprintf("//[@%s='%s']", ctx.IdAttribute, uri.Value[1:]))
|
|
||||||
if referencedElement == nil {
|
|
||||||
return nil, errors.New("Unable to find referenced element: " + uri.Value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform all transformations listed in the 'SignedInfo'
|
// Perform all transformations listed in the 'SignedInfo'
|
||||||
// Basically, this means removing the 'SignedInfo'
|
// Basically, this means removing the 'SignedInfo'
|
||||||
transformed, canonicalizer, err := ctx.transform(referencedElement, sig, transforms.ChildElements())
|
transformed, canonicalizer, err := ctx.transform(el, sig, ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
digestMethod := reference.FindElement(childPath(sig.Space, DigestMethodTag))
|
digestAlgorithm := ref.DigestAlgo.Algorithm
|
||||||
if digestMethod == nil {
|
|
||||||
return nil, errors.New("Missing DigestMethod")
|
|
||||||
}
|
|
||||||
|
|
||||||
digestValue := reference.FindElement(childPath(sig.Space, DigestValueTag))
|
|
||||||
if digestValue == nil {
|
|
||||||
return nil, errors.New("Missing DigestValue")
|
|
||||||
}
|
|
||||||
|
|
||||||
digestAlgorithmAttr := digestMethod.SelectAttr(AlgorithmAttr)
|
|
||||||
if digestAlgorithmAttr == nil {
|
|
||||||
return nil, errors.New("Missing DigestMethod Algorithm attribute")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Digest the transformed XML and compare it to the 'DigestValue' from the 'SignedInfo'
|
// Digest the transformed XML and compare it to the 'DigestValue' from the 'SignedInfo'
|
||||||
digest, err := ctx.digest(transformed, digestAlgorithmAttr.Value, canonicalizer)
|
digest, err := ctx.digest(transformed, digestAlgorithm, canonicalizer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
decodedDigestValue, err := base64.StdEncoding.DecodeString(digestValue.Text())
|
decodedDigestValue, err := base64.StdEncoding.DecodeString(ref.DigestValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -259,30 +227,15 @@ func (ctx *ValidationContext) validateSignature(el, sig *etree.Element, cert *x5
|
||||||
return nil, errors.New("Signature could not be verified")
|
return nil, errors.New("Signature could not be verified")
|
||||||
}
|
}
|
||||||
|
|
||||||
//Verify the signed info
|
|
||||||
signatureMethod := signedInfo.FindElement(childPath(sig.Space, SignatureMethodTag))
|
|
||||||
if signatureMethod == nil {
|
|
||||||
return nil, errors.New("Missing SignatureMethod")
|
|
||||||
}
|
|
||||||
|
|
||||||
signatureMethodAlgorithmAttr := signatureMethod.SelectAttr(AlgorithmAttr)
|
|
||||||
if digestAlgorithmAttr == nil {
|
|
||||||
return nil, errors.New("Missing SignatureMethod Algorithm attribute")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode the 'SignatureValue' so we can compare against it
|
// Decode the 'SignatureValue' so we can compare against it
|
||||||
signatureValue := sig.FindElement(childPath(sig.Space, SignatureValueTag))
|
decodedSignature, err := base64.StdEncoding.DecodeString(sig.SignatureValue.Data)
|
||||||
if signatureValue == nil {
|
|
||||||
return nil, errors.New("Missing SignatureValue")
|
|
||||||
}
|
|
||||||
|
|
||||||
decodedSignature, err := base64.StdEncoding.DecodeString(signatureValue.Text())
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("Could not decode signature")
|
return nil, errors.New("Could not decode signature")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actually verify the 'SignedInfo' was signed by a trusted source
|
// Actually verify the 'SignedInfo' was signed by a trusted source
|
||||||
err = ctx.verifySignedInfo(sig, canonicalizer, signatureMethodAlgorithmAttr.Value, cert, decodedSignature)
|
signatureMethod := sig.SignedInfo.SignatureMethod.Algorithm
|
||||||
|
err = ctx.verifySignedInfo(sig, canonicalizer, signatureMethod, cert, decodedSignature)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -300,38 +253,89 @@ func contains(roots []*x509.Certificate, cert *x509.Certificate) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// findSignature searches for a Signature element referencing the passed root element.
|
// findSignature searches for a Signature element referencing the passed root element.
|
||||||
func (ctx *ValidationContext) findSignature(el *etree.Element) (*etree.Element, error) {
|
func (ctx *ValidationContext) findSignature(el *etree.Element) (*types.Signature, error) {
|
||||||
idAttr := el.SelectAttr(DefaultIdAttr)
|
idAttr := el.SelectAttr(DefaultIdAttr)
|
||||||
if idAttr == nil || idAttr.Value == "" {
|
if idAttr == nil || idAttr.Value == "" {
|
||||||
return nil, errors.New("Missing ID attribute")
|
return nil, errors.New("Missing ID attribute")
|
||||||
}
|
}
|
||||||
|
|
||||||
var signatureElement *etree.Element
|
var sig *types.Signature
|
||||||
|
|
||||||
err := etreeutils.NSFindIterate(el, Namespace, SignatureTag, func(sig *etree.Element) error {
|
// Traverse the tree looking for a Signature element
|
||||||
signedInfo := sig.FindElement(childPath(sig.Space, SignedInfoTag))
|
err := etreeutils.NSFindIterate(el, Namespace, SignatureTag, func(ctx etreeutils.NSContext, el *etree.Element) error {
|
||||||
if signedInfo == nil {
|
|
||||||
|
found := false
|
||||||
|
err := etreeutils.NSFindIterateCtx(ctx, el, Namespace, SignedInfoTag,
|
||||||
|
func(ctx etreeutils.NSContext, signedInfo *etree.Element) error {
|
||||||
|
// Ignore any SignedInfo that isn't an immediate descendent of Signature.
|
||||||
|
if signedInfo.Parent() != el {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
detachedSignedInfo, err := etreeutils.NSDetatch(ctx, signedInfo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c14NMethod := detachedSignedInfo.FindElement(childPath(detachedSignedInfo.Space, CanonicalizationMethodTag))
|
||||||
|
if c14NMethod == nil {
|
||||||
|
return errors.New("missing CanonicalizationMethod on Signature")
|
||||||
|
}
|
||||||
|
|
||||||
|
c14NAlgorithm := c14NMethod.SelectAttrValue(AlgorithmAttr, "")
|
||||||
|
|
||||||
|
var canonicalSignedInfo *etree.Element
|
||||||
|
|
||||||
|
switch AlgorithmID(c14NAlgorithm) {
|
||||||
|
case CanonicalXML10ExclusiveAlgorithmId:
|
||||||
|
err := etreeutils.TransformExcC14n(detachedSignedInfo, "")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: TransformExcC14n transforms the element in-place,
|
||||||
|
// while canonicalPrep isn't meant to. Once we standardize
|
||||||
|
// this behavior we can drop this, as well as the adding and
|
||||||
|
// removing of elements below.
|
||||||
|
canonicalSignedInfo = detachedSignedInfo
|
||||||
|
|
||||||
|
case CanonicalXML11AlgorithmId:
|
||||||
|
canonicalSignedInfo = canonicalPrep(detachedSignedInfo, map[string]struct{}{})
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid CanonicalizationMethod on Signature: %s", c14NAlgorithm)
|
||||||
|
}
|
||||||
|
|
||||||
|
el.RemoveChild(signedInfo)
|
||||||
|
el.AddChild(canonicalSignedInfo)
|
||||||
|
|
||||||
|
found = true
|
||||||
|
|
||||||
|
return etreeutils.ErrTraversalHalted
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
return errors.New("Missing SignedInfo")
|
return errors.New("Missing SignedInfo")
|
||||||
}
|
}
|
||||||
|
|
||||||
referenceElement := signedInfo.FindElement(childPath(sig.Space, ReferenceTag))
|
// Unmarshal the signature into a structured Signature type
|
||||||
if referenceElement == nil {
|
_sig := &types.Signature{}
|
||||||
return errors.New("Missing Reference Element")
|
err = etreeutils.NSUnmarshalElement(ctx, el, _sig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
uriAttr := referenceElement.SelectAttr(URIAttr)
|
// Traverse references in the signature to determine whether it has at least
|
||||||
if uriAttr == nil || uriAttr.Value == "" {
|
// one reference to the top level element. If so, conclude the search.
|
||||||
return errors.New("Missing URI attribute")
|
for _, ref := range _sig.SignedInfo.References {
|
||||||
}
|
if ref.URI == "" || ref.URI[1:] == idAttr.Value {
|
||||||
|
sig = _sig
|
||||||
if !uriRegexp.MatchString(uriAttr.Value) {
|
|
||||||
return errors.New("Invalid URI: " + uriAttr.Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
if uriAttr.Value[1:] == idAttr.Value {
|
|
||||||
signatureElement = sig
|
|
||||||
return etreeutils.ErrTraversalHalted
|
return etreeutils.ErrTraversalHalted
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
@ -340,14 +344,14 @@ func (ctx *ValidationContext) findSignature(el *etree.Element) (*etree.Element,
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if signatureElement == nil {
|
if sig == nil {
|
||||||
return nil, ErrMissingSignature
|
return nil, ErrMissingSignature
|
||||||
}
|
}
|
||||||
|
|
||||||
return signatureElement, nil
|
return sig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *ValidationContext) verifyCertificate(signatureElement *etree.Element) (*x509.Certificate, error) {
|
func (ctx *ValidationContext) verifyCertificate(sig *types.Signature) (*x509.Certificate, error) {
|
||||||
now := ctx.Clock.Now()
|
now := ctx.Clock.Now()
|
||||||
|
|
||||||
roots, err := ctx.CertificateStore.Certificates()
|
roots, err := ctx.CertificateStore.Certificates()
|
||||||
|
@ -357,17 +361,13 @@ func (ctx *ValidationContext) verifyCertificate(signatureElement *etree.Element)
|
||||||
|
|
||||||
var cert *x509.Certificate
|
var cert *x509.Certificate
|
||||||
|
|
||||||
// Get the x509 element from the signature
|
if sig.KeyInfo != nil {
|
||||||
x509Element := signatureElement.FindElement("//" + childPath(signatureElement.Space, X509CertificateTag))
|
// If the Signature includes KeyInfo, extract the certificate from there
|
||||||
if x509Element == nil {
|
if sig.KeyInfo.X509Data.X509Certificate.Data == "" {
|
||||||
// Use root certificate if there is only one and it is not contained in signatureElement
|
return nil, errors.New("missing X509Certificate within KeyInfo")
|
||||||
if len(roots) == 1 {
|
|
||||||
cert = roots[0]
|
|
||||||
} else {
|
|
||||||
return nil, errors.New("Missing x509 Element")
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
certData, err := base64.StdEncoding.DecodeString(x509Element.Text())
|
certData, err := base64.StdEncoding.DecodeString(sig.KeyInfo.X509Data.X509Certificate.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("Failed to parse certificate")
|
return nil, errors.New("Failed to parse certificate")
|
||||||
}
|
}
|
||||||
|
@ -376,6 +376,13 @@ func (ctx *ValidationContext) verifyCertificate(signatureElement *etree.Element)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// If the Signature doesn't have KeyInfo, Use the root certificate if there is only one
|
||||||
|
if len(roots) == 1 {
|
||||||
|
cert = roots[0]
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("Missing x509 Element")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that the certificate is one we trust
|
// Verify that the certificate is one we trust
|
||||||
|
|
Loading…
Reference in a new issue