diff --git a/jni/jni_aescmac.c b/jni/jni_aescmac.c index 2f43a29c..ab31ad19 100644 --- a/jni/jni_aescmac.c +++ b/jni/jni_aescmac.c @@ -37,6 +37,11 @@ /* #define WOLFCRYPT_JNI_DEBUG_ON */ #include +#if (LIBWOLFSSL_VERSION_HEX >= 0x05006006) && (!defined(HAVE_FIPS) || \ + (defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION >= 6))) + #define WC_JNI_CMAC_HAVE_FREE +#endif + JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_AesCmac_mallocNativeStruct_1internal( JNIEnv* env, jobject this) { @@ -94,10 +99,19 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_AesCmac_native_1free( LogStr("free Cmac %p\n", cmac); if (cmac) { - /* Only clear the CMAC struct - do NOT free the memory here. - * The base class NativeStruct.xfree() will handle the actual - * memory deallocation to avoid double-free. */ + /* Release and zeroize CMAC context contents, not freeing the + * memory since base class NativeStruct.xfree() will handle that + * deallocation. */ +#ifdef WC_JNI_CMAC_HAVE_FREE + if (cmac->type == WC_CMAC_AES) { + wc_CmacFree(cmac); + } + else { + XMEMSET(cmac, 0, sizeof(Cmac)); + } +#else XMEMSET(cmac, 0, sizeof(Cmac)); +#endif } #else throwNotCompiledInException(env); @@ -125,6 +139,13 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_AesCmac_wc_1CmacSetKey( if (!cmac || !key) { ret = BAD_FUNC_ARG; } else { +#ifdef WC_JNI_CMAC_HAVE_FREE + /* Free any active context first, reset() and repeated setKey() calls + * reinitialize the same struct */ + if (cmac->type == WC_CMAC_AES) { + wc_CmacFree(cmac); + } +#endif /* Initialize CMAC with the provided key */ ret = wc_InitCmac(cmac, key, keySz, WC_CMAC_AES, NULL); } diff --git a/jni/jni_curve25519.c b/jni/jni_curve25519.c index 43f7837a..989542b2 100644 --- a/jni/jni_curve25519.c +++ b/jni/jni_curve25519.c @@ -170,11 +170,10 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Curve25519_wc_1curve25519_1imp pub = getByteArray(env, pub_object); pubSz = getByteArrayLength(env, pub_object); - /* pub may be null if only importing private key */ if (!curve25519 || !priv) { ret = BAD_FUNC_ARG; } else { - /* detect, and later skip, leading zero byte */ + /* Import raw fixed width private and public key bytes unchanged */ ret = wc_curve25519_import_private_raw(priv, privSz, pub, pubSz, curve25519); } @@ -208,11 +207,10 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Curve25519_wc_1curve25519_1imp priv = getByteArray(env, priv_object); privSz = getByteArrayLength(env, priv_object); - /* pub may be null if only importing private key */ if (!curve25519 || !priv) { ret = BAD_FUNC_ARG; } else { - /* detect, and later skip, leading zero byte */ + /* Import raw fixed width private key bytes unchanged */ ret = wc_curve25519_import_private(priv, privSz, curve25519); } @@ -247,7 +245,7 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Curve25519_wc_1curve25519_1imp if (!curve25519 || !pub) { ret = BAD_FUNC_ARG; } else { - /* detect, and later skip, leading zero byte */ + /* Import raw fixed width public key bytes unchanged */ ret = wc_curve25519_import_public(pub, pubSz, curve25519); } diff --git a/jni/jni_ed25519.c b/jni/jni_ed25519.c index fd629dba..a28ab40a 100644 --- a/jni/jni_ed25519.c +++ b/jni/jni_ed25519.c @@ -201,7 +201,8 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Ed25519_wc_1ed25519_1import_1p if (!ed25519 || !priv) { ret = BAD_FUNC_ARG; } else { - /* detect, and later skip, leading zero byte */ + /* Select private only import when no public key is supplied, + * raw fixed width key bytes are passed unchanged */ if (!pub) ret = wc_ed25519_import_private_only(priv, privSz, ed25519); else @@ -275,7 +276,7 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Ed25519_wc_1ed25519_1import_1p if (!ed25519 || !priv) { ret = BAD_FUNC_ARG; } else { - /* detect, and later skip, leading zero byte */ + /* Import raw fixed width private key bytes unchanged */ ret = wc_ed25519_import_private_only(priv, privSz, ed25519); } diff --git a/jni/jni_fips.c b/jni/jni_fips.c index 7949e24e..e86ad099 100644 --- a/jni/jni_fips.c +++ b/jni/jni_fips.c @@ -108,10 +108,11 @@ void NativeErrorCallback(const int ok, const int err, const char * const hash) #ifdef HAVE_FIPS JNIEnv* env; jobject localCb = NULL; - jclass class; - jmethodID method; + jclass class = NULL; + jmethodID method = NULL; jint ret; - jstring hashStr; + jstring hashStr = NULL; + int needsDetach = 0; if (g_vm == NULL) { return; @@ -128,6 +129,7 @@ void NativeErrorCallback(const int ok, const int err, const char * const hash) printf("Failed to attach JNIEnv to thread\n"); return; } + needsDetach = 1; } else if (ret != JNI_OK) { printf("Unable to get JNIEnv from JavaVM\n"); @@ -138,64 +140,44 @@ void NativeErrorCallback(const int ok, const int err, const char * const hash) * so a concurrent wolfCrypt_1SetCb_1fips() cannot free the global * reference while we use it. The rest of this function uses only the * local reference and never touches g_errCb again. */ - if (!g_fipsCbMutexInit || wc_LockMutex(&g_fipsCbMutex) != 0) { - return; - } - if (g_errCb != NULL && - (*env)->GetObjectRefType(env, g_errCb) == JNIGlobalRefType) { - localCb = (*env)->NewLocalRef(env, g_errCb); - } - wc_UnLockMutex(&g_fipsCbMutex); - - /* Return silently if no valid callback was registered */ - if (localCb == NULL) { - if ((*env)->ExceptionOccurred(env)) { - (*env)->ExceptionDescribe(env); - (*env)->ExceptionClear(env); + if (g_fipsCbMutexInit && wc_LockMutex(&g_fipsCbMutex) == 0) { + if (g_errCb != NULL && + (*env)->GetObjectRefType(env, g_errCb) == JNIGlobalRefType) { + localCb = (*env)->NewLocalRef(env, g_errCb); } - return; + wc_UnLockMutex(&g_fipsCbMutex); } - class = (*env)->GetObjectClass(env, localCb); - if (!class) { - if ((*env)->ExceptionOccurred(env)) { - (*env)->ExceptionDescribe(env); - (*env)->ExceptionClear(env); - } - (*env)->DeleteLocalRef(env, localCb); - return; + if (localCb != NULL) { + class = (*env)->GetObjectClass(env, localCb); } - - method = (*env)->GetMethodID(env, class, "errorCallback", - "(IILjava/lang/String;)V"); - if (!method) { - if ((*env)->ExceptionOccurred(env)) { - (*env)->ExceptionDescribe(env); - (*env)->ExceptionClear(env); - } - (*env)->DeleteLocalRef(env, localCb); - return; + if (class != NULL) { + method = (*env)->GetMethodID(env, class, "errorCallback", + "(IILjava/lang/String;)V"); } - - hashStr = (*env)->NewStringUTF(env, hash); - if (!hashStr) { - if ((*env)->ExceptionOccurred(env)) { - (*env)->ExceptionDescribe(env); - (*env)->ExceptionClear(env); - } - (*env)->DeleteLocalRef(env, localCb); - return; + if (method != NULL) { + hashStr = (*env)->NewStringUTF(env, hash); + } + if (hashStr != NULL) { + (*env)->CallVoidMethod(env, localCb, method, ok, err, hashStr); + (*env)->DeleteLocalRef(env, hashStr); } - - (*env)->CallVoidMethod(env, localCb, method, ok, err, hashStr); - - (*env)->DeleteLocalRef(env, hashStr); - (*env)->DeleteLocalRef(env, localCb); if ((*env)->ExceptionOccurred(env)) { (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); } + + if (class != NULL) { + (*env)->DeleteLocalRef(env, class); + } + if (localCb != NULL) { + (*env)->DeleteLocalRef(env, localCb); + } + + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } #endif } @@ -1231,10 +1213,10 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_Fips_wc_1AesGcmDecrypt_1fips__ LogStr( "AesGcmDecrypt_fips(aes=%p, out, in, iv, authTag, authIn) = %d\n", aes, ret); - LogStr("in[%u]: [%p]\n", (word32)AES_BLOCK_SIZE, in); - LogHex(in, 0, AES_BLOCK_SIZE); - LogStr("out[%u]: [%p]\n", (word32)AES_BLOCK_SIZE, out); - LogHex(out, 0, AES_BLOCK_SIZE); + LogStr("in[%u]: [%p]\n", (word32)size, in); + LogHex(in, 0, size); + LogStr("out[%u]: [%p]\n", (word32)size, out); + LogHex(out, 0, size); LogStr("iv[%u]: [%p]\n", (word32)ivSz, iv); LogHex(iv, 0, ivSz); LogStr("authTag[%u]: [%p]\n", (word32)authTagSz, authTag); @@ -1293,10 +1275,10 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_Fips_wc_1AesGcmDecrypt_1fips__ LogStr( "AesGcmDecrypt_fips(aes=%p, out, in, iv, authTag, authIn) = %d\n", aes, ret); - LogStr("in[%u]: [%p]\n", (word32)AES_BLOCK_SIZE, in); - LogHex(in, 0, AES_BLOCK_SIZE); - LogStr("out[%u]: [%p]\n", (word32)AES_BLOCK_SIZE, out); - LogHex(out, 0, AES_BLOCK_SIZE); + LogStr("in[%u]: [%p]\n", (word32)size, in); + LogHex(in, 0, size); + LogStr("out[%u]: [%p]\n", (word32)size, out); + LogHex(out, 0, size); LogStr("iv[%u]: [%p]\n", (word32)ivSz, iv); LogHex(iv, 0, ivSz); LogStr("authTag[%u]: [%p]\n", (word32)authTagSz, authTag); diff --git a/jni/jni_native_struct.c b/jni/jni_native_struct.c index 858458a5..b2821fee 100644 --- a/jni/jni_native_struct.c +++ b/jni/jni_native_struct.c @@ -59,12 +59,15 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) /* Initialize WolfSSLCertManager global mutex for thread-safe callback * handling. Done here so it is before any CertManager ops happen. */ if (wolfSSL_CertManager_init() != 0) { + g_vm = NULL; return JNI_ERR; } /* Initialize FIPS callback mutex for thread-safe error callback handling. * No-op in non-FIPS builds. */ if (wolfCrypt_JNI_FipsCb_init() != 0) { + wolfSSL_CertManager_cleanup(); + g_vm = NULL; return JNI_ERR; } diff --git a/jni/jni_rsa.c b/jni/jni_rsa.c index d98c030f..8d0a371c 100644 --- a/jni/jni_rsa.c +++ b/jni/jni_rsa.c @@ -1989,6 +1989,14 @@ Java_com_wolfssl_wolfcrypt_Rsa_wc_1RsaExportCrtKey( q == NULL || dP == NULL || dQ == NULL || u == NULL) { ret = BAD_FUNC_ARG; } + /* Supplied buffer sizes must not exceed actual array lengths */ + else if (nSz < 0 || nSz > (jlong)(*env)->GetArrayLength(env, n_object) || + eSz < 0 || eSz > (jlong)(*env)->GetArrayLength(env, e_object) || + dSz < 0 || dSz > (jlong)(*env)->GetArrayLength(env, d_object) || + pSz < 0 || pSz > (jlong)(*env)->GetArrayLength(env, p_object) || + qSz < 0 || qSz > (jlong)(*env)->GetArrayLength(env, q_object)) { + ret = BAD_FUNC_ARG; + } else { /* Export e, n, d, p, q using wc_RsaExportKey() */ nSz32 = (word32)nSz; diff --git a/src/main/java/com/wolfssl/wolfcrypt/BlockCipher.java b/src/main/java/com/wolfssl/wolfcrypt/BlockCipher.java index f7c36d0e..6811511e 100644 --- a/src/main/java/com/wolfssl/wolfcrypt/BlockCipher.java +++ b/src/main/java/com/wolfssl/wolfcrypt/BlockCipher.java @@ -184,9 +184,22 @@ public synchronized byte[] update(byte[] input, int offset, int length) checkStateAndInitialize(); throwIfKeyNotLoaded(); - byte[] output = new byte[input.length]; + if (input == null || offset < 0 || length < 0 || + ((long)offset + (long)length) > input.length) { + throw new WolfCryptException(WolfCryptError.BAD_FUNC_ARG.getCode()); + } + + int outputLength; + byte[] output = new byte[length]; - native_update(opmode, input, offset, length, output, 0); + outputLength = native_update(opmode, input, offset, length, output, 0); + + if (outputLength != length) { + /* resize array to match actual output length */ + byte[] tmp = new byte[outputLength]; + System.arraycopy(output, 0, tmp, 0, outputLength); + output = tmp; + } return output; } diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/AesCmacTest.java b/src/test/java/com/wolfssl/wolfcrypt/test/AesCmacTest.java index 40137d3f..e0bc93c6 100644 --- a/src/test/java/com/wolfssl/wolfcrypt/test/AesCmacTest.java +++ b/src/test/java/com/wolfssl/wolfcrypt/test/AesCmacTest.java @@ -220,6 +220,27 @@ public void aesCmacStaticMethodsShouldWork() { } } + @Test + public void aesCmacResetAndRekeyMidStreamShouldWork() { + byte[] keyBytes = Util.h2b("2b7e151628aed2a6abf7158809cf4f3c"); + byte[] dataBytes = Util.h2b("6bc1bee22e409f96e93d7e117393172a"); + byte[] expectedBytes = Util.h2b("070a16b46b4d4144f79bdd9dd04a287c"); + + /* reset() mid stream must reinitialize the active context */ + AesCmac cmac = new AesCmac(); + cmac.setKey(keyBytes); + cmac.update(new byte[8]); + cmac.reset(); + cmac.update(dataBytes); + assertArrayEquals(expectedBytes, cmac.doFinal()); + + /* setKey() again without doFinal() must also reinitialize */ + cmac.setKey(keyBytes); + cmac.update(new byte[4]); + cmac.setKey(keyBytes); + assertArrayEquals(expectedBytes, cmac.doFinal(dataBytes)); + } + @Test public void aesCmacAlgorithmInfoShouldWork() { try { diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/AesEcbTest.java b/src/test/java/com/wolfssl/wolfcrypt/test/AesEcbTest.java index 1ae09b25..f0f7b17f 100644 --- a/src/test/java/com/wolfssl/wolfcrypt/test/AesEcbTest.java +++ b/src/test/java/com/wolfssl/wolfcrypt/test/AesEcbTest.java @@ -126,6 +126,38 @@ public void checkSetKeyParams() { } } + @Test + public void updateWithInvalidLengthShouldThrow() { + + AesEcb enc = new AesEcb(); + enc.setKey(KEY_128, AesEcb.ENCRYPT_MODE); + + byte[] in = new byte[AesEcb.BLOCK_SIZE]; + + try { + enc.update(null, 0, AesEcb.BLOCK_SIZE); + fail("null input should throw"); + } catch (WolfCryptException e) { + /* expected */ + } + + try { + enc.update(in, 0, -1); + fail("negative length should throw"); + } catch (WolfCryptException e) { + /* expected */ + } + + try { + enc.update(in, AesEcb.BLOCK_SIZE, AesEcb.BLOCK_SIZE); + fail("offset plus length beyond input should throw"); + } catch (WolfCryptException e) { + /* expected */ + } + + enc.releaseNativeStruct(); + } + @Test public void checkUpdateParams() throws ShortBufferException { byte[] input = new byte[AesEcb.BLOCK_SIZE]; diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/AesTest.java b/src/test/java/com/wolfssl/wolfcrypt/test/AesTest.java index bf9d1a49..a34b26f3 100644 --- a/src/test/java/com/wolfssl/wolfcrypt/test/AesTest.java +++ b/src/test/java/com/wolfssl/wolfcrypt/test/AesTest.java @@ -422,6 +422,32 @@ public void releaseAndReInitObject() { dec.releaseNativeStruct(); } + @Test + public void updateWithPartialLengthShouldSizeOutputToLength() { + + byte[] key = Util.h2b("2b7e151628aed2a6abf7158809cf4f3c"); + byte[] iv = Util.h2b("000102030405060708090A0B0C0D0E0F"); + byte[] input = Util.h2b("6bc1bee22e409f96e93d7e117393172a" + + "ae2d8a571e03ac9c9eb76fac45af8e51"); + byte[] expected = Util.h2b("7649abac8119b246cee98e9b12e9197d"); + + /* process only the first block, output must be sized to length */ + Aes enc = new Aes(); + enc.setKey(key, iv, Aes.ENCRYPT_MODE); + byte[] cipher = enc.update(input, 0, Aes.BLOCK_SIZE); + assertEquals(Aes.BLOCK_SIZE, cipher.length); + assertArrayEquals(expected, cipher); + + Aes dec = new Aes(); + dec.setKey(key, iv, Aes.DECRYPT_MODE); + byte[] plain = dec.update(cipher, 0, Aes.BLOCK_SIZE); + assertEquals(Aes.BLOCK_SIZE, plain.length); + assertArrayEquals(Arrays.copyOf(input, Aes.BLOCK_SIZE), plain); + + enc.releaseNativeStruct(); + dec.releaseNativeStruct(); + } + @Test public void reuseObject() { @@ -457,6 +483,41 @@ public void reuseObject() { dec.releaseNativeStruct(); } + @Test + public void updateWithInvalidLengthShouldThrow() { + + byte[] key = Util.h2b("2b7e151628aed2a6abf7158809cf4f3c"); + byte[] iv = Util.h2b("000102030405060708090A0B0C0D0E0F"); + + Aes enc = new Aes(); + enc.setKey(key, iv, Aes.ENCRYPT_MODE); + + byte[] in = new byte[Aes.BLOCK_SIZE]; + + try { + enc.update(null, 0, Aes.BLOCK_SIZE); + fail("null input should throw"); + } catch (WolfCryptException e) { + /* expected */ + } + + try { + enc.update(in, 0, -1); + fail("negative length should throw"); + } catch (WolfCryptException e) { + /* expected */ + } + + try { + enc.update(in, Aes.BLOCK_SIZE, Aes.BLOCK_SIZE); + fail("offset plus length beyond input should throw"); + } catch (WolfCryptException e) { + /* expected */ + } + + enc.releaseNativeStruct(); + } + @Test public void testPadPKCS7() { diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/Des3Test.java b/src/test/java/com/wolfssl/wolfcrypt/test/Des3Test.java index c7d58d17..952b2fa3 100644 --- a/src/test/java/com/wolfssl/wolfcrypt/test/Des3Test.java +++ b/src/test/java/com/wolfssl/wolfcrypt/test/Des3Test.java @@ -254,6 +254,42 @@ public void releaseAndReInitObject() { dec.releaseNativeStruct(); } + @Test + public void updateWithInvalidLengthShouldThrow() { + + byte[] key = Util.h2b("e61a38548694f1fd8cef251c518" + + "cc70bb613751c1ce52aa8"); + byte[] iv = Util.h2b("48a8ceb8551fd4ad"); + + Des3 enc = new Des3(); + enc.setKey(key, iv, Des3.ENCRYPT_MODE); + + byte[] in = new byte[Des3.BLOCK_SIZE]; + + try { + enc.update(null, 0, Des3.BLOCK_SIZE); + fail("null input should throw"); + } catch (WolfCryptException e) { + /* expected */ + } + + try { + enc.update(in, 0, -1); + fail("negative length should throw"); + } catch (WolfCryptException e) { + /* expected */ + } + + try { + enc.update(in, Des3.BLOCK_SIZE, Des3.BLOCK_SIZE); + fail("offset plus length beyond input should throw"); + } catch (WolfCryptException e) { + /* expected */ + } + + enc.releaseNativeStruct(); + } + @Test public void reuseObject() { diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/RsaTest.java b/src/test/java/com/wolfssl/wolfcrypt/test/RsaTest.java index 7ac6acf6..8810d5f5 100644 --- a/src/test/java/com/wolfssl/wolfcrypt/test/RsaTest.java +++ b/src/test/java/com/wolfssl/wolfcrypt/test/RsaTest.java @@ -732,6 +732,27 @@ public void exportRawPrivateKeySizesCorrect() { assertTrue(dQSz[0] >= 127 && dQSz[0] <= 129); assertTrue(uSz[0] >= 127 && uSz[0] <= 129); + /* Capacity larger than the actual array must be rejected */ + byte[] shortN = new byte[1]; + nSz[0] = 256; + try { + key.exportRawPrivateKey(shortN, nSz, e, eSz, d, dSz, p, pSz, + q, qSz, dP, dPSz, dQ, dQSz, u, uSz); + fail("exportRawPrivateKey should reject oversized nSz"); + } catch (WolfCryptException ex) { + /* expected */ + } + + /* Negative capacity must be rejected */ + nSz[0] = -1; + try { + key.exportRawPrivateKey(n, nSz, e, eSz, d, dSz, p, pSz, + q, qSz, dP, dPSz, dQ, dQSz, u, uSz); + fail("exportRawPrivateKey should reject negative nSz"); + } catch (WolfCryptException ex) { + /* expected */ + } + key.releaseNativeStruct(); }