|
// 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.Buffers;
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_ErrClearError")]
internal static partial ulong ErrClearError();
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_ErrGetExceptionError")]
private static partial ulong ErrGetExceptionError([MarshalAs(UnmanagedType.Bool)] out bool isAllocFailure);
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_ErrPeekError")]
internal static partial ulong ErrPeekError();
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_ErrPeekLastError")]
internal static partial ulong ErrPeekLastError();
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_ErrReasonErrorString")]
internal static partial IntPtr ErrReasonErrorString(ulong error);
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_ErrErrorStringN")]
private static unsafe partial void ErrErrorStringN(ulong e, byte* buf, int len);
private static unsafe string ErrErrorStringN(ulong error)
{
byte[] buffer = ArrayPool<byte>.Shared.Rent(1024);
string ret;
fixed (byte* buf = &buffer[0])
{
ErrErrorStringN(error, buf, buffer.Length);
ret = Marshal.PtrToStringUTF8((IntPtr)buf)!;
}
ArrayPool<byte>.Shared.Return(buffer);
return ret;
}
internal static Exception CreateOpenSslCryptographicException()
{
// The Windows cryptography libraries reports error codes through
// return values, or Marshal.GetLastWin32Error, either of which
// has a single value when the function exits.
//
// OpenSSL maintains an error queue. Calls to ERR_get_error read
// values out of the queue in the order that ERR_set_error wrote
// them. Nothing enforces that a single call into an OpenSSL
// function will guarantee at-most one error being set, and there
// are well-known cases where multiple errors are emitted.
//
// In older versions of .NET, we collected the last error in the
// queue, by repeatedly calling into ERR_get_error from managed code
// and using the last error as the basis of the exception.
// Now, we call into the shim once, which is responsible for
// maintaining the error state and informing us of the one value to report.
// (and when fetching that error we go ahead and clear out the rest).
ulong error = ErrGetExceptionError(out bool isAllocFailure);
// If we're in an error flow which results in an Exception, but
// no calls to ERR_set_error were made, throw the unadorned
// CryptographicException.
if (error == 0)
{
return new CryptographicException();
}
if (isAllocFailure)
{
return new OutOfMemoryException();
}
// Even though ErrGetError returns ulong (C++ unsigned long), we
// really only expect error codes in the UInt32 range, since that
// type is only 32 bits on x86 Linux.
Debug.Assert(error <= uint.MaxValue, "ErrGetError should only return error codes in the UInt32 range.");
// If there was an error code, and it wasn't something handled specially,
// use the OpenSSL error string as the message to a CryptographicException.
return new OpenSslCryptographicException(unchecked((int)error), ErrErrorStringN(error));
}
internal static void CheckValidOpenSslHandle(SafeHandle handle)
{
if (handle == null || handle.IsInvalid)
{
Exception e = CreateOpenSslCryptographicException();
handle?.Dispose();
throw e;
}
}
internal static void CheckValidOpenSslHandle(IntPtr handle)
{
if (handle == IntPtr.Zero)
{
throw CreateOpenSslCryptographicException();
}
}
private sealed class OpenSslCryptographicException : CryptographicException
{
internal OpenSslCryptographicException(int errorCode, string message)
: base(message)
{
HResult = errorCode;
}
}
}
}
|