|
// 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.Globalization;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Win32.SafeHandles;
using TrackedAllocationDelegate = System.Action<System.IntPtr, ulong, System.IntPtr, int>;
internal static partial class Interop
{
internal static partial class Crypto
{
internal delegate int NegativeSizeReadMethod<in THandle>(THandle handle, byte[]? buf, int cBuf);
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_BioTell")]
internal static partial int CryptoNative_BioTell(SafeBioHandle bio);
internal static int BioTell(SafeBioHandle bio)
{
int ret = CryptoNative_BioTell(bio);
if (ret < 0)
{
throw CreateOpenSslCryptographicException();
}
return ret;
}
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_BioSeek")]
internal static partial int BioSeek(SafeBioHandle bio, int pos);
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetX509Thumbprint")]
private static partial int GetX509Thumbprint(SafeX509Handle x509, byte[]? buf, int cBuf);
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetX509NameRawBytes")]
private static partial int GetX509NameRawBytes(IntPtr x509Name, byte[]? buf, int cBuf);
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_ReadX509AsDerFromBio")]
internal static partial SafeX509Handle ReadX509AsDerFromBio(SafeBioHandle bio);
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetX509CrlNextUpdate")]
internal static partial IntPtr GetX509CrlNextUpdate(SafeX509CrlHandle crl);
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetX509Version")]
internal static partial int GetX509Version(SafeX509Handle x509);
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetX509PublicKeyParameterBytes")]
private static partial int GetX509PublicKeyParameterBytes(SafeX509Handle x509, byte[]? buf, int cBuf);
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetX509NameInfo")]
internal static partial SafeBioHandle GetX509NameInfo(SafeX509Handle x509, int nameType, [MarshalAs(UnmanagedType.Bool)] bool forIssuer);
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetAsn1StringBytes")]
private static partial int GetAsn1StringBytes(IntPtr asn1, byte[]? buf, int cBuf);
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_PushX509StackField")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool PushX509StackField(SafeX509StackHandle stack, SafeX509Handle x509);
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_PushX509StackField")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool PushX509StackField(SafeSharedX509StackHandle stack, SafeX509Handle x509);
internal static unsafe string? GetX509RootStorePath(out bool defaultPath)
{
byte usedDefault;
IntPtr ptr = GetX509RootStorePath_private(&usedDefault);
defaultPath = (usedDefault != 0);
return Marshal.PtrToStringUTF8(ptr);
}
internal static unsafe string? GetX509RootStoreFile()
{
byte unused;
return Marshal.PtrToStringUTF8(GetX509RootStoreFile_private(&unused));
}
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetX509RootStorePath")]
private static unsafe partial IntPtr GetX509RootStorePath_private(byte* defaultPath);
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetX509RootStoreFile")]
private static unsafe partial IntPtr GetX509RootStoreFile_private(byte* defaultPath);
[LibraryImport(Libraries.CryptoNative)]
private static partial int CryptoNative_X509StoreSetVerifyTime(
SafeX509StoreHandle ctx,
int year,
int month,
int day,
int hour,
int minute,
int second,
[MarshalAs(UnmanagedType.Bool)] bool isDst);
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_CheckX509IpAddress", StringMarshalling = StringMarshalling.Utf8)]
internal static partial int CheckX509IpAddress(SafeX509Handle x509, byte[] addressBytes, int addressLen, string hostname, int cchHostname);
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_CheckX509Hostname", StringMarshalling = StringMarshalling.Utf8)]
internal static partial int CheckX509Hostname(SafeX509Handle x509, string hostname, int cchHostname);
[LibraryImport(Libraries.CryptoNative, StringMarshalling = StringMarshalling.Utf8)]
private static partial int CryptoNative_IsSignatureAlgorithmAvailable(string algorithm);
internal static string? IsSignatureAlgorithmAvailable(string algorithm)
{
const int Available = 1;
const int NotAvailable = 0;
int ret = CryptoNative_IsSignatureAlgorithmAvailable(algorithm);
return ret switch
{
Available => algorithm,
NotAvailable => null,
int other => throw Fail(other),
};
static CryptographicException Fail(int result)
{
Debug.Fail($"Unexpected result {result} from {nameof(CryptoNative_IsSignatureAlgorithmAvailable)}");
return new CryptographicException();
}
}
internal static byte[] GetAsn1StringBytes(IntPtr asn1)
{
return GetDynamicBuffer(GetAsn1StringBytes, asn1);
}
internal static byte[] GetX509Thumbprint(SafeX509Handle x509)
{
return GetDynamicBuffer(GetX509Thumbprint, x509);
}
internal static X500DistinguishedName LoadX500Name(IntPtr namePtr)
{
CheckValidOpenSslHandle(namePtr);
byte[] buf = GetDynamicBuffer(GetX509NameRawBytes, namePtr);
return new X500DistinguishedName(buf);
}
internal static byte[]? GetX509PublicKeyParameterBytes(SafeX509Handle x509)
{
return GetNullableDynamicBuffer(GetX509PublicKeyParameterBytes, x509);
}
internal static void X509StoreSetVerifyTime(SafeX509StoreHandle ctx, DateTime verifyTime)
{
// OpenSSL is going to convert our input time to universal, so we should be in Local or
// Unspecified (local-assumed).
Debug.Assert(verifyTime.Kind != DateTimeKind.Utc, "UTC verifyTime should have been normalized to Local");
int succeeded = CryptoNative_X509StoreSetVerifyTime(
ctx,
verifyTime.Year,
verifyTime.Month,
verifyTime.Day,
verifyTime.Hour,
verifyTime.Minute,
verifyTime.Second,
verifyTime.IsDaylightSavingTime());
if (succeeded != 1)
{
throw Interop.Crypto.CreateOpenSslCryptographicException();
}
}
internal static byte[] GetDynamicBuffer<THandle>(NegativeSizeReadMethod<THandle> method, THandle handle)
{
int negativeSize = method(handle, null, 0);
if (negativeSize > 0)
{
throw Interop.Crypto.CreateOpenSslCryptographicException();
}
byte[] bytes = new byte[-negativeSize];
int ret = method(handle, bytes, bytes.Length);
if (ret != 1)
{
throw Interop.Crypto.CreateOpenSslCryptographicException();
}
return bytes;
}
internal static byte[]? GetNullableDynamicBuffer<THandle>(NegativeSizeReadMethod<THandle> method, THandle handle)
{
const int MissingData = 2;
const int DataCopied = 1;
int returnValue = method(handle, null, 0);
if (returnValue == MissingData)
{
return null;
}
if (returnValue > 0)
{
throw Interop.Crypto.CreateOpenSslCryptographicException();
}
byte[] bytes = new byte[-returnValue];
int ret = method(handle, bytes, bytes.Length);
if (ret != DataCopied)
{
throw Interop.Crypto.CreateOpenSslCryptographicException();
}
return bytes;
}
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetMemoryUse")]
private static partial void GetMemoryUse(ref long memoryUse, ref long allocationCount);
internal static long GetOpenSslAllocatedMemory()
{
long used = 0;
long count = 0;
GetMemoryUse(ref used, ref count);
return used;
}
internal static long GetOpenSslAllocationCount()
{
long used = 0;
long count = 0;
GetMemoryUse(ref used, ref count);
return count;
}
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EnableMemoryTracking")]
internal static unsafe partial void EnableMemoryTracking(int enable);
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_ForEachTrackedAllocation")]
private static unsafe partial void ForEachTrackedAllocation(delegate* unmanaged<IntPtr, ulong, char*, int, IntPtr, void> callback, IntPtr ctx);
internal static unsafe void ForEachTrackedAllocation(TrackedAllocationDelegate callback)
{
ForEachTrackedAllocation(&MemoryTrackingCallback, (IntPtr)(&callback));
}
[UnmanagedCallersOnly]
private static unsafe void MemoryTrackingCallback(IntPtr ptr, ulong size, char* file, int line, IntPtr ctx)
{
TrackedAllocationDelegate callback = *(TrackedAllocationDelegate*)ctx;
callback(ptr, size, (IntPtr)file, line);
}
internal static unsafe void EnableMemoryTracking()
{
EnableMemoryTracking(1);
}
internal static unsafe void DisableMemoryTracking()
{
EnableMemoryTracking(0);
}
}
}
|