@@ -383,6 +383,46 @@ SignedXml.prototype.checkSignature = function(xml, callback) {
383383
384384 var doc = new Dom ( ) . parseFromString ( xml )
385385
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+
386426 if ( ! this . validateReferences ( doc ) ) {
387427 if ( ! callback ) {
388428 return false ;
@@ -392,6 +432,7 @@ SignedXml.prototype.checkSignature = function(xml, callback) {
392432 }
393433 }
394434
435+ // Stage B: Take the signature algorithm and key and verify the SignatureValue against the canonicalized SignedInfo
395436 if ( ! callback ) {
396437 //Syncronous flow
397438 if ( ! this . validateSignatureValue ( doc ) ) {
@@ -414,7 +455,14 @@ SignedXml.prototype.checkSignature = function(xml, callback) {
414455
415456SignedXml . prototype . getCanonSignedInfoXml = function ( doc ) {
416457 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+ }
418466
419467 if ( this . canonicalizationAlgorithm === "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
420468 || this . canonicalizationAlgorithm === "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments" )
@@ -494,7 +542,7 @@ SignedXml.prototype.validateReferences = function(doc) {
494542
495543 var ref = this . references [ r ]
496544
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 ) : "" ;
498546 var elem = [ ] ;
499547
500548 if ( uri == "" ) {
@@ -596,8 +644,43 @@ SignedXml.prototype.loadSignature = function(signatureNode) {
596644 this . signatureAlgorithm =
597645 utils . findFirst ( signatureNode , ".//*[local-name(.)='SignatureMethod']/@Algorithm" ) . value
598646
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+
599681 this . references = [ ]
600- var references = xpath . select ( ".//*[local-name(.)='SignedInfo']/*[local-name(.)='Reference']" , signatureNode )
682+
683+ const references = utils . findChilds ( signedInfoDoc , "Reference" ) ;
601684 if ( references . length == 0 ) throw new Error ( "could not find any Reference elements" )
602685
603686 for ( var i in references ) {
@@ -626,12 +709,16 @@ SignedXml.prototype.loadReference = function(ref) {
626709 var digestAlgo = attr . value
627710
628711 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 ( ) } ` ) ;
633721 }
634- var digestValue = nodes [ 0 ] . firstChild . data
635722
636723 var transforms = [ ]
637724 var inclusiveNamespacesPrefixList ;
@@ -679,7 +766,8 @@ SignedXml.prototype.loadReference = function(ref) {
679766 transforms . push ( "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" )
680767 }
681768
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 )
683771}
684772
685773SignedXml . prototype . addReference = function ( xpath , transforms , digestAlgorithm , uri , digestValue , inclusiveNamespacesPrefixList , isEmptyUri ) {
0 commit comments