From cbd78eb5fb46d2538a04f1a2344236d50cd19ca9 Mon Sep 17 00:00:00 2001 From: Tao Liu Date: Wed, 22 Apr 2026 17:10:11 -0400 Subject: [PATCH 1/2] Validate XDH native secret derivation Add return-code checks for the native XDH secret derivation path, including ICC_EVP_PKEY_derive_init, ICC_EVP_PKEY_derive_set_peer, and the ICC_EVP_PKEY_derive call used to query the secret length. Also validate the derived secret length against the allocated Java byte array length before returning the result. Signed-off-by: Tao Liu --- .../crypto/plus/provider/XDHKeyAgreement.java | 11 +- .../plus/provider/base/NativeInterface.java | 2 +- .../ibm/crypto/plus/provider/base/XECKey.java | 4 +- .../plus/provider/ock/NativeOCKAdapter.java | 4 +- .../provider/ock/NativeOCKImplementation.java | 2 +- src/main/native/ock/ECKey.c | 114 ++++++++++++------ 6 files changed, 81 insertions(+), 56 deletions(-) diff --git a/src/main/java/com/ibm/crypto/plus/provider/XDHKeyAgreement.java b/src/main/java/com/ibm/crypto/plus/provider/XDHKeyAgreement.java index 9ae3f54f7..f08fd909a 100644 --- a/src/main/java/com/ibm/crypto/plus/provider/XDHKeyAgreement.java +++ b/src/main/java/com/ibm/crypto/plus/provider/XDHKeyAgreement.java @@ -102,17 +102,8 @@ protected Key engineDoPhase(Key key, boolean lastPhase) } try { - int secrectBufferSize = 0; - String curveName = ((NamedParameterSpec) xdhPublicKeyImpl.getParams()).getName(); - if (NamedParameterSpec.X25519.getName().equalsIgnoreCase(curveName)) { - secrectBufferSize = SECRET_BUFFER_SIZE_X25519; // X25519 secret buffer size - } else if (NamedParameterSpec.X448.getName().equalsIgnoreCase(curveName)) { - secrectBufferSize = SECRET_BUFFER_SIZE_X448; // X448 secret buffer size - } else { - secrectBufferSize = 0; // Let OCK decide the size - } this.secret = XECKey.computeECDHSecret(genCtx, - ockXecKeyPub.getPKeyId(), ockXecKeyPriv.getPKeyId(), secrectBufferSize, provider); + ockXecKeyPub.getPKeyId(), ockXecKeyPriv.getPKeyId(), provider); } catch (NativeException e) { //Validate the secret value for a small order point condition. byte orValue = (byte) 0; diff --git a/src/main/java/com/ibm/crypto/plus/provider/base/NativeInterface.java b/src/main/java/com/ibm/crypto/plus/provider/base/NativeInterface.java index b81e3d33d..3dae792dd 100644 --- a/src/main/java/com/ibm/crypto/plus/provider/base/NativeInterface.java +++ b/src/main/java/com/ibm/crypto/plus/provider/base/NativeInterface.java @@ -542,7 +542,7 @@ public byte[] ECKEY_computeECDHSecret(long pubEcKeyId, long privEcKeyId) throws NativeException; public byte[] XECKEY_computeECDHSecret(long genCtx, - long pubEcKeyId, long privEcKeyId, int secrectBufferSize) throws NativeException; + long pubEcKeyId, long privEcKeyId) throws NativeException; public byte[] ECKEY_signDatawithECDSA(byte[] digestBytes, diff --git a/src/main/java/com/ibm/crypto/plus/provider/base/XECKey.java b/src/main/java/com/ibm/crypto/plus/provider/base/XECKey.java index 59910be84..8dcb5bfd6 100644 --- a/src/main/java/com/ibm/crypto/plus/provider/base/XECKey.java +++ b/src/main/java/com/ibm/crypto/plus/provider/base/XECKey.java @@ -68,7 +68,7 @@ public static XECKey generateKeyPair(int curveNum, int pub_size, OpenJCEPlusProv } public static byte[] computeECDHSecret(long genCtx, long pubId, - long privId, int secrectBufferSize, OpenJCEPlusProvider provider) throws NativeException { + long privId, OpenJCEPlusProvider provider) throws NativeException { if (pubId == 0) throw new IllegalArgumentException("The public key parameter is not valid"); if (privId == 0) @@ -76,7 +76,7 @@ public static byte[] computeECDHSecret(long genCtx, long pubId, NativeInterface nativeInterface = provider.isFIPS() ? NativeOCKAdapterFIPS.getInstance() : NativeOCKAdapterNonFIPS.getInstance(); byte[] sharedSecretBytes = nativeInterface.XECKEY_computeECDHSecret( - genCtx, pubId, privId, secrectBufferSize); + genCtx, pubId, privId); //OCKDebug.Msg (debPrefix, methodName, "pubId :" + pubId + " privId :" + privId + " sharedSecretBytes :", sharedSecretBytes); return sharedSecretBytes; } diff --git a/src/main/java/com/ibm/crypto/plus/provider/ock/NativeOCKAdapter.java b/src/main/java/com/ibm/crypto/plus/provider/ock/NativeOCKAdapter.java index fd1110e8d..61127e5e6 100644 --- a/src/main/java/com/ibm/crypto/plus/provider/ock/NativeOCKAdapter.java +++ b/src/main/java/com/ibm/crypto/plus/provider/ock/NativeOCKAdapter.java @@ -1116,9 +1116,9 @@ public byte[] ECKEY_computeECDHSecret(long pubEcKeyId, long privEcKeyId) throws } @Override - public byte[] XECKEY_computeECDHSecret(long genCtx, long pubEcKeyId, long privEcKeyId, int secrectBufferSize) + public byte[] XECKEY_computeECDHSecret(long genCtx, long pubEcKeyId, long privEcKeyId) throws OCKException { - return NativeOCKImplementation.XECKEY_computeECDHSecret(ockContext.getId(), genCtx, pubEcKeyId, privEcKeyId, secrectBufferSize); + return NativeOCKImplementation.XECKEY_computeECDHSecret(ockContext.getId(), genCtx, pubEcKeyId, privEcKeyId); } @Override diff --git a/src/main/java/com/ibm/crypto/plus/provider/ock/NativeOCKImplementation.java b/src/main/java/com/ibm/crypto/plus/provider/ock/NativeOCKImplementation.java index 6c255294f..8239bedce 100644 --- a/src/main/java/com/ibm/crypto/plus/provider/ock/NativeOCKImplementation.java +++ b/src/main/java/com/ibm/crypto/plus/provider/ock/NativeOCKImplementation.java @@ -724,7 +724,7 @@ static public native byte[] ECKEY_computeECDHSecret(long ockContextId, long pubE long privEcKeyId) throws OCKException; static public native byte[] XECKEY_computeECDHSecret(long ockContextId, long genCtx, - long pubEcKeyId, long privEcKeyId, int secrectBufferSize) throws OCKException; + long pubEcKeyId, long privEcKeyId) throws OCKException; static public native byte[] ECKEY_signDatawithECDSA(long ockContextId, byte[] digestBytes, diff --git a/src/main/native/ock/ECKey.c b/src/main/native/ock/ECKey.c index 05c611932..bfd8e1ed3 100644 --- a/src/main/native/ock/ECKey.c +++ b/src/main/native/ock/ECKey.c @@ -2221,7 +2221,7 @@ Java_com_ibm_crypto_plus_provider_ock_NativeOCKImplementation_ECKEY_1computeECDH JNIEXPORT jbyteArray JNICALL Java_com_ibm_crypto_plus_provider_ock_NativeOCKImplementation_XECKEY_1computeECDHSecret( JNIEnv *env, jclass thisObj, jlong ockContextId, jlong genCtx, - jlong pubXecKeyId, jlong privXecKeyId, jint secretBufferSize) { + jlong pubXecKeyId, jlong privXecKeyId) { static const char *functionName = "NativeInterface_XECKEY_1computeECDHSecret"; @@ -2233,6 +2233,7 @@ Java_com_ibm_crypto_plus_provider_ock_NativeOCKImplementation_XECKEY_1computeECD jbyteArray secretBytes = NULL; unsigned char *secretBytesNative = NULL; jboolean isCopy = 0; + size_t required_len = 0; size_t secret_key_len = 0; int rc = 0; @@ -2244,56 +2245,89 @@ Java_com_ibm_crypto_plus_provider_ock_NativeOCKImplementation_XECKEY_1computeECD NULL); /* Set private key */ if (NULL == gen_ctx) { throwOCKException(env, 0, "NULL from ICC_EVP_PKEY_CTX_new"); - } else { - ICC_EVP_PKEY_derive_init(ockCtx, gen_ctx); - ICC_EVP_PKEY_derive_set_peer(ockCtx, gen_ctx, - ockPubXecKey); /* Set public key */ - if (secretBufferSize > 0) { - secret_key_len = secretBufferSize; - } else { - ICC_EVP_PKEY_derive(ockCtx, gen_ctx, NULL, - &secret_key_len); /* Get secret key size */ - } - secretBytes = (*env)->NewByteArray( - env, secret_key_len); /* Create Java secret bytes array with size */ - if (NULL == secretBytes) { - throwOCKException(env, 0, "NewByteArray failed"); - } else { - secretBytesNative = - (unsigned char *)((*env)->GetPrimitiveArrayCritical( - env, secretBytes, &isCopy)); - if (NULL == secretBytesNative) { - throwOCKException(env, 0, - "NULL from GetPrimitiveArrayCritical"); - } else { - rc = ICC_EVP_PKEY_derive(ockCtx, gen_ctx, secretBytesNative, - &secret_key_len); - if (ICC_OSSL_SUCCESS != rc) { - throwOCKException( - env, 0, "ICC_EVP_PKEY_derive failed to derive a key"); - } - ICC_EVP_PKEY_CTX_free(ockCtx, gen_ctx); - (*env)->ReleasePrimitiveArrayCritical(env, secretBytes, - secretBytesNative, 0); - if (debug) { - gslogFunctionExit(functionName); - } - return secretBytes; - } - } + goto cleanup; + } + + rc = ICC_EVP_PKEY_derive_init(ockCtx, gen_ctx); + if (ICC_OSSL_SUCCESS != rc) { + throwOCKException(env, 0, "ICC_EVP_PKEY_derive_init failed"); + goto cleanup; + } + + rc = ICC_EVP_PKEY_derive_set_peer(ockCtx, gen_ctx, ockPubXecKey); /* Set public key */ + if (ICC_OSSL_SUCCESS != rc) { + throwOCKException(env, 0, "ICC_EVP_PKEY_derive_set_peer failed"); + goto cleanup; + } + + /* + * Always query the required secret size before deriving the secret. + * This avoids deriving into a buffer with an incorrect size. + */ + rc = ICC_EVP_PKEY_derive(ockCtx, gen_ctx, NULL, &required_len); /* Get secret key size */ + if (ICC_OSSL_SUCCESS != rc) { + throwOCKException(env, 0, + "ICC_EVP_PKEY_derive failed to get secret size"); + goto cleanup; + } + if (required_len == 0) { + throwOCKException(env, 0, "Derived secret size is zero"); + goto cleanup; + } + + secret_key_len = required_len; + + secretBytes = (*env)->NewByteArray(env, secret_key_len); /* Create Java secret bytes array */ + if (NULL == secretBytes) { + throwOCKException(env, 0, "NewByteArray failed"); + goto cleanup; + } + + secretBytesNative = (unsigned char *)(*env)->GetPrimitiveArrayCritical( + env, secretBytes, &isCopy); + if (NULL == secretBytesNative) { + throwOCKException(env, 0, "NULL from GetPrimitiveArrayCritical"); + goto cleanup; } + rc = ICC_EVP_PKEY_derive(ockCtx, gen_ctx, secretBytesNative, &secret_key_len); + if (ICC_OSSL_SUCCESS != rc) { + throwOCKException(env, 0, "ICC_EVP_PKEY_derive failed to derive a key"); + goto cleanup; + } + + if (secret_key_len != required_len) { + throwOCKException(env, 0, + "Derived secret size does not match required size"); + goto cleanup; + } + + ICC_EVP_PKEY_CTX_free(ockCtx, gen_ctx); + gen_ctx = NULL; + + (*env)->ReleasePrimitiveArrayCritical(env, secretBytes, secretBytesNative, 0); + secretBytesNative = NULL; + + if (debug) { + gslogFunctionExit(functionName); + } + + return secretBytes; + +cleanup: if (NULL != gen_ctx) { ICC_EVP_PKEY_CTX_free(ockCtx, gen_ctx); + gen_ctx = NULL; } if (NULL != secretBytesNative) { - (*env)->ReleasePrimitiveArrayCritical(env, secretBytes, - secretBytesNative, 0); + (*env)->ReleasePrimitiveArrayCritical(env, secretBytes, secretBytesNative, 0); + secretBytesNative = NULL; } if (NULL != secretBytes) { (*env)->DeleteLocalRef(env, secretBytes); + secretBytes = NULL; } if (debug) { From 1fe277b4579ac6dca47c5d2d268172f00f21731f Mon Sep 17 00:00:00 2001 From: Tao Liu Date: Fri, 22 May 2026 13:41:39 -0400 Subject: [PATCH 2/2] Set XEC secret buffer size explicitly on z/OS Set the expected XEC secret buffer size explicitly on z/OS before calling the native ECDH secret computation. On other platforms, the native layer can query the required secret size by calling ICC_EVP_PKEY_derive. However, this size query does not work on z/OS. To handle this platform difference, determine the curve name from the public key parameters on z/OS and pass the expected secret buffer size to the native layer. Other platforms continue to use the ICC_EVP_PKEY_derive size query. Signed-off-by: Tao Liu --- .../crypto/plus/provider/XDHKeyAgreement.java | 11 +++++- .../plus/provider/base/NativeInterface.java | 2 +- .../ibm/crypto/plus/provider/base/XECKey.java | 4 +- .../plus/provider/ock/NativeOCKAdapter.java | 4 +- .../provider/ock/NativeOCKImplementation.java | 2 +- src/main/native/ock/ECKey.c | 38 +++++++++++-------- 6 files changed, 39 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/ibm/crypto/plus/provider/XDHKeyAgreement.java b/src/main/java/com/ibm/crypto/plus/provider/XDHKeyAgreement.java index f08fd909a..994baef6f 100644 --- a/src/main/java/com/ibm/crypto/plus/provider/XDHKeyAgreement.java +++ b/src/main/java/com/ibm/crypto/plus/provider/XDHKeyAgreement.java @@ -102,8 +102,17 @@ protected Key engineDoPhase(Key key, boolean lastPhase) } try { + int secrectBufferSize = 0; + if (System.getProperty("os.name").equals("z/OS")) { + String curveName = ((NamedParameterSpec) xdhPublicKeyImpl.getParams()).getName(); + if (NamedParameterSpec.X25519.getName().equalsIgnoreCase(curveName)) { + secrectBufferSize = SECRET_BUFFER_SIZE_X25519; // X25519 secret buffer size + } else if (NamedParameterSpec.X448.getName().equalsIgnoreCase(curveName)) { + secrectBufferSize = SECRET_BUFFER_SIZE_X448; // X448 secret buffer size + } + } this.secret = XECKey.computeECDHSecret(genCtx, - ockXecKeyPub.getPKeyId(), ockXecKeyPriv.getPKeyId(), provider); + ockXecKeyPub.getPKeyId(), ockXecKeyPriv.getPKeyId(), secrectBufferSize, provider); } catch (NativeException e) { //Validate the secret value for a small order point condition. byte orValue = (byte) 0; diff --git a/src/main/java/com/ibm/crypto/plus/provider/base/NativeInterface.java b/src/main/java/com/ibm/crypto/plus/provider/base/NativeInterface.java index 3dae792dd..b81e3d33d 100644 --- a/src/main/java/com/ibm/crypto/plus/provider/base/NativeInterface.java +++ b/src/main/java/com/ibm/crypto/plus/provider/base/NativeInterface.java @@ -542,7 +542,7 @@ public byte[] ECKEY_computeECDHSecret(long pubEcKeyId, long privEcKeyId) throws NativeException; public byte[] XECKEY_computeECDHSecret(long genCtx, - long pubEcKeyId, long privEcKeyId) throws NativeException; + long pubEcKeyId, long privEcKeyId, int secrectBufferSize) throws NativeException; public byte[] ECKEY_signDatawithECDSA(byte[] digestBytes, diff --git a/src/main/java/com/ibm/crypto/plus/provider/base/XECKey.java b/src/main/java/com/ibm/crypto/plus/provider/base/XECKey.java index 8dcb5bfd6..59910be84 100644 --- a/src/main/java/com/ibm/crypto/plus/provider/base/XECKey.java +++ b/src/main/java/com/ibm/crypto/plus/provider/base/XECKey.java @@ -68,7 +68,7 @@ public static XECKey generateKeyPair(int curveNum, int pub_size, OpenJCEPlusProv } public static byte[] computeECDHSecret(long genCtx, long pubId, - long privId, OpenJCEPlusProvider provider) throws NativeException { + long privId, int secrectBufferSize, OpenJCEPlusProvider provider) throws NativeException { if (pubId == 0) throw new IllegalArgumentException("The public key parameter is not valid"); if (privId == 0) @@ -76,7 +76,7 @@ public static byte[] computeECDHSecret(long genCtx, long pubId, NativeInterface nativeInterface = provider.isFIPS() ? NativeOCKAdapterFIPS.getInstance() : NativeOCKAdapterNonFIPS.getInstance(); byte[] sharedSecretBytes = nativeInterface.XECKEY_computeECDHSecret( - genCtx, pubId, privId); + genCtx, pubId, privId, secrectBufferSize); //OCKDebug.Msg (debPrefix, methodName, "pubId :" + pubId + " privId :" + privId + " sharedSecretBytes :", sharedSecretBytes); return sharedSecretBytes; } diff --git a/src/main/java/com/ibm/crypto/plus/provider/ock/NativeOCKAdapter.java b/src/main/java/com/ibm/crypto/plus/provider/ock/NativeOCKAdapter.java index 61127e5e6..fd1110e8d 100644 --- a/src/main/java/com/ibm/crypto/plus/provider/ock/NativeOCKAdapter.java +++ b/src/main/java/com/ibm/crypto/plus/provider/ock/NativeOCKAdapter.java @@ -1116,9 +1116,9 @@ public byte[] ECKEY_computeECDHSecret(long pubEcKeyId, long privEcKeyId) throws } @Override - public byte[] XECKEY_computeECDHSecret(long genCtx, long pubEcKeyId, long privEcKeyId) + public byte[] XECKEY_computeECDHSecret(long genCtx, long pubEcKeyId, long privEcKeyId, int secrectBufferSize) throws OCKException { - return NativeOCKImplementation.XECKEY_computeECDHSecret(ockContext.getId(), genCtx, pubEcKeyId, privEcKeyId); + return NativeOCKImplementation.XECKEY_computeECDHSecret(ockContext.getId(), genCtx, pubEcKeyId, privEcKeyId, secrectBufferSize); } @Override diff --git a/src/main/java/com/ibm/crypto/plus/provider/ock/NativeOCKImplementation.java b/src/main/java/com/ibm/crypto/plus/provider/ock/NativeOCKImplementation.java index 8239bedce..6c255294f 100644 --- a/src/main/java/com/ibm/crypto/plus/provider/ock/NativeOCKImplementation.java +++ b/src/main/java/com/ibm/crypto/plus/provider/ock/NativeOCKImplementation.java @@ -724,7 +724,7 @@ static public native byte[] ECKEY_computeECDHSecret(long ockContextId, long pubE long privEcKeyId) throws OCKException; static public native byte[] XECKEY_computeECDHSecret(long ockContextId, long genCtx, - long pubEcKeyId, long privEcKeyId) throws OCKException; + long pubEcKeyId, long privEcKeyId, int secrectBufferSize) throws OCKException; static public native byte[] ECKEY_signDatawithECDSA(long ockContextId, byte[] digestBytes, diff --git a/src/main/native/ock/ECKey.c b/src/main/native/ock/ECKey.c index bfd8e1ed3..7a080f20b 100644 --- a/src/main/native/ock/ECKey.c +++ b/src/main/native/ock/ECKey.c @@ -2221,7 +2221,7 @@ Java_com_ibm_crypto_plus_provider_ock_NativeOCKImplementation_ECKEY_1computeECDH JNIEXPORT jbyteArray JNICALL Java_com_ibm_crypto_plus_provider_ock_NativeOCKImplementation_XECKEY_1computeECDHSecret( JNIEnv *env, jclass thisObj, jlong ockContextId, jlong genCtx, - jlong pubXecKeyId, jlong privXecKeyId) { + jlong pubXecKeyId, jlong privXecKeyId, jint secretBufferSize) { static const char *functionName = "NativeInterface_XECKEY_1computeECDHSecret"; @@ -2260,21 +2260,29 @@ Java_com_ibm_crypto_plus_provider_ock_NativeOCKImplementation_XECKEY_1computeECD goto cleanup; } - /* - * Always query the required secret size before deriving the secret. - * This avoids deriving into a buffer with an incorrect size. - */ - rc = ICC_EVP_PKEY_derive(ockCtx, gen_ctx, NULL, &required_len); /* Get secret key size */ - if (ICC_OSSL_SUCCESS != rc) { - throwOCKException(env, 0, - "ICC_EVP_PKEY_derive failed to get secret size"); - goto cleanup; - } - if (required_len == 0) { - throwOCKException(env, 0, "Derived secret size is zero"); - goto cleanup; + if (secretBufferSize > 0) { + /* + * On z/OS, querying the required secret size may not work properly. + * In that case, use the caller-provided secretBufferSize as the + * expected derived secret size. + */ + required_len = (size_t) secretBufferSize; + } else { + /* + * Query the required secret size before deriving the secret. + * This avoids deriving into a buffer with an incorrect size. + */ + rc = ICC_EVP_PKEY_derive(ockCtx, gen_ctx, NULL, &required_len); /* Get secret key size */ + if (ICC_OSSL_SUCCESS != rc) { + throwOCKException(env, 0, + "ICC_EVP_PKEY_derive failed to get secret size"); + goto cleanup; + } + if (required_len == 0) { + throwOCKException(env, 0, "Derived secret size is zero"); + goto cleanup; + } } - secret_key_len = required_len; secretBytes = (*env)->NewByteArray(env, secret_key_len); /* Create Java secret bytes array */