1036 lines
43 KiB
Diff
1036 lines
43 KiB
Diff
|
From 16f162f76cdbdd150487eb9824f9d8f8e39df5ca Mon Sep 17 00:00:00 2001
|
||
|
From: Jeremy Barton <jbarton@microsoft.com>
|
||
|
Date: Wed, 24 Mar 2021 10:27:42 -0700
|
||
|
Subject: [PATCH 02/11] Use EVP_PKEY for RSA Decrypt
|
||
|
|
||
|
---
|
||
|
.../Interop.EVP.DigestAlgs.cs | 58 +++++
|
||
|
.../Interop.EVP.cs | 18 +-
|
||
|
.../Interop.EvpPkey.Rsa.cs | 37 +++
|
||
|
.../Interop.EvpPkey.cs | 3 +
|
||
|
.../Interop.Rsa.cs | 16 --
|
||
|
.../Security/Cryptography/RSAOpenSsl.cs | 222 ++++++++++--------
|
||
|
.../apibridge.c | 17 ++
|
||
|
.../apibridge.h | 1 +
|
||
|
.../opensslshim.h | 17 +-
|
||
|
.../pal_evp_pkey.c | 7 +
|
||
|
.../pal_evp_pkey.h | 5 +
|
||
|
.../pal_evp_pkey_rsa.c | 137 ++++++++++-
|
||
|
.../pal_evp_pkey_rsa.h | 27 ++-
|
||
|
.../pal_rsa.c | 13 -
|
||
|
.../pal_rsa.h | 8 -
|
||
|
.../HashProviderDispenser.Unix.cs | 36 +--
|
||
|
...em.Security.Cryptography.Algorithms.csproj | 3 +
|
||
|
...ystem.Security.Cryptography.OpenSsl.csproj | 3 +
|
||
|
18 files changed, 444 insertions(+), 184 deletions(-)
|
||
|
create mode 100644 src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.DigestAlgs.cs
|
||
|
|
||
|
diff --git a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.DigestAlgs.cs b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.DigestAlgs.cs
|
||
|
new file mode 100644
|
||
|
index 0000000000..53ef644d84
|
||
|
--- /dev/null
|
||
|
+++ b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.DigestAlgs.cs
|
||
|
@@ -0,0 +1,58 @@
|
||
|
+// 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.Runtime.InteropServices;
|
||
|
+using System.Security.Cryptography;
|
||
|
+
|
||
|
+internal static partial class Interop
|
||
|
+{
|
||
|
+ internal static partial class Crypto
|
||
|
+ {
|
||
|
+ private static volatile IntPtr s_evpMd5;
|
||
|
+ private static volatile IntPtr s_evpSha1;
|
||
|
+ private static volatile IntPtr s_evpSha256;
|
||
|
+ private static volatile IntPtr s_evpSha384;
|
||
|
+ private static volatile IntPtr s_evpSha512;
|
||
|
+
|
||
|
+ [DllImport(Libraries.CryptoNative)]
|
||
|
+ private static extern IntPtr CryptoNative_EvpMd5();
|
||
|
+
|
||
|
+ internal static IntPtr EvpMd5() =>
|
||
|
+ s_evpMd5 != IntPtr.Zero ? s_evpMd5 : (s_evpMd5 = CryptoNative_EvpMd5());
|
||
|
+
|
||
|
+ [DllImport(Libraries.CryptoNative)]
|
||
|
+ internal static extern IntPtr CryptoNative_EvpSha1();
|
||
|
+
|
||
|
+ internal static IntPtr EvpSha1() =>
|
||
|
+ s_evpSha1 != IntPtr.Zero ? s_evpSha1 : (s_evpSha1 = CryptoNative_EvpSha1());
|
||
|
+
|
||
|
+ [DllImport(Libraries.CryptoNative)]
|
||
|
+ internal static extern IntPtr CryptoNative_EvpSha256();
|
||
|
+
|
||
|
+ internal static IntPtr EvpSha256() =>
|
||
|
+ s_evpSha256 != IntPtr.Zero ? s_evpSha256 : (s_evpSha256 = CryptoNative_EvpSha256());
|
||
|
+
|
||
|
+ [DllImport(Libraries.CryptoNative)]
|
||
|
+ internal static extern IntPtr CryptoNative_EvpSha384();
|
||
|
+
|
||
|
+ internal static IntPtr EvpSha384() =>
|
||
|
+ s_evpSha384 != IntPtr.Zero ? s_evpSha384 : (s_evpSha384 = CryptoNative_EvpSha384());
|
||
|
+
|
||
|
+ [DllImport(Libraries.CryptoNative)]
|
||
|
+ internal static extern IntPtr CryptoNative_EvpSha512();
|
||
|
+
|
||
|
+ internal static IntPtr EvpSha512() =>
|
||
|
+ s_evpSha512 != IntPtr.Zero ? s_evpSha512 : (s_evpSha512 = CryptoNative_EvpSha512());
|
||
|
+
|
||
|
+ internal static IntPtr HashAlgorithmToEvp(string hashAlgorithmId) => hashAlgorithmId switch
|
||
|
+ {
|
||
|
+ nameof(HashAlgorithmName.SHA1) => EvpSha1(),
|
||
|
+ nameof(HashAlgorithmName.SHA256) => EvpSha256(),
|
||
|
+ nameof(HashAlgorithmName.SHA384) => EvpSha384(),
|
||
|
+ nameof(HashAlgorithmName.SHA512) => EvpSha512(),
|
||
|
+ nameof(HashAlgorithmName.MD5) => EvpMd5(),
|
||
|
+ _ => throw new CryptographicException(SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithmId))
|
||
|
+ };
|
||
|
+ }
|
||
|
+}
|
||
|
diff --git a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.cs b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.cs
|
||
|
index 67a9b54230..ea9dc5186d 100644
|
||
|
--- a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.cs
|
||
|
+++ b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.cs
|
||
|
@@ -4,6 +4,7 @@
|
||
|
|
||
|
using System;
|
||
|
using System.Runtime.InteropServices;
|
||
|
+using System.Security.Cryptography;
|
||
|
using Microsoft.Win32.SafeHandles;
|
||
|
|
||
|
internal static partial class Interop
|
||
|
@@ -31,23 +32,6 @@ internal static partial class Crypto
|
||
|
[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpMdSize")]
|
||
|
internal extern static int EvpMdSize(IntPtr md);
|
||
|
|
||
|
-
|
||
|
- [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpMd5")]
|
||
|
- internal extern static IntPtr EvpMd5();
|
||
|
-
|
||
|
- [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpSha1")]
|
||
|
- internal extern static IntPtr EvpSha1();
|
||
|
-
|
||
|
- [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpSha256")]
|
||
|
- internal extern static IntPtr EvpSha256();
|
||
|
-
|
||
|
- [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpSha384")]
|
||
|
- internal extern static IntPtr EvpSha384();
|
||
|
-
|
||
|
- [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpSha512")]
|
||
|
- internal extern static IntPtr EvpSha512();
|
||
|
-
|
||
|
-
|
||
|
[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetMaxMdSize")]
|
||
|
private extern static int GetMaxMdSize();
|
||
|
|
||
|
diff --git a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.Rsa.cs b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.Rsa.cs
|
||
|
index c28522784b..f023ced7f9 100644
|
||
|
--- a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.Rsa.cs
|
||
|
+++ b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.Rsa.cs
|
||
|
@@ -2,6 +2,8 @@
|
||
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||
|
// See the LICENSE file in the project root for more information.
|
||
|
|
||
|
+using System;
|
||
|
+using System.Diagnostics;
|
||
|
using System.Runtime.InteropServices;
|
||
|
using System.Security.Cryptography;
|
||
|
using Microsoft.Win32.SafeHandles;
|
||
|
@@ -26,6 +28,41 @@ internal static SafeEvpPKeyHandle RsaGenerateKey(int keySize)
|
||
|
return pkey;
|
||
|
}
|
||
|
|
||
|
+ [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RsaDecrypt")]
|
||
|
+ private static extern int CryptoNative_RsaDecrypt(
|
||
|
+ SafeEvpPKeyHandle pkey,
|
||
|
+ ref byte source,
|
||
|
+ int sourceLength,
|
||
|
+ RSAEncryptionPaddingMode paddingMode,
|
||
|
+ IntPtr digestAlgorithm,
|
||
|
+ ref byte destination,
|
||
|
+ int destinationLength);
|
||
|
+
|
||
|
+ internal static int RsaDecrypt(
|
||
|
+ SafeEvpPKeyHandle pkey,
|
||
|
+ ReadOnlySpan<byte> source,
|
||
|
+ RSAEncryptionPaddingMode paddingMode,
|
||
|
+ IntPtr digestAlgorithm,
|
||
|
+ Span<byte> destination)
|
||
|
+ {
|
||
|
+ int written = CryptoNative_RsaDecrypt(
|
||
|
+ pkey,
|
||
|
+ ref MemoryMarshal.GetReference(source),
|
||
|
+ source.Length,
|
||
|
+ paddingMode,
|
||
|
+ digestAlgorithm,
|
||
|
+ ref MemoryMarshal.GetReference(destination),
|
||
|
+ destination.Length);
|
||
|
+
|
||
|
+ if (written < 0)
|
||
|
+ {
|
||
|
+ Debug.Assert(written == -1);
|
||
|
+ throw CreateOpenSslCryptographicException();
|
||
|
+ }
|
||
|
+
|
||
|
+ return written;
|
||
|
+ }
|
||
|
+
|
||
|
[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPkeyGetRsa")]
|
||
|
internal static extern SafeRsaHandle EvpPkeyGetRsa(SafeEvpPKeyHandle pkey);
|
||
|
|
||
|
diff --git a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.cs b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.cs
|
||
|
index 2eff6bfcd1..3ad4bc9d08 100644
|
||
|
--- a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.cs
|
||
|
+++ b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.cs
|
||
|
@@ -16,6 +16,9 @@ internal static partial class Crypto
|
||
|
[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPkeyDestroy")]
|
||
|
internal static extern void EvpPkeyDestroy(IntPtr pkey);
|
||
|
|
||
|
+ [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPKeySize")]
|
||
|
+ internal static extern int EvpPKeySize(SafeEvpPKeyHandle pkey);
|
||
|
+
|
||
|
[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_UpRefEvpPkey")]
|
||
|
internal static extern int UpRefEvpPkey(SafeEvpPKeyHandle handle);
|
||
|
}
|
||
|
diff --git a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs
|
||
|
index a05f020ada..5ad534a8f2 100644
|
||
|
--- a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs
|
||
|
+++ b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs
|
||
|
@@ -44,22 +44,6 @@ internal static partial class Crypto
|
||
|
SafeRsaHandle rsa,
|
||
|
RsaPadding padding);
|
||
|
|
||
|
- internal static int RsaPrivateDecrypt(
|
||
|
- int flen,
|
||
|
- ReadOnlySpan<byte> from,
|
||
|
- Span<byte> to,
|
||
|
- SafeRsaHandle rsa,
|
||
|
- RsaPadding padding) =>
|
||
|
- RsaPrivateDecrypt(flen, ref MemoryMarshal.GetReference(from), ref MemoryMarshal.GetReference(to), rsa, padding);
|
||
|
-
|
||
|
- [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RsaPrivateDecrypt")]
|
||
|
- private extern static int RsaPrivateDecrypt(
|
||
|
- int flen,
|
||
|
- ref byte from,
|
||
|
- ref byte to,
|
||
|
- SafeRsaHandle rsa,
|
||
|
- RsaPadding padding);
|
||
|
-
|
||
|
internal static int RsaSignPrimitive(
|
||
|
ReadOnlySpan<byte> from,
|
||
|
Span<byte> to,
|
||
|
diff --git a/src/Common/src/System/Security/Cryptography/RSAOpenSsl.cs b/src/Common/src/System/Security/Cryptography/RSAOpenSsl.cs
|
||
|
index 4d4a8414b3..87e31b9dde 100644
|
||
|
--- a/src/Common/src/System/Security/Cryptography/RSAOpenSsl.cs
|
||
|
+++ b/src/Common/src/System/Security/Cryptography/RSAOpenSsl.cs
|
||
|
@@ -85,10 +85,9 @@ public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding)
|
||
|
if (padding == null)
|
||
|
throw new ArgumentNullException(nameof(padding));
|
||
|
|
||
|
- Interop.Crypto.RsaPadding rsaPadding = GetInteropPadding(padding, out RsaPaddingProcessor oaepProcessor);
|
||
|
- SafeRsaHandle key = GetKey();
|
||
|
-
|
||
|
- int rsaSize = Interop.Crypto.RsaSize(key);
|
||
|
+ ValidatePadding(padding);
|
||
|
+ SafeEvpPKeyHandle key = GetPKey();
|
||
|
+ int rsaSize = Interop.Crypto.EvpPKeySize(key);
|
||
|
byte[] buf = null;
|
||
|
Span<byte> destination = default;
|
||
|
|
||
|
@@ -97,18 +96,15 @@ public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding)
|
||
|
buf = CryptoPool.Rent(rsaSize);
|
||
|
destination = new Span<byte>(buf, 0, rsaSize);
|
||
|
|
||
|
- if (!TryDecrypt(key, data, destination, rsaPadding, oaepProcessor, out int bytesWritten))
|
||
|
- {
|
||
|
- Debug.Fail($"{nameof(TryDecrypt)} should not return false for RSA_size buffer");
|
||
|
- throw new CryptographicException();
|
||
|
- }
|
||
|
-
|
||
|
+ int bytesWritten = Decrypt(key, data, destination, padding);
|
||
|
return destination.Slice(0, bytesWritten).ToArray();
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
CryptographicOperations.ZeroMemory(destination);
|
||
|
CryptoPool.Return(buf, clearSize: 0);
|
||
|
+ // Until EVP_PKEY is what gets stored, free the temporary key handle.
|
||
|
+ key.Dispose();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@@ -119,82 +115,73 @@ public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding)
|
||
|
out int bytesWritten)
|
||
|
{
|
||
|
if (padding == null)
|
||
|
- {
|
||
|
throw new ArgumentNullException(nameof(padding));
|
||
|
- }
|
||
|
-
|
||
|
- Interop.Crypto.RsaPadding rsaPadding = GetInteropPadding(padding, out RsaPaddingProcessor oaepProcessor);
|
||
|
- SafeRsaHandle key = GetKey();
|
||
|
|
||
|
- int keySizeBytes = Interop.Crypto.RsaSize(key);
|
||
|
+ ValidatePadding(padding);
|
||
|
+ SafeEvpPKeyHandle key = GetPKey();
|
||
|
+ int keySizeBytes = Interop.Crypto.EvpPKeySize(key);
|
||
|
|
||
|
- // OpenSSL does not take a length value for the destination, so it can write out of bounds.
|
||
|
- // To prevent the OOB write, decrypt into a temporary buffer.
|
||
|
+ // OpenSSL requires that the decryption buffer be at least as large as EVP_PKEY_size.
|
||
|
+ // So if the destination is too small, use a temporary buffer so we can match
|
||
|
+ // Windows behavior of succeeding so long as the buffer can hold the final output.
|
||
|
if (destination.Length < keySizeBytes)
|
||
|
{
|
||
|
- Span<byte> tmp = stackalloc byte[0];
|
||
|
+ // RSA up through 4096 bits use a stackalloc
|
||
|
+ Span<byte> tmp = stackalloc byte[512];
|
||
|
byte[] rent = null;
|
||
|
|
||
|
- // RSA up through 4096 stackalloc
|
||
|
- if (keySizeBytes <= 512)
|
||
|
+ if (keySizeBytes > tmp.Length)
|
||
|
{
|
||
|
- tmp = stackalloc byte[keySizeBytes];
|
||
|
- }
|
||
|
- else
|
||
|
- {
|
||
|
- rent = ArrayPool<byte>.Shared.Rent(keySizeBytes);
|
||
|
+ rent = CryptoPool.Rent(keySizeBytes);
|
||
|
tmp = rent;
|
||
|
}
|
||
|
|
||
|
- bool ret = TryDecrypt(key, data, tmp, rsaPadding, oaepProcessor, out bytesWritten);
|
||
|
+ int written = Decrypt(key, data, tmp, padding);
|
||
|
+ // Until EVP_PKEY is what gets stored, free the temporary key handle.
|
||
|
+ key.Dispose();
|
||
|
+ bool ret;
|
||
|
|
||
|
- if (ret)
|
||
|
+ if (destination.Length < written)
|
||
|
{
|
||
|
- tmp = tmp.Slice(0, bytesWritten);
|
||
|
-
|
||
|
- if (bytesWritten > destination.Length)
|
||
|
- {
|
||
|
- ret = false;
|
||
|
- bytesWritten = 0;
|
||
|
- }
|
||
|
- else
|
||
|
- {
|
||
|
- tmp.CopyTo(destination);
|
||
|
- }
|
||
|
-
|
||
|
- CryptographicOperations.ZeroMemory(tmp);
|
||
|
+ bytesWritten = 0;
|
||
|
+ ret = false;
|
||
|
+ }
|
||
|
+ else
|
||
|
+ {
|
||
|
+ tmp.Slice(0, written).CopyTo(destination);
|
||
|
+ bytesWritten = written;
|
||
|
+ ret = true;
|
||
|
}
|
||
|
|
||
|
+ // Whether a stackalloc or a rented array, clear our copy of
|
||
|
+ // the decrypted content.
|
||
|
+ CryptographicOperations.ZeroMemory(tmp.Slice(0, written));
|
||
|
+
|
||
|
if (rent != null)
|
||
|
{
|
||
|
- // Already cleared
|
||
|
- ArrayPool<byte>.Shared.Return(rent);
|
||
|
+ // Already cleared.
|
||
|
+ CryptoPool.Return(rent, clearSize: 0);
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
- return TryDecrypt(key, data, destination, rsaPadding, oaepProcessor, out bytesWritten);
|
||
|
+ bytesWritten = Decrypt(key, data, destination, padding);
|
||
|
+ // Until EVP_PKEY is what gets stored, free the temporary key handle.
|
||
|
+ key.Dispose();
|
||
|
+ return true;
|
||
|
}
|
||
|
|
||
|
- private static bool TryDecrypt(
|
||
|
- SafeRsaHandle key,
|
||
|
+ private static int Decrypt(
|
||
|
+ SafeEvpPKeyHandle key,
|
||
|
ReadOnlySpan<byte> data,
|
||
|
Span<byte> destination,
|
||
|
- Interop.Crypto.RsaPadding rsaPadding,
|
||
|
- RsaPaddingProcessor rsaPaddingProcessor,
|
||
|
- out int bytesWritten)
|
||
|
+ RSAEncryptionPadding padding)
|
||
|
{
|
||
|
- // If rsaPadding is PKCS1 or OAEP-SHA1 then no depadding method should be present.
|
||
|
- // If rsaPadding is NoPadding then a depadding method should be present.
|
||
|
- Debug.Assert(
|
||
|
- (rsaPadding == Interop.Crypto.RsaPadding.NoPadding) ==
|
||
|
- (rsaPaddingProcessor != null));
|
||
|
-
|
||
|
// Caller should have already checked this.
|
||
|
Debug.Assert(!key.IsInvalid);
|
||
|
|
||
|
- int rsaSize = Interop.Crypto.RsaSize(key);
|
||
|
+ int rsaSize = Interop.Crypto.EvpPKeySize(key);
|
||
|
|
||
|
if (data.Length != rsaSize)
|
||
|
{
|
||
|
@@ -203,50 +190,24 @@ public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding)
|
||
|
|
||
|
if (destination.Length < rsaSize)
|
||
|
{
|
||
|
- bytesWritten = 0;
|
||
|
- return false;
|
||
|
+ Debug.Fail("Caller is responsible for temporary decryption buffer creation");
|
||
|
+ throw new CryptographicException();
|
||
|
}
|
||
|
|
||
|
- Span<byte> decryptBuf = destination;
|
||
|
- byte[] paddingBuf = null;
|
||
|
+ IntPtr hashAlgorithm = IntPtr.Zero;
|
||
|
|
||
|
- if (rsaPaddingProcessor != null)
|
||
|
+ if (padding.Mode == RSAEncryptionPaddingMode.Oaep)
|
||
|
{
|
||
|
- paddingBuf = CryptoPool.Rent(rsaSize);
|
||
|
- decryptBuf = paddingBuf;
|
||
|
+ Debug.Assert(padding.OaepHashAlgorithm.Name != null);
|
||
|
+ hashAlgorithm = Interop.Crypto.HashAlgorithmToEvp(padding.OaepHashAlgorithm.Name);
|
||
|
}
|
||
|
|
||
|
- try
|
||
|
- {
|
||
|
- int returnValue = Interop.Crypto.RsaPrivateDecrypt(data.Length, data, decryptBuf, key, rsaPadding);
|
||
|
- CheckReturn(returnValue);
|
||
|
-
|
||
|
- if (rsaPaddingProcessor != null)
|
||
|
- {
|
||
|
- return rsaPaddingProcessor.DepadOaep(paddingBuf, destination, out bytesWritten);
|
||
|
- }
|
||
|
- else
|
||
|
- {
|
||
|
- // If the padding mode is RSA_NO_PADDING then the size of the decrypted block
|
||
|
- // will be RSA_size. If any padding was used, then some amount (determined by the padding algorithm)
|
||
|
- // will have been reduced, and only returnValue bytes were part of the decrypted
|
||
|
- // body. Either way, we can just use returnValue, but some additional bytes may have been overwritten
|
||
|
- // in the destination span.
|
||
|
- bytesWritten = returnValue;
|
||
|
- }
|
||
|
-
|
||
|
- return true;
|
||
|
- }
|
||
|
- finally
|
||
|
- {
|
||
|
- if (paddingBuf != null)
|
||
|
- {
|
||
|
- // DecryptBuf is paddingBuf if paddingBuf is not null, erase it before returning it.
|
||
|
- // If paddingBuf IS null then decryptBuf was destination, and shouldn't be cleared.
|
||
|
- CryptographicOperations.ZeroMemory(decryptBuf);
|
||
|
- CryptoPool.Return(paddingBuf, clearSize: 0);
|
||
|
- }
|
||
|
- }
|
||
|
+ return Interop.Crypto.RsaDecrypt(
|
||
|
+ key,
|
||
|
+ data,
|
||
|
+ padding.Mode,
|
||
|
+ hashAlgorithm,
|
||
|
+ destination);
|
||
|
}
|
||
|
|
||
|
public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding)
|
||
|
@@ -550,6 +511,30 @@ private void ThrowIfDisposed()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
+ private SafeEvpPKeyHandle GetPKey()
|
||
|
+ {
|
||
|
+ SafeRsaHandle currentKey = GetKey();
|
||
|
+ SafeEvpPKeyHandle pkeyHandle = Interop.Crypto.EvpPkeyCreate();
|
||
|
+
|
||
|
+ try
|
||
|
+ {
|
||
|
+ // Wrapping our key in an EVP_PKEY will up_ref our key.
|
||
|
+ // When the EVP_PKEY is Disposed it will down_ref the key.
|
||
|
+ // So everything should be copacetic.
|
||
|
+ if (!Interop.Crypto.EvpPkeySetRsa(pkeyHandle, currentKey))
|
||
|
+ {
|
||
|
+ throw Interop.Crypto.CreateOpenSslCryptographicException();
|
||
|
+ }
|
||
|
+ }
|
||
|
+ catch
|
||
|
+ {
|
||
|
+ pkeyHandle.Dispose();
|
||
|
+ throw;
|
||
|
+ }
|
||
|
+
|
||
|
+ return pkeyHandle;
|
||
|
+ }
|
||
|
+
|
||
|
private SafeRsaHandle GetKey()
|
||
|
{
|
||
|
ThrowIfDisposed();
|
||
|
@@ -843,6 +828,55 @@ private static int GetAlgorithmNid(HashAlgorithmName hashAlgorithmName)
|
||
|
return nid;
|
||
|
}
|
||
|
|
||
|
+ private static void ValidatePadding(RSAEncryptionPadding padding)
|
||
|
+ {
|
||
|
+ if (padding == null)
|
||
|
+ {
|
||
|
+ throw new ArgumentNullException(nameof(padding));
|
||
|
+ }
|
||
|
+
|
||
|
+ // There are currently two defined padding modes:
|
||
|
+ // * Oaep has an option (the hash algorithm)
|
||
|
+ // * Pkcs1 has no options
|
||
|
+ //
|
||
|
+ // Anything other than those to modes is an error,
|
||
|
+ // and Pkcs1 having options set is an error, so compare it to
|
||
|
+ // the padding struct instead of the padding mode enum.
|
||
|
+ if (padding.Mode != RSAEncryptionPaddingMode.Oaep &&
|
||
|
+ padding != RSAEncryptionPadding.Pkcs1)
|
||
|
+ {
|
||
|
+ throw PaddingModeNotSupported();
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ private static void ValidatePadding(RSASignaturePadding padding)
|
||
|
+ {
|
||
|
+ if (padding == null)
|
||
|
+ {
|
||
|
+ throw new ArgumentNullException(nameof(padding));
|
||
|
+ }
|
||
|
+
|
||
|
+ // RSASignaturePadding currently only has the mode property, so
|
||
|
+ // there's no need for a runtime check that PKCS#1 doesn't use
|
||
|
+ // nonsensical options like with RSAEncryptionPadding.
|
||
|
+ //
|
||
|
+ // This would change if we supported PSS with an MGF other than MGF-1,
|
||
|
+ // or with a custom salt size, or with a different MGF digest algorithm
|
||
|
+ // than the data digest algorithm.
|
||
|
+ if (padding.Mode == RSASignaturePaddingMode.Pkcs1)
|
||
|
+ {
|
||
|
+ Debug.Assert(padding == RSASignaturePadding.Pkcs1);
|
||
|
+ }
|
||
|
+ else if (padding.Mode == RSASignaturePaddingMode.Pss)
|
||
|
+ {
|
||
|
+ Debug.Assert(padding == RSASignaturePadding.Pss);
|
||
|
+ }
|
||
|
+ else
|
||
|
+ {
|
||
|
+ throw PaddingModeNotSupported();
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
private static Exception PaddingModeNotSupported() =>
|
||
|
new CryptographicException(SR.Cryptography_InvalidPaddingMode);
|
||
|
|
||
|
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/apibridge.c b/src/Native/Unix/System.Security.Cryptography.Native/apibridge.c
|
||
|
index def7198deb..ff71105837 100644
|
||
|
--- a/src/Native/Unix/System.Security.Cryptography.Native/apibridge.c
|
||
|
+++ b/src/Native/Unix/System.Security.Cryptography.Native/apibridge.c
|
||
|
@@ -258,6 +258,23 @@ int32_t local_DSA_set0_key(DSA* dsa, BIGNUM* bnY, BIGNUM* bnX)
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
+RSA* local_EVP_PKEY_get0_RSA(EVP_PKEY* pkey)
|
||
|
+{
|
||
|
+ if (pkey == NULL)
|
||
|
+ {
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ RSA* rsa = EVP_PKEY_get1_RSA(pkey);
|
||
|
+
|
||
|
+ if (rsa != NULL)
|
||
|
+ {
|
||
|
+ RSA_free(rsa);
|
||
|
+ }
|
||
|
+
|
||
|
+ return rsa;
|
||
|
+}
|
||
|
+
|
||
|
int32_t local_EVP_PKEY_up_ref(EVP_PKEY* pkey)
|
||
|
{
|
||
|
if (!pkey)
|
||
|
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/apibridge.h b/src/Native/Unix/System.Security.Cryptography.Native/apibridge.h
|
||
|
index b58611ae73..e1315499f3 100644
|
||
|
--- a/src/Native/Unix/System.Security.Cryptography.Native/apibridge.h
|
||
|
+++ b/src/Native/Unix/System.Security.Cryptography.Native/apibridge.h
|
||
|
@@ -15,6 +15,7 @@ int32_t local_DSA_set0_pqg(DSA* dsa, BIGNUM* bnP, BIGNUM* bnQ, BIGNUM* bnG);
|
||
|
void local_EVP_CIPHER_CTX_free(EVP_CIPHER_CTX* ctx);
|
||
|
EVP_CIPHER_CTX* local_EVP_CIPHER_CTX_new(void);
|
||
|
int32_t local_EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX* ctx);
|
||
|
+RSA* local_EVP_PKEY_get0_RSA(EVP_PKEY* pkey);
|
||
|
int32_t local_EVP_PKEY_up_ref(EVP_PKEY* pkey);
|
||
|
void local_HMAC_CTX_free(HMAC_CTX* ctx);
|
||
|
HMAC_CTX* local_HMAC_CTX_new(void);
|
||
|
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h b/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
|
||
|
index dff6091e9e..47cb142d25 100644
|
||
|
--- a/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
|
||
|
+++ b/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
|
||
|
@@ -128,6 +128,7 @@ EVP_CIPHER_CTX* EVP_CIPHER_CTX_new(void);
|
||
|
int32_t EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX* ctx);
|
||
|
void EVP_MD_CTX_free(EVP_MD_CTX* ctx);
|
||
|
EVP_MD_CTX* EVP_MD_CTX_new(void);
|
||
|
+RSA* EVP_PKEY_get0_RSA(EVP_PKEY* pkey);
|
||
|
int32_t EVP_PKEY_up_ref(EVP_PKEY* pkey);
|
||
|
void HMAC_CTX_free(HMAC_CTX* ctx);
|
||
|
HMAC_CTX* HMAC_CTX_new(void);
|
||
|
@@ -178,6 +179,12 @@ int32_t X509_up_ref(X509* x509);
|
||
|
#define EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) \
|
||
|
RSA_pkey_ctx_ctrl(ctx, EVP_PKEY_OP_KEYGEN, EVP_PKEY_CTRL_RSA_KEYGEN_BITS, bits, NULL)
|
||
|
|
||
|
+#undef EVP_PKEY_CTX_set_rsa_padding
|
||
|
+#define EVP_PKEY_CTX_set_rsa_padding(ctx, pad) \
|
||
|
+ RSA_pkey_ctx_ctrl(ctx, -1, EVP_PKEY_CTRL_RSA_PADDING, pad, NULL)
|
||
|
+
|
||
|
+// EVP_PKEY_CTX_set_rsa_oaep_md doesn't call RSA_pkey_ctx_ctrl in 1.1, so don't redefine it here.
|
||
|
+
|
||
|
#endif
|
||
|
|
||
|
#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_0_2_RTM
|
||
|
@@ -355,10 +362,13 @@ void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsi
|
||
|
REQUIRED_FUNCTION(EVP_PKEY_CTX_new) \
|
||
|
REQUIRED_FUNCTION(EVP_PKEY_CTX_new_id) \
|
||
|
REQUIRED_FUNCTION(EVP_PKEY_base_id) \
|
||
|
+ REQUIRED_FUNCTION(EVP_PKEY_decrypt) \
|
||
|
+ REQUIRED_FUNCTION(EVP_PKEY_decrypt_init) \
|
||
|
REQUIRED_FUNCTION(EVP_PKEY_derive_set_peer) \
|
||
|
REQUIRED_FUNCTION(EVP_PKEY_derive_init) \
|
||
|
REQUIRED_FUNCTION(EVP_PKEY_derive) \
|
||
|
REQUIRED_FUNCTION(EVP_PKEY_free) \
|
||
|
+ FALLBACK_FUNCTION(EVP_PKEY_get0_RSA) \
|
||
|
REQUIRED_FUNCTION(EVP_PKEY_get1_DSA) \
|
||
|
REQUIRED_FUNCTION(EVP_PKEY_get1_EC_KEY) \
|
||
|
REQUIRED_FUNCTION(EVP_PKEY_get1_RSA) \
|
||
|
@@ -368,6 +378,7 @@ void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsi
|
||
|
REQUIRED_FUNCTION(EVP_PKEY_set1_DSA) \
|
||
|
REQUIRED_FUNCTION(EVP_PKEY_set1_EC_KEY) \
|
||
|
REQUIRED_FUNCTION(EVP_PKEY_set1_RSA) \
|
||
|
+ REQUIRED_FUNCTION(EVP_PKEY_size) \
|
||
|
FALLBACK_FUNCTION(EVP_PKEY_up_ref) \
|
||
|
REQUIRED_FUNCTION(EVP_rc2_cbc) \
|
||
|
REQUIRED_FUNCTION(EVP_rc2_ecb) \
|
||
|
@@ -448,7 +459,6 @@ void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsi
|
||
|
REQUIRED_FUNCTION(RSA_new) \
|
||
|
FALLBACK_FUNCTION(RSA_pkey_ctx_ctrl) \
|
||
|
RENAMED_FUNCTION(RSA_PKCS1_OpenSSL, RSA_PKCS1_SSLeay) \
|
||
|
- REQUIRED_FUNCTION(RSA_private_decrypt) \
|
||
|
REQUIRED_FUNCTION(RSA_private_encrypt) \
|
||
|
REQUIRED_FUNCTION(RSA_public_decrypt) \
|
||
|
REQUIRED_FUNCTION(RSA_public_encrypt) \
|
||
|
@@ -748,10 +758,13 @@ FOR_ALL_OPENSSL_FUNCTIONS
|
||
|
#define EVP_PKEY_CTX_new EVP_PKEY_CTX_new_ptr
|
||
|
#define EVP_PKEY_CTX_new_id EVP_PKEY_CTX_new_id_ptr
|
||
|
#define EVP_PKEY_base_id EVP_PKEY_base_id_ptr
|
||
|
+#define EVP_PKEY_decrypt_init EVP_PKEY_decrypt_init_ptr
|
||
|
+#define EVP_PKEY_decrypt EVP_PKEY_decrypt_ptr
|
||
|
#define EVP_PKEY_derive_set_peer EVP_PKEY_derive_set_peer_ptr
|
||
|
#define EVP_PKEY_derive_init EVP_PKEY_derive_init_ptr
|
||
|
#define EVP_PKEY_derive EVP_PKEY_derive_ptr
|
||
|
#define EVP_PKEY_free EVP_PKEY_free_ptr
|
||
|
+#define EVP_PKEY_get0_RSA EVP_PKEY_get0_RSA_ptr
|
||
|
#define EVP_PKEY_get1_DSA EVP_PKEY_get1_DSA_ptr
|
||
|
#define EVP_PKEY_get1_EC_KEY EVP_PKEY_get1_EC_KEY_ptr
|
||
|
#define EVP_PKEY_get1_RSA EVP_PKEY_get1_RSA_ptr
|
||
|
@@ -761,6 +774,7 @@ FOR_ALL_OPENSSL_FUNCTIONS
|
||
|
#define EVP_PKEY_set1_DSA EVP_PKEY_set1_DSA_ptr
|
||
|
#define EVP_PKEY_set1_EC_KEY EVP_PKEY_set1_EC_KEY_ptr
|
||
|
#define EVP_PKEY_set1_RSA EVP_PKEY_set1_RSA_ptr
|
||
|
+#define EVP_PKEY_size EVP_PKEY_size_ptr
|
||
|
#define EVP_PKEY_up_ref EVP_PKEY_up_ref_ptr
|
||
|
#define EVP_rc2_cbc EVP_rc2_cbc_ptr
|
||
|
#define EVP_rc2_ecb EVP_rc2_ecb_ptr
|
||
|
@@ -841,7 +855,6 @@ FOR_ALL_OPENSSL_FUNCTIONS
|
||
|
#define RSA_new RSA_new_ptr
|
||
|
#define RSA_pkey_ctx_ctrl RSA_pkey_ctx_ctrl_ptr
|
||
|
#define RSA_PKCS1_OpenSSL RSA_PKCS1_OpenSSL_ptr
|
||
|
-#define RSA_private_decrypt RSA_private_decrypt_ptr
|
||
|
#define RSA_private_encrypt RSA_private_encrypt_ptr
|
||
|
#define RSA_public_decrypt RSA_public_decrypt_ptr
|
||
|
#define RSA_public_encrypt RSA_public_encrypt_ptr
|
||
|
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.c b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.c
|
||
|
index 4d479dde6c..f232b382ea 100644
|
||
|
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.c
|
||
|
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.c
|
||
|
@@ -2,6 +2,7 @@
|
||
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||
|
// See the LICENSE file in the project root for more information.
|
||
|
|
||
|
+#include <assert.h>
|
||
|
#include "pal_evp_pkey.h"
|
||
|
|
||
|
EVP_PKEY* CryptoNative_EvpPkeyCreate()
|
||
|
@@ -17,6 +18,12 @@ void CryptoNative_EvpPkeyDestroy(EVP_PKEY* pkey)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
+int32_t CryptoNative_EvpPKeySize(EVP_PKEY* pkey)
|
||
|
+{
|
||
|
+ assert(pkey != NULL);
|
||
|
+ return EVP_PKEY_size(pkey);
|
||
|
+}
|
||
|
+
|
||
|
int32_t CryptoNative_UpRefEvpPkey(EVP_PKEY* pkey)
|
||
|
{
|
||
|
if (!pkey)
|
||
|
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.h b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.h
|
||
|
index 7baf997d8d..750282efdb 100644
|
||
|
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.h
|
||
|
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.h
|
||
|
@@ -24,6 +24,11 @@ Always succeeds.
|
||
|
*/
|
||
|
DLLEXPORT void CryptoNative_EvpPkeyDestroy(EVP_PKEY* pkey);
|
||
|
|
||
|
+/*
|
||
|
+Returns the maximum size, in bytes, of an operation with the provided key.
|
||
|
+*/
|
||
|
+DLLEXPORT int32_t CryptoNative_EvpPKeySize(EVP_PKEY* pkey);
|
||
|
+
|
||
|
/*
|
||
|
Used by System.Security.Cryptography.X509Certificates' OpenSslX509CertificateReader when
|
||
|
duplicating a private key context as part of duplicating the Pal object.
|
||
|
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c
|
||
|
index 29f9238ce9..6235c905db 100644
|
||
|
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c
|
||
|
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c
|
||
|
@@ -3,6 +3,10 @@
|
||
|
// See the LICENSE file in the project root for more information.
|
||
|
|
||
|
#include "pal_evp_pkey_rsa.h"
|
||
|
+#include "pal_utilities.h"
|
||
|
+#include <assert.h>
|
||
|
+
|
||
|
+static int HasNoPrivateKey(RSA* rsa);
|
||
|
|
||
|
EVP_PKEY* CryptoNative_RsaGenerateKey(int keySize)
|
||
|
{
|
||
|
@@ -16,8 +20,7 @@ EVP_PKEY* CryptoNative_RsaGenerateKey(int keySize)
|
||
|
EVP_PKEY* pkey = NULL;
|
||
|
EVP_PKEY* ret = NULL;
|
||
|
|
||
|
- if (EVP_PKEY_keygen_init(ctx) == 1 &&
|
||
|
- EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, keySize) == 1 &&
|
||
|
+ if (EVP_PKEY_keygen_init(ctx) == 1 && EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, keySize) == 1 &&
|
||
|
EVP_PKEY_keygen(ctx, &pkey) == 1)
|
||
|
{
|
||
|
ret = pkey;
|
||
|
@@ -33,6 +36,82 @@ EVP_PKEY* CryptoNative_RsaGenerateKey(int keySize)
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
+int32_t CryptoNative_RsaDecrypt(EVP_PKEY* pkey,
|
||
|
+ const uint8_t* source,
|
||
|
+ int32_t sourceLen,
|
||
|
+ RsaPaddingMode padding,
|
||
|
+ const EVP_MD* digest,
|
||
|
+ uint8_t* destination,
|
||
|
+ int32_t destinationLen)
|
||
|
+{
|
||
|
+ assert(pkey != NULL);
|
||
|
+ assert(source != NULL);
|
||
|
+ assert(destination != NULL);
|
||
|
+ assert(padding >= RsaPaddingPkcs1 && padding <= RsaPaddingOaepOrPss);
|
||
|
+ assert(digest != NULL || padding == RsaPaddingPkcs1);
|
||
|
+
|
||
|
+ EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(pkey, NULL);
|
||
|
+
|
||
|
+ int ret = -1;
|
||
|
+
|
||
|
+ if (ctx == NULL || EVP_PKEY_decrypt_init(ctx) <= 0)
|
||
|
+ {
|
||
|
+ goto done;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (padding == RsaPaddingPkcs1)
|
||
|
+ {
|
||
|
+ if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0)
|
||
|
+ {
|
||
|
+ goto done;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ else
|
||
|
+ {
|
||
|
+ assert(padding == RsaPaddingOaepOrPss);
|
||
|
+
|
||
|
+ if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0)
|
||
|
+ {
|
||
|
+ goto done;
|
||
|
+ }
|
||
|
+
|
||
|
+#pragma clang diagnostic push
|
||
|
+#pragma clang diagnostic ignored "-Wcast-qual"
|
||
|
+ if (EVP_PKEY_CTX_set_rsa_oaep_md(ctx, digest) <= 0)
|
||
|
+#pragma clang diagnostic pop
|
||
|
+ {
|
||
|
+ goto done;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ // This check may no longer be needed on OpenSSL 3.0
|
||
|
+ {
|
||
|
+ RSA* rsa = EVP_PKEY_get0_RSA(pkey);
|
||
|
+
|
||
|
+ if (rsa == NULL || HasNoPrivateKey(rsa))
|
||
|
+ {
|
||
|
+ ERR_PUT_error(ERR_LIB_RSA, RSA_F_RSA_NULL_PRIVATE_DECRYPT, RSA_R_VALUE_MISSING, __FILE__, __LINE__);
|
||
|
+ ret = -1;
|
||
|
+ goto done;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ size_t written = Int32ToSizeT(destinationLen);
|
||
|
+
|
||
|
+ if (EVP_PKEY_decrypt(ctx, destination, &written, source, Int32ToSizeT(sourceLen)) > 0)
|
||
|
+ {
|
||
|
+ ret = SizeTToInt32(written);
|
||
|
+ }
|
||
|
+
|
||
|
+done:
|
||
|
+ if (ctx != NULL)
|
||
|
+ {
|
||
|
+ EVP_PKEY_CTX_free(ctx);
|
||
|
+ }
|
||
|
+
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
RSA* CryptoNative_EvpPkeyGetRsa(EVP_PKEY* pkey)
|
||
|
{
|
||
|
return EVP_PKEY_get1_RSA(pkey);
|
||
|
@@ -42,3 +121,57 @@ int32_t CryptoNative_EvpPkeySetRsa(EVP_PKEY* pkey, RSA* rsa)
|
||
|
{
|
||
|
return EVP_PKEY_set1_RSA(pkey, rsa);
|
||
|
}
|
||
|
+
|
||
|
+static int HasNoPrivateKey(RSA* rsa)
|
||
|
+{
|
||
|
+ if (rsa == NULL)
|
||
|
+ return 1;
|
||
|
+
|
||
|
+ // Shared pointer, don't free.
|
||
|
+ const RSA_METHOD* meth = RSA_get_method(rsa);
|
||
|
+
|
||
|
+ // The method has descibed itself as having the private key external to the structure.
|
||
|
+ // That doesn't mean it's actually present, but we can't tell.
|
||
|
+#pragma clang diagnostic push
|
||
|
+#pragma clang diagnostic ignored "-Wcast-qual"
|
||
|
+ if (RSA_meth_get_flags((RSA_METHOD*)meth) & RSA_FLAG_EXT_PKEY)
|
||
|
+#pragma clang diagnostic pop
|
||
|
+ {
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ // In the event that there's a middle-ground where we report failure when success is expected,
|
||
|
+ // one could do something like check if the RSA_METHOD intercepts all private key operations:
|
||
|
+ //
|
||
|
+ // * meth->rsa_priv_enc
|
||
|
+ // * meth->rsa_priv_dec
|
||
|
+ // * meth->rsa_sign (in 1.0.x this is only respected if the RSA_FLAG_SIGN_VER flag is asserted)
|
||
|
+ //
|
||
|
+ // But, for now, leave it at the EXT_PKEY flag test.
|
||
|
+
|
||
|
+ // The module is documented as accepting either d or the full set of CRT parameters (p, q, dp, dq, qInv)
|
||
|
+ // So if we see d, we're good. Otherwise, if any of the rest are missing, we're public-only.
|
||
|
+ const BIGNUM* d;
|
||
|
+ RSA_get0_key(rsa, NULL, NULL, &d);
|
||
|
+
|
||
|
+ if (d != NULL)
|
||
|
+ {
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ const BIGNUM* p;
|
||
|
+ const BIGNUM* q;
|
||
|
+ const BIGNUM* dmp1;
|
||
|
+ const BIGNUM* dmq1;
|
||
|
+ const BIGNUM* iqmp;
|
||
|
+
|
||
|
+ RSA_get0_factors(rsa, &p, &q);
|
||
|
+ RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp);
|
||
|
+
|
||
|
+ if (p == NULL || q == NULL || dmp1 == NULL || dmq1 == NULL || iqmp == NULL)
|
||
|
+ {
|
||
|
+ return 1;
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.h b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.h
|
||
|
index 1fda149414..d220065adf 100644
|
||
|
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.h
|
||
|
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.h
|
||
|
@@ -2,15 +2,38 @@
|
||
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||
|
// See the LICENSE file in the project root for more information.
|
||
|
|
||
|
-#include "pal_types.h"
|
||
|
-#include "pal_compiler.h"
|
||
|
#include "opensslshim.h"
|
||
|
+#include "pal_compiler.h"
|
||
|
+#include "pal_types.h"
|
||
|
+
|
||
|
+/*
|
||
|
+Padding options for RSA.
|
||
|
+Matches RSAEncryptionPaddingMode / RSASignaturePaddingMode.
|
||
|
+*/
|
||
|
+typedef enum
|
||
|
+{
|
||
|
+ RsaPaddingPkcs1,
|
||
|
+ RsaPaddingOaepOrPss,
|
||
|
+} RsaPaddingMode;
|
||
|
|
||
|
/*
|
||
|
Creates an RSA key of the requested size.
|
||
|
*/
|
||
|
DLLEXPORT EVP_PKEY* CryptoNative_RsaGenerateKey(int32_t keySize);
|
||
|
|
||
|
+/*
|
||
|
+Decrypt source into destination using the specified RSA key (wrapped in an EVP_PKEY) and padding/digest options.
|
||
|
+
|
||
|
+Returns the number of bytes written to destination, -1 on error.
|
||
|
+*/
|
||
|
+DLLEXPORT int32_t CryptoNative_RsaDecrypt(EVP_PKEY* pkey,
|
||
|
+ const uint8_t* source,
|
||
|
+ int32_t sourceLen,
|
||
|
+ RsaPaddingMode padding,
|
||
|
+ const EVP_MD* digest,
|
||
|
+ uint8_t* destination,
|
||
|
+ int32_t destinationLen);
|
||
|
+
|
||
|
/*
|
||
|
Shims the EVP_PKEY_get1_RSA method.
|
||
|
|
||
|
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.c b/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.c
|
||
|
index 080027de0e..0c635dfca7 100644
|
||
|
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.c
|
||
|
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.c
|
||
|
@@ -109,19 +109,6 @@ CryptoNative_RsaPublicEncrypt(int32_t flen, const uint8_t* from, uint8_t* to, RS
|
||
|
return RSA_public_encrypt(flen, from, to, rsa, openSslPadding);
|
||
|
}
|
||
|
|
||
|
-int32_t
|
||
|
-CryptoNative_RsaPrivateDecrypt(int32_t flen, const uint8_t* from, uint8_t* to, RSA* rsa, RsaPadding padding)
|
||
|
-{
|
||
|
- if (HasNoPrivateKey(rsa))
|
||
|
- {
|
||
|
- ERR_PUT_error(ERR_LIB_RSA, RSA_F_RSA_NULL_PRIVATE_DECRYPT, RSA_R_VALUE_MISSING, __FILE__, __LINE__);
|
||
|
- return -1;
|
||
|
- }
|
||
|
-
|
||
|
- int openSslPadding = GetOpenSslPadding(padding);
|
||
|
- return RSA_private_decrypt(flen, from, to, rsa, openSslPadding);
|
||
|
-}
|
||
|
-
|
||
|
int32_t CryptoNative_RsaSignPrimitive(int32_t flen, const uint8_t* from, uint8_t* to, RSA* rsa)
|
||
|
{
|
||
|
if (HasNoPrivateKey(rsa))
|
||
|
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.h b/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.h
|
||
|
index 1c0bc2df47..30d7d9fa59 100644
|
||
|
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.h
|
||
|
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.h
|
||
|
@@ -55,14 +55,6 @@ Returns the size of the signature, or -1 on error.
|
||
|
DLLEXPORT int32_t
|
||
|
CryptoNative_RsaPublicEncrypt(int32_t flen, const uint8_t* from, uint8_t* to, RSA* rsa, RsaPadding padding);
|
||
|
|
||
|
-/*
|
||
|
-Shims the RSA_private_decrypt method.
|
||
|
-
|
||
|
-Returns the size of the signature, or -1 on error.
|
||
|
-*/
|
||
|
-DLLEXPORT int32_t
|
||
|
-CryptoNative_RsaPrivateDecrypt(int32_t flen, const uint8_t* from, uint8_t* to, RSA* rsa, RsaPadding padding);
|
||
|
-
|
||
|
/*
|
||
|
Shims RSA_private_encrypt with a fixed value of RSA_NO_PADDING.
|
||
|
|
||
|
diff --git a/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Unix.cs b/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Unix.cs
|
||
|
index ff8e91e7c9..589ff882bf 100644
|
||
|
--- a/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Unix.cs
|
||
|
+++ b/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Unix.cs
|
||
|
@@ -12,40 +12,16 @@ namespace Internal.Cryptography
|
||
|
{
|
||
|
internal static partial class HashProviderDispenser
|
||
|
{
|
||
|
- public static HashProvider CreateHashProvider(string hashAlgorithmId)
|
||
|
+ internal static HashProvider CreateHashProvider(string hashAlgorithmId)
|
||
|
{
|
||
|
- switch (hashAlgorithmId)
|
||
|
- {
|
||
|
- case HashAlgorithmNames.SHA1:
|
||
|
- return new EvpHashProvider(Interop.Crypto.EvpSha1());
|
||
|
- case HashAlgorithmNames.SHA256:
|
||
|
- return new EvpHashProvider(Interop.Crypto.EvpSha256());
|
||
|
- case HashAlgorithmNames.SHA384:
|
||
|
- return new EvpHashProvider(Interop.Crypto.EvpSha384());
|
||
|
- case HashAlgorithmNames.SHA512:
|
||
|
- return new EvpHashProvider(Interop.Crypto.EvpSha512());
|
||
|
- case HashAlgorithmNames.MD5:
|
||
|
- return new EvpHashProvider(Interop.Crypto.EvpMd5());
|
||
|
- }
|
||
|
- throw new CryptographicException(SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithmId));
|
||
|
+ IntPtr evpType = Interop.Crypto.HashAlgorithmToEvp(hashAlgorithmId);
|
||
|
+ return new EvpHashProvider(evpType);
|
||
|
}
|
||
|
|
||
|
- public static unsafe HashProvider CreateMacProvider(string hashAlgorithmId, byte[] key)
|
||
|
+ internal static unsafe HashProvider CreateMacProvider(string hashAlgorithmId, byte[] key)
|
||
|
{
|
||
|
- switch (hashAlgorithmId)
|
||
|
- {
|
||
|
- case HashAlgorithmNames.SHA1:
|
||
|
- return new HmacHashProvider(Interop.Crypto.EvpSha1(), key);
|
||
|
- case HashAlgorithmNames.SHA256:
|
||
|
- return new HmacHashProvider(Interop.Crypto.EvpSha256(), key);
|
||
|
- case HashAlgorithmNames.SHA384:
|
||
|
- return new HmacHashProvider(Interop.Crypto.EvpSha384(), key);
|
||
|
- case HashAlgorithmNames.SHA512:
|
||
|
- return new HmacHashProvider(Interop.Crypto.EvpSha512(), key);
|
||
|
- case HashAlgorithmNames.MD5:
|
||
|
- return new HmacHashProvider(Interop.Crypto.EvpMd5(), key);
|
||
|
- }
|
||
|
- throw new CryptographicException(SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithmId));
|
||
|
+ IntPtr evpType = Interop.Crypto.HashAlgorithmToEvp(hashAlgorithmId);
|
||
|
+ return new HmacHashProvider(evpType, key);
|
||
|
}
|
||
|
|
||
|
// -----------------------------
|
||
|
diff --git a/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj b/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj
|
||
|
index 6ad2b78e01..c6e8b5b69a 100644
|
||
|
--- a/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj
|
||
|
+++ b/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj
|
||
|
@@ -513,6 +513,9 @@
|
||
|
<Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.cs">
|
||
|
<Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.cs</Link>
|
||
|
</Compile>
|
||
|
+ <Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.DigestAlgs.cs">
|
||
|
+ <Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.DigestAlgs.cs</Link>
|
||
|
+ </Compile>
|
||
|
<Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.Hmac.cs">
|
||
|
<Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.Hmac.cs</Link>
|
||
|
</Compile>
|
||
|
diff --git a/src/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj b/src/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj
|
||
|
index 17ab176ec2..dbbc4848e8 100644
|
||
|
--- a/src/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj
|
||
|
+++ b/src/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj
|
||
|
@@ -57,6 +57,9 @@
|
||
|
<Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.ERR.cs">
|
||
|
<Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.ERR.cs</Link>
|
||
|
</Compile>
|
||
|
+ <Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.DigestAlgs.cs">
|
||
|
+ <Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.DigestAlgs.cs</Link>
|
||
|
+ </Compile>
|
||
|
<Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.EvpPkey.cs">
|
||
|
<Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EvpPkey.cs</Link>
|
||
|
</Compile>
|
||
|
--
|
||
|
2.31.1
|
||
|
|