From 0a0fcb49a124354fa71ffbc9d068663163c92bbb Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 18 Feb 2021 15:22:08 -0800 Subject: [PATCH 01/14] Fix up Aes CCM/GCM support partially. Re-enable the length assert. Fixes #48471 This gets 49 more tests passing (split between Ccm and Gcm tests and sadly no full test suites). --- .../pal_evp_cipher.c | 145 +++++++++++++----- .../pal_evp_cipher.h | 2 + .../Security/Cryptography/AesCcm.Unix.cs | 3 +- .../Security/Cryptography/AesGcm.Unix.cs | 2 +- 4 files changed, 107 insertions(+), 45 deletions(-) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c index 9551335f9ec906..9d3da2de508b1a 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. #include "pal_evp_cipher.h" +#include "pal_utilities.h" // just some unique IDs intptr_t CryptoNative_EvpAes128Ecb() { return 1001; } @@ -121,6 +122,20 @@ static bool HasTag(intptr_t type) (type == CryptoNative_EvpAes256Ccm()); } +static bool IsAesCcm(intptr_t type) +{ + return (type == CryptoNative_EvpAes128Ccm()) || + (type == CryptoNative_EvpAes192Ccm()) || + (type == CryptoNative_EvpAes256Ccm()); +} + +static bool IsAesGcm(intptr_t type) +{ + return (type == CryptoNative_EvpAes128Gcm()) || + (type == CryptoNative_EvpAes192Gcm()) || + (type == CryptoNative_EvpAes256Gcm()); +} + CipherCtx* CryptoNative_EvpCipherCreatePartial(intptr_t type) { JNIEnv* env = GetJNIEnv(); @@ -134,40 +149,21 @@ CipherCtx* CryptoNative_EvpCipherCreatePartial(intptr_t type) CipherCtx* ctx = malloc(sizeof(CipherCtx)); ctx->cipher = cipher; ctx->type = type; + ctx->tagLength = TAG_MAX_LENGTH; ctx->ivLength = 0; ctx->encMode = 0; ctx->key = NULL; ctx->iv = NULL; memset(ctx->tag, 0, TAG_MAX_LENGTH); + ctx->dataFromSetTagUpdate = NULL; return CheckJNIExceptions(env) ? FAIL : ctx; } -int32_t CryptoNative_EvpCipherSetKeyAndIV(CipherCtx* ctx, uint8_t* key, uint8_t* iv, int32_t enc) +static int32_t CryptoNative_EvpCipherReinitialize(CipherCtx* ctx) { - if (!ctx) - return FAIL; - - int32_t keyLength = GetAlgorithmWidth(ctx->type); - - // CryptoNative_EvpCipherSetKeyAndIV can be called separately for key and iv - // so we need to wait for both and do Init after. - if (key && !ctx->key) - SaveTo(key, &ctx->key, (size_t)keyLength, /* overwrite */ true); - if (iv && !ctx->iv) - SaveTo(iv, &ctx->iv, (size_t)ctx->ivLength, /* overwrite */ true); - - if (!ctx->key || !ctx->iv) - return SUCCESS; - - // input: 0 for Decrypt, 1 for Encrypt, -1 leave untouched - // Cipher: 2 for Decrypt, 1 for Encrypt, N/A - if (enc != -1) - { - assert(enc == 0 || enc == 1); - ctx->encMode = enc == 0 ? CIPHER_DECRYPT_MODE : CIPHER_ENCRYPT_MODE; - } - JNIEnv* env = GetJNIEnv(); + + int32_t keyLength = GetAlgorithmWidth(ctx->type); // int ivSize = cipher.getBlockSize(); // SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES"); @@ -190,7 +186,7 @@ int32_t CryptoNative_EvpCipherSetKeyAndIV(CipherCtx* ctx, uint8_t* key, uint8_t* jobject ivPsObj; if (HasTag(ctx->type)) - ivPsObj = (*env)->NewObject(env, g_GCMParameterSpecClass, g_GCMParameterSpecCtor, TAG_MAX_LENGTH * 8, ivBytes); + ivPsObj = (*env)->NewObject(env, g_GCMParameterSpecClass, g_GCMParameterSpecCtor, ctx->tagLength * 8, ivBytes); else ivPsObj = (*env)->NewObject(env, g_ivPsClass, g_ivPsCtor, ivBytes); @@ -204,6 +200,34 @@ int32_t CryptoNative_EvpCipherSetKeyAndIV(CipherCtx* ctx, uint8_t* key, uint8_t* return CheckJNIExceptions(env) ? FAIL : SUCCESS; } +int32_t CryptoNative_EvpCipherSetKeyAndIV(CipherCtx* ctx, uint8_t* key, uint8_t* iv, int32_t enc) +{ + if (!ctx) + return FAIL; + + int32_t keyLength = GetAlgorithmWidth(ctx->type); + + // input: 0 for Decrypt, 1 for Encrypt, -1 leave untouched + // Cipher: 2 for Decrypt, 1 for Encrypt, N/A + if (enc != -1) + { + assert(enc == 0 || enc == 1); + ctx->encMode = enc == 0 ? CIPHER_DECRYPT_MODE : CIPHER_ENCRYPT_MODE; + } + + // CryptoNative_EvpCipherSetKeyAndIV can be called separately for key and iv + // so we need to wait for both and do Init after. + if (key) + SaveTo(key, &ctx->key, (size_t)keyLength, /* overwrite */ true); + if (iv) + SaveTo(iv, &ctx->iv, (size_t)ctx->ivLength, /* overwrite */ true); + + if (!ctx->key || !ctx->iv) + return SUCCESS; + + return CryptoNative_EvpCipherReinitialize(ctx); +} + CipherCtx* CryptoNative_EvpCipherCreate2(intptr_t type, uint8_t* key, int32_t keyLength, int32_t effectiveKeyLength, uint8_t* iv, int32_t enc) { if (effectiveKeyLength != 0) @@ -234,7 +258,11 @@ int32_t CryptoNative_EvpCipherUpdate(CipherCtx* ctx, uint8_t* outm, int32_t* out return SUCCESS; if (!in) - return FAIL; + { + // in AES-CCM mode, the input length may be provided in a separate call before the data. + // Since we don't need that information, we can just return success. + return IsAesCcm(ctx->type) ? SUCCESS : FAIL; + } JNIEnv* env = GetJNIEnv(); jbyteArray inDataBytes = (*env)->NewByteArray(env, inl); @@ -247,14 +275,27 @@ int32_t CryptoNative_EvpCipherUpdate(CipherCtx* ctx, uint8_t* outm, int32_t* out } else { + *outl = 0; jbyteArray outDataBytes = (jbyteArray)(*env)->CallObjectMethod(env, ctx->cipher, g_cipherUpdateMethod, inDataBytes); - if (outDataBytes && outm) { + if (outDataBytes && outm) + { jsize outDataBytesLen = (*env)->GetArrayLength(env, outDataBytes); - *outl = (int32_t)outDataBytesLen; + *outl = outDataBytesLen; (*env)->GetByteArrayRegion(env, outDataBytes, 0, outDataBytesLen, (jbyte*) outm); (*env)->DeleteLocalRef(env, outDataBytes); - } else { - *outl = 0; + } + // HACK: + // If the Cipher is AES-CCM, then the managed code won't call into EvpCipherFinalEx in the shim because that's invalid for OpenSSL. + // So, we will call EvpCipherFinalEx here. + if (IsAesCcm(ctx->type) && ctx->encMode == CIPHER_DECRYPT_MODE) + { + int32_t finalOutl; + int32_t result = CryptoNative_EvpCipherFinalEx(ctx, outm + *outl, &finalOutl); + if (!result) + { + return FAIL; + } + outl += finalOutl; } } @@ -269,10 +310,24 @@ int32_t CryptoNative_EvpCipherFinalEx(CipherCtx* ctx, uint8_t* outm, int32_t* ou JNIEnv* env = GetJNIEnv(); + *outl = 0; + + // Since the SetTag operation just uses an update call, we need to copy the data + // that was returned from that call back out to the user. + if (ctx->dataFromSetTagUpdate) + { + jsize dataFromSetTagUpdateLen = (*env)->GetArrayLength(env, ctx->dataFromSetTagUpdate); + (*env)->GetByteArrayRegion(env, ctx->dataFromSetTagUpdate, 0, dataFromSetTagUpdateLen, (jbyte*)outm); + outm += dataFromSetTagUpdateLen; + *outl += dataFromSetTagUpdateLen; + ReleaseGRef(env, ctx->dataFromSetTagUpdate); + ctx->dataFromSetTagUpdate = NULL; + } + // NOTE: Cipher appends TAG to the end of outBytes in case of CCM/GCM and "encryption" mode bool hasTag = HasTag(ctx->type); bool decrypt = ctx->encMode == CIPHER_DECRYPT_MODE; - int tagLength = (hasTag && !decrypt) ? TAG_MAX_LENGTH : 0; + int tagLength = (hasTag && !decrypt) ? ctx->tagLength : 0; jbyteArray outBytes = (jbyteArray)(*env)->CallObjectMethod(env, ctx->cipher, g_cipherDoFinalMethod); if (CheckJNIExceptions(env)) return FAIL; @@ -282,14 +337,10 @@ int32_t CryptoNative_EvpCipherFinalEx(CipherCtx* ctx, uint8_t* outm, int32_t* ou if (outBytesLen > tagLength) { (*env)->GetByteArrayRegion(env, outBytes, 0, outBytesLen - tagLength, (jbyte*) outm); - *outl = outBytesLen - tagLength; + *outl += outBytesLen - tagLength; if (hasTag && !decrypt) - (*env)->GetByteArrayRegion(env, outBytes, outBytesLen - TAG_MAX_LENGTH, TAG_MAX_LENGTH, (jbyte*) ctx->tag); - } - else - { - *outl = 0; + (*env)->GetByteArrayRegion(env, outBytes, outBytesLen - ctx->tagLength, ctx->tagLength, (jbyte*) ctx->tag); } (*env)->DeleteLocalRef(env, outBytes); @@ -360,17 +411,25 @@ int32_t CryptoNative_EvpCipherSetGcmTag(CipherCtx* ctx, uint8_t* tag, int32_t ta if (!ctx) return FAIL; - if (!tag) - return FAIL; - assert(tagLength <= TAG_MAX_LENGTH); + ctx->tagLength = tagLength; + + if (tag == NULL) + { + // A null tag means we should just set the tag length. + return SUCCESS; + } + // Tag is provided using regular "cipher.update(tag)" JNIEnv* env = GetJNIEnv(); jbyteArray inDataBytes = (*env)->NewByteArray(env, tagLength); (*env)->SetByteArrayRegion(env, inDataBytes, 0, tagLength, (jbyte*)tag); jbyteArray outDataBytes = (jbyteArray)(*env)->CallObjectMethod(env, ctx->cipher, g_cipherUpdateMethod, inDataBytes); - (*env)->DeleteLocalRef(env, outDataBytes); + + assert(ctx->dataFromSetTagUpdate == NULL); + ctx->dataFromSetTagUpdate = ToGRef(env, outDataBytes); + (*env)->DeleteLocalRef(env, inDataBytes); return CheckJNIExceptions(env) ? FAIL : SUCCESS; } @@ -384,7 +443,9 @@ void CryptoNative_EvpCipherDestroy(CipherCtx* ctx) { if (ctx) { - ReleaseGRef(GetJNIEnv(), ctx->cipher); + JNIEnv* env = GetJNIEnv(); + ReleaseGRef(env, ctx->cipher); + ReleaseGRef(env, ctx->dataFromSetTagUpdate); free(ctx->key); free(ctx->iv); free(ctx); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.h index 27dbb07da2290f..a234a4ed800487 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.h @@ -17,10 +17,12 @@ typedef struct CipherCtx jobject cipher; intptr_t type; int32_t ivLength; + int32_t tagLength; int32_t encMode; uint8_t* key; uint8_t* iv; uint8_t* tag[TAG_MAX_LENGTH]; + jbyteArray dataFromSetTagUpdate; } CipherCtx; PALEXPORT CipherCtx* CryptoNative_EvpCipherCreate2(intptr_t type, uint8_t* key, int32_t keyLength, int32_t effectiveKeyLength, uint8_t* iv, int32_t enc); 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 03da1e4ad730be..989eef33be48ca 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 @@ -84,9 +84,8 @@ private void DecryptInternal( { Interop.Crypto.CheckValidOpenSslHandle(ctx); Interop.Crypto.EvpCipherSetCcmNonceLength(ctx, nonce.Length); - Interop.Crypto.EvpCipherSetCcmTag(ctx, tag); - Interop.Crypto.EvpCipherSetKeyAndIV(ctx, _key, nonce, Interop.Crypto.EvpCipherDirection.Decrypt); + Interop.Crypto.EvpCipherSetCcmTag(ctx, tag); if (associatedData.Length != 0) { 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 ca45b0be50b1c1..d050604168a0e5 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 @@ -111,7 +111,7 @@ private void DecryptInternal( if (plaintextBytesWritten != plaintext.Length) { - // Debug.Fail($"GCM decrypt wrote {plaintextBytesWritten} of {plaintext.Length} bytes."); + Debug.Fail($"GCM decrypt wrote {plaintextBytesWritten} of {plaintext.Length} bytes."); throw new CryptographicException(); } } From 5509ffacba70025da5614688e6744cc09d70d9d1 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 22 Feb 2021 16:47:29 -0800 Subject: [PATCH 02/14] Use Android-specific classes for AesCcm and AesGcm since OpenSSL's impl has a lot of special cases that we don't need to handle (and handling them makes the code significantly harder to read). --- .../Interop.EVP.Cipher.cs | 268 ++++++++++++++++++ .../pal_evp_cipher.c | 100 ++----- .../pal_evp_cipher.h | 3 - ...em.Security.Cryptography.Algorithms.csproj | 15 +- .../Security/Cryptography/AesCcm.Android.cs | 145 ++++++++++ .../Security/Cryptography/AesGcm.Android.cs | 142 ++++++++++ 6 files changed, 586 insertions(+), 87 deletions(-) create mode 100644 src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.EVP.Cipher.cs create mode 100644 src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesCcm.Android.cs create mode 100644 src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesGcm.Android.cs diff --git a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.EVP.Cipher.cs b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.EVP.Cipher.cs new file mode 100644 index 00000000000000..e191f86a3a6358 --- /dev/null +++ b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.EVP.Cipher.cs @@ -0,0 +1,268 @@ +// 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 Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static partial class Crypto + { + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherCreate2")] + internal static extern SafeEvpCipherCtxHandle EvpCipherCreate( + IntPtr cipher, + ref byte key, + int keyLength, + int effectivekeyLength, + ref byte iv, + int enc); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherCreatePartial")] + internal static extern SafeEvpCipherCtxHandle EvpCipherCreatePartial( + IntPtr cipher); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherSetKeyAndIV")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool EvpCipherSetKeyAndIV( + SafeEvpCipherCtxHandle ctx, + ref byte key, + ref byte iv, + EvpCipherDirection direction); + + internal static void EvpCipherSetKeyAndIV( + SafeEvpCipherCtxHandle ctx, + ReadOnlySpan key, + ReadOnlySpan iv, + EvpCipherDirection direction) + { + if (!EvpCipherSetKeyAndIV( + ctx, + ref MemoryMarshal.GetReference(key), + ref MemoryMarshal.GetReference(iv), + direction)) + { + throw CreateOpenSslCryptographicException(); + } + } + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherSetGcmNonceLength")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool CryptoNative_EvpCipherSetGcmNonceLength( + SafeEvpCipherCtxHandle ctx, int nonceLength); + + internal static void EvpCipherSetGcmNonceLength(SafeEvpCipherCtxHandle ctx, int nonceLength) + { + if (!CryptoNative_EvpCipherSetGcmNonceLength(ctx, nonceLength)) + { + throw CreateOpenSslCryptographicException(); + } + } + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherSetCcmNonceLength")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool CryptoNative_EvpCipherSetCcmNonceLength( + SafeEvpCipherCtxHandle ctx, int nonceLength); + + internal static void EvpCipherSetCcmNonceLength(SafeEvpCipherCtxHandle ctx, int nonceLength) + { + if (!CryptoNative_EvpCipherSetCcmNonceLength(ctx, nonceLength)) + { + throw CreateOpenSslCryptographicException(); + } + } + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherDestroy")] + internal static extern void EvpCipherDestroy(IntPtr ctx); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherReset")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool EvpCipherReset(SafeEvpCipherCtxHandle ctx); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherCtxSetPadding")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool EvpCipherCtxSetPadding(SafeEvpCipherCtxHandle x, int padding); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherUpdate")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool EvpCipherUpdate( + SafeEvpCipherCtxHandle ctx, + ref byte @out, + out int outl, + ref byte @in, + int inl); + + internal static bool EvpCipherUpdate( + SafeEvpCipherCtxHandle ctx, + Span output, + out int bytesWritten, + ReadOnlySpan input) + { + return EvpCipherUpdate( + ctx, + ref MemoryMarshal.GetReference(output), + out bytesWritten, + ref MemoryMarshal.GetReference(input), + input.Length); + } + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherUpdate")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool EvpCipherUpdateAAD( + SafeEvpCipherCtxHandle ctx, + ref byte @in, + int inl); + + internal static bool EvpCipherUpdateAAD( + SafeEvpCipherCtxHandle ctx, + ReadOnlySpan input) + { + return EvpCipherUpdate( + ctx, + ref MemoryMarshal.GetReference(input), + input.Length); + } + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherFinalEx")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool EvpCipherFinalEx( + SafeEvpCipherCtxHandle ctx, + ref byte outm, + out int outl); + + internal static bool EvpCipherFinalEx( + SafeEvpCipherCtxHandle ctx, + Span output, + out int bytesWritten) + { + return EvpCipherFinalEx(ctx, ref MemoryMarshal.GetReference(output), out bytesWritten); + } + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherGetGcmTag")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool EvpCipherGetGcmTag( + SafeEvpCipherCtxHandle ctx, + ref byte tag, + int tagLength); + + internal static void EvpCipherGetGcmTag(SafeEvpCipherCtxHandle ctx, Span tag) + { + if (!EvpCipherGetGcmTag(ctx, ref MemoryMarshal.GetReference(tag), tag.Length)) + { + throw CreateOpenSslCryptographicException(); + } + } + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherGetCcmTag")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool EvpCipherGetCcmTag( + SafeEvpCipherCtxHandle ctx, + ref byte tag, + int tagLength); + + internal static void EvpCipherGetCcmTag(SafeEvpCipherCtxHandle ctx, Span tag) + { + if (!EvpCipherGetCcmTag(ctx, ref MemoryMarshal.GetReference(tag), tag.Length)) + { + throw CreateOpenSslCryptographicException(); + } + } + + internal static void EvpCipherSetCcmTagLength(SafeEvpCipherCtxHandle ctx, int tagLength) + { + ref byte nullRef = ref MemoryMarshal.GetReference(Span.Empty); + if (!EvpCipherSetCcmTag(ctx, ref nullRef, tagLength)) + { + throw CreateOpenSslCryptographicException(); + } + } + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes128Ecb")] + internal static extern IntPtr EvpAes128Ecb(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes128Cbc")] + internal static extern IntPtr EvpAes128Cbc(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes128Gcm")] + internal static extern IntPtr EvpAes128Gcm(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes128Cfb8")] + internal static extern IntPtr EvpAes128Cfb8(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes128Cfb128")] + internal static extern IntPtr EvpAes128Cfb128(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes128Ccm")] + internal static extern IntPtr EvpAes128Ccm(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes192Ecb")] + internal static extern IntPtr EvpAes192Ecb(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes192Cbc")] + internal static extern IntPtr EvpAes192Cbc(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes192Gcm")] + internal static extern IntPtr EvpAes192Gcm(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes192Cfb8")] + internal static extern IntPtr EvpAes192Cfb8(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes192Cfb128")] + internal static extern IntPtr EvpAes192Cfb128(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes192Ccm")] + internal static extern IntPtr EvpAes192Ccm(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes256Ecb")] + internal static extern IntPtr EvpAes256Ecb(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes256Cbc")] + internal static extern IntPtr EvpAes256Cbc(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes256Gcm")] + internal static extern IntPtr EvpAes256Gcm(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes256Cfb128")] + internal static extern IntPtr EvpAes256Cfb128(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes256Cfb8")] + internal static extern IntPtr EvpAes256Cfb8(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes256Ccm")] + internal static extern IntPtr EvpAes256Ccm(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpDesCbc")] + internal static extern IntPtr EvpDesCbc(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpDesEcb")] + internal static extern IntPtr EvpDesEcb(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpDesCfb8")] + internal static extern IntPtr EvpDesCfb8(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpDes3Cbc")] + internal static extern IntPtr EvpDes3Cbc(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpDes3Ecb")] + internal static extern IntPtr EvpDes3Ecb(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpDes3Cfb8")] + internal static extern IntPtr EvpDes3Cfb8(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpDes3Cfb64")] + internal static extern IntPtr EvpDes3Cfb64(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpRC2Cbc")] + internal static extern IntPtr EvpRC2Cbc(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpRC2Ecb")] + internal static extern IntPtr EvpRC2Ecb(); + + internal enum EvpCipherDirection : int + { + NoChange = -1, + Decrypt = 0, + Encrypt = 1, + } + } +} diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c index 9d3da2de508b1a..1b8d8bd2a8d5e6 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c @@ -155,7 +155,6 @@ CipherCtx* CryptoNative_EvpCipherCreatePartial(intptr_t type) ctx->key = NULL; ctx->iv = NULL; memset(ctx->tag, 0, TAG_MAX_LENGTH); - ctx->dataFromSetTagUpdate = NULL; return CheckJNIExceptions(env) ? FAIL : ctx; } @@ -248,6 +247,17 @@ CipherCtx* CryptoNative_EvpCipherCreate2(intptr_t type, uint8_t* key, int32_t ke return ctx; } +int32_t CryptoNative_EvpCipherUpdateAAD(CipherCtx* ctx, uint8_t* in, int32_t inl) +{ + if (!ctx) + return FAIL; + + JNIEnv* env = GetJNIEnv(); + jbyteArray inDataBytes = (*env)->NewByteArray(env, inl); + (*env)->SetByteArrayRegion(env, inDataBytes, 0, inl, (jbyte*)in); + (*env)->CallVoidMethod(env, ctx->cipher, g_cipherUpdateAADMethod, inDataBytes); +} + int32_t CryptoNative_EvpCipherUpdate(CipherCtx* ctx, uint8_t* outm, int32_t* outl, uint8_t* in, int32_t inl) { if (!ctx) @@ -257,46 +267,18 @@ int32_t CryptoNative_EvpCipherUpdate(CipherCtx* ctx, uint8_t* outm, int32_t* out // it means caller wants us to record "inl" but we don't need it. return SUCCESS; - if (!in) - { - // in AES-CCM mode, the input length may be provided in a separate call before the data. - // Since we don't need that information, we can just return success. - return IsAesCcm(ctx->type) ? SUCCESS : FAIL; - } - JNIEnv* env = GetJNIEnv(); jbyteArray inDataBytes = (*env)->NewByteArray(env, inl); (*env)->SetByteArrayRegion(env, inDataBytes, 0, inl, (jbyte*)in); - // it's AAD if outm is null and it's GCM/CCM - if (HasTag(ctx->type) && !outm) - { - (*env)->CallVoidMethod(env, ctx->cipher, g_cipherUpdateAADMethod, inDataBytes); - } - else + *outl = 0; + jbyteArray outDataBytes = (jbyteArray)(*env)->CallObjectMethod(env, ctx->cipher, g_cipherUpdateMethod, inDataBytes); + if (outDataBytes && outm) { - *outl = 0; - jbyteArray outDataBytes = (jbyteArray)(*env)->CallObjectMethod(env, ctx->cipher, g_cipherUpdateMethod, inDataBytes); - if (outDataBytes && outm) - { - jsize outDataBytesLen = (*env)->GetArrayLength(env, outDataBytes); - *outl = outDataBytesLen; - (*env)->GetByteArrayRegion(env, outDataBytes, 0, outDataBytesLen, (jbyte*) outm); - (*env)->DeleteLocalRef(env, outDataBytes); - } - // HACK: - // If the Cipher is AES-CCM, then the managed code won't call into EvpCipherFinalEx in the shim because that's invalid for OpenSSL. - // So, we will call EvpCipherFinalEx here. - if (IsAesCcm(ctx->type) && ctx->encMode == CIPHER_DECRYPT_MODE) - { - int32_t finalOutl; - int32_t result = CryptoNative_EvpCipherFinalEx(ctx, outm + *outl, &finalOutl); - if (!result) - { - return FAIL; - } - outl += finalOutl; - } + jsize outDataBytesLen = (*env)->GetArrayLength(env, outDataBytes); + *outl = outDataBytesLen; + (*env)->GetByteArrayRegion(env, outDataBytes, 0, outDataBytesLen, (jbyte*) outm); + (*env)->DeleteLocalRef(env, outDataBytes); } (*env)->DeleteLocalRef(env, inDataBytes); @@ -312,18 +294,6 @@ int32_t CryptoNative_EvpCipherFinalEx(CipherCtx* ctx, uint8_t* outm, int32_t* ou *outl = 0; - // Since the SetTag operation just uses an update call, we need to copy the data - // that was returned from that call back out to the user. - if (ctx->dataFromSetTagUpdate) - { - jsize dataFromSetTagUpdateLen = (*env)->GetArrayLength(env, ctx->dataFromSetTagUpdate); - (*env)->GetByteArrayRegion(env, ctx->dataFromSetTagUpdate, 0, dataFromSetTagUpdateLen, (jbyte*)outm); - outm += dataFromSetTagUpdateLen; - *outl += dataFromSetTagUpdateLen; - ReleaseGRef(env, ctx->dataFromSetTagUpdate); - ctx->dataFromSetTagUpdate = NULL; - } - // NOTE: Cipher appends TAG to the end of outBytes in case of CCM/GCM and "encryption" mode bool hasTag = HasTag(ctx->type); bool decrypt = ctx->encMode == CIPHER_DECRYPT_MODE; @@ -406,46 +376,12 @@ int32_t CryptoNative_EvpCipherGetCcmTag(CipherCtx* ctx, uint8_t* tag, int32_t ta return CryptoNative_EvpCipherGetGcmTag(ctx, tag, tagLength); } -int32_t CryptoNative_EvpCipherSetGcmTag(CipherCtx* ctx, uint8_t* tag, int32_t tagLength) -{ - if (!ctx) - return FAIL; - - assert(tagLength <= TAG_MAX_LENGTH); - - ctx->tagLength = tagLength; - - if (tag == NULL) - { - // A null tag means we should just set the tag length. - return SUCCESS; - } - - // Tag is provided using regular "cipher.update(tag)" - JNIEnv* env = GetJNIEnv(); - jbyteArray inDataBytes = (*env)->NewByteArray(env, tagLength); - (*env)->SetByteArrayRegion(env, inDataBytes, 0, tagLength, (jbyte*)tag); - jbyteArray outDataBytes = (jbyteArray)(*env)->CallObjectMethod(env, ctx->cipher, g_cipherUpdateMethod, inDataBytes); - - assert(ctx->dataFromSetTagUpdate == NULL); - ctx->dataFromSetTagUpdate = ToGRef(env, outDataBytes); - - (*env)->DeleteLocalRef(env, inDataBytes); - return CheckJNIExceptions(env) ? FAIL : SUCCESS; -} - -int32_t CryptoNative_EvpCipherSetCcmTag(CipherCtx* ctx, uint8_t* tag, int32_t tagLength) -{ - return CryptoNative_EvpCipherSetGcmTag(ctx, tag, tagLength); -} - void CryptoNative_EvpCipherDestroy(CipherCtx* ctx) { if (ctx) { JNIEnv* env = GetJNIEnv(); ReleaseGRef(env, ctx->cipher); - ReleaseGRef(env, ctx->dataFromSetTagUpdate); free(ctx->key); free(ctx->iv); free(ctx); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.h index a234a4ed800487..66dc64afe58578 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.h @@ -22,7 +22,6 @@ typedef struct CipherCtx uint8_t* key; uint8_t* iv; uint8_t* tag[TAG_MAX_LENGTH]; - jbyteArray dataFromSetTagUpdate; } CipherCtx; PALEXPORT CipherCtx* CryptoNative_EvpCipherCreate2(intptr_t type, uint8_t* key, int32_t keyLength, int32_t effectiveKeyLength, uint8_t* iv, int32_t enc); @@ -36,9 +35,7 @@ PALEXPORT int32_t CryptoNative_EvpCipherCtxSetPadding(CipherCtx* ctx, int32_t pa PALEXPORT int32_t CryptoNative_EvpCipherUpdate(CipherCtx* ctx, uint8_t* out, int32_t* outl, uint8_t* in, int32_t inl); PALEXPORT int32_t CryptoNative_EvpCipherFinalEx(CipherCtx* ctx, uint8_t* outm, int32_t* outl); PALEXPORT int32_t CryptoNative_EvpCipherGetGcmTag(CipherCtx* ctx, uint8_t* tag, int32_t tagLength); -PALEXPORT int32_t CryptoNative_EvpCipherSetGcmTag(CipherCtx* ctx, uint8_t* tag, int32_t tagLength); PALEXPORT int32_t CryptoNative_EvpCipherGetCcmTag(CipherCtx* ctx, uint8_t* tag, int32_t tagLength); -PALEXPORT int32_t CryptoNative_EvpCipherSetCcmTag(CipherCtx* ctx, uint8_t* tag, int32_t tagLength); PALEXPORT intptr_t CryptoNative_EvpAes128Ecb(void); PALEXPORT intptr_t CryptoNative_EvpAes128Cbc(void); PALEXPORT intptr_t CryptoNative_EvpAes128Cfb8(void); 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 4e6ab6f54dddce..7bc479a79130c5 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 @@ -11,6 +11,9 @@ SR.SystemSecurityCryptographyAlgorithms_PlatformNotSupported ExcludeApiList.PNSE.Browser.txt + + true @@ -583,8 +586,6 @@ - + + + + + + + + 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 new file mode 100644 index 00000000000000..47f919b90f0b6f --- /dev/null +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesCcm.Android.cs @@ -0,0 +1,145 @@ +// 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.Diagnostics.CodeAnalysis; +using Microsoft.Win32.SafeHandles; + +namespace System.Security.Cryptography +{ + public sealed partial class AesCcm + { + private byte[] _key; + + [MemberNotNull(nameof(_key))] + private void ImportKey(ReadOnlySpan key) + { + // OpenSSL does not allow setting nonce length after setting the key + // we need to store it as bytes instead + _key = key.ToArray(); + } + + private void EncryptInternal( + ReadOnlySpan nonce, + ReadOnlySpan plaintext, + Span ciphertext, + Span tag, + ReadOnlySpan associatedData = default) + { + using (SafeEvpCipherCtxHandle ctx = Interop.Crypto.EvpCipherCreatePartial(GetCipher(_key.Length * 8))) + { + Interop.Crypto.CheckValidOpenSslHandle(ctx); + + // We need to set mode to encryption before setting the tag and nonce length + // otherwise older versions of OpenSSL (i.e. 1.0.1f which can be found on Ubuntu 14.04) will fail + Interop.Crypto.EvpCipherSetKeyAndIV(ctx, Span.Empty, Span.Empty, Interop.Crypto.EvpCipherDirection.Encrypt); + Interop.Crypto.EvpCipherSetCcmTagLength(ctx, tag.Length); + Interop.Crypto.EvpCipherSetCcmNonceLength(ctx, nonce.Length); + Interop.Crypto.EvpCipherSetKeyAndIV(ctx, _key, nonce, Interop.Crypto.EvpCipherDirection.NoChange); + + if (associatedData.Length != 0) + { + if (!Interop.Crypto.EvpCipherUpdateAAD(ctx, associatedData)) + { + throw Interop.Crypto.CreateOpenSslCryptographicException(); + } + } + + if (!Interop.Crypto.EvpCipherUpdate(ctx, ciphertext, out int ciphertextBytesWritten, plaintext)) + { + throw Interop.Crypto.CreateOpenSslCryptographicException(); + } + + if (!Interop.Crypto.EvpCipherFinalEx( + ctx, + ciphertext.Slice(ciphertextBytesWritten), + out int bytesWritten)) + { + throw Interop.Crypto.CreateOpenSslCryptographicException(); + } + + ciphertextBytesWritten += bytesWritten; + + if (ciphertextBytesWritten != ciphertext.Length) + { + Debug.Fail($"CCM encrypt wrote {ciphertextBytesWritten} of {ciphertext.Length} bytes."); + throw new CryptographicException(); + } + + Interop.Crypto.EvpCipherGetCcmTag(ctx, tag); + } + } + + private void DecryptInternal( + ReadOnlySpan nonce, + ReadOnlySpan ciphertext, + ReadOnlySpan tag, + Span plaintext, + ReadOnlySpan associatedData) + { + using (SafeEvpCipherCtxHandle ctx = Interop.Crypto.EvpCipherCreatePartial(GetCipher(_key.Length * 8))) + { + Interop.Crypto.CheckValidOpenSslHandle(ctx); + Interop.Crypto.EvpCipherSetCcmNonceLength(ctx, nonce.Length); + Interop.Crypto.EvpCipherSetKeyAndIV(ctx, _key, nonce, Interop.Crypto.EvpCipherDirection.Decrypt); + + if (associatedData.Length != 0) + { + if (!Interop.Crypto.EvpCipherUpdate(ctx, Span.Empty, out _, associatedData)) + { + throw Interop.Crypto.CreateOpenSslCryptographicException(); + } + } + + if (!Interop.Crypto.EvpCipherUpdate(ctx, plaintext, out int plaintextBytesWritten, ciphertext)) + { + plaintext.Clear(); + throw new CryptographicException(SR.Cryptography_AuthTagMismatch); + } + + if (!Interop.Crypto.EvpCipherUpdate(_ctxHandle, plaintext.Slice(plaintextBytesWritten), out int bytesWritten, tag)) + { + plaintext.Clear(); + throw Interop.Crypto.CreateOpensslCryptographicException(); + } + + plaintextBytesWritten += bytesWritten; + + if (!Interop.Crypto.EvpCipherFinalEx( + _ctxHandle, + plaintext.Slice(plaintextBytesWritten), + out bytesWritten)) + { + CryptographicOperations.ZeroMemory(plaintext); + throw new CryptographicException(SR.Cryptography_AuthTagMismatch); + } + + plaintextBytesWritten += bytesWritten; + + if (plaintextBytesWritten != plaintext.Length) + { + Debug.Fail($"CCM decrypt wrote {plaintextBytesWritten} of {plaintext.Length} bytes."); + throw new CryptographicException(); + } + } + } + + private static IntPtr GetCipher(int keySizeInBits) + { + switch (keySizeInBits) + { + case 128: return Interop.Crypto.EvpAes128Ccm(); + case 192: return Interop.Crypto.EvpAes192Ccm(); + case 256: return Interop.Crypto.EvpAes256Ccm(); + default: + Debug.Fail("Key size should already be validated"); + return IntPtr.Zero; + } + } + + public void Dispose() + { + CryptographicOperations.ZeroMemory(_key); + } + } +} 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 new file mode 100644 index 00000000000000..d7557f5a2e78f3 --- /dev/null +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AesGcm.Android.cs @@ -0,0 +1,142 @@ +// 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.Diagnostics.CodeAnalysis; +using Microsoft.Win32.SafeHandles; + +namespace System.Security.Cryptography +{ + public sealed partial class AesGcm + { + private SafeEvpCipherCtxHandle _ctxHandle; + + [MemberNotNull(nameof(_ctxHandle))] + private void ImportKey(ReadOnlySpan key) + { + _ctxHandle = Interop.Crypto.EvpCipherCreatePartial(GetCipher(key.Length * 8)); + + Interop.Crypto.CheckValidOpenSslHandle(_ctxHandle); + Interop.Crypto.EvpCipherSetKeyAndIV( + _ctxHandle, + key, + Span.Empty, + Interop.Crypto.EvpCipherDirection.NoChange); + Interop.Crypto.EvpCipherSetGcmNonceLength(_ctxHandle, NonceSize); + } + + private void EncryptInternal( + ReadOnlySpan nonce, + ReadOnlySpan plaintext, + Span ciphertext, + Span tag, + ReadOnlySpan associatedData = default) + { + Interop.Crypto.EvpCipherSetKeyAndIV( + _ctxHandle, + Span.Empty, + nonce, + Interop.Crypto.EvpCipherDirection.Encrypt); + + if (associatedData.Length != 0) + { + if (!Interop.Crypto.EvpCipherUpdate(_ctxHandle, Span.Empty, out _, associatedData)) + { + throw Interop.Crypto.CreateOpenSslCryptographicException(); + } + } + + if (!Interop.Crypto.EvpCipherUpdate(_ctxHandle, ciphertext, out int ciphertextBytesWritten, plaintext)) + { + throw Interop.Crypto.CreateOpenSslCryptographicException(); + } + + if (!Interop.Crypto.EvpCipherFinalEx( + _ctxHandle, + ciphertext.Slice(ciphertextBytesWritten), + out int bytesWritten)) + { + throw Interop.Crypto.CreateOpenSslCryptographicException(); + } + + ciphertextBytesWritten += bytesWritten; + + if (ciphertextBytesWritten != ciphertext.Length) + { + Debug.Fail($"GCM encrypt wrote {ciphertextBytesWritten} of {ciphertext.Length} bytes."); + throw new CryptographicException(); + } + + Interop.Crypto.EvpCipherGetGcmTag(_ctxHandle, tag); + } + + private void DecryptInternal( + ReadOnlySpan nonce, + ReadOnlySpan ciphertext, + ReadOnlySpan tag, + Span plaintext, + ReadOnlySpan associatedData) + { + Interop.Crypto.EvpCipherSetKeyAndIV( + _ctxHandle, + ReadOnlySpan.Empty, + nonce, + Interop.Crypto.EvpCipherDirection.Decrypt); + + if (associatedData.Length != 0) + { + if (!Interop.Crypto.EvpCipherUpdate(_ctxHandle, associatedData)) + { + throw Interop.Crypto.CreateOpenSslCryptographicException(); + } + } + + if (!Interop.Crypto.EvpCipherUpdate(_ctxHandle, plaintext, out int plaintextBytesWritten, ciphertext)) + { + throw Interop.Crypto.CreateOpenSslCryptographicException(); + } + + if (!Interop.Crypto.EvpCipherUpdate(_ctxHandle, plaintext.Slice(plaintextBytesWritten), out int bytesWritten, tag)) + { + throw Interop.Crypto.CreateOpensslCryptographicException(); + } + + plaintextBytesWritten += bytesWritten; + + if (!Interop.Crypto.EvpCipherFinalEx( + _ctxHandle, + plaintext.Slice(plaintextBytesWritten), + out bytesWritten)) + { + CryptographicOperations.ZeroMemory(plaintext); + throw new CryptographicException(SR.Cryptography_AuthTagMismatch); + } + + plaintextBytesWritten += bytesWritten; + + if (plaintextBytesWritten != plaintext.Length) + { + Debug.Fail($"GCM decrypt wrote {plaintextBytesWritten} of {plaintext.Length} bytes."); + throw new CryptographicException(); + } + } + + private static IntPtr GetCipher(int keySizeInBits) + { + switch (keySizeInBits) + { + case 128: return Interop.Crypto.EvpAes128Gcm(); + case 192: return Interop.Crypto.EvpAes192Gcm(); + case 256: return Interop.Crypto.EvpAes256Gcm(); + default: + Debug.Fail("Key size should already be validated"); + return IntPtr.Zero; + } + } + + public void Dispose() + { + _ctxHandle.Dispose(); + } + } +} From dc5d1f35608f0dd709f68f35fd46173b6021f4dd Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 22 Feb 2021 18:09:44 -0800 Subject: [PATCH 03/14] In progress work to refactor AES impls for clarity. --- .../Interop.EVP.Cipher.cs | 17 +++++++---------- .../pal_evp_cipher.c | 14 ++++++++++++++ .../pal_evp_cipher.h | 2 ++ .../Security/Cryptography/AesCcm.Android.cs | 15 ++++++++++----- .../Security/Cryptography/AesGcm.Android.cs | 6 +++--- 5 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.EVP.Cipher.cs b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.EVP.Cipher.cs index e191f86a3a6358..ded6eb849ccb1a 100644 --- a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.EVP.Cipher.cs +++ b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.EVP.Cipher.cs @@ -106,7 +106,7 @@ ref MemoryMarshal.GetReference(input), input.Length); } - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherUpdate")] + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherUpdateAAD")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool EvpCipherUpdateAAD( SafeEvpCipherCtxHandle ctx, @@ -117,7 +117,7 @@ internal static bool EvpCipherUpdateAAD( SafeEvpCipherCtxHandle ctx, ReadOnlySpan input) { - return EvpCipherUpdate( + return EvpCipherUpdateAAD( ctx, ref MemoryMarshal.GetReference(input), input.Length); @@ -168,14 +168,11 @@ internal static void EvpCipherGetCcmTag(SafeEvpCipherCtxHandle ctx, Span t } } - internal static void EvpCipherSetCcmTagLength(SafeEvpCipherCtxHandle ctx, int tagLength) - { - ref byte nullRef = ref MemoryMarshal.GetReference(Span.Empty); - if (!EvpCipherSetCcmTag(ctx, ref nullRef, tagLength)) - { - throw CreateOpenSslCryptographicException(); - } - } + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherSetCcmTagLength")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool EvpCipherSetCcmTagLength( + SafeEvpCipherCtxHandle ctx, + int tagLength); [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes128Ecb")] internal static extern IntPtr EvpAes128Ecb(); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c index 1b8d8bd2a8d5e6..0f7b5a94916b77 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c @@ -158,6 +158,18 @@ CipherCtx* CryptoNative_EvpCipherCreatePartial(intptr_t type) return CheckJNIExceptions(env) ? FAIL : ctx; } +int32_t CryptoNative_EvpCipherSetCcmTagLength(CipherCtx* ctx, int32_t tagLength) +{ + if (!ctx) + return FAIL; + + if(tagLength > TAG_MAX_LENGTH) + return FAIL; + + ctx->tagLength = tagLength; + return SUCCESS; +} + static int32_t CryptoNative_EvpCipherReinitialize(CipherCtx* ctx) { JNIEnv* env = GetJNIEnv(); @@ -256,6 +268,8 @@ int32_t CryptoNative_EvpCipherUpdateAAD(CipherCtx* ctx, uint8_t* in, int32_t inl jbyteArray inDataBytes = (*env)->NewByteArray(env, inl); (*env)->SetByteArrayRegion(env, inDataBytes, 0, inl, (jbyte*)in); (*env)->CallVoidMethod(env, ctx->cipher, g_cipherUpdateAADMethod, inDataBytes); + (*env)->DeleteLocalRef(env, inDataBytes); + return CheckJNIExceptions(env) ? FAIL : SUCCESS; } int32_t CryptoNative_EvpCipherUpdate(CipherCtx* ctx, uint8_t* outm, int32_t* outl, uint8_t* in, int32_t inl) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.h index 66dc64afe58578..548147f36fe3b8 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.h @@ -26,12 +26,14 @@ typedef struct CipherCtx PALEXPORT CipherCtx* CryptoNative_EvpCipherCreate2(intptr_t type, uint8_t* key, int32_t keyLength, int32_t effectiveKeyLength, uint8_t* iv, int32_t enc); PALEXPORT CipherCtx* CryptoNative_EvpCipherCreatePartial(intptr_t type); +PALEXPORT int32_t CryptoNative_EvpCipherSetCcmTagLength(CipherCtx* ctx, int32_t tagLength); PALEXPORT int32_t CryptoNative_EvpCipherSetKeyAndIV(CipherCtx* ctx, uint8_t* key, uint8_t* iv, int32_t enc); PALEXPORT int32_t CryptoNative_EvpCipherSetGcmNonceLength(CipherCtx* ctx, int32_t ivLength); PALEXPORT int32_t CryptoNative_EvpCipherSetCcmNonceLength(CipherCtx* ctx, int32_t ivLength); PALEXPORT void CryptoNative_EvpCipherDestroy(CipherCtx* ctx); PALEXPORT int32_t CryptoNative_EvpCipherReset(CipherCtx* ctx); PALEXPORT int32_t CryptoNative_EvpCipherCtxSetPadding(CipherCtx* ctx, int32_t padding); +PALEXPORT int32_t CryptoNative_EvpCipherUpdateAAD(CipherCtx* ctx, uint8_t* in, int32_t inl); PALEXPORT int32_t CryptoNative_EvpCipherUpdate(CipherCtx* ctx, uint8_t* out, int32_t* outl, uint8_t* in, int32_t inl); PALEXPORT int32_t CryptoNative_EvpCipherFinalEx(CipherCtx* ctx, uint8_t* outm, int32_t* outl); PALEXPORT int32_t CryptoNative_EvpCipherGetGcmTag(CipherCtx* ctx, uint8_t* tag, int32_t tagLength); 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 47f919b90f0b6f..207910308557dd 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 @@ -33,7 +33,12 @@ private void EncryptInternal( // We need to set mode to encryption before setting the tag and nonce length // otherwise older versions of OpenSSL (i.e. 1.0.1f which can be found on Ubuntu 14.04) will fail Interop.Crypto.EvpCipherSetKeyAndIV(ctx, Span.Empty, Span.Empty, Interop.Crypto.EvpCipherDirection.Encrypt); - Interop.Crypto.EvpCipherSetCcmTagLength(ctx, tag.Length); + + if (!Interop.Crypto.EvpCipherSetCcmTagLength(ctx, tag.Length)) + { + throw Interop.Crypto.CreateOpenSslCryptographicException(); + } + Interop.Crypto.EvpCipherSetCcmNonceLength(ctx, nonce.Length); Interop.Crypto.EvpCipherSetKeyAndIV(ctx, _key, nonce, Interop.Crypto.EvpCipherDirection.NoChange); @@ -96,17 +101,17 @@ private void DecryptInternal( plaintext.Clear(); throw new CryptographicException(SR.Cryptography_AuthTagMismatch); } - - if (!Interop.Crypto.EvpCipherUpdate(_ctxHandle, plaintext.Slice(plaintextBytesWritten), out int bytesWritten, tag)) + + if (!Interop.Crypto.EvpCipherUpdate(ctx, plaintext.Slice(plaintextBytesWritten), out int bytesWritten, tag)) { plaintext.Clear(); - throw Interop.Crypto.CreateOpensslCryptographicException(); + throw Interop.Crypto.CreateOpenSslCryptographicException(); } plaintextBytesWritten += bytesWritten; if (!Interop.Crypto.EvpCipherFinalEx( - _ctxHandle, + ctx, plaintext.Slice(plaintextBytesWritten), out bytesWritten)) { 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 d7557f5a2e78f3..1ab646c518180b 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 @@ -40,7 +40,7 @@ private void EncryptInternal( if (associatedData.Length != 0) { - if (!Interop.Crypto.EvpCipherUpdate(_ctxHandle, Span.Empty, out _, associatedData)) + if (!Interop.Crypto.EvpCipherUpdateAAD(_ctxHandle, associatedData)) { throw Interop.Crypto.CreateOpenSslCryptographicException(); } @@ -85,7 +85,7 @@ private void DecryptInternal( if (associatedData.Length != 0) { - if (!Interop.Crypto.EvpCipherUpdate(_ctxHandle, associatedData)) + if (!Interop.Crypto.EvpCipherUpdateAAD(_ctxHandle, associatedData)) { throw Interop.Crypto.CreateOpenSslCryptographicException(); } @@ -98,7 +98,7 @@ private void DecryptInternal( if (!Interop.Crypto.EvpCipherUpdate(_ctxHandle, plaintext.Slice(plaintextBytesWritten), out int bytesWritten, tag)) { - throw Interop.Crypto.CreateOpensslCryptographicException(); + throw Interop.Crypto.CreateOpenSslCryptographicException(); } plaintextBytesWritten += bytesWritten; From 1f6130116d030e90f1ca56d79561a41cac301d95 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 23 Feb 2021 10:55:04 -0800 Subject: [PATCH 04/14] Get custom tag length tests to pass. --- .../Interop.EVP.Cipher.cs | 4 ++-- .../pal_evp_cipher.c | 4 +++- .../pal_evp_cipher.h | 2 +- .../src/System/Security/Cryptography/AesCcm.Android.cs | 8 ++------ .../src/System/Security/Cryptography/AesGcm.Android.cs | 6 ++++++ 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.EVP.Cipher.cs b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.EVP.Cipher.cs index ded6eb849ccb1a..d4ad9e9b26d330 100644 --- a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.EVP.Cipher.cs +++ b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.EVP.Cipher.cs @@ -168,9 +168,9 @@ internal static void EvpCipherGetCcmTag(SafeEvpCipherCtxHandle ctx, Span t } } - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherSetCcmTagLength")] + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherSetTagLength")] [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool EvpCipherSetCcmTagLength( + internal static extern bool EvpCipherSetTagLength( SafeEvpCipherCtxHandle ctx, int tagLength); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c index 0f7b5a94916b77..2d8f32b287ed86 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c @@ -158,7 +158,7 @@ CipherCtx* CryptoNative_EvpCipherCreatePartial(intptr_t type) return CheckJNIExceptions(env) ? FAIL : ctx; } -int32_t CryptoNative_EvpCipherSetCcmTagLength(CipherCtx* ctx, int32_t tagLength) +int32_t CryptoNative_EvpCipherSetTagLength(CipherCtx* ctx, int32_t tagLength) { if (!ctx) return FAIL; @@ -324,7 +324,9 @@ int32_t CryptoNative_EvpCipherFinalEx(CipherCtx* ctx, uint8_t* outm, int32_t* ou *outl += outBytesLen - tagLength; if (hasTag && !decrypt) + { (*env)->GetByteArrayRegion(env, outBytes, outBytesLen - ctx->tagLength, ctx->tagLength, (jbyte*) ctx->tag); + } } (*env)->DeleteLocalRef(env, outBytes); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.h index 548147f36fe3b8..b177890c7d0f5b 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.h @@ -26,7 +26,7 @@ typedef struct CipherCtx PALEXPORT CipherCtx* CryptoNative_EvpCipherCreate2(intptr_t type, uint8_t* key, int32_t keyLength, int32_t effectiveKeyLength, uint8_t* iv, int32_t enc); PALEXPORT CipherCtx* CryptoNative_EvpCipherCreatePartial(intptr_t type); -PALEXPORT int32_t CryptoNative_EvpCipherSetCcmTagLength(CipherCtx* ctx, int32_t tagLength); +PALEXPORT int32_t CryptoNative_EvpCipherSetTagLength(CipherCtx* ctx, int32_t tagLength); PALEXPORT int32_t CryptoNative_EvpCipherSetKeyAndIV(CipherCtx* ctx, uint8_t* key, uint8_t* iv, int32_t enc); PALEXPORT int32_t CryptoNative_EvpCipherSetGcmNonceLength(CipherCtx* ctx, int32_t ivLength); PALEXPORT int32_t CryptoNative_EvpCipherSetCcmNonceLength(CipherCtx* ctx, int32_t ivLength); 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 207910308557dd..8e1a35344934bf 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 @@ -30,17 +30,13 @@ private void EncryptInternal( { Interop.Crypto.CheckValidOpenSslHandle(ctx); - // We need to set mode to encryption before setting the tag and nonce length - // otherwise older versions of OpenSSL (i.e. 1.0.1f which can be found on Ubuntu 14.04) will fail - Interop.Crypto.EvpCipherSetKeyAndIV(ctx, Span.Empty, Span.Empty, Interop.Crypto.EvpCipherDirection.Encrypt); - - if (!Interop.Crypto.EvpCipherSetCcmTagLength(ctx, tag.Length)) + if (!Interop.Crypto.EvpCipherSetTagLength(ctx, tag.Length)) { throw Interop.Crypto.CreateOpenSslCryptographicException(); } Interop.Crypto.EvpCipherSetCcmNonceLength(ctx, nonce.Length); - Interop.Crypto.EvpCipherSetKeyAndIV(ctx, _key, nonce, Interop.Crypto.EvpCipherDirection.NoChange); + Interop.Crypto.EvpCipherSetKeyAndIV(ctx, _key, nonce, Interop.Crypto.EvpCipherDirection.Encrypt); if (associatedData.Length != 0) { 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 1ab646c518180b..cf267cc97612a5 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 @@ -32,6 +32,12 @@ private void EncryptInternal( Span tag, ReadOnlySpan associatedData = default) { + + if (!Interop.Crypto.EvpCipherSetTagLength(_ctxHandle, tag.Length)) + { + throw Interop.Crypto.CreateOpenSslCryptographicException(); + } + Interop.Crypto.EvpCipherSetKeyAndIV( _ctxHandle, Span.Empty, From e4eebfd15f97b90ba0bea957044aba16e8441916 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 24 Feb 2021 10:09:39 -0800 Subject: [PATCH 05/14] Implement EvpCipherReset. --- .../pal_evp_cipher.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c index 2d8f32b287ed86..9d009970d1c263 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c @@ -352,13 +352,22 @@ int32_t CryptoNative_EvpCipherCtxSetPadding(CipherCtx* ctx, int32_t padding) int32_t CryptoNative_EvpCipherReset(CipherCtx* ctx) { - // TODO: re-init ctx->cipher ? - LOG_ERROR("EvpCipherReset is no-op."); - if (!ctx) return FAIL; + + free(ctx->iv); + ctx->iv = NULL; + ctx->ivLength = 0; + + JNIEnv* env = GetJNIEnv(); + ReleaseGRef(env, ctx->cipher); + jobject algName = GetAlgorithmName(env, ctx->type); + if (!algName) + return FAIL; - return SUCCESS; + ctx->cipher = ToGRef(env, (*env)->CallStaticObjectMethod(env, g_cipherClass, g_cipherGetInstanceMethod, algName)); + (*env)->DeleteLocalRef(env, algName); + return CheckJNIExceptions(env) ? FAIL : SUCCESS; } int32_t CryptoNative_EvpCipherSetGcmNonceLength(CipherCtx* ctx, int32_t ivLength) From 93d2f0e6f2687baa79c599f95046a24f282f7c83 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 24 Feb 2021 11:13:02 -0800 Subject: [PATCH 06/14] Handle how Java appends the tag from the managed side instead of in the shim. This fixes cases where the ciphertext returned from doFinal is either part or all of the tag (and no ciphertext). --- .../Interop.EVP.Cipher.cs | 30 ---------- .../pal_evp_cipher.c | 36 +---------- .../pal_evp_cipher.h | 3 - .../Security/Cryptography/AesCcm.Android.cs | 59 ++++++++++++++----- .../Security/Cryptography/AesGcm.Android.cs | 59 ++++++++++++++----- 5 files changed, 88 insertions(+), 99 deletions(-) diff --git a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.EVP.Cipher.cs b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.EVP.Cipher.cs index d4ad9e9b26d330..abdd15961698f3 100644 --- a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.EVP.Cipher.cs +++ b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.EVP.Cipher.cs @@ -138,36 +138,6 @@ internal static bool EvpCipherFinalEx( return EvpCipherFinalEx(ctx, ref MemoryMarshal.GetReference(output), out bytesWritten); } - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherGetGcmTag")] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool EvpCipherGetGcmTag( - SafeEvpCipherCtxHandle ctx, - ref byte tag, - int tagLength); - - internal static void EvpCipherGetGcmTag(SafeEvpCipherCtxHandle ctx, Span tag) - { - if (!EvpCipherGetGcmTag(ctx, ref MemoryMarshal.GetReference(tag), tag.Length)) - { - throw CreateOpenSslCryptographicException(); - } - } - - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherGetCcmTag")] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool EvpCipherGetCcmTag( - SafeEvpCipherCtxHandle ctx, - ref byte tag, - int tagLength); - - internal static void EvpCipherGetCcmTag(SafeEvpCipherCtxHandle ctx, Span tag) - { - if (!EvpCipherGetCcmTag(ctx, ref MemoryMarshal.GetReference(tag), tag.Length)) - { - throw CreateOpenSslCryptographicException(); - } - } - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherSetTagLength")] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool EvpCipherSetTagLength( diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c index 9d009970d1c263..664b228aec0a05 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c @@ -154,7 +154,6 @@ CipherCtx* CryptoNative_EvpCipherCreatePartial(intptr_t type) ctx->encMode = 0; ctx->key = NULL; ctx->iv = NULL; - memset(ctx->tag, 0, TAG_MAX_LENGTH); return CheckJNIExceptions(env) ? FAIL : ctx; } @@ -308,26 +307,12 @@ int32_t CryptoNative_EvpCipherFinalEx(CipherCtx* ctx, uint8_t* outm, int32_t* ou *outl = 0; - // NOTE: Cipher appends TAG to the end of outBytes in case of CCM/GCM and "encryption" mode - bool hasTag = HasTag(ctx->type); - bool decrypt = ctx->encMode == CIPHER_DECRYPT_MODE; - int tagLength = (hasTag && !decrypt) ? ctx->tagLength : 0; - jbyteArray outBytes = (jbyteArray)(*env)->CallObjectMethod(env, ctx->cipher, g_cipherDoFinalMethod); if (CheckJNIExceptions(env)) return FAIL; jsize outBytesLen = (*env)->GetArrayLength(env, outBytes); - - if (outBytesLen > tagLength) - { - (*env)->GetByteArrayRegion(env, outBytes, 0, outBytesLen - tagLength, (jbyte*) outm); - *outl += outBytesLen - tagLength; - - if (hasTag && !decrypt) - { - (*env)->GetByteArrayRegion(env, outBytes, outBytesLen - ctx->tagLength, ctx->tagLength, (jbyte*) ctx->tag); - } - } + *outl = outBytesLen; + (*env)->GetByteArrayRegion(env, outBytes, 0, outBytesLen, (jbyte*) outm); (*env)->DeleteLocalRef(env, outBytes); return CheckJNIExceptions(env) ? FAIL : SUCCESS; @@ -384,23 +369,6 @@ int32_t CryptoNative_EvpCipherSetCcmNonceLength(CipherCtx* ctx, int32_t ivLength return CryptoNative_EvpCipherSetGcmNonceLength(ctx, ivLength); } -int32_t CryptoNative_EvpCipherGetGcmTag(CipherCtx* ctx, uint8_t* tag, int32_t tagLength) -{ - if (!ctx) - return FAIL; - - assert(tagLength <= TAG_MAX_LENGTH); - - // Just return what we extracted during CryptoNative_EvpCipherFinalEx - memcpy(tag, ctx->tag, (size_t)tagLength); - return SUCCESS; -} - -int32_t CryptoNative_EvpCipherGetCcmTag(CipherCtx* ctx, uint8_t* tag, int32_t tagLength) -{ - return CryptoNative_EvpCipherGetGcmTag(ctx, tag, tagLength); -} - void CryptoNative_EvpCipherDestroy(CipherCtx* ctx) { if (ctx) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.h index b177890c7d0f5b..fe24dc56141615 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.h @@ -21,7 +21,6 @@ typedef struct CipherCtx int32_t encMode; uint8_t* key; uint8_t* iv; - uint8_t* tag[TAG_MAX_LENGTH]; } CipherCtx; PALEXPORT CipherCtx* CryptoNative_EvpCipherCreate2(intptr_t type, uint8_t* key, int32_t keyLength, int32_t effectiveKeyLength, uint8_t* iv, int32_t enc); @@ -36,8 +35,6 @@ PALEXPORT int32_t CryptoNative_EvpCipherCtxSetPadding(CipherCtx* ctx, int32_t pa PALEXPORT int32_t CryptoNative_EvpCipherUpdateAAD(CipherCtx* ctx, uint8_t* in, int32_t inl); PALEXPORT int32_t CryptoNative_EvpCipherUpdate(CipherCtx* ctx, uint8_t* out, int32_t* outl, uint8_t* in, int32_t inl); PALEXPORT int32_t CryptoNative_EvpCipherFinalEx(CipherCtx* ctx, uint8_t* outm, int32_t* outl); -PALEXPORT int32_t CryptoNative_EvpCipherGetGcmTag(CipherCtx* ctx, uint8_t* tag, int32_t tagLength); -PALEXPORT int32_t CryptoNative_EvpCipherGetCcmTag(CipherCtx* ctx, uint8_t* tag, int32_t tagLength); PALEXPORT intptr_t CryptoNative_EvpAes128Ecb(void); PALEXPORT intptr_t CryptoNative_EvpAes128Cbc(void); PALEXPORT intptr_t CryptoNative_EvpAes128Cfb8(void); 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 8e1a35344934bf..c9509c974bac4e 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 @@ -46,28 +46,55 @@ private void EncryptInternal( } } - if (!Interop.Crypto.EvpCipherUpdate(ctx, ciphertext, out int ciphertextBytesWritten, plaintext)) + byte[]? rented = null; + try { - throw Interop.Crypto.CreateOpenSslCryptographicException(); - } + Span ciphertextAndTag = stackalloc byte[0]; + // Arbitrary limit. + const int StackAllocMax = 128; + if (ciphertext.Length + tag.Length <= StackAllocMax) + { + ciphertextAndTag = stackalloc byte[ciphertext.Length + tag.Length]; + } + else + { + rented = CryptoPool.Rent(ciphertext.Length + tag.Length); + ciphertextAndTag = new Span(rented, 0, ciphertext.Length + tag.Length); + } - if (!Interop.Crypto.EvpCipherFinalEx( - ctx, - ciphertext.Slice(ciphertextBytesWritten), - out int bytesWritten)) - { - throw Interop.Crypto.CreateOpenSslCryptographicException(); - } + if (!Interop.Crypto.EvpCipherUpdate(ctx, ciphertextAndTag, out int ciphertextBytesWritten, plaintext)) + { + throw Interop.Crypto.CreateOpenSslCryptographicException(); + } - ciphertextBytesWritten += bytesWritten; + if (!Interop.Crypto.EvpCipherFinalEx( + ctx, + ciphertextAndTag.Slice(ciphertextBytesWritten), + out int bytesWritten)) + { + throw Interop.Crypto.CreateOpenSslCryptographicException(); + } + + ciphertextBytesWritten += bytesWritten; - if (ciphertextBytesWritten != ciphertext.Length) + // NOTE: Android appends tag to the end of the ciphertext in case of CCM/GCM and "encryption" mode + + if (ciphertextBytesWritten != ciphertextAndTag.Length) + { + Debug.Fail($"CCM encrypt wrote {ciphertextBytesWritten} of {ciphertextAndTag.Length} bytes."); + throw new CryptographicException(); + } + + ciphertextAndTag.Slice(0, ciphertext.Length).CopyTo(ciphertext); + ciphertextAndTag.Slice(ciphertext.Length).CopyTo(tag); + } + finally { - Debug.Fail($"CCM encrypt wrote {ciphertextBytesWritten} of {ciphertext.Length} bytes."); - throw new CryptographicException(); + if (rented != null) + { + CryptoPool.Return(rented, ciphertext.Length + tag.Length); + } } - - Interop.Crypto.EvpCipherGetCcmTag(ctx, tag); } } 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 cf267cc97612a5..30ec15b72e6a60 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 @@ -52,28 +52,55 @@ private void EncryptInternal( } } - if (!Interop.Crypto.EvpCipherUpdate(_ctxHandle, ciphertext, out int ciphertextBytesWritten, plaintext)) + byte[]? rented = null; + try { - throw Interop.Crypto.CreateOpenSslCryptographicException(); - } + Span ciphertextAndTag = stackalloc byte[0]; + // Arbitrary limit. + const int StackAllocMax = 128; + if (ciphertext.Length + tag.Length <= StackAllocMax) + { + ciphertextAndTag = stackalloc byte[ciphertext.Length + tag.Length]; + } + else + { + rented = CryptoPool.Rent(ciphertext.Length + tag.Length); + ciphertextAndTag = new Span(rented, 0, ciphertext.Length + tag.Length); + } - if (!Interop.Crypto.EvpCipherFinalEx( - _ctxHandle, - ciphertext.Slice(ciphertextBytesWritten), - out int bytesWritten)) - { - throw Interop.Crypto.CreateOpenSslCryptographicException(); - } + if (!Interop.Crypto.EvpCipherUpdate(_ctxHandle, ciphertextAndTag, out int ciphertextBytesWritten, plaintext)) + { + throw Interop.Crypto.CreateOpenSslCryptographicException(); + } - ciphertextBytesWritten += bytesWritten; + if (!Interop.Crypto.EvpCipherFinalEx( + _ctxHandle, + ciphertextAndTag.Slice(ciphertextBytesWritten), + out int bytesWritten)) + { + throw Interop.Crypto.CreateOpenSslCryptographicException(); + } + + ciphertextBytesWritten += bytesWritten; - if (ciphertextBytesWritten != ciphertext.Length) + // NOTE: Android appends tag to the end of the ciphertext in case of CCM/GCM and "encryption" mode + + if (ciphertextBytesWritten != ciphertextAndTag.Length) + { + Debug.Fail($"GCM encrypt wrote {ciphertextBytesWritten} of {ciphertextAndTag.Length} bytes."); + throw new CryptographicException(); + } + + ciphertextAndTag.Slice(0, ciphertext.Length).CopyTo(ciphertext); + ciphertextAndTag.Slice(ciphertext.Length).CopyTo(tag); + } + finally { - Debug.Fail($"GCM encrypt wrote {ciphertextBytesWritten} of {ciphertext.Length} bytes."); - throw new CryptographicException(); + if (rented != null) + { + CryptoPool.Return(rented, ciphertext.Length + tag.Length); + } } - - Interop.Crypto.EvpCipherGetGcmTag(_ctxHandle, tag); } private void DecryptInternal( From 2011fef252657a0cb74e076ca20601ef8782b030 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 24 Feb 2021 14:38:28 -0800 Subject: [PATCH 07/14] Fix AES/CCM --- .../pal_evp_cipher.c | 1 + .../System/Security/Cryptography/AesCcm.Android.cs | 12 +++++++++--- .../System/Security/Cryptography/AesGcm.Android.cs | 9 +++++++-- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c index 664b228aec0a05..4b0d23d478268c 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c @@ -286,6 +286,7 @@ int32_t CryptoNative_EvpCipherUpdate(CipherCtx* ctx, uint8_t* outm, int32_t* out *outl = 0; jbyteArray outDataBytes = (jbyteArray)(*env)->CallObjectMethod(env, ctx->cipher, g_cipherUpdateMethod, inDataBytes); + if (outDataBytes && outm) { jsize outDataBytesLen = (*env)->GetArrayLength(env, outDataBytes); 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 c9509c974bac4e..ac9b2371032acb 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 @@ -85,8 +85,8 @@ private void EncryptInternal( throw new CryptographicException(); } - ciphertextAndTag.Slice(0, ciphertext.Length).CopyTo(ciphertext); - ciphertextAndTag.Slice(ciphertext.Length).CopyTo(tag); + ciphertextAndTag[..ciphertext.Length].CopyTo(ciphertext); + ciphertextAndTag[ciphertext.Length..].CopyTo(tag); } finally { @@ -109,11 +109,17 @@ private void DecryptInternal( { Interop.Crypto.CheckValidOpenSslHandle(ctx); Interop.Crypto.EvpCipherSetCcmNonceLength(ctx, nonce.Length); + + if (!Interop.Crypto.EvpCipherSetTagLength(ctx, tag.Length)) + { + throw Interop.Crypto.CreateOpenSslCryptographicException(); + } + Interop.Crypto.EvpCipherSetKeyAndIV(ctx, _key, nonce, Interop.Crypto.EvpCipherDirection.Decrypt); if (associatedData.Length != 0) { - if (!Interop.Crypto.EvpCipherUpdate(ctx, Span.Empty, out _, associatedData)) + if (!Interop.Crypto.EvpCipherUpdateAAD(ctx, associatedData)) { throw Interop.Crypto.CreateOpenSslCryptographicException(); } 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 30ec15b72e6a60..6caf3d90bc447e 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 @@ -91,8 +91,8 @@ private void EncryptInternal( throw new CryptographicException(); } - ciphertextAndTag.Slice(0, ciphertext.Length).CopyTo(ciphertext); - ciphertextAndTag.Slice(ciphertext.Length).CopyTo(tag); + ciphertextAndTag[..ciphertext.Length].CopyTo(ciphertext); + ciphertextAndTag[ciphertext.Length..].CopyTo(tag); } finally { @@ -110,6 +110,11 @@ private void DecryptInternal( Span plaintext, ReadOnlySpan associatedData) { + if (!Interop.Crypto.EvpCipherSetTagLength(_ctxHandle, tag.Length)) + { + throw Interop.Crypto.CreateOpenSslCryptographicException(); + } + Interop.Crypto.EvpCipherSetKeyAndIV( _ctxHandle, ReadOnlySpan.Empty, From 08449ed9279a70390a8e941eac13a3cffa1b3643 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 24 Feb 2021 14:45:00 -0800 Subject: [PATCH 08/14] Remove unused functions. --- .../pal_evp_cipher.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c index 4b0d23d478268c..b1c50a6f4f1342 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c @@ -122,20 +122,6 @@ static bool HasTag(intptr_t type) (type == CryptoNative_EvpAes256Ccm()); } -static bool IsAesCcm(intptr_t type) -{ - return (type == CryptoNative_EvpAes128Ccm()) || - (type == CryptoNative_EvpAes192Ccm()) || - (type == CryptoNative_EvpAes256Ccm()); -} - -static bool IsAesGcm(intptr_t type) -{ - return (type == CryptoNative_EvpAes128Gcm()) || - (type == CryptoNative_EvpAes192Gcm()) || - (type == CryptoNative_EvpAes256Gcm()); -} - CipherCtx* CryptoNative_EvpCipherCreatePartial(intptr_t type) { JNIEnv* env = GetJNIEnv(); From 965eeba38fbee4a378d1749a39de0a7d3306755e Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 24 Feb 2021 15:05:09 -0800 Subject: [PATCH 09/14] Support more CFB algorithm settings --- .../pal_evp_cipher.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c index b1c50a6f4f1342..9fd1c3b6e4d080 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c @@ -103,10 +103,9 @@ static jobject GetAlgorithmName(JNIEnv* env, intptr_t type) if (type == CryptoNative_EvpDes3Cfb8()) return JSTRING("DESede/CFB/NoPadding"); if (type == CryptoNative_EvpDes3Cfb64()) return JSTRING("DESede/CFB/NoPadding"); - // custom feedback size is not supported yet: - //if (type == CryptoNative_EvpAes128Cfb128()) return JSTRING("AES/CFB/NoPadding"); - //if (type == CryptoNative_EvpAes192Cfb128()) return JSTRING("AES/CFB/NoPadding"); - //if (type == CryptoNative_EvpAes256Cfb128()) return JSTRING("AES/CFB/NoPadding"); + if (type == CryptoNative_EvpAes128Cfb128()) return JSTRING("AES/CFB128/NoPadding"); + if (type == CryptoNative_EvpAes192Cfb128()) return JSTRING("AES/CFB128/NoPadding"); + if (type == CryptoNative_EvpAes256Cfb128()) return JSTRING("AES/CFB128/NoPadding"); LOG_ERROR("This algorithm (%ld) is not supported", (long)type); return FAIL; From 06eb35a722d44e12b9f50478d0d1a47ddc672dfb Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 24 Feb 2021 15:52:40 -0800 Subject: [PATCH 10/14] PR feedback. --- .../Security/Cryptography/AesCcm.Android.cs | 15 +++++++-------- .../System/Security/Cryptography/AesCcm.Unix.cs | 3 ++- .../Security/Cryptography/AesGcm.Android.cs | 15 +++++++-------- 3 files changed, 16 insertions(+), 17 deletions(-) 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 ac9b2371032acb..8c6ff39aa3f382 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 @@ -26,6 +26,7 @@ private void EncryptInternal( Span tag, ReadOnlySpan associatedData = default) { + // Convert key length to bits. using (SafeEvpCipherCtxHandle ctx = Interop.Crypto.EvpCipherCreatePartial(GetCipher(_key.Length * 8))) { Interop.Crypto.CheckValidOpenSslHandle(ctx); @@ -160,15 +161,13 @@ private void DecryptInternal( private static IntPtr GetCipher(int keySizeInBits) { - switch (keySizeInBits) + return keySizeInBits switch { - case 128: return Interop.Crypto.EvpAes128Ccm(); - case 192: return Interop.Crypto.EvpAes192Ccm(); - case 256: return Interop.Crypto.EvpAes256Ccm(); - default: - Debug.Fail("Key size should already be validated"); - return IntPtr.Zero; - } + 128 => Interop.Crypto.EvpAes128Ccm(), + 192 => Interop.Crypto.EvpAes192Ccm(), + 256 => Interop.Crypto.EvpAes256Ccm(), + _ => IntPtr.Zero + }; } public void Dispose() 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 989eef33be48ca..03da1e4ad730be 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 @@ -84,9 +84,10 @@ private void DecryptInternal( { Interop.Crypto.CheckValidOpenSslHandle(ctx); Interop.Crypto.EvpCipherSetCcmNonceLength(ctx, nonce.Length); - Interop.Crypto.EvpCipherSetKeyAndIV(ctx, _key, nonce, Interop.Crypto.EvpCipherDirection.Decrypt); Interop.Crypto.EvpCipherSetCcmTag(ctx, tag); + Interop.Crypto.EvpCipherSetKeyAndIV(ctx, _key, nonce, Interop.Crypto.EvpCipherDirection.Decrypt); + if (associatedData.Length != 0) { // length needs to be known ahead of time in CCM mode 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 6caf3d90bc447e..a4e12f368e8714 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 @@ -14,6 +14,7 @@ public sealed partial class AesGcm [MemberNotNull(nameof(_ctxHandle))] private void ImportKey(ReadOnlySpan key) { + // Convert key length to bits. _ctxHandle = Interop.Crypto.EvpCipherCreatePartial(GetCipher(key.Length * 8)); Interop.Crypto.CheckValidOpenSslHandle(_ctxHandle); @@ -161,15 +162,13 @@ private void DecryptInternal( private static IntPtr GetCipher(int keySizeInBits) { - switch (keySizeInBits) + return keySizeInBits switch { - case 128: return Interop.Crypto.EvpAes128Gcm(); - case 192: return Interop.Crypto.EvpAes192Gcm(); - case 256: return Interop.Crypto.EvpAes256Gcm(); - default: - Debug.Fail("Key size should already be validated"); - return IntPtr.Zero; - } + 128 => Interop.Crypto.EvpAes128Gcm(), + 192 => Interop.Crypto.EvpAes192Gcm(), + 256 => Interop.Crypto.EvpAes256Gcm(), + _ => IntPtr.Zero + }; } public void Dispose() From 56b7f4082576c9e3dfb539311870340b31c97edc Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 25 Feb 2021 11:35:15 -0800 Subject: [PATCH 11/14] Rename native layer to be Android-specific. Directly throw CryptographicExceptions. Check for overflow. --- ...nterop.EVP.Cipher.cs => Interop.Cipher.cs} | 95 ++--- .../CMakeLists.txt | 2 +- .../pal_cipher.c | 363 +++++++++++++++++ .../pal_cipher.h | 61 +++ .../pal_evp_cipher.c | 368 ------------------ .../pal_evp_cipher.h | 64 --- ...em.Security.Cryptography.Algorithms.csproj | 4 +- .../Security/Cryptography/AesCcm.Android.cs | 32 +- .../Security/Cryptography/AesGcm.Android.cs | 4 +- 9 files changed, 488 insertions(+), 505 deletions(-) rename src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/{Interop.EVP.Cipher.cs => Interop.Cipher.cs} (56%) create mode 100644 src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_cipher.c create mode 100644 src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_cipher.h delete mode 100644 src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c delete mode 100644 src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.h diff --git a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.EVP.Cipher.cs b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Cipher.cs similarity index 56% rename from src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.EVP.Cipher.cs rename to src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Cipher.cs index abdd15961698f3..4a4f52ed86d8d8 100644 --- a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.EVP.Cipher.cs +++ b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Cipher.cs @@ -9,7 +9,7 @@ internal static partial class Interop { internal static partial class Crypto { - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherCreate2")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_CipherCreate2")] internal static extern SafeEvpCipherCtxHandle EvpCipherCreate( IntPtr cipher, ref byte key, @@ -18,11 +18,11 @@ internal static extern SafeEvpCipherCtxHandle EvpCipherCreate( ref byte iv, int enc); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherCreatePartial")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_CipherCreatePartial")] internal static extern SafeEvpCipherCtxHandle EvpCipherCreatePartial( IntPtr cipher); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherSetKeyAndIV")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_CipherSetKeyAndIV")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool EvpCipherSetKeyAndIV( SafeEvpCipherCtxHandle ctx, @@ -46,44 +46,31 @@ ref MemoryMarshal.GetReference(iv), } } - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherSetGcmNonceLength")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_CipherSetNonceLength")] [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool CryptoNative_EvpCipherSetGcmNonceLength( + private static extern bool AndroidCryptoNative_CipherSetNonceLength( SafeEvpCipherCtxHandle ctx, int nonceLength); - internal static void EvpCipherSetGcmNonceLength(SafeEvpCipherCtxHandle ctx, int nonceLength) + internal static void EvpCipherSetNonceLength(SafeEvpCipherCtxHandle ctx, int nonceLength) { - if (!CryptoNative_EvpCipherSetGcmNonceLength(ctx, nonceLength)) + if (!AndroidCryptoNative_CipherSetNonceLength(ctx, nonceLength)) { throw CreateOpenSslCryptographicException(); } } - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherSetCcmNonceLength")] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool CryptoNative_EvpCipherSetCcmNonceLength( - SafeEvpCipherCtxHandle ctx, int nonceLength); - - internal static void EvpCipherSetCcmNonceLength(SafeEvpCipherCtxHandle ctx, int nonceLength) - { - if (!CryptoNative_EvpCipherSetCcmNonceLength(ctx, nonceLength)) - { - throw CreateOpenSslCryptographicException(); - } - } - - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherDestroy")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_CipherDestroy")] internal static extern void EvpCipherDestroy(IntPtr ctx); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherReset")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_CipherReset")] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool EvpCipherReset(SafeEvpCipherCtxHandle ctx); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherCtxSetPadding")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_CipherCtxSetPadding")] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool EvpCipherCtxSetPadding(SafeEvpCipherCtxHandle x, int padding); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherUpdate")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_CipherUpdate")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool EvpCipherUpdate( SafeEvpCipherCtxHandle ctx, @@ -106,7 +93,7 @@ ref MemoryMarshal.GetReference(input), input.Length); } - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherUpdateAAD")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_CipherUpdateAAD")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool EvpCipherUpdateAAD( SafeEvpCipherCtxHandle ctx, @@ -123,7 +110,7 @@ ref MemoryMarshal.GetReference(input), input.Length); } - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherFinalEx")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_CipherFinalEx")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool EvpCipherFinalEx( SafeEvpCipherCtxHandle ctx, @@ -138,91 +125,91 @@ internal static bool EvpCipherFinalEx( return EvpCipherFinalEx(ctx, ref MemoryMarshal.GetReference(output), out bytesWritten); } - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipherSetTagLength")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_CipherSetTagLength")] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool EvpCipherSetTagLength( SafeEvpCipherCtxHandle ctx, int tagLength); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes128Ecb")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_Aes128Ecb")] internal static extern IntPtr EvpAes128Ecb(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes128Cbc")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_Aes128Cbc")] internal static extern IntPtr EvpAes128Cbc(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes128Gcm")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_Aes128Gcm")] internal static extern IntPtr EvpAes128Gcm(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes128Cfb8")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_Aes128Cfb8")] internal static extern IntPtr EvpAes128Cfb8(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes128Cfb128")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_Aes128Cfb128")] internal static extern IntPtr EvpAes128Cfb128(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes128Ccm")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_Aes128Ccm")] internal static extern IntPtr EvpAes128Ccm(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes192Ecb")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_Aes192Ecb")] internal static extern IntPtr EvpAes192Ecb(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes192Cbc")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_Aes192Cbc")] internal static extern IntPtr EvpAes192Cbc(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes192Gcm")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_Aes192Gcm")] internal static extern IntPtr EvpAes192Gcm(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes192Cfb8")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_Aes192Cfb8")] internal static extern IntPtr EvpAes192Cfb8(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes192Cfb128")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_Aes192Cfb128")] internal static extern IntPtr EvpAes192Cfb128(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes192Ccm")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_Aes192Ccm")] internal static extern IntPtr EvpAes192Ccm(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes256Ecb")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_Aes256Ecb")] internal static extern IntPtr EvpAes256Ecb(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes256Cbc")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_Aes256Cbc")] internal static extern IntPtr EvpAes256Cbc(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes256Gcm")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_Aes256Gcm")] internal static extern IntPtr EvpAes256Gcm(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes256Cfb128")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_Aes256Cfb128")] internal static extern IntPtr EvpAes256Cfb128(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes256Cfb8")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_Aes256Cfb8")] internal static extern IntPtr EvpAes256Cfb8(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpAes256Ccm")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_Aes256Ccm")] internal static extern IntPtr EvpAes256Ccm(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpDesCbc")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_DesCbc")] internal static extern IntPtr EvpDesCbc(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpDesEcb")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_DesEcb")] internal static extern IntPtr EvpDesEcb(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpDesCfb8")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_DesCfb8")] internal static extern IntPtr EvpDesCfb8(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpDes3Cbc")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_Des3Cbc")] internal static extern IntPtr EvpDes3Cbc(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpDes3Ecb")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_Des3Ecb")] internal static extern IntPtr EvpDes3Ecb(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpDes3Cfb8")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_Des3Cfb8")] internal static extern IntPtr EvpDes3Cfb8(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpDes3Cfb64")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_Des3Cfb64")] internal static extern IntPtr EvpDes3Cfb64(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpRC2Cbc")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_RC2Cbc")] internal static extern IntPtr EvpRC2Cbc(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpRC2Ecb")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_RC2Ecb")] internal static extern IntPtr EvpRC2Ecb(); internal enum EvpCipherDirection : int diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/CMakeLists.txt b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/CMakeLists.txt index 4cc0ba49444597..afcb702969366d 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/CMakeLists.txt +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/CMakeLists.txt @@ -8,7 +8,7 @@ set(NATIVECRYPTO_SOURCES pal_jni.c pal_misc.c pal_evp.c - pal_evp_cipher.c + pal_cipher.c pal_hmac.c pal_bignum.c pal_ssl.c diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_cipher.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_cipher.c new file mode 100644 index 00000000000000..81fd8f6127179b --- /dev/null +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_cipher.c @@ -0,0 +1,363 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "pal_cipher.h" +#include "pal_utilities.h" + +// just some unique IDs +intptr_t AndroidCryptoNative_Aes128Ecb() { return 1001; } +intptr_t AndroidCryptoNative_Aes128Cbc() { return 1002; } +intptr_t AndroidCryptoNative_Aes128Cfb8() { return 1003; } +intptr_t AndroidCryptoNative_Aes128Cfb128() { return 1004; } +intptr_t AndroidCryptoNative_Aes128Gcm() { return 1005; } +intptr_t AndroidCryptoNative_Aes128Ccm() { return 1006; } + +intptr_t AndroidCryptoNative_Aes192Ecb() { return 1007; } +intptr_t AndroidCryptoNative_Aes192Cbc() { return 1008; } +intptr_t AndroidCryptoNative_Aes192Cfb8() { return 1009; } +intptr_t AndroidCryptoNative_Aes192Cfb128() { return 1010; } +intptr_t AndroidCryptoNative_Aes192Gcm() { return 1011; } +intptr_t AndroidCryptoNative_Aes192Ccm() { return 1012; } + +intptr_t AndroidCryptoNative_Aes256Ecb() { return 1013; } +intptr_t AndroidCryptoNative_Aes256Cbc() { return 1014; } +intptr_t AndroidCryptoNative_Aes256Cfb8() { return 1015; } +intptr_t AndroidCryptoNative_Aes256Cfb128() { return 1016; } +intptr_t AndroidCryptoNative_Aes256Gcm() { return 1017; } +intptr_t AndroidCryptoNative_Aes256Ccm() { return 1018; } + +intptr_t AndroidCryptoNative_Des3Ecb() { return 1019; } +intptr_t AndroidCryptoNative_Des3Cbc() { return 1020; } +intptr_t AndroidCryptoNative_Des3Cfb8() { return 1021; } +intptr_t AndroidCryptoNative_Des3Cfb64() { return 1022; } + +intptr_t AndroidCryptoNative_DesEcb() { return 1023; } +intptr_t AndroidCryptoNative_DesCfb8() { return 1024; } +intptr_t AndroidCryptoNative_DesCbc() { return 1025; } + +intptr_t AndroidCryptoNative_RC2Ecb() { return 1026; } +intptr_t AndroidCryptoNative_RC2Cbc() { return 1027; } + +static int32_t GetAlgorithmWidth(intptr_t type) +{ + if (type == AndroidCryptoNative_Aes128Ecb()) return 128; + if (type == AndroidCryptoNative_Aes128Cbc()) return 128; + if (type == AndroidCryptoNative_Aes128Gcm()) return 128; + if (type == AndroidCryptoNative_Aes128Ccm()) return 128; + if (type == AndroidCryptoNative_Aes128Cfb8()) return 128; + if (type == AndroidCryptoNative_Aes128Cfb128()) return 128; + + if (type == AndroidCryptoNative_Aes192Ecb()) return 192; + if (type == AndroidCryptoNative_Aes192Cbc()) return 192; + if (type == AndroidCryptoNative_Aes192Gcm()) return 192; + if (type == AndroidCryptoNative_Aes192Ccm()) return 192; + if (type == AndroidCryptoNative_Aes192Cfb8()) return 192; + if (type == AndroidCryptoNative_Aes192Cfb128()) return 192; + + if (type == AndroidCryptoNative_Aes256Ecb()) return 256; + if (type == AndroidCryptoNative_Aes256Cbc()) return 256; + if (type == AndroidCryptoNative_Aes256Gcm()) return 256; + if (type == AndroidCryptoNative_Aes256Ccm()) return 256; + if (type == AndroidCryptoNative_Aes256Cfb8()) return 256; + if (type == AndroidCryptoNative_Aes256Cfb128()) return 256; + + if (type == AndroidCryptoNative_DesEcb()) return 56; + if (type == AndroidCryptoNative_DesCfb8()) return 56; + if (type == AndroidCryptoNative_DesCbc()) return 56; + + if (type == AndroidCryptoNative_Des3Ecb()) return 168; + if (type == AndroidCryptoNative_Des3Cbc()) return 168; + if (type == AndroidCryptoNative_Des3Cfb8()) return 168; + if (type == AndroidCryptoNative_Des3Cfb64()) return 168; + + assert(0 && "unexpected type"); + return FAIL; +} + +static jobject GetAlgorithmName(JNIEnv* env, intptr_t type) +{ + if (type == AndroidCryptoNative_Aes128Ecb()) return JSTRING("AES/ECB/NoPadding"); + if (type == AndroidCryptoNative_Aes128Cbc()) return JSTRING("AES/CBC/NoPadding"); + if (type == AndroidCryptoNative_Aes128Gcm()) return JSTRING("AES/GCM/NoPadding"); + if (type == AndroidCryptoNative_Aes128Ccm()) return JSTRING("AES/CCM/NoPadding"); + if (type == AndroidCryptoNative_Aes128Cfb8()) return JSTRING("AES/CFB/NoPadding"); + + if (type == AndroidCryptoNative_Aes192Ecb()) return JSTRING("AES/ECB/NoPadding"); + if (type == AndroidCryptoNative_Aes192Cbc()) return JSTRING("AES/CBC/NoPadding"); + if (type == AndroidCryptoNative_Aes192Gcm()) return JSTRING("AES/GCM/NoPadding"); + if (type == AndroidCryptoNative_Aes192Ccm()) return JSTRING("AES/CCM/NoPadding"); + if (type == AndroidCryptoNative_Aes192Cfb8()) return JSTRING("AES/CFB/NoPadding"); + + if (type == AndroidCryptoNative_Aes256Ecb()) return JSTRING("AES/ECB/NoPadding"); + if (type == AndroidCryptoNative_Aes256Cbc()) return JSTRING("AES/CBC/NoPadding"); + if (type == AndroidCryptoNative_Aes256Gcm()) return JSTRING("AES/GCM/NoPadding"); + if (type == AndroidCryptoNative_Aes256Ccm()) return JSTRING("AES/CCM/NoPadding"); + if (type == AndroidCryptoNative_Aes256Cfb8()) return JSTRING("AES/CFB/NoPadding"); + + if (type == AndroidCryptoNative_DesEcb()) return JSTRING("DES/ECB/NoPadding"); + if (type == AndroidCryptoNative_DesCfb8()) return JSTRING("DES/CFB/NoPadding"); + if (type == AndroidCryptoNative_DesCbc()) return JSTRING("DES/CBC/NoPadding"); + + if (type == AndroidCryptoNative_Des3Ecb()) return JSTRING("DESede/ECB/NoPadding"); + if (type == AndroidCryptoNative_Des3Cbc()) return JSTRING("DESede/CBC/NoPadding"); + if (type == AndroidCryptoNative_Des3Cfb8()) return JSTRING("DESede/CFB/NoPadding"); + if (type == AndroidCryptoNative_Des3Cfb64()) return JSTRING("DESede/CFB/NoPadding"); + + if (type == AndroidCryptoNative_Aes128Cfb128()) return JSTRING("AES/CFB128/NoPadding"); + if (type == AndroidCryptoNative_Aes192Cfb128()) return JSTRING("AES/CFB128/NoPadding"); + if (type == AndroidCryptoNative_Aes256Cfb128()) return JSTRING("AES/CFB128/NoPadding"); + + LOG_ERROR("This algorithm (%ld) is not supported", (long)type); + return FAIL; +} + +static bool HasTag(intptr_t type) +{ + return (type == AndroidCryptoNative_Aes128Gcm()) || + (type == AndroidCryptoNative_Aes128Ccm()) || + (type == AndroidCryptoNative_Aes192Gcm()) || + (type == AndroidCryptoNative_Aes192Ccm()) || + (type == AndroidCryptoNative_Aes256Gcm()) || + (type == AndroidCryptoNative_Aes256Ccm()); +} + +CipherCtx* AndroidCryptoNative_CipherCreatePartial(intptr_t type) +{ + JNIEnv* env = GetJNIEnv(); + jobject algName = GetAlgorithmName(env, type); + if (!algName) + return FAIL; + + jobject cipher = ToGRef(env, (*env)->CallStaticObjectMethod(env, g_cipherClass, g_cipherGetInstanceMethod, algName)); + (*env)->DeleteLocalRef(env, algName); + + CipherCtx* ctx = malloc(sizeof(CipherCtx)); + ctx->cipher = cipher; + ctx->type = type; + ctx->tagLength = TAG_MAX_LENGTH; + ctx->ivLength = 0; + ctx->encMode = 0; + ctx->key = NULL; + ctx->iv = NULL; + return CheckJNIExceptions(env) ? FAIL : ctx; +} + +int32_t AndroidCryptoNative_CipherSetTagLength(CipherCtx* ctx, int32_t tagLength) +{ + if (!ctx) + return FAIL; + + if(tagLength > TAG_MAX_LENGTH) + return FAIL; + + ctx->tagLength = tagLength; + return SUCCESS; +} + +static int32_t ReinitializeCipher(CipherCtx* ctx) +{ + JNIEnv* env = GetJNIEnv(); + + int32_t keyLength = GetAlgorithmWidth(ctx->type); + + // int ivSize = cipher.getBlockSize(); + // SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES"); + // IvParameterSpec ivSpec = new IvParameterSpec(IV); or GCMParameterSpec for GCM/CCM + // cipher.init(encMode, keySpec, ivSpec); + + jobject algName = GetAlgorithmName(env, ctx->type); + if (!algName) + return FAIL; + + if (!ctx->ivLength) + ctx->ivLength = (*env)->CallIntMethod(env, ctx->cipher, g_getBlockSizeMethod); + + jbyteArray keyBytes = (*env)->NewByteArray(env, keyLength / 8); // bits to bytes, e.g. 256 -> 32 + (*env)->SetByteArrayRegion(env, keyBytes, 0, keyLength / 8, (jbyte*)ctx->key); + jbyteArray ivBytes = (*env)->NewByteArray(env, ctx->ivLength); + (*env)->SetByteArrayRegion(env, ivBytes, 0, ctx->ivLength, (jbyte*)ctx->iv); + + jobject sksObj = (*env)->NewObject(env, g_sksClass, g_sksCtor, keyBytes, algName); + jobject ivPsObj; + + if (HasTag(ctx->type)) + ivPsObj = (*env)->NewObject(env, g_GCMParameterSpecClass, g_GCMParameterSpecCtor, ctx->tagLength * 8, ivBytes); + else + ivPsObj = (*env)->NewObject(env, g_ivPsClass, g_ivPsCtor, ivBytes); + + (*env)->CallVoidMethod(env, ctx->cipher, g_cipherInitMethod, ctx->encMode, sksObj, ivPsObj); + (*env)->DeleteLocalRef(env, algName); + (*env)->DeleteLocalRef(env, sksObj); + (*env)->DeleteLocalRef(env, ivPsObj); + (*env)->DeleteLocalRef(env, keyBytes); + (*env)->DeleteLocalRef(env, ivBytes); + + return CheckJNIExceptions(env) ? FAIL : SUCCESS; +} + +int32_t AndroidCryptoNative_CipherSetKeyAndIV(CipherCtx* ctx, uint8_t* key, uint8_t* iv, int32_t enc) +{ + if (!ctx) + return FAIL; + + int32_t keyLength = GetAlgorithmWidth(ctx->type); + + // input: 0 for Decrypt, 1 for Encrypt, -1 leave untouched + // Cipher: 2 for Decrypt, 1 for Encrypt, N/A + if (enc != -1) + { + assert(enc == 0 || enc == 1); + ctx->encMode = enc == 0 ? CIPHER_DECRYPT_MODE : CIPHER_ENCRYPT_MODE; + } + + // CryptoNative_CipherSetKeyAndIV can be called separately for key and iv + // so we need to wait for both and do Init after. + if (key) + SaveTo(key, &ctx->key, (size_t)keyLength, /* overwrite */ true); + if (iv) + SaveTo(iv, &ctx->iv, (size_t)ctx->ivLength, /* overwrite */ true); + + if (!ctx->key || !ctx->iv) + return SUCCESS; + + return ReinitializeCipher(ctx); +} + +CipherCtx* AndroidCryptoNative_CipherCreate2(intptr_t type, uint8_t* key, int32_t keyLength, int32_t effectiveKeyLength, uint8_t* iv, int32_t enc) +{ + if (effectiveKeyLength != 0) + { + LOG_ERROR("Non-zero effectiveKeyLength is not supported"); + return FAIL; + } + + CipherCtx* ctx = AndroidCryptoNative_CipherCreatePartial(type); + if (AndroidCryptoNative_CipherSetKeyAndIV(ctx, key, iv, enc) != SUCCESS) + return FAIL; + + if (keyLength != GetAlgorithmWidth(type)) + { + LOG_ERROR("Key length must match algorithm width."); + return FAIL; + } + return ctx; +} + +int32_t AndroidCryptoNative_CipherUpdateAAD(CipherCtx* ctx, uint8_t* in, int32_t inl) +{ + if (!ctx) + return FAIL; + + JNIEnv* env = GetJNIEnv(); + jbyteArray inDataBytes = (*env)->NewByteArray(env, inl); + (*env)->SetByteArrayRegion(env, inDataBytes, 0, inl, (jbyte*)in); + (*env)->CallVoidMethod(env, ctx->cipher, g_cipherUpdateAADMethod, inDataBytes); + (*env)->DeleteLocalRef(env, inDataBytes); + return CheckJNIExceptions(env) ? FAIL : SUCCESS; +} + +int32_t AndroidCryptoNative_CipherUpdate(CipherCtx* ctx, uint8_t* outm, int32_t* outl, uint8_t* in, int32_t inl) +{ + if (!ctx) + return FAIL; + + if (!outl && !in) + // it means caller wants us to record "inl" but we don't need it. + return SUCCESS; + + JNIEnv* env = GetJNIEnv(); + jbyteArray inDataBytes = (*env)->NewByteArray(env, inl); + (*env)->SetByteArrayRegion(env, inDataBytes, 0, inl, (jbyte*)in); + + *outl = 0; + jbyteArray outDataBytes = (jbyteArray)(*env)->CallObjectMethod(env, ctx->cipher, g_cipherUpdateMethod, inDataBytes); + + if (outDataBytes && outm) + { + jsize outDataBytesLen = (*env)->GetArrayLength(env, outDataBytes); + *outl = outDataBytesLen; + (*env)->GetByteArrayRegion(env, outDataBytes, 0, outDataBytesLen, (jbyte*) outm); + (*env)->DeleteLocalRef(env, outDataBytes); + } + + (*env)->DeleteLocalRef(env, inDataBytes); + return CheckJNIExceptions(env) ? FAIL : SUCCESS; +} + +int32_t AndroidCryptoNative_CipherFinalEx(CipherCtx* ctx, uint8_t* outm, int32_t* outl) +{ + if (!ctx) + return FAIL; + + JNIEnv* env = GetJNIEnv(); + + *outl = 0; + + jbyteArray outBytes = (jbyteArray)(*env)->CallObjectMethod(env, ctx->cipher, g_cipherDoFinalMethod); + if (CheckJNIExceptions(env)) return FAIL; + + jsize outBytesLen = (*env)->GetArrayLength(env, outBytes); + *outl = outBytesLen; + (*env)->GetByteArrayRegion(env, outBytes, 0, outBytesLen, (jbyte*) outm); + + (*env)->DeleteLocalRef(env, outBytes); + return CheckJNIExceptions(env) ? FAIL : SUCCESS; +} + +int32_t AndroidCryptoNative_CipherCtxSetPadding(CipherCtx* ctx, int32_t padding) +{ + if (!ctx) + return FAIL; + + if (padding == 0) + { + return SUCCESS; + } + else + { + // TODO: re-init ctx->cipher ? + LOG_ERROR("Non-zero padding (%d) is not supported yet", (int)padding); + return FAIL; + } +} + +int32_t AndroidCryptoNative_CipherReset(CipherCtx* ctx) +{ + if (!ctx) + return FAIL; + + free(ctx->iv); + ctx->iv = NULL; + ctx->ivLength = 0; + + JNIEnv* env = GetJNIEnv(); + ReleaseGRef(env, ctx->cipher); + jobject algName = GetAlgorithmName(env, ctx->type); + if (!algName) + return FAIL; + + ctx->cipher = ToGRef(env, (*env)->CallStaticObjectMethod(env, g_cipherClass, g_cipherGetInstanceMethod, algName)); + (*env)->DeleteLocalRef(env, algName); + return CheckJNIExceptions(env) ? FAIL : SUCCESS; +} + +int32_t AndroidCryptoNative_CipherSetNonceLength(CipherCtx* ctx, int32_t ivLength) +{ + if (!ctx) + return FAIL; + + ctx->ivLength = ivLength; + return SUCCESS; +} + +void AndroidCryptoNative_CipherDestroy(CipherCtx* ctx) +{ + if (ctx) + { + JNIEnv* env = GetJNIEnv(); + ReleaseGRef(env, ctx->cipher); + free(ctx->key); + free(ctx->iv); + free(ctx); + } +} diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_cipher.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_cipher.h new file mode 100644 index 00000000000000..4ae28eb266a00e --- /dev/null +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_cipher.h @@ -0,0 +1,61 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#pragma once + +#include "pal_jni.h" + +#define TAG_MAX_LENGTH 16 + +#define CIPHER_ENCRYPT_MODE 1 +#define CIPHER_DECRYPT_MODE 2 + +typedef struct CipherCtx +{ + jobject cipher; + intptr_t type; + int32_t ivLength; + int32_t tagLength; + int32_t encMode; + uint8_t* key; + uint8_t* iv; +} CipherCtx; + +PALEXPORT CipherCtx* AndroidCryptoNative_CipherCreate2(intptr_t type, uint8_t* key, int32_t keyLength, int32_t effectiveKeyLength, uint8_t* iv, int32_t enc); +PALEXPORT CipherCtx* AndroidCryptoNative_CipherCreatePartial(intptr_t type); +PALEXPORT int32_t AndroidCryptoNative_CipherSetTagLength(CipherCtx* ctx, int32_t tagLength); +PALEXPORT int32_t AndroidCryptoNative_CipherSetKeyAndIV(CipherCtx* ctx, uint8_t* key, uint8_t* iv, int32_t enc); +PALEXPORT int32_t AndroidCryptoNative_CipherSetNonceLength(CipherCtx* ctx, int32_t ivLength); +PALEXPORT void AndroidCryptoNative_CipherDestroy(CipherCtx* ctx); +PALEXPORT int32_t AndroidCryptoNative_CipherReset(CipherCtx* ctx); +PALEXPORT int32_t AndroidCryptoNative_CipherCtxSetPadding(CipherCtx* ctx, int32_t padding); +PALEXPORT int32_t AndroidCryptoNative_CipherUpdateAAD(CipherCtx* ctx, uint8_t* in, int32_t inl); +PALEXPORT int32_t AndroidCryptoNative_CipherUpdate(CipherCtx* ctx, uint8_t* out, int32_t* outl, uint8_t* in, int32_t inl); +PALEXPORT int32_t AndroidCryptoNative_CipherFinalEx(CipherCtx* ctx, uint8_t* outm, int32_t* outl); +PALEXPORT intptr_t AndroidCryptoNative_Aes128Ecb(void); +PALEXPORT intptr_t AndroidCryptoNative_Aes128Cbc(void); +PALEXPORT intptr_t AndroidCryptoNative_Aes128Cfb8(void); +PALEXPORT intptr_t AndroidCryptoNative_Aes128Cfb128(void); +PALEXPORT intptr_t AndroidCryptoNative_Aes128Gcm(void); +PALEXPORT intptr_t AndroidCryptoNative_Aes128Ccm(void); +PALEXPORT intptr_t AndroidCryptoNative_Aes192Ecb(void); +PALEXPORT intptr_t AndroidCryptoNative_Aes192Cbc(void); +PALEXPORT intptr_t AndroidCryptoNative_Aes192Cfb8(void); +PALEXPORT intptr_t AndroidCryptoNative_Aes192Cfb128(void); +PALEXPORT intptr_t AndroidCryptoNative_Aes192Gcm(void); +PALEXPORT intptr_t AndroidCryptoNative_Aes192Ccm(void); +PALEXPORT intptr_t AndroidCryptoNative_Aes256Ecb(void); +PALEXPORT intptr_t AndroidCryptoNative_Aes256Cbc(void); +PALEXPORT intptr_t AndroidCryptoNative_Aes256Cfb8(void); +PALEXPORT intptr_t AndroidCryptoNative_Aes256Cfb128(void); +PALEXPORT intptr_t AndroidCryptoNative_Aes256Gcm(void); +PALEXPORT intptr_t AndroidCryptoNative_Aes256Ccm(void); +PALEXPORT intptr_t AndroidCryptoNative_Des3Ecb(void); +PALEXPORT intptr_t AndroidCryptoNative_Des3Cbc(void); +PALEXPORT intptr_t AndroidCryptoNative_Des3Cfb8(void); +PALEXPORT intptr_t AndroidCryptoNative_Des3Cfb64(void); +PALEXPORT intptr_t AndroidCryptoNative_DesEcb(void); +PALEXPORT intptr_t AndroidCryptoNative_DesCfb8(void); +PALEXPORT intptr_t AndroidCryptoNative_DesCbc(void); +PALEXPORT intptr_t AndroidCryptoNative_RC2Ecb(void); +PALEXPORT intptr_t AndroidCryptoNative_RC2Cbc(void); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c deleted file mode 100644 index 9fd1c3b6e4d080..00000000000000 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.c +++ /dev/null @@ -1,368 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "pal_evp_cipher.h" -#include "pal_utilities.h" - -// just some unique IDs -intptr_t CryptoNative_EvpAes128Ecb() { return 1001; } -intptr_t CryptoNative_EvpAes128Cbc() { return 1002; } -intptr_t CryptoNative_EvpAes128Cfb8() { return 1003; } -intptr_t CryptoNative_EvpAes128Cfb128() { return 1004; } -intptr_t CryptoNative_EvpAes128Gcm() { return 1005; } -intptr_t CryptoNative_EvpAes128Ccm() { return 1006; } - -intptr_t CryptoNative_EvpAes192Ecb() { return 1007; } -intptr_t CryptoNative_EvpAes192Cbc() { return 1008; } -intptr_t CryptoNative_EvpAes192Cfb8() { return 1009; } -intptr_t CryptoNative_EvpAes192Cfb128() { return 1010; } -intptr_t CryptoNative_EvpAes192Gcm() { return 1011; } -intptr_t CryptoNative_EvpAes192Ccm() { return 1012; } - -intptr_t CryptoNative_EvpAes256Ecb() { return 1013; } -intptr_t CryptoNative_EvpAes256Cbc() { return 1014; } -intptr_t CryptoNative_EvpAes256Cfb8() { return 1015; } -intptr_t CryptoNative_EvpAes256Cfb128() { return 1016; } -intptr_t CryptoNative_EvpAes256Gcm() { return 1017; } -intptr_t CryptoNative_EvpAes256Ccm() { return 1018; } - -intptr_t CryptoNative_EvpDes3Ecb() { return 1019; } -intptr_t CryptoNative_EvpDes3Cbc() { return 1020; } -intptr_t CryptoNative_EvpDes3Cfb8() { return 1021; } -intptr_t CryptoNative_EvpDes3Cfb64() { return 1022; } - -intptr_t CryptoNative_EvpDesEcb() { return 1023; } -intptr_t CryptoNative_EvpDesCfb8() { return 1024; } -intptr_t CryptoNative_EvpDesCbc() { return 1025; } - -intptr_t CryptoNative_EvpRC2Ecb() { return 1026; } -intptr_t CryptoNative_EvpRC2Cbc() { return 1027; } - -static int32_t GetAlgorithmWidth(intptr_t type) -{ - if (type == CryptoNative_EvpAes128Ecb()) return 128; - if (type == CryptoNative_EvpAes128Cbc()) return 128; - if (type == CryptoNative_EvpAes128Gcm()) return 128; - if (type == CryptoNative_EvpAes128Ccm()) return 128; - if (type == CryptoNative_EvpAes128Cfb8()) return 128; - if (type == CryptoNative_EvpAes128Cfb128()) return 128; - - if (type == CryptoNative_EvpAes192Ecb()) return 192; - if (type == CryptoNative_EvpAes192Cbc()) return 192; - if (type == CryptoNative_EvpAes192Gcm()) return 192; - if (type == CryptoNative_EvpAes192Ccm()) return 192; - if (type == CryptoNative_EvpAes192Cfb8()) return 192; - if (type == CryptoNative_EvpAes192Cfb128()) return 192; - - if (type == CryptoNative_EvpAes256Ecb()) return 256; - if (type == CryptoNative_EvpAes256Cbc()) return 256; - if (type == CryptoNative_EvpAes256Gcm()) return 256; - if (type == CryptoNative_EvpAes256Ccm()) return 256; - if (type == CryptoNative_EvpAes256Cfb8()) return 256; - if (type == CryptoNative_EvpAes256Cfb128()) return 256; - - if (type == CryptoNative_EvpDesEcb()) return 56; - if (type == CryptoNative_EvpDesCfb8()) return 56; - if (type == CryptoNative_EvpDesCbc()) return 56; - - if (type == CryptoNative_EvpDes3Ecb()) return 168; - if (type == CryptoNative_EvpDes3Cbc()) return 168; - if (type == CryptoNative_EvpDes3Cfb8()) return 168; - if (type == CryptoNative_EvpDes3Cfb64()) return 168; - - assert(0 && "unexpected type"); - return FAIL; -} - -static jobject GetAlgorithmName(JNIEnv* env, intptr_t type) -{ - if (type == CryptoNative_EvpAes128Ecb()) return JSTRING("AES/ECB/NoPadding"); - if (type == CryptoNative_EvpAes128Cbc()) return JSTRING("AES/CBC/NoPadding"); - if (type == CryptoNative_EvpAes128Gcm()) return JSTRING("AES/GCM/NoPadding"); - if (type == CryptoNative_EvpAes128Ccm()) return JSTRING("AES/CCM/NoPadding"); - if (type == CryptoNative_EvpAes128Cfb8()) return JSTRING("AES/CFB/NoPadding"); - - if (type == CryptoNative_EvpAes192Ecb()) return JSTRING("AES/ECB/NoPadding"); - if (type == CryptoNative_EvpAes192Cbc()) return JSTRING("AES/CBC/NoPadding"); - if (type == CryptoNative_EvpAes192Gcm()) return JSTRING("AES/GCM/NoPadding"); - if (type == CryptoNative_EvpAes192Ccm()) return JSTRING("AES/CCM/NoPadding"); - if (type == CryptoNative_EvpAes192Cfb8()) return JSTRING("AES/CFB/NoPadding"); - - if (type == CryptoNative_EvpAes256Ecb()) return JSTRING("AES/ECB/NoPadding"); - if (type == CryptoNative_EvpAes256Cbc()) return JSTRING("AES/CBC/NoPadding"); - if (type == CryptoNative_EvpAes256Gcm()) return JSTRING("AES/GCM/NoPadding"); - if (type == CryptoNative_EvpAes256Ccm()) return JSTRING("AES/CCM/NoPadding"); - if (type == CryptoNative_EvpAes256Cfb8()) return JSTRING("AES/CFB/NoPadding"); - - if (type == CryptoNative_EvpDesEcb()) return JSTRING("DES/ECB/NoPadding"); - if (type == CryptoNative_EvpDesCfb8()) return JSTRING("DES/CFB/NoPadding"); - if (type == CryptoNative_EvpDesCbc()) return JSTRING("DES/CBC/NoPadding"); - - if (type == CryptoNative_EvpDes3Ecb()) return JSTRING("DESede/ECB/NoPadding"); - if (type == CryptoNative_EvpDes3Cbc()) return JSTRING("DESede/CBC/NoPadding"); - if (type == CryptoNative_EvpDes3Cfb8()) return JSTRING("DESede/CFB/NoPadding"); - if (type == CryptoNative_EvpDes3Cfb64()) return JSTRING("DESede/CFB/NoPadding"); - - if (type == CryptoNative_EvpAes128Cfb128()) return JSTRING("AES/CFB128/NoPadding"); - if (type == CryptoNative_EvpAes192Cfb128()) return JSTRING("AES/CFB128/NoPadding"); - if (type == CryptoNative_EvpAes256Cfb128()) return JSTRING("AES/CFB128/NoPadding"); - - LOG_ERROR("This algorithm (%ld) is not supported", (long)type); - return FAIL; -} - -static bool HasTag(intptr_t type) -{ - return (type == CryptoNative_EvpAes128Gcm()) || - (type == CryptoNative_EvpAes128Ccm()) || - (type == CryptoNative_EvpAes192Gcm()) || - (type == CryptoNative_EvpAes192Ccm()) || - (type == CryptoNative_EvpAes256Gcm()) || - (type == CryptoNative_EvpAes256Ccm()); -} - -CipherCtx* CryptoNative_EvpCipherCreatePartial(intptr_t type) -{ - JNIEnv* env = GetJNIEnv(); - jobject algName = GetAlgorithmName(env, type); - if (!algName) - return FAIL; - - jobject cipher = ToGRef(env, (*env)->CallStaticObjectMethod(env, g_cipherClass, g_cipherGetInstanceMethod, algName)); - (*env)->DeleteLocalRef(env, algName); - - CipherCtx* ctx = malloc(sizeof(CipherCtx)); - ctx->cipher = cipher; - ctx->type = type; - ctx->tagLength = TAG_MAX_LENGTH; - ctx->ivLength = 0; - ctx->encMode = 0; - ctx->key = NULL; - ctx->iv = NULL; - return CheckJNIExceptions(env) ? FAIL : ctx; -} - -int32_t CryptoNative_EvpCipherSetTagLength(CipherCtx* ctx, int32_t tagLength) -{ - if (!ctx) - return FAIL; - - if(tagLength > TAG_MAX_LENGTH) - return FAIL; - - ctx->tagLength = tagLength; - return SUCCESS; -} - -static int32_t CryptoNative_EvpCipherReinitialize(CipherCtx* ctx) -{ - JNIEnv* env = GetJNIEnv(); - - int32_t keyLength = GetAlgorithmWidth(ctx->type); - - // int ivSize = cipher.getBlockSize(); - // SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES"); - // IvParameterSpec ivSpec = new IvParameterSpec(IV); or GCMParameterSpec for GCM/CCM - // cipher.init(encMode, keySpec, ivSpec); - - jobject algName = GetAlgorithmName(env, ctx->type); - if (!algName) - return FAIL; - - if (!ctx->ivLength) - ctx->ivLength = (*env)->CallIntMethod(env, ctx->cipher, g_getBlockSizeMethod); - - jbyteArray keyBytes = (*env)->NewByteArray(env, keyLength / 8); // bits to bytes, e.g. 256 -> 32 - (*env)->SetByteArrayRegion(env, keyBytes, 0, keyLength / 8, (jbyte*)ctx->key); - jbyteArray ivBytes = (*env)->NewByteArray(env, ctx->ivLength); - (*env)->SetByteArrayRegion(env, ivBytes, 0, ctx->ivLength, (jbyte*)ctx->iv); - - jobject sksObj = (*env)->NewObject(env, g_sksClass, g_sksCtor, keyBytes, algName); - jobject ivPsObj; - - if (HasTag(ctx->type)) - ivPsObj = (*env)->NewObject(env, g_GCMParameterSpecClass, g_GCMParameterSpecCtor, ctx->tagLength * 8, ivBytes); - else - ivPsObj = (*env)->NewObject(env, g_ivPsClass, g_ivPsCtor, ivBytes); - - (*env)->CallVoidMethod(env, ctx->cipher, g_cipherInitMethod, ctx->encMode, sksObj, ivPsObj); - (*env)->DeleteLocalRef(env, algName); - (*env)->DeleteLocalRef(env, sksObj); - (*env)->DeleteLocalRef(env, ivPsObj); - (*env)->DeleteLocalRef(env, keyBytes); - (*env)->DeleteLocalRef(env, ivBytes); - - return CheckJNIExceptions(env) ? FAIL : SUCCESS; -} - -int32_t CryptoNative_EvpCipherSetKeyAndIV(CipherCtx* ctx, uint8_t* key, uint8_t* iv, int32_t enc) -{ - if (!ctx) - return FAIL; - - int32_t keyLength = GetAlgorithmWidth(ctx->type); - - // input: 0 for Decrypt, 1 for Encrypt, -1 leave untouched - // Cipher: 2 for Decrypt, 1 for Encrypt, N/A - if (enc != -1) - { - assert(enc == 0 || enc == 1); - ctx->encMode = enc == 0 ? CIPHER_DECRYPT_MODE : CIPHER_ENCRYPT_MODE; - } - - // CryptoNative_EvpCipherSetKeyAndIV can be called separately for key and iv - // so we need to wait for both and do Init after. - if (key) - SaveTo(key, &ctx->key, (size_t)keyLength, /* overwrite */ true); - if (iv) - SaveTo(iv, &ctx->iv, (size_t)ctx->ivLength, /* overwrite */ true); - - if (!ctx->key || !ctx->iv) - return SUCCESS; - - return CryptoNative_EvpCipherReinitialize(ctx); -} - -CipherCtx* CryptoNative_EvpCipherCreate2(intptr_t type, uint8_t* key, int32_t keyLength, int32_t effectiveKeyLength, uint8_t* iv, int32_t enc) -{ - if (effectiveKeyLength != 0) - { - LOG_ERROR("Non-zero effectiveKeyLength is not supported"); - return FAIL; - } - - CipherCtx* ctx = CryptoNative_EvpCipherCreatePartial(type); - if (CryptoNative_EvpCipherSetKeyAndIV(ctx, key, iv, enc) != SUCCESS) - return FAIL; - - if (keyLength != GetAlgorithmWidth(type)) - { - LOG_ERROR("Key length must match algorithm width."); - return FAIL; - } - return ctx; -} - -int32_t CryptoNative_EvpCipherUpdateAAD(CipherCtx* ctx, uint8_t* in, int32_t inl) -{ - if (!ctx) - return FAIL; - - JNIEnv* env = GetJNIEnv(); - jbyteArray inDataBytes = (*env)->NewByteArray(env, inl); - (*env)->SetByteArrayRegion(env, inDataBytes, 0, inl, (jbyte*)in); - (*env)->CallVoidMethod(env, ctx->cipher, g_cipherUpdateAADMethod, inDataBytes); - (*env)->DeleteLocalRef(env, inDataBytes); - return CheckJNIExceptions(env) ? FAIL : SUCCESS; -} - -int32_t CryptoNative_EvpCipherUpdate(CipherCtx* ctx, uint8_t* outm, int32_t* outl, uint8_t* in, int32_t inl) -{ - if (!ctx) - return FAIL; - - if (!outl && !in) - // it means caller wants us to record "inl" but we don't need it. - return SUCCESS; - - JNIEnv* env = GetJNIEnv(); - jbyteArray inDataBytes = (*env)->NewByteArray(env, inl); - (*env)->SetByteArrayRegion(env, inDataBytes, 0, inl, (jbyte*)in); - - *outl = 0; - jbyteArray outDataBytes = (jbyteArray)(*env)->CallObjectMethod(env, ctx->cipher, g_cipherUpdateMethod, inDataBytes); - - if (outDataBytes && outm) - { - jsize outDataBytesLen = (*env)->GetArrayLength(env, outDataBytes); - *outl = outDataBytesLen; - (*env)->GetByteArrayRegion(env, outDataBytes, 0, outDataBytesLen, (jbyte*) outm); - (*env)->DeleteLocalRef(env, outDataBytes); - } - - (*env)->DeleteLocalRef(env, inDataBytes); - return CheckJNIExceptions(env) ? FAIL : SUCCESS; -} - -int32_t CryptoNative_EvpCipherFinalEx(CipherCtx* ctx, uint8_t* outm, int32_t* outl) -{ - if (!ctx) - return FAIL; - - JNIEnv* env = GetJNIEnv(); - - *outl = 0; - - jbyteArray outBytes = (jbyteArray)(*env)->CallObjectMethod(env, ctx->cipher, g_cipherDoFinalMethod); - if (CheckJNIExceptions(env)) return FAIL; - - jsize outBytesLen = (*env)->GetArrayLength(env, outBytes); - *outl = outBytesLen; - (*env)->GetByteArrayRegion(env, outBytes, 0, outBytesLen, (jbyte*) outm); - - (*env)->DeleteLocalRef(env, outBytes); - return CheckJNIExceptions(env) ? FAIL : SUCCESS; -} - -int32_t CryptoNative_EvpCipherCtxSetPadding(CipherCtx* ctx, int32_t padding) -{ - if (!ctx) - return FAIL; - - if (padding == 0) - { - return SUCCESS; - } - else - { - // TODO: re-init ctx->cipher ? - LOG_ERROR("Non-zero padding (%d) is not supported yet", (int)padding); - return FAIL; - } -} - -int32_t CryptoNative_EvpCipherReset(CipherCtx* ctx) -{ - if (!ctx) - return FAIL; - - free(ctx->iv); - ctx->iv = NULL; - ctx->ivLength = 0; - - JNIEnv* env = GetJNIEnv(); - ReleaseGRef(env, ctx->cipher); - jobject algName = GetAlgorithmName(env, ctx->type); - if (!algName) - return FAIL; - - ctx->cipher = ToGRef(env, (*env)->CallStaticObjectMethod(env, g_cipherClass, g_cipherGetInstanceMethod, algName)); - (*env)->DeleteLocalRef(env, algName); - return CheckJNIExceptions(env) ? FAIL : SUCCESS; -} - -int32_t CryptoNative_EvpCipherSetGcmNonceLength(CipherCtx* ctx, int32_t ivLength) -{ - if (!ctx) - return FAIL; - - ctx->ivLength = ivLength; - return SUCCESS; -} - -int32_t CryptoNative_EvpCipherSetCcmNonceLength(CipherCtx* ctx, int32_t ivLength) -{ - return CryptoNative_EvpCipherSetGcmNonceLength(ctx, ivLength); -} - -void CryptoNative_EvpCipherDestroy(CipherCtx* ctx) -{ - if (ctx) - { - JNIEnv* env = GetJNIEnv(); - ReleaseGRef(env, ctx->cipher); - free(ctx->key); - free(ctx->iv); - free(ctx); - } -} diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.h deleted file mode 100644 index fe24dc56141615..00000000000000 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_evp_cipher.h +++ /dev/null @@ -1,64 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#pragma once - -#include "pal_jni.h" -#include "pal_evp.h" - - -#define TAG_MAX_LENGTH 16 - -#define CIPHER_ENCRYPT_MODE 1 -#define CIPHER_DECRYPT_MODE 2 - -typedef struct CipherCtx -{ - jobject cipher; - intptr_t type; - int32_t ivLength; - int32_t tagLength; - int32_t encMode; - uint8_t* key; - uint8_t* iv; -} CipherCtx; - -PALEXPORT CipherCtx* CryptoNative_EvpCipherCreate2(intptr_t type, uint8_t* key, int32_t keyLength, int32_t effectiveKeyLength, uint8_t* iv, int32_t enc); -PALEXPORT CipherCtx* CryptoNative_EvpCipherCreatePartial(intptr_t type); -PALEXPORT int32_t CryptoNative_EvpCipherSetTagLength(CipherCtx* ctx, int32_t tagLength); -PALEXPORT int32_t CryptoNative_EvpCipherSetKeyAndIV(CipherCtx* ctx, uint8_t* key, uint8_t* iv, int32_t enc); -PALEXPORT int32_t CryptoNative_EvpCipherSetGcmNonceLength(CipherCtx* ctx, int32_t ivLength); -PALEXPORT int32_t CryptoNative_EvpCipherSetCcmNonceLength(CipherCtx* ctx, int32_t ivLength); -PALEXPORT void CryptoNative_EvpCipherDestroy(CipherCtx* ctx); -PALEXPORT int32_t CryptoNative_EvpCipherReset(CipherCtx* ctx); -PALEXPORT int32_t CryptoNative_EvpCipherCtxSetPadding(CipherCtx* ctx, int32_t padding); -PALEXPORT int32_t CryptoNative_EvpCipherUpdateAAD(CipherCtx* ctx, uint8_t* in, int32_t inl); -PALEXPORT int32_t CryptoNative_EvpCipherUpdate(CipherCtx* ctx, uint8_t* out, int32_t* outl, uint8_t* in, int32_t inl); -PALEXPORT int32_t CryptoNative_EvpCipherFinalEx(CipherCtx* ctx, uint8_t* outm, int32_t* outl); -PALEXPORT intptr_t CryptoNative_EvpAes128Ecb(void); -PALEXPORT intptr_t CryptoNative_EvpAes128Cbc(void); -PALEXPORT intptr_t CryptoNative_EvpAes128Cfb8(void); -PALEXPORT intptr_t CryptoNative_EvpAes128Cfb128(void); -PALEXPORT intptr_t CryptoNative_EvpAes128Gcm(void); -PALEXPORT intptr_t CryptoNative_EvpAes128Ccm(void); -PALEXPORT intptr_t CryptoNative_EvpAes192Ecb(void); -PALEXPORT intptr_t CryptoNative_EvpAes192Cbc(void); -PALEXPORT intptr_t CryptoNative_EvpAes192Cfb8(void); -PALEXPORT intptr_t CryptoNative_EvpAes192Cfb128(void); -PALEXPORT intptr_t CryptoNative_EvpAes192Gcm(void); -PALEXPORT intptr_t CryptoNative_EvpAes192Ccm(void); -PALEXPORT intptr_t CryptoNative_EvpAes256Ecb(void); -PALEXPORT intptr_t CryptoNative_EvpAes256Cbc(void); -PALEXPORT intptr_t CryptoNative_EvpAes256Cfb8(void); -PALEXPORT intptr_t CryptoNative_EvpAes256Cfb128(void); -PALEXPORT intptr_t CryptoNative_EvpAes256Gcm(void); -PALEXPORT intptr_t CryptoNative_EvpAes256Ccm(void); -PALEXPORT intptr_t CryptoNative_EvpDes3Ecb(void); -PALEXPORT intptr_t CryptoNative_EvpDes3Cbc(void); -PALEXPORT intptr_t CryptoNative_EvpDes3Cfb8(void); -PALEXPORT intptr_t CryptoNative_EvpDes3Cfb64(void); -PALEXPORT intptr_t CryptoNative_EvpDesEcb(void); -PALEXPORT intptr_t CryptoNative_EvpDesCfb8(void); -PALEXPORT intptr_t CryptoNative_EvpDesCbc(void); -PALEXPORT intptr_t CryptoNative_EvpRC2Ecb(void); -PALEXPORT intptr_t CryptoNative_EvpRC2Cbc(void); 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 7bc479a79130c5..bb1a23e466ed85 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 @@ -606,8 +606,8 @@ - + 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 8c6ff39aa3f382..ec498d8bc1bea3 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 @@ -14,8 +14,6 @@ public sealed partial class AesCcm [MemberNotNull(nameof(_key))] private void ImportKey(ReadOnlySpan key) { - // OpenSSL does not allow setting nonce length after setting the key - // we need to store it as bytes instead _key = key.ToArray(); } @@ -29,21 +27,24 @@ private void EncryptInternal( // Convert key length to bits. using (SafeEvpCipherCtxHandle ctx = Interop.Crypto.EvpCipherCreatePartial(GetCipher(_key.Length * 8))) { - Interop.Crypto.CheckValidOpenSslHandle(ctx); + if (ctx.IsInvalid) + { + throw new CryptographicException(); + } if (!Interop.Crypto.EvpCipherSetTagLength(ctx, tag.Length)) { - throw Interop.Crypto.CreateOpenSslCryptographicException(); + throw new CryptographicException(); } - Interop.Crypto.EvpCipherSetCcmNonceLength(ctx, nonce.Length); + Interop.Crypto.EvpCipherSetNonceLength(ctx, nonce.Length); Interop.Crypto.EvpCipherSetKeyAndIV(ctx, _key, nonce, Interop.Crypto.EvpCipherDirection.Encrypt); if (associatedData.Length != 0) { if (!Interop.Crypto.EvpCipherUpdateAAD(ctx, associatedData)) { - throw Interop.Crypto.CreateOpenSslCryptographicException(); + throw new CryptographicException(); } } @@ -53,7 +54,7 @@ private void EncryptInternal( Span ciphertextAndTag = stackalloc byte[0]; // Arbitrary limit. const int StackAllocMax = 128; - if (ciphertext.Length + tag.Length <= StackAllocMax) + if (checked(ciphertext.Length + tag.Length) <= StackAllocMax) { ciphertextAndTag = stackalloc byte[ciphertext.Length + tag.Length]; } @@ -65,7 +66,7 @@ private void EncryptInternal( if (!Interop.Crypto.EvpCipherUpdate(ctx, ciphertextAndTag, out int ciphertextBytesWritten, plaintext)) { - throw Interop.Crypto.CreateOpenSslCryptographicException(); + throw new CryptographicException(); } if (!Interop.Crypto.EvpCipherFinalEx( @@ -73,7 +74,7 @@ private void EncryptInternal( ciphertextAndTag.Slice(ciphertextBytesWritten), out int bytesWritten)) { - throw Interop.Crypto.CreateOpenSslCryptographicException(); + throw new CryptographicException(); } ciphertextBytesWritten += bytesWritten; @@ -108,12 +109,15 @@ private void DecryptInternal( { using (SafeEvpCipherCtxHandle ctx = Interop.Crypto.EvpCipherCreatePartial(GetCipher(_key.Length * 8))) { - Interop.Crypto.CheckValidOpenSslHandle(ctx); - Interop.Crypto.EvpCipherSetCcmNonceLength(ctx, nonce.Length); + if (ctx.IsInvalid) + { + throw new CryptographicException(); + } + Interop.Crypto.EvpCipherSetNonceLength(ctx, nonce.Length); if (!Interop.Crypto.EvpCipherSetTagLength(ctx, tag.Length)) { - throw Interop.Crypto.CreateOpenSslCryptographicException(); + throw new CryptographicException(); } Interop.Crypto.EvpCipherSetKeyAndIV(ctx, _key, nonce, Interop.Crypto.EvpCipherDirection.Decrypt); @@ -122,7 +126,7 @@ private void DecryptInternal( { if (!Interop.Crypto.EvpCipherUpdateAAD(ctx, associatedData)) { - throw Interop.Crypto.CreateOpenSslCryptographicException(); + throw new CryptographicException(); } } @@ -135,7 +139,7 @@ private void DecryptInternal( if (!Interop.Crypto.EvpCipherUpdate(ctx, plaintext.Slice(plaintextBytesWritten), out int bytesWritten, tag)) { plaintext.Clear(); - throw Interop.Crypto.CreateOpenSslCryptographicException(); + throw new CryptographicException(SR.Cryptography_AuthTagMismatch); } plaintextBytesWritten += bytesWritten; 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 a4e12f368e8714..dfd33489cab3d0 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 @@ -23,7 +23,7 @@ private void ImportKey(ReadOnlySpan key) key, Span.Empty, Interop.Crypto.EvpCipherDirection.NoChange); - Interop.Crypto.EvpCipherSetGcmNonceLength(_ctxHandle, NonceSize); + Interop.Crypto.EvpCipherSetNonceLength(_ctxHandle, NonceSize); } private void EncryptInternal( @@ -59,7 +59,7 @@ private void EncryptInternal( Span ciphertextAndTag = stackalloc byte[0]; // Arbitrary limit. const int StackAllocMax = 128; - if (ciphertext.Length + tag.Length <= StackAllocMax) + if (checked(ciphertext.Length + tag.Length) <= StackAllocMax) { ciphertextAndTag = stackalloc byte[ciphertext.Length + tag.Length]; } From 4c6dbefb43bcae728eb7eb2656640b7a70ea6eb7 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 25 Feb 2021 15:03:10 -0800 Subject: [PATCH 12/14] Create a table of the cipher info instead of just using arbitrary pointer-sized values. --- .../pal_cipher.c | 187 +++++++----------- .../pal_cipher.h | 62 +++--- 2 files changed, 108 insertions(+), 141 deletions(-) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_cipher.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_cipher.c index 81fd8f6127179b..079a312d4c8f51 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_cipher.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_cipher.c @@ -4,124 +4,84 @@ #include "pal_cipher.h" #include "pal_utilities.h" -// just some unique IDs -intptr_t AndroidCryptoNative_Aes128Ecb() { return 1001; } -intptr_t AndroidCryptoNative_Aes128Cbc() { return 1002; } -intptr_t AndroidCryptoNative_Aes128Cfb8() { return 1003; } -intptr_t AndroidCryptoNative_Aes128Cfb128() { return 1004; } -intptr_t AndroidCryptoNative_Aes128Gcm() { return 1005; } -intptr_t AndroidCryptoNative_Aes128Ccm() { return 1006; } - -intptr_t AndroidCryptoNative_Aes192Ecb() { return 1007; } -intptr_t AndroidCryptoNative_Aes192Cbc() { return 1008; } -intptr_t AndroidCryptoNative_Aes192Cfb8() { return 1009; } -intptr_t AndroidCryptoNative_Aes192Cfb128() { return 1010; } -intptr_t AndroidCryptoNative_Aes192Gcm() { return 1011; } -intptr_t AndroidCryptoNative_Aes192Ccm() { return 1012; } - -intptr_t AndroidCryptoNative_Aes256Ecb() { return 1013; } -intptr_t AndroidCryptoNative_Aes256Cbc() { return 1014; } -intptr_t AndroidCryptoNative_Aes256Cfb8() { return 1015; } -intptr_t AndroidCryptoNative_Aes256Cfb128() { return 1016; } -intptr_t AndroidCryptoNative_Aes256Gcm() { return 1017; } -intptr_t AndroidCryptoNative_Aes256Ccm() { return 1018; } - -intptr_t AndroidCryptoNative_Des3Ecb() { return 1019; } -intptr_t AndroidCryptoNative_Des3Cbc() { return 1020; } -intptr_t AndroidCryptoNative_Des3Cfb8() { return 1021; } -intptr_t AndroidCryptoNative_Des3Cfb64() { return 1022; } - -intptr_t AndroidCryptoNative_DesEcb() { return 1023; } -intptr_t AndroidCryptoNative_DesCfb8() { return 1024; } -intptr_t AndroidCryptoNative_DesCbc() { return 1025; } - -intptr_t AndroidCryptoNative_RC2Ecb() { return 1026; } -intptr_t AndroidCryptoNative_RC2Cbc() { return 1027; } - -static int32_t GetAlgorithmWidth(intptr_t type) +typedef struct CipherInfo { - if (type == AndroidCryptoNative_Aes128Ecb()) return 128; - if (type == AndroidCryptoNative_Aes128Cbc()) return 128; - if (type == AndroidCryptoNative_Aes128Gcm()) return 128; - if (type == AndroidCryptoNative_Aes128Ccm()) return 128; - if (type == AndroidCryptoNative_Aes128Cfb8()) return 128; - if (type == AndroidCryptoNative_Aes128Cfb128()) return 128; - - if (type == AndroidCryptoNative_Aes192Ecb()) return 192; - if (type == AndroidCryptoNative_Aes192Cbc()) return 192; - if (type == AndroidCryptoNative_Aes192Gcm()) return 192; - if (type == AndroidCryptoNative_Aes192Ccm()) return 192; - if (type == AndroidCryptoNative_Aes192Cfb8()) return 192; - if (type == AndroidCryptoNative_Aes192Cfb128()) return 192; - - if (type == AndroidCryptoNative_Aes256Ecb()) return 256; - if (type == AndroidCryptoNative_Aes256Cbc()) return 256; - if (type == AndroidCryptoNative_Aes256Gcm()) return 256; - if (type == AndroidCryptoNative_Aes256Ccm()) return 256; - if (type == AndroidCryptoNative_Aes256Cfb8()) return 256; - if (type == AndroidCryptoNative_Aes256Cfb128()) return 256; - - if (type == AndroidCryptoNative_DesEcb()) return 56; - if (type == AndroidCryptoNative_DesCfb8()) return 56; - if (type == AndroidCryptoNative_DesCbc()) return 56; - - if (type == AndroidCryptoNative_Des3Ecb()) return 168; - if (type == AndroidCryptoNative_Des3Cbc()) return 168; - if (type == AndroidCryptoNative_Des3Cfb8()) return 168; - if (type == AndroidCryptoNative_Des3Cfb64()) return 168; - - assert(0 && "unexpected type"); - return FAIL; + bool isSupported; + bool hasTag; + int32_t width; + const char* name; +} CipherInfo; + +#define DEFINE_CIPHER(cipherId, width, javaName, hasTag) \ +CipherInfo* AndroidCryptoNative_ ## cipherId() \ +{ \ + static CipherInfo info = { true, hasTag, width, javaName }; \ + return &info; \ } -static jobject GetAlgorithmName(JNIEnv* env, intptr_t type) +#define DEFINE_UNSUPPORTED_CIPHER(cipherId) \ +CipherInfo* AndroidCryptoNative_ ## cipherId() \ +{ \ + static CipherInfo info = { false, false, 0, NULL }; \ + return &info; \ +} + +DEFINE_CIPHER(Aes128Ecb, 128, "AES/ECB/NoPadding", false) +DEFINE_CIPHER(Aes128Cbc, 128, "AES/CBC/NoPadding", false) +DEFINE_CIPHER(Aes128Cfb8, 128, "AES/CFB/NoPadding", false) +DEFINE_CIPHER(Aes128Cfb128, 128, "AES/CFB128/NoPadding", false) +DEFINE_CIPHER(Aes128Gcm, 128, "AES/GCM/NoPadding", true) +DEFINE_CIPHER(Aes128Ccm, 128, "AES/CCM/NoPadding", true) +DEFINE_CIPHER(Aes192Ecb, 192, "AES/ECB/NoPadding", false) +DEFINE_CIPHER(Aes192Cbc, 192, "AES/CBC/NoPadding", false) +DEFINE_CIPHER(Aes192Cfb8, 192, "AES/CFB/NoPadding", false) +DEFINE_CIPHER(Aes192Cfb128, 192, "AES/CFB128/NoPadding", false) +DEFINE_CIPHER(Aes192Gcm, 192, "AES/GCM/NoPadding", true) +DEFINE_CIPHER(Aes192Ccm, 192, "AES/CCM/NoPadding", true) +DEFINE_CIPHER(Aes256Ecb, 256, "AES/ECB/NoPadding", false) +DEFINE_CIPHER(Aes256Cbc, 256, "AES/CBC/NoPadding", false) +DEFINE_CIPHER(Aes256Cfb8, 256, "AES/CFB/NoPadding", false) +DEFINE_CIPHER(Aes256Cfb128, 256, "AES/CFB128/NoPadding", false) +DEFINE_CIPHER(Aes256Gcm, 256, "AES/GCM/NoPadding", true) +DEFINE_CIPHER(Aes256Ccm, 256, "AES/CCM/NoPadding", true) +DEFINE_CIPHER(DesEcb, 56, "DES/ECB/NoPadding", false) +DEFINE_CIPHER(DesCbc, 56, "DES/CBC/NoPadding", false) +DEFINE_CIPHER(DesCfb8, 56, "DES/CFB/NoPadding", false) +DEFINE_CIPHER(Des3Ecb, 168, "DESede/ECB/NoPadding", false) +DEFINE_CIPHER(Des3Cbc, 168, "DESede/CBC/NoPadding", false) +DEFINE_CIPHER(Des3Cfb8, 168, "DESede/CFB/NoPadding", false) +DEFINE_CIPHER(Des3Cfb64, 168, "DESede/CFB/NoPadding", false) +DEFINE_UNSUPPORTED_CIPHER(RC2Ecb) +DEFINE_UNSUPPORTED_CIPHER(RC2Cbc) + + +static int32_t GetAlgorithmWidth(CipherInfo* type) +{ + if (!type->isSupported) + { + assert(false); + return FAIL; + } + return type->width; +} + +static jobject GetAlgorithmName(JNIEnv* env, CipherInfo* type) { - if (type == AndroidCryptoNative_Aes128Ecb()) return JSTRING("AES/ECB/NoPadding"); - if (type == AndroidCryptoNative_Aes128Cbc()) return JSTRING("AES/CBC/NoPadding"); - if (type == AndroidCryptoNative_Aes128Gcm()) return JSTRING("AES/GCM/NoPadding"); - if (type == AndroidCryptoNative_Aes128Ccm()) return JSTRING("AES/CCM/NoPadding"); - if (type == AndroidCryptoNative_Aes128Cfb8()) return JSTRING("AES/CFB/NoPadding"); - - if (type == AndroidCryptoNative_Aes192Ecb()) return JSTRING("AES/ECB/NoPadding"); - if (type == AndroidCryptoNative_Aes192Cbc()) return JSTRING("AES/CBC/NoPadding"); - if (type == AndroidCryptoNative_Aes192Gcm()) return JSTRING("AES/GCM/NoPadding"); - if (type == AndroidCryptoNative_Aes192Ccm()) return JSTRING("AES/CCM/NoPadding"); - if (type == AndroidCryptoNative_Aes192Cfb8()) return JSTRING("AES/CFB/NoPadding"); - - if (type == AndroidCryptoNative_Aes256Ecb()) return JSTRING("AES/ECB/NoPadding"); - if (type == AndroidCryptoNative_Aes256Cbc()) return JSTRING("AES/CBC/NoPadding"); - if (type == AndroidCryptoNative_Aes256Gcm()) return JSTRING("AES/GCM/NoPadding"); - if (type == AndroidCryptoNative_Aes256Ccm()) return JSTRING("AES/CCM/NoPadding"); - if (type == AndroidCryptoNative_Aes256Cfb8()) return JSTRING("AES/CFB/NoPadding"); - - if (type == AndroidCryptoNative_DesEcb()) return JSTRING("DES/ECB/NoPadding"); - if (type == AndroidCryptoNative_DesCfb8()) return JSTRING("DES/CFB/NoPadding"); - if (type == AndroidCryptoNative_DesCbc()) return JSTRING("DES/CBC/NoPadding"); - - if (type == AndroidCryptoNative_Des3Ecb()) return JSTRING("DESede/ECB/NoPadding"); - if (type == AndroidCryptoNative_Des3Cbc()) return JSTRING("DESede/CBC/NoPadding"); - if (type == AndroidCryptoNative_Des3Cfb8()) return JSTRING("DESede/CFB/NoPadding"); - if (type == AndroidCryptoNative_Des3Cfb64()) return JSTRING("DESede/CFB/NoPadding"); - - if (type == AndroidCryptoNative_Aes128Cfb128()) return JSTRING("AES/CFB128/NoPadding"); - if (type == AndroidCryptoNative_Aes192Cfb128()) return JSTRING("AES/CFB128/NoPadding"); - if (type == AndroidCryptoNative_Aes256Cfb128()) return JSTRING("AES/CFB128/NoPadding"); - - LOG_ERROR("This algorithm (%ld) is not supported", (long)type); - return FAIL; + if (!type->isSupported) + { + LOG_ERROR("This cipher is not supported"); + assert(false); + return FAIL; + } + return JSTRING(type->name); } -static bool HasTag(intptr_t type) +static bool HasTag(CipherInfo* type) { - return (type == AndroidCryptoNative_Aes128Gcm()) || - (type == AndroidCryptoNative_Aes128Ccm()) || - (type == AndroidCryptoNative_Aes192Gcm()) || - (type == AndroidCryptoNative_Aes192Ccm()) || - (type == AndroidCryptoNative_Aes256Gcm()) || - (type == AndroidCryptoNative_Aes256Ccm()); + return type->hasTag; } -CipherCtx* AndroidCryptoNative_CipherCreatePartial(intptr_t type) +CipherCtx* AndroidCryptoNative_CipherCreatePartial(CipherInfo* type) { JNIEnv* env = GetJNIEnv(); jobject algName = GetAlgorithmName(env, type); @@ -131,6 +91,11 @@ CipherCtx* AndroidCryptoNative_CipherCreatePartial(intptr_t type) jobject cipher = ToGRef(env, (*env)->CallStaticObjectMethod(env, g_cipherClass, g_cipherGetInstanceMethod, algName)); (*env)->DeleteLocalRef(env, algName); + if (CheckJNIExceptions(env)) + { + return FAIL; + } + CipherCtx* ctx = malloc(sizeof(CipherCtx)); ctx->cipher = cipher; ctx->type = type; @@ -139,7 +104,7 @@ CipherCtx* AndroidCryptoNative_CipherCreatePartial(intptr_t type) ctx->encMode = 0; ctx->key = NULL; ctx->iv = NULL; - return CheckJNIExceptions(env) ? FAIL : ctx; + return ctx; } int32_t AndroidCryptoNative_CipherSetTagLength(CipherCtx* ctx, int32_t tagLength) @@ -223,7 +188,7 @@ int32_t AndroidCryptoNative_CipherSetKeyAndIV(CipherCtx* ctx, uint8_t* key, uint return ReinitializeCipher(ctx); } -CipherCtx* AndroidCryptoNative_CipherCreate2(intptr_t type, uint8_t* key, int32_t keyLength, int32_t effectiveKeyLength, uint8_t* iv, int32_t enc) +CipherCtx* AndroidCryptoNative_CipherCreate2(CipherInfo* type, uint8_t* key, int32_t keyLength, int32_t effectiveKeyLength, uint8_t* iv, int32_t enc) { if (effectiveKeyLength != 0) { diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_cipher.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_cipher.h index 4ae28eb266a00e..9c14f317ab23de 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_cipher.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_cipher.h @@ -10,10 +10,12 @@ #define CIPHER_ENCRYPT_MODE 1 #define CIPHER_DECRYPT_MODE 2 +typedef struct CipherInfo CipherInfo; + typedef struct CipherCtx { jobject cipher; - intptr_t type; + CipherInfo* type; int32_t ivLength; int32_t tagLength; int32_t encMode; @@ -21,8 +23,8 @@ typedef struct CipherCtx uint8_t* iv; } CipherCtx; -PALEXPORT CipherCtx* AndroidCryptoNative_CipherCreate2(intptr_t type, uint8_t* key, int32_t keyLength, int32_t effectiveKeyLength, uint8_t* iv, int32_t enc); -PALEXPORT CipherCtx* AndroidCryptoNative_CipherCreatePartial(intptr_t type); +PALEXPORT CipherCtx* AndroidCryptoNative_CipherCreate2(CipherInfo* type, uint8_t* key, int32_t keyLength, int32_t effectiveKeyLength, uint8_t* iv, int32_t enc); +PALEXPORT CipherCtx* AndroidCryptoNative_CipherCreatePartial(CipherInfo* type); PALEXPORT int32_t AndroidCryptoNative_CipherSetTagLength(CipherCtx* ctx, int32_t tagLength); PALEXPORT int32_t AndroidCryptoNative_CipherSetKeyAndIV(CipherCtx* ctx, uint8_t* key, uint8_t* iv, int32_t enc); PALEXPORT int32_t AndroidCryptoNative_CipherSetNonceLength(CipherCtx* ctx, int32_t ivLength); @@ -32,30 +34,30 @@ PALEXPORT int32_t AndroidCryptoNative_CipherCtxSetPadding(CipherCtx* ctx, int32_ PALEXPORT int32_t AndroidCryptoNative_CipherUpdateAAD(CipherCtx* ctx, uint8_t* in, int32_t inl); PALEXPORT int32_t AndroidCryptoNative_CipherUpdate(CipherCtx* ctx, uint8_t* out, int32_t* outl, uint8_t* in, int32_t inl); PALEXPORT int32_t AndroidCryptoNative_CipherFinalEx(CipherCtx* ctx, uint8_t* outm, int32_t* outl); -PALEXPORT intptr_t AndroidCryptoNative_Aes128Ecb(void); -PALEXPORT intptr_t AndroidCryptoNative_Aes128Cbc(void); -PALEXPORT intptr_t AndroidCryptoNative_Aes128Cfb8(void); -PALEXPORT intptr_t AndroidCryptoNative_Aes128Cfb128(void); -PALEXPORT intptr_t AndroidCryptoNative_Aes128Gcm(void); -PALEXPORT intptr_t AndroidCryptoNative_Aes128Ccm(void); -PALEXPORT intptr_t AndroidCryptoNative_Aes192Ecb(void); -PALEXPORT intptr_t AndroidCryptoNative_Aes192Cbc(void); -PALEXPORT intptr_t AndroidCryptoNative_Aes192Cfb8(void); -PALEXPORT intptr_t AndroidCryptoNative_Aes192Cfb128(void); -PALEXPORT intptr_t AndroidCryptoNative_Aes192Gcm(void); -PALEXPORT intptr_t AndroidCryptoNative_Aes192Ccm(void); -PALEXPORT intptr_t AndroidCryptoNative_Aes256Ecb(void); -PALEXPORT intptr_t AndroidCryptoNative_Aes256Cbc(void); -PALEXPORT intptr_t AndroidCryptoNative_Aes256Cfb8(void); -PALEXPORT intptr_t AndroidCryptoNative_Aes256Cfb128(void); -PALEXPORT intptr_t AndroidCryptoNative_Aes256Gcm(void); -PALEXPORT intptr_t AndroidCryptoNative_Aes256Ccm(void); -PALEXPORT intptr_t AndroidCryptoNative_Des3Ecb(void); -PALEXPORT intptr_t AndroidCryptoNative_Des3Cbc(void); -PALEXPORT intptr_t AndroidCryptoNative_Des3Cfb8(void); -PALEXPORT intptr_t AndroidCryptoNative_Des3Cfb64(void); -PALEXPORT intptr_t AndroidCryptoNative_DesEcb(void); -PALEXPORT intptr_t AndroidCryptoNative_DesCfb8(void); -PALEXPORT intptr_t AndroidCryptoNative_DesCbc(void); -PALEXPORT intptr_t AndroidCryptoNative_RC2Ecb(void); -PALEXPORT intptr_t AndroidCryptoNative_RC2Cbc(void); +PALEXPORT CipherInfo* AndroidCryptoNative_Aes128Ecb(void); +PALEXPORT CipherInfo* AndroidCryptoNative_Aes128Cbc(void); +PALEXPORT CipherInfo* AndroidCryptoNative_Aes128Cfb8(void); +PALEXPORT CipherInfo* AndroidCryptoNative_Aes128Cfb128(void); +PALEXPORT CipherInfo* AndroidCryptoNative_Aes128Gcm(void); +PALEXPORT CipherInfo* AndroidCryptoNative_Aes128Ccm(void); +PALEXPORT CipherInfo* AndroidCryptoNative_Aes192Ecb(void); +PALEXPORT CipherInfo* AndroidCryptoNative_Aes192Cbc(void); +PALEXPORT CipherInfo* AndroidCryptoNative_Aes192Cfb8(void); +PALEXPORT CipherInfo* AndroidCryptoNative_Aes192Cfb128(void); +PALEXPORT CipherInfo* AndroidCryptoNative_Aes192Gcm(void); +PALEXPORT CipherInfo* AndroidCryptoNative_Aes192Ccm(void); +PALEXPORT CipherInfo* AndroidCryptoNative_Aes256Ecb(void); +PALEXPORT CipherInfo* AndroidCryptoNative_Aes256Cbc(void); +PALEXPORT CipherInfo* AndroidCryptoNative_Aes256Cfb8(void); +PALEXPORT CipherInfo* AndroidCryptoNative_Aes256Cfb128(void); +PALEXPORT CipherInfo* AndroidCryptoNative_Aes256Gcm(void); +PALEXPORT CipherInfo* AndroidCryptoNative_Aes256Ccm(void); +PALEXPORT CipherInfo* AndroidCryptoNative_Des3Ecb(void); +PALEXPORT CipherInfo* AndroidCryptoNative_Des3Cbc(void); +PALEXPORT CipherInfo* AndroidCryptoNative_Des3Cfb8(void); +PALEXPORT CipherInfo* AndroidCryptoNative_Des3Cfb64(void); +PALEXPORT CipherInfo* AndroidCryptoNative_DesEcb(void); +PALEXPORT CipherInfo* AndroidCryptoNative_DesCfb8(void); +PALEXPORT CipherInfo* AndroidCryptoNative_DesCbc(void); +PALEXPORT CipherInfo* AndroidCryptoNative_RC2Ecb(void); +PALEXPORT CipherInfo* AndroidCryptoNative_RC2Cbc(void); From 09173acc913cb68bc83ee78854eff50c8a4e2597 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 26 Feb 2021 10:10:30 -0800 Subject: [PATCH 13/14] PR feedback. --- .../Interop.Cipher.cs | 21 ++++++++------ .../pal_cipher.c | 2 +- .../pal_cipher.h | 2 +- .../Security/Cryptography/AesCcm.Android.cs | 20 ++++++------- .../Security/Cryptography/AesGcm.Android.cs | 28 ++++++++++--------- 5 files changed, 39 insertions(+), 34 deletions(-) diff --git a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Cipher.cs b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Cipher.cs index 4a4f52ed86d8d8..a00ecd9b6fbc05 100644 --- a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Cipher.cs +++ b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Cipher.cs @@ -9,7 +9,7 @@ internal static partial class Interop { internal static partial class Crypto { - [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_CipherCreate2")] + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_CipherCreate")] internal static extern SafeEvpCipherCtxHandle EvpCipherCreate( IntPtr cipher, ref byte key, @@ -42,7 +42,7 @@ ref MemoryMarshal.GetReference(key), ref MemoryMarshal.GetReference(iv), direction)) { - throw CreateOpenSslCryptographicException(); + throw new CryptographicException(); } } @@ -51,11 +51,11 @@ ref MemoryMarshal.GetReference(iv), private static extern bool AndroidCryptoNative_CipherSetNonceLength( SafeEvpCipherCtxHandle ctx, int nonceLength); - internal static void EvpCipherSetNonceLength(SafeEvpCipherCtxHandle ctx, int nonceLength) + internal static void CipherSetNonceLength(SafeEvpCipherCtxHandle ctx, int nonceLength) { if (!AndroidCryptoNative_CipherSetNonceLength(ctx, nonceLength)) { - throw CreateOpenSslCryptographicException(); + throw new CryptographicException(); } } @@ -95,19 +95,22 @@ ref MemoryMarshal.GetReference(input), [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_CipherUpdateAAD")] [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool EvpCipherUpdateAAD( + private static extern bool CipherUpdateAAD( SafeEvpCipherCtxHandle ctx, ref byte @in, int inl); - internal static bool EvpCipherUpdateAAD( + internal static void CipherUpdateAAD( SafeEvpCipherCtxHandle ctx, ReadOnlySpan input) { - return EvpCipherUpdateAAD( + if(!CipherUpdateAAD( ctx, ref MemoryMarshal.GetReference(input), - input.Length); + input.Length)) + { + throw new CryptographicException(); + } } [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_CipherFinalEx")] @@ -127,7 +130,7 @@ internal static bool EvpCipherFinalEx( [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_CipherSetTagLength")] [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool EvpCipherSetTagLength( + internal static extern bool CipherSetTagLength( SafeEvpCipherCtxHandle ctx, int tagLength); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_cipher.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_cipher.c index 079a312d4c8f51..d07b47d6d2647f 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_cipher.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_cipher.c @@ -188,7 +188,7 @@ int32_t AndroidCryptoNative_CipherSetKeyAndIV(CipherCtx* ctx, uint8_t* key, uint return ReinitializeCipher(ctx); } -CipherCtx* AndroidCryptoNative_CipherCreate2(CipherInfo* type, uint8_t* key, int32_t keyLength, int32_t effectiveKeyLength, uint8_t* iv, int32_t enc) +CipherCtx* AndroidCryptoNative_CipherCreate(CipherInfo* type, uint8_t* key, int32_t keyLength, int32_t effectiveKeyLength, uint8_t* iv, int32_t enc) { if (effectiveKeyLength != 0) { diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_cipher.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_cipher.h index 9c14f317ab23de..bd7963a770f362 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_cipher.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_cipher.h @@ -23,7 +23,7 @@ typedef struct CipherCtx uint8_t* iv; } CipherCtx; -PALEXPORT CipherCtx* AndroidCryptoNative_CipherCreate2(CipherInfo* type, uint8_t* key, int32_t keyLength, int32_t effectiveKeyLength, uint8_t* iv, int32_t enc); +PALEXPORT CipherCtx* AndroidCryptoNative_CipherCreate(CipherInfo* type, uint8_t* key, int32_t keyLength, int32_t effectiveKeyLength, uint8_t* iv, int32_t enc); PALEXPORT CipherCtx* AndroidCryptoNative_CipherCreatePartial(CipherInfo* type); PALEXPORT int32_t AndroidCryptoNative_CipherSetTagLength(CipherCtx* ctx, int32_t tagLength); PALEXPORT int32_t AndroidCryptoNative_CipherSetKeyAndIV(CipherCtx* ctx, uint8_t* key, uint8_t* iv, int32_t enc); 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 ec498d8bc1bea3..c1881d7a359e76 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 @@ -32,17 +32,17 @@ private void EncryptInternal( throw new CryptographicException(); } - if (!Interop.Crypto.EvpCipherSetTagLength(ctx, tag.Length)) + if (!Interop.Crypto.CipherSetTagLength(ctx, tag.Length)) { throw new CryptographicException(); } - Interop.Crypto.EvpCipherSetNonceLength(ctx, nonce.Length); + Interop.Crypto.CipherSetNonceLength(ctx, nonce.Length); Interop.Crypto.EvpCipherSetKeyAndIV(ctx, _key, nonce, Interop.Crypto.EvpCipherDirection.Encrypt); if (associatedData.Length != 0) { - if (!Interop.Crypto.EvpCipherUpdateAAD(ctx, associatedData)) + if (!Interop.Crypto.CipherUpdateAAD(ctx, associatedData)) { throw new CryptographicException(); } @@ -113,9 +113,9 @@ private void DecryptInternal( { throw new CryptographicException(); } - Interop.Crypto.EvpCipherSetNonceLength(ctx, nonce.Length); + Interop.Crypto.CipherSetNonceLength(ctx, nonce.Length); - if (!Interop.Crypto.EvpCipherSetTagLength(ctx, tag.Length)) + if (!Interop.Crypto.CipherSetTagLength(ctx, tag.Length)) { throw new CryptographicException(); } @@ -124,7 +124,7 @@ private void DecryptInternal( if (associatedData.Length != 0) { - if (!Interop.Crypto.EvpCipherUpdateAAD(ctx, associatedData)) + if (!Interop.Crypto.CipherUpdateAAD(ctx, associatedData)) { throw new CryptographicException(); } @@ -132,14 +132,14 @@ private void DecryptInternal( if (!Interop.Crypto.EvpCipherUpdate(ctx, plaintext, out int plaintextBytesWritten, ciphertext)) { - plaintext.Clear(); - throw new CryptographicException(SR.Cryptography_AuthTagMismatch); + CryptographicOperations.ZeroMemory(plaintext); + throw new CryptographicException(); } if (!Interop.Crypto.EvpCipherUpdate(ctx, plaintext.Slice(plaintextBytesWritten), out int bytesWritten, tag)) { - plaintext.Clear(); - throw new CryptographicException(SR.Cryptography_AuthTagMismatch); + CryptographicOperations.ZeroMemory(plaintext); + throw new CryptographicException(); } plaintextBytesWritten += bytesWritten; 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 dfd33489cab3d0..74bb6755726763 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 @@ -23,7 +23,7 @@ private void ImportKey(ReadOnlySpan key) key, Span.Empty, Interop.Crypto.EvpCipherDirection.NoChange); - Interop.Crypto.EvpCipherSetNonceLength(_ctxHandle, NonceSize); + Interop.Crypto.CipherSetNonceLength(_ctxHandle, NonceSize); } private void EncryptInternal( @@ -34,9 +34,9 @@ private void EncryptInternal( ReadOnlySpan associatedData = default) { - if (!Interop.Crypto.EvpCipherSetTagLength(_ctxHandle, tag.Length)) + if (!Interop.Crypto.CipherSetTagLength(_ctxHandle, tag.Length)) { - throw Interop.Crypto.CreateOpenSslCryptographicException(); + throw new CryptographicException(); } Interop.Crypto.EvpCipherSetKeyAndIV( @@ -47,9 +47,9 @@ private void EncryptInternal( if (associatedData.Length != 0) { - if (!Interop.Crypto.EvpCipherUpdateAAD(_ctxHandle, associatedData)) + if (!Interop.Crypto.CipherUpdateAAD(_ctxHandle, associatedData)) { - throw Interop.Crypto.CreateOpenSslCryptographicException(); + throw new CryptographicException(); } } @@ -71,7 +71,7 @@ private void EncryptInternal( if (!Interop.Crypto.EvpCipherUpdate(_ctxHandle, ciphertextAndTag, out int ciphertextBytesWritten, plaintext)) { - throw Interop.Crypto.CreateOpenSslCryptographicException(); + throw new CryptographicException(); } if (!Interop.Crypto.EvpCipherFinalEx( @@ -79,7 +79,7 @@ private void EncryptInternal( ciphertextAndTag.Slice(ciphertextBytesWritten), out int bytesWritten)) { - throw Interop.Crypto.CreateOpenSslCryptographicException(); + throw new CryptographicException(); } ciphertextBytesWritten += bytesWritten; @@ -111,9 +111,9 @@ private void DecryptInternal( Span plaintext, ReadOnlySpan associatedData) { - if (!Interop.Crypto.EvpCipherSetTagLength(_ctxHandle, tag.Length)) + if (!Interop.Crypto.CipherSetTagLength(_ctxHandle, tag.Length)) { - throw Interop.Crypto.CreateOpenSslCryptographicException(); + throw new CryptographicException(); } Interop.Crypto.EvpCipherSetKeyAndIV( @@ -124,20 +124,22 @@ private void DecryptInternal( if (associatedData.Length != 0) { - if (!Interop.Crypto.EvpCipherUpdateAAD(_ctxHandle, associatedData)) + if (!Interop.Crypto.CipherUpdateAAD(_ctxHandle, associatedData)) { - throw Interop.Crypto.CreateOpenSslCryptographicException(); + throw new CryptographicException(); } } if (!Interop.Crypto.EvpCipherUpdate(_ctxHandle, plaintext, out int plaintextBytesWritten, ciphertext)) { - throw Interop.Crypto.CreateOpenSslCryptographicException(); + CryptographicOperations.ZeroMemory(plaintext); + throw new CryptographicException(); } if (!Interop.Crypto.EvpCipherUpdate(_ctxHandle, plaintext.Slice(plaintextBytesWritten), out int bytesWritten, tag)) { - throw Interop.Crypto.CreateOpenSslCryptographicException(); + CryptographicOperations.ZeroMemory(plaintext); + throw new CryptographicException(); } plaintextBytesWritten += bytesWritten; From 7684187d0529f22864e1abd192c1d91dc4c2d75c Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 26 Feb 2021 10:38:18 -0800 Subject: [PATCH 14/14] Fixup some missed updates. --- .../Interop.Cipher.cs | 3 ++- .../src/System/Security/Cryptography/AesCcm.Android.cs | 10 ++-------- .../src/System/Security/Cryptography/AesGcm.Android.cs | 10 ++-------- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Cipher.cs b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Cipher.cs index a00ecd9b6fbc05..23caee885edf4c 100644 --- a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Cipher.cs +++ b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Cipher.cs @@ -3,6 +3,7 @@ using System; using System.Runtime.InteropServices; +using System.Security.Cryptography; using Microsoft.Win32.SafeHandles; internal static partial class Interop @@ -104,7 +105,7 @@ internal static void CipherUpdateAAD( SafeEvpCipherCtxHandle ctx, ReadOnlySpan input) { - if(!CipherUpdateAAD( + if (!CipherUpdateAAD( ctx, ref MemoryMarshal.GetReference(input), input.Length)) 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 c1881d7a359e76..d6c5b9726dbaa9 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 @@ -42,10 +42,7 @@ private void EncryptInternal( if (associatedData.Length != 0) { - if (!Interop.Crypto.CipherUpdateAAD(ctx, associatedData)) - { - throw new CryptographicException(); - } + Interop.Crypto.CipherUpdateAAD(ctx, associatedData); } byte[]? rented = null; @@ -124,10 +121,7 @@ private void DecryptInternal( if (associatedData.Length != 0) { - if (!Interop.Crypto.CipherUpdateAAD(ctx, associatedData)) - { - throw new CryptographicException(); - } + Interop.Crypto.CipherUpdateAAD(ctx, associatedData); } if (!Interop.Crypto.EvpCipherUpdate(ctx, plaintext, out int plaintextBytesWritten, ciphertext)) 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 74bb6755726763..fd6b898db67c7f 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 @@ -47,10 +47,7 @@ private void EncryptInternal( if (associatedData.Length != 0) { - if (!Interop.Crypto.CipherUpdateAAD(_ctxHandle, associatedData)) - { - throw new CryptographicException(); - } + Interop.Crypto.CipherUpdateAAD(_ctxHandle, associatedData); } byte[]? rented = null; @@ -124,10 +121,7 @@ private void DecryptInternal( if (associatedData.Length != 0) { - if (!Interop.Crypto.CipherUpdateAAD(_ctxHandle, associatedData)) - { - throw new CryptographicException(); - } + Interop.Crypto.CipherUpdateAAD(_ctxHandle, associatedData); } if (!Interop.Crypto.EvpCipherUpdate(_ctxHandle, plaintext, out int plaintextBytesWritten, ciphertext))