From ce08f7325eea8b48951bf29c9457631f6a806909 Mon Sep 17 00:00:00 2001 From: Jason Katonica Date: Thu, 17 Aug 2023 14:55:45 -0400 Subject: [PATCH] Optimize AES/CBC cipher initialization The EVP cipher initialization cost has been found to be expensive in the `OpenSSL 3.x` API compared to the `OpenSSL 1.x` API. This update allows for two different types of initializations to occur. The first initialization type is a full initialization which sets the key, iv, and EVP cipher context. The second type of initialization sets just the key and iv and does NOT recreate and reinitialize the EVP context. The former of these two is required once per Cipher instance, the later of these two can be used whenever we are reusing a specific Java cipher object within methods such as `Cipher.doFinal()`. Signed-off by: Jason Katonica --- .../provider/NativeCipherBlockChaining.java | 14 +++++--- .../jdk/crypto/jniprovider/NativeCrypto.java | 3 +- .../share/native/libjncrypto/NativeCrypto.c | 32 +++++++++++-------- 3 files changed, 30 insertions(+), 19 deletions(-) diff --git a/closed/src/java.base/share/classes/com/sun/crypto/provider/NativeCipherBlockChaining.java b/closed/src/java.base/share/classes/com/sun/crypto/provider/NativeCipherBlockChaining.java index f06958492b8..0ce2ba6b8bc 100644 --- a/closed/src/java.base/share/classes/com/sun/crypto/provider/NativeCipherBlockChaining.java +++ b/closed/src/java.base/share/classes/com/sun/crypto/provider/NativeCipherBlockChaining.java @@ -24,7 +24,7 @@ */ /* * =========================================================================== - * (c) Copyright IBM Corp. 2018, 2022 All Rights Reserved + * (c) Copyright IBM Corp. 2018, 2023 All Rights Reserved * =========================================================================== */ @@ -56,6 +56,7 @@ class NativeCipherBlockChaining extends FeedbackCipher { private static final NativeCrypto nativeCrypto; private static final Cleaner contextCleaner; + private int previousKeyLength = -1; /* * Initialize the CBC context. @@ -184,7 +185,12 @@ void init(boolean decrypting, String algorithm, byte[] key, byte[] iv) int ret; synchronized (this) { - ret = nativeCrypto.CBCInit(nativeContext, mode, iv, iv.length, key, key.length); + if (previousKeyLength == key.length) { + ret = nativeCrypto.CBCInit(nativeContext, mode, iv, iv.length, key, key.length, true); + } else { + ret = nativeCrypto.CBCInit(nativeContext, mode, iv, iv.length, key, key.length, false); + previousKeyLength = key.length; + } } if (ret == -1) { throw new ProviderException("Error in Native CipherBlockChaining"); @@ -201,7 +207,7 @@ void reset() { System.arraycopy(iv, 0, r, 0, blockSize); int ret; synchronized (this) { - ret = nativeCrypto.CBCInit(nativeContext, mode, iv, iv.length, key, key.length); + ret = nativeCrypto.CBCInit(nativeContext, mode, iv, iv.length, key, key.length, true); } if (ret == -1) { throw new ProviderException("Error in Native CipherBlockChaining"); @@ -225,7 +231,7 @@ void restore() { System.arraycopy(rSave, 0, r, 0, blockSize); int ret; synchronized (this) { - ret = nativeCrypto.CBCInit(nativeContext, mode, r, r.length, key, key.length); + ret = nativeCrypto.CBCInit(nativeContext, mode, r, r.length, key, key.length, true); } if (ret == -1) { throw new ProviderException("Error in Native CipherBlockChaining"); 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 4ac58f14a35..b5090db1241 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 @@ -233,7 +233,8 @@ public final native int CBCInit(long context, byte[] iv, int ivlen, byte[] key, - int keylen); + int keylen, + boolean doReset); public final native int CBCUpdate(long context, byte[] input, diff --git a/closed/src/java.base/share/native/libjncrypto/NativeCrypto.c b/closed/src/java.base/share/native/libjncrypto/NativeCrypto.c index 6062abde8e1..d964bb11c2b 100644 --- a/closed/src/java.base/share/native/libjncrypto/NativeCrypto.c +++ b/closed/src/java.base/share/native/libjncrypto/NativeCrypto.c @@ -1092,7 +1092,7 @@ JNIEXPORT jint JNICALL Java_jdk_crypto_jniprovider_NativeCrypto_DestroyContext */ JNIEXPORT jint JNICALL Java_jdk_crypto_jniprovider_NativeCrypto_CBCInit (JNIEnv *env, jclass thisObj, jlong c, jint mode, jbyteArray iv, jint iv_len, - jbyteArray key, jint key_len) { + jbyteArray key, jint key_len, jboolean doReset) { EVP_CIPHER_CTX *ctx = (EVP_CIPHER_CTX*)(intptr_t) c; unsigned char* ivNative = NULL; @@ -1103,18 +1103,20 @@ JNIEXPORT jint JNICALL Java_jdk_crypto_jniprovider_NativeCrypto_CBCInit return -1; } - switch(key_len) { - case 16: - evp_cipher1 = (*OSSL_aes_128_cbc)(); - break; - case 24: - evp_cipher1 = (*OSSL_aes_192_cbc)(); - break; - case 32: - evp_cipher1 = (*OSSL_aes_256_cbc)(); - break; - default: - break; + if (JNI_FALSE == doReset) { + switch (key_len) { + case 16: + evp_cipher1 = (*OSSL_aes_128_cbc)(); + break; + case 24: + evp_cipher1 = (*OSSL_aes_192_cbc)(); + break; + case 32: + evp_cipher1 = (*OSSL_aes_256_cbc)(); + break; + default: + break; + } } ivNative = (unsigned char*)((*env)->GetByteArrayElements(env, iv, 0)); @@ -1135,7 +1137,9 @@ JNIEXPORT jint JNICALL Java_jdk_crypto_jniprovider_NativeCrypto_CBCInit return -1; } - (*OSSL_CIPHER_CTX_set_padding)(ctx, 0); + if (JNI_FALSE == doReset) { + (*OSSL_CIPHER_CTX_set_padding)(ctx, 0); + } (*env)->ReleaseByteArrayElements(env, iv, (jbyte*)ivNative, JNI_ABORT); (*env)->ReleaseByteArrayElements(env, key, (jbyte*)keyNative, JNI_ABORT);