@@ -383,6 +383,46 @@ SignedXml.prototype.checkSignature = function(xml, callback) {
383
383
384
384
var doc = new Dom ( ) . parseFromString ( xml )
385
385
386
+ // Reset the references as only references from our re-parsed signedInfo node can be trusted
387
+ this . references = [ ] ;
388
+
389
+ const unverifiedSignedInfoCanon = this . getCanonSignedInfoXml ( doc ) ;
390
+ if ( ! unverifiedSignedInfoCanon ) {
391
+ if ( callback ) {
392
+ callback ( new Error ( "Canonical signed info not be empty" ) ) ;
393
+ return ;
394
+ } else {
395
+ throw new Error ( "Canonical signed info not be empty" ) ;
396
+ }
397
+ }
398
+
399
+ // unsigned, verify later to keep with consistent callback behavior
400
+ const unverifiedParsedSignedInfo = new Dom ( ) . parseFromString ( unverifiedSignedInfoCanon , "text/xml" ) ;
401
+
402
+ const unverifiedSignedInfoDoc = unverifiedParsedSignedInfo . documentElement ;
403
+ if ( ! unverifiedSignedInfoDoc ) {
404
+ if ( callback ) {
405
+ callback ( new Error ( "Could not parse signedInfoCanon into a document" ) ) ;
406
+ return ;
407
+ } else {
408
+ throw new Error ( "Could not parse signedInfoCanon into a document" ) ;
409
+ }
410
+ }
411
+
412
+ const references = utils . findChilds ( unverifiedSignedInfoDoc , "Reference" ) ;
413
+ if ( references . length === 0 ) {
414
+ if ( callback ) {
415
+ callback ( new Error ( "Could not find any Reference elements" ) ) ;
416
+ return ;
417
+ } else {
418
+ throw new Error ( "Could not find any Reference elements" ) ;
419
+ }
420
+ }
421
+
422
+ for ( const reference of references ) {
423
+ this . loadReference ( reference ) ;
424
+ }
425
+
386
426
if ( ! this . validateReferences ( doc ) ) {
387
427
if ( ! callback ) {
388
428
return false ;
@@ -392,6 +432,7 @@ SignedXml.prototype.checkSignature = function(xml, callback) {
392
432
}
393
433
}
394
434
435
+ // Stage B: Take the signature algorithm and key and verify the SignatureValue against the canonicalized SignedInfo
395
436
if ( ! callback ) {
396
437
//Syncronous flow
397
438
if ( ! this . validateSignatureValue ( doc ) ) {
@@ -414,7 +455,14 @@ SignedXml.prototype.checkSignature = function(xml, callback) {
414
455
415
456
SignedXml . prototype . getCanonSignedInfoXml = function ( doc ) {
416
457
var signedInfo = utils . findChilds ( this . signatureNode , "SignedInfo" )
417
- if ( signedInfo . length == 0 ) throw new Error ( "could not find SignedInfo element in the message" )
458
+ if ( signedInfo . length == 0 ) {
459
+ throw new Error ( "could not find SignedInfo element in the message" )
460
+ }
461
+ if ( signedInfo . length > 1 ) {
462
+ throw new Error (
463
+ "could not get canonicalized signed info for a signature that contains multiple SignedInfo nodes" ,
464
+ ) ;
465
+ }
418
466
419
467
if ( this . canonicalizationAlgorithm === "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
420
468
|| this . canonicalizationAlgorithm === "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments" )
@@ -494,7 +542,7 @@ SignedXml.prototype.validateReferences = function(doc) {
494
542
495
543
var ref = this . references [ r ]
496
544
497
- var uri = ref . uri [ 0 ] == "#" ? ref . uri . substring ( 1 ) : ref . uri
545
+ var uri = ref . uri ? ( ref . uri [ 0 ] == "#" ? ref . uri . substring ( 1 ) : ref . uri ) : "" ;
498
546
var elem = [ ] ;
499
547
500
548
if ( uri == "" ) {
@@ -596,8 +644,43 @@ SignedXml.prototype.loadSignature = function(signatureNode) {
596
644
this . signatureAlgorithm =
597
645
utils . findFirst ( signatureNode , ".//*[local-name(.)='SignatureMethod']/@Algorithm" ) . value
598
646
647
+ const signedInfoNodes = utils . findChilds ( this . signatureNode , "SignedInfo" ) ;
648
+ if ( signedInfoNodes . length == 0 ) {
649
+ throw new Error ( "no signed info node found" ) ;
650
+ }
651
+ if ( signedInfoNodes . length > 1 ) {
652
+ throw new Error ( "could not load signature that contains multiple SignedInfo nodes" ) ;
653
+ }
654
+
655
+ // Try to operate on the c14n version of `signedInfo`. This forces the initial `getReferences()`
656
+ // API call to always return references that are loaded under the canonical `SignedInfo`
657
+ // in the case that the client access the `.references` **before** signature verification.
658
+
659
+ // Ensure canonicalization algorithm is exclusive, otherwise we'd need the entire document
660
+ let canonicalizationAlgorithmForSignedInfo = this . canonicalizationAlgorithm ;
661
+ if (
662
+ ! canonicalizationAlgorithmForSignedInfo ||
663
+ canonicalizationAlgorithmForSignedInfo ===
664
+ "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" ||
665
+ canonicalizationAlgorithmForSignedInfo ===
666
+ "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"
667
+ ) {
668
+ canonicalizationAlgorithmForSignedInfo = "http://www.w3.org/2001/10/xml-exc-c14n#" ;
669
+ }
670
+
671
+ const temporaryCanonSignedInfo = this . getCanonXml (
672
+ [ canonicalizationAlgorithmForSignedInfo ] ,
673
+ signedInfoNodes [ 0 ] ,
674
+ ) ;
675
+ const temporaryCanonSignedInfoXml = new Dom ( ) . parseFromString (
676
+ temporaryCanonSignedInfo ,
677
+ "text/xml" ,
678
+ ) ;
679
+ const signedInfoDoc = temporaryCanonSignedInfoXml . documentElement ;
680
+
599
681
this . references = [ ]
600
- var references = xpath . select ( ".//*[local-name(.)='SignedInfo']/*[local-name(.)='Reference']" , signatureNode )
682
+
683
+ const references = utils . findChilds ( signedInfoDoc , "Reference" ) ;
601
684
if ( references . length == 0 ) throw new Error ( "could not find any Reference elements" )
602
685
603
686
for ( var i in references ) {
@@ -626,12 +709,16 @@ SignedXml.prototype.loadReference = function(ref) {
626
709
var digestAlgo = attr . value
627
710
628
711
nodes = utils . findChilds ( ref , "DigestValue" )
629
- if ( nodes . length == 0 ) throw new Error ( "could not find DigestValue node in reference " + ref . toString ( ) )
630
- if ( nodes [ 0 ] . childNodes . length == 0 || ! nodes [ 0 ] . firstChild . data )
631
- {
632
- throw new Error ( "could not find the value of DigestValue in " + nodes [ 0 ] . toString ( ) )
712
+ if ( nodes . length > 1 ) {
713
+ throw new Error (
714
+ `could not load reference for a node that contains multiple DigestValue nodes: ${ ref . toString ( ) } ` ,
715
+ ) ;
716
+ }
717
+
718
+ const digestValue = nodes [ 0 ] . textContent ;
719
+ if ( ! digestValue ) {
720
+ throw new Error ( `could not find the value of DigestValue in ${ ref . toString ( ) } ` ) ;
633
721
}
634
- var digestValue = nodes [ 0 ] . firstChild . data
635
722
636
723
var transforms = [ ]
637
724
var inclusiveNamespacesPrefixList ;
@@ -679,7 +766,8 @@ SignedXml.prototype.loadReference = function(ref) {
679
766
transforms . push ( "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" )
680
767
}
681
768
682
- this . addReference ( null , transforms , digestAlgo , utils . findAttr ( ref , "URI" ) . value , digestValue , inclusiveNamespacesPrefixList , false )
769
+ const refUri = ref . getAttribute ( "URI" ) || undefined ;
770
+ this . addReference ( null , transforms , digestAlgo , refUri , digestValue , inclusiveNamespacesPrefixList , false )
683
771
}
684
772
685
773
SignedXml . prototype . addReference = function ( xpath , transforms , digestAlgorithm , uri , digestValue , inclusiveNamespacesPrefixList , isEmptyUri ) {
0 commit comments