diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eb4b902e..d3e45cf4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,18 +7,92 @@ on: branches: [ "master" ] jobs: - build: - + applet: runs-on: ubuntu-latest permissions: contents: read strategy: matrix: - java: [ "8", "11", "17", "21" ] + java: [ "8", "11", "17"] env: JAVA_VERSION: ${{ matrix.java }} - name: Build Java ${{ matrix.java }} + name: Build applet with Java ${{ matrix.java }} + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + java-version: ${{ matrix.java }} + distribution: "temurin" + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 + + - name: Build applets + run: ./gradlew applet:buildJavaCard + + - name: Test + run: ./gradlew applet:test + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: built-applet-${{ matrix.java }} + path: | + applet/build/javacard/*.cap + + reader: + runs-on: ubuntu-latest + permissions: + contents: read + + strategy: + matrix: + java: [ "11", "17", "21" ] + name: Build reader on Java ${{ matrix.java }} + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + java-version: ${{ matrix.java }} + distribution: "temurin" + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 + + - name: Build reader + run: ./gradlew reader:uberJar + + - name: Test + run: ./gradlew reader:test + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: built-reader-${{ matrix.java }} + path: | + reader/build/libs/ECTesterReader.jar + + standalone: + runs-on: ubuntu-latest + permissions: + contents: read + + strategy: + matrix: + java: [ "11", "17", "21" ] + env: + # ffs: https://github.com/adoptium/adoptium-support/issues/485 !!! + LD_LIBRARY_PATH: "/usr/lib/x86_64-linux-gnu/" + name: Build standalone on Java ${{ matrix.java }} steps: - uses: actions/checkout@v4 with: @@ -40,60 +114,72 @@ jobs: echo "BORINGSSL_VERSION=$(git submodule status ext/boringssl | cut -f2 -d' ')" >> $GITHUB_ENV echo "LIBRESSL_VERSION=$(git submodule status ext/libressl | cut -f2 -d' ')" >> $GITHUB_ENV echo "IPPCP_VERSION=$(git submodule status ext/ipp-crypto | cut -f2 -d' ')" >> $GITHUB_ENV - - - name: Build applets - run: if [ $JAVA_VERSION != 21 ]; then ./gradlew applet:buildJavaCard; fi - - - name: Build reader - run: ./gradlew reader:uberJar + echo "WOLFCRYPT_VERSION=$(git submodule status ext/wolfcrypt-jni | cut -f2 -d' ')" >> $GITHUB_ENV + echo "WOLFSSL_VERSION=$(dpkg -s libwolfssl-dev | grep 'Version' | cut -f2 -d' ')" >> $GITHUB_ENV - name: Cache libs uses: actions/cache@v4 id: cache-libs with: - key: libs-${{ env.BORINGSSL_VERSION }}-${{ env.LIBRESSL_VERSION }}-${{ env.IPPCP_VERSION }} + key: libs-${{ env.BORINGSSL_VERSION }}-${{ env.LIBRESSL_VERSION }}-${{ env.IPPCP_VERSION }}-${{ env.WOLFCRYPT_VERSION }}-${{ env.WOLFSSL_VERSION }} path: | ext/boringssl/build/crypto/libcrypto.so ext/libressl/build/crypto/libcrypto.so ext/ipp-crypto/build/.build/RELEASE/lib/libippcp.so + ext/wolfcrypt-jni/lib/wolfcrypt-jni.jar + ext/wolfcrypt-jni/lib/libwolfcryptjni.so - name: Build libs if: steps.cache-libs.outputs.cache-hit != 'true' run: | + # ------------ Build BoringSSL ------------ cd ext/boringssl cmake -DBUILD_SHARED_LIBS=1 -Bbuild cd build make -j4 crypto cd ../../.. + # ------------ Build LibreSSL ------------ cd ext/libressl ./autogen.sh cmake -DBUILD_SHARED_LIBS=ON -Bbuild cd build make -j4 crypto cd ../../.. + # ------------ Build IPP-crypto ------------ cd ext/ipp-crypto CC=clang CXX=clang++ cmake CMakeLists.txt -Bbuild -DARCH=intel64 cd build make -j4 cd ../../.. + # ------------ Build wolfcrypt-jni ------------ + cd ext/wolfcrypt-jni + mkdir junit + wget -P junit/ https://repo1.maven.org/maven2/junit/junit/4.13.2/junit-4.13.2.jar + wget -P junit/ https://repo1.maven.org/maven2/org/hamcrest/hamcrest-all/1.3/hamcrest-all-1.3.jar + make -j4 -f makefile.linux + env JUNIT_HOME=junit/ ant build-jce-release + cd ../../.. - name: Build standalone run: | ./gradlew standalone:libs || true ./gradlew standalone:uberJar - # ffs: https://github.com/adoptium/adoptium-support/issues/485 !!! - name: List libraries - run: env LD_LIBRARY_PATH="/usr/lib/x86_64-linux-gnu/" ./gradlew standalone:run --args="list-libs" + run: ./gradlew standalone:run --args="list-libs" - name: Test - run: ./gradlew test + run: ./gradlew standalone:test - name: Upload build artifacts uses: actions/upload-artifact@v4 with: - name: built-${{ matrix.java }} + name: built-standalone-${{ matrix.java }} path: | - applet/build/javacard/*.cap - reader/build/libs/ECTesterReader.jar - standalone/build/libs/ECTesterStandalone.jar \ No newline at end of file + standalone/build/libs/ECTesterStandalone.jar + + - name: Upload code coverage + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + slug: crocs-muni/ECTester \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 603e4d97..54819aa2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,6 @@ [submodule "ext/ipp-crypto"] path = ext/ipp-crypto url = https://github.com/intel/ipp-crypto +[submodule "ext/wolfcrypt-jni"] + path = ext/wolfcrypt-jni + url = https://github.com/wolfSSL/wolfcrypt-jni diff --git a/README.md b/README.md index 8c1da32c..ed0741db 100644 --- a/README.md +++ b/README.md @@ -308,7 +308,7 @@ For more information on ECC libraries see [LIBS](docs/LIBS.md). ### Setup ```shell -./gradlew :standalone:libs # To build the native library shims. (Necessary +./gradlew :standalone:libs # To build the native library shims. ./gradlew :standalone:uberJar # To build the standalone tool (jar) -> "standalone/build/libs/ECTesterStandalone.jar" ``` Simply doing the above should build everything necessary to test libraries via the standalone app, @@ -356,9 +356,9 @@ g++ -fPIC -shared -O2 -o botan_provider.so -Wl,-rpath,'$ORIGIN/lib' botan.o cpp_ g++ -fPIC -shared -O2 -o cryptopp_provider.so -Wl,-rpath,'$ORIGIN/lib' cryptopp.o cpp_utils.o -L. -L/usr/local/lib -lcryptopp -l:lib_timing.so ``` -BoringSSL, LibreSSL and ipp-crypto are included as git submodules. Make sure you run: `git submodule update --init --recursive` +BoringSSL, LibreSSL, ipp-crypto and partially wolfCrypt are included as git submodules. Make sure you run: `git submodule update --init --recursive` after checking out the ECTester repository to initialize them. To build BoringSSL do: -``` +```shell cd ext/boringssl cmake -GNinja -Bbuild -DBUILD_SHARED_LIBS=1 cd build @@ -366,7 +366,7 @@ ninja ``` To build LibreSSL do: -``` +```shell cd ext/libressl ./autogen.sh cmake -GNinja -Bbuild -DBUILD_SHARED_LIBS=1 @@ -376,13 +376,27 @@ ninja To build ipp-crypto do: (Make sure you have the necessary [build requirements](https://github.com/intel/ipp-crypto/blob/develop/BUILD.md)) -``` +```shell cd ext/ipp-crypto CC=clang CXX=clang++ cmake CMakeLists.txt -GNinja -Bbuild -DARCH=intel64 # Does not work with GCC 12+ cd build ninja ``` +To build wolfCrypt-JNI do: +(You need to have wolfSSL installed and ready for development) +```shell +cd ext/wolfcrypt-jni +mkdir junit +wget -P junit/ https://repo1.maven.org/maven2/junit/junit/4.13.2/junit-4.13.2.jar +wget -P junit/ https://repo1.maven.org/maven2/org/hamcrest/hamcrest-all/1.3/hamcrest-all-1.3.jar +make -f makefile.linux +env JUNIT_HOME=junit/ ant build-jce-release +``` +The produced `lib/wolfcrypt-jni.jar` will be automatically included into the standalone JAR when building `standalone:uberJar`. +However, the produced `lib/libwolfcryptjni.so` native library will not be automatically loaded. You thus need to include it +on `LD_LIBRARY_PATH`. + #### Java diff --git a/applet/build.gradle.kts b/applet/build.gradle.kts index 5380baea..d5ef7f45 100644 --- a/applet/build.gradle.kts +++ b/applet/build.gradle.kts @@ -51,6 +51,10 @@ dependencies { runtimeOnly("com.klinec:gradle-javacard:1.8.0") } +java { + sourceCompatibility = if (JavaVersion.current() == JavaVersion.VERSION_1_8) JavaVersion.VERSION_1_8 else JavaVersion.VERSION_11 +} + tasks.named("test") { useJUnitPlatform() } diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 5829d8c6..0aca7fb3 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -20,4 +20,8 @@ dependencies { api("org.yaml:snakeyaml:2.2") // https://mvnrepository.com/artifact/com.klinec/jcardsim api("com.klinec:jcardsim:3.0.5.11") +} + +java { + sourceCompatibility = JavaVersion.VERSION_11 } \ No newline at end of file diff --git a/ext/wolfcrypt-jni b/ext/wolfcrypt-jni new file mode 160000 index 00000000..0497ee76 --- /dev/null +++ b/ext/wolfcrypt-jni @@ -0,0 +1 @@ +Subproject commit 0497ee767c994775beda2f2091009593961e5c7e diff --git a/ext/wolfcrypt-jni.jar b/ext/wolfcrypt-jni.jar index 890ae140..be579ee8 100644 Binary files a/ext/wolfcrypt-jni.jar and b/ext/wolfcrypt-jni.jar differ diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index d64cd491..e6441136 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a80b22ce..b82aa23a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/reader/build.gradle.kts b/reader/build.gradle.kts index b0c5fea6..ac322143 100644 --- a/reader/build.gradle.kts +++ b/reader/build.gradle.kts @@ -1,5 +1,7 @@ plugins { application + jacoco + id("com.adarshr.test-logger") version "4.0.0" } repositories { @@ -13,6 +15,15 @@ repositories { dependencies { implementation(project(":common")) implementation(project(":applet")) + + testImplementation(platform("org.junit:junit-bom:5.10.2")) + testImplementation("org.junit.jupiter:junit-jupiter") + testImplementation("org.junit-pioneer:junit-pioneer:2.2.0") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") +} + +java { + sourceCompatibility = JavaVersion.VERSION_11 } application { @@ -21,6 +32,23 @@ application { version = "0.3.3" } +tasks.named("test") { + useJUnitPlatform() + // Report is always generated after tests run + finalizedBy(tasks.jacocoTestReport) +} + +tasks.jacocoTestReport { + reports { + xml.required = true + } +} + +testlogger { + theme = com.adarshr.gradle.testlogger.theme.ThemeType.MOCHA + showStandardStreams = true +} + tasks.register("uberJar") { archiveFileName = "ECTesterReader.jar" duplicatesStrategy = DuplicatesStrategy.WARN diff --git a/standalone/build.gradle.kts b/standalone/build.gradle.kts index 68bfd13c..d05480da 100644 --- a/standalone/build.gradle.kts +++ b/standalone/build.gradle.kts @@ -1,7 +1,8 @@ - plugins { application + jacoco id("com.google.osdetector") version "1.7.3" + id("com.adarshr.test-logger") version "4.0.0" } repositories { @@ -9,14 +10,24 @@ repositories { } dependencies { - implementation(files("$rootDir/ext/wolfcrypt-jni.jar")) + // Fallback to bundled wolfcrypt-jni if the submodule one is not built. + if (file("$rootDir/ext/wolfcrypt-jni/lib/wolfcrypt-jni.jar").exists()) { + implementation(files("$rootDir/ext/wolfcrypt-jni/lib/wolfcrypt-jni.jar")) + } else { + implementation(files("$rootDir/ext/wolfcrypt-jni.jar")) + } implementation(project(":common")) testImplementation(platform("org.junit:junit-bom:5.10.2")) testImplementation("org.junit.jupiter:junit-jupiter") + testImplementation("org.junit-pioneer:junit-pioneer:2.2.0") testRuntimeOnly("org.junit.platform:junit-platform-launcher") } +java { + sourceCompatibility = JavaVersion.VERSION_11 +} + application { applicationName = "ECTesterStandalone" mainClass = "cz.crcs.ectester.standalone.ECTesterStandalone" @@ -25,6 +36,23 @@ application { tasks.named("test") { useJUnitPlatform() + // Report is always generated after tests run + finalizedBy(tasks.jacocoTestReport) + // Add wolfcrypt JNI lib path to LD_LIBRARY_PATH (as our native library loading does not handle it) + environment( + "LD_LIBRARY_PATH", "$rootDir/ext/wolfcrypt-jni/lib/:" + System.getenv("LD_LIBRARY_PATH") + ) +} + +tasks.jacocoTestReport { + reports { + xml.required = true + } +} + +testlogger { + theme = com.adarshr.gradle.testlogger.theme.ThemeType.MOCHA + showStandardStreams = true } tasks.withType { @@ -48,6 +76,7 @@ tasks.register("libs") { tasks.register("uberJar") { archiveFileName = "ECTesterStandalone.jar" + duplicatesStrategy = DuplicatesStrategy.WARN from(sourceSets.main.get().output) diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/ECTesterStandalone.java b/standalone/src/main/java/cz/crcs/ectester/standalone/ECTesterStandalone.java index 57ab98fa..cfdb9644 100644 --- a/standalone/src/main/java/cz/crcs/ectester/standalone/ECTesterStandalone.java +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/ECTesterStandalone.java @@ -839,9 +839,13 @@ private void export() throws NoSuchAlgorithmException, IOException { KeyPair kp = kpg.genKeyPair(); ECPrivateKey privateKey = (ECPrivateKey) kp.getPrivate(); ECParameterSpec params = privateKey.getParams(); - System.out.println(params); - EC_Curve curve = EC_Curve.fromSpec(params); - curve.writeCSV(System.out); + if (params == null) { + System.err.println("Parameters could not be exported (they are NULL)."); + } else { + System.out.println(params); + EC_Curve curve = EC_Curve.fromSpec(params); + curve.writeCSV(System.out); + } } public static void main(String[] args) { diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/libs/WolfCryptLib.java b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/WolfCryptLib.java index b58eb919..ff592d1d 100644 --- a/standalone/src/main/java/cz/crcs/ectester/standalone/libs/WolfCryptLib.java +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/WolfCryptLib.java @@ -11,6 +11,16 @@ public WolfCryptLib() { super(new WolfCryptProvider()); } + @Override + public boolean initialize() { + try { + System.loadLibrary("wolfcryptjni"); + return super.initialize(); + } catch (UnsatisfiedLinkError ule) { + return false; + } + } + @Override public Set getCurves() { return new HashSet<>(); diff --git a/standalone/src/main/resources/cz/crcs/ectester/standalone/libs/jni/nettle.c b/standalone/src/main/resources/cz/crcs/ectester/standalone/libs/jni/nettle.c index e8d874a3..d4fa0a54 100644 --- a/standalone/src/main/resources/cz/crcs/ectester/standalone/libs/jni/nettle.c +++ b/standalone/src/main/resources/cz/crcs/ectester/standalone/libs/jni/nettle.c @@ -75,27 +75,42 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPa return JNI_FALSE; } -static const struct ecc_curve* create_curve(JNIEnv *env, const char* curve_name) { - const struct ecc_curve* curve = NULL; - if (curve_name) { - if (strcasecmp("secp192r1", curve_name) == 0) { - curve = nettle_get_secp_192r1(); - } - if (strcasecmp("secp224r1", curve_name) == 0) { - curve = nettle_get_secp_224r1(); - } - if (strcasecmp("secp256r1", curve_name) == 0) { - curve = nettle_get_secp_256r1(); - } - if (strcasecmp("secp384r1", curve_name) == 0) { - curve = nettle_get_secp_384r1(); - } - if (strcasecmp("secp521r1", curve_name) == 0) { - curve = nettle_get_secp_521r1(); - } - return curve; - } - return NULL; +static const struct ecc_curve* create_curve_from_name(JNIEnv *env, const char* curve_name) { + if (!curve_name) { + return NULL; + } + if (strcasecmp("secp192r1", curve_name) == 0) { + return nettle_get_secp_192r1(); + } + if (strcasecmp("secp224r1", curve_name) == 0) { + return nettle_get_secp_224r1(); + } + if (strcasecmp("secp256r1", curve_name) == 0) { + return nettle_get_secp_256r1(); + } + if (strcasecmp("secp384r1", curve_name) == 0) { + return nettle_get_secp_384r1(); + } + if (strcasecmp("secp521r1", curve_name) == 0) { + return nettle_get_secp_521r1(); + } +} + +static const struct ecc_curve* create_curve_from_size(JNIEnv *env, jint keysize) { + switch (keysize) { + case 192: + return nettle_get_secp_192r1(); + case 224: + return nettle_get_secp_224r1(); + case 256: + return nettle_get_secp_256r1(); + case 384: + return nettle_get_secp_384r1(); + case 521: + return nettle_get_secp_521r1(); + default: + return NULL; + } } JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Nettle_paramsSupported(JNIEnv *env, jobject self, jobject params){ @@ -153,7 +168,6 @@ static jobject generate_from_curve(JNIEnv *env, const struct ecc_curve* curve, j mpz_export((unsigned char*) key_priv + diff, &size, 1, sizeof(unsigned char), 0, 0, private_value); (*env)->ReleaseByteArrayElements(env, priv_bytes, key_priv, 0); - unsigned long key_len = 2*byte_size + 1; jbyteArray pub_bytes = (*env)->NewByteArray(env, key_len); mpz_t pub_value_x; @@ -175,7 +189,6 @@ static jobject generate_from_curve(JNIEnv *env, const struct ecc_curve* curve, j mpz_export((unsigned char*) key_pub + 1 + byte_size + diff, &yLen, 1, sizeof(unsigned char), 0, 0, pub_value_y); (*env)->ReleaseByteArrayElements(env, pub_bytes, key_pub, 0); - jobject ec_pub_param_spec = (*env)->NewLocalRef(env, spec); jmethodID ec_pub_init = (*env)->GetMethodID(env, pubkey_class, "", "([BLjava/security/spec/ECParameterSpec;)V"); jobject pubkey = (*env)->NewObject(env, pubkey_class, ec_pub_init, pub_bytes, ec_pub_param_spec); @@ -189,39 +202,41 @@ static jobject generate_from_curve(JNIEnv *env, const struct ecc_curve* curve, j ecc_point_clear(&pub); ecc_scalar_clear(&priv); return (*env)->NewObject(env, keypair_class, keypair_init, pubkey, privkey); - - } JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Nettle_generate__ILjava_security_SecureRandom_2(JNIEnv *env, jobject self, jint keysize, jobject random) { - throw_new(env, "java/lang/UnsupportedOperationException", "Not supported."); + const struct ecc_curve* curve = create_curve_from_size(env, keysize); + if (!curve) { + throw_new(env, "java/lang/UnsupportedOperationException", "Not supported."); + return NULL; + } + int byte_size = (keysize + 7) / 8; + jobject result = generate_from_curve(env, curve, NULL, byte_size); + return result; return NULL; } - - JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Nettle_generate__Ljava_security_spec_AlgorithmParameterSpec_2Ljava_security_SecureRandom_2Ljava_security_spec_AlgorithmParameterSpec_2(JNIEnv *env, jobject self, jobject params, jobject random, jobject spec) { - if ((*env)->IsInstanceOf(env, params, ec_parameter_spec_class)) { return NULL; } else if ((*env)->IsInstanceOf(env, params, ecgen_parameter_spec_class)) { jmethodID get_name = (*env)->GetMethodID(env, ecgen_parameter_spec_class, "getName", "()Ljava/lang/String;"); jstring name = (*env)->CallObjectMethod(env, params, get_name); const char* utf_name = (*env)->GetStringUTFChars(env, name, NULL); - const struct ecc_curve* curve; + const struct ecc_curve* curve = NULL; int byte_size; char *curve_name[5] = {"secp192r1", "secp224r1", "secp256r1", "secp384r1", "secp521r1"}; int byte_sizes[] = {24, 28, 32, 48, 66}; for (int i = 0; i < sizeof(curve_name); i++) { if (strcasecmp(utf_name, curve_name[i]) == 0) { - curve = create_curve(env, curve_name[i]); + curve = create_curve_from_name(env, curve_name[i]); byte_size = byte_sizes[i]; break; } } (*env)->ReleaseStringUTFChars(env, name, utf_name); if (!curve) { - throw_new(env, "java/security/InvalidAlgorithmParameterException", "Curve for given bitsize not found."); + throw_new(env, "java/security/InvalidAlgorithmParameterException", "Curve with given name not found."); return NULL; } jobject result = generate_from_curve(env, curve, spec, byte_size); @@ -263,13 +278,13 @@ JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKey jmethodID get_name = (*env)->GetMethodID(env, ecgen_parameter_spec_class, "getName", "()Ljava/lang/String;"); jstring name = (*env)->CallObjectMethod(env, params, get_name); const char* utf_name = (*env)->GetStringUTFChars(env, name, NULL); - const struct ecc_curve* curve; + const struct ecc_curve* curve = NULL; char *curve_name[5] = {"secp192r1", "secp224r1", "secp256r1", "secp384r1", "secp521r1"}; int byte_sizes[] = {24, 28, 32, 48, 66}; int byte_size; for (int i = 0; i < sizeof(curve_name); i++) { if (strcasecmp(utf_name, curve_name[i]) == 0) { - curve = create_curve(env, curve_name[i]); + curve = create_curve_from_name(env, curve_name[i]); byte_size = byte_sizes[i]; break; } @@ -412,20 +427,19 @@ int der_to_signature(struct dsa_signature* signature, unsigned char* der) { size_t sLength = der[index++]; mpz_import(signature->s, sLength, 1, sizeof(unsigned char), 0, 0, der + index); return 1; - } JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_00024Nettle_sign(JNIEnv *env, jobject self, jbyteArray data, jbyteArray privkey, jobject params) { jmethodID get_name = (*env)->GetMethodID(env, ecgen_parameter_spec_class, "getName", "()Ljava/lang/String;"); jstring name = (*env)->CallObjectMethod(env, params, get_name); const char* utf_name = (*env)->GetStringUTFChars(env, name, NULL); - const struct ecc_curve* curve; + const struct ecc_curve* curve = NULL; int byte_size; char *curve_name[5] = {"secp192r1", "secp224r1", "secp256r1", "secp384r1", "secp521r1"}; int byte_sizes[] = {24, 28, 32, 48, 66}; for (int i = 0; i < sizeof(curve_name); i++) { if (strcasecmp(utf_name, curve_name[i]) == 0) { - curve = create_curve(env, curve_name[i]); + curve = create_curve_from_name(env, curve_name[i]); byte_size = byte_sizes[i] + 1; break; } @@ -451,7 +465,6 @@ JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSig (*env)->ReleaseByteArrayElements(env, data, data_data, JNI_ABORT); - jsize sig_len = signature_to_der(&signature, NULL, byte_size); jbyteArray result = (*env)->NewByteArray(env, sig_len); jbyte *result_data = (*env)->GetByteArrayElements(env, result, NULL); @@ -467,11 +480,11 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna jmethodID get_name = (*env)->GetMethodID(env, ecgen_parameter_spec_class, "getName", "()Ljava/lang/String;"); jstring name = (*env)->CallObjectMethod(env, params, get_name); const char* utf_name = (*env)->GetStringUTFChars(env, name, NULL); - const struct ecc_curve* curve; + const struct ecc_curve* curve = NULL; char *curve_name[5] = {"secp192r1", "secp224r1", "secp256r1", "secp384r1", "secp521r1"}; for (int i = 0; i < sizeof(curve_name); i++) { if (strcasecmp(utf_name, curve_name[i]) == 0) { - curve = create_curve(env, curve_name[i]); + curve = create_curve_from_name(env, curve_name[i]); break; } } diff --git a/standalone/src/test/java/cz/crcs/ectester/reader/IdentTests.java b/standalone/src/test/java/cz/crcs/ectester/reader/IdentTests.java deleted file mode 100644 index 2940f1e9..00000000 --- a/standalone/src/test/java/cz/crcs/ectester/reader/IdentTests.java +++ /dev/null @@ -1,14 +0,0 @@ -package cz.crcs.ectester.reader; - -import cz.crcs.ectester.standalone.consts.KeyAgreementIdent; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertNotNull; -public class IdentTests { - @Test - void kaIdents() { - for (KeyAgreementIdent keyAgreementIdent : KeyAgreementIdent.list()) { - assertNotNull(keyAgreementIdent.getBaseAlgo()); - } - } -} diff --git a/standalone/src/test/java/cz/crcs/ectester/standalone/AppTests.java b/standalone/src/test/java/cz/crcs/ectester/standalone/AppTests.java new file mode 100644 index 00000000..1fdde0cc --- /dev/null +++ b/standalone/src/test/java/cz/crcs/ectester/standalone/AppTests.java @@ -0,0 +1,147 @@ +package cz.crcs.ectester.standalone; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.junitpioneer.jupiter.StdIo; +import org.junitpioneer.jupiter.StdOut; + +import static org.junit.jupiter.api.Assertions.*; + +public class AppTests { + + @Test + @StdIo() + public void help(StdOut out) { + ECTesterStandalone.main(new String[]{"-h"}); + String s = out.capturedString(); + assertTrue(s.contains("ECTesterStandalone")); + } + + @Test + @StdIo() + public void listLibraries(StdOut out) { + ECTesterStandalone.main(new String[]{"list-libs"}); + String s = out.capturedString(); + assertTrue(s.contains("BouncyCastle")); + } + + @Test + @StdIo() + public void listData(StdOut out) { + ECTesterStandalone.main(new String[]{"list-data"}); + String s = out.capturedString(); + assertTrue(s.contains("secg")); + } + + @Test + @StdIo() + public void listSuites(StdOut out) { + ECTesterStandalone.main(new String[]{"list-suites"}); + String s = out.capturedString(); + assertTrue(s.contains("default test suite")); + } + + @Test + @StdIo() + public void listIdents(StdOut out) { + ECTesterStandalone.main(new String[]{"list-types"}); + String s = out.capturedString(); + assertTrue(s.contains("NONEwithECDSA")); + } + + @SuppressWarnings("JUnitMalformedDeclaration") + @ParameterizedTest + @ValueSource(strings = {"Bouncy", "Sun", "libtomcrypt", "Botan", "Crypto++", "OpenSSL 3", "BoringSSL", "libgcrypt", "mbed TLS", "2021" /* IPPCP */, "Nettle", "LibreSSL", "wolfCrypt"}) + @StdIo() + public void defaultSuite(String libName, StdOut out) { + String[] args = new String[]{"test", "default", libName}; + if (libName.equals("Botan") || libName.equals("Crypto++")) { + args = new String[]{"test", "--kpg-type", "ECDH", "default", libName}; + } + ECTesterStandalone.main(args); + String sout = out.capturedString(); + if (sout.contains("Exception")) { + System.err.printf("%s: Default suite has exceptions.%n", libName); + } + } + + @SuppressWarnings("JUnitMalformedDeclaration") + @ParameterizedTest + @ValueSource(strings = {"Bouncy", "Sun", "libtomcrypt", "Botan", "Crypto++", "OpenSSL 3", "BoringSSL", "libgcrypt", "mbed TLS", "2021" /* IPPCP */, "Nettle", "LibreSSL", "wolfCrypt"}) + @StdIo() + public void generate(String libName, StdOut out) { + String[] args = new String[]{"generate", "-n", "10", "-nc", "secg/secp256r1", libName}; + switch (libName) { + case "Botan": + case "Crypto++": + args = new String[]{"generate", "-n", "10", "-nc", "secg/secp256r1", "-t", "ECDH", libName}; + break; + case "Nettle": + case "libgcrypt": + args = new String[]{"generate", "-n", "10", "-cn", "secp256r1", libName}; + break; + case "BoringSSL": + args = new String[]{"generate", "-n", "10", "-cn", "prime256v1", libName}; + break; + } + ECTesterStandalone.main(args); + } + + @SuppressWarnings("JUnitMalformedDeclaration") + @ParameterizedTest + @ValueSource(strings = {"Bouncy", "Sun", "libtomcrypt", "Botan", "Crypto++", "OpenSSL 3", "BoringSSL", "libgcrypt", "mbed TLS", "2021" /* IPPCP */, "Nettle", "LibreSSL", "wolfCrypt"}) + @StdIo() + public void ecdh(String libName, StdOut out) { + String[] args = new String[]{"ecdh", "-n", "10", "-nc", "secg/secp256r1", libName}; + switch (libName) { + case "Nettle": + case "libgcrypt": + args = new String[]{"ecdh", "-n", "10", "-cn", "secp256r1", libName}; + break; + case "BoringSSL": + args = new String[]{"ecdh", "-n", "10", "-cn", "prime256v1", libName}; + break; + } + ECTesterStandalone.main(args); + } + + @SuppressWarnings("JUnitMalformedDeclaration") + @ParameterizedTest + @ValueSource(strings = {"Bouncy", "Sun", "libtomcrypt", "Botan", "Crypto++", "OpenSSL 3", "BoringSSL", "libgcrypt", "mbed TLS", "2021" /* IPPCP */, "Nettle", "LibreSSL", "wolfCrypt"}) + @StdIo() + public void ecdsa(String libName, StdOut out) { + String[] args = new String[]{"ecdsa", "-n", "10", "-nc", "secg/secp256r1", libName}; + switch (libName) { + case "Nettle": + case "libgcrypt": + args = new String[]{"ecdsa", "-n", "10", "-cn", "secp256r1", "-t", "NONEwithECDSA", libName}; + break; + case "BoringSSL": + args = new String[]{"ecdsa", "-n", "10", "-cn", "prime256v1", "-t", "NONEwithECDSA", libName}; + break; + case "OpenSSL 3": + case "libtomcrypt": + case "LibreSSL": + case "2021": + args = new String[]{"ecdsa", "-n", "10", "-nc", "secg/secp256r1", "-t", "NONEwithECDSA", libName}; + break; + } + ECTesterStandalone.main(args); + } + + @SuppressWarnings("JUnitMalformedDeclaration") + @ParameterizedTest + @ValueSource(strings = {"Bouncy", "Sun", "libtomcrypt", "Botan", "Crypto++", "OpenSSL 3", "BoringSSL", "libgcrypt", "mbed TLS", "2021" /* IPPCP */, "Nettle", "LibreSSL", "wolfCrypt"}) + @StdIo() + public void export(String libName, StdOut out) { + String[] args = new String[]{"export", "-b", "256", libName}; + switch (libName) { + case "Botan": + case "Crypto++": + args = new String[]{"export", "-b", "256", "-t", "ECDH", libName}; + break; + } + ECTesterStandalone.main(args); + System.err.println(out.capturedString()); + } +} diff --git a/standalone/src/test/java/cz/crcs/ectester/standalone/IdentTests.java b/standalone/src/test/java/cz/crcs/ectester/standalone/IdentTests.java new file mode 100644 index 00000000..e6f520ed --- /dev/null +++ b/standalone/src/test/java/cz/crcs/ectester/standalone/IdentTests.java @@ -0,0 +1,49 @@ +package cz.crcs.ectester.standalone; + +import cz.crcs.ectester.standalone.consts.KeyAgreementIdent; +import cz.crcs.ectester.standalone.consts.KeyPairGeneratorIdent; +import cz.crcs.ectester.standalone.consts.SignatureIdent; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.junit.jupiter.api.Test; + +import javax.crypto.KeyAgreement; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.Signature; + +import static org.junit.jupiter.api.Assertions.*; + +public class IdentTests { + + Provider bc = new BouncyCastleProvider(); + + @Test + void kaIdents() throws NoSuchAlgorithmException { + for (KeyAgreementIdent keyAgreementIdent : KeyAgreementIdent.list()) { + assertNotNull(keyAgreementIdent.getBaseAlgo()); + } + KeyAgreementIdent ecdh = KeyAgreementIdent.get("ECDH"); + assertNotNull(ecdh); + KeyAgreement instance = ecdh.getInstance(bc); + assertNotNull(instance); + } + + @Test + void kpgIdents() throws NoSuchAlgorithmException { + assertFalse(KeyPairGeneratorIdent.list().isEmpty()); + KeyPairGeneratorIdent kpg = KeyPairGeneratorIdent.get("ECDH"); + assertNotNull(kpg); + KeyPairGenerator instance = kpg.getInstance(bc); + assertNotNull(instance); + } + + @Test + void sigIdents() throws NoSuchAlgorithmException { + assertFalse(SignatureIdent.list().isEmpty()); + SignatureIdent ecdsa = SignatureIdent.get("NONEwithECDSA"); + assertNotNull(ecdsa); + Signature instance = ecdsa.getInstance(bc); + assertNotNull(instance); + } +} diff --git a/standalone/src/test/java/cz/crcs/ectester/standalone/LibTests.java b/standalone/src/test/java/cz/crcs/ectester/standalone/LibTests.java new file mode 100644 index 00000000..6e11ccd6 --- /dev/null +++ b/standalone/src/test/java/cz/crcs/ectester/standalone/LibTests.java @@ -0,0 +1,54 @@ +package cz.crcs.ectester.standalone; +import cz.crcs.ectester.standalone.libs.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +import java.lang.reflect.InvocationTargetException; +import java.util.LinkedList; +import java.util.List; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class LibTests { + + ProviderECLibrary[] libs; + + @BeforeAll + public void loadLibs() { + List libObjects = new LinkedList<>(); + Class[] libClasses = new Class[]{SunECLib.class, + BouncyCastleLib.class, + TomcryptLib.class, + BotanLib.class, + CryptoppLib.class, + OpensslLib.class, + BoringsslLib.class, + GcryptLib.class, + MscngLib.class, + WolfCryptLib.class, + MbedTLSLib.class, + IppcpLib.class, + MatrixsslLib.class, + NettleLib.class, + LibresslLib.class}; + for (Class c : libClasses) { + try { + libObjects.add((ProviderECLibrary) c.getDeclaredConstructor().newInstance()); + } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | + InvocationTargetException ignored) { + } + } + libs = libObjects.toArray(new ProviderECLibrary[0]); + for (ProviderECLibrary lib : libs) { + lib.initialize(); + } + } + + @Test + public void loaded() { + for (ProviderECLibrary lib : libs) { + System.err.printf("%s: %b%n", lib.getClass().getSimpleName(), lib.isInitialized()); + } + + } +}