@@ -362,6 +362,46 @@ SignedXml.prototype.checkSignature = function (xml, callback) {
362362
363363 var doc = new Dom ( ) . parseFromString ( xml ) ;
364364
365+ // Reset the references as only references from our re-parsed signedInfo node can be trusted
366+ this . references = [ ] ;
367+
368+ const unverifiedSignedInfoCanon = this . getCanonSignedInfoXml ( doc ) ;
369+ if ( ! unverifiedSignedInfoCanon ) {
370+ if ( callback ) {
371+ callback ( new Error ( "Canonical signed info cannot be empty" ) ) ;
372+ return ;
373+ }
374+
375+ throw new Error ( "Canonical signed info cannot be empty" ) ;
376+ }
377+
378+ // unsigned, verify later to keep with consistent callback behavior
379+ const parsedUnverifiedSignedInfo = new Dom ( ) . parseFromString ( unverifiedSignedInfoCanon , "text/xml" ) ;
380+
381+ const unverifiedSignedInfoDoc = parsedUnverifiedSignedInfo . documentElement ;
382+ if ( ! unverifiedSignedInfoDoc ) {
383+ if ( callback ) {
384+ callback ( new Error ( "Could not parse signedInfoCanon into a document" ) ) ;
385+ return ;
386+ }
387+
388+ throw new Error ( "Could not parse signedInfoCanon into a document" ) ;
389+ }
390+
391+ const references = utils . findChilds ( unverifiedSignedInfoDoc , "Reference" ) ;
392+ if ( references . length === 0 ) {
393+ if ( callback ) {
394+ callback ( new Error ( "could not find any Reference elements" ) ) ;
395+ return ;
396+ }
397+
398+ throw new Error ( "could not find any Reference elements" ) ;
399+ }
400+
401+ for ( const reference of references ) {
402+ this . loadReference ( reference ) ;
403+ }
404+
365405 if ( ! this . validateReferences ( doc ) ) {
366406 if ( ! callback ) {
367407 return false ;
@@ -371,6 +411,7 @@ SignedXml.prototype.checkSignature = function (xml, callback) {
371411 }
372412 }
373413
414+ // Stage B: Take the signature algorithm and key and verify the SignatureValue against the canonicalized SignedInfo
374415 if ( ! callback ) {
375416 // Synchronous flow
376417 if ( ! this . validateSignatureValue ( doc ) ) {
@@ -394,7 +435,14 @@ SignedXml.prototype.checkSignature = function (xml, callback) {
394435
395436SignedXml . prototype . getCanonSignedInfoXml = function ( doc ) {
396437 var signedInfo = utils . findChilds ( this . signatureNode , "SignedInfo" ) ;
397- if ( signedInfo . length == 0 ) throw new Error ( "could not find SignedInfo element in the message" ) ;
438+ if ( signedInfo . length == 0 ) {
439+ throw new Error ( "could not find SignedInfo element in the message" ) ;
440+ }
441+ if ( signedInfo . length > 1 ) {
442+ throw new Error (
443+ "could not get canonicalized signed info for a signature that contains multiple SignedInfo nodes"
444+ ) ;
445+ }
398446
399447 if (
400448 this . canonicalizationAlgorithm === "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" ||
@@ -477,7 +525,7 @@ SignedXml.prototype.validateReferences = function (doc) {
477525
478526 var ref = this . references [ r ] ;
479527
480- var uri = ref . uri [ 0 ] == "#" ? ref . uri . substring ( 1 ) : ref . uri ;
528+ var uri = ref . uri ? ( ref . uri [ 0 ] == "#" ? ref . uri . substring ( 1 ) : ref . uri ) : "" ;
481529 var elem = [ ] ;
482530
483531 if ( uri == "" ) {
@@ -594,11 +642,43 @@ SignedXml.prototype.loadSignature = function (signatureNode) {
594642 ".//*[local-name(.)='SignatureMethod']/@Algorithm"
595643 ) . value ;
596644
597- this . references = [ ] ;
598- var references = xpath . select (
599- ".//*[local-name(.)='SignedInfo']/*[local-name(.)='Reference']" ,
600- signatureNode
645+ const signedInfoNodes = utils . findChilds ( this . signatureNode , "SignedInfo" ) ;
646+ if ( signedInfoNodes . length == 0 ) {
647+ throw new Error ( "no signed info node found" ) ;
648+ }
649+ if ( signedInfoNodes . length > 1 ) {
650+ throw new Error ( "could not load signature that contains multiple SignedInfo nodes" ) ;
651+ }
652+
653+ // Try to operate on the c14n version of signedInfo. This forces the initial getReferences()
654+ // API call to always return references that are loaded under the canonical SignedInfo
655+ // in the case that the client access the .references **before** signature verification.
656+
657+ // Ensure canonicalization algorithm is exclusive, otherwise we'd need the entire document
658+ let canonicalizationAlgorithmForSignedInfo = this . canonicalizationAlgorithm ;
659+ if (
660+ ! canonicalizationAlgorithmForSignedInfo ||
661+ canonicalizationAlgorithmForSignedInfo ===
662+ "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" ||
663+ canonicalizationAlgorithmForSignedInfo ===
664+ "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"
665+ ) {
666+ canonicalizationAlgorithmForSignedInfo = "http://www.w3.org/2001/10/xml-exc-c14n#" ;
667+ }
668+
669+ const temporaryCanonSignedInfo = this . getCanonXml (
670+ [ this . canonicalizationAlgorithm || "http://www.w3.org/2001/10/xml-exc-c14n#" ] ,
671+ signedInfoNodes [ 0 ]
601672 ) ;
673+ const temporaryCanonSignedInfoXml = new Dom ( ) . parseFromString (
674+ temporaryCanonSignedInfo ,
675+ "text/xml"
676+ ) ;
677+ const signedInfoDoc = temporaryCanonSignedInfoXml . documentElement ;
678+
679+ this . references = [ ] ;
680+
681+ const references = utils . findChilds ( signedInfoDoc , "Reference" ) ;
602682 if ( references . length == 0 ) throw new Error ( "could not find any Reference elements" ) ;
603683
604684 for ( var i in references ) {
@@ -632,10 +712,17 @@ SignedXml.prototype.loadReference = function (ref) {
632712 nodes = utils . findChilds ( ref , "DigestValue" ) ;
633713 if ( nodes . length == 0 )
634714 throw new Error ( "could not find DigestValue node in reference " + ref . toString ( ) ) ;
635- if ( nodes [ 0 ] . childNodes . length == 0 || ! nodes [ 0 ] . firstChild . data ) {
636- throw new Error ( "could not find the value of DigestValue in " + nodes [ 0 ] . toString ( ) ) ;
715+
716+ if ( nodes . length > 1 ) {
717+ throw new Error (
718+ `could not load reference for a node that contains multiple DigestValue nodes: ${ ref . toString ( ) } `
719+ ) ;
720+ }
721+
722+ const digestValue = nodes [ 0 ] . textContent ;
723+ if ( ! digestValue ) {
724+ throw new Error ( `could not find the value of DigestValue in ${ ref . toString ( ) } ` ) ;
637725 }
638- var digestValue = nodes [ 0 ] . firstChild . data ;
639726
640727 var transforms = [ ] ;
641728 var inclusiveNamespacesPrefixList ;
@@ -688,11 +775,12 @@ SignedXml.prototype.loadReference = function (ref) {
688775 transforms . push ( "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" ) ;
689776 }
690777
778+ const refUri = ref . getAttribute ( "URI" ) || undefined ;
691779 this . addReference (
692780 null ,
693781 transforms ,
694782 digestAlgo ,
695- utils . findAttr ( ref , "URI" ) . value ,
783+ refUri ,
696784 digestValue ,
697785 inclusiveNamespacesPrefixList ,
698786 false
0 commit comments