diff options
8 files changed, 193 insertions, 56 deletions
diff --git a/common/src/main/java/cz/crcs/ectester/common/output/BaseFileTestWriter.java b/common/src/main/java/cz/crcs/ectester/common/output/BaseFileTestWriter.java index 523a92f..9050e87 100644 --- a/common/src/main/java/cz/crcs/ectester/common/output/BaseFileTestWriter.java +++ b/common/src/main/java/cz/crcs/ectester/common/output/BaseFileTestWriter.java @@ -4,21 +4,24 @@ import javax.xml.parsers.ParserConfigurationException; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.PrintStream; +import java.util.LinkedList; +import java.util.List; public abstract class BaseFileTestWriter extends TeeTestWriter { public BaseFileTestWriter(String defaultFormat, boolean systemOut, String[] files) throws ParserConfigurationException, FileNotFoundException { int fLength = files == null ? 0 : files.length; - writers = new TestWriter[systemOut ? fLength + 1 : fLength]; + List<TestWriter> lWriters = new LinkedList<>(); if (systemOut) { - writers[0] = createWriter(defaultFormat, System.out); + lWriters.add(createWriter(defaultFormat, System.out)); } for (int i = 0; i < fLength; ++i) { String[] matched = matchName(files[i]); String format = matched[0]; String fName = matched[1]; - writers[i + 1] = createWriter(format, new PrintStream(new FileOutputStream(fName))); + lWriters.add(createWriter(format, new PrintStream(new FileOutputStream(fName)))); } + writers = lWriters.toArray(new TestWriter[0]); } protected abstract String[] matchName(String name); diff --git a/standalone/build.gradle.kts b/standalone/build.gradle.kts index bc7e42a..e58fd9b 100644 --- a/standalone/build.gradle.kts +++ b/standalone/build.gradle.kts @@ -39,7 +39,7 @@ tasks.named<Test>("test") { doFirst { resultsDir.mkdirs(); } - + ignoreFailures = true useJUnitPlatform() // Report is always generated after tests run finalizedBy(tasks.jacocoTestReport) 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 a3b6e5b..c702dee 100644 --- a/standalone/src/main/java/cz/crcs/ectester/standalone/ECTesterStandalone.java +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/ECTesterStandalone.java @@ -197,6 +197,7 @@ public class ECTesterStandalone { Option bits = Option.builder("b").longOpt("bits").hasArg().argName("n").optionalArg(false).desc("What size of curve to use.").numberOfArgs(1).build(); Option output = Option.builder("o").longOpt("output").desc("Output into file <output_file>. The file can be prefixed by the format (one of text,yml,xml), such as: xml:<output_file>.").hasArgs().argName("output_file").optionalArg(false).numberOfArgs(1).build(); Option outputRaw = Option.builder("o").longOpt("output").desc("Output CSV into file <output_file>.").hasArgs().argName("output_file").optionalArg(false).numberOfArgs(1).build(); + Option quiet = Option.builder("q").longOpt("quiet").desc("Do not output to stdout.").build(); Option timeSource = Option.builder("ts").longOpt("time-source").desc("Use a given native timing source: {rdtsc, monotonic, monotonic-raw, cputime-process, cputime-thread, perfcount}").hasArgs().argName("source").optionalArg(false).numberOfArgs(1).build(); Options testOpts = new Options(); @@ -204,6 +205,7 @@ public class ECTesterStandalone { testOpts.addOption(namedCurve); testOpts.addOption(curveName); testOpts.addOption(output); + testOpts.addOption(quiet); testOpts.addOption(Option.builder("gt").longOpt("kpg-type").desc("Set the KeyPairGenerator object [type].").hasArg().argName("type").optionalArg(false).build()); testOpts.addOption(Option.builder("kt").longOpt("ka-type").desc("Set the KeyAgreement object [type].").hasArg().argName("type").optionalArg(false).build()); testOpts.addOption(Option.builder("st").longOpt("sig-type").desc("Set the Signature object [type].").hasArg().argName("type").optionalArg(false).build()); @@ -759,7 +761,7 @@ public class ECTesterStandalone { * */ private void test() throws TestException, ParserConfigurationException, FileNotFoundException { - TestWriter writer = new FileTestWriter(cli.getOptionValue("test.format", "text"), true, cli.getOptionValues("test.output")); + TestWriter writer = new FileTestWriter(cli.getOptionValue("test.format", "text"), !cli.hasOption("test.quiet"), cli.getOptionValues("test.output")); StandaloneTestSuite suite; switch (cli.getArg(0).toLowerCase()) { diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/libs/jni/NativeKeyAgreementSpi.java b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/jni/NativeKeyAgreementSpi.java index dd982aa..afed02b 100644 --- a/standalone/src/main/java/cz/crcs/ectester/standalone/libs/jni/NativeKeyAgreementSpi.java +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/jni/NativeKeyAgreementSpi.java @@ -411,7 +411,6 @@ public abstract class NativeKeyAgreementSpi extends KeyAgreementSpi { return generateSecret(pubkey, privkey, spec); } catch (NoSuchAlgorithmException | InvalidParameterSpecException e) { - e.printStackTrace(); return null; } } diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/libs/jni/NativeSignatureSpi.java b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/jni/NativeSignatureSpi.java index 571a2ee..9557288 100644 --- a/standalone/src/main/java/cz/crcs/ectester/standalone/libs/jni/NativeSignatureSpi.java +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/jni/NativeSignatureSpi.java @@ -618,7 +618,6 @@ public abstract class NativeSignatureSpi extends SignatureSpi { return verify(signature, data, pubkey, spec); } catch (NoSuchAlgorithmException | InvalidParameterSpecException e) { - e.printStackTrace(); return false; } } diff --git a/standalone/src/main/resources/cz/crcs/ectester/standalone/libs/jni/botan.cpp b/standalone/src/main/resources/cz/crcs/ectester/standalone/libs/jni/botan.cpp index b26d11a..c5bc5f1 100644 --- a/standalone/src/main/resources/cz/crcs/ectester/standalone/libs/jni/botan.cpp +++ b/standalone/src/main/resources/cz/crcs/ectester/standalone/libs/jni/botan.cpp @@ -318,7 +318,13 @@ JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPai throw_new(env, "java/security/InvalidAlgorithmParameterException", "Curve not found."); return nullptr; } - Botan::EC_Group curve_group = group_from_params(env, params); + Botan::EC_Group curve_group; + try { + curve_group = group_from_params(env, params); + } catch (Botan::Exception & ex) { + throw_new(env, "java/security/GeneralSecurityException", ex.what()); + return nullptr; + } return generate_from_group(env, self, curve_group); } @@ -357,7 +363,13 @@ jbyteArray generate_secret(JNIEnv *env, jobject self, jbyteArray pubkey, jbyteAr throw_new(env, "java/security/InvalidAlgorithmParameterException", "Curve not found."); return nullptr; } - Botan::EC_Group curve_group = group_from_params(env, params); + Botan::EC_Group curve_group; + try { + curve_group = group_from_params(env, params); + } catch (Botan::Exception & ex) { + throw_new(env, "java/security/GeneralSecurityException", ex.what()); + return nullptr; + } jsize privkey_length = env->GetArrayLength(privkey); jbyte *privkey_data = env->GetByteArrayElements(privkey, nullptr); @@ -428,7 +440,13 @@ JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSig throw_new(env, "java/security/InvalidAlgorithmParameterException", "Curve not found."); return nullptr; } - Botan::EC_Group curve_group = group_from_params(env, params); + Botan::EC_Group curve_group; + try { + curve_group = group_from_params(env, params); + } catch (Botan::Exception & ex) { + throw_new(env, "java/security/GeneralSecurityException", ex.what()); + return nullptr; + } jclass botan_sig_class = env->FindClass("cz/crcs/ectester/standalone/libs/jni/NativeSignatureSpi$Botan"); jfieldID type_id = env->GetFieldID(botan_sig_class, "type", "Ljava/lang/String;"); @@ -439,16 +457,28 @@ JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSig jsize privkey_length = env->GetArrayLength(privkey); jbyte *privkey_bytes = env->GetByteArrayElements(privkey, nullptr); - Botan::BigInt privkey_scalar((uint8_t*) privkey_bytes, privkey_length); + Botan::BigInt privkey_scalar; + try { + privkey_scalar = Botan::BigInt((uint8_t*) privkey_bytes, privkey_length); + } catch (Botan::Exception & ex) { + throw_new(env, "java/security/GeneralSecurityException", ex.what()); + env->ReleaseByteArrayElements(privkey, privkey_bytes, JNI_ABORT); + return NULL; + } env->ReleaseByteArrayElements(privkey, privkey_bytes, JNI_ABORT); std::unique_ptr<Botan::EC_PrivateKey> skey; - if (type_str.find("ECDSA") != std::string::npos) { - skey = std::make_unique<Botan::ECDSA_PrivateKey>(rng, curve_group, privkey_scalar); - } else if (type_str.find("ECKCDSA") != std::string::npos) { - skey = std::make_unique<Botan::ECKCDSA_PrivateKey>(rng, curve_group, privkey_scalar); - } else if (type_str.find("ECGDSA") != std::string::npos) { - skey = std::make_unique<Botan::ECGDSA_PrivateKey>(rng, curve_group, privkey_scalar); + try { + if (type_str.find("ECDSA") != std::string::npos) { + skey = std::make_unique<Botan::ECDSA_PrivateKey>(rng, curve_group, privkey_scalar); + } else if (type_str.find("ECKCDSA") != std::string::npos) { + skey = std::make_unique<Botan::ECKCDSA_PrivateKey>(rng, curve_group, privkey_scalar); + } else if (type_str.find("ECGDSA") != std::string::npos) { + skey = std::make_unique<Botan::ECGDSA_PrivateKey>(rng, curve_group, privkey_scalar); + } + } catch (Botan::Exception & ex) { + throw_new(env, "java/security/GeneralSecurityException", ex.what()); + return NULL; } std::string emsa; @@ -466,12 +496,12 @@ JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSig emsa = "EMSA1(SHA-512)"; } - Botan::PK_Signer signer(*skey, rng, emsa, Botan::DER_SEQUENCE); - jsize data_length = env->GetArrayLength(data); jbyte *data_bytes = env->GetByteArrayElements(data, nullptr); std::vector<uint8_t> sig; try { + Botan::PK_Signer signer(*skey, rng, emsa, Botan::DER_SEQUENCE); + native_timing_start(); sig = signer.sign_message((uint8_t*) data_bytes, data_length, rng); native_timing_stop(); @@ -495,7 +525,13 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna throw_new(env, "java/security/InvalidAlgorithmParameterException", "Curve not found."); return JNI_FALSE; } - Botan::EC_Group curve_group = group_from_params(env, params); + Botan::EC_Group curve_group; + try { + curve_group = group_from_params(env, params); + } catch (Botan::Exception & ex) { + throw_new(env, "java/security/GeneralSecurityException", ex.what()); + return JNI_FALSE; + } jclass botan_sig_class = env->FindClass("cz/crcs/ectester/standalone/libs/jni/NativeSignatureSpi$Botan"); jfieldID type_id = env->GetFieldID(botan_sig_class, "type", "Ljava/lang/String;"); @@ -511,17 +547,23 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna public_point = curve_group.OS2ECP((uint8_t*) pubkey_data, pubkey_length); } catch (Botan::Exception & ex) { throw_new(env, "java/security/GeneralSecurityException", ex.what()); + env->ReleaseByteArrayElements(pubkey, pubkey_data, JNI_ABORT); return JNI_FALSE; } env->ReleaseByteArrayElements(pubkey, pubkey_data, JNI_ABORT); std::unique_ptr<Botan::EC_PublicKey> pkey; - if (type_str.find("ECDSA") != std::string::npos) { - pkey = std::make_unique<Botan::ECDSA_PublicKey>(curve_group, public_point); - } else if (type_str.find("ECKCDSA") != std::string::npos) { - pkey = std::make_unique<Botan::ECKCDSA_PublicKey>(curve_group, public_point); - } else if (type_str.find("ECGDSA") != std::string::npos) { - pkey = std::make_unique<Botan::ECGDSA_PublicKey>(curve_group, public_point); + try { + if (type_str.find("ECDSA") != std::string::npos) { + pkey = std::make_unique<Botan::ECDSA_PublicKey>(curve_group, public_point); + } else if (type_str.find("ECKCDSA") != std::string::npos) { + pkey = std::make_unique<Botan::ECKCDSA_PublicKey>(curve_group, public_point); + } else if (type_str.find("ECGDSA") != std::string::npos) { + pkey = std::make_unique<Botan::ECGDSA_PublicKey>(curve_group, public_point); + } + } catch (Botan::Exception & ex) { + throw_new(env, "java/security/GeneralSecurityException", ex.what()); + return JNI_FALSE; } std::string emsa; @@ -539,15 +581,16 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna emsa = "EMSA1(SHA-512)"; } - Botan::PK_Verifier verifier(*pkey, emsa, Botan::DER_SEQUENCE); - jsize data_length = env->GetArrayLength(data); jsize sig_length = env->GetArrayLength(signature); jbyte *data_bytes = env->GetByteArrayElements(data, nullptr); jbyte *sig_bytes = env->GetByteArrayElements(signature, nullptr); bool result; + try { + Botan::PK_Verifier verifier(*pkey, emsa, Botan::DER_SEQUENCE); + native_timing_start(); result = verifier.verify_message((uint8_t*)data_bytes, data_length, (uint8_t*)sig_bytes, sig_length); native_timing_stop(); diff --git a/standalone/src/main/resources/cz/crcs/ectester/standalone/libs/jni/cryptopp.cpp b/standalone/src/main/resources/cz/crcs/ectester/standalone/libs/jni/cryptopp.cpp index bffa6a3..6ec2060 100644 --- a/standalone/src/main/resources/cz/crcs/ectester/standalone/libs/jni/cryptopp.cpp +++ b/standalone/src/main/resources/cz/crcs/ectester/standalone/libs/jni/cryptopp.cpp @@ -558,13 +558,19 @@ JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPai } JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Cryptopp_generate__Ljava_security_spec_AlgorithmParameterSpec_2Ljava_security_SecureRandom_2(JNIEnv *env, jobject self, jobject params, jobject random) { - std::unique_ptr<DL_GroupParameters_EC<ECP>> ecp_group = fp_group_from_params(env, params); - if (ecp_group == nullptr) { - std::unique_ptr<DL_GroupParameters_EC<EC2N>> ec2n_group = f2m_group_from_params(env, params); - return generate_from_group<EC2N>(env, *ec2n_group, params); - } else { - return generate_from_group<ECP>(env, *ecp_group, params); + try { + std::unique_ptr<DL_GroupParameters_EC<ECP>> ecp_group = fp_group_from_params(env, params); + if (ecp_group == nullptr) { + std::unique_ptr<DL_GroupParameters_EC<EC2N>> ec2n_group = f2m_group_from_params(env, params); + return generate_from_group<EC2N>(env, *ec2n_group, params); + } else { + return generate_from_group<ECP>(env, *ecp_group, params); + } + } catch (Exception & ex) { + throw_new(env, "java/security/GeneralSecurityException", ex.what()); + return nullptr; } + return nullptr; } @@ -581,33 +587,32 @@ JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKey bool success; std::unique_ptr<SecByteBlock> secret; - std::unique_ptr<DL_GroupParameters_EC<ECP>> ecp_group = fp_group_from_params(env, params); - if (ecp_group == nullptr) { - std::unique_ptr<DL_GroupParameters_EC<EC2N>> ec2n_group = f2m_group_from_params(env, params); - ECDH<EC2N>::Domain dh_agreement(*ec2n_group); + try { + std::unique_ptr<DL_GroupParameters_EC<ECP>> ecp_group = fp_group_from_params(env, params); + if (ecp_group == nullptr) { + std::unique_ptr<DL_GroupParameters_EC<EC2N>> ec2n_group = f2m_group_from_params(env, params); + + ECDH<EC2N>::Domain dh_agreement(*ec2n_group); - try { secret = std::make_unique<SecByteBlock>(dh_agreement.AgreedValueLength()); native_timing_start(); success = dh_agreement.Agree(*secret, private_key, public_key); native_timing_stop(); - } catch (Exception & ex) { - throw_new(env, "java/security/GeneralSecurityException", ex.what()); - return nullptr; - } - } else { - ECDH<ECP>::Domain dh_agreement(*ecp_group); - try { + } else { + ECDH<ECP>::Domain dh_agreement(*ecp_group); + secret = std::make_unique<SecByteBlock>(dh_agreement.AgreedValueLength()); native_timing_start(); success = dh_agreement.Agree(*secret, private_key, public_key); native_timing_stop(); - } catch (Exception & ex) { - throw_new(env, "java/security/GeneralSecurityException", ex.what()); - return nullptr; + } + } catch (Exception & ex) { + throw_new(env, "java/security/GeneralSecurityException", ex.what()); + return nullptr; } + if (!success) { throw_new(env, "java/security/GeneralSecurityException", "Agreement was unsuccessful."); return nullptr; diff --git a/standalone/src/test/java/cz/crcs/ectester/standalone/AppTests.java b/standalone/src/test/java/cz/crcs/ectester/standalone/AppTests.java index 0476818..1408ff3 100644 --- a/standalone/src/test/java/cz/crcs/ectester/standalone/AppTests.java +++ b/standalone/src/test/java/cz/crcs/ectester/standalone/AppTests.java @@ -1,6 +1,8 @@ package cz.crcs.ectester.standalone; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.junitpioneer.jupiter.StdIo; @@ -9,7 +11,6 @@ import org.junitpioneer.jupiter.StdOut; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.PrintStream; -import java.nio.file.Path; import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -143,7 +144,7 @@ public class AppTests { @ParameterizedTest @MethodSource("libs") public void signatureSuite(String libName) { - String[] args = buildCLIArgs(libName, "signature"); + String[] args = buildCLIArgs(libName, "signature", "-q"); switch (libName) { case "Nettle": case "libgcrypt": @@ -153,7 +154,7 @@ public class AppTests { case "LibreSSL": case "IPPCP": case "mbedTLS": - args = buildCLIArgs(libName, "signature", "-st", "NONEwithECDSA"); + args = buildCLIArgs(libName, "signature", "-st", "NONEwithECDSA", "-q"); break; } ECTesterStandalone.main(args); @@ -161,23 +162,108 @@ public class AppTests { @ParameterizedTest @MethodSource("libs") + @Timeout(20) public void miscSuite(String libName) { - String[] args = buildCLIArgs(libName, "miscellaneous"); + String[] args = buildCLIArgs(libName, "miscellaneous", "-q"); if (libName.equals("Botan") || libName.equals("Crypto++")) { - args = buildCLIArgs(libName, "miscellaneous", "--kpg-type", "ECDH"); + args = buildCLIArgs(libName, "miscellaneous", "--kpg-type", "ECDH", "-q"); } ECTesterStandalone.main(args); } @ParameterizedTest @MethodSource("libs") + @Timeout(20) + public void twistSuite(String libName) { + // TODO: "Nettle" is very broken here for a weird reason. + assumeFalse(libName.equals("Nettle")); + + String[] args = buildCLIArgs(libName, "twist", "-q"); + if (libName.equals("Botan") || libName.equals("Crypto++")) { + args = buildCLIArgs(libName, "twist", "--kpg-type", "ECDH", "-q"); + } + ECTesterStandalone.main(args); + } + + @ParameterizedTest + @MethodSource("libs") + @Timeout(20) + public void degenerateSuite(String libName) { + // TODO: "Nettle" is very broken here for a weird reason. + assumeFalse(libName.equals("Nettle")); + + String[] args = buildCLIArgs(libName, "degenerate", "-q"); + if (libName.equals("Botan") || libName.equals("Crypto++")) { + args = buildCLIArgs(libName, "degenerate", "--kpg-type", "ECDH", "-q"); + } + ECTesterStandalone.main(args); + } + + @ParameterizedTest + @MethodSource("libs") + @Timeout(20) + public void edgeCasesSuite(String libName) { + // TODO: Crypto++ and tomcrypt is broken here. + assumeFalse(libName.equals("Crypto++") || libName.equals("tomcrypt")); + + String[] args = buildCLIArgs(libName, "edge-cases", "-q"); + if (libName.equals("Botan") || libName.equals("Crypto++")) { + args = buildCLIArgs(libName, "edge-cases", "--kpg-type", "ECDH", "-q"); + } + ECTesterStandalone.main(args); + } + + @ParameterizedTest + @MethodSource("libs") + @Timeout(20) + public void compositeSuite(String libName) { + // TODO: "Crypto++" and IPPCP cycles indefinitely here. + assumeFalse(libName.equals("Crypto++") || libName.equals("IPPCP")); + + String[] args = buildCLIArgs(libName, "composite", "-q"); + if (libName.equals("Botan") || libName.equals("Crypto++")) { + args = buildCLIArgs(libName, "composite", "--kpg-type", "ECDH", "-q"); + } + ECTesterStandalone.main(args); + } + + @ParameterizedTest + @MethodSource("libs") + @Timeout(20) + public void cofactorSuite(String libName) { + String[] args = buildCLIArgs(libName, "cofactor", "-q"); + if (libName.equals("Botan") || libName.equals("Crypto++")) { + args = buildCLIArgs(libName, "cofactor", "--kpg-type", "ECDH", "-q"); + } + ECTesterStandalone.main(args); + } + + @ParameterizedTest + @MethodSource("libs") + @Timeout(20) + // TODO: This breaks the tests because the libs do all sorts of weird stuff here. + @Disabled + public void wrongSuite(String libName) { + // TODO: "BouncyCastle" and Crypto++ cycles indefinitely here. + assumeFalse(libName.equals("BouncyCastle") || libName.equals("Crypto++") || libName.equals("IPPCP") || libName.equals("wolfCrypt")); + + String[] args = buildCLIArgs(libName, "wrong", "-q"); + if (libName.equals("Botan") || libName.equals("Crypto++")) { + args = buildCLIArgs(libName, "wrong", "--kpg-type", "ECDH", "-q"); + } + ECTesterStandalone.main(args); + } + + @ParameterizedTest + @MethodSource("libs") + @Timeout(20) public void invalidSuite(String libName) { // TODO: "Nettle" is very broken here for a weird reason. assumeFalse(libName.equals("Nettle")); - String[] args = buildCLIArgs(libName, "invalid"); + String[] args = buildCLIArgs(libName, "invalid", "-q"); if (libName.equals("Botan") || libName.equals("Crypto++")) { - args = buildCLIArgs(libName, "invalid", "--kpg-type", "ECDH"); + args = buildCLIArgs(libName, "invalid", "--kpg-type", "ECDH", "-q"); } ECTesterStandalone.main(args); } |
