diff --git a/src/libraries/System.Security.Cryptography.Xml/src/Resources/Strings.resx b/src/libraries/System.Security.Cryptography.Xml/src/Resources/Strings.resx
index 7ae8e2bde12314..e6cb1aff13c863 100644
--- a/src/libraries/System.Security.Cryptography.Xml/src/Resources/Strings.resx
+++ b/src/libraries/System.Security.Cryptography.Xml/src/Resources/Strings.resx
@@ -183,6 +183,9 @@
Signing key is not loaded.
+
+ The XML element has exceeded the maximum nesting depth allowed for decryption.
+
Symmetric algorithm is not specified. If the application has been trimmed, ensure the required algorithm implementations are preserved.
diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalizationDispatcher.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalizationDispatcher.cs
index b73839e5c7cfd1..f20bcdc3ac2202 100644
--- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalizationDispatcher.cs
+++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalizationDispatcher.cs
@@ -10,15 +10,32 @@ namespace System.Security.Cryptography.Xml
// implement ICanonicalizableNode; so a manual dispatch is sometimes necessary.
internal static class CanonicalizationDispatcher
{
+ [ThreadStatic]
+ private static int t_depth;
+
public static void Write(XmlNode node, StringBuilder strBuilder, DocPosition docPos, AncestralNamespaceContextManager anc)
{
- if (node is ICanonicalizableNode)
+ int maxDepth = LocalAppContextSwitches.DangerousMaxRecursionDepth;
+ if (maxDepth > 0 && t_depth > maxDepth)
+ {
+ throw new CryptographicException(SR.Cryptography_Xml_MaxDepthExceeded);
+ }
+
+ t_depth++;
+ try
{
- ((ICanonicalizableNode)node).Write(strBuilder, docPos, anc);
+ if (node is ICanonicalizableNode canonicalizableNode)
+ {
+ canonicalizableNode.Write(strBuilder, docPos, anc);
+ }
+ else
+ {
+ WriteGenericNode(node, strBuilder, docPos, anc);
+ }
}
- else
+ finally
{
- WriteGenericNode(node, strBuilder, docPos, anc);
+ t_depth--;
}
}
@@ -35,13 +52,27 @@ public static void WriteGenericNode(XmlNode node, StringBuilder strBuilder, DocP
public static void WriteHash(XmlNode node, HashAlgorithm hash, DocPosition docPos, AncestralNamespaceContextManager anc)
{
- if (node is ICanonicalizableNode)
+ int maxDepth = LocalAppContextSwitches.DangerousMaxRecursionDepth;
+ if (maxDepth > 0 && t_depth > maxDepth)
+ {
+ throw new CryptographicException(SR.Cryptography_Xml_MaxDepthExceeded);
+ }
+
+ t_depth++;
+ try
{
- ((ICanonicalizableNode)node).WriteHash(hash, docPos, anc);
+ if (node is ICanonicalizableNode canonicalizableNode)
+ {
+ canonicalizableNode.WriteHash(hash, docPos, anc);
+ }
+ else
+ {
+ WriteHashGenericNode(node, hash, docPos, anc);
+ }
}
- else
+ finally
{
- WriteHashGenericNode(node, hash, docPos, anc);
+ t_depth--;
}
}
diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/EncryptedData.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/EncryptedData.cs
index 1c8da88a6d8370..514e075bed4f17 100644
--- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/EncryptedData.cs
+++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/EncryptedData.cs
@@ -14,55 +14,63 @@ public override void LoadXml(XmlElement value)
{
ArgumentNullException.ThrowIfNull(value);
- XmlNamespaceManager nsm = new XmlNamespaceManager(value.OwnerDocument.NameTable);
- nsm.AddNamespace("enc", EncryptedXml.XmlEncNamespaceUrl);
- nsm.AddNamespace("ds", SignedXml.XmlDsigNamespaceUrl);
-
- Id = Utils.GetAttribute(value, "Id", EncryptedXml.XmlEncNamespaceUrl);
- Type = Utils.GetAttribute(value, "Type", EncryptedXml.XmlEncNamespaceUrl);
- MimeType = Utils.GetAttribute(value, "MimeType", EncryptedXml.XmlEncNamespaceUrl);
- Encoding = Utils.GetAttribute(value, "Encoding", EncryptedXml.XmlEncNamespaceUrl);
-
- XmlNode? encryptionMethodNode = value.SelectSingleNode("enc:EncryptionMethod", nsm);
-
- // EncryptionMethod
- EncryptionMethod = new EncryptionMethod();
- if (encryptionMethodNode != null)
- EncryptionMethod.LoadXml((encryptionMethodNode as XmlElement)!);
-
- // Key Info
- KeyInfo = new KeyInfo();
- XmlNode? keyInfoNode = value.SelectSingleNode("ds:KeyInfo", nsm);
- if (keyInfoNode != null)
- KeyInfo.LoadXml((keyInfoNode as XmlElement)!);
-
- // CipherData
- XmlNode? cipherDataNode = value.SelectSingleNode("enc:CipherData", nsm);
- if (cipherDataNode == null)
- throw new CryptographicException(SR.Cryptography_Xml_MissingCipherData);
-
- CipherData = new CipherData();
- CipherData.LoadXml((cipherDataNode as XmlElement)!);
-
- // EncryptionProperties
- XmlNode? encryptionPropertiesNode = value.SelectSingleNode("enc:EncryptionProperties", nsm);
- if (encryptionPropertiesNode != null)
+ IncrementLoadXmlCurrentThreadDepth();
+ try
{
- // Select the EncryptionProperty elements inside the EncryptionProperties element
- XmlNodeList? encryptionPropertyNodes = encryptionPropertiesNode.SelectNodes("enc:EncryptionProperty", nsm);
- if (encryptionPropertyNodes != null)
+ XmlNamespaceManager nsm = new XmlNamespaceManager(value.OwnerDocument.NameTable);
+ nsm.AddNamespace("enc", EncryptedXml.XmlEncNamespaceUrl);
+ nsm.AddNamespace("ds", SignedXml.XmlDsigNamespaceUrl);
+
+ Id = Utils.GetAttribute(value, "Id", EncryptedXml.XmlEncNamespaceUrl);
+ Type = Utils.GetAttribute(value, "Type", EncryptedXml.XmlEncNamespaceUrl);
+ MimeType = Utils.GetAttribute(value, "MimeType", EncryptedXml.XmlEncNamespaceUrl);
+ Encoding = Utils.GetAttribute(value, "Encoding", EncryptedXml.XmlEncNamespaceUrl);
+
+ XmlNode? encryptionMethodNode = value.SelectSingleNode("enc:EncryptionMethod", nsm);
+
+ // EncryptionMethod
+ EncryptionMethod = new EncryptionMethod();
+ if (encryptionMethodNode != null)
+ EncryptionMethod.LoadXml((encryptionMethodNode as XmlElement)!);
+
+ // Key Info
+ KeyInfo = new KeyInfo();
+ XmlNode? keyInfoNode = value.SelectSingleNode("ds:KeyInfo", nsm);
+ if (keyInfoNode != null)
+ KeyInfo.LoadXml((keyInfoNode as XmlElement)!);
+
+ // CipherData
+ XmlNode? cipherDataNode = value.SelectSingleNode("enc:CipherData", nsm);
+ if (cipherDataNode == null)
+ throw new CryptographicException(SR.Cryptography_Xml_MissingCipherData);
+
+ CipherData = new CipherData();
+ CipherData.LoadXml((cipherDataNode as XmlElement)!);
+
+ // EncryptionProperties
+ XmlNode? encryptionPropertiesNode = value.SelectSingleNode("enc:EncryptionProperties", nsm);
+ if (encryptionPropertiesNode != null)
{
- foreach (XmlNode node in encryptionPropertyNodes)
+ // Select the EncryptionProperty elements inside the EncryptionProperties element
+ XmlNodeList? encryptionPropertyNodes = encryptionPropertiesNode.SelectNodes("enc:EncryptionProperty", nsm);
+ if (encryptionPropertyNodes != null)
{
- EncryptionProperty ep = new EncryptionProperty();
- ep.LoadXml((node as XmlElement)!);
- EncryptionProperties.Add(ep);
+ foreach (XmlNode node in encryptionPropertyNodes)
+ {
+ EncryptionProperty ep = new EncryptionProperty();
+ ep.LoadXml((node as XmlElement)!);
+ EncryptionProperties.Add(ep);
+ }
}
}
- }
- // Save away the cached value
- _cachedXml = value;
+ // Save away the cached value
+ _cachedXml = value;
+ }
+ finally
+ {
+ DecrementLoadXmlCurrentThreadDepth();
+ }
}
public override XmlElement GetXml()
diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/EncryptedKey.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/EncryptedKey.cs
index ed21cbb5d46551..c96da07fc72c18 100644
--- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/EncryptedKey.cs
+++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/EncryptedKey.cs
@@ -49,91 +49,99 @@ public override void LoadXml(XmlElement value)
{
ArgumentNullException.ThrowIfNull(value);
- XmlNamespaceManager nsm = new XmlNamespaceManager(value.OwnerDocument.NameTable);
- nsm.AddNamespace("enc", EncryptedXml.XmlEncNamespaceUrl);
- nsm.AddNamespace("ds", SignedXml.XmlDsigNamespaceUrl);
-
- Id = Utils.GetAttribute(value, "Id", EncryptedXml.XmlEncNamespaceUrl);
- Type = Utils.GetAttribute(value, "Type", EncryptedXml.XmlEncNamespaceUrl);
- MimeType = Utils.GetAttribute(value, "MimeType", EncryptedXml.XmlEncNamespaceUrl);
- Encoding = Utils.GetAttribute(value, "Encoding", EncryptedXml.XmlEncNamespaceUrl);
- Recipient = Utils.GetAttribute(value, "Recipient", EncryptedXml.XmlEncNamespaceUrl);
-
- XmlNode? encryptionMethodNode = value.SelectSingleNode("enc:EncryptionMethod", nsm);
-
- // EncryptionMethod
- EncryptionMethod = new EncryptionMethod();
- if (encryptionMethodNode != null)
- EncryptionMethod.LoadXml((encryptionMethodNode as XmlElement)!);
-
- // Key Info
- KeyInfo = new KeyInfo();
- XmlNode? keyInfoNode = value.SelectSingleNode("ds:KeyInfo", nsm);
- if (keyInfoNode != null)
- KeyInfo.LoadXml((keyInfoNode as XmlElement)!);
-
- // CipherData
- XmlNode? cipherDataNode = value.SelectSingleNode("enc:CipherData", nsm);
- if (cipherDataNode == null)
- throw new CryptographicException(SR.Cryptography_Xml_MissingCipherData);
-
- CipherData = new CipherData();
- CipherData.LoadXml((cipherDataNode as XmlElement)!);
-
- // EncryptionProperties
- XmlNode? encryptionPropertiesNode = value.SelectSingleNode("enc:EncryptionProperties", nsm);
- if (encryptionPropertiesNode != null)
+ IncrementLoadXmlCurrentThreadDepth();
+ try
{
- // Select the EncryptionProperty elements inside the EncryptionProperties element
- XmlNodeList? encryptionPropertyNodes = encryptionPropertiesNode.SelectNodes("enc:EncryptionProperty", nsm);
- if (encryptionPropertyNodes != null)
+ XmlNamespaceManager nsm = new XmlNamespaceManager(value.OwnerDocument.NameTable);
+ nsm.AddNamespace("enc", EncryptedXml.XmlEncNamespaceUrl);
+ nsm.AddNamespace("ds", SignedXml.XmlDsigNamespaceUrl);
+
+ Id = Utils.GetAttribute(value, "Id", EncryptedXml.XmlEncNamespaceUrl);
+ Type = Utils.GetAttribute(value, "Type", EncryptedXml.XmlEncNamespaceUrl);
+ MimeType = Utils.GetAttribute(value, "MimeType", EncryptedXml.XmlEncNamespaceUrl);
+ Encoding = Utils.GetAttribute(value, "Encoding", EncryptedXml.XmlEncNamespaceUrl);
+ Recipient = Utils.GetAttribute(value, "Recipient", EncryptedXml.XmlEncNamespaceUrl);
+
+ XmlNode? encryptionMethodNode = value.SelectSingleNode("enc:EncryptionMethod", nsm);
+
+ // EncryptionMethod
+ EncryptionMethod = new EncryptionMethod();
+ if (encryptionMethodNode != null)
+ EncryptionMethod.LoadXml((encryptionMethodNode as XmlElement)!);
+
+ // Key Info
+ KeyInfo = new KeyInfo();
+ XmlNode? keyInfoNode = value.SelectSingleNode("ds:KeyInfo", nsm);
+ if (keyInfoNode != null)
+ KeyInfo.LoadXml((keyInfoNode as XmlElement)!);
+
+ // CipherData
+ XmlNode? cipherDataNode = value.SelectSingleNode("enc:CipherData", nsm);
+ if (cipherDataNode == null)
+ throw new CryptographicException(SR.Cryptography_Xml_MissingCipherData);
+
+ CipherData = new CipherData();
+ CipherData.LoadXml((cipherDataNode as XmlElement)!);
+
+ // EncryptionProperties
+ XmlNode? encryptionPropertiesNode = value.SelectSingleNode("enc:EncryptionProperties", nsm);
+ if (encryptionPropertiesNode != null)
{
- foreach (XmlNode node in encryptionPropertyNodes)
+ // Select the EncryptionProperty elements inside the EncryptionProperties element
+ XmlNodeList? encryptionPropertyNodes = encryptionPropertiesNode.SelectNodes("enc:EncryptionProperty", nsm);
+ if (encryptionPropertyNodes != null)
{
- EncryptionProperty ep = new EncryptionProperty();
- ep.LoadXml((node as XmlElement)!);
- EncryptionProperties.Add(ep);
+ foreach (XmlNode node in encryptionPropertyNodes)
+ {
+ EncryptionProperty ep = new EncryptionProperty();
+ ep.LoadXml((node as XmlElement)!);
+ EncryptionProperties.Add(ep);
+ }
}
}
- }
- // CarriedKeyName
- XmlNode? carriedKeyNameNode = value.SelectSingleNode("enc:CarriedKeyName", nsm);
- if (carriedKeyNameNode != null)
- {
- CarriedKeyName = carriedKeyNameNode.InnerText;
- }
+ // CarriedKeyName
+ XmlNode? carriedKeyNameNode = value.SelectSingleNode("enc:CarriedKeyName", nsm);
+ if (carriedKeyNameNode != null)
+ {
+ CarriedKeyName = carriedKeyNameNode.InnerText;
+ }
- // ReferenceList
- XmlNode? referenceListNode = value.SelectSingleNode("enc:ReferenceList", nsm);
- if (referenceListNode != null)
- {
- // Select the DataReference elements inside the ReferenceList element
- XmlNodeList? dataReferenceNodes = referenceListNode.SelectNodes("enc:DataReference", nsm);
- if (dataReferenceNodes != null)
+ // ReferenceList
+ XmlNode? referenceListNode = value.SelectSingleNode("enc:ReferenceList", nsm);
+ if (referenceListNode != null)
{
- foreach (XmlNode node in dataReferenceNodes)
+ // Select the DataReference elements inside the ReferenceList element
+ XmlNodeList? dataReferenceNodes = referenceListNode.SelectNodes("enc:DataReference", nsm);
+ if (dataReferenceNodes != null)
{
- DataReference dr = new DataReference();
- dr.LoadXml((node as XmlElement)!);
- ReferenceList.Add(dr);
+ foreach (XmlNode node in dataReferenceNodes)
+ {
+ DataReference dr = new DataReference();
+ dr.LoadXml((node as XmlElement)!);
+ ReferenceList.Add(dr);
+ }
}
- }
- // Select the KeyReference elements inside the ReferenceList element
- XmlNodeList? keyReferenceNodes = referenceListNode.SelectNodes("enc:KeyReference", nsm);
- if (keyReferenceNodes != null)
- {
- foreach (XmlNode node in keyReferenceNodes)
+ // Select the KeyReference elements inside the ReferenceList element
+ XmlNodeList? keyReferenceNodes = referenceListNode.SelectNodes("enc:KeyReference", nsm);
+ if (keyReferenceNodes != null)
{
- KeyReference kr = new KeyReference();
- kr.LoadXml((node as XmlElement)!);
- ReferenceList.Add(kr);
+ foreach (XmlNode node in keyReferenceNodes)
+ {
+ KeyReference kr = new KeyReference();
+ kr.LoadXml((node as XmlElement)!);
+ ReferenceList.Add(kr);
+ }
}
}
- }
- // Save away the cached value
- _cachedXml = value;
+ // Save away the cached value
+ _cachedXml = value;
+ }
+ finally
+ {
+ DecrementLoadXmlCurrentThreadDepth();
+ }
}
public override XmlElement GetXml()
diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/EncryptedType.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/EncryptedType.cs
index 77d2ad8f85848f..3e2ac79f941db9 100644
--- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/EncryptedType.cs
+++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/EncryptedType.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Xml;
@@ -8,6 +9,9 @@ namespace System.Security.Cryptography.Xml
{
public abstract class EncryptedType
{
+ [ThreadStatic]
+ private static int t_depth;
+
private string? _id;
private string? _type;
private string? _mimeType;
@@ -72,6 +76,24 @@ public KeyInfo KeyInfo
set => field = value;
}
+ internal static void IncrementLoadXmlCurrentThreadDepth()
+ {
+ Debug.Assert(t_depth >= 0, "LoadXml current thread depth is negative.");
+ int maxDepth = LocalAppContextSwitches.DangerousMaxRecursionDepth;
+ if (maxDepth > 0 && t_depth > maxDepth)
+ {
+ throw new CryptographicException(SR.Cryptography_Xml_MaxDepthExceeded);
+ }
+
+ t_depth++;
+ }
+
+ internal static void DecrementLoadXmlCurrentThreadDepth()
+ {
+ Debug.Assert(t_depth > 0, "LoadXml current thread depth is already 0.");
+ t_depth--;
+ }
+
public virtual EncryptionMethod? EncryptionMethod
{
get { return _encryptionMethod; }
diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/EncryptedXml.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/EncryptedXml.cs
index bdbea9731e876d..edd847aa085a3b 100644
--- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/EncryptedXml.cs
+++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/EncryptedXml.cs
@@ -72,6 +72,22 @@ public class EncryptedXml
private int _xmlDsigSearchDepthCounter;
private int _xmlDsigSearchDepth;
+ private static readonly string[] s_defaultSafeTransformMethods =
+ {
+ // Built in canonicalization algorithms
+ SignedXml.XmlDsigC14NTransformUrl,
+ SignedXml.XmlDsigC14NWithCommentsTransformUrl,
+ SignedXml.XmlDsigExcC14NTransformUrl,
+ SignedXml.XmlDsigExcC14NWithCommentsTransformUrl,
+
+ // Other built in transform algorithms
+ SignedXml.XmlDsigBase64TransformUrl,
+ SignedXml.XmlLicenseTransformUrl,
+ // Keep this aligned with SignedXml's safe-transform allow-list. The decryption
+ // transform is further bounded by DangerousMaxRecursionDepth.
+ SignedXml.XmlDecryptionTransformUrl,
+ };
+
//
// public constructors
//
@@ -208,6 +224,11 @@ private byte[] GetCipherValue(CipherData cipherData)
{
throw new CryptographicException(SR.Cryptography_Xml_UriNotSupported);
}
+ if (!ReferenceUsesSafeTransformMethods(cipherData.CipherReference))
+ {
+ throw new CryptographicException(SR.Cryptography_Xml_NotSupportedCryptographicTransform);
+ }
+
decInputStream = tc.TransformToOctetStream(_document, _xmlResolver, baseUri);
}
else if (cipherData.CipherReference.Uri[0] == '#')
@@ -226,6 +247,11 @@ private byte[] GetCipherValue(CipherData cipherData)
{
throw new CryptographicException(SR.Cryptography_Xml_UriNotSupported);
}
+ if (!ReferenceUsesSafeTransformMethods(cipherData.CipherReference))
+ {
+ throw new CryptographicException(SR.Cryptography_Xml_NotSupportedCryptographicTransform);
+ }
+
decInputStream = tc.TransformToOctetStream(inputStream, _xmlResolver, baseUri);
}
else
@@ -934,5 +960,43 @@ public static byte[] DecryptKey(byte[] keyData, RSA rsa, bool useOAEP)
return rsaDeformatter.DecryptKeyExchange(keyData);
}
}
+
+ private static bool ReferenceUsesSafeTransformMethods(CipherReference reference)
+ {
+ // If the compatibility switch to allow dangerous encrypted XML transforms
+ // is enabled, skip the safe-transform allow-list check.
+ if (LocalAppContextSwitches.AllowDangerousEncryptedXmlTransforms)
+ {
+ return true;
+ }
+
+ TransformChain transformChain = reference.TransformChain;
+ int transformCount = transformChain.Count;
+
+ for (int i = 0; i < transformCount; i++)
+ {
+ Transform transform = transformChain[i];
+
+ if (!IsSafeTransform(transform.Algorithm!))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static bool IsSafeTransform(string transformAlgorithm)
+ {
+ foreach (string safeAlgorithm in s_defaultSafeTransformMethods)
+ {
+ if (string.Equals(safeAlgorithm, transformAlgorithm, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
}
}
diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/KeyInfo.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/KeyInfo.cs
index 86bd9685e31263..2398277d29562f 100644
--- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/KeyInfo.cs
+++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/KeyInfo.cs
@@ -64,48 +64,56 @@ public void LoadXml(XmlElement value)
{
ArgumentNullException.ThrowIfNull(value);
- XmlElement keyInfoElement = value;
- _id = Utils.GetAttribute(keyInfoElement, "Id", SignedXml.XmlDsigNamespaceUrl);
- if (!Utils.VerifyAttributes(keyInfoElement, "Id"))
- throw new CryptographicException(SR.Cryptography_Xml_InvalidElement, "KeyInfo");
-
- XmlNode? child = keyInfoElement.FirstChild;
- while (child != null)
+ EncryptedType.IncrementLoadXmlCurrentThreadDepth();
+ try
{
- XmlElement? elem = child as XmlElement;
- if (elem != null)
+ XmlElement keyInfoElement = value;
+ _id = Utils.GetAttribute(keyInfoElement, "Id", SignedXml.XmlDsigNamespaceUrl);
+ if (!Utils.VerifyAttributes(keyInfoElement, "Id"))
+ throw new CryptographicException(SR.Cryptography_Xml_InvalidElement, "KeyInfo");
+
+ XmlNode? child = keyInfoElement.FirstChild;
+ while (child != null)
{
- // Create the right type of KeyInfoClause; we use a combination of the namespace and tag name (local name)
- string kicString = elem.NamespaceURI + " " + elem.LocalName;
- // Special-case handling for KeyValue -- we have to go one level deeper
- if (kicString == "http://www.w3.org/2000/09/xmldsig# KeyValue")
+ XmlElement? elem = child as XmlElement;
+ if (elem != null)
{
- if (!Utils.VerifyAttributes(elem, (string[]?)null))
+ // Create the right type of KeyInfoClause; we use a combination of the namespace and tag name (local name)
+ string kicString = elem.NamespaceURI + " " + elem.LocalName;
+ // Special-case handling for KeyValue -- we have to go one level deeper
+ if (kicString == "http://www.w3.org/2000/09/xmldsig# KeyValue")
{
- throw new CryptographicException(SR.Cryptography_Xml_InvalidElement, "KeyInfo/KeyValue");
- }
- XmlNodeList nodeList2 = elem.ChildNodes;
- foreach (XmlNode node2 in nodeList2)
- {
- XmlElement? elem2 = node2 as XmlElement;
- if (elem2 != null)
+ if (!Utils.VerifyAttributes(elem, (string[]?)null))
+ {
+ throw new CryptographicException(SR.Cryptography_Xml_InvalidElement, "KeyInfo/KeyValue");
+ }
+ XmlNodeList nodeList2 = elem.ChildNodes;
+ foreach (XmlNode node2 in nodeList2)
{
- kicString += "/" + elem2.LocalName;
- break;
+ XmlElement? elem2 = node2 as XmlElement;
+ if (elem2 != null)
+ {
+ kicString += "/" + elem2.LocalName;
+ break;
+ }
}
}
- }
- KeyInfoClause? keyInfoClause = CryptoHelpers.CreateNonTransformFromName(kicString);
- // if we don't know what kind of KeyInfoClause we're looking at, use a generic KeyInfoNode:
- keyInfoClause ??= new KeyInfoNode();
+ KeyInfoClause? keyInfoClause = CryptoHelpers.CreateNonTransformFromName(kicString);
+ // if we don't know what kind of KeyInfoClause we're looking at, use a generic KeyInfoNode:
+ keyInfoClause ??= new KeyInfoNode();
- // Ask the create clause to fill itself with the corresponding XML
- keyInfoClause.LoadXml(elem);
- // Add it to our list of KeyInfoClauses
- AddClause(keyInfoClause);
+ // Ask the create clause to fill itself with the corresponding XML
+ keyInfoClause.LoadXml(elem);
+ // Add it to our list of KeyInfoClauses
+ AddClause(keyInfoClause);
+ }
+ child = child.NextSibling;
}
- child = child.NextSibling;
+ }
+ finally
+ {
+ EncryptedType.DecrementLoadXmlCurrentThreadDepth();
}
}
diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/LocalAppContextSwitches.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/LocalAppContextSwitches.cs
index 9c047f838b4a8d..cd3575ace46f1a 100644
--- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/LocalAppContextSwitches.cs
+++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/LocalAppContextSwitches.cs
@@ -7,26 +7,51 @@ namespace System
{
internal static partial class LocalAppContextSwitches
{
- internal static int MaxReferencesPerSignedInfo { get; } = InitializeMaxReferencesPerSignedInfo();
+ internal const int DefaultMaxRecursionDepth = 64;
+ internal const string DangerousMaxRecursionDepthAppContextSwitch = "System.Security.Cryptography.Xml.DangerousMaxRecursionDepth";
+ internal const string AllowDangerousEncryptedXmlTransformsAppContextSwitch = "System.Security.Cryptography.Xml.AllowDangerousEncryptedXmlTransforms";
- private static int InitializeMaxReferencesPerSignedInfo()
+ // 0 disables the limit for compatibility. Negative values fall back to the default.
+ internal static int DangerousMaxRecursionDepth { get; } =
+ GetInt32Config(DangerousMaxRecursionDepthAppContextSwitch, DefaultMaxRecursionDepth, allowNegative: false);
+
+ internal static bool AllowDangerousEncryptedXmlTransforms { get; } =
+ GetBooleanConfig(AllowDangerousEncryptedXmlTransformsAppContextSwitch, defaultValue: false);
+
+ internal static int MaxReferencesPerSignedInfo { get; } =
+ GetInt32Config("System.Security.Cryptography.MaxReferencesPerSignedInfo", defaultValue: 100);
+
+ private static int GetInt32Config(string appContextName, int defaultValue, bool allowNegative = true)
{
- const int DefaultMaxReferencesPerSignedInfo = 100;
- object? data = AppContext.GetData("System.Security.Cryptography.MaxReferencesPerSignedInfo");
+ object? data = AppContext.GetData(appContextName);
if (data is null)
{
- return DefaultMaxReferencesPerSignedInfo;
+ return defaultValue;
}
+ int value;
+
try
{
- return Convert.ToInt32(data, CultureInfo.InvariantCulture);
+ value = Convert.ToInt32(data, CultureInfo.InvariantCulture);
}
catch
{
- return DefaultMaxReferencesPerSignedInfo;
+ return defaultValue;
}
+
+ return (allowNegative || value >= 0) ? value : defaultValue;
+ }
+
+ private static bool GetBooleanConfig(string appContextName, bool defaultValue)
+ {
+ if (AppContext.TryGetSwitch(appContextName, out bool isEnabled))
+ {
+ return isEnabled;
+ }
+
+ return defaultValue;
}
}
}
diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/XmlDecryptionTransform.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/XmlDecryptionTransform.cs
index 7113ea0346bfc9..9289c7a6834de3 100644
--- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/XmlDecryptionTransform.cs
+++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/XmlDecryptionTransform.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections;
+using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Xml;
@@ -215,15 +216,19 @@ private void ProcessElementRecursively(XmlNodeList encryptedDatas)
{
if (encryptedDatas == null || encryptedDatas.Count == 0)
return;
- Queue encryptedDatasQueue = new Queue();
+ int maxDepth = LocalAppContextSwitches.DangerousMaxRecursionDepth;
+ Queue encryptedDatasQueue = new();
foreach (XmlNode value in encryptedDatas)
{
- encryptedDatasQueue.Enqueue(value);
+ encryptedDatasQueue.Enqueue(new(value, depth: 0));
}
- XmlNode? node = encryptedDatasQueue.Dequeue() as XmlNode;
- while (node != null)
+
+ while (encryptedDatasQueue.Count > 0)
{
- XmlElement? encryptedDataElement = node as XmlElement;
+ ProcessElementWorkItem workItem = encryptedDatasQueue.Dequeue();
+ XmlElement? encryptedDataElement = workItem.Element as XmlElement;
+ int depth = workItem.Depth;
+
if (encryptedDataElement != null && encryptedDataElement.LocalName == "EncryptedData" &&
encryptedDataElement.NamespaceURI == EncryptedXml.XmlEncNamespaceUrl)
{
@@ -240,17 +245,19 @@ private void ProcessElementRecursively(XmlNodeList encryptedDatas)
XmlNodeList nodes = child.SelectNodes("//enc:EncryptedData", _nsm!)!;
if (nodes.Count > 0)
{
+ if (maxDepth > 0 && depth >= maxDepth)
+ {
+ throw new CryptographicException(SR.Cryptography_Xml_MaxDepthExceeded);
+ }
+
foreach (XmlNode value in nodes)
{
- encryptedDatasQueue.Enqueue(value);
+ encryptedDatasQueue.Enqueue(new(value, depth + 1));
}
}
}
}
}
- if (encryptedDatasQueue.Count == 0)
- break;
- node = encryptedDatasQueue.Dequeue() as XmlNode;
}
}
@@ -271,5 +278,11 @@ public override object GetOutput(Type type)
else
throw new ArgumentException(SR.Cryptography_Xml_TransformIncorrectInputType, nameof(type));
}
+
+ private readonly struct ProcessElementWorkItem(XmlNode element, int depth)
+ {
+ public readonly XmlNode Element = element;
+ public readonly int Depth = depth;
+ }
}
}
diff --git a/src/libraries/System.Security.Cryptography.Xml/tests/EncryptedXmlSample4.xml b/src/libraries/System.Security.Cryptography.Xml/tests/EncryptedXmlSample4.xml
new file mode 100644
index 00000000000000..585a61ecdbe527
--- /dev/null
+++ b/src/libraries/System.Security.Cryptography.Xml/tests/EncryptedXmlSample4.xml
@@ -0,0 +1,1994 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+