File: src\libraries\Common\src\Interop\Unix\System.Security.Cryptography.Native\Interop.EvpPkey.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;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
 
internal static partial class Interop
{
    internal static partial class Crypto
    {
        [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPkeyCreate")]
        internal static partial SafeEvpPKeyHandle EvpPkeyCreate();
 
        [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPkeyDestroy")]
        internal static partial void EvpPkeyDestroy(IntPtr pkey, IntPtr extraHandle);
 
        [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPKeyBits")]
        internal static partial int EvpPKeyBits(SafeEvpPKeyHandle pkey);
 
        internal static int GetEvpPKeySizeBytes(SafeEvpPKeyHandle pkey)
        {
            // EVP_PKEY_size returns the maximum suitable size for the output buffers for almost all operations that can be done with the key.
            // For most of the OpenSSL 'default' provider keys it will return the same size as this method,
            // but other providers such as 'tpm2' it may return larger size.
            // Instead we will round up EVP_PKEY_bits result.
            int keySizeBits = Interop.Crypto.EvpPKeyBits(pkey);
 
            if (keySizeBits <= 0)
            {
                Debug.Fail($"EVP_PKEY_bits returned non-positive value: {keySizeBits}");
                throw new CryptographicException();
            }
 
            return (keySizeBits + 7) / 8;
        }
 
        [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_UpRefEvpPkey")]
        private static partial int UpRefEvpPkey(SafeEvpPKeyHandle handle, IntPtr extraHandle);
 
        internal static int UpRefEvpPkey(SafeEvpPKeyHandle handle)
        {
            return UpRefEvpPkey(handle, handle.ExtraHandle);
        }
 
        [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPKeyType")]
        internal static partial EvpAlgorithmId EvpPKeyType(SafeEvpPKeyHandle handle);
 
        [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPKeyFamily")]
        internal static partial EvpAlgorithmFamilyId EvpPKeyFamily(SafeEvpPKeyHandle handle);
 
        [LibraryImport(Libraries.CryptoNative)]
        private static unsafe partial SafeEvpPKeyHandle CryptoNative_DecodeSubjectPublicKeyInfo(
            byte* buf,
            int len,
            int algId);
 
        [LibraryImport(Libraries.CryptoNative)]
        private static unsafe partial SafeEvpPKeyHandle CryptoNative_DecodePkcs8PrivateKey(
            byte* buf,
            int len,
            int algId);
 
        internal static unsafe SafeEvpPKeyHandle DecodeSubjectPublicKeyInfo(
            ReadOnlySpan<byte> source,
            EvpAlgorithmId algorithmId)
        {
            SafeEvpPKeyHandle handle;
 
            fixed (byte* sourcePtr = source)
            {
                handle = CryptoNative_DecodeSubjectPublicKeyInfo(
                    sourcePtr,
                    source.Length,
                    (int)algorithmId);
            }
 
            if (handle.IsInvalid)
            {
                handle.Dispose();
                throw CreateOpenSslCryptographicException();
            }
 
            return handle;
        }
 
        internal static unsafe SafeEvpPKeyHandle DecodePkcs8PrivateKey(
            ReadOnlySpan<byte> source,
            EvpAlgorithmId algorithmId)
        {
            SafeEvpPKeyHandle handle;
 
            fixed (byte* sourcePtr = source)
            {
                handle = CryptoNative_DecodePkcs8PrivateKey(
                    sourcePtr,
                    source.Length,
                    (int)algorithmId);
            }
 
            if (handle.IsInvalid)
            {
                handle.Dispose();
                throw CreateOpenSslCryptographicException();
            }
 
            return handle;
        }
 
        [LibraryImport(Libraries.CryptoNative)]
        private static partial int CryptoNative_GetPkcs8PrivateKeySize(IntPtr pkey, out int p8size);
 
        private static int GetPkcs8PrivateKeySize(IntPtr pkey)
        {
            const int Success = 1;
            const int Error = -1;
            const int MissingPrivateKey = -2;
 
            int ret = CryptoNative_GetPkcs8PrivateKeySize(pkey, out int p8size);
 
            switch (ret)
            {
                case Success:
                    return p8size;
                case Error:
                    throw CreateOpenSslCryptographicException();
                case MissingPrivateKey:
                    throw new CryptographicException(SR.Cryptography_CSP_NoPrivateKey);
                default:
                    Debug.Fail($"Unexpected return '{ret}' value from {nameof(CryptoNative_GetPkcs8PrivateKeySize)}.");
                    throw new CryptographicException();
            }
        }
 
        [LibraryImport(Libraries.CryptoNative)]
        private static unsafe partial int CryptoNative_EncodePkcs8PrivateKey(IntPtr pkey, byte* buf);
 
        internal static ArraySegment<byte> RentEncodePkcs8PrivateKey(SafeEvpPKeyHandle pkey)
        {
            bool addedRef = false;
 
            try
            {
                pkey.DangerousAddRef(ref addedRef);
                IntPtr handle = pkey.DangerousGetHandle();
 
                int size = GetPkcs8PrivateKeySize(handle);
                byte[] rented = CryptoPool.Rent(size);
                int written;
 
                unsafe
                {
                    fixed (byte* buf = rented)
                    {
                        written = CryptoNative_EncodePkcs8PrivateKey(handle, buf);
                    }
                }
 
                Debug.Assert(written == size);
                return new ArraySegment<byte>(rented, 0, written);
            }
            finally
            {
                if (addedRef)
                {
                    pkey.DangerousRelease();
                }
            }
        }
 
        [LibraryImport(Libraries.CryptoNative)]
        private static partial int CryptoNative_GetSubjectPublicKeyInfoSize(IntPtr pkey);
 
        private static int GetSubjectPublicKeyInfoSize(IntPtr pkey)
        {
            int ret = CryptoNative_GetSubjectPublicKeyInfoSize(pkey);
 
            if (ret < 0)
            {
                throw CreateOpenSslCryptographicException();
            }
 
            return ret;
        }
 
        [LibraryImport(Libraries.CryptoNative)]
        private static unsafe partial int CryptoNative_EncodeSubjectPublicKeyInfo(IntPtr pkey, byte* buf);
 
        internal static ArraySegment<byte> RentEncodeSubjectPublicKeyInfo(SafeEvpPKeyHandle pkey)
        {
            bool addedRef = false;
 
            try
            {
                pkey.DangerousAddRef(ref addedRef);
                IntPtr handle = pkey.DangerousGetHandle();
 
                int size = GetSubjectPublicKeyInfoSize(handle);
                byte[] rented = CryptoPool.Rent(size);
                int written;
 
                unsafe
                {
                    fixed (byte* buf = rented)
                    {
                        written = CryptoNative_EncodeSubjectPublicKeyInfo(handle, buf);
                    }
                }
 
                Debug.Assert(written == size);
                return new ArraySegment<byte>(rented, 0, written);
            }
            finally
            {
                if (addedRef)
                {
                    pkey.DangerousRelease();
                }
            }
        }
 
        [LibraryImport(Libraries.CryptoNative, StringMarshalling = StringMarshalling.Utf8)]
        private static partial SafeEvpPKeyHandle CryptoNative_LoadPrivateKeyFromEngine(
            string engineName,
            string keyName,
            [MarshalAs(UnmanagedType.Bool)] out bool haveEngine);
 
        internal static SafeEvpPKeyHandle LoadPrivateKeyFromEngine(
            string engineName,
            string keyName)
        {
            Debug.Assert(engineName is not null);
            Debug.Assert(keyName is not null);
 
            SafeEvpPKeyHandle pkey = CryptoNative_LoadPrivateKeyFromEngine(engineName, keyName, out bool haveEngine);
 
            if (!haveEngine)
            {
                pkey.Dispose();
                throw new CryptographicException(SR.Cryptography_EnginesNotSupported);
            }
 
            if (pkey.IsInvalid)
            {
                pkey.Dispose();
                throw CreateOpenSslCryptographicException();
            }
 
            return pkey;
        }
 
        [LibraryImport(Libraries.CryptoNative, StringMarshalling = StringMarshalling.Utf8)]
        private static partial SafeEvpPKeyHandle CryptoNative_LoadPublicKeyFromEngine(
            string engineName,
            string keyName,
            [MarshalAs(UnmanagedType.Bool)] out bool haveEngine);
 
        internal static SafeEvpPKeyHandle LoadPublicKeyFromEngine(
            string engineName,
            string keyName)
        {
            Debug.Assert(engineName is not null);
            Debug.Assert(keyName is not null);
 
            SafeEvpPKeyHandle pkey = CryptoNative_LoadPublicKeyFromEngine(engineName, keyName, out bool haveEngine);
 
            if (!haveEngine)
            {
                pkey.Dispose();
                throw new CryptographicException(SR.Cryptography_EnginesNotSupported);
            }
 
            if (pkey.IsInvalid)
            {
                pkey.Dispose();
                throw CreateOpenSslCryptographicException();
            }
 
            return pkey;
        }
 
        [LibraryImport(Libraries.CryptoNative, StringMarshalling = StringMarshalling.Utf8)]
        private static partial IntPtr CryptoNative_LoadKeyFromProvider(
            string providerName,
            string keyUri,
            ref IntPtr extraHandle,
            [MarshalAs(UnmanagedType.Bool)] out bool haveProvider);
 
        internal static SafeEvpPKeyHandle LoadKeyFromProvider(
            string providerName,
            string keyUri)
        {
            IntPtr extraHandle = IntPtr.Zero;
            IntPtr evpPKeyHandle = IntPtr.Zero;
 
            try
            {
                evpPKeyHandle = CryptoNative_LoadKeyFromProvider(providerName, keyUri, ref extraHandle, out bool haveProvider);
 
                if (!haveProvider)
                {
                    Debug.Assert(evpPKeyHandle == IntPtr.Zero && extraHandle == IntPtr.Zero, "both handles should be null if provider is not supported");
                    throw new PlatformNotSupportedException(SR.PlatformNotSupported_CryptographyOpenSSLProvidersNotSupported);
                }
 
                if (evpPKeyHandle == IntPtr.Zero || extraHandle == IntPtr.Zero)
                {
                    Debug.Assert(evpPKeyHandle == IntPtr.Zero, "extraHandle should not be null if evpPKeyHandle is not null");
                    throw CreateOpenSslCryptographicException();
                }
 
                return new SafeEvpPKeyHandle(evpPKeyHandle, extraHandle: extraHandle);
            }
            catch
            {
                if (evpPKeyHandle != IntPtr.Zero || extraHandle != IntPtr.Zero)
                {
                    EvpPkeyDestroy(evpPKeyHandle, extraHandle);
                }
 
                throw;
            }
        }
 
        internal enum EvpAlgorithmId
        {
            Unknown = 0,
            RSA = 6,
            DSA = 116,
            ECC = 408,
        }
 
        internal enum EvpAlgorithmFamilyId
        {
            Unknown = 0,
            RSA = 1,
            DSA = 2,
            ECC = 3,
            MLKem = 4,
            SlhDsa = 5,
            MLDsa = 6,
        }
    }
}