55namespace SimpleSAML \XMLSecurity \XML ;
66
77use DOMElement ;
8- use SimpleSAML \XMLSecurity \Utils \XML ;
8+ use SimpleSAML \Assert \Assert ;
9+ use SimpleSAML \XMLSecurity \Constants as C ;
10+ use SimpleSAML \XMLSecurity \Exception \ReferenceValidationFailedException ;
11+ use SimpleSAML \XMLSecurity \Utils \XPath ;
12+ use SimpleSAML \XMLSecurity \XML \ds \Transforms ;
913
1014/**
1115 * A trait implementing the CanonicalizableElementInterface.
@@ -25,6 +29,64 @@ trait CanonicalizableElementTrait
2529 abstract protected function getOriginalXML (): DOMElement ;
2630
2731
32+ /**
33+ * Canonicalize any given node.
34+ *
35+ * @param \DOMElement $element The DOM element that needs canonicalization.
36+ * @param string $c14nMethod The identifier of the canonicalization algorithm to use.
37+ * See \SimpleSAML\XMLSecurity\Constants.
38+ * @param string[]|null $xpaths An array of xpaths to filter the nodes by. Defaults to null (no filters).
39+ * @param string[]|null $prefixes An array of namespace prefixes to filter the nodes by.
40+ * Defaults to null (no filters).
41+ *
42+ * @return string The canonical representation of the given DOM node, according to the algorithm requested.
43+ */
44+ public function canonicalizeData (
45+ DOMElement $ element ,
46+ string $ c14nMethod ,
47+ ?array $ xpaths = null ,
48+ ?array $ prefixes = null ,
49+ ): string {
50+ $ withComments = match ($ c14nMethod ) {
51+ C::C14N_EXCLUSIVE_WITH_COMMENTS , C::C14N_INCLUSIVE_WITH_COMMENTS => true ,
52+ default => false ,
53+ };
54+ $ exclusive = match ($ c14nMethod ) {
55+ C::C14N_EXCLUSIVE_WITH_COMMENTS , C::C14N_EXCLUSIVE_WITHOUT_COMMENTS => true ,
56+ default => false ,
57+ };
58+
59+ if (
60+ is_null ($ xpaths )
61+ && ($ element ->ownerDocument !== null )
62+ && ($ element ->ownerDocument ->documentElement !== null )
63+ && $ element ->isSameNode ($ element ->ownerDocument ->documentElement )
64+ ) {
65+ // check for any PI or comments as they would have been excluded
66+ $ current = $ element ;
67+ for ($ refNode = $ current ->previousSibling ; $ refNode !== null ; $ current = $ refNode ) {
68+ if (
69+ (($ refNode ->nodeType === XML_COMMENT_NODE ) && $ withComments )
70+ || $ refNode ->nodeType === XML_PI_NODE
71+ ) {
72+ break ;
73+ }
74+ }
75+
76+ if ($ refNode === null ) {
77+ $ element = $ element ->ownerDocument ;
78+ }
79+ }
80+
81+ $ ret = $ element ->C14N ($ exclusive , $ withComments , $ xpaths , $ prefixes );
82+ if ($ ret === false ) {
83+ // GHSA-h25p-2wxc-6584
84+ throw new CanonicalizationFailedException ();
85+ }
86+ return $ ret ;
87+ }
88+
89+
2890 /**
2991 * Get the canonical (string) representation of this object.
3092 *
@@ -39,7 +101,61 @@ abstract protected function getOriginalXML(): DOMElement;
39101 #[\NoDiscard]
40102 public function canonicalize (string $ method , ?array $ xpaths = null , ?array $ prefixes = null ): string
41103 {
42- return XML ::canonicalizeData ($ this ->getOriginalXML (), $ method , $ xpaths , $ prefixes );
104+ return $ this ->canonicalizeData ($ this ->getOriginalXML (), $ method , $ xpaths , $ prefixes );
105+ }
106+
107+
108+ /**
109+ * Process all transforms specified by a given Reference element.
110+ *
111+ * @param \SimpleSAML\XMLSecurity\XML\ds\Transforms $transforms The transforms to apply.
112+ * @param \DOMElement $data The data referenced.
113+ *
114+ * @return string The canonicalized data after applying all transforms specified by $ref.
115+ *
116+ * @see http://www.w3.org/TR/xmldsig-core/#sec-ReferenceProcessingModel
117+ */
118+ public function processTransforms (
119+ Transforms $ transforms ,
120+ DOMElement $ data ,
121+ ): string {
122+ $ canonicalMethod = C::C14N_EXCLUSIVE_WITHOUT_COMMENTS ;
123+ $ arXPath = null ;
124+ $ prefixList = null ;
125+ foreach ($ transforms ->getTransform () as $ transform ) {
126+ $ canonicalMethod = $ transform ->getAlgorithm ()->getValue ();
127+ switch ($ canonicalMethod ) {
128+ case C::C14N_EXCLUSIVE_WITHOUT_COMMENTS :
129+ case C::C14N_EXCLUSIVE_WITH_COMMENTS :
130+ $ inclusiveNamespaces = $ transform ->getInclusiveNamespaces ();
131+ if ($ inclusiveNamespaces !== null ) {
132+ $ prefixes = $ inclusiveNamespaces ->getPrefixes ();
133+ if ($ prefixes !== null ) {
134+ $ prefixList = array_map ('strval ' , $ prefixes ->toArray ());
135+ }
136+ }
137+ break ;
138+ case XPATH_C ::XPATH10_URI :
139+ $ xpath = $ transform ->getXPath ();
140+ if ($ xpath !== null ) {
141+ $ arXPath = [];
142+ $ xpathValue = $ xpath ->getContent ()->getValue ();
143+ $ arXPath ['query ' ] = '(.//. | .//@* | .//namespace::*)[ ' . $ xpathValue . '] ' ;
144+
145+ // $arXpath['namespaces'] = $xpath->getNamespaces();
146+ // TODO: review if $nsnode->localName is equivalent to the keys in getNamespaces()
147+ // $nslist = $xp->query('./namespace::*', $node);
148+ // foreach ($nslist as $nsnode) {
149+ // if ($nsnode->localName != "xml") {
150+ // $arXPath['namespaces'][$nsnode->localName] = $nsnode->nodeValue;
151+ // }
152+ // }
153+ }
154+ break ;
155+ }
156+ }
157+
158+ return $ this ->canonicalizeData ($ data , $ canonicalMethod , $ arXPath , $ prefixList );
43159 }
44160
45161
0 commit comments