File: System\Security\Cryptography\OidLookup.cs
Web Access
Project: src\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.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
 
namespace System.Security.Cryptography
{
    internal static partial class OidLookup
    {
        private static readonly ConcurrentDictionary<string, string> s_lateBoundOidToFriendlyName =
            new ConcurrentDictionary<string, string>();
 
        private static readonly ConcurrentDictionary<string, string> s_lateBoundFriendlyNameToOid =
            new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
 
        //
        // Attempts to map a friendly name to an OID. Returns null if not a known name.
        //
        public static string? ToFriendlyName(string oid, OidGroup oidGroup, bool fallBackToAllGroups)
        {
            ArgumentNullException.ThrowIfNull(oid);
 
            string? mappedName;
            bool shouldUseCache = ShouldUseCache(oidGroup);
 
            // On Unix shouldUseCache is always true, so no matter what OidGroup is passed in the Windows
            // friendly name will be returned.
            //
            // On Windows shouldUseCache is only true for OidGroup.All, because otherwise the OS may filter
            // out the answer based on the group criteria.
            if (shouldUseCache)
            {
                if (s_oidToFriendlyName.TryGetValue(oid, out mappedName) ||
                    s_compatOids.TryGetValue(oid, out mappedName) ||
                    s_lateBoundOidToFriendlyName.TryGetValue(oid, out mappedName))
                {
                    return mappedName;
                }
            }
 
            mappedName = NativeOidToFriendlyName(oid, oidGroup, fallBackToAllGroups);
 
            if (shouldUseCache && mappedName != null)
            {
                s_lateBoundOidToFriendlyName.TryAdd(oid, mappedName);
 
                // Don't add the reverse here.  Just because oid => name doesn't mean name => oid.
                // And don't bother doing the reverse lookup proactively, just wait until they ask for it.
            }
 
            return mappedName;
        }
 
        //
        // Attempts to retrieve the friendly name for an OID. Returns null if not a known or valid OID.
        //
        public static string? ToOid(string friendlyName, OidGroup oidGroup, bool fallBackToAllGroups)
        {
            ArgumentNullException.ThrowIfNull(friendlyName);
 
            if (friendlyName.Length == 0)
                return null;
 
            string? mappedOid;
            bool shouldUseCache = ShouldUseCache(oidGroup);
 
            if (shouldUseCache)
            {
                if (s_friendlyNameToOid.TryGetValue(friendlyName, out mappedOid) ||
                    s_lateBoundFriendlyNameToOid.TryGetValue(friendlyName, out mappedOid))
                {
                    return mappedOid;
                }
            }
 
            mappedOid = NativeFriendlyNameToOid(friendlyName, oidGroup, fallBackToAllGroups);
 
            if (shouldUseCache && mappedOid != null)
            {
                s_lateBoundFriendlyNameToOid.TryAdd(friendlyName, mappedOid);
 
                // Don't add the reverse here.  Friendly Name => OID is a case insensitive search,
                // so the casing provided as input here may not be the 'correct' one.  Just let
                // ToFriendlyName capture the response and cache it itself.
            }
 
            return mappedOid;
        }
 
        /// <summary>Expected size of <see cref="s_friendlyNameToOid"/>.</summary>
        private const int FriendlyNameToOidCount = 114;
 
        /// <summary>Expected size of <see cref="s_oidToFriendlyName"/>.</summary>
        private const int OidToFriendlyNameCount = 103;
 
        private static readonly Dictionary<string, string> s_friendlyNameToOid =
            new Dictionary<string, string>(FriendlyNameToOidCount, StringComparer.OrdinalIgnoreCase);
 
        private static readonly Dictionary<string, string> s_oidToFriendlyName =
            new Dictionary<string, string>(OidToFriendlyNameCount, StringComparer.Ordinal);
 
        private static readonly Dictionary<string, string> s_compatOids =
            new Dictionary<string, string>
            {
                { "1.2.840.113549.1.3.1", "DH" },
                { "1.3.14.3.2.12", "DSA" },
                { "1.3.14.3.2.13", "sha1DSA" },
                { "1.3.14.3.2.15", "shaRSA" },
                { "1.3.14.3.2.18", "sha" },
                { "1.3.14.3.2.2", "md4RSA" },
                { "1.3.14.3.2.22", "RSA_KEYX" },
                { "1.3.14.3.2.29", "sha1RSA" },
                { "1.3.14.3.2.3", "md5RSA" },
                { "1.3.14.3.2.4", "md4RSA" },
                { "1.3.14.7.2.3.1", "md2RSA" },
            };
 
        static OidLookup()
        {
            InitializeLookupDictionaries();
#if DEBUG
            // Validate we hardcoded the right dictionary size
            Debug.Assert(s_friendlyNameToOid.Count == FriendlyNameToOidCount,
                $"Expected {nameof(s_friendlyNameToOid)}.{nameof(s_friendlyNameToOid.Count)} == {FriendlyNameToOidCount}, got {s_friendlyNameToOid.Count}");
            Debug.Assert(s_oidToFriendlyName.Count == OidToFriendlyNameCount,
                $"Expected {nameof(s_oidToFriendlyName)}.{nameof(s_oidToFriendlyName.Count)} == {OidToFriendlyNameCount}, got {s_oidToFriendlyName.Count}");
 
            ExtraStaticDebugValidation();
#endif
        }
 
        private static void InitializeLookupDictionaries()
        {
            static void AddEntry(string oid, string primaryFriendlyName, ReadOnlySpan<string> additionalFriendlyNames = default)
            {
                s_oidToFriendlyName.Add(oid, primaryFriendlyName);
                s_friendlyNameToOid.Add(primaryFriendlyName, oid);
 
                foreach (string additionalName in additionalFriendlyNames)
                {
                    s_friendlyNameToOid.Add(additionalName, oid);
                }
            }
 
            // This lookup was originally built by extracting every szOID #define out of wincrypt.h,
            // and running them through new Oid(string) on Windows 10.  Then, take the list of everything
            // which produced a FriendlyName value, and run it through two other languages. If all 3 agree
            // on the mapping, consider the value to be non-localized.
            //
            // This original list was produced on English (Win10), cross-checked with Spanish (Win8.1) and
            // Japanese (Win10).
            //
            // Sometimes wincrypt.h has more than one OID which results in the same name.  The OIDs whose value
            // doesn't roundtrip (new Oid(new Oid(value).FriendlyName).Value) are contained in s_compatOids.
            //
            // X-Plat: The names (and casing) in this table come from Windows. Part of the intent of this table
            // is to prevent issues wherein an identifier is different between Windows and Unix;
            // since any existing code would be using the Windows identifier, it is the de facto standard.
            AddEntry("1.2.840.113549.3.7", "3des");
            AddEntry("2.16.840.1.101.3.4.1.2", "aes128");
            AddEntry("2.16.840.1.101.3.4.1.5", "aes128wrap");
            AddEntry("2.16.840.1.101.3.4.1.22", "aes192");
            AddEntry("2.16.840.1.101.3.4.1.25", "aes192wrap");
            AddEntry("2.16.840.1.101.3.4.1.42", "aes256");
            AddEntry("2.16.840.1.101.3.4.1.45", "aes256wrap");
            AddEntry("1.3.36.3.3.2.8.1.1.1", "brainpoolP160r1");
            AddEntry("1.3.36.3.3.2.8.1.1.2", "brainpoolP160t1");
            AddEntry("1.3.36.3.3.2.8.1.1.3", "brainpoolP192r1");
            AddEntry("1.3.36.3.3.2.8.1.1.4", "brainpoolP192t1");
            AddEntry("1.3.36.3.3.2.8.1.1.5", "brainpoolP224r1");
            AddEntry("1.3.36.3.3.2.8.1.1.6", "brainpoolP224t1");
            AddEntry("1.3.36.3.3.2.8.1.1.7", "brainpoolP256r1");
            AddEntry("1.3.36.3.3.2.8.1.1.8", "brainpoolP256t1");
            AddEntry("1.3.36.3.3.2.8.1.1.9", "brainpoolP320r1");
            AddEntry("1.3.36.3.3.2.8.1.1.10", "brainpoolP320t1");
            AddEntry("1.3.36.3.3.2.8.1.1.11", "brainpoolP384r1");
            AddEntry("1.3.36.3.3.2.8.1.1.12", "brainpoolP384t1");
            AddEntry("1.3.36.3.3.2.8.1.1.13", "brainpoolP512r1");
            AddEntry("1.3.36.3.3.2.8.1.1.14", "brainpoolP512t1");
            AddEntry("2.5.4.6", "C");
            AddEntry("1.2.840.113549.1.9.16.3.6", "CMS3DESwrap");
            AddEntry("1.2.840.113549.1.9.16.3.7", "CMSRC2wrap");
            AddEntry("2.5.4.3", "CN");
            AddEntry("1.3.6.1.5.5.7.2.1", "CPS");
            AddEntry("0.9.2342.19200300.100.1.25", "DC");
            AddEntry("1.3.14.3.2.7", "des");
            AddEntry("2.5.4.13", "Description");
            AddEntry("1.2.840.10046.2.1", "DH");
            AddEntry("2.5.4.46", "dnQualifier");
            AddEntry("1.2.840.10040.4.1", "DSA");
            AddEntry("1.3.14.3.2.27", "dsaSHA1");
            AddEntry("1.2.840.113549.1.9.1", "E");
            AddEntry("1.2.156.11235.1.1.2.1", "ec192wapi");
            AddEntry("1.2.840.10045.2.1", "ECC");
            AddEntry("1.3.133.16.840.63.0.2", "ECDH_STD_SHA1_KDF");
            AddEntry("1.3.132.1.11.1", "ECDH_STD_SHA256_KDF");
            AddEntry("1.3.132.1.11.2", "ECDH_STD_SHA384_KDF");
            AddEntry("1.2.840.10045.3.1.7", "ECDSA_P256", ["nistP256", "secP256r1", "x962P256v1", "ECDH_P256"]);
            AddEntry("1.3.132.0.34", "ECDSA_P384", ["nistP384", "secP384r1", "ECDH_P384"]);
            AddEntry("1.3.132.0.35", "ECDSA_P521", ["nistP521", "secP521r1", "ECDH_P521"]);
            AddEntry("1.2.840.113549.1.9.16.3.5", "ESDH");
            AddEntry("2.5.4.42", "G");
            AddEntry("2.5.4.43", "I");
            AddEntry("2.5.4.7", "L");
            AddEntry("1.2.840.113549.2.2", "md2");
            AddEntry("1.2.840.113549.1.1.2", "md2RSA");
            AddEntry("1.2.840.113549.2.4", "md4");
            AddEntry("1.2.840.113549.1.1.3", "md4RSA");
            AddEntry("1.2.840.113549.2.5", "md5");
            AddEntry("1.2.840.113549.1.1.4", "md5RSA");
            AddEntry("1.2.840.113549.1.1.8", "mgf1");
            AddEntry("2.16.840.1.101.2.1.1.20", "mosaicKMandUpdSig");
            AddEntry("2.16.840.1.101.2.1.1.19", "mosaicUpdatedSig");
            AddEntry("1.2.840.10045.3.1.1", "nistP192");
            AddEntry("1.3.132.0.33", "nistP224");
            AddEntry("1.3.6.1.5.5.7.6.2", "NO_SIGN");
            AddEntry("2.5.4.10", "O");
            AddEntry("2.5.4.11", "OU");
            AddEntry("2.5.4.20", "Phone");
            AddEntry("2.5.4.18", "POBox");
            AddEntry("2.5.4.17", "PostalCode");
            AddEntry("1.2.840.113549.3.2", "rc2");
            AddEntry("1.2.840.113549.3.4", "rc4");
            AddEntry("1.2.840.113549.1.1.1", "RSA");
            AddEntry("1.2.840.113549.1.1.7", "RSAES_OAEP");
            AddEntry("1.2.840.113549.1.1.10", "RSASSA-PSS");
            AddEntry("2.5.4.8", "S", ["ST"]);
            AddEntry("1.3.132.0.9", "secP160k1");
            AddEntry("1.3.132.0.8", "secP160r1");
            AddEntry("1.3.132.0.30", "secP160r2");
            AddEntry("1.3.132.0.31", "secP192k1");
            AddEntry("1.3.132.0.32", "secP224k1");
            AddEntry("1.3.132.0.10", "secP256k1");
            AddEntry("2.5.4.5", "SERIALNUMBER");
            AddEntry("1.3.14.3.2.26", "sha1");
            AddEntry("1.2.840.10040.4.3", "sha1DSA");
            AddEntry("1.2.840.10045.4.1", "sha1ECDSA");
            AddEntry("1.2.840.113549.1.1.5", "sha1RSA");
            AddEntry("2.16.840.1.101.3.4.2.1", "sha256");
            AddEntry("1.2.840.10045.4.3.2", "sha256ECDSA");
            AddEntry("1.2.840.113549.1.1.11", "sha256RSA");
            AddEntry("2.16.840.1.101.3.4.2.2", "sha384");
            AddEntry("1.2.840.10045.4.3.3", "sha384ECDSA");
            AddEntry("1.2.840.113549.1.1.12", "sha384RSA");
            AddEntry("2.16.840.1.101.3.4.2.3", "sha512");
            AddEntry("1.2.840.10045.4.3.4", "sha512ECDSA");
            AddEntry("1.2.840.113549.1.1.13", "sha512RSA");
            AddEntry("2.5.4.4", "SN");
            AddEntry("1.2.840.10045.4.3", "specifiedECDSA");
            AddEntry("2.5.4.9", "STREET");
            AddEntry("2.5.4.12", "T");
            AddEntry("2.23.133.2.1", "TPMManufacturer");
            AddEntry("2.23.133.2.2", "TPMModel");
            AddEntry("2.23.133.2.3", "TPMVersion");
            AddEntry("2.23.43.1.4.9", "wtls9");
            AddEntry("2.5.4.24", "X21Address");
            AddEntry("1.2.840.10045.3.1.2", "x962P192v2");
            AddEntry("1.2.840.10045.3.1.3", "x962P192v3");
            AddEntry("1.2.840.10045.3.1.4", "x962P239v1");
            AddEntry("1.2.840.10045.3.1.5", "x962P239v2");
            AddEntry("1.2.840.10045.3.1.6", "x962P239v3");
        }
 
        static partial void ExtraStaticDebugValidation();
    }
}