From 20affef0b48de38112d132bfd981089549e307d9 Mon Sep 17 00:00:00 2001 From: Kostas Tsiounis Date: Thu, 24 Aug 2023 16:31:30 -0400 Subject: [PATCH] Optimize AES/GCM cipher and IV init and improve array cleanup code The cipher initialization cost has been found to be computationally expensive, especially in the OpenSSL 3.x API. In order to optimize the cost of using OpenSSL to perform consecutive AES/GCM encryption and decryption operations, two flags are set and passed as parameters. The first flag indicates whether a different cipher is required for the upcoming operations (i.e., the key size has changed). If that is the case, a cipher is initialized and set to the provided context. If not, those steps are omitted, thus reducing the time required for the operation. The second flag indicates whether the IV length has changed and is used in a similar way as the first flag. The rest of the steps required for the operation are performed regardless of the flags. The handling of freeing arrays that were part of that functionality is, also, updated to comply with the newer approach to cleanup code. Signed-off by: Kostas Tsiounis --- .../provider/NativeGaloisCounterMode.java | 102 +++- .../jdk/crypto/jniprovider/NativeCrypto.java | 16 +- .../share/native/libjncrypto/NativeCrypto.c | 451 +++++++----------- 3 files changed, 265 insertions(+), 304 deletions(-) diff --git a/closed/src/java.base/share/classes/com/sun/crypto/provider/NativeGaloisCounterMode.java b/closed/src/java.base/share/classes/com/sun/crypto/provider/NativeGaloisCounterMode.java index a4826ddc168..4d568995bff 100644 --- a/closed/src/java.base/share/classes/com/sun/crypto/provider/NativeGaloisCounterMode.java +++ b/closed/src/java.base/share/classes/com/sun/crypto/provider/NativeGaloisCounterMode.java @@ -24,20 +24,26 @@ */ /* * =========================================================================== - * (c) Copyright IBM Corp. 2018, 2021 All Rights Reserved + * (c) Copyright IBM Corp. 2018, 2023 All Rights Reserved * =========================================================================== */ package com.sun.crypto.provider; -import java.util.Arrays; -import java.io.*; -import java.security.*; -import javax.crypto.*; -import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE; import com.sun.crypto.provider.AESCrypt; +import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE; + +import java.io.ByteArrayOutputStream; +import java.lang.ref.Cleaner; +import java.security.InvalidKeyException; +import java.security.ProviderException; + +import javax.crypto.AEADBadTagException; +import javax.crypto.ShortBufferException; +import javax.crypto.IllegalBlockSizeException; import jdk.crypto.jniprovider.NativeCrypto; +import jdk.internal.ref.CleanerFactory; /** * This class represents ciphers in GaloisCounter (GCM) mode. @@ -54,9 +60,11 @@ */ final class NativeGaloisCounterMode extends FeedbackCipher { + private static final byte[] EMPTY_BUF = new byte[0]; + private byte[] key; private boolean decrypting; - private static final byte[] emptyAAD = new byte[0]; + private final long context; static int DEFAULT_TAG_LEN = AES_BLOCK_SIZE; static int DEFAULT_IV_LEN = 12; // in bytes @@ -92,10 +100,34 @@ final class NativeGaloisCounterMode extends FeedbackCipher { private byte[] ibufferSave = null; private byte[] ibufferSave_enc = null; - private static NativeCrypto nativeCrypto; + private byte[] lastKey = EMPTY_BUF; + private byte[] lastIv = EMPTY_BUF; + + private boolean newIVLen; + private boolean newKeyLen; + + private static final NativeCrypto nativeCrypto = NativeCrypto.getNativeCrypto(); + private static final Cleaner contextCleaner = CleanerFactory.cleaner(); + + private static final class GCMCleanerRunnable implements Runnable { + private final long nativeContext; + + public GCMCleanerRunnable(long nativeContext) { + this.nativeContext = nativeContext; + } - static { - nativeCrypto = NativeCrypto.getNativeCrypto(); + @Override + public void run() { + /* + * Release the GCM context. + */ + synchronized (NativeGaloisCounterMode.class) { + long ret = nativeCrypto.DestroyContext(nativeContext); + if (ret == -1) { + throw new ProviderException("Error in destroying context in NativeGaloisCounterMode."); + } + } + } } private static void checkDataLength(int processed, int len) { @@ -111,6 +143,12 @@ private static void checkDataLength(int processed, int len) { NativeGaloisCounterMode(SymmetricCipher embeddedCipher) { super(embeddedCipher); aadBuffer = new ByteArrayOutputStream(); + + context = nativeCrypto.CreateContext(); + if (context == -1) { + throw new ProviderException("Error in creating context for NativeGaloisCounterMode."); + } + contextCleaner.register(this, new GCMCleanerRunnable(context)); } /** @@ -244,6 +282,22 @@ void init(boolean decrypting, String algorithm, byte[] keyValue, } else { ibuffer_enc = new ByteArrayOutputStream(); } + + /* + * Check whether cipher and IV need to be set, + * whether because something changed here or + * a call to set them in context hasn't been + * made yet. + */ + if (lastIv.length != this.iv.length) { + newIVLen = true; + } + if (lastKey.length != this.key.length) { + newKeyLen = true; + } + + lastKey = keyValue; + lastIv = iv; } } @@ -354,18 +408,26 @@ int encryptFinal(byte[] in, int inOfs, int len, byte[] out, int outOfs) len = in.length; ibuffer_enc.reset(); - byte[] aad = (((aadBuffer == null) || (aadBuffer.size() == 0)) ? emptyAAD : aadBuffer.toByteArray()); + byte[] aad = (((aadBuffer == null) || (aadBuffer.size() == 0)) ? EMPTY_BUF : aadBuffer.toByteArray()); - ret = nativeCrypto.GCMEncrypt(key, key.length, + ret = nativeCrypto.GCMEncrypt(context, + key, key.length, iv, iv.length, in, inOfs, len, out, outOfs, - aad, aad.length, localTagLenBytes); + aad, aad.length, + localTagLenBytes, + newIVLen, + newKeyLen); } if (ret == -1) { throw new ProviderException("Error in Native GaloisCounterMode"); } + /* Cipher and IV length were set, since call to GCMEncrypt succeeded. */ + newKeyLen = false; + newIVLen = false; + return (len + localTagLenBytes); } @@ -442,7 +504,7 @@ int decryptFinal(byte[] in, int inOfs, int len, } byte[] aad = (((aadBuffer == null) || (aadBuffer.size() == 0)) ? - emptyAAD : aadBuffer.toByteArray()); + EMPTY_BUF : aadBuffer.toByteArray()); aadBuffer = null; @@ -456,11 +518,15 @@ int decryptFinal(byte[] in, int inOfs, int len, len = in.length; ibuffer.reset(); - ret = nativeCrypto.GCMDecrypt(key, key.length, + ret = nativeCrypto.GCMDecrypt(context, + key, key.length, iv, iv.length, in, inOfs, len, out, outOfs, - aad, aad.length, localTagLenBytes); + aad, aad.length, + localTagLenBytes, + newIVLen, + newKeyLen); } if (ret == -2) { throw new AEADBadTagException("Tag mismatch!"); @@ -468,6 +534,10 @@ int decryptFinal(byte[] in, int inOfs, int len, throw new ProviderException("Error in Native GaloisCounterMode"); } + /* Cipher and IV length were set, since call to GCMDecrypt succeeded. */ + newKeyLen = false; + newIVLen = false; + return ret; } diff --git a/closed/src/java.base/share/classes/jdk/crypto/jniprovider/NativeCrypto.java b/closed/src/java.base/share/classes/jdk/crypto/jniprovider/NativeCrypto.java index 176c5e6289f..02e681f93c2 100644 --- a/closed/src/java.base/share/classes/jdk/crypto/jniprovider/NativeCrypto.java +++ b/closed/src/java.base/share/classes/jdk/crypto/jniprovider/NativeCrypto.java @@ -220,7 +220,7 @@ public final native int DigestComputeAndReset(long context, public final native int DigestReset(long context); - /* Native interfaces shared by CBC and ChaCha20 */ + /* Native interfaces shared by CBC, ChaCha20 and GCM. */ public final native long CreateContext(); @@ -252,7 +252,8 @@ public final native int CBCFinalEncrypt(long context, /* Native GCM interfaces */ - public final native int GCMEncrypt(byte[] key, + public final native int GCMEncrypt(long context, + byte[] key, int keylen, byte[] iv, int ivlen, @@ -263,9 +264,12 @@ public final native int GCMEncrypt(byte[] key, int outOffset, byte[] aad, int aadLen, - int tagLen); + int tagLen, + boolean newIVLen, + boolean newKeyLen); - public final native int GCMDecrypt(byte[] key, + public final native int GCMDecrypt(long context, + byte[] key, int keylen, byte[] iv, int ivlen, @@ -276,7 +280,9 @@ public final native int GCMDecrypt(byte[] key, int outOffset, byte[] aad, int aadLen, - int tagLen); + int tagLen, + boolean newIVLen, + boolean newKeyLen); /* Native RSA interfaces */ diff --git a/closed/src/java.base/share/native/libjncrypto/NativeCrypto.c b/closed/src/java.base/share/native/libjncrypto/NativeCrypto.c index 470143feec2..6bca26cab6b 100644 --- a/closed/src/java.base/share/native/libjncrypto/NativeCrypto.c +++ b/closed/src/java.base/share/native/libjncrypto/NativeCrypto.c @@ -50,6 +50,11 @@ #define OPENSSL_VERSION_3_0_0 OPENSSL_VERSION_CODE(3, 0, 0, 0) #define OPENSSL_VERSION_4_0_0 OPENSSL_VERSION_CODE(4, 0, 0, 0) +/* OpenSSL operation modes. */ +#define OPENSSL_ENCRYPTION_MODE 1 +#define OPENSSL_DECRYPTION_MODE 0 +#define OPENSSL_SAME_MODE (-1) + /* needed for OpenSSL 1.0.2 Thread handling routines */ # define CRYPTO_LOCK 1 @@ -1307,61 +1312,33 @@ int first_time_gcm = 0; * * Class: jdk_crypto_jniprovider_NativeCrypto * Method: GCMEncrypt - * Signature: ([BI[BI[BII[BI[BII)I + * Signature: (J[BI[BI[BII[BI[BIIZZ)I */ JNIEXPORT jint JNICALL Java_jdk_crypto_jniprovider_NativeCrypto_GCMEncrypt - (JNIEnv * env, jclass obj, jbyteArray key, jint keyLen, jbyteArray iv, jint ivLen, + (JNIEnv * env, jclass obj, jlong context, jbyteArray key, jint keyLen, jbyteArray iv, jint ivLen, jbyteArray input, jint inOffset, jint inLen, jbyteArray output, jint outOffset, - jbyteArray aad, jint aadLen, jint tagLen) { - - unsigned char* inputNative = NULL; - int len = 0, len_cipher = 0; - unsigned char* keyNative = NULL; - unsigned char* ivNative = NULL; - unsigned char* outputNative = NULL; - unsigned char* aadNative = NULL; - - EVP_CIPHER_CTX* ctx = NULL; - const EVP_CIPHER* evp_gcm_cipher = NULL; - - keyNative = (unsigned char*)((*env)->GetPrimitiveArrayCritical(env, key, 0)); - if (NULL == keyNative) { - return -1; - } - - ivNative = (unsigned char*)((*env)->GetPrimitiveArrayCritical(env, iv, 0)); - if (NULL == ivNative) { - (*env)->ReleasePrimitiveArrayCritical(env, key, keyNative, JNI_ABORT); - return -1; - } + jbyteArray aad, jint aadLen, jint tagLen, jboolean newIVLen, jboolean newKeyLen) +{ + jint ret = -1; - aadNative = (unsigned char*)((*env)->GetPrimitiveArrayCritical(env, aad, 0)); - if (NULL == aadNative) { - (*env)->ReleasePrimitiveArrayCritical(env, key, keyNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, iv, ivNative, JNI_ABORT); - return -1; - } + int len = 0; + int len_cipher = 0; + unsigned char *keyNative = NULL; + unsigned char *ivNative = NULL; + unsigned char *inputNative = NULL; + unsigned char *outputNative = NULL; + unsigned char *aadNative = NULL; - outputNative = (unsigned char*)((*env)->GetPrimitiveArrayCritical(env, output, 0)); - if (NULL == outputNative) { - (*env)->ReleasePrimitiveArrayCritical(env, key, keyNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, iv, ivNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, aad, aadNative, JNI_ABORT); - return -1; - } + EVP_CIPHER_CTX *ctx = (EVP_CIPHER_CTX *)(intptr_t) context; + const EVP_CIPHER *evp_gcm_cipher = NULL; - if (inLen > 0) { - inputNative = (unsigned char*)((*env)->GetPrimitiveArrayCritical(env, input, 0)); - if (NULL == inputNative) { - (*env)->ReleasePrimitiveArrayCritical(env, key, keyNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, iv, ivNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, aad, aadNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, output, outputNative, JNI_ABORT); - return -1; - } + if (NULL == ctx) { + printErrors(); + goto cleanup; } - switch(keyLen) { + if (newKeyLen) { + switch (keyLen) { case 16: evp_gcm_cipher = (*OSSL_aes_128_gcm)(); break; @@ -1373,196 +1350,133 @@ JNIEXPORT jint JNICALL Java_jdk_crypto_jniprovider_NativeCrypto_GCMEncrypt break; default: break; - } + } - ctx = (*OSSL_CIPHER_CTX_new)(); - if (NULL == ctx) { - printErrors(); - (*env)->ReleasePrimitiveArrayCritical(env, key, keyNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, iv, ivNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, aad, aadNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, output, outputNative, JNI_ABORT); - if (inLen > 0) { - (*env)->ReleasePrimitiveArrayCritical(env, input, inputNative, JNI_ABORT); + if (1 != (*OSSL_CipherInit_ex)(ctx, evp_gcm_cipher, NULL, NULL, NULL, OPENSSL_SAME_MODE)) { + printErrors(); + goto cleanup; } - return -1; } - if (1 != (*OSSL_CipherInit_ex)(ctx, evp_gcm_cipher, NULL, NULL, NULL, 1 )) { /* 1 - Encrypt mode 0 Decrypt Mode*/ - printErrors(); - (*OSSL_CIPHER_CTX_free)(ctx); - (*env)->ReleasePrimitiveArrayCritical(env, key, keyNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, iv, ivNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, aad, aadNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, output, outputNative, JNI_ABORT); - if (inLen > 0) { - (*env)->ReleasePrimitiveArrayCritical(env, input, inputNative, JNI_ABORT); + if (newIVLen) { + if (1 != (*OSSL_CIPHER_CTX_ctrl)(ctx, EVP_CTRL_GCM_SET_IVLEN, ivLen, NULL)) { + printErrors(); + goto cleanup; } - return -1; } - if (1 != (*OSSL_CIPHER_CTX_ctrl)(ctx, EVP_CTRL_GCM_SET_IVLEN, ivLen, NULL)) { - printErrors(); - (*OSSL_CIPHER_CTX_free)(ctx); - (*env)->ReleasePrimitiveArrayCritical(env, key, keyNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, iv, ivNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, aad, aadNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, output, outputNative, JNI_ABORT); - if (inLen > 0) { - (*env)->ReleasePrimitiveArrayCritical(env, input, inputNative, JNI_ABORT); - } - return -1; + /* Initialize context with key and IV. */ + keyNative = (unsigned char *)((*env)->GetPrimitiveArrayCritical(env, key, 0)); + if (NULL == keyNative) { + goto cleanup; } - if (1 != (*OSSL_CipherInit_ex)(ctx, NULL, NULL, keyNative, ivNative, -1)) { + ivNative = (unsigned char *)((*env)->GetPrimitiveArrayCritical(env, iv, 0)); + if (NULL == ivNative) { + goto cleanup; + } + + if (1 != (*OSSL_CipherInit_ex)(ctx, NULL, NULL, keyNative, ivNative, OPENSSL_ENCRYPTION_MODE)) { printErrors(); - (*OSSL_CIPHER_CTX_free)(ctx); - (*env)->ReleasePrimitiveArrayCritical(env, key, keyNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, iv, ivNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, aad, aadNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, output, outputNative, JNI_ABORT); - if (inLen > 0) { - (*env)->ReleasePrimitiveArrayCritical(env, input, inputNative, JNI_ABORT); - } - return -1; + goto cleanup; + } + + /* Provide AAD. */ + aadNative = (unsigned char *)((*env)->GetPrimitiveArrayCritical(env, aad, 0)); + if (NULL == aadNative) { + goto cleanup; } - /* provide AAD */ if (1 != (*OSSL_CipherUpdate)(ctx, NULL, &len, aadNative, aadLen)) { printErrors(); - (*OSSL_CIPHER_CTX_free)(ctx); - (*env)->ReleasePrimitiveArrayCritical(env, key, keyNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, iv, ivNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, aad, aadNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, output, outputNative, JNI_ABORT); - if (inLen > 0) { - (*env)->ReleasePrimitiveArrayCritical(env, input, inputNative, JNI_ABORT); - } - return -1; + goto cleanup; } - /* encrypt plaintext and obtain ciphertext */ + outputNative = (unsigned char *)((*env)->GetPrimitiveArrayCritical(env, output, 0)); + if (NULL == outputNative) { + goto cleanup; + } + + /* Encrypt plaintext, if available and obtain ciphertext. */ if (inLen > 0) { + inputNative = (unsigned char *)((*env)->GetPrimitiveArrayCritical(env, input, 0)); + if (NULL == inputNative) { + goto cleanup; + } + if (1 != (*OSSL_CipherUpdate)(ctx, outputNative + outOffset, &len, inputNative + inOffset, inLen)) { printErrors(); - (*OSSL_CIPHER_CTX_free)(ctx); - (*env)->ReleasePrimitiveArrayCritical(env, key, keyNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, iv, ivNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, aad, aadNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, output, outputNative, JNI_ABORT); - if (inLen > 0) { - (*env)->ReleasePrimitiveArrayCritical(env, input, inputNative, JNI_ABORT); - } - return -1; + goto cleanup; } len_cipher = len; } - /* finalize the encryption */ + /* Finalize the encryption. */ if (1 != (*OSSL_CipherFinal_ex)(ctx, outputNative + outOffset + len_cipher, &len)) { printErrors(); - (*OSSL_CIPHER_CTX_free)(ctx); - (*env)->ReleasePrimitiveArrayCritical(env, key, keyNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, iv, ivNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, aad, aadNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, output, outputNative, JNI_ABORT); - if (inLen > 0) { - (*env)->ReleasePrimitiveArrayCritical(env, input, inputNative, JNI_ABORT); - } - return -1; + goto cleanup; } + len_cipher += len; - /* Get the tag, place it at the end of the cipherText buffer */ - if (1 != (*OSSL_CIPHER_CTX_ctrl)(ctx, EVP_CTRL_GCM_GET_TAG, tagLen, outputNative + outOffset + len + len_cipher)) { + /* Get the tag, place it at the end of the cipherText buffer. */ + if (1 != (*OSSL_CIPHER_CTX_ctrl)(ctx, EVP_CTRL_GCM_GET_TAG, tagLen, outputNative + outOffset + len_cipher)) { printErrors(); - (*OSSL_CIPHER_CTX_free)(ctx); - (*env)->ReleasePrimitiveArrayCritical(env, key, keyNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, iv, ivNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, aad, aadNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, output, outputNative, JNI_ABORT); - if (inLen > 0) { - (*env)->ReleasePrimitiveArrayCritical(env, input, inputNative, JNI_ABORT); - } - return -1; + goto cleanup; } - (*OSSL_CIPHER_CTX_free)(ctx); + ret = (jint)len_cipher; - (*env)->ReleasePrimitiveArrayCritical(env, key, keyNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, iv, ivNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, output, outputNative, 0); - - if (inLen > 0) { +cleanup: + if (NULL != inputNative) { (*env)->ReleasePrimitiveArrayCritical(env, input, inputNative, JNI_ABORT); } + if (NULL != outputNative) { + (*env)->ReleasePrimitiveArrayCritical(env, output, outputNative, 0); + } + if (NULL != aadNative) { + (*env)->ReleasePrimitiveArrayCritical(env, aad, aadNative, JNI_ABORT); + } + if (NULL != ivNative) { + (*env)->ReleasePrimitiveArrayCritical(env, iv, ivNative, JNI_ABORT); + } + if (NULL != keyNative) { + (*env)->ReleasePrimitiveArrayCritical(env, key, keyNative, JNI_ABORT); + } - (*env)->ReleasePrimitiveArrayCritical(env, aad, aadNative, JNI_ABORT); - - return (jint)len_cipher; + return ret; } /* GCM Decryption * * Class: jdk_crypto_jniprovider_NativeCrypto * Method: GCMDecrypt - * Signature: ([BI[BI[BII[BI[BII)I + * Signature: (J[BI[BI[BII[BI[BIIZZ)I */ JNIEXPORT jint JNICALL Java_jdk_crypto_jniprovider_NativeCrypto_GCMDecrypt - (JNIEnv * env, jclass obj, jbyteArray key, jint keyLen, jbyteArray iv, jint ivLen, + (JNIEnv * env, jclass obj, jlong context, jbyteArray key, jint keyLen, jbyteArray iv, jint ivLen, jbyteArray input, jint inOffset, jint inLen, jbyteArray output, jint outOffset, - jbyteArray aad, jint aadLen, jint tagLen) { - - unsigned char* inputNative = NULL; - unsigned char* aadNative = NULL; - int ret = 0, len = 0, plaintext_len = 0; - unsigned char* keyNative = NULL; - unsigned char* ivNative = NULL; - unsigned char* outputNative = NULL; - EVP_CIPHER_CTX* ctx = NULL; - const EVP_CIPHER* evp_gcm_cipher = NULL; - - keyNative = (unsigned char*)((*env)->GetPrimitiveArrayCritical(env, key, 0)); - if (NULL == keyNative) { - return -1; - } - - ivNative = (unsigned char*)((*env)->GetPrimitiveArrayCritical(env, iv, 0)); - if (NULL == ivNative) { - (*env)->ReleasePrimitiveArrayCritical(env, key, keyNative, JNI_ABORT); - return -1; - } + jbyteArray aad, jint aadLen, jint tagLen, jboolean newIVLen, jboolean newKeyLen) +{ + jint ret = -1; - outputNative = (unsigned char*)((*env)->GetPrimitiveArrayCritical(env, output, 0)); - if (NULL == outputNative) { - (*env)->ReleasePrimitiveArrayCritical(env, key, keyNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, iv, ivNative, JNI_ABORT); - return -1; - } + int len = 0; + int plaintext_len = 0; + unsigned char *keyNative = NULL; + unsigned char *ivNative = NULL; + unsigned char *aadNative = NULL; + unsigned char *inputNative = NULL; + unsigned char *outputNative = NULL; - if (inLen > 0) { - inputNative = (unsigned char*)((*env)->GetPrimitiveArrayCritical(env, input, 0)); - if (NULL == inputNative) { - (*env)->ReleasePrimitiveArrayCritical(env, key, keyNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, iv, ivNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, output, outputNative, JNI_ABORT); - return -1; - } - } + EVP_CIPHER_CTX *ctx = (EVP_CIPHER_CTX *)(intptr_t) context; + const EVP_CIPHER *evp_gcm_cipher = NULL; - if (aadLen > 0) { - aadNative = (unsigned char*)((*env)->GetPrimitiveArrayCritical(env, aad, 0)); - if (NULL == aadNative) { - (*env)->ReleasePrimitiveArrayCritical(env, key, keyNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, iv, ivNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, output, outputNative, JNI_ABORT); - if (inLen > 0) { - (*env)->ReleasePrimitiveArrayCritical(env, input, inputNative, JNI_ABORT); - } - return -1; - } + if (NULL == ctx) { + printErrors(); + goto cleanup; } - switch(keyLen) { + if (newKeyLen) { + switch (keyLen) { case 16: evp_gcm_cipher = (*OSSL_aes_128_gcm)(); break; @@ -1574,131 +1488,102 @@ JNIEXPORT jint JNICALL Java_jdk_crypto_jniprovider_NativeCrypto_GCMDecrypt break; default: break; - } - - ctx = (*OSSL_CIPHER_CTX_new)(); - - if (1 != (*OSSL_CipherInit_ex)(ctx, evp_gcm_cipher, NULL, NULL, NULL, 0 )) { /* 1 - Encrypt mode 0 Decrypt Mode*/ - printErrors(); - (*OSSL_CIPHER_CTX_free)(ctx); - (*env)->ReleasePrimitiveArrayCritical(env, key, keyNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, iv, ivNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, output, outputNative, JNI_ABORT); - if (inLen > 0) { - (*env)->ReleasePrimitiveArrayCritical(env, input, inputNative, JNI_ABORT); } - if (aadLen > 0) { - (*env)->ReleasePrimitiveArrayCritical(env, aad, aadNative, JNI_ABORT); + + if (1 != (*OSSL_CipherInit_ex)(ctx, evp_gcm_cipher, NULL, NULL, NULL, OPENSSL_DECRYPTION_MODE)) { + printErrors(); + goto cleanup; } - return -1; } - if (1 != (*OSSL_CIPHER_CTX_ctrl)(ctx, EVP_CTRL_GCM_SET_IVLEN, ivLen, NULL)) { - printErrors(); - (*OSSL_CIPHER_CTX_free)(ctx); - (*env)->ReleasePrimitiveArrayCritical(env, key, keyNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, iv, ivNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, output, outputNative, JNI_ABORT); - if (inLen > 0) { - (*env)->ReleasePrimitiveArrayCritical(env, input, inputNative, JNI_ABORT); - } - if (aadLen > 0) { - (*env)->ReleasePrimitiveArrayCritical(env, aad, aadNative, JNI_ABORT); + if (newIVLen) { + if (1 != (*OSSL_CIPHER_CTX_ctrl)(ctx, EVP_CTRL_GCM_SET_IVLEN, ivLen, NULL)) { + printErrors(); + goto cleanup; } - return -1; } - /* Initialise key and IV */ + /* Initialise context with key and IV. */ + keyNative = (unsigned char *)((*env)->GetPrimitiveArrayCritical(env, key, 0)); + if (NULL == keyNative) { + goto cleanup; + } + + ivNative = (unsigned char *)((*env)->GetPrimitiveArrayCritical(env, iv, 0)); + if (NULL == ivNative) { + goto cleanup; + } + if (0 == (*OSSL_DecryptInit_ex)(ctx, NULL, NULL, keyNative, ivNative)) { printErrors(); - (*OSSL_CIPHER_CTX_free)(ctx); - (*env)->ReleasePrimitiveArrayCritical(env, key, keyNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, iv, ivNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, output, outputNative, JNI_ABORT); - if (inLen > 0) { - (*env)->ReleasePrimitiveArrayCritical(env, input, inputNative, JNI_ABORT); - } - if (aadLen > 0) { - (*env)->ReleasePrimitiveArrayCritical(env, aad, aadNative, JNI_ABORT); - } - return -1; + goto cleanup; } - /* Provide any AAD data */ + /* Provide any AAD data. */ if (aadLen > 0) { + aadNative = (unsigned char *)((*env)->GetPrimitiveArrayCritical(env, aad, 0)); + if (NULL == aadNative) { + goto cleanup; + } + if (0 == (*OSSL_DecryptUpdate)(ctx, NULL, &len, aadNative, aadLen)) { printErrors(); - (*OSSL_CIPHER_CTX_free)(ctx); - (*env)->ReleasePrimitiveArrayCritical(env, key, keyNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, iv, ivNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, output, outputNative, JNI_ABORT); - if (inLen > 0) { - (*env)->ReleasePrimitiveArrayCritical(env, input, inputNative, JNI_ABORT); - } - if (aadLen > 0) { - (*env)->ReleasePrimitiveArrayCritical(env, aad, aadNative, JNI_ABORT); - } - return -1; + goto cleanup; + } + } + + outputNative = (unsigned char *)((*env)->GetPrimitiveArrayCritical(env, output, 0)); + if (NULL == outputNative) { + goto cleanup; + } + + if (inLen > 0) { + inputNative = (unsigned char *)((*env)->GetPrimitiveArrayCritical(env, input, 0)); + if (NULL == inputNative) { + goto cleanup; } } if (inLen - tagLen > 0) { if(0 == (*OSSL_DecryptUpdate)(ctx, outputNative + outOffset, &len, inputNative + inOffset, inLen - tagLen)) { printErrors(); - (*OSSL_CIPHER_CTX_free)(ctx); - (*env)->ReleasePrimitiveArrayCritical(env, key, keyNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, iv, ivNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, output, outputNative, JNI_ABORT); - if (inLen > 0) { - (*env)->ReleasePrimitiveArrayCritical(env, input, inputNative, JNI_ABORT); - } - if (aadLen > 0) { - (*env)->ReleasePrimitiveArrayCritical(env, aad, aadNative, JNI_ABORT); - } - return -1; + goto cleanup; } plaintext_len = len; } if (0 == (*OSSL_CIPHER_CTX_ctrl)(ctx, EVP_CTRL_GCM_SET_TAG, tagLen, inputNative + inOffset + inLen - tagLen)) { printErrors(); - (*OSSL_CIPHER_CTX_free)(ctx); - (*env)->ReleasePrimitiveArrayCritical(env, key, keyNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, iv, ivNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, output, outputNative, JNI_ABORT); - if (inLen > 0) { - (*env)->ReleasePrimitiveArrayCritical(env, input, inputNative, JNI_ABORT); - } - if (aadLen > 0) { - (*env)->ReleasePrimitiveArrayCritical(env, aad, aadNative, JNI_ABORT); - } - return -1; + goto cleanup; } - ret = (*OSSL_DecryptFinal)(ctx, outputNative + outOffset + len, &len); - - (*OSSL_CIPHER_CTX_free)(ctx); - - (*env)->ReleasePrimitiveArrayCritical(env, key, keyNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, iv, ivNative, JNI_ABORT); - (*env)->ReleasePrimitiveArrayCritical(env, output, outputNative, 0); + if (0 < (*OSSL_DecryptFinal)(ctx, outputNative + outOffset + len, &len)) { + /* Decryption was successful. */ + plaintext_len += len; + ret = (jint)plaintext_len; + } else { + /* There was a tag mismatch. */ + ret = -2; + } - if (inLen > 0) { +cleanup: + if (NULL != inputNative) { (*env)->ReleasePrimitiveArrayCritical(env, input, inputNative, JNI_ABORT); } - - if (aadLen > 0) { + if (NULL != outputNative) { + (*env)->ReleasePrimitiveArrayCritical(env, output, outputNative, 0); + } + if (NULL != aadNative) { (*env)->ReleasePrimitiveArrayCritical(env, aad, aadNative, JNI_ABORT); } - - if (ret > 0) { - /* Successful Decryption */ - plaintext_len += len; - return (jint)plaintext_len; - } else { - /* Tag Mismatch */ - return -2; + if (NULL != ivNative) { + (*env)->ReleasePrimitiveArrayCritical(env, iv, ivNative, JNI_ABORT); } + if (NULL != keyNative) { + (*env)->ReleasePrimitiveArrayCritical(env, key, keyNative, JNI_ABORT); + } + + return ret; } BIGNUM* convertJavaBItoBN(unsigned char* in, int len); @@ -2214,13 +2099,13 @@ JNIEXPORT jint JNICALL Java_jdk_crypto_jniprovider_NativeCrypto_ChaCha20Init unsigned char *ivNative = NULL; unsigned char *keyNative = NULL; const EVP_CIPHER *evp_cipher1 = NULL; - int encrypt = -1; + int encrypt = OPENSSL_SAME_MODE; if (NULL == ctx) { return -1; } - if ((0 == mode) || (1 == mode)) { + if ((OPENSSL_DECRYPTION_MODE == mode) || (OPENSSL_ENCRYPTION_MODE == mode)) { /* Use the existing evp_cipher? */ if (JNI_FALSE == doReset) { evp_cipher1 = (*OSSL_chacha20_poly1305)(); @@ -2232,7 +2117,7 @@ JNIEXPORT jint JNICALL Java_jdk_crypto_jniprovider_NativeCrypto_ChaCha20Init evp_cipher1 = (*OSSL_chacha20)(); } /* encrypt or decrypt does not matter */ - encrypt = 1; + encrypt = OPENSSL_ENCRYPTION_MODE; } else { return -1; }