File: System\Security\Cryptography\Xml\LocalAppContextSwitches.cs
Web Access
Project: System.Security.Cryptography.Xml.csproj (System.Security.Cryptography.Xml)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Globalization;

namespace System
{
    internal static partial class LocalAppContextSwitches
    {
        internal const int DefaultMaxRecursionDepth = 64;
        internal const int DefaultMaxCanonicalizationAttributesPerElement = 512;
        internal const int DefaultMaxCount = 256;
        internal const string DangerousMaxRecursionDepthAppContextSwitch = "System.Security.Cryptography.Xml.DangerousMaxRecursionDepth";
        internal const string DangerousMaxCanonicalizationAttributesPerElementAppContextSwitch = "System.Security.Cryptography.Xml.DangerousMaxCanonicalizationAttributesPerElement";
        internal const string AllowDangerousEncryptedXmlTransformsAppContextSwitch = "System.Security.Cryptography.Xml.AllowDangerousEncryptedXmlTransforms";
        internal const string CryptoXmlMaxInputDocumentDepthAppContextSwitch = "System.Security.Cryptography.Xml.CryptoXmlMaxInputDocumentDepth";
        internal const string CryptoXmlAllowDtdProcessingAppContextSwitch = "System.Security.Cryptography.Xml.CryptoXmlAllowDtdProcessing";
        internal const string MaxTransformsPerChainAppContextSwitch = "System.Security.Cryptography.Xml.MaxTransformsPerChain";
        internal const string MaxDecryptedDataElementsAppContextSwitch = "System.Security.Cryptography.Xml.MaxDecryptedDataElements";
        internal const string DangerousMaxDecryptionCountAppContextSwitch = "System.Security.Cryptography.Xml.DangerousMaxDecryptionCount";
        internal const string AllowUnsafeTruncatedHmacSignatureVerificationAppContextSwitch = "Switch.System.Security.Cryptography.Xml.SignedXml.AllowUnsafeTruncatedHmacSignatureVerification";

        internal const int DefaultMaxTransformsPerChain = 20;
        internal const int DefaultMaxDecryptedDataElements = 100;

        // 0 disables the limit for compatibility. Negative values fall back to the default.
        internal static int DangerousMaxRecursionDepth { get; } =
            GetInt32Config(DangerousMaxRecursionDepthAppContextSwitch, DefaultMaxRecursionDepth, allowNegative: false);

        /// <summary>
        /// Gets the maximum number of attributes, and separately of namespace declarations, that
        /// canonicalization will render for a single element.
        /// Configurable via AppContext data "System.Security.Cryptography.Xml.DangerousMaxCanonicalizationAttributesPerElement".
        /// Default value is 512. A value of 0 means infinite (no limit).
        /// </summary>
        /// <remarks>
        /// Canonicalization scans the pending render lists linearly for each candidate name, so lists
        /// of unbounded size make the work quadratic in the size of a single element, on a path that
        /// processes attacker-supplied documents. Bounding the lists keeps that work constant per
        /// element. The bound also depends on <see cref="DangerousMaxRecursionDepth"/> to limit how
        /// many ancestor declarations can be in scope at once; setting that switch to 0 removes the
        /// depth limit and reintroduces the quadratic behavior.
        /// </remarks>
        internal static int DangerousMaxCanonicalizationAttributesPerElement { get; } =
            GetInt32Config(
                DangerousMaxCanonicalizationAttributesPerElementAppContextSwitch,
                DefaultMaxCanonicalizationAttributesPerElement,
                allowNegative: false);

        /// <summary>
        /// Gets whether to enforce safe transforms for XML encryption by default.
        /// Configurable via AppContext switch "System.Security.Cryptography.Xml.AllowDangerousEncryptedXmlTransforms".
        /// Default value is false.
        /// </summary>
        internal static bool AllowDangerousEncryptedXmlTransforms { get; } =
            GetBooleanConfig(AllowDangerousEncryptedXmlTransformsAppContextSwitch, defaultValue: false);

        /// <summary>
        /// Gets the maximum input-document depth (XmlReader.Depth) accepted by the cryptographic XML
        /// ingestion and decrypt re-parse paths.
        /// Configurable via AppContext data "System.Security.Cryptography.Xml.CryptoXmlMaxInputDocumentDepth".
        /// Defaults to <see cref="DangerousMaxRecursionDepth"/>. A value of 0 means infinite (no limit).
        /// </summary>
        internal static int CryptoXmlMaxInputDocumentDepth { get; } = GetCryptoXmlMaxInputDocumentDepth();

        private static int GetCryptoXmlMaxInputDocumentDepth()
        {
            int value = GetInt32Config(CryptoXmlMaxInputDocumentDepthAppContextSwitch, DangerousMaxRecursionDepth, allowNegative: false);
            return value;
        }

        /// <summary>
        /// Gets whether DTD processing is allowed on the cryptographic XML ingestion and re-parse paths.
        /// Configurable via AppContext switch "System.Security.Cryptography.Xml.CryptoXmlAllowDtdProcessing".
        /// Default value is false.
        /// </summary>
        internal static bool CryptoXmlAllowDtdProcessing { get; } =
            GetBooleanConfig(CryptoXmlAllowDtdProcessingAppContextSwitch, defaultValue: false);

        /// <summary>
        /// Gets the maximum number of transforms allowed in a deserialized transform chain.
        /// Configurable via AppContext data "System.Security.Cryptography.Xml.MaxTransformsPerChain".
        /// Default value is 20. A value of 0 means infinite (no limit).
        /// </summary>
        internal static int MaxTransformsPerChain { get; } =
            GetInt32Config(MaxTransformsPerChainAppContextSwitch, DefaultMaxTransformsPerChain, allowNegative: false);

        /// <summary>
        /// Gets the maximum number of <c>EncryptedData</c> references that may be processed during a single
        /// <see cref="System.Security.Cryptography.Xml.XmlDecryptionTransform"/> operation.
        /// Configurable via AppContext data "System.Security.Cryptography.Xml.MaxDecryptedDataElements".
        /// Default value is 100. A value of 0 means infinite (no limit).
        /// </summary>
        internal static int MaxDecryptedDataElements { get; } =
            GetInt32Config(MaxDecryptedDataElementsAppContextSwitch, DefaultMaxDecryptedDataElements, allowNegative: false);

        internal static int MaxReferencesPerSignedInfo { get; } =
            GetInt32Config("System.Security.Cryptography.MaxReferencesPerSignedInfo", defaultValue: 100);

        /// <summary>
        /// Gets the maximum number of encrypted elements processed during a decryption operation.
        /// Configurable via AppContext data "System.Security.Cryptography.Xml.DangerousMaxDecryptionCount".
        /// Default value is 256. A value of 0 means infinite (no limit).
        /// </summary>
        internal static int DangerousMaxDecryptionCount { get; } =
            GetInt32Config(DangerousMaxDecryptionCountAppContextSwitch, DefaultMaxCount, allowNegative: false);

        /// <summary>
        /// Gets whether HMAC signature verification accepts truncated signature values.
        /// Configurable via AppContext switch "Switch.System.Security.Cryptography.Xml.SignedXml.AllowUnsafeTruncatedHmacSignatureVerification".
        /// Default value is false.
        /// </summary>
        internal static bool AllowUnsafeTruncatedHmacSignatureVerification { get; } =
            GetBooleanConfig(AllowUnsafeTruncatedHmacSignatureVerificationAppContextSwitch, defaultValue: false);

        private static int GetInt32Config(string appContextName, int defaultValue, bool allowNegative = true)
        {
            object? data = AppContext.GetData(appContextName);

            if (data is null)
            {
                return defaultValue;
            }

            int value;

            try
            {
                value = Convert.ToInt32(data, CultureInfo.InvariantCulture);
            }
            catch
            {
                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;
        }
    }
}