File: System\Security\Cryptography\CngKey.EC.cs
Web Access
Project: src\src\runtime\src\libraries\System.Security.Cryptography\src\System.Security.Cryptography.csproj (System.Security.Cryptography)
// 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;

namespace System.Security.Cryptography
{
    public sealed partial class CngKey : IDisposable
    {
        /// <summary>
        /// Does the key represent a named curve (Win10+)
        /// </summary>
        /// <returns></returns>
        internal bool IsECNamedCurve()
        {
            return IsECNamedCurve(Algorithm.Algorithm);
        }

        internal static bool IsECNamedCurve(string algorithm)
        {
            return (algorithm == CngAlgorithm.ECDiffieHellman.Algorithm ||
                algorithm == CngAlgorithm.ECDsa.Algorithm);
        }

        internal string? GetCurveName(out string? oidValue)
        {
            if (IsECNamedCurve())
            {
                string? curveName = _keyHandle.GetPropertyAsString(KeyPropertyName.ECCCurveName, CngPropertyOptions.None);
                oidValue = curveName is null ? null : OidLookup.ToOid(curveName, OidGroup.PublicKeyAlgorithm, fallBackToAllGroups: false);
                return curveName;
            }

            // Use hard-coded values (for use with pre-Win10 APIs)
            return GetECSpecificCurveName(out oidValue);
        }

        private string GetECSpecificCurveName(out string oidValue)
        {
            string algorithm = Algorithm.Algorithm;

            if (algorithm == CngAlgorithm.ECDiffieHellmanP256.Algorithm ||
                algorithm == CngAlgorithm.ECDsaP256.Algorithm)
            {
                oidValue = Oids.secp256r1;
                return "nistP256";
            }

            if (algorithm == CngAlgorithm.ECDiffieHellmanP384.Algorithm ||
                algorithm == CngAlgorithm.ECDsaP384.Algorithm)
            {
                oidValue = Oids.secp384r1;
                return "nistP384";
            }

            if (algorithm == CngAlgorithm.ECDiffieHellmanP521.Algorithm ||
                algorithm == CngAlgorithm.ECDsaP521.Algorithm)
            {
                oidValue = Oids.secp521r1;
                return "nistP521";
            }

            Debug.Fail($"Unknown curve {algorithm}");
            throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_CurveNotSupported, algorithm));
        }

        /// <summary>
        ///     Return a CngProperty representing a named curve.
        /// </summary>
        internal static CngProperty GetPropertyFromNamedCurve(ECCurve curve)
        {
            string curveName = curve.Oid.FriendlyName!;
            byte[] curveNameBytes = new byte[(curveName.Length + 1) * sizeof(char)]; // +1 to add trailing null
            System.Text.Encoding.Unicode.GetBytes(curveName, 0, curveName.Length, curveNameBytes, 0);
            return new CngProperty(KeyPropertyName.ECCCurveName, curveNameBytes, CngPropertyOptions.None);
        }

        /// <summary>
        /// Map a curve name to algorithm. This enables curves that worked pre-Win10
        /// to work with newer APIs for import and export.
        /// </summary>
        internal static unsafe CngAlgorithm EcdsaCurveNameToAlgorithm(ReadOnlySpan<char> name)
        {
            const int MaxCurveNameLength = 16;
            Span<char> nameLower = stackalloc char[MaxCurveNameLength];
            int written = name.ToLowerInvariant(nameLower);

            // Either it is empty or too big for the buffer, and the buffer is large enough to hold all mapped
            // curve names, so return the generic algorithm.
            if (written < 1)
            {
                return CngAlgorithm.ECDsa;
            }

            return nameLower.Slice(0, written) switch
            {
                "nistp256" or "ecdsa_p256" => CngAlgorithm.ECDsaP256,
                "nistp384" or "ecdsa_p384" => CngAlgorithm.ECDsaP384,
                "nistp521" or "ecdsa_p521" => CngAlgorithm.ECDsaP521,
                _ => CngAlgorithm.ECDsa, // All other curves are new in Win10 so use generic algorithm
            };
        }

        /// <summary>
        /// Map a curve name to algorithm. This enables curves that worked pre-Win10
        /// to work with newer APIs for import and export.
        /// </summary>
        internal static unsafe CngAlgorithm EcdhCurveNameToAlgorithm(ReadOnlySpan<char> name)
        {
            const int MaxCurveNameLength = 16;
            Span<char> nameLower = stackalloc char[MaxCurveNameLength];
            int written = name.ToLowerInvariant(nameLower);

            // Either it is empty or too big for the buffer, and the buffer is large enough to hold all mapped
            // curve names, so return the generic algorithm.
            if (written < 1)
            {
                return CngAlgorithm.ECDiffieHellman;
            }

            return nameLower.Slice(0, written) switch
            {
                "nistp256" or "ecdsa_p256" or "ecdh_p256" => CngAlgorithm.ECDiffieHellmanP256,
                "nistp384" or "ecdsa_p384" or "ecdh_p384" => CngAlgorithm.ECDiffieHellmanP384,
                "nistp521" or "ecdsa_p521" or "ecdh_p521" => CngAlgorithm.ECDiffieHellmanP521,
                _ => CngAlgorithm.ECDiffieHellman, // All other curves are new in Win10 so use generic algorithm
            };
        }
    }
}