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