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