Skip to content

Commit e1c8a5b

Browse files
ahacker1-securesamlrussellhaering
authored andcommitted
Refactor to help eliminate potential vulnerabilities:
- Obtain references from only the verified signed info bytes - Return the actual bytes that were verified, but reparsed into a new etree document
1 parent 2ac5490 commit e1c8a5b

File tree

1 file changed

+105
-18
lines changed

1 file changed

+105
-18
lines changed

validate.go

Lines changed: 105 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"crypto/x509"
66
"encoding/base64"
7+
"encoding/xml"
78
"errors"
89
"fmt"
910
"regexp"
@@ -169,6 +170,7 @@ func (ctx *ValidationContext) transform(
169170
return el, canonicalizer, nil
170171
}
171172

173+
// deprecated
172174
func (ctx *ValidationContext) digest(el *etree.Element, digestAlgorithmId string, canonicalizer Canonicalizer) ([]byte, error) {
173175
data, err := canonicalizer.Canonicalize(el)
174176
if err != nil {
@@ -189,6 +191,33 @@ func (ctx *ValidationContext) digest(el *etree.Element, digestAlgorithmId string
189191
return hash.Sum(nil), nil
190192
}
191193

194+
func (ctx *ValidationContext) getCanonicalSignedInfo(sig *types.Signature) ([]byte, error) {
195+
signatureElement := sig.UnderlyingElement()
196+
197+
nsCtx, err := etreeutils.NSBuildParentContext(signatureElement)
198+
if err != nil {
199+
return nil, err
200+
}
201+
202+
signedInfo, err := etreeutils.NSFindOneChildCtx(nsCtx, signatureElement, Namespace, SignedInfoTag)
203+
if err != nil {
204+
return nil, err
205+
}
206+
207+
if signedInfo == nil {
208+
return nil, errors.New("Missing SignedInfo")
209+
}
210+
211+
// Canonicalize the xml
212+
canonical, err := canonicalSerialize(signedInfo)
213+
if err != nil {
214+
return nil, err
215+
}
216+
217+
return canonical, nil
218+
}
219+
220+
// deprecated
192221
func (ctx *ValidationContext) verifySignedInfo(sig *types.Signature, canonicalizer Canonicalizer, signatureMethodId string, cert *x509.Certificate, decodedSignature []byte) error {
193222
signatureElement := sig.UnderlyingElement()
194223

@@ -226,6 +255,48 @@ func (ctx *ValidationContext) verifySignedInfo(sig *types.Signature, canonicaliz
226255
}
227256

228257
func (ctx *ValidationContext) validateSignature(el *etree.Element, sig *types.Signature, cert *x509.Certificate) (*etree.Element, error) {
258+
259+
// Actually verify the 'SignedInfo' was signed by a trusted source
260+
signatureMethod := sig.SignedInfo.SignatureMethod.Algorithm
261+
262+
canonicalSignedInfoBytes, err := ctx.getCanonicalSignedInfo(sig)
263+
if err != nil {
264+
return nil, errors.New("Could not obtain canonical signed info bytes")
265+
}
266+
267+
if canonicalSignedInfoBytes == nil {
268+
return nil, errors.New("Missing SignedInfo")
269+
}
270+
271+
algo, ok := x509SignatureAlgorithmByIdentifier[signatureMethod]
272+
if !ok {
273+
return nil, errors.New("Unknown signature method: " + signatureMethod)
274+
}
275+
276+
if sig.SignatureValue == nil {
277+
return nil, errors.New("Signature could not be verified")
278+
}
279+
280+
// Decode the 'SignatureValue' so we can compare against it
281+
decodedSignature, err := base64.StdEncoding.DecodeString(sig.SignatureValue.Data)
282+
if err != nil {
283+
return nil, errors.New("Could not decode signature")
284+
}
285+
286+
err = cert.CheckSignature(algo, canonicalSignedInfoBytes, decodedSignature)
287+
if err != nil {
288+
return nil, err
289+
}
290+
291+
// only use the verified canonicalSignedInfoBytes
292+
// unmarshal canonicalSignedInfoBytes into a new SignedInfo type
293+
// to obtain the reference
294+
signedInfo := &types.SignedInfo{}
295+
err = xml.Unmarshal(canonicalSignedInfoBytes, signedInfo)
296+
if err != nil {
297+
return nil, err
298+
}
299+
229300
idAttrEl := el.SelectAttr(ctx.IdAttribute)
230301
idAttr := ""
231302
if idAttrEl != nil {
@@ -235,53 +306,69 @@ func (ctx *ValidationContext) validateSignature(el *etree.Element, sig *types.Si
235306
var ref *types.Reference
236307

237308
// Find the first reference which references the top-level element
238-
for _, _ref := range sig.SignedInfo.References {
309+
for _, _ref := range signedInfo.References {
239310
if _ref.URI == "" || _ref.URI[1:] == idAttr {
240311
ref = &_ref
241312
}
242313
}
243314

315+
// prevents null pointer deref
316+
if ref == nil {
317+
return nil, errors.New("Missing reference")
318+
}
319+
320+
digestAlgorithmId := ref.DigestAlgo.Algorithm
321+
signedDigestValue, err := base64.StdEncoding.DecodeString(ref.DigestValue)
322+
if err != nil {
323+
return nil, err
324+
}
325+
244326
// Perform all transformations listed in the 'SignedInfo'
245327
// Basically, this means removing the 'SignedInfo'
246328
transformed, canonicalizer, err := ctx.transform(el, sig, ref)
247329
if err != nil {
248330
return nil, err
249331
}
250332

251-
digestAlgorithm := ref.DigestAlgo.Algorithm
252-
253-
// Digest the transformed XML and compare it to the 'DigestValue' from the 'SignedInfo'
254-
digest, err := ctx.digest(transformed, digestAlgorithm, canonicalizer)
333+
referencedBytes, err := canonicalizer.Canonicalize(transformed)
255334
if err != nil {
256335
return nil, err
257336
}
258337

259-
decodedDigestValue, err := base64.StdEncoding.DecodeString(ref.DigestValue)
338+
// use a known digest hashing algorithm
339+
hashAlgorithm, ok := digestAlgorithmsByIdentifier[digestAlgorithmId]
340+
if !ok {
341+
return nil, errors.New("Unknown digest algorithm: " + digestAlgorithmId)
342+
}
343+
344+
hash := hashAlgorithm.New()
345+
_, err = hash.Write(referencedBytes)
260346
if err != nil {
261347
return nil, err
262348
}
263349

264-
if !bytes.Equal(digest, decodedDigestValue) {
265-
return nil, errors.New("Signature could not be verified")
266-
}
267-
if sig.SignatureValue == nil {
350+
computedDigest := hash.Sum(nil)
351+
/* Digest the transformed XML and compare it to the 'DigestValue' from the 'SignedInfo'
352+
digest, err := ctx.digest(transformed, digestAlgorithm, canonicalizer)
353+
*/
354+
355+
if !bytes.Equal(computedDigest, signedDigestValue) {
268356
return nil, errors.New("Signature could not be verified")
269357
}
270358

271-
// Decode the 'SignatureValue' so we can compare against it
272-
decodedSignature, err := base64.StdEncoding.DecodeString(sig.SignatureValue.Data)
273-
if err != nil {
274-
return nil, errors.New("Could not decode signature")
359+
if !(len(computedDigest) >= 20) {
360+
return nil, errors.New("Computed digest is less than 20 something went wrong")
275361
}
276362

277-
// Actually verify the 'SignedInfo' was signed by a trusted source
278-
signatureMethod := sig.SignedInfo.SignatureMethod.Algorithm
279-
err = ctx.verifySignedInfo(sig, canonicalizer, signatureMethod, cert, decodedSignature)
363+
// now only the referencedBytes is verified,
364+
// unmarshal into new etree
365+
doc := etree.NewDocument()
366+
err = doc.ReadFromBytes(referencedBytes)
280367
if err != nil {
281368
return nil, err
282369
}
283370

284-
return transformed, nil
371+
return doc.Root(), nil
285372
}
286373

287374
func contains(roots []*x509.Certificate, cert *x509.Certificate) bool {

0 commit comments

Comments
 (0)