F-6437: zeroize secret ML-KEM input copies before JNI release

pull/257/head
Chris Conlon 2026-08-13 16:20:09 -06:00
parent 40a1039110
commit 5a75925fef
3 changed files with 49 additions and 12 deletions

View File

@ -40,6 +40,9 @@ byte* getByteArrayIsCopy(JNIEnv* env, jbyteArray array, jboolean* isCopy);
void zeroizeByteArrayCopy(byte* buf, word32 sz, jboolean isCopy);
void releaseByteArray(JNIEnv* env, jbyteArray array, byte* elements, jint abort);
word32 getByteArrayLength(JNIEnv* env, jbyteArray array);
byte* getSecretByteArray(JNIEnv* env, jbyteArray array, jboolean* isCopy);
void releaseSecretByteArray(JNIEnv* env, jbyteArray array, byte* elements,
word32 len, jboolean isCopy);
void initializeNativeStruct(JNIEnv* env, jobject obj);
#ifdef __cplusplus

View File

@ -200,16 +200,17 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_MlKem_wc_1mlkem_1make_1key_1fr
MlKemKey* key = NULL;
byte* seed = NULL;
word32 seedSz = 0;
jboolean seedIsCopy = JNI_FALSE;
key = (MlKemKey*) getNativeStruct(env, this);
if ((*env)->ExceptionOccurred(env)) {
return;
}
seed = getByteArray(env, seed_object);
seed = getSecretByteArray(env, seed_object, &seedIsCopy);
seedSz = getByteArrayLength(env, seed_object);
/* getByteArray() can return NULL with a pending exception */
/* getSecretByteArray() can return NULL with a pending exception */
if (seed_object != NULL && seed == NULL) {
return;
}
@ -228,7 +229,7 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_MlKem_wc_1mlkem_1make_1key_1fr
LogStr("wc_MlKemKey_MakeKeyWithRandom(key=%p, seedSz=%u) = %d\n",
key, (word32)seedSz, ret);
releaseByteArray(env, seed_object, seed, JNI_ABORT);
releaseSecretByteArray(env, seed_object, seed, seedSz, seedIsCopy);
#else
(void)this;
(void)seed_object;
@ -324,6 +325,7 @@ Java_com_wolfssl_wolfcrypt_MlKem_wc_1mlkem_1encapsulate_1with_1random(
MlKemKey* key = NULL;
byte* rand = NULL;
word32 randSz = 0;
jboolean randIsCopy = JNI_FALSE;
byte* output = NULL;
word32 ctSz = 0;
word32 ssSz = 0;
@ -334,16 +336,16 @@ Java_com_wolfssl_wolfcrypt_MlKem_wc_1mlkem_1encapsulate_1with_1random(
return NULL;
}
rand = getByteArray(env, rand_object);
rand = getSecretByteArray(env, rand_object, &randIsCopy);
randSz = getByteArrayLength(env, rand_object);
/* getByteArray() can return NULL with a pending exception */
/* getSecretByteArray() can return NULL with a pending exception */
if (rand_object != NULL && rand == NULL) {
return NULL;
}
if (key == NULL || rand == NULL) {
releaseByteArray(env, rand_object, rand, JNI_ABORT);
releaseSecretByteArray(env, rand_object, rand, randSz, randIsCopy);
throwWolfCryptExceptionFromError(env, BAD_FUNC_ARG);
return NULL;
}
@ -353,7 +355,7 @@ Java_com_wolfssl_wolfcrypt_MlKem_wc_1mlkem_1encapsulate_1with_1random(
ret = wc_MlKemKey_SharedSecretSize(key, &ssSz);
}
if (ret != 0) {
releaseByteArray(env, rand_object, rand, JNI_ABORT);
releaseSecretByteArray(env, rand_object, rand, randSz, randIsCopy);
throwWolfCryptExceptionFromError(env, ret);
return NULL;
}
@ -361,7 +363,7 @@ Java_com_wolfssl_wolfcrypt_MlKem_wc_1mlkem_1encapsulate_1with_1random(
totalSz = ctSz + ssSz;
output = (byte*)XMALLOC(totalSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (output == NULL) {
releaseByteArray(env, rand_object, rand, JNI_ABORT);
releaseSecretByteArray(env, rand_object, rand, randSz, randIsCopy);
throwOutOfMemoryException(env, "Failed to allocate encapsulation");
return NULL;
}
@ -390,7 +392,7 @@ Java_com_wolfssl_wolfcrypt_MlKem_wc_1mlkem_1encapsulate_1with_1random(
MLKEM_FORCE_ZERO(output, totalSz);
XFREE(output, NULL, DYNAMIC_TYPE_TMP_BUFFER);
releaseByteArray(env, rand_object, rand, JNI_ABORT);
releaseSecretByteArray(env, rand_object, rand, randSz, randIsCopy);
#else
(void)this;
(void)rand_object;
@ -659,16 +661,17 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_MlKem_wc_1mlkem_1import_1priva
MlKemKey* key = NULL;
byte* priv = NULL;
word32 privSz = 0;
jboolean privIsCopy = JNI_FALSE;
key = (MlKemKey*) getNativeStruct(env, this);
if ((*env)->ExceptionOccurred(env)) {
return;
}
priv = getByteArray(env, priv_object);
priv = getSecretByteArray(env, priv_object, &privIsCopy);
privSz = getByteArrayLength(env, priv_object);
/* getByteArray() can return NULL with a pending exception */
/* getSecretByteArray() can return NULL with a pending exception */
if (priv_object != NULL && priv == NULL) {
return;
}
@ -687,7 +690,7 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_MlKem_wc_1mlkem_1import_1priva
LogStr("wc_MlKemKey_DecodePrivateKey(key=%p, privSz=%u) = %d\n",
key, (word32)privSz, ret);
releaseByteArray(env, priv_object, priv, JNI_ABORT);
releaseSecretByteArray(env, priv_object, priv, privSz, privIsCopy);
#else
(void)this;
(void)priv_object;

View File

@ -247,6 +247,37 @@ word32 getByteArrayLength(JNIEnv* env, jbyteArray array)
return array ? (*env)->GetArrayLength(env, array) : 0;
}
/* Zeroize sensitive buffer 'buf' of size 'sz' bytes, using wc_ForceZero
* when available, otherwise XMEMSET. */
#if (LIBWOLFSSL_VERSION_HEX >= 0x05008004) && !defined(WOLFSSL_NO_FORCE_ZERO)
#define WC_JNI_FORCE_ZERO(buf, sz) wc_ForceZero((buf), (sz))
#else
#define WC_JNI_FORCE_ZERO(buf, sz) XMEMSET((buf), 0, (sz))
#endif
/* Pin secret input array elements, capturing if JVM returned a copy */
byte* getSecretByteArray(JNIEnv* env, jbyteArray array, jboolean* isCopy)
{
*isCopy = JNI_FALSE;
if (array == NULL) {
return NULL;
}
return (byte*)(*env)->GetByteArrayElements(env, array, isCopy);
}
/* Zero a pinned copy of secret input, then release without copy back. */
void releaseSecretByteArray(JNIEnv* env, jbyteArray array,
byte* elements, word32 len, jboolean isCopy)
{
if (elements != NULL && isCopy == JNI_TRUE) {
WC_JNI_FORCE_ZERO(elements, len);
}
releaseByteArray(env, array, elements, JNI_ABORT);
}
void initializeNativeStruct(JNIEnv* env, jobject obj)
{
jclass class;