diff --git a/jni/jni_pwdbased.c b/jni/jni_pwdbased.c index 97e5f5e8..3a5a0c66 100644 --- a/jni/jni_pwdbased.c +++ b/jni/jni_pwdbased.c @@ -48,6 +48,7 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_Pwdbased_wc_1PKCS12_1PBK byte* pass = NULL; byte* salt = NULL; byte* outKey = NULL; + jboolean passIsCopy = JNI_FALSE; jbyteArray result = NULL; (void)jcl; @@ -64,7 +65,7 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_Pwdbased_wc_1PKCS12_1PBK XMEMSET(outKey, 0, kLen); if (passBuf != NULL) { - pass = (byte*)(*env)->GetByteArrayElements(env, passBuf, NULL); + pass = (byte*)(*env)->GetByteArrayElements(env, passBuf, &passIsCopy); } if (saltBuf != NULL) { salt = (byte*)(*env)->GetByteArrayElements(env, saltBuf, NULL); @@ -96,6 +97,15 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_Pwdbased_wc_1PKCS12_1PBK } if (pass != NULL) { + /* Zero native copy of password, JNI_ABORT does not copy back */ + if (passIsCopy == JNI_TRUE && passBufLen > 0) { + #if (LIBWOLFSSL_VERSION_HEX >= 0x05008004) && \ + !defined(WOLFSSL_NO_FORCE_ZERO) + wc_ForceZero(pass, passBufLen); + #else + XMEMSET(pass, 0, passBufLen); + #endif + } (*env)->ReleaseByteArrayElements(env, passBuf, (jbyte*)pass, JNI_ABORT); } if (salt != NULL) { @@ -133,6 +143,7 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_Pwdbased_wc_1PBKDF2 byte* pass = NULL; byte* salt = NULL; byte* outKey = NULL; + jboolean passIsCopy = JNI_FALSE; jbyteArray result = NULL; (void)jcl; @@ -149,7 +160,7 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_Pwdbased_wc_1PBKDF2 XMEMSET(outKey, 0, kLen); if ((passBuf != NULL) && (passBufLen > 0)) { - pass = (byte*)(*env)->GetByteArrayElements(env, passBuf, NULL); + pass = (byte*)(*env)->GetByteArrayElements(env, passBuf, &passIsCopy); } if (saltBuf != NULL) { @@ -182,6 +193,15 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_Pwdbased_wc_1PBKDF2 } if (pass != NULL) { + /* Zero native copy of password, JNI_ABORT does not copy back */ + if (passIsCopy == JNI_TRUE && passBufLen > 0) { + #if (LIBWOLFSSL_VERSION_HEX >= 0x05008004) && \ + !defined(WOLFSSL_NO_FORCE_ZERO) + wc_ForceZero(pass, passBufLen); + #else + XMEMSET(pass, 0, passBufLen); + #endif + } (*env)->ReleaseByteArrayElements(env, passBuf, (jbyte*)pass, JNI_ABORT); } if (salt != NULL) { diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/PwdbasedTest.java b/src/test/java/com/wolfssl/wolfcrypt/test/PwdbasedTest.java new file mode 100644 index 00000000..14b11591 --- /dev/null +++ b/src/test/java/com/wolfssl/wolfcrypt/test/PwdbasedTest.java @@ -0,0 +1,137 @@ +/* PwdbasedTest.java + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +package com.wolfssl.wolfcrypt.test; + +import static org.junit.Assert.*; + +import org.junit.Assume; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.Rule; +import org.junit.rules.TestRule; + +import com.wolfssl.wolfcrypt.WolfCrypt; +import com.wolfssl.wolfcrypt.Pwdbased; +import com.wolfssl.wolfcrypt.FeatureDetect; +import com.wolfssl.wolfcrypt.Fips; +import com.wolfssl.wolfcrypt.WolfCryptError; +import com.wolfssl.wolfcrypt.WolfCryptException; + +/** + * Tests for the Pwdbased PBKDF2 and PKCS12 PBKDF JNI wrappers. + */ +public class PwdbasedTest { + + @Rule(order = Integer.MIN_VALUE) + public TestRule testWatcher = TimedTestWatcher.create(); + + @BeforeClass + public static void testSetup() { + System.out.println("JNI Pwdbased Class"); + } + + /* Password with distinctive pattern, used to detect modification */ + private static byte[] makePassword() { + byte[] pass = new byte[32]; + for (int i = 0; i < pass.length; i++) { + pass[i] = (byte)(0x41 + ((i * 7) % 26)); + } + return pass; + } + + /** + * PBKDF2-HMAC-SHA256 known answer test from RFC 7914 Section 11. + */ + @Test + public void testPbkdf2KnownAnswer() { + + Assume.assumeTrue("PBKDF2 not compiled in native wolfSSL", + FeatureDetect.Pbkdf2Enabled()); + /* RFC 7914 vector uses a 6 byte key and 4 byte salt, below the FIPS + * HMAC key and salt minimums, skip this known answer test in FIPS */ + Assume.assumeTrue("RFC 7914 KAT inputs below FIPS minimums", + !Fips.enabled); + + byte[] pass = "passwd".getBytes(); + byte[] salt = "salt".getBytes(); + byte[] expected = Util.h2b( + "55AC046E56E3089FEC1691C22544B605" + + "F94185216DDE0465E68B9D57C20DACBC" + + "49CA9CCCF179B645991664B39D77EF31" + + "7C71B845B1E30BD509112041D3A19783"); + + byte[] key = Pwdbased.PBKDF2(pass, salt, 1, 64, + WolfCrypt.WC_HASH_TYPE_SHA256); + + assertArrayEquals(expected, key); + } + + /** + * PBKDF2 must not modify the caller's password array, and repeated + * calls with the same password must derive the same key. + */ + @Test + public void testPbkdf2DoesNotModifyPassword() { + + Assume.assumeTrue("PBKDF2 not compiled in native wolfSSL", + FeatureDetect.Pbkdf2Enabled()); + + byte[] pass = makePassword(); + byte[] passCopy = pass.clone(); + byte[] salt = new byte[] {1, 2, 3, 4, 5, 6, 7, 8}; + + byte[] key1 = Pwdbased.PBKDF2(pass, salt, 1000, 32, + WolfCrypt.WC_HASH_TYPE_SHA256); + assertArrayEquals("PBKDF2 modified caller password array", + passCopy, pass); + + byte[] key2 = Pwdbased.PBKDF2(pass, salt, 1000, 32, + WolfCrypt.WC_HASH_TYPE_SHA256); + assertArrayEquals("repeated PBKDF2 derived different key", + key1, key2); + } + + /** + * PKCS12 PBKDF must not modify the caller's password array. + */ + @Test + public void testPkcs12PbkdfDoesNotModifyPassword() { + + byte[] pass = makePassword(); + byte[] passCopy = pass.clone(); + byte[] salt = new byte[] {1, 2, 3, 4, 5, 6, 7, 8}; + byte[] key = null; + + try { + key = Pwdbased.PKCS12_PBKDF(pass, salt, 100, 24, + WolfCrypt.WC_HASH_TYPE_SHA256, 1); + } catch (WolfCryptException e) { + Assume.assumeTrue("PKCS12 PBKDF not compiled in native wolfSSL", + e.getError() != WolfCryptError.NOT_COMPILED_IN); + throw e; + } + + assertNotNull(key); + assertArrayEquals("PKCS12_PBKDF modified caller password array", + passCopy, pass); + } +} diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/WolfCryptTestSuite.java b/src/test/java/com/wolfssl/wolfcrypt/test/WolfCryptTestSuite.java index bd451288..8872c03b 100644 --- a/src/test/java/com/wolfssl/wolfcrypt/test/WolfCryptTestSuite.java +++ b/src/test/java/com/wolfssl/wolfcrypt/test/WolfCryptTestSuite.java @@ -47,6 +47,7 @@ import org.junit.runners.Suite.SuiteClasses; Sha512Test.class, Sha3Test.class, HmacTest.class, + PwdbasedTest.class, RngTest.class, RsaTest.class, DhTest.class,