1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
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
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
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
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
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
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
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
127 if len(certs) == 0 {
128 return nil, errors.New("xmldsig: missing public key")
129 }
130
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
145 var reference *etree.Element
146 if sig.Reference.URI == "" {
147
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
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