From 189105a302766b4d71c99c8fe0d6458476a53568 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Thu, 13 May 2021 16:09:12 -0400 Subject: [PATCH] Detect presence of OpenSSL for AEAD algorithms on macOS This makes the IsSupported values for AesCcm and AesGcm return false on macOS when the OpenSSL shim library can't find a suitable OpenSSL, and makes AesCcm/AesGcm throw an informative exception in those cases. Additionally it fixes the IsSupported on ChaCha/Poly to not tear down the process when OpenSSL isn't found on macOS. Co-authored-by: Filip Navara --- .../Interop.OpenSslAvailable.cs | 19 ++++++++++++++++ .../TestUtilities/System/PlatformDetection.Unix.cs | 2 ++ .../tests/TestUtilities/TestUtilities.csproj | 2 ++ .../entrypoints.c | 1 + .../System.Security.Cryptography.Native/openssl.c | 11 ++++++++++ .../System.Security.Cryptography.Native/openssl.h | 2 ++ .../opensslshim.c | 12 ++++++++--- .../opensslshim.h | 1 + .../System.Security.Cryptography.Algorithms.csproj | 2 ++ .../System/Security/Cryptography/AesCcm.Android.cs | 2 ++ .../System/Security/Cryptography/AesCcm.Unix.cs | 2 ++ .../System/Security/Cryptography/AesCcm.Windows.cs | 2 ++ .../src/System/Security/Cryptography/AesCcm.cs | 14 ++++++++++-- .../System/Security/Cryptography/AesGcm.Android.cs | 2 ++ .../System/Security/Cryptography/AesGcm.Unix.cs | 2 ++ .../System/Security/Cryptography/AesGcm.Windows.cs | 2 ++ .../src/System/Security/Cryptography/AesGcm.cs | 14 ++++++++++-- .../Security/Cryptography/ChaCha20Poly1305.Unix.cs | 3 ++- .../tests/AesCcmTests.cs | 25 ++++++++++++++++------ .../tests/AesGcmTests.cs | 16 ++++++++++++++ .../tests/ChaCha20Poly1305Tests.cs | 5 +++-- 21 files changed, 125 insertions(+), 16 deletions(-) create mode 100644 src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSslAvailable.cs diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSslAvailable.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSslAvailable.cs new file mode 100644 index 0000000..66b3f72 --- /dev/null +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSslAvailable.cs @@ -0,0 +1,19 @@ +// 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; + +internal static partial class Interop +{ + internal static class OpenSslNoInit + { + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_OpenSslAvailable")] + private static extern int OpenSslAvailable(); + + private static readonly Lazy s_openSslAvailable = + new Lazy(() => OpenSslAvailable() != 0); + + internal static bool OpenSslIsAvailable => s_openSslAvailable.Value; + } +} diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Unix.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Unix.cs index e2b2631..97cbacd 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Unix.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Unix.cs @@ -101,6 +101,8 @@ namespace System } } + public static bool OpenSslPresentOnSystem => !IsBrowser && Interop.OpenSslNoInit.OpenSslIsAvailable; + private static Version s_opensslVersion; private static Version GetOpenSslVersion() { diff --git a/src/libraries/Common/tests/TestUtilities/TestUtilities.csproj b/src/libraries/Common/tests/TestUtilities/TestUtilities.csproj index 4488547..8594081 100644 --- a/src/libraries/Common/tests/TestUtilities/TestUtilities.csproj +++ b/src/libraries/Common/tests/TestUtilities/TestUtilities.csproj @@ -64,6 +64,8 @@ + #include #include -#include #include #include "opensslshim.h" @@ -51,7 +50,7 @@ static void DlOpen(const char* libraryName) } } -static bool OpenLibrary() +int OpenLibrary() { // If there is an override of the version specified using the CLR_OPENSSL_VERSION_OVERRIDE // env variable, try to load that first. @@ -124,7 +123,14 @@ static bool OpenLibrary() DlOpen(MAKELIB("8")); } - return libssl != NULL; + if (libssl != NULL) + { + return 1; + } + else + { + return 0; + } } void InitializeOpenSSLShim(void) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/opensslshim.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native/opensslshim.h index 8650fe5..602cca6 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/opensslshim.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/opensslshim.h @@ -95,6 +95,7 @@ void ERR_put_error(int32_t lib, int32_t func, int32_t reason, const char* file, #define NEED_OPENSSL_1_1 true #define NEED_OPENSSL_3_0 true +int OpenLibrary(void); void InitializeOpenSSLShim(void); #if !HAVE_OPENSSL_EC2M diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj b/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj index 90cda5e..38ec443 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj @@ -586,6 +586,8 @@ Link="Common\System\Security\Cryptography\ECDiffieHellmanDerivation.cs" /> + diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesCcm.Android.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesCcm.Android.cs index 07c20a1..ba37947 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesCcm.Android.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesCcm.Android.cs @@ -11,6 +11,8 @@ namespace System.Security.Cryptography { private byte[] _key; + public static bool IsSupported => true; + [MemberNotNull(nameof(_key))] private void ImportKey(ReadOnlySpan key) { diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesCcm.Unix.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesCcm.Unix.cs index cd455c3..6c78bee0 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesCcm.Unix.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesCcm.Unix.cs @@ -11,6 +11,8 @@ namespace System.Security.Cryptography { private byte[] _key; + public static bool IsSupported { get; } = Interop.OpenSslNoInit.OpenSslIsAvailable; + [MemberNotNull(nameof(_key))] private void ImportKey(ReadOnlySpan key) { diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesCcm.Windows.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesCcm.Windows.cs index 95884be..345bd13 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesCcm.Windows.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesCcm.Windows.cs @@ -11,6 +11,8 @@ namespace System.Security.Cryptography { private SafeKeyHandle _keyHandle; + public static bool IsSupported => true; + [MemberNotNull(nameof(_keyHandle))] private void ImportKey(ReadOnlySpan key) { diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesCcm.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesCcm.cs index 5940ece..91c73dc 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesCcm.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesCcm.cs @@ -14,12 +14,16 @@ namespace System.Security.Cryptography public AesCcm(ReadOnlySpan key) { + ThrowIfNotSupported(); + AesAEAD.CheckKeySize(key.Length); ImportKey(key); } public AesCcm(byte[] key) { + ThrowIfNotSupported(); + if (key == null) throw new ArgumentNullException(nameof(key)); @@ -27,8 +31,6 @@ namespace System.Security.Cryptography ImportKey(key); } - public static bool IsSupported => true; - public void Encrypt(byte[] nonce, byte[] plaintext, byte[] ciphertext, byte[] tag, byte[]? associatedData = null) { AeadCommon.CheckArgumentsForNull(nonce, plaintext, ciphertext, tag); @@ -78,5 +80,13 @@ namespace System.Security.Cryptography if (!tag.Length.IsLegalSize(TagByteSizes)) throw new ArgumentException(SR.Cryptography_InvalidTagLength, nameof(tag)); } + + private static void ThrowIfNotSupported() + { + if (!IsSupported) + { + throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_AlgorithmNotSupported, nameof(AesCcm))); + } + } } } diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesGcm.Android.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesGcm.Android.cs index 8a69977..a43c16c 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesGcm.Android.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesGcm.Android.cs @@ -11,6 +11,8 @@ namespace System.Security.Cryptography { private SafeEvpCipherCtxHandle _ctxHandle; + public static bool IsSupported => true; + [MemberNotNull(nameof(_ctxHandle))] private void ImportKey(ReadOnlySpan key) { diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesGcm.Unix.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesGcm.Unix.cs index b18ce7f..1691f33 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesGcm.Unix.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesGcm.Unix.cs @@ -11,6 +11,8 @@ namespace System.Security.Cryptography { private SafeEvpCipherCtxHandle _ctxHandle; + public static bool IsSupported { get; } = Interop.OpenSslNoInit.OpenSslIsAvailable; + [MemberNotNull(nameof(_ctxHandle))] private void ImportKey(ReadOnlySpan key) { diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesGcm.Windows.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesGcm.Windows.cs index 31e6f64..f6326f4 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesGcm.Windows.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesGcm.Windows.cs @@ -11,6 +11,8 @@ namespace System.Security.Cryptography { private SafeKeyHandle _keyHandle; + public static bool IsSupported => true; + [MemberNotNull(nameof(_keyHandle))] private void ImportKey(ReadOnlySpan key) { diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesGcm.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesGcm.cs index db1e51d..78b0ce8 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesGcm.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesGcm.cs @@ -15,12 +15,16 @@ namespace System.Security.Cryptography public AesGcm(ReadOnlySpan key) { + ThrowIfNotSupported(); + AesAEAD.CheckKeySize(key.Length); ImportKey(key); } public AesGcm(byte[] key) { + ThrowIfNotSupported(); + if (key == null) throw new ArgumentNullException(nameof(key)); @@ -28,8 +32,6 @@ namespace System.Security.Cryptography ImportKey(key); } - public static bool IsSupported => true; - public void Encrypt(byte[] nonce, byte[] plaintext, byte[] ciphertext, byte[] tag, byte[]? associatedData = null) { AeadCommon.CheckArgumentsForNull(nonce, plaintext, ciphertext, tag); @@ -79,5 +81,13 @@ namespace System.Security.Cryptography if (!tag.Length.IsLegalSize(TagByteSizes)) throw new ArgumentException(SR.Cryptography_InvalidTagLength, nameof(tag)); } + + private static void ThrowIfNotSupported() + { + if (!IsSupported) + { + throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_AlgorithmNotSupported, nameof(AesGcm))); + } + } } } diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ChaCha20Poly1305.Unix.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ChaCha20Poly1305.Unix.cs index 824d37c..6fd2116 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ChaCha20Poly1305.Unix.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ChaCha20Poly1305.Unix.cs @@ -9,7 +9,8 @@ namespace System.Security.Cryptography { public sealed partial class ChaCha20Poly1305 { - public static bool IsSupported { get; } = Interop.Crypto.EvpChaCha20Poly1305() != IntPtr.Zero; + public static bool IsSupported { get; } = Interop.OpenSslNoInit.OpenSslIsAvailable && + Interop.Crypto.EvpChaCha20Poly1305() != IntPtr.Zero; private SafeEvpCipherCtxHandle _ctxHandle; diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/AesCcmTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/AesCcmTests.cs index 149b28d..7ee3d6c 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/AesCcmTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/AesCcmTests.cs @@ -687,15 +687,28 @@ namespace System.Security.Cryptography.Algorithms.Tests public class AesCcmIsSupportedTests { - public class AesGcmIsSupportedTests + public static bool RuntimeSaysIsNotSupported => !AesCcm.IsSupported; + + [ConditionalFact(nameof(RuntimeSaysIsNotSupported))] + public static void CtorThrowsPNSEIfNotSupported() { - [Fact] - public static void CheckIsSupported() - { - bool expectedIsSupported = !PlatformDetection.IsBrowser; + byte[] key = RandomNumberGenerator.GetBytes(256 / 8); - Assert.Equal(expectedIsSupported, AesCcm.IsSupported); + Assert.Throws(() => new AesCcm(key)); + Assert.Throws(() => new AesCcm(key.AsSpan())); + } + + [Fact] + public static void CheckIsSupported() + { + bool expectedIsSupported = !PlatformDetection.IsBrowser; + + if (PlatformDetection.IsOSX) + { + expectedIsSupported = PlatformDetection.OpenSslPresentOnSystem; } + + Assert.Equal(expectedIsSupported, AesCcm.IsSupported); } } } diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/AesGcmTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/AesGcmTests.cs index aafb9ff..514c189 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/AesGcmTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/AesGcmTests.cs @@ -855,11 +855,27 @@ namespace System.Security.Cryptography.Algorithms.Tests public class AesGcmIsSupportedTests { + public static bool RuntimeSaysIsNotSupported => !AesGcm.IsSupported; + + [ConditionalFact(nameof(RuntimeSaysIsNotSupported))] + public static void CtorThrowsPNSEIfNotSupported() + { + byte[] key = RandomNumberGenerator.GetBytes(256 / 8); + + Assert.Throws(() => new AesGcm(key)); + Assert.Throws(() => new AesGcm(key.AsSpan())); + } + [Fact] public static void CheckIsSupported() { bool expectedIsSupported = !PlatformDetection.IsBrowser; + if (PlatformDetection.IsOSX) + { + expectedIsSupported = PlatformDetection.OpenSslPresentOnSystem; + } + Assert.Equal(expectedIsSupported, AesGcm.IsSupported); } } diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/ChaCha20Poly1305Tests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/ChaCha20Poly1305Tests.cs index 55a894e..5a96943 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/ChaCha20Poly1305Tests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/ChaCha20Poly1305Tests.cs @@ -447,9 +447,10 @@ namespace System.Security.Cryptography.Algorithms.Tests // The test queries the OS directly to ensure our version check is correct. expectedIsSupported = CngUtility.IsAlgorithmSupported("CHACHA20_POLY1305"); } - else if (PlatformDetection.IsOSX || PlatformDetection.IsOpenSslSupported) + else if (PlatformDetection.OpenSslPresentOnSystem && + (PlatformDetection.IsOSX || PlatformDetection.IsOpenSslSupported)) { - const int OpenSslChaChaMinimumVersion = 0x1010000F; + const int OpenSslChaChaMinimumVersion = 0x1_01_00_00_F; //major_minor_fix_patch_status expectedIsSupported = SafeEvpPKeyHandle.OpenSslVersion >= OpenSslChaChaMinimumVersion; } -- 2.7.4