From c6d4819d8aeda5fd9753e8b5f1228849cd581ca1 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Tue, 19 Mar 2024 16:31:42 -0600 Subject: [PATCH] JCE: add SecretKeyFactory implementation for PBKDF2 --- README_JCE.md | 11 + .../com_wolfssl_wolfcrypt_FeatureDetect.h | 48 + jni/include/com_wolfssl_wolfcrypt_Pwdbased.h | 29 + jni/include/com_wolfssl_wolfcrypt_WolfCrypt.h | 112 ++ jni/jni_feature_detect.c | 72 ++ jni/jni_pwdbased.c | 180 +++ jni/jni_wolfcrypt.c | 84 ++ makefile.linux | 11 +- makefile.macosx | 11 +- scripts/infer.sh | 11 +- .../wolfssl/provider/jce/WolfCryptPBEKey.java | 291 +++++ .../provider/jce/WolfCryptProvider.java | 38 + .../jce/WolfCryptSecretKeyFactory.java | 721 +++++++++++ .../com/wolfssl/wolfcrypt/FeatureDetect.java | 43 + .../java/com/wolfssl/wolfcrypt/Pwdbased.java | 135 ++ .../java/com/wolfssl/wolfcrypt/WolfCrypt.java | 76 ++ .../test/WolfCryptSecretKeyFactoryTest.java | 1090 +++++++++++++++++ .../provider/jce/test/WolfJCETestSuite.java | 1 + 18 files changed, 2951 insertions(+), 13 deletions(-) create mode 100644 jni/include/com_wolfssl_wolfcrypt_Pwdbased.h create mode 100644 jni/jni_pwdbased.c create mode 100644 src/main/java/com/wolfssl/provider/jce/WolfCryptPBEKey.java create mode 100644 src/main/java/com/wolfssl/provider/jce/WolfCryptSecretKeyFactory.java create mode 100644 src/main/java/com/wolfssl/wolfcrypt/Pwdbased.java create mode 100644 src/test/java/com/wolfssl/provider/jce/test/WolfCryptSecretKeyFactoryTest.java diff --git a/README_JCE.md b/README_JCE.md index df0ef2ab..9bfde157 100644 --- a/README_JCE.md +++ b/README_JCE.md @@ -75,6 +75,17 @@ The JCE provider currently supports the following algorithms: CertPathValidator Class PKIX + SecretKeyFactory + PBKDF2WithHmacSHA1 + PBKDF2WithHmacSHA224 + PBKDF2WithHmacSHA256 + PBKDF2WithHmacSHA384 + PBKDF2WithHmacSHA512 + PBKDF2WithHmacSHA3-224 + PBKDF2WithHmacSHA3-256 + PBKDF2WithHmacSHA3-384 + PBKDF2WithHmacSHA3-512 + ### SecureRandom.getInstanceStrong() When registered as the highest priority security provider, wolfJCE will provide diff --git a/jni/include/com_wolfssl_wolfcrypt_FeatureDetect.h b/jni/include/com_wolfssl_wolfcrypt_FeatureDetect.h index 1b0c9bce..35e4449b 100644 --- a/jni/include/com_wolfssl_wolfcrypt_FeatureDetect.h +++ b/jni/include/com_wolfssl_wolfcrypt_FeatureDetect.h @@ -127,6 +127,14 @@ JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_HmacMd5Enabl JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_HmacShaEnabled (JNIEnv *, jclass); +/* + * Class: com_wolfssl_wolfcrypt_FeatureDetect + * Method: HmacSha224Enabled + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_HmacSha224Enabled + (JNIEnv *, jclass); + /* * Class: com_wolfssl_wolfcrypt_FeatureDetect * Method: HmacSha256Enabled @@ -151,6 +159,46 @@ JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_HmacSha384En JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_HmacSha512Enabled (JNIEnv *, jclass); +/* + * Class: com_wolfssl_wolfcrypt_FeatureDetect + * Method: HmacSha3_224Enabled + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_HmacSha3_1224Enabled + (JNIEnv *, jclass); + +/* + * Class: com_wolfssl_wolfcrypt_FeatureDetect + * Method: HmacSha3_256Enabled + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_HmacSha3_1256Enabled + (JNIEnv *, jclass); + +/* + * Class: com_wolfssl_wolfcrypt_FeatureDetect + * Method: HmacSha3_384Enabled + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_HmacSha3_1384Enabled + (JNIEnv *, jclass); + +/* + * Class: com_wolfssl_wolfcrypt_FeatureDetect + * Method: HmacSha3_512Enabled + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_HmacSha3_1512Enabled + (JNIEnv *, jclass); + +/* + * Class: com_wolfssl_wolfcrypt_FeatureDetect + * Method: Pbkdf2Enabled + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_Pbkdf2Enabled + (JNIEnv *, jclass); + /* * Class: com_wolfssl_wolfcrypt_FeatureDetect * Method: RsaEnabled diff --git a/jni/include/com_wolfssl_wolfcrypt_Pwdbased.h b/jni/include/com_wolfssl_wolfcrypt_Pwdbased.h new file mode 100644 index 00000000..21abad50 --- /dev/null +++ b/jni/include/com_wolfssl_wolfcrypt_Pwdbased.h @@ -0,0 +1,29 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class com_wolfssl_wolfcrypt_Pwdbased */ + +#ifndef _Included_com_wolfssl_wolfcrypt_Pwdbased +#define _Included_com_wolfssl_wolfcrypt_Pwdbased +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: com_wolfssl_wolfcrypt_Pwdbased + * Method: wc_PKCS12_PBKDF + * Signature: ([BI[BIIIII)[B + */ +JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_Pwdbased_wc_1PKCS12_1PBKDF + (JNIEnv *, jclass, jbyteArray, jint, jbyteArray, jint, jint, jint, jint, jint); + +/* + * Class: com_wolfssl_wolfcrypt_Pwdbased + * Method: wc_PBKDF2 + * Signature: ([BI[BIIII)[B + */ +JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_Pwdbased_wc_1PBKDF2 + (JNIEnv *, jclass, jbyteArray, jint, jbyteArray, jint, jint, jint, jint); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/jni/include/com_wolfssl_wolfcrypt_WolfCrypt.h b/jni/include/com_wolfssl_wolfcrypt_WolfCrypt.h index 5f763a8d..fae28471 100644 --- a/jni/include/com_wolfssl_wolfcrypt_WolfCrypt.h +++ b/jni/include/com_wolfssl_wolfcrypt_WolfCrypt.h @@ -29,6 +29,118 @@ extern "C" { #define com_wolfssl_wolfcrypt_WolfCrypt_SIZE_OF_1024_BITS 128L #undef com_wolfssl_wolfcrypt_WolfCrypt_SIZE_OF_2048_BITS #define com_wolfssl_wolfcrypt_WolfCrypt_SIZE_OF_2048_BITS 256L +/* + * Class: com_wolfssl_wolfcrypt_WolfCrypt + * Method: getWC_HASH_TYPE_NONE + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1NONE + (JNIEnv *, jclass); + +/* + * Class: com_wolfssl_wolfcrypt_WolfCrypt + * Method: getWC_HASH_TYPE_MD2 + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1MD2 + (JNIEnv *, jclass); + +/* + * Class: com_wolfssl_wolfcrypt_WolfCrypt + * Method: getWC_HASH_TYPE_MD4 + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1MD4 + (JNIEnv *, jclass); + +/* + * Class: com_wolfssl_wolfcrypt_WolfCrypt + * Method: getWC_HASH_TYPE_MD5 + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1MD5 + (JNIEnv *, jclass); + +/* + * Class: com_wolfssl_wolfcrypt_WolfCrypt + * Method: getWC_HASH_TYPE_SHA + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1SHA + (JNIEnv *, jclass); + +/* + * Class: com_wolfssl_wolfcrypt_WolfCrypt + * Method: getWC_HASH_TYPE_SHA224 + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1SHA224 + (JNIEnv *, jclass); + +/* + * Class: com_wolfssl_wolfcrypt_WolfCrypt + * Method: getWC_HASH_TYPE_SHA256 + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1SHA256 + (JNIEnv *, jclass); + +/* + * Class: com_wolfssl_wolfcrypt_WolfCrypt + * Method: getWC_HASH_TYPE_SHA384 + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1SHA384 + (JNIEnv *, jclass); + +/* + * Class: com_wolfssl_wolfcrypt_WolfCrypt + * Method: getWC_HASH_TYPE_SHA512 + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1SHA512 + (JNIEnv *, jclass); + +/* + * Class: com_wolfssl_wolfcrypt_WolfCrypt + * Method: getWC_HASH_TYPE_MD5_SHA + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1MD5_1SHA + (JNIEnv *, jclass); + +/* + * Class: com_wolfssl_wolfcrypt_WolfCrypt + * Method: getWC_HASH_TYPE_SHA3_224 + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1SHA3_1224 + (JNIEnv *, jclass); + +/* + * Class: com_wolfssl_wolfcrypt_WolfCrypt + * Method: getWC_HASH_TYPE_SHA3_256 + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1SHA3_1256 + (JNIEnv *, jclass); + +/* + * Class: com_wolfssl_wolfcrypt_WolfCrypt + * Method: getWC_HASH_TYPE_SHA3_384 + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1SHA3_1384 + (JNIEnv *, jclass); + +/* + * Class: com_wolfssl_wolfcrypt_WolfCrypt + * Method: getWC_HASH_TYPE_SHA3_512 + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1SHA3_1512 + (JNIEnv *, jclass); + /* * Class: com_wolfssl_wolfcrypt_WolfCrypt * Method: CrlEnabled diff --git a/jni/jni_feature_detect.c b/jni/jni_feature_detect.c index e62fe8d2..1fdfc510 100644 --- a/jni/jni_feature_detect.c +++ b/jni/jni_feature_detect.c @@ -208,6 +208,18 @@ JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_HmacShaEnabl #endif } +JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_HmacSha224Enabled + (JNIEnv* env, jclass jcl) +{ + (void)env; + (void)jcl; +#if !defined(NO_HMAC) && !defined(WOLFSSL_SHA224) + return JNI_TRUE; +#else + return JNI_FALSE; +#endif +} + JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_HmacSha256Enabled (JNIEnv* env, jclass jcl) { @@ -244,6 +256,66 @@ JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_HmacSha512En #endif } +JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_HmacSha3_1224Enabled + (JNIEnv* env, jclass jcl) +{ + (void)env; + (void)jcl; +#if !defined(NO_HMAC) && defined(WOLFSSL_SHA3) && !defined(WOLFSSL_NOSHA3_224) + return JNI_TRUE; +#else + return JNI_FALSE; +#endif +} + +JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_HmacSha3_1256Enabled + (JNIEnv* env, jclass jcl) +{ + (void)env; + (void)jcl; +#if !defined(NO_HMAC) && defined(WOLFSSL_SHA3) && !defined(WOLFSSL_NOSHA3_256) + return JNI_TRUE; +#else + return JNI_FALSE; +#endif +} + +JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_HmacSha3_1384Enabled + (JNIEnv* env, jclass jcl) +{ + (void)env; + (void)jcl; +#if !defined(NO_HMAC) && defined(WOLFSSL_SHA3) && !defined(WOLFSSL_NOSHA3_384) + return JNI_TRUE; +#else + return JNI_FALSE; +#endif +} + +JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_HmacSha3_1512Enabled + (JNIEnv* env, jclass jcl) +{ + (void)env; + (void)jcl; +#if !defined(NO_HMAC) && defined(WOLFSSL_SHA3) && !defined(WOLFSSL_NOSHA3_512) + return JNI_TRUE; +#else + return JNI_FALSE; +#endif +} + +JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_Pbkdf2Enabled + (JNIEnv* env, jclass jcl) +{ + (void)env; + (void)jcl; +#if !defined(NO_PWDBASED) && defined(HAVE_PBKDF2) && !defined(NO_HMAC) + return JNI_TRUE; +#else + return JNI_FALSE; +#endif +} + JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_RsaEnabled (JNIEnv* env, jclass jcl) { diff --git a/jni/jni_pwdbased.c b/jni/jni_pwdbased.c new file mode 100644 index 00000000..190588a7 --- /dev/null +++ b/jni/jni_pwdbased.c @@ -0,0 +1,180 @@ +/* jni_pwdbased.c + * + * Copyright (C) 2006-2024 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 2 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 + */ + +#include + +#ifdef WOLFSSL_USER_SETTINGS + #include +#elif !defined(__ANDROID__) + #include +#endif + +#include +#include +#include + +/* #define WOLFCRYPT_JNI_DEBUG_ON */ +#include + +JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_Pwdbased_wc_1PKCS12_1PBKDF + (JNIEnv* env, jclass jcl, jbyteArray passBuf, jint passBufLen, + jbyteArray saltBuf, jint sBufLen, jint iterations, jint kLen, + jint typeH, jint id) +{ +#if !defined(NO_PWDBASED) && defined(WOLFSSL_PKCS12) + int ret = 0; + byte* pass = NULL; + byte* salt = NULL; + byte* outKey = NULL; + jbyteArray result = NULL; + (void)jcl; + + if (env == NULL || kLen == 0) { + throwWolfCryptExceptionFromError(env, BAD_FUNC_ARG); + return NULL; + } + + outKey = (byte*)XMALLOC(kLen, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (outKey == NULL) { + throwWolfCryptExceptionFromError(env, MEMORY_E); + return NULL; + } + XMEMSET(outKey, 0, kLen); + + pass = (byte*)(*env)->GetByteArrayElements(env, passBuf, NULL); + salt = (byte*)(*env)->GetByteArrayElements(env, saltBuf, NULL); + + ret = wc_PKCS12_PBKDF(outKey, pass, passBufLen, salt, sBufLen, + iterations, kLen, typeH, id); + if (ret == 0) { + result = (*env)->NewByteArray(env, kLen); + if (result != NULL) { + (*env)->SetByteArrayRegion(env, result, 0, kLen, + (const jbyte*) outKey); + } else { + LogStr("NewByteArray failed in JNI PKCS12_PBKDF\n"); + ret = MEMORY_E; + } + } + + if (outKey != NULL) { + XMEMSET(outKey, 0, kLen); + XFREE(outKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + + (*env)->ReleaseByteArrayElements(env, passBuf, (jbyte*)pass, JNI_ABORT); + (*env)->ReleaseByteArrayElements(env, saltBuf, (jbyte*)salt, JNI_ABORT); + + if (ret != 0) { + throwWolfCryptExceptionFromError(env, ret); + return NULL; + } + + return result; +#else + (void)env; + (void)jcl; + (void)passBuf; + (void)passBufLen; + (void)saltBuf; + (void)sBufLen; + (void)iterations; + (void)kLen; + (void)typeH; + (void)id; + throwWolfCryptExceptionFromError(env, NOT_COMPILED_IN); + return NULL; +#endif +} + +JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_Pwdbased_wc_1PBKDF2 + (JNIEnv* env, jclass jcl, jbyteArray passBuf, jint passBufLen, + jbyteArray saltBuf, jint sBufLen, jint iterations, jint kLen, jint hashType) +{ +#if !defined(NO_PWDBASED) && defined(HAVE_PBKDF2) && !defined(NO_HMAC) + int ret = 0; + byte* pass = NULL; + byte* salt = NULL; + byte* outKey = NULL; + jbyteArray result = NULL; + (void)jcl; + + if (env == NULL || kLen == 0) { + throwWolfCryptExceptionFromError(env, BAD_FUNC_ARG); + return NULL; + } + + outKey = (byte*)XMALLOC(kLen, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (outKey == NULL) { + throwWolfCryptExceptionFromError(env, MEMORY_E); + return NULL; + } + XMEMSET(outKey, 0, kLen); + + if ((passBuf != NULL) && (passBufLen > 0)) { + pass = (byte*)(*env)->GetByteArrayElements(env, passBuf, NULL); + } + salt = (byte*)(*env)->GetByteArrayElements(env, saltBuf, NULL); + + ret = wc_PBKDF2(outKey, pass, passBufLen, salt, sBufLen, + iterations, kLen, hashType); + if (ret == 0) { + result = (*env)->NewByteArray(env, kLen); + if (result != NULL) { + (*env)->SetByteArrayRegion(env, result, 0, kLen, + (const jbyte*) outKey); + } else { + LogStr("NewByteArray failed in JNI PBKDF2\n"); + ret = MEMORY_E; + } + } + + if (outKey != NULL) { + XMEMSET(outKey, 0, kLen); + XFREE(outKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + + if (pass != NULL) { + (*env)->ReleaseByteArrayElements(env, passBuf, (jbyte*)pass, JNI_ABORT); + } + (*env)->ReleaseByteArrayElements(env, saltBuf, (jbyte*)salt, JNI_ABORT); + + if (ret != 0) { + throwWolfCryptExceptionFromError(env, ret); + return NULL; + } + + return result; +#else + (void)env; + (void)jcl; + (void)passBuf; + (void)passBufLen; + (void)salt; + (void)sBufLen; + (void)iterations; + (void)kLen; + (void)hashType; + throwWolfCryptExceptionFromError(env, NOT_COMPILED_IN); + return NULL; +#endif +} + diff --git a/jni/jni_wolfcrypt.c b/jni/jni_wolfcrypt.c index f3050a74..c57c597e 100644 --- a/jni/jni_wolfcrypt.c +++ b/jni/jni_wolfcrypt.c @@ -33,6 +33,90 @@ /* #define WOLFCRYPT_JNI_DEBUG_ON */ #include +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1NONE + (JNIEnv* env, jclass class) +{ + return WC_HASH_TYPE_NONE; +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1MD2 + (JNIEnv* env, jclass class) +{ + return WC_HASH_TYPE_MD2; +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1MD4 + (JNIEnv* env, jclass class) +{ + return WC_HASH_TYPE_MD4; +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1MD5 + (JNIEnv* env, jclass class) +{ + return WC_HASH_TYPE_MD5; +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1SHA + (JNIEnv* env, jclass class) +{ + return WC_HASH_TYPE_SHA; +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1SHA224 + (JNIEnv* env, jclass class) +{ + return WC_HASH_TYPE_SHA224; +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1SHA256 + (JNIEnv* env, jclass class) +{ + return WC_HASH_TYPE_SHA256; +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1SHA384 + (JNIEnv* env, jclass class) +{ + return WC_HASH_TYPE_SHA384; +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1SHA512 + (JNIEnv* env, jclass class) +{ + return WC_HASH_TYPE_SHA512; +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1MD5_1SHA + (JNIEnv* env, jclass class) +{ + return WC_HASH_TYPE_MD5_SHA; +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1SHA3_1224 + (JNIEnv* env, jclass class) +{ + return WC_HASH_TYPE_SHA3_224; +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1SHA3_1256 + (JNIEnv* env, jclass class) +{ + return WC_HASH_TYPE_SHA3_256; +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1SHA3_1384 + (JNIEnv* env, jclass class) +{ + return WC_HASH_TYPE_SHA3_384; +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_getWC_1HASH_1TYPE_1SHA3_1512 + (JNIEnv* env, jclass class) +{ + return WC_HASH_TYPE_SHA3_512; +} + JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_CrlEnabled (JNIEnv* env, jclass jcl) { diff --git a/makefile.linux b/makefile.linux index f80d3842..0b4fe24a 100644 --- a/makefile.linux +++ b/makefile.linux @@ -26,11 +26,12 @@ endif DIST_FILES = AUTHORS build.xml COPYING examples jni LICENSING makefile makefile.linux makefile.macosx \ pom.xml README_JCE.md README.md rpm src -OBJ_LIST = jni_fips.o jni_native_struct.o jni_aes.o jni_aesgcm.o jni_des3.o \ - jni_md5.o jni_sha.o jni_hmac.o jni_rng.o jni_rsa.o jni_dh.o \ - jni_ecc.o jni_ed25519.o jni_curve25519.o jni_chacha.o jni_error.o \ - jni_asn.o jni_logging.o jni_feature_detect.o jni_wolfobject.o \ - jni_wolfcrypt.o jni_wolfssl_cert_manager.o +OBJ_LIST = jni_fips.o jni_native_struct.o jni_pwdbased.o jni_aes.o \ + jni_aesgcm.o jni_des3.o jni_md5.o jni_sha.o jni_hmac.o \ + jni_rng.o jni_rsa.o jni_dh.o jni_ecc.o jni_ed25519.o \ + jni_curve25519.o jni_chacha.o jni_error.o jni_asn.o jni_logging.o \ + jni_feature_detect.o jni_wolfobject.o jni_wolfcrypt.o \ + jni_wolfssl_cert_manager.o OBJS = $(patsubst %,$(OUT_PATH)/%,$(OBJ_LIST)) TARGET = $(OUT_PATH)/libwolfcryptjni.so diff --git a/makefile.macosx b/makefile.macosx index 3985f037..58c10911 100644 --- a/makefile.macosx +++ b/makefile.macosx @@ -20,11 +20,12 @@ ifeq ($(WOLFSSL_LIBNAME),) WOLFSSL_LIBNAME=wolfssl endif -OBJ_LIST = jni_fips.o jni_native_struct.o jni_aes.o jni_aesgcm.o jni_des3.o \ - jni_md5.o jni_sha.o jni_hmac.o jni_rng.o jni_rsa.o jni_dh.o \ - jni_ecc.o jni_ed25519.o jni_curve25519.o jni_chacha.o jni_error.o \ - jni_asn.o jni_logging.o jni_feature_detect.o jni_wolfobject.o \ - jni_wolfcrypt.o jni_wolfssl_cert_manager.o +OBJ_LIST = jni_fips.o jni_native_struct.o jni_pwdbased.o jni_aes.o \ + jni_aesgcm.o jni_des3.o jni_md5.o jni_sha.o jni_hmac.o jni_rng.o \ + jni_rsa.o jni_dh.o jni_ecc.o jni_ed25519.o jni_curve25519.o \ + jni_chacha.o jni_error.o jni_asn.o jni_logging.o \ + jni_feature_detect.o jni_wolfobject.o jni_wolfcrypt.o \ + jni_wolfssl_cert_manager.o OBJS = $(patsubst %,$(OUT_PATH)/%,$(OBJ_LIST)) TARGET = $(OUT_PATH)/libwolfcryptjni.dylib diff --git a/scripts/infer.sh b/scripts/infer.sh index 7294c684..1f9dbbd0 100755 --- a/scripts/infer.sh +++ b/scripts/infer.sh @@ -34,29 +34,34 @@ infer run -- javac \ src/main/java/com/wolfssl/wolfcrypt/Md5.java \ src/main/java/com/wolfssl/wolfcrypt/MessageDigest.java \ src/main/java/com/wolfssl/wolfcrypt/NativeStruct.java \ + src/main/java/com/wolfssl/wolfcrypt/Pwdbased.java \ src/main/java/com/wolfssl/wolfcrypt/Rng.java \ src/main/java/com/wolfssl/wolfcrypt/Rsa.java \ + src/main/java/com/wolfssl/wolfcrypt/Sha.java \ src/main/java/com/wolfssl/wolfcrypt/Sha256.java \ src/main/java/com/wolfssl/wolfcrypt/Sha384.java \ src/main/java/com/wolfssl/wolfcrypt/Sha512.java \ - src/main/java/com/wolfssl/wolfcrypt/Sha.java \ + src/main/java/com/wolfssl/wolfcrypt/WolfCrypt.java \ src/main/java/com/wolfssl/wolfcrypt/WolfCryptError.java \ src/main/java/com/wolfssl/wolfcrypt/WolfCryptException.java \ - src/main/java/com/wolfssl/wolfcrypt/WolfCrypt.java \ src/main/java/com/wolfssl/wolfcrypt/WolfCryptState.java \ src/main/java/com/wolfssl/wolfcrypt/WolfObject.java \ + src/main/java/com/wolfssl/wolfcrypt/WolfSSLCertManager.java \ src/main/java/com/wolfssl/provider/jce/WolfCryptCipher.java \ src/main/java/com/wolfssl/provider/jce/WolfCryptDebug.java \ src/main/java/com/wolfssl/provider/jce/WolfCryptKeyAgreement.java \ src/main/java/com/wolfssl/provider/jce/WolfCryptKeyPairGenerator.java \ src/main/java/com/wolfssl/provider/jce/WolfCryptMac.java \ src/main/java/com/wolfssl/provider/jce/WolfCryptMessageDigestMd5.java \ + src/main/java/com/wolfssl/provider/jce/WolfCryptMessageDigestSha.java \ src/main/java/com/wolfssl/provider/jce/WolfCryptMessageDigestSha256.java \ src/main/java/com/wolfssl/provider/jce/WolfCryptMessageDigestSha384.java \ src/main/java/com/wolfssl/provider/jce/WolfCryptMessageDigestSha512.java \ - src/main/java/com/wolfssl/provider/jce/WolfCryptMessageDigestSha.java \ + src/main/java/com/wolfssl/provider/jce/WolfCryptPBEKey.java \ + src/main/java/com/wolfssl/provider/jce/WolfCryptPKIXCertPathValidator.java \ src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java \ src/main/java/com/wolfssl/provider/jce/WolfCryptRandom.java \ + src/main/java/com/wolfssl/provider/jce/WolfCryptSecretKeyFactory.java \ src/main/java/com/wolfssl/provider/jce/WolfCryptSignature.java # remove compiled class files diff --git a/src/main/java/com/wolfssl/provider/jce/WolfCryptPBEKey.java b/src/main/java/com/wolfssl/provider/jce/WolfCryptPBEKey.java new file mode 100644 index 00000000..a6bff881 --- /dev/null +++ b/src/main/java/com/wolfssl/provider/jce/WolfCryptPBEKey.java @@ -0,0 +1,291 @@ +/* WolfCryptPBEKey.java + * + * Copyright (C) 2006-2024 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 2 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.provider.jce; + +import java.util.Arrays; +import java.security.spec.InvalidKeySpecException; +import javax.security.auth.Destroyable; +import javax.crypto.interfaces.PBEKey; + +import com.wolfssl.provider.jce.WolfCryptDebug; + +/** + * wolfCrypt PBEKey implementation. + */ +public class WolfCryptPBEKey implements PBEKey, Destroyable { + + private static final long serialVersionUID = 1L; + + /** PBKDF2 iterations used to derive encoded key */ + private int iterations = 0; + + /** Password used to derive encoded key */ + private char[] password = null; + + /** Salt used to derive encoded key */ + private byte[] salt = null; + + /** Key encoded as byte array */ + private byte[] encoded = null; + + /** Algorithm used by this key */ + private String algorithm = null; + + /** Track if object has been destroyed */ + private boolean destroyed = false; + + /** Lock around use of destroyed boolean */ + private final Object destroyedLock = new Object(); + + /** + * Create new WolfCryptPBEKey object. + * + * @param password password used to derive the encoded key + * @param salt salt used to derive the encoded key + * @param iterations PBKDF iterations used to derive the encoded key + * @param algorithm algorithm of this key + * @param encoded encoded byte array of key + * + * @throws InvalidKeySpecException if arguments are not compatible + * with creation of this object + */ + protected WolfCryptPBEKey(char[] password, byte[] salt, + int iterations, String algorithm, byte[] encoded) + throws InvalidKeySpecException { + + if (salt == null || salt.length == 0) { + throw new InvalidKeySpecException( + "Salt cannot be null or zero length"); + } + + if (iterations <= 0) { + throw new InvalidKeySpecException( + "Iterations cannot be less than or equal to zero"); + } + + if (algorithm == null || algorithm.isEmpty()) { + throw new InvalidKeySpecException( + "Algorithm String cannot be null or empty"); + } + + if (encoded == null || encoded.length == 0) { + throw new InvalidKeySpecException( + "Encoded key cannot be null or zero length"); + } + + this.password = password.clone(); + this.salt = salt.clone(); + this.iterations = iterations; + this.algorithm = algorithm; + this.encoded = encoded.clone(); + } + + /** + * Check if this object has been destroyed with destroy(). + * + * @throws IllegalStateException if object has been destroyed + */ + private synchronized void checkDestroyed() + throws IllegalStateException { + + synchronized (destroyedLock) { + if (this.destroyed == true) { + throw new IllegalStateException( + "PBEKey has been destroyed"); + } + } + } + + /** + * Return password used with this PBEKey. + * + * @return a copy of the internal password buffer + * + * @throws IllegalStateException if object has been destroyed + */ + public synchronized char[] getPassword() { + + checkDestroyed(); + + if (this.password == null) { + return null; + } + + return this.password.clone(); + } + + /** + * Return salt used with this PBEKey. + * + * @return a copy of the internal salt buffer + * + * @throws IllegalStateException if object has been destroyed + */ + public synchronized byte[] getSalt() { + + checkDestroyed(); + + return this.salt.clone(); + } + + /** + * Return iteration count used with this PBEKey. + * + * @return iteration count + * + * @throws IllegalStateException if object has been destroyed + */ + public synchronized int getIterationCount() { + + checkDestroyed(); + + return this.iterations; + } + + /** + * Return algorithm String representing this PBEKey. + * + * @return PBE algorithm string matching this object + * + * @throws IllegalStateException if object has been destroyed + */ + public synchronized String getAlgorithm() { + + checkDestroyed(); + + return this.algorithm; + } + + /** + * Return encoding format for this PBEKey. + * + * @return encoding format string, will be "RAW" for this object + * + * @throws IllegalStateException if object has been destroyed + */ + public synchronized String getFormat() { + + checkDestroyed(); + + return "RAW"; + } + + /** + * Return encoded byte array of this PBEKey. + * + * @return encoded byte array + * + * @throws IllegalStateException if object has been destroyed + */ + public synchronized byte[] getEncoded() { + + checkDestroyed(); + + return this.encoded.clone(); + } + + /** + * Destroy this object. + * + * Calling this method will zeroize the password and salt arrays + * contained in this object and mark it as unusable. + */ + public synchronized void destroy() { + synchronized (destroyedLock) { + if (this.password != null) { + Arrays.fill(this.password, (char)0); + } + if (this.salt != null) { + Arrays.fill(this.salt, (byte)0); + } + this.iterations = 0; + this.algorithm = null; + this.destroyed = true; + } + } + + /** + * Return if this object has been destroyed. + * + * Object can be destroyed by calling destroy(), which will zeroize + * internal buffers for this object. + * + * @return true if object has been destroyed, otherwise false + */ + public synchronized boolean isDestroyed() { + synchronized (destroyedLock) { + if (this.destroyed) { + return true; + } + return false; + } + } + + @Override + public synchronized int hashCode() { + return Arrays.hashCode(encoded); + } + + @Override + public synchronized boolean equals(Object obj) { + + PBEKey pKey = null; + + synchronized (destroyedLock) { + if (obj == this) { + return true; + } + + if (!(obj instanceof PBEKey)) { + return false; + } + pKey = (PBEKey)obj; + + if (!Arrays.equals(pKey.getEncoded(), getEncoded())) { + return false; + } + + if (!Arrays.equals(pKey.getSalt(), getSalt())) { + return false; + } + + if (!Arrays.equals(pKey.getPassword(), getPassword())) { + return false; + } + + if (pKey.getIterationCount() != getIterationCount()) { + return false; + } + + if (!pKey.getAlgorithm().equals(getAlgorithm())) { + return false; + } + + if (!pKey.getFormat().equals(getFormat())) { + return false; + } + + return true; + } + } +} + diff --git a/src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java b/src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java index c07c2883..0d502c0f 100644 --- a/src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java +++ b/src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java @@ -178,6 +178,44 @@ public WolfCryptProvider() { put("CertPathValidator.PKIX", "com.wolfssl.provider.jce.WolfCryptPKIXCertPathValidator"); + /* SecretKeyFactory */ + if (FeatureDetect.HmacShaEnabled()) { + put("SecretKeyFactory.PBKDF2WithHmacSHA1", + "com.wolfssl.provider.jce.WolfCryptSecretKeyFactory$wcPBKDF2WithHmacSHA1"); + } + if (FeatureDetect.HmacSha224Enabled()) { + put("SecretKeyFactory.PBKDF2WithHmacSHA224", + "com.wolfssl.provider.jce.WolfCryptSecretKeyFactory$wcPBKDF2WithHmacSHA224"); + } + if (FeatureDetect.HmacSha256Enabled()) { + put("SecretKeyFactory.PBKDF2WithHmacSHA256", + "com.wolfssl.provider.jce.WolfCryptSecretKeyFactory$wcPBKDF2WithHmacSHA256"); + } + if (FeatureDetect.HmacSha384Enabled()) { + put("SecretKeyFactory.PBKDF2WithHmacSHA384", + "com.wolfssl.provider.jce.WolfCryptSecretKeyFactory$wcPBKDF2WithHmacSHA384"); + } + if (FeatureDetect.HmacSha512Enabled()) { + put("SecretKeyFactory.PBKDF2WithHmacSHA512", + "com.wolfssl.provider.jce.WolfCryptSecretKeyFactory$wcPBKDF2WithHmacSHA512"); + } + if (FeatureDetect.HmacSha3_224Enabled()) { + put("SecretKeyFactory.PBKDF2WithHmacSHA3-224", + "com.wolfssl.provider.jce.WolfCryptSecretKeyFactory$wcPBKDF2WithHmacSHA3_224"); + } + if (FeatureDetect.HmacSha3_256Enabled()) { + put("SecretKeyFactory.PBKDF2WithHmacSHA3-256", + "com.wolfssl.provider.jce.WolfCryptSecretKeyFactory$wcPBKDF2WithHmacSHA3_256"); + } + if (FeatureDetect.HmacSha3_384Enabled()) { + put("SecretKeyFactory.PBKDF2WithHmacSHA3-384", + "com.wolfssl.provider.jce.WolfCryptSecretKeyFactory$wcPBKDF2WithHmacSHA3_384"); + } + if (FeatureDetect.HmacSha3_512Enabled()) { + put("SecretKeyFactory.PBKDF2WithHmacSHA3-512", + "com.wolfssl.provider.jce.WolfCryptSecretKeyFactory$wcPBKDF2WithHmacSHA3_512"); + } + /* If using a FIPS version of wolfCrypt, allow private key to be * exported for use. Only applicable to FIPS 140-3 */ if (Fips.enabled) { diff --git a/src/main/java/com/wolfssl/provider/jce/WolfCryptSecretKeyFactory.java b/src/main/java/com/wolfssl/provider/jce/WolfCryptSecretKeyFactory.java new file mode 100644 index 00000000..bcf5b706 --- /dev/null +++ b/src/main/java/com/wolfssl/provider/jce/WolfCryptSecretKeyFactory.java @@ -0,0 +1,721 @@ +/* WolfCryptSecretKeyFactory.java + * + * Copyright (C) 2006-2024 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 2 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.provider.jce; + +import java.util.Arrays; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.StandardCharsets; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactorySpi; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.interfaces.PBEKey; +import java.security.NoSuchAlgorithmException; +import java.security.InvalidKeyException; +import java.security.spec.KeySpec; +import java.security.spec.InvalidKeySpecException; + +import com.wolfssl.wolfcrypt.WolfCrypt; +import com.wolfssl.wolfcrypt.Pwdbased; +import com.wolfssl.provider.jce.WolfCryptDebug; + +/** + * wolfCrypt JCE SecretKeyFactory implementation. + */ +public class WolfCryptSecretKeyFactory extends SecretKeyFactorySpi { + + private enum FactoryType { + WC_SKF_PBKDF2_HMAC_SHA1, + WC_SKF_PBKDF2_HMAC_SHA224, + WC_SKF_PBKDF2_HMAC_SHA256, + WC_SKF_PBKDF2_HMAC_SHA384, + WC_SKF_PBKDF2_HMAC_SHA512, + WC_SKF_PBKDF2_HMAC_SHA3_224, + WC_SKF_PBKDF2_HMAC_SHA3_256, + WC_SKF_PBKDF2_HMAC_SHA3_384, + WC_SKF_PBKDF2_HMAC_SHA3_512 + } + + /* PBKDF2/HMAC type of this factory */ + private FactoryType factoryType; + + /* String representation of this factory type */ + private String typeString; + + /* wolfCrypt int representing hash used in this factory */ + private int hashType; + + private WolfCryptDebug debug; + + private WolfCryptSecretKeyFactory(FactoryType type) + throws NoSuchAlgorithmException { + + this.factoryType = type; + + switch (type) { + case WC_SKF_PBKDF2_HMAC_SHA1: + this.hashType = WolfCrypt.WC_HASH_TYPE_SHA; + break; + + case WC_SKF_PBKDF2_HMAC_SHA224: + this.hashType = WolfCrypt.WC_HASH_TYPE_SHA224; + break; + + case WC_SKF_PBKDF2_HMAC_SHA256: + this.hashType = WolfCrypt.WC_HASH_TYPE_SHA256; + break; + + case WC_SKF_PBKDF2_HMAC_SHA384: + this.hashType = WolfCrypt.WC_HASH_TYPE_SHA384; + break; + + case WC_SKF_PBKDF2_HMAC_SHA512: + this.hashType = WolfCrypt.WC_HASH_TYPE_SHA512; + break; + + case WC_SKF_PBKDF2_HMAC_SHA3_224: + this.hashType = WolfCrypt.WC_HASH_TYPE_SHA3_224; + break; + + case WC_SKF_PBKDF2_HMAC_SHA3_256: + this.hashType = WolfCrypt.WC_HASH_TYPE_SHA3_256; + break; + + case WC_SKF_PBKDF2_HMAC_SHA3_384: + this.hashType = WolfCrypt.WC_HASH_TYPE_SHA3_384; + break; + + case WC_SKF_PBKDF2_HMAC_SHA3_512: + this.hashType = WolfCrypt.WC_HASH_TYPE_SHA3_512; + break; + + default: + throw new NoSuchAlgorithmException( + "Unsupported SecretKeyFactory type"); + } + + typeString = typeToString(type); + + log("created new SecretKeyFactory"); + } + + /** + * Internal method for logging output. + * + * @param msg message to be logged + */ + private void log(String msg) { + if (debug.DEBUG) { + debug.print("[SecretKeyFactory, " + typeString + "] " + msg); + } + } + + /** + * Return String name of provided FactoryType. + * + * @param type FactoryType to return corresponding name String for + * + * @return String name matching provided FactoryType + */ + private String typeToString(FactoryType type) { + switch (type) { + case WC_SKF_PBKDF2_HMAC_SHA1: + return "PBKDF2WithHmacSHA1"; + case WC_SKF_PBKDF2_HMAC_SHA224: + return "PBKDF2WithHmacSHA224"; + case WC_SKF_PBKDF2_HMAC_SHA256: + return "PBKDF2WithHmacSHA256"; + case WC_SKF_PBKDF2_HMAC_SHA384: + return "PBKDF2WithHmacSHA384"; + case WC_SKF_PBKDF2_HMAC_SHA512: + return "PBKDF2WithHmacSHA512"; + case WC_SKF_PBKDF2_HMAC_SHA3_224: + return "PBKDF2WithHmacSHA3-224"; + case WC_SKF_PBKDF2_HMAC_SHA3_256: + return "PBKDF2WithHmacSHA3-256"; + case WC_SKF_PBKDF2_HMAC_SHA3_384: + return "PBKDF2WithHmacSHA3-384"; + case WC_SKF_PBKDF2_HMAC_SHA3_512: + return "PBKDF2WithHmacSHA3-512"; + default: + return "None"; + } + } + + /** + * Test if this SecretKeyFactory is PBKDF2. + * + * @return true if PBKDF2 factory, otherwise false + */ + private boolean isFactoryPBKDF() { + + switch (this.factoryType) { + case WC_SKF_PBKDF2_HMAC_SHA1: + case WC_SKF_PBKDF2_HMAC_SHA224: + case WC_SKF_PBKDF2_HMAC_SHA256: + case WC_SKF_PBKDF2_HMAC_SHA384: + case WC_SKF_PBKDF2_HMAC_SHA512: + case WC_SKF_PBKDF2_HMAC_SHA3_224: + case WC_SKF_PBKDF2_HMAC_SHA3_256: + case WC_SKF_PBKDF2_HMAC_SHA3_384: + case WC_SKF_PBKDF2_HMAC_SHA3_512: + return true; + default: + return false; + } + } + + /** + * Test if provided algorithm String is supported by this + * SecretKeyFactory. + * + * @param algorithm String to test for support + * + * @return true if supported, otherwise false + */ + private boolean isAlgorithmSupported(String algo) { + + if (algo == null) { + return false; + } + + switch (algo) { + case "PBKDF2WithHmacSHA1": + case "PBKDF2WithHmacSHA224": + case "PBKDF2WithHmacSHA256": + case "PBKDF2WithHmacSHA384": + case "PBKDF2WithHmacSHA512": + case "PBKDF2WithHmacSHA3-224": + case "PBKDF2WithHmacSHA3-256": + case "PBKDF2WithHmacSHA3-384": + case "PBKDF2WithHmacSHA3-512": + return true; + default: + return false; + } + } + + /** + * Check if provided KeySpec is supported by this SecretKeyFactory. + * + * @throws InvalidKeySpecException if KeySpec is invalid or incompatible + * with this factory + */ + private void checkKeySpecSupported(KeySpec spec) + throws InvalidKeySpecException { + + if (spec == null) { + throw new InvalidKeySpecException("KeySpec cannot be null"); + } + + if (isFactoryPBKDF()) { + if (!(spec instanceof PBEKeySpec)) { + throw new InvalidKeySpecException( + "KeySpec must be type PBEKeySpec"); + } + } else { + throw new InvalidKeySpecException( + "Unsupported SecretKeyFactory type"); + } + } + + /** + * Convert password from char[] to byte[]. + * + * RFC 2898 (PBKDF2) considers password to be an octet string and + * recommends for interop ASCII or UTF-8 encoding is used. SunJCE uses + * UTF-8 for PBKDF2 SecretKeyFactory, so we do the same here for interop + * compatibility. + * + * @param pass password as char array + * + * @return password as UTF-8 encoded byte array, or null if input password + * is null or zero length + */ + private static byte[] passwordToByteArray(char[] pass) { + + byte[] passBytes = null; + CharBuffer passBuf = null; + ByteBuffer utf8Buf = null; + + if (pass == null || pass.length == 0) { + return null; + } + + passBuf = CharBuffer.wrap(pass); + utf8Buf = StandardCharsets.UTF_8.encode(passBuf); + passBytes = new byte[utf8Buf.limit()]; + utf8Buf.get(passBytes); + + return passBytes; + } + + /** + * Generate SecretKey (PBEKey) from provided PBEKeySpec. + * + * @param spec PBEKeySpec to use for generating SecretKey + * + * @throws InvalidKeySpecException if SecretKey generation fails + */ + private SecretKey genSecretKeyFromPBEKeySpec(PBEKeySpec spec) + throws InvalidKeySpecException { + + int iterations; + int kLen; + byte[] salt = null; + char[] pass = null; + byte[] derivedKey = null; + SecretKey key = null; + + try { + iterations = spec.getIterationCount(); + kLen = spec.getKeyLength(); + salt = spec.getSalt(); + pass = spec.getPassword(); + + if (salt == null || salt.length == 0) { + throw new InvalidKeySpecException( + "Null or zero length salt not allowed"); + } + + if (kLen < 8) { + throw new InvalidKeySpecException( + "Key length must be at least one byte (8 bits)"); + } + if ((kLen % 8) != 0) { + throw new InvalidKeySpecException( + "Key length bits is not divisible by 8 (byte conversion)"); + } + + /* Key length is given in bits, convert to bytes */ + kLen = kLen / 8; + + log("generating PBEKey (iterations: " + iterations + + ", key len: " + kLen + " bytes)"); + + derivedKey = Pwdbased.PBKDF2(passwordToByteArray(pass), + salt, iterations, kLen, this.hashType); + + if (derivedKey == null || derivedKey.length == 0) { + throw new InvalidKeySpecException( + "Error deriving key with PBKDF2"); + } + + key = new WolfCryptPBEKey(pass, salt, iterations, + this.typeString, derivedKey); + + } finally { + + iterations = 0; + kLen = 0; + + if (salt != null) { + Arrays.fill(salt, (byte)0); + } + if (pass != null) { + Arrays.fill(pass, (char)0); + } + if (derivedKey != null) { + Arrays.fill(derivedKey, (byte)0); + } + } + + return key; + } + + /** + * Generate a SecretKey object from the provided KeySpec. + * + * @param spec specification of the secret key + * + * @return SecretKey generated from KeySpec + * + * @throws InvalidKeySpecException if provided KeySpec is incorrect + * or incomplete for generating a SecretKey + */ + @Override + protected synchronized SecretKey engineGenerateSecret(KeySpec spec) + throws InvalidKeySpecException { + + log("generating SecretKey from KeySpec"); + + checkKeySpecSupported(spec); + + return genSecretKeyFromPBEKeySpec((PBEKeySpec)spec); + } + + /** + * Return a KeySpec from the provided PBEKey in the requested format. + * + * Called by engineGetKeySpec(). + * + * @param key PBEKey for which to return KeySpec + * @param keSpec the requested format that the KeySpec should be returned in + * + * @return KeySpec for the PBEKey, in the requested format + * + * @throws InvalidKeySpecException if the requested format is not + * appropriate for the given key, or the provided PBEKey + * cannot be used. + */ + private KeySpec getKeySpecFromPBEKeyByType(PBEKey key, Class keySpec) + throws InvalidKeySpecException { + + int iterations = 0; + char[] password = null; + byte[] salt = null; + byte[] encoded = null; + PBEKeySpec pbSpec = null; + + if (key != null && keySpec != null) { + + if (keySpec.isAssignableFrom(PBEKeySpec.class)) { + + try { + password = key.getPassword(); + salt = key.getSalt(); + iterations = key.getIterationCount(); + encoded = key.getEncoded(); + + if (encoded == null) { + throw new InvalidKeySpecException( + "Error getting encoded key from PBEKey"); + } + + pbSpec = new PBEKeySpec(password, salt, iterations, + encoded.length); + + } finally { + if (password != null) { + Arrays.fill(password, (char)0); + } + if (salt != null) { + Arrays.fill(salt, (byte)0); + } + if (encoded != null) { + Arrays.fill(encoded, (byte)0); + } + } + } + } + + return pbSpec; + } + + /** + * Return a KeySpec (key material) of the provided SecretKey in the + * requested format. + * + * @param key SecretKey for which to return KeySpec + * @param keySpec the requested format that the KeySpec should be + * returned in + * + * @return the KeySpec for the SecretKey in the requested format + * + * @throws InvalidKeySpecException if the requested format is not + * appropriate for the given key, or the provided SecretKey + * cannot be used. + */ + @Override + protected synchronized KeySpec engineGetKeySpec(SecretKey key, + Class keySpec) throws InvalidKeySpecException { + + PBEKey pKey = null; + int iterations = 0; + char[] password = null; + byte[] salt = null; + byte[] encoded = null; + + log("returning KeySpec from SecretKey in requested type"); + + if (key == null) { + throw new InvalidKeySpecException("SecretKey cannot be null"); + } + + if (keySpec == null) { + throw new InvalidKeySpecException( + "Requested KeySpec format cannot be null"); + } + + if (key instanceof PBEKey) { + return getKeySpecFromPBEKeyByType((PBEKey)key, keySpec); + } + else { + throw new InvalidKeySpecException( + "Only SecretKey of type PBEKey currently supported"); + } + } + + /** + * Translates PBEKey to one generated by this SecretKeyFactory. + * + * Called by engineTranslateKey(). + * + * @param PBEKey (SecretKey) to translate + * + * @return New/translated SecretKey (PBEKey) generated by this + * SecretKeyFactory. + * + * @throws InvalidKeyException if the provided SecretKey can not be + * used or converted + */ + private SecretKey translatePBEKey(PBEKey key) + throws InvalidKeyException { + + char[] password = null; + byte[] salt = null; + byte[] enc = null; + int iterations = 0; + PBEKeySpec spec = null; + SecretKey sKey = null; + + if (key != null) { + + if (!isAlgorithmSupported(key.getAlgorithm())) { + throw new InvalidKeyException( + "SecretKey algorithm not supported: " + key.getAlgorithm()); + } + + try { + iterations = key.getIterationCount(); + salt = key.getSalt(); + password = key.getPassword(); + enc = key.getEncoded(); + + if (enc == null) { + throw new InvalidKeySpecException( + "Error getting encoded key from PBEKey"); + } + + /* PBEKeySpec holds key length in bits */ + spec = new PBEKeySpec(password, salt, iterations, + enc.length * 8); + sKey = genSecretKeyFromPBEKeySpec(spec); + + } catch (InvalidKeySpecException e) { + throw new InvalidKeyException(e); + + } finally { + spec.clearPassword(); + + if (password != null) { + Arrays.fill(password, (char)0); + } + if (salt != null) { + Arrays.fill(salt, (byte)0); + } + if (enc != null) { + Arrays.fill(enc, (byte)0); + } + } + } + + return sKey; + } + + /** + * Translate a SecretKey object from another provider (or unknown source) + * into a SecretKey object from this SecretKeyFactory. + * + * This method will extract necessary parameters from the original + * SecretKey then re-generate the SecretKey using this factory. + * + * @param key SecretKey to translate + * + * @return Translated SecretKey object from this SecretKeyFactory + * + * @throws InvalidKeyException if the provided SecretKey can not be + * used or converted + */ + @Override + protected synchronized SecretKey engineTranslateKey(SecretKey key) + throws InvalidKeyException { + + log("translating SecretKey to wolfJCE SecretKeyFactory type"); + + if (key == null) { + throw new InvalidKeyException("SecretKey cannot be null"); + } + + if (key instanceof PBEKey) { + return translatePBEKey((PBEKey)key); + } + else { + throw new InvalidKeyException( + "Only SecretKey of type PBEKey currently supported"); + } + } + + /** + * wolfJCE PBKDF2WithHmacSHA1 SecretKeyFactory class. + */ + public static final class wcPBKDF2WithHmacSHA1 + extends WolfCryptSecretKeyFactory { + + /** + * Create new wcPBKDF2WithHmacSHA1 object. + * + * @throws NoSuchAlgorithmException if PBKDF2-HMAC-SHA1 is not + * available in native wolfCrypt. + */ + public wcPBKDF2WithHmacSHA1() throws NoSuchAlgorithmException { + super(FactoryType.WC_SKF_PBKDF2_HMAC_SHA1); + } + } + + /** + * wolfJCE PBKDF2WithHmacSHA224 SecretKeyFactory class. + */ + public static final class wcPBKDF2WithHmacSHA224 + extends WolfCryptSecretKeyFactory { + + /** + * Create new wcPBKDF2WithHmacSHA224 object. + * + * @throws NoSuchAlgorithmException if PBKDF2-HMAC-SHA224 is not + * available in native wolfCrypt. + */ + public wcPBKDF2WithHmacSHA224() throws NoSuchAlgorithmException { + super(FactoryType.WC_SKF_PBKDF2_HMAC_SHA224); + } + } + + /** + * wolfJCE PBKDF2WithHmacSHA256 SecretKeyFactory class. + */ + public static final class wcPBKDF2WithHmacSHA256 + extends WolfCryptSecretKeyFactory { + + /** + * Create new wcPBKDF2WithHmacSHA256 object. + * + * @throws NoSuchAlgorithmException if PBKDF2-HMAC-SHA256 is not + * available in native wolfCrypt. + */ + public wcPBKDF2WithHmacSHA256() throws NoSuchAlgorithmException { + super(FactoryType.WC_SKF_PBKDF2_HMAC_SHA256); + } + } + + /** + * wolfJCE PBKDF2WithHmacSHA384 SecretKeyFactory class. + */ + public static final class wcPBKDF2WithHmacSHA384 + extends WolfCryptSecretKeyFactory { + + /** + * Create new wcPBKDF2WithHmacSHA384 object. + * + * @throws NoSuchAlgorithmException if PBKDF2-HMAC-SHA384 is not + * available in native wolfCrypt. + */ + public wcPBKDF2WithHmacSHA384() throws NoSuchAlgorithmException { + super(FactoryType.WC_SKF_PBKDF2_HMAC_SHA384); + } + } + + /** + * wolfJCE PBKDF2WithHmacSHA512 SecretKeyFactory class. + */ + public static final class wcPBKDF2WithHmacSHA512 + extends WolfCryptSecretKeyFactory { + + /** + * Create new wcPBKDF2WithHmacSHA512 object. + * + * @throws NoSuchAlgorithmException if PBKDF2-HMAC-SHA512 is not + * available in native wolfCrypt. + */ + public wcPBKDF2WithHmacSHA512() throws NoSuchAlgorithmException { + super(FactoryType.WC_SKF_PBKDF2_HMAC_SHA512); + } + } + + /** + * wolfJCE PBKDF2WithHmacSHA3_224 SecretKeyFactory class. + */ + public static final class wcPBKDF2WithHmacSHA3_224 + extends WolfCryptSecretKeyFactory { + + /** + * Create new wcPBKDF2WithHmacSHA3_224 object. + * + * @throws NoSuchAlgorithmException if PBKDF2-HMAC-SHA3-224 is not + * available in native wolfCrypt. + */ + public wcPBKDF2WithHmacSHA3_224() throws NoSuchAlgorithmException { + super(FactoryType.WC_SKF_PBKDF2_HMAC_SHA3_224); + } + } + + /** + * wolfJCE PBKDF2WithHmacSHA3_256 SecretKeyFactory class. + */ + public static final class wcPBKDF2WithHmacSHA3_256 + extends WolfCryptSecretKeyFactory { + + /** + * Create new wcPBKDF2WithHmacSHA3_256 object. + * + * @throws NoSuchAlgorithmException if PBKDF2-HMAC-SHA3-256 is not + * available in native wolfCrypt. + */ + public wcPBKDF2WithHmacSHA3_256() throws NoSuchAlgorithmException { + super(FactoryType.WC_SKF_PBKDF2_HMAC_SHA3_256); + } + } + + /** + * wolfJCE PBKDF2WithHmacSHA3_384 SecretKeyFactory class. + */ + public static final class wcPBKDF2WithHmacSHA3_384 + extends WolfCryptSecretKeyFactory { + + /** + * Create new wcPBKDF2WithHmacSHA3_384 object. + * + * @throws NoSuchAlgorithmException if PBKDF2-HMAC-SHA3-384 is not + * available in native wolfCrypt. + */ + public wcPBKDF2WithHmacSHA3_384() throws NoSuchAlgorithmException { + super(FactoryType.WC_SKF_PBKDF2_HMAC_SHA3_384); + } + } + + /** + * wolfJCE PBKDF2WithHmacSHA3_512 SecretKeyFactory class. + */ + public static final class wcPBKDF2WithHmacSHA3_512 + extends WolfCryptSecretKeyFactory { + + /** + * Create new wcPBKDF2WithHmacSHA3_512 object. + * + * @throws NoSuchAlgorithmException if PBKDF2-HMAC-SHA3-512 is not + * available in native wolfCrypt. + */ + public wcPBKDF2WithHmacSHA3_512() throws NoSuchAlgorithmException { + super(FactoryType.WC_SKF_PBKDF2_HMAC_SHA3_512); + } + } +} + diff --git a/src/main/java/com/wolfssl/wolfcrypt/FeatureDetect.java b/src/main/java/com/wolfssl/wolfcrypt/FeatureDetect.java index 7ac3db1d..bbe045c1 100644 --- a/src/main/java/com/wolfssl/wolfcrypt/FeatureDetect.java +++ b/src/main/java/com/wolfssl/wolfcrypt/FeatureDetect.java @@ -139,6 +139,13 @@ public class FeatureDetect { */ public static native boolean HmacShaEnabled(); + /** + * Tests if HMAC-SHA224 is compiled into the native wolfSSL library. + * + * @return true if enabled, otherwise false. + */ + public static native boolean HmacSha224Enabled(); + /** * Tests if HMAC-SHA256 is compiled into the native wolfSSL library. * @@ -160,6 +167,42 @@ public class FeatureDetect { */ public static native boolean HmacSha512Enabled(); + /** + * Tests if HMAC-SHA3-224 is compiled into the native wolfSSL library. + * + * @return true if enabled, otherwise false. + */ + public static native boolean HmacSha3_224Enabled(); + + /** + * Tests if HMAC-SHA3-256 is compiled into the native wolfSSL library. + * + * @return true if enabled, otherwise false. + */ + public static native boolean HmacSha3_256Enabled(); + + /** + * Tests if HMAC-SHA3-384 is compiled into the native wolfSSL library. + * + * @return true if enabled, otherwise false. + */ + public static native boolean HmacSha3_384Enabled(); + + /** + * Tests if HMAC-SHA3-512 is compiled into the native wolfSSL library. + * + * @return true if enabled, otherwise false. + */ + public static native boolean HmacSha3_512Enabled(); + + /** + * Tests if PKCS#5 v2.1 PBKDF2 is compiled into the native wolfSSL library. + * + * @return true if PBKDF2 is enabled (HAVE_PBKDF2, !NO_PWDBASED, !NO_HMAC), + * otherwise false. + */ + public static native boolean Pbkdf2Enabled(); + /** * Tests if RSA is compiled into the native wolfSSL library. * diff --git a/src/main/java/com/wolfssl/wolfcrypt/Pwdbased.java b/src/main/java/com/wolfssl/wolfcrypt/Pwdbased.java new file mode 100644 index 00000000..6e1bdac2 --- /dev/null +++ b/src/main/java/com/wolfssl/wolfcrypt/Pwdbased.java @@ -0,0 +1,135 @@ +/* Pwdbased.java + * + * Copyright (C) 2006-2024 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 2 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; + +import java.util.Enumeration; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.security.cert.X509CRL; +import java.security.cert.CRLException; +import java.security.cert.CertificateEncodingException; + +/** + * Password based key derivation class with wraps native wolfCrypt + * pwdbased.c/h APIs. + * + * @author wolfSSL + */ +public class Pwdbased { + + static native byte[] wc_PKCS12_PBKDF(byte[] passwd, int pLen, + byte[] salt, int sLen, int iterations, int kLen, int typeH, + int id); + static native byte[] wc_PBKDF2(byte[] passwd, int pLen, byte[] salt, + int sLen, int iterations, int kLen, int hashType); + + /** + * Create new Pwdbased object. + * + * Currently all methods in this class are static, so no initialization + * logic needed. + */ + public Pwdbased() { + } + + /** + * PKCS#12 PBKDF (Password Based Key Derivation Function). + * + * Implements the PBKDF from RFC 7292 Appendix B. This method converts + * an input password with a concatenated salt into a more secure key, + * which it returns as output. It allows a user to select any of the + * supported HMAC hash functions. + * + * @param passwd byte array containing the password to use for key + * derivation + * @param salt byte array containing salt to use for key generation + * @param iterations number of times to process the hash + * @param kLen desired length of the derived key + * @param typeH the hashing algorithm to use + * @param id byte identifier indicating the purpose of the key + * generation. It is used to diversify the key output, and should + * be assigned as follows: + * ID=1: pseudorandom bits are to be used as key material for + * performing encryption or decryption + * ID=2: pseduorandom bits are to be used as asn IV (Initial + * Value) for encryption or decryption. + * ID=3: pseudorandom bits are to be used as an integrity key + * for MACing. + * + * @return new byte[] containing derived key + * + * @throws WolfCryptException on native wolfCrypt error + */ + public static synchronized byte[] PKCS12_PBKDF(byte[] passwd, byte[] salt, + int iterations, int kLen, int typeH, int id) throws WolfCryptException { + + /* Throws WolfCryptException with error on failure */ + return wc_PKCS12_PBKDF(passwd, passwd.length, salt, salt.length, + iterations, kLen, typeH, id); + } + + /** + * Implements the PBKDF2 from PKCS#5. This method converts and input + * password with a concatenated salt into a more secure key, + * which it returns as output. It allows a user to select any of the + * supported hash functions. + * + * @param passwd byte array containing the password to use for key + * derivation, can be null + * @param salt byte array containing salt to use for key generation + * @param iterations number of times to process the hash + * @param kLen desired length of the derived key + * @param hashType the hashing algorithm to use, from WolfCrypt class and + * one of the following: + * WolfCrypt.WC_HASH_TYPE_MD5 + * WolfCrypt.WC_HASH_TYPE_SHA + * WolfCrypt.WC_HASH_TYPE_SHA224 + * WolfCrypt.WC_HASH_TYPE_SHA256 + * WolfCrypt.WC_HASH_TYPE_SHA384 + * WolfCrypt.WC_HASH_TYPE_SHA512 + * WolfCrypt.WC_HASH_TYPE_SHA3_224 + * WolfCrypt.WC_HASH_TYPE_SHA3_256 + * WolfCrypt.WC_HASH_TYPE_SHA3_384 + * WolfCrypt.WC_HASH_TYPE_SHA3_512 + * + * @return new byte[] containing derived key + * + * @throws IllegalArgumentException on invalid arguments + * @throws WolfCryptException on native wolfCrypt error + */ + public static synchronized byte[] PBKDF2(byte[] passwd, byte[] salt, + int iterations, int kLen, int hashType) throws WolfCryptException { + + int passLen = 0; + + if (passwd != null) { + passLen = passwd.length; + } + + /* Throws WolfCryptException with error on failure */ + return wc_PBKDF2(passwd, passLen, salt, salt.length, + iterations, kLen, hashType); + } +} + diff --git a/src/main/java/com/wolfssl/wolfcrypt/WolfCrypt.java b/src/main/java/com/wolfssl/wolfcrypt/WolfCrypt.java index 88b8989b..abf3f0ca 100644 --- a/src/main/java/com/wolfssl/wolfcrypt/WolfCrypt.java +++ b/src/main/java/com/wolfssl/wolfcrypt/WolfCrypt.java @@ -51,6 +51,82 @@ public class WolfCrypt extends WolfObject { /** Size of 2048 bits in bytes */ public static final int SIZE_OF_2048_BITS = 256; + /* + * Native wolfCrypt hash types, from wolfssl/wolfcrypt/types.h + * wc_HashType enum. + */ + + /** wolfSSL hash type: None */ + public static int WC_HASH_TYPE_NONE = + WolfCrypt.getWC_HASH_TYPE_NONE(); + + /** wolfSSL hash type: MD2 */ + public static int WC_HASH_TYPE_MD2 = + WolfCrypt.getWC_HASH_TYPE_MD2(); + + /** wolfSSL hash type: MD4 */ + public static int WC_HASH_TYPE_MD4 = + WolfCrypt.getWC_HASH_TYPE_MD4(); + + /** wolfSSL hash type: MD5 */ + public static int WC_HASH_TYPE_MD5 = + WolfCrypt.getWC_HASH_TYPE_MD5(); + + /** wolfSSL hash type: SHA-1 */ + public static int WC_HASH_TYPE_SHA = + WolfCrypt.getWC_HASH_TYPE_SHA(); + + /** wolfSSL hash type: SHA-224 */ + public static int WC_HASH_TYPE_SHA224 = + WolfCrypt.getWC_HASH_TYPE_SHA224(); + + /** wolfSSL hash type: SHA-256 */ + public static int WC_HASH_TYPE_SHA256 = + WolfCrypt.getWC_HASH_TYPE_SHA256(); + + /** wolfSSL hash type: SHA-384 */ + public static int WC_HASH_TYPE_SHA384 = + WolfCrypt.getWC_HASH_TYPE_SHA384(); + + /** wolfSSL hash type: SHA-512 */ + public static int WC_HASH_TYPE_SHA512 = + WolfCrypt.getWC_HASH_TYPE_SHA512(); + + /** wolfSSL hash type: MD5-SHA */ + public static int WC_HASH_TYPE_MD5_SHA = + WolfCrypt.getWC_HASH_TYPE_MD5_SHA(); + + /** wolfSSL hash type: SHA3-224 */ + public static int WC_HASH_TYPE_SHA3_224 = + WolfCrypt.getWC_HASH_TYPE_SHA3_224(); + + /** wolfSSL hash type: SHA3-256 */ + public static int WC_HASH_TYPE_SHA3_256 = + WolfCrypt.getWC_HASH_TYPE_SHA3_256(); + + /** wolfSSL hash type: SHA3-384 */ + public static int WC_HASH_TYPE_SHA3_384 = + WolfCrypt.getWC_HASH_TYPE_SHA3_384(); + + /** wolfSSL hash type: SHA3-512 */ + public static int WC_HASH_TYPE_SHA3_512 = + WolfCrypt.getWC_HASH_TYPE_SHA3_512(); + + private static native int getWC_HASH_TYPE_NONE(); + private static native int getWC_HASH_TYPE_MD2(); + private static native int getWC_HASH_TYPE_MD4(); + private static native int getWC_HASH_TYPE_MD5(); + private static native int getWC_HASH_TYPE_SHA(); + private static native int getWC_HASH_TYPE_SHA224(); + private static native int getWC_HASH_TYPE_SHA256(); + private static native int getWC_HASH_TYPE_SHA384(); + private static native int getWC_HASH_TYPE_SHA512(); + private static native int getWC_HASH_TYPE_MD5_SHA(); + private static native int getWC_HASH_TYPE_SHA3_224(); + private static native int getWC_HASH_TYPE_SHA3_256(); + private static native int getWC_HASH_TYPE_SHA3_384(); + private static native int getWC_HASH_TYPE_SHA3_512(); + /* Public mappings of some SSL/TLS level enums/defines */ /** wolfSSL file type: PEM */ public static int SSL_FILETYPE_PEM = 1; diff --git a/src/test/java/com/wolfssl/provider/jce/test/WolfCryptSecretKeyFactoryTest.java b/src/test/java/com/wolfssl/provider/jce/test/WolfCryptSecretKeyFactoryTest.java new file mode 100644 index 00000000..d2601894 --- /dev/null +++ b/src/test/java/com/wolfssl/provider/jce/test/WolfCryptSecretKeyFactoryTest.java @@ -0,0 +1,1090 @@ +/* wolfCryptSecretKeyFactoryTest.java + * + * Copyright (C) 2006-2024 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 2 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.provider.jce.test; + +import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.Assume; +import org.junit.BeforeClass; + +import java.util.Arrays; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.LinkedBlockingQueue; +import java.security.Security; +import java.security.Provider; +import java.security.NoSuchProviderException; +import java.security.NoSuchAlgorithmException; +import java.security.InvalidKeyException; +import java.security.spec.KeySpec; +import java.security.spec.InvalidKeySpecException; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; + +import com.wolfssl.wolfcrypt.Fips; +import com.wolfssl.wolfcrypt.FeatureDetect; +import com.wolfssl.provider.jce.WolfCryptProvider; + +public class WolfCryptSecretKeyFactoryTest { + + /* Test provider, switching this here will run all tests below + * against the given provider. */ + private static final String provider = "wolfJCE"; + + private static String wolfJCEAlgos[] = { + "PBKDF2WithHmacSHA1", + "PBKDF2WithHmacSHA224", + "PBKDF2WithHmacSHA256", + "PBKDF2WithHmacSHA384", + "PBKDF2WithHmacSHA512", + "PBKDF2WithHmacSHA3-224", + "PBKDF2WithHmacSHA3-256", + "PBKDF2WithHmacSHA3-384", + "PBKDF2WithHmacSHA3-512" + }; + + private static ArrayList enabledAlgos = + new ArrayList(); + + private boolean algoSupported(String algo) { + return enabledAlgos.contains(algo); + } + + @BeforeClass + public static void testProviderInstallationAtRuntime() + throws NoSuchProviderException { + + SecretKeyFactory kf; + + /* Install wolfJCE provider at runtime. Not registering as top priority + * provider so we can still likely get SunJCE or platform provider + * when not specifying wolfJCE explicitly. */ + Security.addProvider(new WolfCryptProvider()); + + Provider p = Security.getProvider(provider); + assertNotNull(p); + + /* populate enabledAlgos, some native features may be compiled out */ + for (int i = 0; i < wolfJCEAlgos.length; i++) { + try { + kf = SecretKeyFactory.getInstance(wolfJCEAlgos[i], provider); + enabledAlgos.add(wolfJCEAlgos[i]); + } catch (NoSuchAlgorithmException e) { + /* algo not compiled in */ + } + } + } + + @Test + public void testGetSecretKeyFactoryFromProvider() + throws NoSuchProviderException, NoSuchAlgorithmException { + + SecretKeyFactory kf; + + /* try to get all available options we expect to have */ + for (int i = 0; i < enabledAlgos.size(); i++) { + kf = SecretKeyFactory.getInstance(enabledAlgos.get(i), provider); + } + + /* getting a garbage algorithm should throw an exception */ + try { + kf = SecretKeyFactory.getInstance("NotValid", provider); + + fail("SecretKeyFactory.getInstance should throw " + + "NoSuchAlgorithmException when given bad algorithm value"); + + } catch (NoSuchAlgorithmException e) { } + } + + /** + * PBKDF2-HMAC-SHA1 basic test vector. + * + * This test vector was generated from native wolfCrypt, based off + * the same parameters as the PBKDF2-HMAC-SHA256 vector in + * wolfcrypt/test/test.c. This is meant to test basic functionality. + */ + @Test + public void testPBKDF2WithHmacSHA1() + throws NoSuchAlgorithmException, InvalidKeySpecException, + NoSuchProviderException { + + char[] pass = "passwordpassword".toCharArray(); + byte[] salt = { + (byte)0x78, (byte)0x57, (byte)0x8E, (byte)0x5a, + (byte)0x5d, (byte)0x63, (byte)0xcb, (byte)0x06 + }; + int iterations = 2048; + int kLen = 192; + byte[] verify = { + (byte)0xba, (byte)0x9b, (byte)0x3b, (byte)0x95, + (byte)0x04, (byte)0x4d, (byte)0x78, (byte)0x11, + (byte)0xec, (byte)0xa1, (byte)0xff, (byte)0x3f, + (byte)0xea, (byte)0x3a, (byte)0xdb, (byte)0x55, + (byte)0x3e, (byte)0x54, (byte)0x0b, (byte)0xa0, + (byte)0x9f, (byte)0xad, (byte)0xe6, (byte)0x81 + }; + byte[] result = null; + PBEKeySpec spec = null; + SecretKeyFactory sf = null; + SecretKey key = null; + + if (!FeatureDetect.Pbkdf2Enabled() || + !FeatureDetect.HmacShaEnabled() || + !algoSupported("PBKDF2WithHmacSHA1")) { + System.out.println( + "Skipped: SecretKeyFactory(PBKDF2WithHmacSHA1) test"); + Assume.assumeTrue(false); + } + + spec = new PBEKeySpec(pass, salt, iterations, kLen); + assertNotNull(spec); + + sf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1", provider); + assertNotNull(sf); + + key = sf.generateSecret(spec); + assertNotNull(key); + result = key.getEncoded(); + assertNotNull(result); + assertTrue(Arrays.equals(result, verify)); + } + + /** + * PBKDF2-HMAC-SHA224 basic test vector. + * + * This test vector was generated from native wolfCrypt, based off + * the same parameters as the PBKDF2-HMAC-SHA256 vector in + * wolfcrypt/test/test.c. This is meant to test basic functionality. + */ + @Test + public void testPBKDF2WithHmacSHA224() + throws NoSuchAlgorithmException, InvalidKeySpecException, + NoSuchProviderException { + + char[] pass = "passwordpassword".toCharArray(); + byte[] salt = { + (byte)0x78, (byte)0x57, (byte)0x8E, (byte)0x5a, + (byte)0x5d, (byte)0x63, (byte)0xcb, (byte)0x06 + }; + int iterations = 2048; + int kLen = 192; + byte[] verify = { + (byte)0xeb, (byte)0xa2, (byte)0x2b, (byte)0x95, + (byte)0x0d, (byte)0x5b, (byte)0xf5, (byte)0x74, + (byte)0x2b, (byte)0xa2, (byte)0x8d, (byte)0xb0, + (byte)0x6e, (byte)0x19, (byte)0xe7, (byte)0x61, + (byte)0x57, (byte)0xa3, (byte)0x19, (byte)0xe7, + (byte)0x5f, (byte)0xfa, (byte)0x22, (byte)0xb2 + }; + byte[] result = null; + PBEKeySpec spec = null; + SecretKeyFactory sf = null; + SecretKey key = null; + + if (!FeatureDetect.Pbkdf2Enabled() || + !FeatureDetect.HmacSha224Enabled() || + !algoSupported("PBKDF2WithHmacSHA224")) { + System.out.println( + "Skipped: SecretKeyFactory(PBKDF2WithHmacSHA224) test"); + Assume.assumeTrue(false); + } + + spec = new PBEKeySpec(pass, salt, iterations, kLen); + assertNotNull(spec); + + sf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA224", provider); + assertNotNull(sf); + + key = sf.generateSecret(spec); + assertNotNull(key); + result = key.getEncoded(); + assertNotNull(result); + assertTrue(Arrays.equals(result, verify)); + } + + /** + * PBKDF2-HMAC-SHA256 basic test vector. + * + * This test vector comes directly from native wolfCrypt + * wolfcrypt/test/test.c. + */ + @Test + public void testPBKDF2WithHmacSHA256() + throws NoSuchAlgorithmException, InvalidKeySpecException, + NoSuchProviderException { + + char[] pass = "passwordpassword".toCharArray(); + byte[] salt = { + (byte)0x78, (byte)0x57, (byte)0x8E, (byte)0x5a, + (byte)0x5d, (byte)0x63, (byte)0xcb, (byte)0x06 + }; + int iterations = 2048; + int kLen = 192; + byte[] verify = { + (byte)0x43, (byte)0x6d, (byte)0xb5, (byte)0xe8, + (byte)0xd0, (byte)0xfb, (byte)0x3f, (byte)0x35, + (byte)0x42, (byte)0x48, (byte)0x39, (byte)0xbc, + (byte)0x2d, (byte)0xd4, (byte)0xf9, (byte)0x37, + (byte)0xd4, (byte)0x95, (byte)0x16, (byte)0xa7, + (byte)0x2a, (byte)0x9a, (byte)0x21, (byte)0xd1 + }; + byte[] result = null; + PBEKeySpec spec = null; + SecretKeyFactory sf = null; + SecretKey key = null; + + if (!FeatureDetect.Pbkdf2Enabled() || + !FeatureDetect.HmacSha256Enabled() || + !algoSupported("PBKDF2WithHmacSHA256")) { + System.out.println( + "Skipped: SecretKeyFactory(PBKDF2WithHmacSHA256) test"); + Assume.assumeTrue(false); + } + + spec = new PBEKeySpec(pass, salt, iterations, kLen); + assertNotNull(spec); + + sf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256", provider); + assertNotNull(sf); + + key = sf.generateSecret(spec); + assertNotNull(key); + result = key.getEncoded(); + assertNotNull(result); + assertTrue(Arrays.equals(result, verify)); + } + + /** + * PBKDF2-HMAC-SHA384 basic test vector. + * + * This test vector was generated from native wolfCrypt, based off + * the same parameters as the PBKDF2-HMAC-SHA256 vector in + * wolfcrypt/test/test.c. This is meant to test basic functionality. + */ + @Test + public void testPBKDF2WithHmacSHA384() + throws NoSuchAlgorithmException, InvalidKeySpecException, + NoSuchProviderException { + + char[] pass = "passwordpassword".toCharArray(); + byte[] salt = { + (byte)0x78, (byte)0x57, (byte)0x8E, (byte)0x5a, + (byte)0x5d, (byte)0x63, (byte)0xcb, (byte)0x06 + }; + int iterations = 2048; + int kLen = 192; + byte[] verify = { + (byte)0xa4, (byte)0xf5, (byte)0x63, (byte)0x91, + (byte)0x66, (byte)0xd9, (byte)0xe6, (byte)0xec, + (byte)0xa2, (byte)0x52, (byte)0x58, (byte)0x06, + (byte)0xa9, (byte)0x8c, (byte)0x18, (byte)0xc1, + (byte)0x81, (byte)0x2e, (byte)0xc2, (byte)0xfa, + (byte)0x8d, (byte)0x3d, (byte)0xb8, (byte)0x38 + }; + byte[] result = null; + PBEKeySpec spec = null; + SecretKeyFactory sf = null; + SecretKey key = null; + + if (!FeatureDetect.Pbkdf2Enabled() || + !FeatureDetect.HmacSha384Enabled() || + !algoSupported("PBKDF2WithHmacSHA384")) { + System.out.println( + "Skipped: SecretKeyFactory(PBKDF2WithHmacSHA384) test"); + Assume.assumeTrue(false); + } + + spec = new PBEKeySpec(pass, salt, iterations, kLen); + assertNotNull(spec); + + sf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA384", provider); + assertNotNull(sf); + + key = sf.generateSecret(spec); + assertNotNull(key); + result = key.getEncoded(); + assertNotNull(result); + assertTrue(Arrays.equals(result, verify)); + } + + /** + * PBKDF2-HMAC-SHA512 basic test vector. + * + * This test vector was generated from native wolfCrypt, based off + * the same parameters as the PBKDF2-HMAC-SHA256 vector in + * wolfcrypt/test/test.c. This is meant to test basic functionality. + */ + @Test + public void testPBKDF2WithHmacSHA512() + throws NoSuchAlgorithmException, InvalidKeySpecException, + NoSuchProviderException { + + char[] pass = "passwordpassword".toCharArray(); + byte[] salt = { + (byte)0x78, (byte)0x57, (byte)0x8E, (byte)0x5a, + (byte)0x5d, (byte)0x63, (byte)0xcb, (byte)0x06 + }; + int iterations = 2048; + int kLen = 192; + byte[] verify = { + (byte)0xa9, (byte)0x2f, (byte)0x3e, (byte)0x9c, + (byte)0xb1, (byte)0xcd, (byte)0x5b, (byte)0xab, + (byte)0xb6, (byte)0x43, (byte)0xe6, (byte)0x70, + (byte)0x2f, (byte)0x91, (byte)0x29, (byte)0x03, + (byte)0x93, (byte)0xdb, (byte)0x48, (byte)0x69, + (byte)0x93, (byte)0x01, (byte)0x29, (byte)0x42 + }; + byte[] result = null; + PBEKeySpec spec = null; + SecretKeyFactory sf = null; + SecretKey key = null; + + if (!FeatureDetect.Pbkdf2Enabled() || + !FeatureDetect.HmacSha512Enabled() || + !algoSupported("PBKDF2WithHmacSHA512")) { + System.out.println( + "Skipped: SecretKeyFactory(PBKDF2WithHmacSHA512) test"); + Assume.assumeTrue(false); + } + + spec = new PBEKeySpec(pass, salt, iterations, kLen); + assertNotNull(spec); + + sf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512", provider); + assertNotNull(sf); + + key = sf.generateSecret(spec); + assertNotNull(key); + result = key.getEncoded(); + assertNotNull(result); + assertTrue(Arrays.equals(result, verify)); + } + + /** + * PBKDF2-HMAC-SHA3-224 basic test vector. + * + * This test vector was generated from native wolfCrypt, based off + * the same parameters as the PBKDF2-HMAC-SHA256 vector in + * wolfcrypt/test/test.c. This is meant to test basic functionality. + */ + @Test + public void testPBKDF2WithHmacSHA3_224() + throws NoSuchAlgorithmException, InvalidKeySpecException, + NoSuchProviderException { + + char[] pass = "passwordpassword".toCharArray(); + byte[] salt = { + (byte)0x78, (byte)0x57, (byte)0x8E, (byte)0x5a, + (byte)0x5d, (byte)0x63, (byte)0xcb, (byte)0x06 + }; + int iterations = 2048; + int kLen = 192; + byte[] verify = { + (byte)0x66, (byte)0x63, (byte)0x94, (byte)0xe8, + (byte)0x75, (byte)0x02, (byte)0xdb, (byte)0xad, + (byte)0x64, (byte)0x5b, (byte)0xcd, (byte)0x28, + (byte)0xae, (byte)0x59, (byte)0xe5, (byte)0x89, + (byte)0x97, (byte)0x35, (byte)0xb4, (byte)0x8c, + (byte)0xc3, (byte)0xe9, (byte)0x02, (byte)0x0e + }; + byte[] result = null; + PBEKeySpec spec = null; + SecretKeyFactory sf = null; + SecretKey key = null; + + if (!FeatureDetect.Pbkdf2Enabled() || + !FeatureDetect.HmacSha3_224Enabled() || + !algoSupported("PBKDF2WithHmacSHA3-224")) { + System.out.println( + "Skipped: SecretKeyFactory(PBKDF2WithHmacSHA3-224) test"); + Assume.assumeTrue(false); + } + + spec = new PBEKeySpec(pass, salt, iterations, kLen); + assertNotNull(spec); + + sf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA3-224", provider); + assertNotNull(sf); + + key = sf.generateSecret(spec); + assertNotNull(key); + result = key.getEncoded(); + assertNotNull(result); + assertTrue(Arrays.equals(result, verify)); + } + + /** + * PBKDF2-HMAC-SHA3-256 basic test vector. + * + * This test vector was generated from native wolfCrypt, based off + * the same parameters as the PBKDF2-HMAC-SHA256 vector in + * wolfcrypt/test/test.c. This is meant to test basic functionality. + */ + @Test + public void testPBKDF2WithHmacSHA3_256() + throws NoSuchAlgorithmException, InvalidKeySpecException, + NoSuchProviderException { + + char[] pass = "passwordpassword".toCharArray(); + byte[] salt = { + (byte)0x78, (byte)0x57, (byte)0x8E, (byte)0x5a, + (byte)0x5d, (byte)0x63, (byte)0xcb, (byte)0x06 + }; + int iterations = 2048; + int kLen = 192; + byte[] verify = { + (byte)0xc3, (byte)0xdb, (byte)0x13, (byte)0x90, + (byte)0x0f, (byte)0x34, (byte)0x54, (byte)0xcf, + (byte)0x44, (byte)0x8a, (byte)0xae, (byte)0xda, + (byte)0x6f, (byte)0xa4, (byte)0x98, (byte)0xad, + (byte)0x8e, (byte)0x2d, (byte)0x73, (byte)0xb2, + (byte)0x8b, (byte)0xfd, (byte)0x27, (byte)0x46 + }; + byte[] result = null; + PBEKeySpec spec = null; + SecretKeyFactory sf = null; + SecretKey key = null; + + if (!FeatureDetect.Pbkdf2Enabled() || + !FeatureDetect.HmacSha3_256Enabled() || + !algoSupported("PBKDF2WithHmacSHA3-256")) { + System.out.println( + "Skipped: SecretKeyFactory(PBKDF2WithHmacSHA3-256) test"); + Assume.assumeTrue(false); + } + + spec = new PBEKeySpec(pass, salt, iterations, kLen); + assertNotNull(spec); + + sf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA3-256", provider); + assertNotNull(sf); + + key = sf.generateSecret(spec); + assertNotNull(key); + result = key.getEncoded(); + assertNotNull(result); + assertTrue(Arrays.equals(result, verify)); + } + + /** + * PBKDF2-HMAC-SHA3-384 basic test vector. + * + * This test vector was generated from native wolfCrypt, based off + * the same parameters as the PBKDF2-HMAC-SHA256 vector in + * wolfcrypt/test/test.c. This is meant to test basic functionality. + */ + @Test + public void testPBKDF2WithHmacSHA3_384() + throws NoSuchAlgorithmException, InvalidKeySpecException, + NoSuchProviderException { + + char[] pass = "passwordpassword".toCharArray(); + byte[] salt = { + (byte)0x78, (byte)0x57, (byte)0x8E, (byte)0x5a, + (byte)0x5d, (byte)0x63, (byte)0xcb, (byte)0x06 + }; + int iterations = 2048; + int kLen = 192; + byte[] verify = { + (byte)0x81, (byte)0x76, (byte)0xf5, (byte)0x57, + (byte)0x38, (byte)0x0d, (byte)0x76, (byte)0x32, + (byte)0x62, (byte)0xf1, (byte)0xb0, (byte)0xc2, + (byte)0xe6, (byte)0x66, (byte)0xbf, (byte)0xc3, + (byte)0x9f, (byte)0xc3, (byte)0x7b, (byte)0x3b, + (byte)0x44, (byte)0x11, (byte)0x81, (byte)0x5b + }; + byte[] result = null; + PBEKeySpec spec = null; + SecretKeyFactory sf = null; + SecretKey key = null; + + if (!FeatureDetect.Pbkdf2Enabled() || + !FeatureDetect.HmacSha3_384Enabled() || + !algoSupported("PBKDF2WithHmacSHA3-384")) { + System.out.println( + "Skipped: SecretKeyFactory(PBKDF2WithHmacSHA3-384) test"); + Assume.assumeTrue(false); + } + + spec = new PBEKeySpec(pass, salt, iterations, kLen); + assertNotNull(spec); + + sf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA3-384", provider); + assertNotNull(sf); + + key = sf.generateSecret(spec); + assertNotNull(key); + result = key.getEncoded(); + assertNotNull(result); + assertTrue(Arrays.equals(result, verify)); + } + + /** + * PBKDF2-HMAC-SHA3-512 basic test vector. + * + * This test vector was generated from native wolfCrypt, based off + * the same parameters as the PBKDF2-HMAC-SHA256 vector in + * wolfcrypt/test/test.c. This is meant to test basic functionality. + */ + @Test + public void testPBKDF2WithHmacSHA3_512() + throws NoSuchAlgorithmException, InvalidKeySpecException, + NoSuchProviderException { + + char[] pass = "passwordpassword".toCharArray(); + byte[] salt = { + (byte)0x78, (byte)0x57, (byte)0x8E, (byte)0x5a, + (byte)0x5d, (byte)0x63, (byte)0xcb, (byte)0x06 + }; + int iterations = 2048; + int kLen = 192; + byte[] verify = { + (byte)0x90, (byte)0x6e, (byte)0x60, (byte)0xce, + (byte)0xdf, (byte)0xcb, (byte)0xcd, (byte)0x56, + (byte)0x44, (byte)0xc7, (byte)0xf6, (byte)0x9a, + (byte)0xbb, (byte)0x68, (byte)0x63, (byte)0x77, + (byte)0xc4, (byte)0x4b, (byte)0x83, (byte)0x7d, + (byte)0x11, (byte)0x97, (byte)0x67, (byte)0xc8 + }; + byte[] result = null; + PBEKeySpec spec = null; + SecretKeyFactory sf = null; + SecretKey key = null; + + if (!FeatureDetect.Pbkdf2Enabled() || + !FeatureDetect.HmacSha3_512Enabled() || + !algoSupported("PBKDF2WithHmacSHA3-512")) { + System.out.println( + "Skipped: SecretKeyFactory(PBKDF2WithHmacSHA3-512) test"); + Assume.assumeTrue(false); + } + + spec = new PBEKeySpec(pass, salt, iterations, kLen); + assertNotNull(spec); + + sf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA3-512", provider); + assertNotNull(sf); + + key = sf.generateSecret(spec); + assertNotNull(key); + result = key.getEncoded(); + assertNotNull(result); + assertTrue(Arrays.equals(result, verify)); + } + + /** + * Test that calling generateSecret() with null or invalid + * KeySpec throws exception. + */ + @Test + public void testPBKDF2WithHmacSHA256_InvalidKeySpec() + throws NoSuchAlgorithmException, InvalidKeySpecException, + NoSuchProviderException { + + char[] pass = "passwordpassword".toCharArray(); + byte[] salt = { + (byte)0x78, (byte)0x57, (byte)0x8E, (byte)0x5a, + (byte)0x5d, (byte)0x63, (byte)0xcb, (byte)0x06 + }; + int iterations = 2048; + int kLen = 192; + PBEKeySpec spec = null; + SecretKeyFactory sf = null; + SecretKey key = null; + + if (!FeatureDetect.Pbkdf2Enabled() || + !FeatureDetect.HmacSha256Enabled() || + !algoSupported("PBKDF2WithHmacSHA256")) { + /* skip */ + Assume.assumeTrue(false); + } + + sf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256", provider); + assertNotNull(sf); + + /* null KeySpec should throw exception */ + try { + key = sf.generateSecret(spec); + fail("generateSecret() should fail with null KeySpec"); + } catch (InvalidKeySpecException e) { + /* expected */ + } + } + + /** + * Test calling generateSecret() with null password. + */ + @Test + public void testPBKDF2WithHmacSHA256_NullPassword() + throws NoSuchAlgorithmException, InvalidKeySpecException, + NoSuchProviderException { + + byte[] salt = { + (byte)0x78, (byte)0x57, (byte)0x8E, (byte)0x5a, + (byte)0x5d, (byte)0x63, (byte)0xcb, (byte)0x06 + }; + int iterations = 2048; + int kLen = 192; + byte[] verify = { + (byte)0xf7, (byte)0x39, (byte)0xde, (byte)0x5c, + (byte)0x50, (byte)0x14, (byte)0xf5, (byte)0xc3, + (byte)0x19, (byte)0xae, (byte)0x5e, (byte)0x13, + (byte)0x24, (byte)0x83, (byte)0xa2, (byte)0x39, + (byte)0xca, (byte)0xf5, (byte)0x34, (byte)0xbf, + (byte)0xed, (byte)0x2e, (byte)0xa0, (byte)0x32 + }; + byte[] result = null; + PBEKeySpec spec = null; + SecretKeyFactory sf = null; + SecretKey key = null; + + if (!FeatureDetect.Pbkdf2Enabled() || + !FeatureDetect.HmacSha256Enabled() || + !algoSupported("PBKDF2WithHmacSHA256") || + Fips.enabled) { + /* skip if algo not available for in FIPS mode, since HMAC + * minimum key size (14) won't allow use of zero length pass. */ + Assume.assumeTrue(false); + } + + sf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256", provider); + assertNotNull(sf); + + /* test with null password */ + spec = new PBEKeySpec(null, salt, iterations, kLen); + assertNotNull(spec); + + key = sf.generateSecret(spec); + assertNotNull(key); + result = key.getEncoded(); + assertNotNull(result); + assertTrue(Arrays.equals(result, verify)); + + /* test with empty (new char[0]) password */ + spec = new PBEKeySpec(new char[0], salt, iterations, kLen); + assertNotNull(spec); + + key = sf.generateSecret(spec); + assertNotNull(key); + result = key.getEncoded(); + assertNotNull(result); + assertTrue(Arrays.equals(result, verify)); + } + + @Test + public void testPBKDF2WithHmacSHA256_Interop() + throws NoSuchAlgorithmException, InvalidKeySpecException, + NoSuchProviderException { + + char[] pass = "passwordpassword".toCharArray(); + byte[] salt = { + (byte)0x78, (byte)0x57, (byte)0x8E, (byte)0x5a, + (byte)0x5d, (byte)0x63, (byte)0xcb, (byte)0x06 + }; + int iterations = 2048; + int kLen = 192; + PBEKeySpec spec = null; + SecretKey key = null; + + SecretKeyFactory sysFact = null; + SecretKeyFactory wolfFact = null; + byte[] sysResult = null; + byte[] wolfResult = null; + + if (!FeatureDetect.Pbkdf2Enabled() || + !FeatureDetect.HmacSha256Enabled() || + !algoSupported("PBKDF2WithHmacSHA256")) { + /* skipped */ + Assume.assumeTrue(false); + } + + sysFact = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); + Provider provider = sysFact.getProvider(); + + if (!provider.equals("wolfJCE")) { + + wolfFact = SecretKeyFactory.getInstance( + "PBKDF2WithHmacSHA256", "wolfJCE"); + + /* Set up one KeySpec for both providers to use */ + spec = new PBEKeySpec(pass, salt, iterations, kLen); + assertNotNull(spec); + + /* Generate secret from system provider */ + key = sysFact.generateSecret(spec); + sysResult = key.getEncoded(); + + /* Generate secret from wolfJCE provider */ + key = wolfFact.generateSecret(spec); + wolfResult = key.getEncoded(); + + assertTrue(Arrays.equals(sysResult, wolfResult)); + } + } + + /** + * Test getting KeySpec from SecretKey object using + * SecretKeyFactory.getKeySpec(). + */ + @Test + public void testGetKeySpec() + throws NoSuchAlgorithmException, InvalidKeySpecException, + NoSuchProviderException, InterruptedException { + + char[] pass = "passwordpassword".toCharArray(); + byte[] salt = { + (byte)0x78, (byte)0x57, (byte)0x8E, (byte)0x5a, + (byte)0x5d, (byte)0x63, (byte)0xcb, (byte)0x06 + }; + int iterations = 2048; + int kLen = 192; + byte[] verify = { + (byte)0x43, (byte)0x6d, (byte)0xb5, (byte)0xe8, + (byte)0xd0, (byte)0xfb, (byte)0x3f, (byte)0x35, + (byte)0x42, (byte)0x48, (byte)0x39, (byte)0xbc, + (byte)0x2d, (byte)0xd4, (byte)0xf9, (byte)0x37, + (byte)0xd4, (byte)0x95, (byte)0x16, (byte)0xa7, + (byte)0x2a, (byte)0x9a, (byte)0x21, (byte)0xd1 + }; + byte[] result = null; + PBEKeySpec spec = null; + KeySpec spec2 = null; + PBEKeySpec pbSpec = null; + SecretKeyFactory sf = null; + SecretKey key = null; + + if (!FeatureDetect.Pbkdf2Enabled() || + !FeatureDetect.HmacSha256Enabled() || + !algoSupported("PBKDF2WithHmacSHA256")) { + /* skipped */ + Assume.assumeTrue(false); + } + + /* Generate secret, setting up known PBEKeySpec */ + spec = new PBEKeySpec(pass, salt, iterations, kLen); + assertNotNull(spec); + + sf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256", provider); + assertNotNull(sf); + + key = sf.generateSecret(spec); + assertNotNull(key); + result = key.getEncoded(); + assertNotNull(result); + assertTrue(Arrays.equals(result, verify)); + + /* Try to get KeySpec directly from generated SecretKey */ + spec2 = sf.getKeySpec(key, PBEKeySpec.class); + assertNotNull(spec2); + + /* Test that the KeySpec we got is as expected */ + assertTrue(spec2 instanceof PBEKeySpec); + pbSpec = (PBEKeySpec)spec2; + assertTrue(Arrays.equals(pbSpec.getPassword(), pass)); + assertTrue(Arrays.equals(pbSpec.getSalt(), salt)); + assertEquals(pbSpec.getIterationCount(), iterations); + } + + /** + * Test translating existing SecretKey object to one generated + * by SecretKeyFactory using translateKey(). + */ + @Test + public void testTranslateKey() + throws NoSuchAlgorithmException, InvalidKeySpecException, + NoSuchProviderException, InterruptedException, + InvalidKeyException { + + char[] pass = "passwordpassword".toCharArray(); + byte[] salt = { + (byte)0x78, (byte)0x57, (byte)0x8E, (byte)0x5a, + (byte)0x5d, (byte)0x63, (byte)0xcb, (byte)0x06 + }; + int iterations = 2048; + int kLen = 192; + byte[] verify = { + (byte)0x43, (byte)0x6d, (byte)0xb5, (byte)0xe8, + (byte)0xd0, (byte)0xfb, (byte)0x3f, (byte)0x35, + (byte)0x42, (byte)0x48, (byte)0x39, (byte)0xbc, + (byte)0x2d, (byte)0xd4, (byte)0xf9, (byte)0x37, + (byte)0xd4, (byte)0x95, (byte)0x16, (byte)0xa7, + (byte)0x2a, (byte)0x9a, (byte)0x21, (byte)0xd1 + }; + byte[] result = null; + PBEKeySpec spec = null; + SecretKeyFactory sf = null; + SecretKey keyA = null; + SecretKey keyB = null; + + if (!FeatureDetect.Pbkdf2Enabled() || + !FeatureDetect.HmacSha256Enabled() || + !algoSupported("PBKDF2WithHmacSHA256")) { + /* skipped */ + Assume.assumeTrue(false); + } + + /* Generate SecretKey from SecretKeyFactory without specifying + * provider. This will use system provider if wolfJCE is not registered + * as top priority in system, otherwise will use wolfJCE. Still a good + * test, but not as accurate as testing translation between different + * providers */ + sf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); + assertNotNull(sf); + + spec = new PBEKeySpec(pass, salt, iterations, kLen); + assertNotNull(spec); + + keyA = sf.generateSecret(spec); + assertNotNull(keyA); + result = keyA.getEncoded(); + assertNotNull(result); + assertTrue(Arrays.equals(result, verify)); + + /* Try translating SecretKey to SecretKeyFactory of type wolfJCE */ + sf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256", "wolfJCE"); + assertNotNull(sf); + + keyB = sf.translateKey(keyA); + assertNotNull(keyB); + result = keyB.getEncoded(); + assertNotNull(result); + assertTrue(Arrays.equals(result, verify)); + } + + /** + * Set up one SecretKeyFactory and KeySpec, then test parallel + * threaded calls to generateSecret. + */ + @Test + public void testPBKDF2WithHmacSHA256_ThreadedGenerateSecret() + throws NoSuchAlgorithmException, InvalidKeySpecException, + NoSuchProviderException, InterruptedException { + + char[] pass = "passwordpassword".toCharArray(); + byte[] salt = { + (byte)0x78, (byte)0x57, (byte)0x8E, (byte)0x5a, + (byte)0x5d, (byte)0x63, (byte)0xcb, (byte)0x06 + }; + int iterations = 2048; + int kLen = 192; + byte[] verify = { + (byte)0x43, (byte)0x6d, (byte)0xb5, (byte)0xe8, + (byte)0xd0, (byte)0xfb, (byte)0x3f, (byte)0x35, + (byte)0x42, (byte)0x48, (byte)0x39, (byte)0xbc, + (byte)0x2d, (byte)0xd4, (byte)0xf9, (byte)0x37, + (byte)0xd4, (byte)0x95, (byte)0x16, (byte)0xa7, + (byte)0x2a, (byte)0x9a, (byte)0x21, (byte)0xd1 + }; + + int numThreads = 30; + ExecutorService service = Executors.newFixedThreadPool(numThreads); + final CountDownLatch latch = new CountDownLatch(numThreads); + final LinkedBlockingQueue results = + new LinkedBlockingQueue<>(); + + if (!FeatureDetect.Pbkdf2Enabled() || + !FeatureDetect.HmacSha256Enabled() || + !algoSupported("PBKDF2WithHmacSHA256")) { + System.out.println( + "Skipped: SecretKeyFactory(generateSecret) threaded test"); + Assume.assumeTrue(false); + } + + /* Set up one SecretKeyFactory and KeySpec, we want to test + * threaded call to generateSecret() */ + final SecretKeyFactory sf = + SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256", provider); + assertNotNull(sf); + + final PBEKeySpec spec = + new PBEKeySpec(pass, salt, iterations, kLen); + assertNotNull(spec); + + /* Insert/store/load/verify from numThreads parallel threads */ + for (int i = 0; i < numThreads; i++) { + service.submit(new Runnable() { + @Override public void run() { + + int ret = 0; + byte[] result = null; + SecretKey key = null; + try { + try { + key = sf.generateSecret(spec); + if (key == null) { + ret = 1; + } + } catch (InvalidKeySpecException e) { + ret = 1; + } + + if (ret == 0) { + result = key.getEncoded(); + if (result == null) { + ret = 1; + } + } + if (ret == 0) { + if (!Arrays.equals(result, verify)) { + ret = 1; + } + } + + /* record error if we got one */ + if (ret != 0) { + results.add(1); + } + } finally { + latch.countDown(); + } + } + }); + } + + /* wait for all threads to complete */ + latch.await(); + + /* compare all digests, all should be the same across threads */ + Iterator listIterator = results.iterator(); + while (listIterator.hasNext()) { + Integer cur = listIterator.next(); + if (cur == 1) { + fail("Threading error in generateSecret() threaded test"); + } + } + } + + /** + * Test creating SecretKeyFactory/KeySpec in parallel across multiple + * threads, with each one calling generateSecret(). + */ + @Test + public void testPBKDF2WithHmacSHA256_ThreadedSecretKeyFactory() + throws NoSuchAlgorithmException, InvalidKeySpecException, + NoSuchProviderException, InterruptedException { + + char[] pass = "passwordpassword".toCharArray(); + byte[] salt = { + (byte)0x78, (byte)0x57, (byte)0x8E, (byte)0x5a, + (byte)0x5d, (byte)0x63, (byte)0xcb, (byte)0x06 + }; + int iterations = 2048; + int kLen = 192; + byte[] verify = { + (byte)0x43, (byte)0x6d, (byte)0xb5, (byte)0xe8, + (byte)0xd0, (byte)0xfb, (byte)0x3f, (byte)0x35, + (byte)0x42, (byte)0x48, (byte)0x39, (byte)0xbc, + (byte)0x2d, (byte)0xd4, (byte)0xf9, (byte)0x37, + (byte)0xd4, (byte)0x95, (byte)0x16, (byte)0xa7, + (byte)0x2a, (byte)0x9a, (byte)0x21, (byte)0xd1 + }; + + int numThreads = 30; + ExecutorService service = Executors.newFixedThreadPool(numThreads); + final CountDownLatch latch = new CountDownLatch(numThreads); + final LinkedBlockingQueue results = + new LinkedBlockingQueue<>(); + + if (!FeatureDetect.Pbkdf2Enabled() || + !FeatureDetect.HmacSha256Enabled() || + !algoSupported("PBKDF2WithHmacSHA256")) { + System.out.println( + "Skipped: SecretKeyFactory threaded test"); + Assume.assumeTrue(false); + } + + /* Insert/store/load/verify from numThreads parallel threads */ + for (int i = 0; i < numThreads; i++) { + service.submit(new Runnable() { + @Override public void run() { + + int ret = 0; + byte[] result = null; + SecretKey key = null; + SecretKeyFactory sf = null; + PBEKeySpec spec = null; + + try { + try { + sf = SecretKeyFactory.getInstance( + "PBKDF2WithHmacSHA256", provider); + if (sf == null) { + throw new InvalidKeySpecException("fail"); + } + + spec = new PBEKeySpec(pass, salt, iterations, kLen); + if (spec == null) { + throw new InvalidKeySpecException("fail"); + } + + key = sf.generateSecret(spec); + if (key == null) { + throw new InvalidKeySpecException("fail"); + } + + } catch (InvalidKeySpecException | + NoSuchAlgorithmException | + NoSuchProviderException e) { + ret = 1; + } + + if (ret == 0) { + result = key.getEncoded(); + if (result == null) { + ret = 1; + } + } + if (ret == 0) { + if (!Arrays.equals(result, verify)) { + ret = 1; + } + } + + /* record error if we got one */ + if (ret != 0) { + results.add(1); + } + } finally { + latch.countDown(); + } + } + }); + } + + /* wait for all threads to complete */ + latch.await(); + + /* compare all digests, all should be the same across threads */ + Iterator listIterator = results.iterator(); + while (listIterator.hasNext()) { + Integer cur = listIterator.next(); + if (cur == 1) { + fail("Threading error in generateSecret() threaded test"); + } + } + } +} + diff --git a/src/test/java/com/wolfssl/provider/jce/test/WolfJCETestSuite.java b/src/test/java/com/wolfssl/provider/jce/test/WolfJCETestSuite.java index 3cb8f930..929381a2 100644 --- a/src/test/java/com/wolfssl/provider/jce/test/WolfJCETestSuite.java +++ b/src/test/java/com/wolfssl/provider/jce/test/WolfJCETestSuite.java @@ -33,6 +33,7 @@ WolfCryptMessageDigestSha384Test.class, WolfCryptMessageDigestSha512Test.class, WolfCryptRandomTest.class, + WolfCryptSecretKeyFactoryTest.class, WolfCryptSignatureTest.class, WolfCryptMacTest.class, WolfCryptCipherTest.class,