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