Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make SafeEvpPKeyHandle.OpenKeyFromProvider throw PNSE on OSSL less than 3.0 #106397

Merged
merged 3 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,9 @@
<data name="PlatformNotSupported_CryptographyOpenSSLNotFound" xml:space="preserve">
<value>OpenSSL is required for algorithm '{0}' but could not be found or loaded.</value>
</data>
<data name="PlatformNotSupported_CryptographyOpenSSL3NotFound" xml:space="preserve">
<value>OpenSSL 3.0 or higher is required but could not be found or loaded.</value>
</data>
krwq marked this conversation as resolved.
Show resolved Hide resolved
<data name="Security_AccessDenied" xml:space="preserve">
<value>Access is denied.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,13 @@ public static SafeEvpPKeyHandle OpenKeyFromProvider(string providerName, string
throw new PlatformNotSupportedException(SR.PlatformNotSupported_CryptographyOpenSSL);
}

// 3.0+ are M_NN_00_PP_p (Major, Minor, 0, Patch, Preview)
// 1.x.y are 1_XX_YY_PP_p
if (SafeEvpPKeyHandle.OpenSslVersion < 0x3_00_00_00_0)
{
throw new PlatformNotSupportedException(SR.PlatformNotSupported_CryptographyOpenSSL3NotFound);
}

krwq marked this conversation as resolved.
Show resolved Hide resolved
return Interop.Crypto.LoadKeyFromProvider(providerName, keyUri);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Linq;
using System.Text;
using Test.Cryptography;
using Xunit;
using TempFileHolder = System.Security.Cryptography.X509Certificates.Tests.TempFileHolder;

namespace System.Security.Cryptography.Tests
{
Expand Down Expand Up @@ -43,10 +45,13 @@ public class OpenSslNamedKeysTests
private static string TpmRsaDecryptKeyHandleUri { get; } = GetHandleKeyUri(TpmRsaDecryptKeyHandle);

public static bool ShouldRunEngineTests { get; } = PlatformDetection.OpenSslPresentOnSystem && StringToBool(Environment.GetEnvironmentVariable(TestEngineEnabledEnvVarName));
public static bool ShouldRunProviderEcDsaTests { get; } = PlatformDetection.OpenSslPresentOnSystem && !string.IsNullOrEmpty(TpmEcDsaKeyHandleUri);
public static bool ShouldRunProviderEcDhTests { get; } = PlatformDetection.OpenSslPresentOnSystem && !string.IsNullOrEmpty(TpmEcDhKeyHandleUri);
public static bool ShouldRunProviderRsaSignTests { get; } = PlatformDetection.OpenSslPresentOnSystem && !string.IsNullOrEmpty(TpmRsaSignKeyHandleUri);
public static bool ShouldRunProviderRsaDecryptTests { get; } = PlatformDetection.OpenSslPresentOnSystem && !string.IsNullOrEmpty(TpmRsaDecryptKeyHandleUri);

public static bool ProvidersSupported { get; } = PlatformDetection.OpenSslPresentOnSystem && PlatformDetection.OpenSslVersion >= new Version(3, 0, 0);
krwq marked this conversation as resolved.
Show resolved Hide resolved
public static bool ProvidersNotSupported => !ProvidersSupported;
public static bool ShouldRunProviderEcDsaTests { get; } = ProvidersSupported && !string.IsNullOrEmpty(TpmEcDsaKeyHandleUri);
public static bool ShouldRunProviderEcDhTests { get; } = ProvidersSupported && !string.IsNullOrEmpty(TpmEcDhKeyHandleUri);
public static bool ShouldRunProviderRsaSignTests { get; } = ProvidersSupported && !string.IsNullOrEmpty(TpmRsaSignKeyHandleUri);
public static bool ShouldRunProviderRsaDecryptTests { get; } = ProvidersSupported && !string.IsNullOrEmpty(TpmRsaDecryptKeyHandleUri);
public static bool ShouldRunAnyProviderTests => ShouldRunProviderEcDsaTests || ShouldRunProviderEcDhTests || ShouldRunProviderRsaSignTests || ShouldRunProviderRsaDecryptTests;

public static bool ShouldRunTpmTssTests => ShouldRunEngineTests && !string.IsNullOrEmpty(TpmEcDsaKeyHandle);
Expand Down Expand Up @@ -86,10 +91,15 @@ private static string GetHandleKeyUri(string handle)
"B27434FA544BDAC679E1E16581D0E90203010001").HexToByteArray();

[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.OpenSslNotPresentOnSystem))]
public static void NotSupported()
public static void EngineNotSupported_ThrowsPlatformNotSupported()
{
Assert.Throws<PlatformNotSupportedException>(() => SafeEvpPKeyHandle.OpenPublicKeyFromEngine(TestEngineName, TestEngineKeyId));
Assert.Throws<PlatformNotSupportedException>(() => SafeEvpPKeyHandle.OpenPrivateKeyFromEngine(TestEngineName, TestEngineKeyId));
}

[ConditionalFact(nameof(ProvidersNotSupported))]
public static void ProvidersNotSupported_ThrowsPlatformNotSupported()
{
Assert.Throws<PlatformNotSupportedException>(() => SafeEvpPKeyHandle.OpenKeyFromProvider(Tpm2ProviderName, AnyProviderKeyUri));
}

Expand All @@ -111,10 +121,14 @@ public static void EmptyNameThroughNullCharacter()
{
Assert.ThrowsAny<CryptographicException>(() => SafeEvpPKeyHandle.OpenPrivateKeyFromEngine("\0", "foo"));
Assert.ThrowsAny<CryptographicException>(() => SafeEvpPKeyHandle.OpenPublicKeyFromEngine("\0", "foo"));
Assert.ThrowsAny<CryptographicException>(() => SafeEvpPKeyHandle.OpenKeyFromProvider("\0", "foo"));

if (ProvidersSupported)
{
Assert.ThrowsAny<CryptographicException>(() => SafeEvpPKeyHandle.OpenKeyFromProvider("\0", "foo"));
}
}

[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.OpenSslPresentOnSystem))]
[ConditionalFact(nameof(ProvidersSupported))]
public static void EmptyUriThroughNullCharacter()
{
Assert.ThrowsAny<CryptographicException>(() => SafeEvpPKeyHandle.OpenKeyFromProvider("default", "\0"));
Expand All @@ -127,7 +141,7 @@ public static void Engine_NonExisting()
Assert.ThrowsAny<CryptographicException>(() => SafeEvpPKeyHandle.OpenPublicKeyFromEngine(NonExistingEngineOrProviderKeyName, TestEngineKeyId));
}

[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.OpenSslPresentOnSystem))]
[ConditionalFact(nameof(ProvidersSupported))]
public static void Provider_NonExisting()
{
Assert.ThrowsAny<CryptographicException>(() => SafeEvpPKeyHandle.OpenKeyFromProvider(NonExistingEngineOrProviderKeyName, AnyProviderKeyUri));
Expand All @@ -146,6 +160,63 @@ public static void Provider_NonExistingKey()
Assert.ThrowsAny<CryptographicException>(() => SafeEvpPKeyHandle.OpenKeyFromProvider(Tpm2ProviderName, NonExistingEngineOrProviderKeyName));
}

[ConditionalFact(nameof(ProvidersSupported))]
public static void Provider_Default_RSASignAndDecrypt()
{
using RSA originalKey = RSA.Create();
string pem = originalKey.ExportRSAPrivateKeyPem();

using TempFileHolder pemFile = new TempFileHolder(Encoding.UTF8.GetBytes(pem));
Uri fileUri = new Uri(pemFile.FilePath);
string keyUri = fileUri.AbsoluteUri;
using SafeEvpPKeyHandle priKeyHandle = SafeEvpPKeyHandle.OpenKeyFromProvider("default", keyUri);
using RSA rsaPri = new RSAOpenSsl(priKeyHandle);
byte[] data = new byte[] { 1, 2, 3, 1, 1, 2, 3 };
byte[] signature = rsaPri.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pss);
Assert.True(originalKey.VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pss), "signature does not verify with the right key");

byte[] encrypted = originalKey.Encrypt(data, RSAEncryptionPadding.OaepSHA256);
byte[] decrypted = rsaPri.Decrypt(encrypted, RSAEncryptionPadding.OaepSHA256);
Assert.Equal(data, decrypted);
}

[ConditionalFact(nameof(ProvidersSupported))]
public static void Provider_Default_ECDsaSignAndVerify()
{
using ECDsa originalKey = ECDsa.Create();
string pem = originalKey.ExportECPrivateKeyPem();

using TempFileHolder pemFile = new TempFileHolder(Encoding.UTF8.GetBytes(pem));
Uri fileUri = new Uri(pemFile.FilePath);
string keyUri = fileUri.AbsoluteUri;
using SafeEvpPKeyHandle priKeyHandle = SafeEvpPKeyHandle.OpenKeyFromProvider("default", keyUri);
using ECDsa ecdsaPri = new ECDsaOpenSsl(priKeyHandle);
byte[] data = new byte[] { 1, 2, 3, 1, 1, 2, 3 };
byte[] signature = ecdsaPri.SignData(data, HashAlgorithmName.SHA256);
Assert.True(originalKey.VerifyData(data, signature, HashAlgorithmName.SHA256), "signature does not verify with the right key");
}

[ConditionalFact(nameof(ProvidersSupported))]
public static void Provider_Default_ECDHKeyExchange()
{
using ECDiffieHellman originalAliceKey = ECDiffieHellman.Create();
string pem = originalAliceKey.ExportECPrivateKeyPem();

using TempFileHolder pemFile = new TempFileHolder(Encoding.UTF8.GetBytes(pem));
Uri fileUri = new Uri(pemFile.FilePath);
string keyUri = fileUri.AbsoluteUri;
using SafeEvpPKeyHandle priKeyHandle = SafeEvpPKeyHandle.OpenKeyFromProvider("default", keyUri);
using ECDiffieHellman alicePri = new ECDiffieHellmanOpenSsl(priKeyHandle);
using ECDiffieHellman bobPri = ECDiffieHellman.Create(alicePri.ExportParameters(false).Curve);

byte[] sharedSecret1 = originalAliceKey.DeriveRawSecretAgreement(bobPri.PublicKey);
byte[] sharedSecret2 = alicePri.DeriveRawSecretAgreement(bobPri.PublicKey);
byte[] sharedSecret3 = bobPri.DeriveRawSecretAgreement(alicePri.PublicKey);

Assert.Equal(sharedSecret1, sharedSecret2);
Assert.Equal(sharedSecret1, sharedSecret3);
}

[ConditionalFact(nameof(ShouldRunEngineTests))]
public static void Engine_OpenExistingPrivateKey()
{
Expand Down
Loading