diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.Cipher.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.Cipher.cs index df82c65487de19..1a90ae1c95a371 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.Cipher.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.Cipher.cs @@ -258,6 +258,9 @@ internal static void EvpCipherSetCcmTagLength(SafeEvpCipherCtxHandle ctx, int ta [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes128Ccm")] internal static partial IntPtr EvpAes128Ccm(); + [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes128WrapPad")] + internal static partial IntPtr EvpAes128WrapPad(); + [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes192Ecb")] internal static partial IntPtr EvpAes192Ecb(); @@ -276,6 +279,9 @@ internal static void EvpCipherSetCcmTagLength(SafeEvpCipherCtxHandle ctx, int ta [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes192Ccm")] internal static partial IntPtr EvpAes192Ccm(); + [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes192WrapPad")] + internal static partial IntPtr EvpAes192WrapPad(); + [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes256Ecb")] internal static partial IntPtr EvpAes256Ecb(); @@ -294,6 +300,9 @@ internal static void EvpCipherSetCcmTagLength(SafeEvpCipherCtxHandle ctx, int ta [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes256Ccm")] internal static partial IntPtr EvpAes256Ccm(); + [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes256WrapPad")] + internal static partial IntPtr EvpAes256WrapPad(); + [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpDesCbc")] internal static partial IntPtr EvpDesCbc(); diff --git a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj index 13ae5bdccba4f2..76d05a021137b7 100644 --- a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj +++ b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj @@ -1244,7 +1244,7 @@ Link="Common\System\Security\Cryptography\SlhDsaImplementation.NotSupported.cs" /> - + diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesImplementation.Android.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesImplementation.Android.cs new file mode 100644 index 00000000000000..98fe89c6ac544c --- /dev/null +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesImplementation.Android.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Security.Cryptography +{ + internal sealed partial class AesImplementation + { + private static UniversalCryptoTransform CreateTransformCore( + CipherMode cipherMode, + PaddingMode paddingMode, + ReadOnlySpan key, + byte[]? iv, + int blockSize, + int paddingSize, + int feedback, + bool encrypting) + { + // The algorithm pointer is a static pointer, so not having any cleanup code is correct. + IntPtr algorithm = GetAlgorithm(key.Length * 8, feedback * 8, cipherMode); + + BasicSymmetricCipher cipher = new OpenSslCipher(algorithm, cipherMode, blockSize, paddingSize, key, iv, encrypting); + return UniversalCryptoTransform.Create(paddingMode, cipher, encrypting); + } + + private static OpenSslCipherLite CreateLiteCipher( + CipherMode cipherMode, + ReadOnlySpan key, + ReadOnlySpan iv, + int blockSize, + int paddingSize, + int feedback, + bool encrypting) + { + IntPtr algorithm = GetAlgorithm(key.Length * 8, feedback * 8, cipherMode); + return new OpenSslCipherLite(algorithm, blockSize, paddingSize, key, iv, encrypting); + } + + private static IntPtr GetAlgorithm(int keySize, int feedback, CipherMode cipherMode) => + (keySize, cipherMode) switch + { + // Neither OpenSSL nor Cng Aes support CTS mode. + + (128, CipherMode.CBC) => Interop.Crypto.EvpAes128Cbc(), + (128, CipherMode.ECB) => Interop.Crypto.EvpAes128Ecb(), + (128, CipherMode.CFB) when feedback == 8 => Interop.Crypto.EvpAes128Cfb8(), + (128, CipherMode.CFB) when feedback == 128 => Interop.Crypto.EvpAes128Cfb128(), + + (192, CipherMode.CBC) => Interop.Crypto.EvpAes192Cbc(), + (192, CipherMode.ECB) => Interop.Crypto.EvpAes192Ecb(), + (192, CipherMode.CFB) when feedback == 8 => Interop.Crypto.EvpAes192Cfb8(), + (192, CipherMode.CFB) when feedback == 128 => Interop.Crypto.EvpAes192Cfb128(), + + (256, CipherMode.CBC) => Interop.Crypto.EvpAes256Cbc(), + (256, CipherMode.ECB) => Interop.Crypto.EvpAes256Ecb(), + (256, CipherMode.CFB) when feedback == 8 => Interop.Crypto.EvpAes256Cfb8(), + (256, CipherMode.CFB) when feedback == 128 => Interop.Crypto.EvpAes256Cfb128(), + + _ => throw (keySize == 128 || keySize == 192 || keySize == 256 ? (Exception) + new NotSupportedException() : + new CryptographicException(SR.Cryptography_InvalidKeySize)), + }; + } +} diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesImplementation.OpenSsl.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesImplementation.OpenSsl.cs index 98fe89c6ac544c..6c9437e18d9ef1 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesImplementation.OpenSsl.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesImplementation.OpenSsl.cs @@ -1,6 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + namespace System.Security.Cryptography { internal sealed partial class AesImplementation @@ -35,6 +39,75 @@ private static OpenSslCipherLite CreateLiteCipher( return new OpenSslCipherLite(algorithm, blockSize, paddingSize, key, iv, encrypting); } + protected override void EncryptKeyWrapPaddedCore(ReadOnlySpan source, Span destination) + { + int written = KeyWrap(source, destination, enc: 1); + Debug.Assert(written == destination.Length); + } + + protected override int DecryptKeyWrapPaddedCore(ReadOnlySpan source, Span destination) + { + return KeyWrap(source, destination, enc: 0); + } + + private int KeyWrap(ReadOnlySpan source, Span destination, int enc) + { + Debug.Assert(enc is 0 or 1); + + SafeEvpCipherCtxHandle ctx = GetKey().UseKey( + state: enc, + static (enc, key) => + { + int keySizeInBits = key.Length * 8; + + IntPtr algorithm = GetKeyWrapAlgorithm(keySizeInBits); + + SafeEvpCipherCtxHandle ctx = Interop.Crypto.EvpCipherCreate( + algorithm, + ref MemoryMarshal.GetReference(key), + key.Length * 8, + ref MemoryMarshal.GetReference(ReadOnlySpan.Empty), + enc); + + if (ctx.IsInvalid) + { + ctx.Dispose(); + throw Interop.Crypto.CreateOpenSslCryptographicException(); + } + + return ctx; + }); + + int written; + + using (ctx) + { + bool ret = Interop.Crypto.EvpCipherUpdate( + ctx, + destination, + out written, + source); + + if (!ret) + { + throw Interop.Crypto.CreateOpenSslCryptographicException(); + } + + Debug.Assert(written > 0); + + // Experimentation and code insepection show that EVP_CipherFinal_ex is not needed here, + // the work is done in EVP_CipherUpdate. + // Since AES-KW(P) involves multiple passes over the data, where the end of each pass + // stores a tag/checksum back in the beginning of the buffer, it makes sense that only + // one of Update or Final could write data, and they chose to go with Update. + // + // As the call to Final does not yield more data, and we're about to dispose the context, + // don't bother making the call. + } + + return written; + } + private static IntPtr GetAlgorithm(int keySize, int feedback, CipherMode cipherMode) => (keySize, cipherMode) switch { @@ -59,5 +132,14 @@ private static IntPtr GetAlgorithm(int keySize, int feedback, CipherMode cipherM new NotSupportedException() : new CryptographicException(SR.Cryptography_InvalidKeySize)), }; + + private static IntPtr GetKeyWrapAlgorithm(int keySize) => + keySize switch + { + 128 => Interop.Crypto.EvpAes128WrapPad(), + 192 => Interop.Crypto.EvpAes192WrapPad(), + 256 => Interop.Crypto.EvpAes256WrapPad(), + _ => throw new CryptographicException(SR.Cryptography_InvalidKeySize), + }; } } diff --git a/src/native/libs/System.Security.Cryptography.Native/entrypoints.c b/src/native/libs/System.Security.Cryptography.Native/entrypoints.c index a16bbe9c135742..a6e647ecaf50e5 100644 --- a/src/native/libs/System.Security.Cryptography.Native/entrypoints.c +++ b/src/native/libs/System.Security.Cryptography.Native/entrypoints.c @@ -100,18 +100,21 @@ static const Entry s_cryptoNative[] = DllImportEntry(CryptoNative_EvpAes128Cfb8) DllImportEntry(CryptoNative_EvpAes128Ecb) DllImportEntry(CryptoNative_EvpAes128Gcm) + DllImportEntry(CryptoNative_EvpAes128WrapPad) DllImportEntry(CryptoNative_EvpAes192Cbc) DllImportEntry(CryptoNative_EvpAes192Ccm) DllImportEntry(CryptoNative_EvpAes192Cfb128) DllImportEntry(CryptoNative_EvpAes192Cfb8) DllImportEntry(CryptoNative_EvpAes192Ecb) DllImportEntry(CryptoNative_EvpAes192Gcm) + DllImportEntry(CryptoNative_EvpAes192WrapPad) DllImportEntry(CryptoNative_EvpAes256Cbc) DllImportEntry(CryptoNative_EvpAes256Ccm) DllImportEntry(CryptoNative_EvpAes256Cfb128) DllImportEntry(CryptoNative_EvpAes256Cfb8) DllImportEntry(CryptoNative_EvpAes256Ecb) DllImportEntry(CryptoNative_EvpAes256Gcm) + DllImportEntry(CryptoNative_EvpAes256WrapPad) DllImportEntry(CryptoNative_EvpChaCha20Poly1305) DllImportEntry(CryptoNative_EvpCipherCreate2) DllImportEntry(CryptoNative_EvpCipherCreatePartial) diff --git a/src/native/libs/System.Security.Cryptography.Native/opensslshim.h b/src/native/libs/System.Security.Cryptography.Native/opensslshim.h index 47e4a3994a3932..ec5a01079782f9 100644 --- a/src/native/libs/System.Security.Cryptography.Native/opensslshim.h +++ b/src/native/libs/System.Security.Cryptography.Native/opensslshim.h @@ -457,18 +457,21 @@ extern bool g_libSslUses32BitTime; REQUIRED_FUNCTION(EVP_aes_128_cfb8) \ REQUIRED_FUNCTION(EVP_aes_128_ecb) \ REQUIRED_FUNCTION(EVP_aes_128_gcm) \ + REQUIRED_FUNCTION(EVP_aes_128_wrap_pad) \ REQUIRED_FUNCTION(EVP_aes_192_cbc) \ REQUIRED_FUNCTION(EVP_aes_192_ccm) \ REQUIRED_FUNCTION(EVP_aes_192_cfb128) \ REQUIRED_FUNCTION(EVP_aes_192_cfb8) \ REQUIRED_FUNCTION(EVP_aes_192_ecb) \ REQUIRED_FUNCTION(EVP_aes_192_gcm) \ + REQUIRED_FUNCTION(EVP_aes_192_wrap_pad) \ REQUIRED_FUNCTION(EVP_aes_256_cbc) \ REQUIRED_FUNCTION(EVP_aes_256_ccm) \ REQUIRED_FUNCTION(EVP_aes_256_cfb128) \ REQUIRED_FUNCTION(EVP_aes_256_cfb8) \ REQUIRED_FUNCTION(EVP_aes_256_ecb) \ REQUIRED_FUNCTION(EVP_aes_256_gcm) \ + REQUIRED_FUNCTION(EVP_aes_256_wrap_pad) \ LIGHTUP_FUNCTION(EVP_chacha20_poly1305) \ LEGACY_FUNCTION(EVP_CIPHER_CTX_cleanup) \ REQUIRED_FUNCTION(EVP_CIPHER_CTX_ctrl) \ @@ -476,6 +479,7 @@ extern bool g_libSslUses32BitTime; LEGACY_FUNCTION(EVP_CIPHER_CTX_init) \ FALLBACK_FUNCTION(EVP_CIPHER_CTX_new) \ FALLBACK_FUNCTION(EVP_CIPHER_CTX_reset) \ + REQUIRED_FUNCTION(EVP_CIPHER_CTX_set_flags) \ REQUIRED_FUNCTION(EVP_CIPHER_CTX_set_key_length) \ REQUIRED_FUNCTION(EVP_CIPHER_CTX_set_padding) \ RENAMED_FUNCTION(EVP_CIPHER_get_nid, EVP_CIPHER_nid) \ @@ -1029,18 +1033,21 @@ extern TYPEOF(OPENSSL_gmtime)* OPENSSL_gmtime_ptr; #define EVP_aes_128_ecb EVP_aes_128_ecb_ptr #define EVP_aes_128_gcm EVP_aes_128_gcm_ptr #define EVP_aes_128_ccm EVP_aes_128_ccm_ptr +#define EVP_aes_128_wrap_pad EVP_aes_128_wrap_pad_ptr #define EVP_aes_192_cbc EVP_aes_192_cbc_ptr #define EVP_aes_192_cfb8 EVP_aes_192_cfb8_ptr #define EVP_aes_192_cfb128 EVP_aes_192_cfb128_ptr #define EVP_aes_192_ecb EVP_aes_192_ecb_ptr #define EVP_aes_192_gcm EVP_aes_192_gcm_ptr #define EVP_aes_192_ccm EVP_aes_192_ccm_ptr +#define EVP_aes_192_wrap_pad EVP_aes_192_wrap_pad_ptr #define EVP_aes_256_cbc EVP_aes_256_cbc_ptr #define EVP_aes_256_cfb8 EVP_aes_256_cfb8_ptr #define EVP_aes_256_cfb128 EVP_aes_256_cfb128_ptr #define EVP_aes_256_ecb EVP_aes_256_ecb_ptr #define EVP_aes_256_gcm EVP_aes_256_gcm_ptr #define EVP_aes_256_ccm EVP_aes_256_ccm_ptr +#define EVP_aes_256_wrap_pad EVP_aes_256_wrap_pad_ptr #define EVP_chacha20_poly1305 EVP_chacha20_poly1305_ptr #define EVP_CIPHER_CTX_cleanup EVP_CIPHER_CTX_cleanup_ptr #define EVP_CIPHER_CTX_ctrl EVP_CIPHER_CTX_ctrl_ptr @@ -1048,6 +1055,7 @@ extern TYPEOF(OPENSSL_gmtime)* OPENSSL_gmtime_ptr; #define EVP_CIPHER_CTX_init EVP_CIPHER_CTX_init_ptr #define EVP_CIPHER_CTX_new EVP_CIPHER_CTX_new_ptr #define EVP_CIPHER_CTX_reset EVP_CIPHER_CTX_reset_ptr +#define EVP_CIPHER_CTX_set_flags EVP_CIPHER_CTX_set_flags_ptr #define EVP_CIPHER_CTX_set_key_length EVP_CIPHER_CTX_set_key_length_ptr #define EVP_CIPHER_CTX_set_padding EVP_CIPHER_CTX_set_padding_ptr #define EVP_CIPHER_get_nid EVP_CIPHER_get_nid_ptr diff --git a/src/native/libs/System.Security.Cryptography.Native/pal_evp_cipher.c b/src/native/libs/System.Security.Cryptography.Native/pal_evp_cipher.c index 4cdfbce6bbcda5..17562f965e6fda 100644 --- a/src/native/libs/System.Security.Cryptography.Native/pal_evp_cipher.c +++ b/src/native/libs/System.Security.Cryptography.Native/pal_evp_cipher.c @@ -8,6 +8,8 @@ #define SUCCESS 1 #define KEEP_CURRENT_DIRECTION -1 +c_static_assert(EVP_CIPHER_CTX_FLAG_WRAP_ALLOW == 1); + EVP_CIPHER_CTX* CryptoNative_EvpCipherCreate2(const EVP_CIPHER* type, uint8_t* key, int32_t keyLength, unsigned char* iv, int32_t enc) { @@ -30,6 +32,9 @@ CryptoNative_EvpCipherCreate2(const EVP_CIPHER* type, uint8_t* key, int32_t keyL return NULL; } + // Required for OpenSSL 1.1 AES-KWP, no-op in OpenSSL 3. + EVP_CIPHER_CTX_set_flags(ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); + // Perform partial initialization so we can set the key lengths int ret = EVP_CipherInit_ex(ctx, type, NULL, NULL, NULL, 0); if (!ret) @@ -275,6 +280,12 @@ const EVP_CIPHER* CryptoNative_EvpAes128Ccm(void) return EVP_aes_128_ccm(); } +const EVP_CIPHER* CryptoNative_EvpAes128WrapPad(void) +{ + // No error queue impact. + return EVP_aes_128_wrap_pad(); +} + const EVP_CIPHER* CryptoNative_EvpAes192Ecb(void) { // No error queue impact. @@ -311,6 +322,12 @@ const EVP_CIPHER* CryptoNative_EvpAes192Ccm(void) return EVP_aes_192_ccm(); } +const EVP_CIPHER* CryptoNative_EvpAes192WrapPad(void) +{ + // No error queue impact. + return EVP_aes_192_wrap_pad(); +} + const EVP_CIPHER* CryptoNative_EvpAes256Ecb(void) { // No error queue impact. @@ -347,6 +364,12 @@ const EVP_CIPHER* CryptoNative_EvpAes256Ccm(void) return EVP_aes_256_ccm(); } +const EVP_CIPHER* CryptoNative_EvpAes256WrapPad(void) +{ + // No error queue impact. + return EVP_aes_256_wrap_pad(); +} + const EVP_CIPHER* CryptoNative_EvpDesEcb(void) { // No error queue impact. diff --git a/src/native/libs/System.Security.Cryptography.Native/pal_evp_cipher.h b/src/native/libs/System.Security.Cryptography.Native/pal_evp_cipher.h index 0899cea9dbe3af..d585c3a1d5e5ef 100644 --- a/src/native/libs/System.Security.Cryptography.Native/pal_evp_cipher.h +++ b/src/native/libs/System.Security.Cryptography.Native/pal_evp_cipher.h @@ -158,6 +158,14 @@ Direct shim to EVP_aes_128_ccm. */ PALEXPORT const EVP_CIPHER* CryptoNative_EvpAes128Ccm(void); +/* +Function: +EvpAes128WrapPad + +Direct shim to EVP_aes_128_wrap_pad. +*/ +PALEXPORT const EVP_CIPHER* CryptoNative_EvpAes128WrapPad(void); + /* Function: EvpAes192Ecb @@ -206,6 +214,14 @@ Direct shim to EVP_aes_192_ccm. */ PALEXPORT const EVP_CIPHER* CryptoNative_EvpAes192Ccm(void); +/* +Function: +EvpAes192WrapPad + +Direct shim to EVP_aes_192_wrap_pad. +*/ +PALEXPORT const EVP_CIPHER* CryptoNative_EvpAes192WrapPad(void); + /* Function: EvpAes256Ecb @@ -254,6 +270,14 @@ Direct shim to EVP_aes_256_ccm. */ PALEXPORT const EVP_CIPHER* CryptoNative_EvpAes256Ccm(void); +/* +Function: +EvpAes256WrapPad + +Direct shim to EVP_aes_256_wrap_pad. +*/ +PALEXPORT const EVP_CIPHER* CryptoNative_EvpAes256WrapPad(void); + /* Function: EvpDes3Ecb