@@ -202,6 +202,48 @@ class SignedXml {
202202
203203 final doc = parseFromString (xml);
204204
205+ // Reset the references as only references from our re-parsed signedInfo node can be trusted
206+ this .references.clear ();
207+
208+ final unverifiedSignedInfoCanon = _getCanonSignedInfoXml (doc);
209+ if (unverifiedSignedInfoCanon.isEmpty) {
210+ if (callback != null ) {
211+ callback (ArgumentError ('Canonical signed info cannot be empty' ), false );
212+ return false ;
213+ }
214+
215+ throw ArgumentError ('Canonical signed info cannot be empty' );
216+ }
217+
218+ // unsigned, verify later to keep with consistent callback behavior
219+ final parsedUnverifiedSignedInfo =
220+ parseFromString (unverifiedSignedInfoCanon);
221+ final unverifiedSignedInfoDoc = parsedUnverifiedSignedInfo.document;
222+ if (unverifiedSignedInfoDoc == null ) {
223+ if (callback != null ) {
224+ callback (
225+ ArgumentError ('Could not parse signedInfoCanon into a document' ),
226+ false );
227+ return false ;
228+ }
229+
230+ throw ArgumentError ('Could not parse signedInfoCanon into a document' );
231+ }
232+
233+ final references = findChilds (unverifiedSignedInfoDoc, 'Reference' );
234+ if (references.isEmpty) {
235+ if (callback != null ) {
236+ callback (ArgumentError ('could not find any Reference elements' ), false );
237+ return false ;
238+ }
239+
240+ throw ArgumentError ('could not find any Reference elements' );
241+ }
242+
243+ for (var reference in references) {
244+ _loadReference (reference);
245+ }
246+
205247 if (! _validateReferences (doc)) {
206248 if (callback == null ) {
207249 return false ;
@@ -211,6 +253,7 @@ class SignedXml {
211253 }
212254 }
213255
256+ // Stage B: Take the signature algorithm and key and verify the SignatureValue against the canonicalized SignedInfo
214257 if (callback == null ) {
215258 //Synchronous flow
216259 if (! _validateSignatureValue (doc)) {
@@ -237,6 +280,10 @@ class SignedXml {
237280 if (signedInfo.isEmpty) {
238281 throw ArgumentError ('could not find SignedInfo element in the message' );
239282 }
283+ if (signedInfo.length > 1 ) {
284+ throw ArgumentError (
285+ 'could not get canonicalized signed info for a signature that contains multiple SignedInfo nodes' );
286+ }
240287
241288 // Since in Dart the doc is always a XmlDocument
242289 // if (canonicalizationAlgorithm == 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315'
@@ -321,7 +368,9 @@ class SignedXml {
321368
322369 bool _validateReferences (XmlDocument doc) {
323370 for (final ref in references) {
324- final uri = ref.uri.startsWith ('#' ) ? ref.uri.substring (1 ) : ref.uri;
371+ final uri = ref.uri != null
372+ ? (ref.uri! .startsWith ('#' ) ? ref.uri! .substring (1 ) : ref.uri! )
373+ : '' ;
325374 final elem = < XPathNode <XmlNode >> [];
326375
327376 if (uri == '' ) {
@@ -399,15 +448,46 @@ class SignedXml {
399448 throw ArgumentError ('could not find SignatureMethod/@Algorithm element' );
400449 }
401450 signatureAlgorithm = nodes.attr ?? '' ;
402- references.clear ();
403- final refs = XmlXPath .node (signatureNode)
404- .query (".//*[local-name()='SignedInfo']/*[local-name()='Reference']" );
405- if (refs.nodes.isEmpty) {
451+ final signedInfoNodes = findChilds (signatureNode, 'SignedInfo' );
452+ if (signedInfoNodes.isEmpty) {
453+ throw ArgumentError ('no signed info node found' );
454+ }
455+ if (signedInfoNodes.length > 1 ) {
456+ throw ArgumentError (
457+ 'could not load signature that contains multiple SignedInfo nodes' );
458+ }
459+
460+ // Try to operate on the c14n version of signedInfo. This forces the initial getReferences()
461+ // API call to always return references that are loaded under the canonical SignedInfo
462+ // in the case that the client access the .references **before** signature verification.
463+
464+ // Ensure canonicalization algorithm is exclusive, otherwise we'd need the entire document
465+ var canonicalizationAlgorithmForSignedInfo = canonicalizationAlgorithm;
466+ if (canonicalizationAlgorithmForSignedInfo ==
467+ "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" ||
468+ canonicalizationAlgorithmForSignedInfo ==
469+ "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments" ) {
470+ canonicalizationAlgorithmForSignedInfo =
471+ "http://www.w3.org/2001/10/xml-exc-c14n#" ;
472+ }
473+
474+ final temporaryCanonSignedInfo = _getCanonXml (
475+ [canonicalizationAlgorithm],
476+ signedInfoNodes.first,
477+ );
478+ final temporaryCanonSignedInfoXml =
479+ parseFromString (temporaryCanonSignedInfo);
480+ final signedInfoDoc = temporaryCanonSignedInfoXml.rootElement;
481+
482+ this .references.clear ();
483+
484+ final references = findChilds (signedInfoDoc, 'Reference' );
485+ if (references.isEmpty) {
406486 throw ArgumentError ('could not find any Reference elements' );
407487 }
408488
409- for (final ref in refs.nodes ) {
410- _loadReference (ref.node );
489+ for (final ref in references ) {
490+ _loadReference (ref);
411491 }
412492
413493 signatureValue =
@@ -439,12 +519,16 @@ class SignedXml {
439519 if (nodes.isEmpty) {
440520 throw ArgumentError ('could not find DigestValue in reference $ref ' );
441521 }
442- if (nodes.first.firstChild == null ||
443- (nodes.first.firstChild ? .value ?? '' ) == '' ) {
522+
523+ if (nodes.length > 1 ) {
444524 throw ArgumentError (
445- 'could not find the value of DigestValue in ${nodes .first }' );
525+ 'could not load reference for a node that contains multiple DigestValue nodes: $ref ' );
526+ }
527+
528+ final digestValue = nodes.first.innerText;
529+ if (digestValue.isEmpty) {
530+ throw ArgumentError ('could not find the value of DigestValue in $ref ' );
446531 }
447- final digestValue = nodes.first.firstChild! .value;
448532
449533 final transforms = < String > [];
450534 String ? inclusiveNamespacesPrefixList;
@@ -490,14 +574,9 @@ class SignedXml {
490574 transforms.add ('http://www.w3.org/TR/2001/REC-xml-c14n-20010315' );
491575 }
492576
493- addReference (
494- null ,
495- transforms,
496- digestAlgo,
497- findAttr (ref, 'URI' )? .value ?? '' ,
498- digestValue,
499- inclusiveNamespacesPrefixList,
500- false );
577+ final refUri = ref.getAttribute ('URI' );
578+ addReference (null , transforms, digestAlgo, refUri, digestValue,
579+ inclusiveNamespacesPrefixList, false );
501580 }
502581
503582 void addReference (String ? xpath,
@@ -1079,7 +1158,7 @@ class _Reference {
10791158 String ? xpath;
10801159 final List <String > transforms;
10811160 final String digestAlgorithm;
1082- String uri;
1161+ String ? uri;
10831162 final String digestValue;
10841163 final String ? inclusiveNamespacesPrefixList;
10851164 final bool isEmptyUri;
0 commit comments