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