diff --git a/ChangeLog.md b/ChangeLog.md new file mode 100644 index 00000000..90c7d2e4 --- /dev/null +++ b/ChangeLog.md @@ -0,0 +1,155 @@ +### wolfCrypt JNI Release 1.6.0 (4/17/2024) + +Release 1.6.0 of wolfCrypt JNI and JCE has bug fixes and new features including: + +**New JCE Functionality:** +- Add RSA support to `KeyPairGenerator` class (PR 49) +- Add `AES/CBC/PKCS5Padding` support to `Cipher` class (PR 51) +- Add `RSA` support to `Cipher` class (PR 51) +- Add `PKIX` implementation of `CertPathValidator` class (PR 60, 66) +- Add `SHA1` alias for `MessageDigest` `SHA-1` for interop compatibility (PR 61) +- Add `AES/GCM/NoPadding` support to `Cipher` class (PR 62) +- Add `SecretKeyFactory` implementation supporting `PBKDF2` (PR 70) +- Add `DEFAULT` support to `SecureRandom` class (PR 72) + +**New JNI Wrapped APIs and Functionality:** +- Add `AES-GCM` support to `com.wolfssl.wolfcrypt.AesGcm` class (PR 62) + +**JNI and JCE Changes:** +- Add synchronization to `com.wolfssl.wolfcrypt.Rng` class (PR 44) +- Correct preprocessor guards for 3DES with wolfCrypt FIPS (PR 47) +- Correct order of operations in `wc_CreatePKCS8Key()` JNI wrapper API (PR 50) +- Add synchronization around native structure pointer use (PR 53) +- Remove inclusion of CyaSSL header includes, switch to wolfSSL (PR 56) +- Call `PRIVATE_KEY_LOCK/UNLOCK()` for wolfCrypt FIPS 140-3 compatibility (PR 57) +- Improve native HMAC feature detection (PR 58) +- Prepend zero byte to DH shared secret if less than prime length (PR 69) +- Add synchronization to protected methods in `WolfCryptSignature` (PR 68) +- Add synchronization to public methods of `WolfCryptKeyPairGenerator` (PR 73) +- Only allocate one `Rng` object per `WolfCryptSignature`, not per sign operation (PR 73) +- Reduce extra `WolfCryptRng` object creation in `Signature` and `KeyPairGenerator` (PR 73) + +**New Platform Support:** +- Add Windows support with Visual Studio, see IDE/WIN/README.md (PR 46) + +**Build System Changes:** +- Support custom wolfSSL library prefix and name in `makefile.linux` (PR 45) +- Standardize JNI library name on OSX to .dylib (PR 54) +- Update Maven build support (PR 55) + +**Example Changes:** +- Print provider of `SecureRandom` from `ProviderTest.java` (PR 43) +- Add Windows batch script to run `ProviderTest` example (PR 52) + +**Testing Changes:** +- Add extended threading test for `WolfCryptRandom` class (PR 44) +- Add Facebook Infer test script, make fixes (PR 48, 63) +- Add GitHub Actions tests for Oracle/Zulu/Coretto/Temurin/Microsoft JDKs on Linux and OS X (PR 65) + +**Documentation Changes:** +- Remove build instructions from `README.md` for FIPS historical cert #2425 (PR 56) +- Fix Javadoc warnings for Java 21 and 22 (PR 71) + +The wolfCrypt JNI/JCE Manual is available at: +https://www.wolfssl.com/documentation/manuals/wolfcryptjni/. For build +instructions and more details comments, please check the manual. + +### wolfCrypt JNI Release 1.5.0 (11/14/2022) + +Release 1.5.0 of wolfCrypt JNI has bug fixes and new features including: + +- Add build compatibility for Java 7 (PR 38) +- Add support for "SHA" algorithm string in wolfJCE (PR 39) +- Add rpm package support (PR 40) +- Add wolfJCE MessageDigest.clone() support (PR 41) +- Improve error checking of native Md5 API calls (PR 41) +- Add unit tests for com.wolfssl.wolfcrypt.Md5 (PR 41) + +### wolfCrypt JNI Release 1.4.0 (08/11/2022) + +Release 1.4.0 of wolfCrypt JNI has bug fixes and new features including: + +- Add example directory with one simple ProviderTest example (PR 32) +- Fix double free of ChaCha pointer (PR 34) +- Add test cases for ChaCha.java (PR 34) +- Skip WolfCryptMacTest for HMAC-MD5 when using wolfCrypt FIPS 140-3 (PR 35) +- Use new hash struct names (wc\_Md5/wc\_Sha/etc) in native code (PR 35) +- Fix potential build error with non-ASCII apostrophes in Fips.java (PR 36) + +### wolfCrypt JNI Release 1.3.0 (05/13/2022) + +Release 1.3.0 of wolfCrypt JNI has bug fixes and new features including: + +- Run FIPS tests on `ant test` when linked against a wolfCrypt FIPS library (PR 24) +- Wrap native AesGcmSetExtIV\_fips() API (PR 24) +- Fix releaseByteArray() usage in Fips.RsaSSL\_Sign() (PR 24) +- Fix AES-GCM FIPS test cases (PR 24) +- Keep existing JAVA\_HOME in makefiles if already set (PR 25) +- Add JCE support for MessageDigestSpi.engineGetDigestLength() (PR 27) +- Update junit to 4.13.2 (PR 28) +- Update missing Javadocs, fixes warnings on newer Java versions (PR 29) + +### wolfCrypt JNI Release 1.2.0 (11/16/2021) + +Release 1.2.0 of wolfCrypt JNI has bug fixes and new features including: + +- Add **FIPS 140-3** compatibility when using wolfCrypt FIPS or FIPS Ready +- Increase junit version from 4.12 to 4.13 in pom.xml +- Add local `./lib` directory to `java.library.path` in pom.xml +- Fix builds with `WOLFCRYPT_JNI_DEBUG_ON` defined +- Fix compatibility with wolfCrypt `NO_OLD_*` defines +- Fix compatibility with wolfSSL `./configure --enable-all` and ECC tests + +### wolfCrypt JNI Release 1.1.0 (08/26/2020) + +Release 1.1.0 of wolfCrypt JNI has bug fixes and new features including: + +- New JNI-level wrappers for ChaCha, Curve25519, and Ed25519 +- Maven pom.xml build file +- Runtime detection of hash type enum values for broader wolfSSL support +- Updated wolfSSL error codes to match native wolfSSL updates +- Native HMAC wrapper fixes for building with wolfCrypt FIPSv2 +- Native wrapper to return `HAVE_FIPS_VERSION` value to Java +- Remove Blake2b from HMAC types, to match native wolfSSL changes +- Better native wolfSSL feature detection +- Increase Junit version to 4.13 +- Use nativeheaderdir on supported platforms instead of javah +- Use hamcrest-all-1.3.jar in build.xml +- Add call to `wc_ecc_set_rng()` when needed + +### wolfCrypt JNI Release 1.0.0 (7/10/2017) + +Release 1.0.0 of wolfCrypt JNI has bug fixes and new features including: + +- Bug fixes to JCE classes: Cipher, KeyAgreement (DH), Signature +- JCE debug logging with wolfjce.debug system property +- Additional unit tests for JCE provider +- Conditional ant build for JNI and/or JCE +- New ant targets with choice of debug or release builds + +### wolfCrypt JNI Release 0.3 BETA + +Release 0.3 BETA of wolfCrypt JNI includes: + +- Support for ECC and DH key generation +- Bug fixes regarding key import/export +- Better argument sanitization at JNI level + +### wolfCrypt JNI Release 0.2 BETA + +Release 0.2 BETA of wolfCrypt JNI includes: + +- Support for Android +- Support for Oracle JDK/JVM +- Support for code signing wolfcrypt-jni.jar file +- Compatibility with non-FIPS wolfSSL and wolfCrypt builds +- Bug fixes regarding releasing native resources +- Test package changed to (com.wolfssl.provider.jce.test) + +### wolfCrypt JNI Release 0.1 BETA + +Release 0.1 BETA of wolfCrypt JNI includes: + +- Initial JCE package +- Support for OpenJDK + diff --git a/IDE/WIN/README.md b/IDE/WIN/README.md index 818bf067..1bba6bee 100644 --- a/IDE/WIN/README.md +++ b/IDE/WIN/README.md @@ -136,6 +136,7 @@ section titled `/* Configuration */`: ``` #define WOLFSSL_KEY_GEN +#define HAVE_CRL ``` After editing and saving the `user_settings.h` file, select one of the following @@ -202,6 +203,7 @@ and set the values for `HAVE_FIPS`, `HAVE_FIPS_VERSION`, and ``` #define WOLFSSL_KEY_GEN +#define HAVE_CRL ``` 6. Build the `wolfssl-fips` project, which will create a DLL in one of the @@ -258,6 +260,7 @@ The following additional defines will also need to be added to ``` #define WOLFSSL_KEY_GEN +#define HAVE_CRL ``` For additional help, contact support@wolfssl.com. diff --git a/IDE/WIN/wolfcryptjni.vcxproj b/IDE/WIN/wolfcryptjni.vcxproj index 0d67b3a2..f748ee4b 100644 --- a/IDE/WIN/wolfcryptjni.vcxproj +++ b/IDE/WIN/wolfcryptjni.vcxproj @@ -66,6 +66,7 @@ + @@ -80,10 +81,13 @@ + + + 16.0 @@ -97,52 +101,52 @@ DynamicLibrary true - v142 + v143 Unicode DynamicLibrary true - v142 + v143 Unicode DynamicLibrary false - v142 + v143 true Unicode DynamicLibrary false - v142 + v143 true Unicode DynamicLibrary true - v142 + v143 Unicode DynamicLibrary true - v142 + v143 Unicode DynamicLibrary false - v142 + v143 true Unicode DynamicLibrary false - v142 + v143 true Unicode diff --git a/IDE/WIN/wolfcryptjni.vcxproj.filters b/IDE/WIN/wolfcryptjni.vcxproj.filters index 0c193f56..f4bcc94c 100644 --- a/IDE/WIN/wolfcryptjni.vcxproj.filters +++ b/IDE/WIN/wolfcryptjni.vcxproj.filters @@ -158,5 +158,17 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + \ No newline at end of file diff --git a/README.md b/README.md index 24eb17cb..70d92435 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ ## wolfCrypt JCE Provider and JNI Wrapper -This package provides a Java, JNI-based interface to the native wolfCrypt -(and wolfCrypt FIPS API, if using with a FIPS version of wolfCrypt). It also -includes a JCE provider for wolfCrypt. +This packages includes both a JNI wrapper and JCE provider around the native +wolfCrypt cryptography library. It supports both normal and FIPS validated +versions of wolfCrypt. -For instructions and notes on the JNI wrapper, please reference this README.md, -or the wolfSSL online documentation. +For instructions and notes on the **JNI wrapper**, please reference this +README.md, or the wolfSSL online user manual. -For instructions and notes on the JCE provider, please reference the -[README_JCE.md](./README_JCE.md) file, or online instructions. +For instructions and notes on the **JCE provider**, please reference the +[README_JCE.md](./README_JCE.md) file, or online user manual. ### Compiling Native wolfSSL (Dependency) --------- @@ -18,14 +18,19 @@ To compile the wolfCrypt JNI wrapper and JCE provider, first the native (C) wolfSSL library must be compiled and installed. Compile and install a wolfSSL (wolfssl-x.x.x), wolfSSL FIPS -release (wolfssl-x.x.x-commercial-fips), or wolfSSL FIPS Ready release: +release (wolfssl-x.x.x-commercial-fips), or wolfSSL FIPS Ready release. -In any of these cases, you will need the `--enable-keygen` ./configure option. +In any of these cases, you will need the `--enable-jni` ./configure option. +The `--enable-jni` option includes all native wolfSSL features needed by +both wolfCrypt JNI/JCE (this package) as well as wolfSSL JNI/JSSE (a +separate package and repo). If you want the minimal set of requirements needed +for only wolfJCE, you can use `--enable-keygen --enable-crl`, where +CRL support is needed to support JCE `CertPathValidator(PKIX)` CRL support. **wolfSSL Standard Build**: ``` $ cd wolfssl-x.x.x -$ ./configure --enable-keygen +$ ./configure --enable-jni $ make check $ sudo make install ``` @@ -34,7 +39,7 @@ $ sudo make install ``` $ cd wolfssl-x.x.x-commercial-fips -$ ./configure --enable-fips=v2 --enable-keygen +$ ./configure --enable-fips=v2 --enable-jni $ make check $ sudo make install ``` @@ -43,7 +48,7 @@ $ sudo make install ``` $ cd wolfssl-x.x.x-commercial-fips -$ ./configure --enable-fips=ready --enable-keygen +$ ./configure --enable-fips=ready --enable-jni $ make check $ sudo make install ``` @@ -203,7 +208,7 @@ on the current release): com.wolfssl wolfcrypt-jni - 1.5.0-SNAPSHOT + 1.6.0-SNAPSHOT ... @@ -248,105 +253,8 @@ Signing the JAR is important especially if using the JCE Provider with a JDK that requires JCE provider JAR's to be authenticated. Please see [README_JCE.md](./README_JCE.md) for more details. -### Revision History +### Release Notes --------- -#### wolfCrypt JNI Release 1.5.0 (11/14/2022) - -Release 1.5.0 of wolfCrypt JNI has bug fixes and new features including: - -- Add build compatibility for Java 7 (PR 38) -- Add support for "SHA" algorithm string in wolfJCE (PR 39) -- Add rpm package support (PR 40) -- Add wolfJCE MessageDigest.clone() support (PR 41) -- Improve error checking of native Md5 API calls (PR 41) -- Add unit tests for com.wolfssl.wolfcrypt.Md5 (PR 41) - -#### wolfCrypt JNI Release 1.4.0 (08/11/2022) - -Release 1.4.0 of wolfCrypt JNI has bug fixes and new features including: - -- Add example directory with one simple ProviderTest example (PR 32) -- Fix double free of ChaCha pointer (PR 34) -- Add test cases for ChaCha.java (PR 34) -- Skip WolfCryptMacTest for HMAC-MD5 when using wolfCrypt FIPS 140-3 (PR 35) -- Use new hash struct names (wc\_Md5/wc\_Sha/etc) in native code (PR 35) -- Fix potential build error with non-ASCII apostrophes in Fips.java (PR 36) - -#### wolfCrypt JNI Release 1.3.0 (05/13/2022) - -Release 1.3.0 of wolfCrypt JNI has bug fixes and new features including: - -- Run FIPS tests on `ant test` when linked against a wolfCrypt FIPS library (PR 24) -- Wrap native AesGcmSetExtIV\_fips() API (PR 24) -- Fix releaseByteArray() usage in Fips.RsaSSL\_Sign() (PR 24) -- Fix AES-GCM FIPS test cases (PR 24) -- Keep existing JAVA\_HOME in makefiles if already set (PR 25) -- Add JCE support for MessageDigestSpi.engineGetDigestLength() (PR 27) -- Update junit to 4.13.2 (PR 28) -- Update missing Javadocs, fixes warnings on newer Java versions (PR 29) - -#### wolfCrypt JNI Release 1.2.0 (11/16/2021) - -Release 1.2.0 of wolfCrypt JNI has bug fixes and new features including: - -- Add **FIPS 140-3** compatibility when using wolfCrypt FIPS or FIPS Ready -- Increase junit version from 4.12 to 4.13 in pom.xml -- Add local `./lib` directory to `java.library.path` in pom.xml -- Fix builds with `WOLFCRYPT_JNI_DEBUG_ON` defined -- Fix compatibility with wolfCrypt `NO_OLD_*` defines -- Fix compatibility with wolfSSL `./configure --enable-all` and ECC tests - -#### wolfCrypt JNI Release 1.1.0 (08/26/2020) - -Release 1.1.0 of wolfCrypt JNI has bug fixes and new features including: - -- New JNI-level wrappers for ChaCha, Curve25519, and Ed25519 -- Maven pom.xml build file -- Runtime detection of hash type enum values for broader wolfSSL support -- Updated wolfSSL error codes to match native wolfSSL updates -- Native HMAC wrapper fixes for building with wolfCrypt FIPSv2 -- Native wrapper to return `HAVE_FIPS_VERSION` value to Java -- Remove Blake2b from HMAC types, to match native wolfSSL changes -- Better native wolfSSL feature detection -- Increase Junit version to 4.13 -- Use nativeheaderdir on supported platforms instead of javah -- Use hamcrest-all-1.3.jar in build.xml -- Add call to `wc_ecc_set_rng()` when needed - -#### wolfCrypt JNI Release 1.0.0 (7/10/2017) - -Release 1.0.0 of wolfCrypt JNI has bug fixes and new features including: - -- Bug fixes to JCE classes: Cipher, KeyAgreement (DH), Signature -- JCE debug logging with wolfjce.debug system property -- Additional unit tests for JCE provider -- Conditional ant build for JNI and/or JCE -- New ant targets with choice of debug or release builds - -#### wolfCrypt JNI Release 0.3 BETA - -Release 0.3 BETA of wolfCrypt JNI includes: - -- Support for ECC and DH key generation -- Bug fixes regarding key import/export -- Better argument sanitization at JNI level - -#### wolfCrypt JNI Release 0.2 BETA - -Release 0.2 BETA of wolfCrypt JNI includes: - -- Support for Android -- Support for Oracle JDK/JVM -- Support for code signing wolfcrypt-jni.jar file -- Compatibility with non-FIPS wolfSSL and wolfCrypt builds -- Bug fixes regarding releasing native resources -- Test package changed to (com.wolfssl.provider.jce.test) - -#### wolfCrypt JNI Release 0.1 BETA - -Release 0.1 BETA of wolfCrypt JNI includes: - -- Initial JCE package -- Support for OpenJDK +Release notes can be found in [ChangeLog.md](./ChangeLog.md). diff --git a/build.xml b/build.xml index b4e4dc19..d8a1ee26 100644 --- a/build.xml +++ b/build.xml @@ -19,7 +19,7 @@ - + diff --git a/jni/jni_ecc.c b/jni/jni_ecc.c index 3ae35d30..81115e50 100644 --- a/jni/jni_ecc.c +++ b/jni/jni_ecc.c @@ -821,7 +821,9 @@ Java_com_wolfssl_wolfcrypt_Ecc_wc_1ecc_1sign_1hash( RNG* rng = NULL; byte* hash = NULL; byte* signature = NULL; - word32 hashSz = 0, signatureSz = 0; + word32 hashSz = 0; + word32 expectedSigSz = 0; + word32 signatureSz = 0; word32 signatureBufSz = 0; ecc = (ecc_key*) getNativeStruct(env, this); @@ -844,7 +846,8 @@ Java_com_wolfssl_wolfcrypt_Ecc_wc_1ecc_1sign_1hash( } if (ret == 0) { - signatureSz = wc_ecc_sig_size(ecc); + expectedSigSz = wc_ecc_sig_size(ecc); + signatureSz = expectedSigSz; signatureBufSz = signatureSz; signature = (byte*)XMALLOC(signatureSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); @@ -860,25 +863,39 @@ Java_com_wolfssl_wolfcrypt_Ecc_wc_1ecc_1sign_1hash( ret = wc_ecc_sign_hash(hash, hashSz, signature, &signatureSz, rng, ecc); } + if (ret == 0) { + /* Sanity check on wc_ecc_sig_size() and actual length */ + if (expectedSigSz < signatureSz) { + ret = BUFFER_E; + throwWolfCryptException(env, + "wc_ecc_sig_size() less than actual sig size"); + } + } + if (ret == 0) { result = (*env)->NewByteArray(env, signatureSz); - if (result) { + if (result != NULL) { (*env)->SetByteArrayRegion(env, result, 0, signatureSz, (const jbyte*)signature); } else { + releaseByteArray(env, hash_object, hash, JNI_ABORT); throwWolfCryptException(env, "Failed to allocate signature"); + return NULL; } } else { + releaseByteArray(env, hash_object, hash, JNI_ABORT); throwWolfCryptExceptionFromError(env, ret); + return NULL; } LogStr("wc_ecc_sign_hash(input, inSz, output, &outSz, rng, ecc) = %d\n", ret); - LogStr("signature[%u]: [%p]\n", (word32)signatureSz, signature); - LogHex((byte*) signature, 0, signatureSz); if (signature != NULL) { + LogStr("signature[%u]: [%p]\n", (word32)signatureSz, signature); + LogHex((byte*) signature, 0, signatureSz); + XMEMSET(signature, 0, signatureBufSz); XFREE(signature, NULL, DYNAMIC_TYPE_TMP_BUFFER); } @@ -896,8 +913,8 @@ Java_com_wolfssl_wolfcrypt_Ecc_wc_1ecc_1verify_1hash( JNIEnv* env, jobject this, jbyteArray hash_object, jbyteArray signature_object) { - int ret = 0; #ifdef HAVE_ECC_VERIFY + int ret = 0; int status = 0; ecc_key* ecc = NULL; byte* hash = NULL; @@ -907,7 +924,7 @@ Java_com_wolfssl_wolfcrypt_Ecc_wc_1ecc_1verify_1hash( ecc = (ecc_key*) getNativeStruct(env, this); if ((*env)->ExceptionOccurred(env)) { /* getNativeStruct may throw exception, prevent throwing another */ - return 0; + return JNI_FALSE; } hash = getByteArray(env, hash_object); @@ -921,26 +938,29 @@ Java_com_wolfssl_wolfcrypt_Ecc_wc_1ecc_1verify_1hash( } else { ret = wc_ecc_verify_hash(signature, signatureSz, hash, - hashSz, &status, ecc); + hashSz, &status, ecc); } - if (ret == 0) { - ret = status; - } else { - throwWolfCryptExceptionFromError(env, ret); - } + releaseByteArray(env, hash_object, hash, JNI_ABORT); + releaseByteArray(env, signature_object, signature, JNI_ABORT); LogStr( "wc_ecc_verify_hash(sig, sigSz, hash, hashSz, &status, ecc); = %d\n", ret); - releaseByteArray(env, hash_object, hash, JNI_ABORT); - releaseByteArray(env, signature_object, signature, JNI_ABORT); + if (ret != 0) { + throwWolfCryptExceptionFromError(env, ret); + } + + if (status == 1) { + return JNI_TRUE; + } else { + return JNI_FALSE; + } #else throwNotCompiledInException(env); + return JNI_FALSE; #endif - - return ret; } JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_Ecc_wc_1ecc_1get_1curve_1size_1from_1name diff --git a/jni/jni_feature_detect.c b/jni/jni_feature_detect.c index 1fdfc510..54cc6f9d 100644 --- a/jni/jni_feature_detect.c +++ b/jni/jni_feature_detect.c @@ -213,7 +213,7 @@ JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_HmacSha224En { (void)env; (void)jcl; -#if !defined(NO_HMAC) && !defined(WOLFSSL_SHA224) +#if !defined(NO_HMAC) && defined(WOLFSSL_SHA224) return JNI_TRUE; #else return JNI_FALSE; diff --git a/jni/jni_native_struct.c b/jni/jni_native_struct.c index 247d3dcf..c6574af1 100644 --- a/jni/jni_native_struct.c +++ b/jni/jni_native_struct.c @@ -151,9 +151,10 @@ byte* getByteArray(JNIEnv* env, jbyteArray array) void releaseByteArray(JNIEnv* env, jbyteArray array, byte* elements, jint abort) { - if (elements) + if ((env != NULL) && (array != NULL) && (elements != NULL)) { (*env)->ReleaseByteArrayElements(env, array, (jbyte*) elements, abort ? JNI_ABORT : 0); + } } word32 getByteArrayLength(JNIEnv* env, jbyteArray array) diff --git a/jni/jni_wolfobject.c b/jni/jni_wolfobject.c index 52a33ada..48b7460f 100644 --- a/jni/jni_wolfobject.c +++ b/jni/jni_wolfobject.c @@ -49,7 +49,17 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfObject_init } #endif -#if defined(HAVE_FIPS) && defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION == 5) +#if defined(HAVE_FIPS) && defined(HAVE_FIPS_VERSION) && \ + (HAVE_FIPS_VERSION >= 7) + + ret = wc_RunAllCast_fips(); + if (ret != 0) { + printf("FIPS CASTs failed to run"); + } + +#elif defined(HAVE_FIPS) && defined(HAVE_FIPS_VERSION) && \ + (HAVE_FIPS_VERSION == 5) + /* run FIPS 140-3 conditional algorithm self tests early to prevent * multi threaded issues later on */ #if !defined(NO_AES) && !defined(NO_AES_CBC) diff --git a/pom.xml b/pom.xml index acf13a2f..bf969207 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.wolfssl wolfcrypt-jni - 1.5.0-SNAPSHOT + 1.6.0-SNAPSHOT jar wolfcrypt-jni https://www.wolfssl.com diff --git a/src/main/java/com/wolfssl/provider/jce/WolfCryptKeyPairGenerator.java b/src/main/java/com/wolfssl/provider/jce/WolfCryptKeyPairGenerator.java index 5032ef57..b2c96273 100644 --- a/src/main/java/com/wolfssl/provider/jce/WolfCryptKeyPairGenerator.java +++ b/src/main/java/com/wolfssl/provider/jce/WolfCryptKeyPairGenerator.java @@ -80,6 +80,9 @@ enum KeyType { private Rng rng = null; + /* Lock around Rng access */ + private final Object rngLock = new Object(); + /* for debug logging */ private WolfCryptDebug debug; private String algString; @@ -88,15 +91,12 @@ private WolfCryptKeyPairGenerator(KeyType type) { this.type = type; - rng = new Rng(); - rng.init(); - if (debug.DEBUG) algString = typeToString(type); } @Override - public void initialize(int keysize, SecureRandom random) { + public synchronized void initialize(int keysize, SecureRandom random) { if (type == KeyType.WC_DH) { throw new RuntimeException( @@ -111,19 +111,33 @@ public void initialize(int keysize, SecureRandom random) { this.publicExponent = Rsa.getDefaultRsaExponent(); } + synchronized (rngLock) { + if (this.rng == null) { + this.rng = new Rng(); + this.rng.init(); + } + } + if (debug.DEBUG) log("init with keysize: " + keysize); } @Override - public void initialize(AlgorithmParameterSpec params, - SecureRandom random) throws InvalidAlgorithmParameterException { + public synchronized void initialize(AlgorithmParameterSpec params, + SecureRandom random) throws InvalidAlgorithmParameterException { if (params == null) { throw new InvalidAlgorithmParameterException( "AlgorithmParameterSpec must not be null"); } + synchronized (rngLock) { + if (this.rng == null) { + this.rng = new Rng(); + this.rng.init(); + } + } + switch (type) { case WC_RSA: @@ -206,7 +220,7 @@ public void initialize(AlgorithmParameterSpec params, } @Override - public KeyPair generateKeyPair() { + public synchronized KeyPair generateKeyPair() { KeyPair pair = null; @@ -232,7 +246,10 @@ public KeyPair generateKeyPair() { Rsa rsa = new Rsa(); try { - rsa.makeKey(this.keysize, this.publicExponent, rng); + synchronized (rngLock) { + rsa.makeKey(this.keysize, this.publicExponent, + this.rng); + } /* private key */ privDer = rsa.privateKeyEncodePKCS8(); @@ -280,13 +297,16 @@ public KeyPair generateKeyPair() { ECPrivateKey eccPriv = null; ECPublicKey eccPub = null; + Ecc ecc = null; - Ecc ecc = new Ecc(); + synchronized (rngLock) { + ecc = new Ecc(this.rng); - if (this.curve == null) { - ecc.makeKey(rng, this.keysize); - } else { - ecc.makeKeyOnCurve(rng, this.keysize, this.curve); + if (this.curve == null) { + ecc.makeKey(this.rng, this.keysize); + } else { + ecc.makeKeyOnCurve(this.rng, this.keysize, this.curve); + } } /* private key */ @@ -343,7 +363,9 @@ public KeyPair generateKeyPair() { dh.setParams(dhP, dhG); /* make key */ - dh.makeKey(rng); + synchronized (rngLock) { + dh.makeKey(this.rng); + } privSpec = new DHPrivateKeySpec( new BigInteger(dh.getPrivateKey()), @@ -401,11 +423,13 @@ private void log(String msg) { @SuppressWarnings("deprecation") @Override - protected void finalize() throws Throwable { + protected synchronized void finalize() throws Throwable { try { - if (this.rng != null) { - rng.free(); - rng.releaseNativeStruct(); + synchronized (rngLock) { + if (this.rng != null) { + this.rng.free(); + this.rng.releaseNativeStruct(); + } } } finally { super.finalize(); diff --git a/src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java b/src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java index c38f9cad..142b0099 100644 --- a/src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java +++ b/src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java @@ -34,7 +34,7 @@ public final class WolfCryptProvider extends Provider { * Create new WolfCryptProvider object */ public WolfCryptProvider() { - super("wolfJCE", 1.5, "wolfCrypt JCE Provider"); + super("wolfJCE", 1.6, "wolfCrypt JCE Provider"); /* MessageDigest */ if (FeatureDetect.Md5Enabled()) { @@ -181,41 +181,43 @@ public WolfCryptProvider() { "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 (FeatureDetect.Pbkdf2Enabled()) { + 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 diff --git a/src/main/java/com/wolfssl/provider/jce/WolfCryptSignature.java b/src/main/java/com/wolfssl/provider/jce/WolfCryptSignature.java index c071ee76..60561ab2 100644 --- a/src/main/java/com/wolfssl/provider/jce/WolfCryptSignature.java +++ b/src/main/java/com/wolfssl/provider/jce/WolfCryptSignature.java @@ -101,21 +101,30 @@ enum DigestType { private String keyString; private String digestString; + /* Class-wide RNG to be used for padding during sign operations */ + private Rng rng = null; + private final Object rngLock = new Object(); + private WolfCryptSignature(KeyType ktype, DigestType dtype) throws NoSuchAlgorithmException { this.keyType = ktype; this.digestType = dtype; - /* init asn object */ - asn = new Asn(); - if ((ktype != KeyType.WC_RSA) && (ktype != KeyType.WC_ECDSA)) { throw new NoSuchAlgorithmException( "Signature algorithm key type must be RSA or ECC"); } + synchronized (rngLock) { + this.rng = new Rng(); + this.rng.init(); + } + + /* init asn object */ + asn = new Asn(); + /* init hash type */ switch (dtype) { case WC_MD5: @@ -238,14 +247,18 @@ protected synchronized void engineInitSign(PrivateKey privateKey) /* initialize native struct */ switch (keyType) { case WC_RSA: - if (this.rsa != null) + if (this.rsa != null) { this.rsa.releaseNativeStruct(); + } this.rsa = new Rsa(); break; case WC_ECDSA: - if (this.ecc != null) + if (this.ecc != null) { this.ecc.releaseNativeStruct(); - this.ecc = new Ecc(); + } + synchronized (this.rngLock) { + this.ecc = new Ecc(this.rng); + } break; } @@ -303,14 +316,18 @@ protected synchronized void engineInitVerify(PublicKey publicKey) /* initialize native struct */ switch (keyType) { case WC_RSA: - if (this.rsa != null) + if (this.rsa != null) { this.rsa.releaseNativeStruct(); + } this.rsa = new Rsa(); break; case WC_ECDSA: - if (this.ecc != null) + if (this.ecc != null) { this.ecc.releaseNativeStruct(); - this.ecc = new Ecc(); + } + synchronized (this.rngLock) { + this.ecc = new Ecc(this.rng); + } break; } @@ -390,10 +407,6 @@ protected synchronized byte[] engineSign() throws SignatureException { throw new SignatureException(e.getMessage()); } - /* init RNG for padding */ - Rng rng = new Rng(); - rng.init(); - /* sign digest */ switch (this.keyType) { case WC_RSA: @@ -409,7 +422,9 @@ protected synchronized byte[] engineSign() throws SignatureException { byte[] tmp = new byte[encodedSz]; System.arraycopy(encDigest, 0, tmp, 0, encodedSz); - signature = this.rsa.sign(tmp, rng); + synchronized (rngLock) { + signature = this.rsa.sign(tmp, this.rng); + } zeroArray(tmp); break; @@ -417,7 +432,9 @@ protected synchronized byte[] engineSign() throws SignatureException { case WC_ECDSA: /* ECC sign */ - signature = this.ecc.sign(digest, rng); + synchronized (rngLock) { + signature = this.ecc.sign(digest, this.rng); + } break; @@ -426,10 +443,6 @@ protected synchronized byte[] engineSign() throws SignatureException { "Invalid signature algorithm type"); } - /* release RNG */ - rng.free(); - rng.releaseNativeStruct(); - if (debug.DEBUG) { if (signature != null) { log("generated signature, len: " + signature.length); @@ -617,7 +630,7 @@ private void log(String msg) { @SuppressWarnings("deprecation") @Override - protected void finalize() throws Throwable { + protected synchronized void finalize() throws Throwable { try { /* free native digest objects */ if (this.md5 != null) @@ -642,6 +655,15 @@ protected void finalize() throws Throwable { if (this.ecc != null) this.ecc.releaseNativeStruct(); /* frees internally */ + synchronized (rngLock) { + if (this.rng != null) { + /* release RNG */ + this.rng.free(); + this.rng.releaseNativeStruct(); + this.rng = null; + } + } + } finally { super.finalize(); } diff --git a/src/main/java/com/wolfssl/wolfcrypt/Ecc.java b/src/main/java/com/wolfssl/wolfcrypt/Ecc.java index 3f908d9a..7c875757 100644 --- a/src/main/java/com/wolfssl/wolfcrypt/Ecc.java +++ b/src/main/java/com/wolfssl/wolfcrypt/Ecc.java @@ -38,9 +38,17 @@ public class Ecc extends NativeStruct { /* used with native wc_ecc_set_rng() */ private Rng rng = null; + /* Do we own the Rng struct, or has that been passed in? Used + * during Rng cleanup. */ + private boolean weOwnRng = true; + + /** Lock around Rng object access */ + private final Object rngLock = new Object(); + /** Lock around object state */ protected final Object stateLock = new Object(); + /** * Create new Ecc object */ @@ -48,6 +56,18 @@ public Ecc() { init(); } + /** + * Create new Ecc object with existing Rng object. + * + * @param rng initialized com.wolfssl.wolfcrypt.Rng object + */ + public Ecc(Rng rng) { + this.rng = rng; + weOwnRng = false; + + init(); + } + @Override public synchronized void releaseNativeStruct() { free(); @@ -100,9 +120,12 @@ protected void init() { } /* used with native wc_ecc_set_rng() */ - if (rng == null) { - rng = new Rng(); - rng.init(); + synchronized (rngLock) { + if (rng == null) { + rng = new Rng(); + rng.init(); + weOwnRng = true; + } } state = WolfCryptState.INITIALIZED; @@ -124,9 +147,11 @@ protected void free() { wc_ecc_free(); } - if (this.rng != null) { - rng.free(); - rng.releaseNativeStruct(); + synchronized (rngLock) { + if (this.weOwnRng && this.rng != null) { + rng.free(); + rng.releaseNativeStruct(); + } } state = WolfCryptState.UNINITIALIZED; @@ -445,7 +470,9 @@ public synchronized byte[] makeSharedSecret(Ecc pubKey) if (state == WolfCryptState.READY) { synchronized (pointerLock) { - return wc_ecc_shared_secret(pubKey, this.rng); + synchronized (rngLock) { + return wc_ecc_shared_secret(pubKey, this.rng); + } } } else { throw new IllegalStateException( @@ -473,7 +500,9 @@ public synchronized byte[] sign(byte[] hash, Rng rng) synchronized (stateLock) { if (state == WolfCryptState.READY) { synchronized (pointerLock) { - signature = wc_ecc_sign_hash(hash, rng); + synchronized (rngLock) { + signature = wc_ecc_sign_hash(hash, rng); + } } } else { throw new IllegalStateException( diff --git a/src/test/java/com/wolfssl/provider/jce/test/WolfCryptKeyAgreementTest.java b/src/test/java/com/wolfssl/provider/jce/test/WolfCryptKeyAgreementTest.java index 4d316f8e..c6c366f7 100644 --- a/src/test/java/com/wolfssl/provider/jce/test/WolfCryptKeyAgreementTest.java +++ b/src/test/java/com/wolfssl/provider/jce/test/WolfCryptKeyAgreementTest.java @@ -29,10 +29,11 @@ import java.util.ArrayList; import java.util.Random; import java.util.Iterator; +import java.util.concurrent.TimeUnit; import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicIntegerArray; import javax.crypto.KeyAgreement; import javax.crypto.ShortBufferException; @@ -537,9 +538,18 @@ private void threadRunnerKeyAgreeTest(String algo) int numThreads = 10; ExecutorService service = Executors.newFixedThreadPool(numThreads); final CountDownLatch latch = new CountDownLatch(numThreads); - final LinkedBlockingQueue results = new LinkedBlockingQueue<>(); final String currentAlgo = algo; + /* Used to detect timeout of CountDownLatch, don't run indefinitely + * if threads are stalled out or deadlocked */ + boolean returnWithoutTimeout = true; + + /* Keep track of failure and success count */ + final AtomicIntegerArray failures = new AtomicIntegerArray(1); + final AtomicIntegerArray success = new AtomicIntegerArray(1); + failures.set(0, 0); + success.set(0, 0); + /* DH Tests */ AlgorithmParameterGenerator paramGen = AlgorithmParameterGenerator.getInstance("DH"); @@ -552,7 +562,6 @@ private void threadRunnerKeyAgreeTest(String algo) service.submit(new Runnable() { @Override public void run() { - int failed = 0; KeyPairGenerator keyGen = null; KeyAgreement aKeyAgree = null; KeyAgreement bKeyAgree = null; @@ -603,53 +612,56 @@ private void threadRunnerKeyAgreeTest(String algo) byte secretB[] = bKeyAgree.generateSecret(); if (!Arrays.equals(secretA, secretB)) { - failed = 1; + throw new Exception( + "Secrets A and B to not match"); } - if (failed == 0) { - cKeyAgree = KeyAgreement.getInstance( - currentAlgo, "wolfJCE"); - cPair = keyGen.generateKeyPair(); - cKeyAgree.init(cPair.getPrivate()); + cKeyAgree = KeyAgreement.getInstance( + currentAlgo, "wolfJCE"); + cPair = keyGen.generateKeyPair(); + cKeyAgree.init(cPair.getPrivate()); - aKeyAgree.doPhase(cPair.getPublic(), true); - cKeyAgree.doPhase(aPair.getPublic(), true); + aKeyAgree.doPhase(cPair.getPublic(), true); + cKeyAgree.doPhase(aPair.getPublic(), true); - byte secretA2[] = aKeyAgree.generateSecret(); - byte secretC[] = cKeyAgree.generateSecret(); + byte secretA2[] = aKeyAgree.generateSecret(); + byte secretC[] = cKeyAgree.generateSecret(); - if (!Arrays.equals(secretA2, secretC)) { - failed = 1; - } + if (!Arrays.equals(secretA2, secretC)) { + throw new Exception( + "Secrets A2 and C do not match"); } + /* Log success */ + success.incrementAndGet(0); + } catch (Exception e) { e.printStackTrace(); - failed = 1; + + /* Log failure */ + failures.incrementAndGet(0); } finally { latch.countDown(); } - - if (failed == 1) { - results.add(1); - } - else { - results.add(0); - } } }); } /* wait for all threads to complete */ - latch.await(); - - /* Look for any failures that happened */ - Iterator listIterator = results.iterator(); - while (listIterator.hasNext()) { - Integer cur = listIterator.next(); - if (cur == 1) { - fail("Threading error in KeyAgreement thread test"); + returnWithoutTimeout = latch.await(10, TimeUnit.SECONDS); + service.shutdown(); + + /* Check failure count and success count against thread count */ + if ((failures.get(0) != 0) || + (success.get(0) != numThreads)) { + if (returnWithoutTimeout == true) { + fail("KeyAgreement test threading error: " + + failures.get(0) + " failures, " + + success.get(0) + " success, " + + numThreads + " num threads total"); + } else { + fail("KeyAgreement test threading error, threads timed out"); } } } diff --git a/src/test/java/com/wolfssl/provider/jce/test/WolfCryptKeyPairGeneratorTest.java b/src/test/java/com/wolfssl/provider/jce/test/WolfCryptKeyPairGeneratorTest.java index e6d06133..b7b9255a 100644 --- a/src/test/java/com/wolfssl/provider/jce/test/WolfCryptKeyPairGeneratorTest.java +++ b/src/test/java/com/wolfssl/provider/jce/test/WolfCryptKeyPairGeneratorTest.java @@ -55,6 +55,7 @@ import com.wolfssl.wolfcrypt.Rsa; import com.wolfssl.wolfcrypt.Ecc; +import com.wolfssl.wolfcrypt.Fips; import com.wolfssl.wolfcrypt.test.Util; import com.wolfssl.wolfcrypt.WolfCryptException; import com.wolfssl.provider.jce.WolfCryptProvider; @@ -93,6 +94,16 @@ public class WolfCryptKeyPairGeneratorTest { "brainpoolp512r1" }; + private static String supportedCurvesFIPS1403[] = { + "secp224r1", + "secp256r1", + "secp384r1", + "secp521r1", + + "secp224k1", + "secp256k1", + }; + private static ArrayList enabledCurves = new ArrayList(); @@ -100,9 +111,7 @@ public class WolfCryptKeyPairGeneratorTest { new ArrayList(); /* Test generation of these RSA key sizes */ - private static int testedRSAKeySizes[] = { - 1024, 2048, 3072, 4096 - }; + private static int testedRSAKeySizes[] = null; /* DH test params */ private static byte[] prime = Util.h2b( @@ -127,16 +136,35 @@ public static void testProviderInstallationAtRuntime() { Provider p = Security.getProvider("wolfJCE"); assertNotNull(p); + if (Fips.enabled && Fips.fipsVersion >= 5) { + /* FIPS after 2425 doesn't allow 1024-bit RSA key gen */ + testedRSAKeySizes = new int[] { + 2048, 3072, 4096 + }; + } + else { + testedRSAKeySizes = new int[] { + 1024, 2048, 3072, 4096 + }; + } + /* build list of enabled curves and key sizes, * getCurveSizeFromName() will return 0 if curve not found */ Ecc tmp = new Ecc(); - for (int i = 0; i < supportedCurves.length; i++) { + String[] curves = null; + + if (Fips.enabled && Fips.fipsVersion >= 5) { + curves = supportedCurvesFIPS1403; + } else { + curves = supportedCurves; + } + + for (int i = 0; i < curves.length; i++) { - int size = tmp.getCurveSizeFromName( - supportedCurves[i].toUpperCase()); + int size = tmp.getCurveSizeFromName(curves[i].toUpperCase()); if (size > 0) { - enabledCurves.add(supportedCurves[i]); + enabledCurves.add(curves[i]); if (!enabledEccKeySizes.contains(Integer.valueOf(size))) { enabledEccKeySizes.add(Integer.valueOf(size)); diff --git a/src/test/java/com/wolfssl/provider/jce/test/WolfCryptPKIXCertPathValidatorTest.java b/src/test/java/com/wolfssl/provider/jce/test/WolfCryptPKIXCertPathValidatorTest.java index 6ec089dc..24a4e41b 100644 --- a/src/test/java/com/wolfssl/provider/jce/test/WolfCryptPKIXCertPathValidatorTest.java +++ b/src/test/java/com/wolfssl/provider/jce/test/WolfCryptPKIXCertPathValidatorTest.java @@ -63,6 +63,7 @@ import java.security.cert.CollectionCertStoreParameters; import java.lang.IllegalArgumentException; +import com.wolfssl.wolfcrypt.WolfCrypt; import com.wolfssl.provider.jce.WolfCryptProvider; public class WolfCryptPKIXCertPathValidatorTest { @@ -388,6 +389,13 @@ public void testSingleCertValidateRSAWithCRL() Collection crls = null; List certStores = null; + if (!WolfCrypt.CrlEnabled()) { + /* Native CRL not enabled, skip CRL test */ + System.out.println("CertPathValidator CRL test skipped, " + + "CRL not compiled in"); + return; + } + /* Use example KeyStore that verifies server-cert.der */ store = createKeyStoreFromFile(jksCaServerRSA2048, keyStorePass); if (store == null || store.size() != 1) { diff --git a/src/test/java/com/wolfssl/provider/jce/test/WolfCryptSignatureTest.java b/src/test/java/com/wolfssl/provider/jce/test/WolfCryptSignatureTest.java index 4cd273b2..87bcfc45 100644 --- a/src/test/java/com/wolfssl/provider/jce/test/WolfCryptSignatureTest.java +++ b/src/test/java/com/wolfssl/provider/jce/test/WolfCryptSignatureTest.java @@ -29,10 +29,12 @@ import java.util.ArrayList; import java.util.Random; import java.util.Iterator; +import java.util.concurrent.TimeUnit; import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicIntegerArray; import java.security.Security; import java.security.Provider; @@ -343,15 +345,48 @@ private KeyPair generateKeyPair(String algo, SecureRandom rand) return pair; } - private void threadRunnerSignVerify(byte[] inBuf, String algo) - throws InterruptedException { + private void threadRunnerSignVerify(byte[] inBuf, String algo, + final AtomicIntegerArray failures, final AtomicIntegerArray success, + ExecutorService service, final CountDownLatch latch, int numThreads) + throws InterruptedException, Exception { - int numThreads = 10; - ExecutorService service = Executors.newFixedThreadPool(numThreads); - final CountDownLatch latch = new CountDownLatch(numThreads); - final LinkedBlockingQueue results = new LinkedBlockingQueue<>(); - final String currentAlgo = algo; + final String currentAlg = algo; final byte[] toSignBuf = inBuf; + KeyPair pair = null; + KeyPairGenerator keyGen = null; + final PrivateKey priv; + final PublicKey pub; + + /* Generate key pairs once up front to minimize use of entropy from + * RNG. We also are just interested in testing sign/verify across + * multiple threads, not key gen in this test. */ + if (currentAlg.contains("RSA")) { + keyGen = KeyPairGenerator.getInstance("RSA"); + keyGen.initialize(2048, secureRandom); + pair = keyGen.generateKeyPair(); + + } else if (currentAlg.contains("ECDSA")) { + keyGen = KeyPairGenerator.getInstance("EC", + "wolfJCE"); + ECGenParameterSpec ecSpec = + new ECGenParameterSpec("secp521r1"); + keyGen.initialize(ecSpec); + pair = keyGen.generateKeyPair(); + } + + if (pair == null) { + throw new Exception("KeyPair from generateKeyPair() is null"); + } + else { + priv = pair.getPrivate(); + if (priv == null) { + throw new Exception("KeyPair.getPrivate() returned null"); + } + pub = pair.getPublic(); + if (pub == null) { + throw new Exception("KeyPair.getPublic() returned null"); + } + } /* Do encrypt/decrypt and sign/verify in parallel across numThreads * threads, all operations should pass */ @@ -359,107 +394,115 @@ private void threadRunnerSignVerify(byte[] inBuf, String algo) service.submit(new Runnable() { @Override public void run() { - int failed = 0; - KeyPair pair = null; - KeyPairGenerator keyGen = null; - PrivateKey priv = null; - PublicKey pub = null; + byte[] signature = null; Signature signer = null; Signature verifier = null; - byte[] signature = null; try { - signer = Signature.getInstance( - currentAlgo, "wolfJCE"); - verifier = Signature.getInstance( - currentAlgo, "wolfJCE"); + signer = Signature.getInstance(currentAlg, "wolfJCE"); + verifier = Signature.getInstance(currentAlg, "wolfJCE"); if (signer == null || verifier == null) { - failed = 1; + throw new Exception( + "signer or verifier Signature object null"); } - /* generate key pair */ - if (failed == 0) { - if (currentAlgo.contains("RSA")) { - keyGen = KeyPairGenerator.getInstance("RSA"); - keyGen.initialize(2048, secureRandom); - pair = keyGen.generateKeyPair(); - - } else if (currentAlgo.contains("ECDSA")) { - keyGen = KeyPairGenerator.getInstance("EC", - "wolfJCE"); - ECGenParameterSpec ecSpec = - new ECGenParameterSpec("secp521r1"); - keyGen.initialize(ecSpec); - pair = keyGen.generateKeyPair(); - } - - if (pair == null) { - failed = 1; - } - else { - priv = pair.getPrivate(); - pub = pair.getPublic(); - } + /* generate signature */ + signer.initSign(priv); + signer.update(toSignBuf, 0, toSignBuf.length); + signature = signer.sign(); + if (signature == null || signature.length == 0) { + throw new Exception( + "signer.sign() returned null or zero " + + "length array"); } - if (failed == 0) { - /* generate signature */ - signer.initSign(priv); - signer.update(toSignBuf, 0, toSignBuf.length); - signature = signer.sign(); - - /* verify signature */ - verifier.initVerify(pub); - verifier.update(toSignBuf, 0, toSignBuf.length); - boolean verified = verifier.verify(signature); - - if (verified == false) { - failed = 1; - } + /* verify signature */ + verifier.initVerify(pub); + verifier.update(toSignBuf, 0, toSignBuf.length); + boolean verified = verifier.verify(signature); + + if (verified == false) { + throw new Exception( + "verifier.verify() returned false:\n" + + "algo: " + currentAlg + "\n" + + "signature (" + signature.length + " bytes): " + + arrayToHex(signature) + "\n" + + "private (" + priv.getEncoded().length + + " bytes): " + arrayToHex(priv.getEncoded()) + + "\npublic (" + pub.getEncoded().length + + " bytes): " + arrayToHex(pub.getEncoded())); } + /* Log success */ + success.incrementAndGet(0); + } catch (Exception e) { e.printStackTrace(); - failed = 1; + + /* Log failure */ + failures.incrementAndGet(0); } finally { latch.countDown(); } - - if (failed == 1) { - results.add(1); - } - else { - results.add(0); - } } }); } + } - /* wait for all threads to complete */ - latch.await(); - - /* Look for any failures that happened */ - Iterator listIterator = results.iterator(); - while (listIterator.hasNext()) { - Integer cur = listIterator.next(); - if (cur == 1) { - fail("Threading error in RSA sign/verify thread test"); - } + private synchronized String arrayToHex(byte[] in) { + StringBuilder builder = new StringBuilder(); + for (byte b: in) { + builder.append(String.format("%02X", b)); } + + return builder.toString(); } @Test - public void testThreadedWolfSignWolfVerify() throws InterruptedException { - + public void testThreadedWolfSignWolfVerify() throws Exception { final String toSign = "Hello World"; final byte[] toSignBuf = toSign.getBytes(); + int numThreads = 10; + int numAlgos = enabledAlgos.size(); + ExecutorService service = + Executors.newFixedThreadPool(numAlgos * numThreads); + final CountDownLatch latch = new CountDownLatch(numAlgos * numThreads); + + /* Used to detect timeout of CountDownLatch, don't run indefinitely + * if threads are stalled out or deadlocked */ + boolean returnWithoutTimeout = true; + + /* Keep track of failure and success count */ + final AtomicIntegerArray failures = new AtomicIntegerArray(1); + final AtomicIntegerArray success = new AtomicIntegerArray(1); + failures.set(0, 0); + success.set(0, 0); + /* run threaded test for each enabled Signature algorithm */ - for (int i = 0; i < enabledAlgos.size(); i++) { - threadRunnerSignVerify(toSignBuf, enabledAlgos.get(i)); + for (int i = 0; i < numAlgos; i++) { + threadRunnerSignVerify(toSignBuf, enabledAlgos.get(i), + failures, success, service, latch, numThreads); + } + + /* wait for all threads to complete */ + returnWithoutTimeout = latch.await(10, TimeUnit.SECONDS); + service.shutdown(); + + /* Check failure count and success count against thread count */ + if ((failures.get(0) != 0) || + (success.get(0) != (enabledAlgos.size() * numThreads))) { + if (returnWithoutTimeout == true) { + fail("Signature test threading error: " + + failures.get(0) + " failures, " + + success.get(0) + " success, " + + (numThreads * enabledAlgos.size()) + " num threads total"); + } else { + fail("Signature test threading error, threads timed out"); + } } } } diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/DhTest.java b/src/test/java/com/wolfssl/wolfcrypt/test/DhTest.java index d738bdef..13d516c9 100644 --- a/src/test/java/com/wolfssl/wolfcrypt/test/DhTest.java +++ b/src/test/java/com/wolfssl/wolfcrypt/test/DhTest.java @@ -30,10 +30,11 @@ import java.util.Arrays; import java.util.Random; import java.util.Iterator; +import java.util.concurrent.TimeUnit; import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicIntegerArray; import com.wolfssl.wolfcrypt.Dh; import com.wolfssl.wolfcrypt.Rng; @@ -105,7 +106,16 @@ public void threadedDhSharedSecretTest() throws InterruptedException { int numThreads = 10; ExecutorService service = Executors.newFixedThreadPool(numThreads); final CountDownLatch latch = new CountDownLatch(numThreads); - final LinkedBlockingQueue results = new LinkedBlockingQueue<>(); + + /* Used to detect timeout of CountDownLatch, don't run indefinitely + * if threads are stalled out or deadlocked */ + boolean returnWithoutTimeout = true; + + /* Keep track of failure and success count */ + final AtomicIntegerArray failures = new AtomicIntegerArray(1); + final AtomicIntegerArray success = new AtomicIntegerArray(1); + failures.set(0, 0); + success.set(0, 0); final byte[] p = Util.h2b( "E6969D3D495BE32C7CF180C3BDD4798E91B7818251BB055E" @@ -121,7 +131,6 @@ public void threadedDhSharedSecretTest() throws InterruptedException { service.submit(new Runnable() { @Override public void run() { - int failed = 0; Dh alice = null; Dh bob = null; @@ -134,65 +143,65 @@ public void threadedDhSharedSecretTest() throws InterruptedException { /* keys should be null before generation */ if (alice.getPublicKey() != null || bob.getPublicKey() != null) { - failed = 1; + throw new Exception( + "keys not null before generation"); } /* generate Dh keys */ - if (failed == 0) { - synchronized (rngLock) { - alice.makeKey(rng); - bob.makeKey(rng); - } + synchronized (rngLock) { + alice.makeKey(rng); + bob.makeKey(rng); } /* keys should not be null after generation */ - if (failed == 0) { - if (alice.getPublicKey() == null || - bob.getPublicKey() == null) { - failed = 1; - } + if (alice.getPublicKey() == null || + bob.getPublicKey() == null) { + throw new Exception( + "keys null after generation"); } - if (failed == 0) { - byte[] sharedSecretA = alice.makeSharedSecret(bob); - byte[] sharedSecretB = bob.makeSharedSecret(alice); + byte[] sharedSecretA = alice.makeSharedSecret(bob); + byte[] sharedSecretB = bob.makeSharedSecret(alice); - if (sharedSecretA == null || - sharedSecretB == null || - !Arrays.equals(sharedSecretA, sharedSecretB)) { - failed = 1; - } + if (sharedSecretA == null || + sharedSecretB == null || + !Arrays.equals(sharedSecretA, sharedSecretB)) { + throw new Exception( + "shared secrets null or not equal"); } + /* Log success */ + success.incrementAndGet(0); + } catch (Exception e) { e.printStackTrace(); - failed = 1; + + /* Log failure */ + failures.incrementAndGet(0); } finally { alice.releaseNativeStruct(); bob.releaseNativeStruct(); latch.countDown(); } - - if (failed == 1) { - results.add(1); - } - else { - results.add(0); - } } }); } /* wait for all threads to complete */ - latch.await(); - - /* Look for any failures that happened */ - Iterator listIterator = results.iterator(); - while (listIterator.hasNext()) { - Integer cur = listIterator.next(); - if (cur == 1) { - fail("Threading error in DH shared secret thread test"); + returnWithoutTimeout = latch.await(10, TimeUnit.SECONDS); + service.shutdown(); + + /* Check failure count and success count against thread count */ + if ((failures.get(0) != 0) || + (success.get(0) != numThreads)) { + if (returnWithoutTimeout == true) { + fail("DH shared secret test threading error: " + + failures.get(0) + " failures, " + + success.get(0) + " success, " + + numThreads + " num threads total"); + } else { + fail("DH shared secret test error, threads timed out"); } } } diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/RsaTest.java b/src/test/java/com/wolfssl/wolfcrypt/test/RsaTest.java index a0b086f1..d0bd2b6c 100644 --- a/src/test/java/com/wolfssl/wolfcrypt/test/RsaTest.java +++ b/src/test/java/com/wolfssl/wolfcrypt/test/RsaTest.java @@ -71,9 +71,14 @@ public void constructorShouldInitializeNativeStruct() { @Test public void testMakeKey() { - Rsa key = new Rsa(); - key.makeKey(1024, 65537, rng); - key.releaseNativeStruct(); + Rsa key = null; + + /* FIPS after 2425 doesn't allow 1024-bit RSA key gen */ + if (Fips.enabled && Fips.fipsVersion < 5) { + key = new Rsa(); + key.makeKey(1024, 65537, rng); + key.releaseNativeStruct(); + } key = new Rsa(); key.makeKey(2048, 65537, rng); @@ -219,6 +224,12 @@ public void rsaPrivateToPkcs8() { + "4e8c3a458fe69640eb63f919863a51dd894bb0f3f99f5d289538" + "be35abca5ce7935334a1455d1339654246a19fcdf5bf"); + /* FIPS after 2425 doesn't allow 1024-bit RSA key gen */ + if (Fips.enabled && Fips.fipsVersion >= 5) { + /* skip */ + return; + } + /* Test that exception is thrown without private key available */ try { pkcs8 = key.privateKeyEncodePKCS8();