Skip to content

Commit

Permalink
Cache openssl message digest contexts
Browse files Browse the repository at this point in the history
It has been identified that when using `OpenSSL` version `3.x` the
message digest context initialization is much more expensive than using
`OpenSSL` version `1.x`. This update optimizes the use of `OpenSSL`
contexts by making use of a simple cache.

We will create a second initialized openssl digest context when the
message digest cipher is initialized. This second context, defined as
`context->cachedInitalizedDigestContext`, will be copied over the
working context, `context->ctx`, using the `EVP_MD_CTX_copy_ex` API
whenever we wish to re-initalize this cipher. The restoration of a
context occurs whenever a cipher is explictly reset or whenever a final
digest is computed.

Backported from:

ibmruntimes/openj9-openjdk-jdk#643

Signed-off by: Jason Katonica <katonica@us.ibm.com>
  • Loading branch information
jasonkatonica committed Sep 8, 2023
1 parent a152046 commit 71c6eaa
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ public final native int DigestComputeAndReset(long context,
int digestOffset,
int digestLen);

public final native void DigestReset(long context);
public final native int DigestReset(long context);

/* Native interfaces shared by CBC and ChaCha20 */

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,10 @@
*/
/*
* ===========================================================================
* (c) Copyright IBM Corp. 2018, 2019 All Rights Reserved
* (c) Copyright IBM Corp. 2018, 2023 All Rights Reserved
* ===========================================================================
*/


package sun.security.provider;

import java.security.MessageDigestSpi;
Expand Down Expand Up @@ -137,7 +136,10 @@ synchronized protected final void engineReset() {
return;
}

nativeCrypto.DigestReset(context);
int ret = nativeCrypto.DigestReset(context);
if (ret == -1) {
throw new ProviderException("Error in Native Digest Reset");
}
bytesProcessed = 0;
}

Expand Down
95 changes: 75 additions & 20 deletions closed/src/java.base/share/native/libjncrypto/NativeCrypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ OSSL_PKCS12_key_gen_t* OSSL_PKCS12_key_gen;
typedef struct OpenSSLMDContext {
EVP_MD_CTX *ctx;
const EVP_MD *digestAlg;
EVP_MD_CTX *cachedInitializedDigestContext;
} OpenSSLMDContext;

/* Handle errors from OpenSSL calls. */
Expand Down Expand Up @@ -896,22 +897,38 @@ JNIEXPORT jlong JNICALL Java_jdk_crypto_jniprovider_NativeCrypto_DigestCreateCon
context->ctx = ctx;
context->digestAlg = digestAlg;

/*
* Create a second initialized openssl digest context. This is being done for performance reasons since
* creating and or re-initializing digest contexts later during processing is found to be expensive.
* This second context, context->cachedInitializedDigestContext, will be copied over the working context,
* context->ctx, using the EVP_MD_CTX_copy_ex API whenever we wish to re-initalize this cipher. This occurs
* during an explicit reset of the cipher or whenever a final digest is computed.
*/
context->cachedInitializedDigestContext = (*OSSL_MD_CTX_new)();
if (NULL == context->cachedInitializedDigestContext) {
goto releaseContexts;
}

if (1 != (*OSSL_MD_CTX_copy_ex)(context->cachedInitializedDigestContext, context->ctx)) {
goto releaseContexts;
}

if (0 != copyContext) {
EVP_MD_CTX *contextToCopy = ((OpenSSLMDContext*)(intptr_t)copyContext)->ctx;
if (NULL == contextToCopy) {
(*OSSL_MD_CTX_free)(ctx);
free(context);
return -1;
goto releaseContexts;
}
if (0 == (*OSSL_MD_CTX_copy_ex)(ctx, contextToCopy)) {
printErrors();
(*OSSL_MD_CTX_free)(ctx);
free(context);
return -1;
goto releaseContexts;
}
}

return (jlong)(intptr_t)context;

releaseContexts:
printErrors();
Java_jdk_crypto_jniprovider_NativeCrypto_DigestDestroyContext(env, thisObj, (jlong)(intptr_t)context);
return -1;
}

/*
Expand All @@ -923,11 +940,20 @@ JNIEXPORT jint JNICALL Java_jdk_crypto_jniprovider_NativeCrypto_DigestDestroyCon
(JNIEnv *env, jclass thisObj, jlong c) {

OpenSSLMDContext *context = (OpenSSLMDContext*)(intptr_t) c;
if ((NULL == context) || (NULL == context->ctx)) {
if (NULL == context) {
return -1;
}

(*OSSL_MD_CTX_free)(context->ctx);
if (NULL != context->ctx) {
(*OSSL_MD_CTX_free)(context->ctx);
context->ctx = NULL;
}

if (NULL != context->cachedInitializedDigestContext) {
(*OSSL_MD_CTX_free)(context->cachedInitializedDigestContext);
context->cachedInitializedDigestContext = NULL;
}

free(context);
return 0;
}
Expand Down Expand Up @@ -984,7 +1010,7 @@ JNIEXPORT jint JNICALL Java_jdk_crypto_jniprovider_NativeCrypto_DigestComputeAnd
unsigned char* messageNative = NULL;
unsigned char* digestNative = NULL;

if ((NULL == context) || (NULL == context->ctx)) {
if ((NULL == context) || (NULL == context->ctx) || (NULL == context->cachedInitializedDigestContext)) {
return -1;
}

Expand Down Expand Up @@ -1016,10 +1042,23 @@ JNIEXPORT jint JNICALL Java_jdk_crypto_jniprovider_NativeCrypto_DigestComputeAnd

(*env)->ReleasePrimitiveArrayCritical(env, digest, digestNative, 0);

(*OSSL_MD_CTX_reset)(context->ctx);

if (1 != (*OSSL_DigestInit_ex)(context->ctx, context->digestAlg, NULL)) {
/*
* Reset the message digest context to the original context. We are then ready to perform
* digest operations again using a copy of this cached context.
*/
if (1 != (*OSSL_MD_CTX_copy_ex)(context->ctx, context->cachedInitializedDigestContext)) {
printErrors();

if (NULL != context->ctx) {
(*OSSL_MD_CTX_free)(context->ctx);
context->ctx = NULL;
}

if (NULL != context->cachedInitializedDigestContext) {
(*OSSL_MD_CTX_free)(context->cachedInitializedDigestContext);
context->cachedInitializedDigestContext = NULL;
}

return -1;
}

Expand All @@ -1030,22 +1069,38 @@ JNIEXPORT jint JNICALL Java_jdk_crypto_jniprovider_NativeCrypto_DigestComputeAnd
*
* Class: jdk_crypto_jniprovider_NativeCrypto
* Method: DigestReset
* Signature: (J)V
* Signature: (J)I
*/
JNIEXPORT void JNICALL Java_jdk_crypto_jniprovider_NativeCrypto_DigestReset
JNIEXPORT jint JNICALL Java_jdk_crypto_jniprovider_NativeCrypto_DigestReset
(JNIEnv *env, jclass thisObj, jlong c) {

OpenSSLMDContext *context = (OpenSSLMDContext*)(intptr_t) c;

if ((NULL == context) || (NULL == context->ctx)) {
return;
if ((NULL == context) || (NULL == context->ctx) || (NULL == context->cachedInitializedDigestContext)) {
return -1;
}

(*OSSL_MD_CTX_reset)(context->ctx);

if (1 != (*OSSL_DigestInit_ex)(context->ctx, context->digestAlg, NULL)) {
/*
* Reset the message digest context to the original context. We are then ready to perform
* digest operations again using a copy of this cached context.
*/
if (1 != (*OSSL_MD_CTX_copy_ex)(context->ctx, context->cachedInitializedDigestContext)) {
printErrors();

if (NULL != context->ctx) {
(*OSSL_MD_CTX_free)(context->ctx);
context->ctx = NULL;
}

if (NULL != context->cachedInitializedDigestContext) {
(*OSSL_MD_CTX_free)(context->cachedInitializedDigestContext);
context->cachedInitializedDigestContext = NULL;
}

return -1;
}

return 0;
}

/*
Expand Down

0 comments on commit 71c6eaa

Please sign in to comment.