...

Source file src/github.com/sassoftware/relic/lib/xmldsig/verify.go

Documentation: github.com/sassoftware/relic/lib/xmldsig

     1  //
     2  // Copyright (c) SAS Institute Inc.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  //
    16  
    17  package xmldsig
    18  
    19  import (
    20  	"crypto"
    21  	"crypto/ecdsa"
    22  	"crypto/hmac"
    23  	"crypto/rsa"
    24  	"crypto/x509"
    25  	"encoding/base64"
    26  	"encoding/xml"
    27  	"errors"
    28  	"fmt"
    29  	"math/big"
    30  	"strings"
    31  
    32  	"github.com/sassoftware/relic/lib/x509tools"
    33  	"github.com/sassoftware/relic/signers/sigerrors"
    34  
    35  	"github.com/beevik/etree"
    36  )
    37  
    38  type Signature struct {
    39  	PublicKey       crypto.PublicKey
    40  	Certificates    []*x509.Certificate
    41  	Hash            crypto.Hash
    42  	EncryptedDigest []byte
    43  	Reference       *etree.Element
    44  }
    45  
    46  func (s Signature) Leaf() *x509.Certificate {
    47  	for _, cert := range s.Certificates {
    48  		if x509tools.SameKey(cert.PublicKey, s.PublicKey) {
    49  			return cert
    50  		}
    51  	}
    52  	return nil
    53  }
    54  
    55  // Extract and verify an enveloped signature at the given root
    56  func Verify(root *etree.Element, sigpath string, extraCerts []*x509.Certificate) (*Signature, error) {
    57  	root = root.Copy()
    58  	sigs := root.FindElements(sigpath)
    59  	if len(sigs) == 0 {
    60  		return nil, sigerrors.NotSignedError{Type: "xmldsig"}
    61  	} else if len(sigs) > 1 {
    62  		return nil, errors.New("xmldsig: multiple signatures found")
    63  	}
    64  	sigEl := sigs[0]
    65  	// parse signature tree
    66  	sigbytes, err := SerializeCanonical(sigEl)
    67  	if err != nil {
    68  		return nil, fmt.Errorf("xmldsig: %s", err)
    69  	}
    70  	var sig signature
    71  	if err := xml.Unmarshal(sigbytes, &sig); err != nil {
    72  		return nil, fmt.Errorf("xmldsig: %s", err)
    73  	}
    74  	// parse algorithms
    75  	if sig.CanonicalizationMethod.Algorithm != AlgXMLExcC14n && sig.CanonicalizationMethod.Algorithm != AlgXMLExcC14nRec {
    76  		return nil, errors.New("xmldsig: unsupported canonicalization method")
    77  	}
    78  	hash, pubtype, err := parseAlgs(sig.Reference.DigestMethod.Algorithm, sig.SignatureMethod.Algorithm)
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  	// parse public key
    83  	var pubkey crypto.PublicKey
    84  	if sig.KeyValue != nil {
    85  		pubkey, err = parseKey(sig.KeyValue, pubtype)
    86  		if err != nil {
    87  			return nil, err
    88  		}
    89  	}
    90  	// parse x509 certs
    91  	certs := make([]*x509.Certificate, len(extraCerts))
    92  	copy(certs, extraCerts)
    93  	for _, b64 := range sig.X509Certificates {
    94  		der, err := base64.StdEncoding.DecodeString(b64)
    95  		if err != nil {
    96  			return nil, fmt.Errorf("xmldsig: invalid X509 certificate")
    97  		}
    98  		cert, err := x509.ParseCertificate(der)
    99  		if err != nil {
   100  			return nil, fmt.Errorf("xmldsig: invalid X509 certificate: %s", err)
   101  		}
   102  		certs = append(certs, cert)
   103  	}
   104  	// check signature
   105  	signedinfo := sigEl.SelectElement("SignedInfo")
   106  	if signedinfo == nil {
   107  		return nil, errors.New("xmldsig: invalid signature")
   108  	}
   109  	siCalc, err := hashCanon(signedinfo, hash)
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  	sigv, err := base64.StdEncoding.DecodeString(sig.SignatureValue)
   114  	if err != nil {
   115  		return nil, errors.New("xmldsig: invalid signature")
   116  	}
   117  	if pubtype == "ecdsa" {
   118  		// reformat with ASN.1 structure
   119  		sig, err := x509tools.UnpackEcdsaSignature(sigv)
   120  		if err != nil {
   121  			return nil, err
   122  		}
   123  		sigv = sig.Marshal()
   124  	}
   125  	if pubkey == nil {
   126  		// if no KeyValue is present then use the X509 certificate
   127  		if len(certs) == 0 {
   128  			return nil, errors.New("xmldsig: missing public key")
   129  		}
   130  		// no guarantee is made about the order in which certs appear, so try all of them
   131  		for _, cert := range certs {
   132  			err = x509tools.Verify(cert.PublicKey, hash, siCalc, sigv)
   133  			if err == nil {
   134  				pubkey = cert.PublicKey
   135  				break
   136  			}
   137  		}
   138  	} else {
   139  		err = x509tools.Verify(pubkey, hash, siCalc, sigv)
   140  	}
   141  	if err != nil {
   142  		return nil, fmt.Errorf("xmldsig: %s", err)
   143  	}
   144  	// check reference digest
   145  	var reference *etree.Element
   146  	if sig.Reference.URI == "" {
   147  		// enveloped signature
   148  		if len(sig.Reference.Transforms) != 2 ||
   149  			sig.Reference.Transforms[0].Algorithm != AlgDsigEnvelopedSignature ||
   150  			(sig.Reference.Transforms[1].Algorithm != AlgXMLExcC14n && sig.Reference.Transforms[1].Algorithm != AlgXMLExcC14nRec) {
   151  			return nil, errors.New("xmldsig: unsupported reference transform")
   152  		}
   153  		sigEl.Parent().RemoveChild(sigEl)
   154  		reference = root
   155  	} else {
   156  		// enveloping signature
   157  		if len(sig.Reference.Transforms) != 1 ||
   158  			(sig.Reference.Transforms[0].Algorithm != AlgXMLExcC14n && sig.Reference.Transforms[0].Algorithm != AlgXMLExcC14nRec) {
   159  			return nil, errors.New("xmldsig: unsupported reference transform")
   160  		}
   161  		if sig.Reference.URI[0] != '#' {
   162  			return nil, errors.New("xmldsig: unsupported reference URI")
   163  		}
   164  		reference = root.FindElement(fmt.Sprintf("[@Id='%s']", sig.Reference.URI[1:]))
   165  	}
   166  	if reference == nil {
   167  		return nil, errors.New("xmldsig: unable to locate reference")
   168  	}
   169  	refCalc, err := hashCanon(reference, hash)
   170  	if err != nil {
   171  		return nil, err
   172  	}
   173  	refGiven, err := base64.StdEncoding.DecodeString(sig.Reference.DigestValue)
   174  	if len(refGiven) != len(refCalc) || err != nil {
   175  		return nil, errors.New("xmldsig: invalid signature")
   176  	}
   177  	if !hmac.Equal(refGiven, refCalc) {
   178  		return nil, fmt.Errorf("xmldsig: digest mismatch: calculated %x, found %x", refCalc, refGiven)
   179  	}
   180  	return &Signature{
   181  		PublicKey:       pubkey,
   182  		Certificates:    certs,
   183  		Hash:            hash,
   184  		EncryptedDigest: sigv,
   185  		Reference:       reference,
   186  	}, nil
   187  }
   188  
   189  func HashAlgorithm(hashAlg string) (string, crypto.Hash) {
   190  	for _, prefix := range nsPrefixes {
   191  		if strings.HasPrefix(hashAlg, prefix) {
   192  			hashAlg = hashAlg[len(prefix):]
   193  			break
   194  		}
   195  	}
   196  	for hash, name := range hashNames {
   197  		if hashAlg == name {
   198  			return hashAlg, hash
   199  		}
   200  	}
   201  	return hashAlg, 0
   202  }
   203  
   204  func parseAlgs(hashAlg, sigAlg string) (crypto.Hash, string, error) {
   205  	hashAlg, hash := HashAlgorithm(hashAlg)
   206  	if !hash.Available() {
   207  		return 0, "", errors.New("xmldsig: unsupported digest algorithm")
   208  	}
   209  
   210  	for _, prefix := range nsPrefixes {
   211  		if strings.HasPrefix(sigAlg, prefix) {
   212  			sigAlg = sigAlg[len(prefix):]
   213  			break
   214  		}
   215  	}
   216  	if !strings.HasSuffix(sigAlg, "-"+hashAlg) {
   217  		return 0, "", errors.New("xmldsig: unsupported signature algorithm")
   218  	}
   219  	sigAlg = sigAlg[:len(sigAlg)-len(hashAlg)-1]
   220  	if sigAlg != "rsa" && sigAlg != "ecdsa" {
   221  		return 0, "", errors.New("xmldsig: unsupported signature algorithm")
   222  	}
   223  	return hash, sigAlg, nil
   224  }
   225  
   226  func parseKey(kv *keyValue, pubtype string) (crypto.PublicKey, error) {
   227  	switch pubtype {
   228  	case "rsa":
   229  		nbytes, err := base64.StdEncoding.DecodeString(kv.Modulus)
   230  		if len(nbytes) == 0 || err != nil {
   231  			return nil, errors.New("xmldsig: invalid public key")
   232  		}
   233  		n := new(big.Int).SetBytes(nbytes)
   234  		ebytes, err := base64.StdEncoding.DecodeString(kv.Exponent)
   235  		if len(ebytes) == 0 || err != nil {
   236  			return nil, errors.New("xmldsig: invalid public key")
   237  		}
   238  		ebig := new(big.Int).SetBytes(ebytes)
   239  		if ebig.BitLen() > 30 {
   240  			return nil, errors.New("xmldsig: invalid public key")
   241  		}
   242  		e := int(ebig.Int64())
   243  		return &rsa.PublicKey{N: n, E: e}, nil
   244  	case "ecdsa":
   245  		if !strings.HasPrefix(kv.NamedCurve.URN, "urn:oid:") {
   246  			return nil, errors.New("xmldsig: unsupported ECDSA curve")
   247  		}
   248  		curve, err := x509tools.CurveByOidString(kv.NamedCurve.URN[8:])
   249  		if err != nil {
   250  			return nil, fmt.Errorf("xmldsig: %s", err)
   251  		}
   252  		x, ok := new(big.Int).SetString(kv.X.Value, 10)
   253  		if !ok {
   254  			return nil, errors.New("xmldsig: invalid public key")
   255  		}
   256  		y, ok := new(big.Int).SetString(kv.Y.Value, 10)
   257  		if !ok {
   258  			return nil, errors.New("xmldsig: invalid public key")
   259  		}
   260  		if !curve.Curve.IsOnCurve(x, y) {
   261  			return nil, errors.New("xmldsig: invalid public key")
   262  		}
   263  		return &ecdsa.PublicKey{Curve: curve.Curve, X: x, Y: y}, nil
   264  	default:
   265  		return nil, errors.New("xmldsig: unsupported signature algorithm")
   266  	}
   267  }
   268  

View as plain text