diff options
| author | Ján Jančár | 2024-03-25 20:13:02 +0100 |
|---|---|---|
| committer | GitHub | 2024-03-25 20:13:02 +0100 |
| commit | dc7bb975e1c230f1f0a1937c454b2b90817d18e9 (patch) | |
| tree | 8b4e3437e489894afcead9a6466050f3611cb38d /standalone/src/main/java | |
| parent | 64b95fa059295e1dc23371c849f2302c1c18f5b4 (diff) | |
| parent | 591deaece718b7821385ff0069b44adac4e1baf3 (diff) | |
| download | ECTester-dc7bb975e1c230f1f0a1937c454b2b90817d18e9.tar.gz ECTester-dc7bb975e1c230f1f0a1937c454b2b90817d18e9.tar.zst ECTester-dc7bb975e1c230f1f0a1937c454b2b90817d18e9.zip | |
Merge pull request #20 from crocs-muni/feat/gradle
Move to gradle build
Diffstat (limited to 'standalone/src/main/java')
53 files changed, 6827 insertions, 0 deletions
diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/ECTesterStandalone.java b/standalone/src/main/java/cz/crcs/ectester/standalone/ECTesterStandalone.java new file mode 100644 index 0000000..5d3bad2 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/ECTesterStandalone.java @@ -0,0 +1,949 @@ +/* + * ECTester, tool for testing Elliptic curve cryptography implementations. + * Copyright (c) 2016-2018 Petr Svenda <petr@svenda.com> + * Copyright (c) 2016-2019 Jan Jancar <johny@neuromancer.sk> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package cz.crcs.ectester.standalone; + +import cz.crcs.ectester.common.cli.*; +import cz.crcs.ectester.common.ec.EC_Consts; +import cz.crcs.ectester.common.ec.EC_Curve; +import cz.crcs.ectester.common.output.TestWriter; +import cz.crcs.ectester.common.test.TestException; +import cz.crcs.ectester.common.util.ByteUtil; +import cz.crcs.ectester.common.util.ECUtil; +import cz.crcs.ectester.common.util.FileUtil; +import cz.crcs.ectester.data.EC_Store; +import cz.crcs.ectester.standalone.consts.KeyAgreementIdent; +import cz.crcs.ectester.standalone.consts.KeyPairGeneratorIdent; +import cz.crcs.ectester.standalone.consts.SignatureIdent; +import cz.crcs.ectester.standalone.libs.*; +import cz.crcs.ectester.standalone.output.TextTestWriter; +import cz.crcs.ectester.standalone.output.XMLTestWriter; +import cz.crcs.ectester.standalone.output.YAMLTestWriter; +import cz.crcs.ectester.standalone.test.suites.*; +import org.apache.commons.cli.*; + +import javax.crypto.KeyAgreement; +import javax.crypto.SecretKey; +import javax.xml.parsers.ParserConfigurationException; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.InvocationTargetException; +import java.math.BigInteger; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.*; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.ECParameterSpec; +import java.util.*; +import java.util.stream.Collectors; + +/** + * Standalone part of ECTester, a tool for testing Elliptic curve implementations in software libraries. + * + * @author Jan Jancar johny@neuromancer.sk + * @version v0.3.3 + */ +public class ECTesterStandalone { + private ProviderECLibrary[] libs; + private Config cfg; + + private Options opts = new Options(); + private TreeParser optParser; + private TreeCommandLine cli; + public static final String VERSION = "v0.3.3"; + private static final String DESCRIPTION = "ECTesterStandalone " + VERSION + ", an Elliptic Curve Cryptography support tester/utility."; + private static final String LICENSE = "MIT Licensed\nCopyright © 2016-2019 Petr Svenda <petr@svenda.com>\nCopyright © 2016-2019 Jan Jancar <johny@neuromancer.sk>"; + private static final String CLI_HEADER = "\n" + DESCRIPTION + "\n\n"; + private static final String CLI_FOOTER = "\n" + LICENSE; + + public static String LIB_RESOURCE_DIR = "/cz/crcs/ectester/standalone/libs/jni/"; + + private void run(String[] args) { + try { + cli = parseArgs(args); + + if (cli.hasOption("version")) { + CLITools.version(DESCRIPTION, LICENSE); + return; + } else if (cli.hasOption("help") || cli.getNext() == null) { + String command = cli.getOptionValue("help"); + if (command == null) { + CLITools.help("ECTesterStandalone.jar", CLI_HEADER, opts, optParser, CLI_FOOTER, true); + } else { + CLITools.help(CLI_HEADER, optParser, CLI_FOOTER, command); + } + return; + } + + Path reqs = FileUtil.getRequirementsDir(); + reqs.toFile().mkdirs(); + + if (!System.getProperty("os.name").startsWith("Windows")) { + FileUtil.writeNewer(LIB_RESOURCE_DIR + "lib_timing.so", reqs.resolve("lib_timing.so")); + System.load(reqs.resolve("lib_timing.so").toString()); + } + + List<ProviderECLibrary> 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 e) { + + } + } + libs = libObjects.toArray(new ProviderECLibrary[0]); + + //TODO: push this further down to only initialize if necessary. + // and only initialize the chosen lib (so give libs a name in Java only) + for (ECLibrary lib : libs) { + try { + lib.initialize(); + } catch (Exception ex) { + System.err.println(ex.getMessage()); + } + } + + cfg = new Config(libs); + if (!cfg.readOptions(cli)) { + return; + } + + if (cli.isNext("list-libs")) { + listLibraries(); + } else if (cli.isNext("list-data")) { + CLITools.listNamed(EC_Store.getInstance(), cli.getNext().getArg(0)); + } else if (cli.isNext("list-suites")) { + listSuites(); + } else if (cli.isNext("list-types")) { + listIdents(); + } else if (cli.isNext("ecdh")) { + ecdh(); + } else if (cli.isNext("ecdsa")) { + ecdsa(); + } else if (cli.isNext("generate")) { + generate(); + } else if (cli.isNext("test")) { + test(); + } else if (cli.isNext("export")) { + export(); + } + + } catch (ParseException | ParserConfigurationException | IOException ex) { + System.err.println(ex.getMessage()); + } catch (InvalidAlgorithmParameterException | InvalidParameterException e) { + System.err.println("Invalid algorithm parameter: " + e.getMessage()); + } catch (NoSuchAlgorithmException nsaex) { + System.err.println("Algorithm not supported by the selected library: " + nsaex.getMessage()); + } catch (InvalidKeyException | SignatureException e) { + e.printStackTrace(); + } + } + + private TreeCommandLine parseArgs(String[] args) throws ParseException { + Map<String, ParserOptions> actions = new TreeMap<>(); + + Option namedCurve = Option.builder("nc").longOpt("named-curve").desc("Use a named curve, from CurveDB: <cat/id>").hasArg().argName("cat/id").optionalArg(false).numberOfArgs(1).build(); + Option namedPublic = Option.builder("npub").longOpt("named-public").desc("Use a named public key, from CurveDB: <cat/id>").hasArg().argName("cat/id").optionalArg(false).numberOfArgs(1).build(); + Option filePublic = Option.builder("pub").longOpt("public").desc("Use a given public key from file.").hasArg().argName("pubkey").optionalArg(false).numberOfArgs(1).build(); + OptionGroup publicKey = new OptionGroup(); + publicKey.addOption(namedPublic); + publicKey.addOption(filePublic); + Option namedPrivate = Option.builder("npriv").longOpt("named-private").desc("Use a named private key, from CurveDB: <cat/id>").hasArg().argName("cat/id").optionalArg(false).numberOfArgs(1).build(); + Option filePrivate = Option.builder("priv").longOpt("private").desc("Use a given private key from file.").hasArg().argName("privkey").optionalArg(false).numberOfArgs(1).build(); + OptionGroup privateKey = new OptionGroup(); + privateKey.addOption(namedPrivate); + privateKey.addOption(filePrivate); + Option curveName = Option.builder("cn").longOpt("curve-name").desc("Use a named curve, search from curves supported by the library: <name>").hasArg().argName("name").optionalArg(false).numberOfArgs(1).build(); + 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>.").hasArgs().argName("output_file").optionalArg(false).numberOfArgs(1).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(); + testOpts.addOption(bits); + testOpts.addOption(namedCurve); + testOpts.addOption(curveName); + 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()); + testOpts.addOption(Option.builder("f").longOpt("format").desc("Set the output format, one of text,yaml,xml.").hasArg().argName("format").optionalArg(false).build()); + testOpts.addOption(Option.builder().longOpt("key-type").desc("Set the key [algorithm] for which the key should be derived in KeyAgreements with KDF. Default is \"AES\".").hasArg().argName("algorithm").optionalArg(false).build()); + List<Argument> testArgs = new LinkedList<>(); + testArgs.add(new Argument("test-suite", "The test suite to run.", true)); + ParserOptions test = new ParserOptions(new TreeParser(Collections.emptyMap(), true, testArgs), testOpts, "Test a library."); + actions.put("test", test); + + Options ecdhOpts = new Options(); + ecdhOpts.addOption(bits); + ecdhOpts.addOption(namedCurve); + ecdhOpts.addOption(curveName); + ecdhOpts.addOption(output); + ecdhOpts.addOption(timeSource); + ecdhOpts.addOption(Option.builder("t").longOpt("type").desc("Set KeyAgreement object [type].").hasArg().argName("type").optionalArg(false).build()); + ecdhOpts.addOption(Option.builder().longOpt("key-type").desc("Set the key [algorithm] for which the key should be derived in KeyAgreements with KDF. Default is \"AES\".").hasArg().argName("algorithm").optionalArg(false).build()); + ecdhOpts.addOption(Option.builder("n").longOpt("amount").hasArg().argName("amount").optionalArg(false).desc("Do ECDH [amount] times.").build()); + ecdhOpts.addOptionGroup(publicKey); + ecdhOpts.addOption(Option.builder().longOpt("fixed-private").desc("Perform ECDH with fixed private key.").build()); + ecdhOpts.addOptionGroup(privateKey); + ecdhOpts.addOption(Option.builder().longOpt("fixed-public").desc("Perform ECDH with fixed public key.").build()); + ParserOptions ecdh = new ParserOptions(new DefaultParser(), ecdhOpts, "Perform EC based KeyAgreement."); + actions.put("ecdh", ecdh); + + Options ecdsaOpts = new Options(); + ecdsaOpts.addOption(bits); + ecdsaOpts.addOption(namedCurve); + ecdsaOpts.addOption(curveName); + ecdsaOpts.addOption(output); + ecdsaOpts.addOption(timeSource); + ecdsaOpts.addOptionGroup(privateKey); + ecdsaOpts.addOptionGroup(publicKey); + ecdsaOpts.addOption(Option.builder().longOpt("fixed").desc("Perform all ECDSA with fixed keypair.").build()); + ecdsaOpts.addOption(Option.builder("t").longOpt("type").desc("Set Signature object [type].").hasArg().argName("type").optionalArg(false).build()); + ecdsaOpts.addOption(Option.builder("n").longOpt("amount").hasArg().argName("amount").optionalArg(false).desc("Do ECDSA [amount] times.").build()); + ecdsaOpts.addOption(Option.builder("f").longOpt("file").hasArg().argName("file").optionalArg(false).desc("Input [file] to sign.").build()); + ParserOptions ecdsa = new ParserOptions(new DefaultParser(), ecdsaOpts, "Perform EC based Signature."); + actions.put("ecdsa", ecdsa); + + Options generateOpts = new Options(); + generateOpts.addOption(bits); + generateOpts.addOption(namedCurve); + generateOpts.addOption(curveName); + generateOpts.addOption(output); + generateOpts.addOption(timeSource); + generateOpts.addOption(Option.builder("n").longOpt("amount").hasArg().argName("amount").optionalArg(false).desc("Generate [amount] of EC keys.").build()); + generateOpts.addOption(Option.builder("t").longOpt("type").hasArg().argName("type").optionalArg(false).desc("Set KeyPairGenerator object [type].").build()); + ParserOptions generate = new ParserOptions(new DefaultParser(), generateOpts, "Generate EC keypairs."); + actions.put("generate", generate); + + Options exportOpts = new Options(); + exportOpts.addOption(bits); + exportOpts.addOption(output); + exportOpts.addOption(Option.builder("t").longOpt("type").hasArg().argName("type").optionalArg(false).desc("Set KeyPair object [type].").build()); + ParserOptions export = new ParserOptions(new DefaultParser(), exportOpts, "Export default curve parameters."); + actions.put("export", export); + + Options listDataOpts = new Options(); + List<Argument> listDataArgs = new LinkedList<>(); + listDataArgs.add(new Argument("what", "what to list.", false)); + ParserOptions listData = new ParserOptions(new TreeParser(Collections.emptyMap(), false, listDataArgs), listDataOpts, "List/show contained EC domain parameters/keys."); + actions.put("list-data", listData); + + Options listLibsOpts = new Options(); + ParserOptions listLibs = new ParserOptions(new DefaultParser(), listLibsOpts, "List supported libraries."); + actions.put("list-libs", listLibs); + + Options listSuitesOpts = new Options(); + ParserOptions listSuites = new ParserOptions(new DefaultParser(), listSuitesOpts, "List supported test suites."); + actions.put("list-suites", listSuites); + + Options listIdentsOpts = new Options(); + ParserOptions listIdents = new ParserOptions(new DefaultParser(), listIdentsOpts, "List KeyPairGenerator, KeyAgreement and Signature types."); + actions.put("list-types", listIdents); + + List<Argument> baseArgs = new LinkedList<>(); + baseArgs.add(new Argument("lib", "What library to use.", false)); + optParser = new TreeParser(actions, false, baseArgs); + + opts.addOption(Option.builder("V").longOpt("version").desc("Print version info.").build()); + opts.addOption(Option.builder("h").longOpt("help").desc("Print help(about <command>).").hasArg().argName("command").optionalArg(true).build()); + opts.addOption(Option.builder("C").longOpt("color").desc("Print stuff with color, requires ANSI terminal.").build()); + + return optParser.parse(opts, args); + } + + /** + * + */ + private void listLibraries() { + for (ProviderECLibrary lib : libs) { + if (lib.isInitialized() && (cfg.selected == null || lib == cfg.selected)) { + System.out.println("\t- " + Colors.bold(lib.name())); + System.out.println(Colors.bold("\t\t- Version: ") + String.format("%f", lib.getProvider().getVersion())); + System.out.println(Colors.bold("\t\t- Supports native timing: ") + lib.getNativeTimingSupport().toString()); + Set<KeyPairGeneratorIdent> kpgs = lib.getKPGs(); + if (!kpgs.isEmpty()) { + System.out.println(Colors.bold("\t\t- KeyPairGenerators: ") + String.join(", ", kpgs.stream().map(KeyPairGeneratorIdent::getName).collect(Collectors.toList()))); + } + Set<KeyAgreementIdent> eckas = lib.getKAs(); + if (!eckas.isEmpty()) { + System.out.println(Colors.bold("\t\t- KeyAgreements: ") + String.join(", ", eckas.stream().map(KeyAgreementIdent::getName).collect(Collectors.toList()))); + } + Set<SignatureIdent> sigs = lib.getSigs(); + if (!sigs.isEmpty()) { + System.out.println(Colors.bold("\t\t- Signatures: ") + String.join(", ", sigs.stream().map(SignatureIdent::getName).collect(Collectors.toList()))); + } + Set<String> curves = lib.getCurves(); + if (!curves.isEmpty()) { + System.out.println(Colors.bold("\t\t- Curves: ") + String.join(", ", curves)); + } + System.out.println(); + } + } + } + + /** + * + */ + private void listSuites() { + StandaloneTestSuite[] suites = new StandaloneTestSuite[]{ + new StandaloneDefaultSuite(null, null, null), + new StandaloneTestVectorSuite(null, null, null), + new StandaloneInvalidSuite(null, null, null), + new StandaloneWrongSuite(null, null, null), + new StandaloneDegenerateSuite(null, null, null), + new StandaloneCofactorSuite(null, null, null), + new StandaloneEdgeCasesSuite(null, null, null), + new StandaloneSignatureSuite(null, null, null), + new StandaloneCompositeSuite(null, null, null), + new StandaloneTwistSuite(null, null, null), + new StandaloneMiscSuite(null, null, null), + new StandalonePerformanceSuite(null, null, null)}; + for (StandaloneTestSuite suite : suites) { + System.out.println(" - " + suite.getName()); + for (String line : suite.getDescription()) { + System.out.println("\t" + line); + } + } + } + + /** + * + */ + private void listIdents() { + System.out.println(Colors.bold("\t- KeyPairGenerator")); + for (KeyPairGeneratorIdent kpgIdent : KeyPairGeneratorIdent.list()) { + System.out.println("\t\t- " + Colors.underline(kpgIdent.getName()) + " " + kpgIdent.toString()); + } + System.out.println(Colors.bold("\t- KeyAgreement")); + for (KeyAgreementIdent kaIdent : KeyAgreementIdent.list()) { + System.out.println("\t\t- " + Colors.underline(kaIdent.getName()) + " " + kaIdent.toString()); + } + System.out.println(Colors.bold("\t- Signature")); + for (SignatureIdent sigIdent : SignatureIdent.list()) { + System.out.println("\t\t- " + Colors.underline(sigIdent.getName()) + " " + sigIdent.toString()); + } + } + + /** + * + */ + private void ecdh() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IOException { + ProviderECLibrary lib = cfg.selected; + + String algo = cli.getOptionValue("ecdh.type", "ECDH"); + String keyAlgo = cli.getOptionValue("ecdh.key-type", "AES"); + KeyAgreementIdent kaIdent = lib.getKAs().stream() + .filter((ident) -> ident.contains(algo)) + .findFirst() + .orElse(null); + + String baseAlgo; + if (algo.contains("with")) { + baseAlgo = algo.split("with")[0]; + } else { + baseAlgo = algo; + } + + KeyPairGeneratorIdent kpIdent = lib.getKPGs().stream() + .filter((ident) -> ident.contains(algo)) + .findFirst() + .orElse(lib.getKPGs().stream() + .filter((ident) -> ident.contains(baseAlgo)) + .findFirst() + .orElse(lib.getKPGs().stream() + .filter((ident) -> ident.contains("ECDH")) + .findFirst() + .orElse(lib.getKPGs().stream() + .filter((ident) -> ident.contains("EC")) + .findFirst() + .orElse(null)))); + + if (kaIdent == null || kpIdent == null) { + throw new NoSuchAlgorithmException(algo); + } + + KeyAgreement ka = kaIdent.getInstance(lib.getProvider()); + KeyPairGenerator kpg = kpIdent.getInstance(lib.getProvider()); + AlgorithmParameterSpec spec = null; + if (cli.hasOption("ecdh.bits")) { + int bits = Integer.parseInt(cli.getOptionValue("ecdh.bits")); + kpg.initialize(bits); + } else if (cli.hasOption("ecdh.named-curve")) { + String curveName = cli.getOptionValue("ecdh.named-curve"); + EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, curveName); + if (curve == null) { + System.err.println("Curve not found: " + curveName); + return; + } + spec = curve.toSpec(); + kpg.initialize(spec); + } else if (cli.hasOption("ecdh.curve-name")) { + String curveName = cli.getOptionValue("ecdh.curve-name"); + spec = new ECGenParameterSpec(curveName); + kpg.initialize(spec); + } + + if (cli.hasOption("ecdh.time-source")) { + if (!lib.setNativeTimingType(cli.getOptionValue("ecdh.time-source"))) { + System.err.println("Couldn't set native time source."); + return; + } + } + + PrintStream out; + if (cli.hasOption("ecdh.output")) { + out = new PrintStream(FileUtil.openStream(cli.getOptionValues("ecdh.output"))); + } else { + out = System.out; + } + + String timeUnit = "nano"; + if (!lib.getNativeTimingSupport().isEmpty()) { + timeUnit = lib.getNativeTimingUnit(); + } + + String hashAlgo = kaIdent.getBaseAlgo() != null ? String.format("[%s]", kaIdent.getBaseAlgo()) : "[NONE]"; + out.println(String.format("index;time[%s];pubW;privS;secret%s", timeUnit, hashAlgo)); + + KeyPair one = null; + if (cli.hasOption("ecdh.fixed-private") && !cli.hasOption("ecdh.named-private") && !cli.hasOption("ecdh.private")) { + one = kpg.genKeyPair(); + } + KeyPair other = null; + if (cli.hasOption("ecdh.fixed-public") && !cli.hasOption("ecdh.named-public") && !cli.hasOption("ecdh.public")) { + other = kpg.genKeyPair(); + } + + ECPrivateKey privkey = (ECPrivateKey) ECUtil.loadKey(EC_Consts.PARAMETER_S, cli.getOptionValue("ecdh.named-private"), cli.getOptionValue("ecdh.private"), spec); + ECPublicKey pubkey = (ECPublicKey) ECUtil.loadKey(EC_Consts.PARAMETER_W, cli.getOptionValue("ecdh.named-public"), cli.getOptionValue("ecdh.public"), spec); + + int amount = Integer.parseInt(cli.getOptionValue("ecdh.amount", "1")); + for (int i = 0; i < amount || amount == 0; ++i) { + if (!cli.hasOption("ecdh.fixed-private") && !cli.hasOption("ecdh.named-private") && !cli.hasOption("ecdh.private")) { + one = kpg.genKeyPair(); + } + if (!cli.hasOption("ecdh.fixed-public") && !cli.hasOption("ecdh.named-public") && !cli.hasOption("ecdh.public")) { + other = kpg.genKeyPair(); + } + + if (!cli.hasOption("ecdh.named-private") && !cli.hasOption("ecdh.private")) { + privkey = (ECPrivateKey) one.getPrivate(); + } + + if (!cli.hasOption("ecdh.named-public") && !cli.hasOption("ecdh.public")) { + pubkey = (ECPublicKey) other.getPublic(); + } + + long elapsed = -System.nanoTime(); + if (spec instanceof ECParameterSpec && lib instanceof NativeECLibrary) { + ka.init(privkey, spec); + } else { + ka.init(privkey); + } + ka.doPhase(pubkey, true); + elapsed += System.nanoTime(); + SecretKey derived; + byte[] result; + elapsed -= System.nanoTime(); + if (kaIdent.requiresKeyAlgo()) { + derived = ka.generateSecret(keyAlgo); + result = derived.getEncoded(); + } else { + result = ka.generateSecret(); + } + elapsed += System.nanoTime(); + if (!lib.getNativeTimingSupport().isEmpty()) { + elapsed = lib.getLastNativeTiming(); + } + ka = kaIdent.getInstance(lib.getProvider()); + + String pub = ByteUtil.bytesToHex(ECUtil.toX962Uncompressed(pubkey.getW(), pubkey.getParams()), false); + String priv = ByteUtil.bytesToHex(privkey.getS().toByteArray(), false); + String dh = ByteUtil.bytesToHex(result, false); + out.println(String.format("%d;%d;%s;%s;%s", i, elapsed, pub, priv, dh)); + } + + if (cli.hasOption("ecdh.output")) { + out.close(); + } + } + + /** + * + */ + private void ecdsa() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IOException, SignatureException { + byte[] data; + String dataString; + if (cli.hasOption("ecdsa.file")) { + String fileName = cli.getOptionValue("ecdsa.file"); + File in = new File(fileName); + long len = in.length(); + if (len == 0) { + throw new FileNotFoundException(fileName); + } + data = Files.readAllBytes(in.toPath()); + dataString = ""; + } else { + Random random = new Random(); + data = new byte[32]; + random.nextBytes(data); + dataString = ByteUtil.bytesToHex(data, false); + } + ProviderECLibrary lib = cfg.selected; + String algo = cli.getOptionValue("ecdsa.type", "ECDSA"); + SignatureIdent sigIdent = lib.getSigs().stream() + .filter((ident) -> ident.contains(algo)) + .findFirst() + .orElse(null); + + String baseAlgo; + if (algo.contains("with")) { + baseAlgo = algo.split("with")[1]; + } else { + baseAlgo = algo; + } + KeyPairGeneratorIdent kpIdent = lib.getKPGs().stream() + .filter((ident) -> ident.contains(algo)) + .findFirst() + .orElse(lib.getKPGs().stream() + .filter((ident) -> ident.contains(baseAlgo)) + .findFirst() + .orElse(lib.getKPGs().stream() + .filter((ident) -> ident.contains("ECDSA")) + .findFirst() + .orElse(lib.getKPGs().stream() + .filter((ident) -> ident.contains("EC")) + .findFirst() + .orElse(null)))); + + if (sigIdent == null || kpIdent == null) { + throw new NoSuchAlgorithmException(algo); + } + Signature sig = sigIdent.getInstance(lib.getProvider()); + KeyPairGenerator kpg = kpIdent.getInstance(lib.getProvider()); + ECParameterSpec spec = null; + if (cli.hasOption("ecdsa.bits")) { + int bits = Integer.parseInt(cli.getOptionValue("ecdsa.bits")); + kpg.initialize(bits); + } else if (cli.hasOption("ecdsa.named-curve")) { + String curveName = cli.getOptionValue("ecdsa.named-curve"); + EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, curveName); + if (curve == null) { + System.err.println("Curve not found: " + curveName); + return; + } + spec = curve.toSpec(); + kpg.initialize(spec); + } else if (cli.hasOption("ecdsa.curve-name")) { + String curveName = cli.getOptionValue("ecdsa.curve-name"); + kpg.initialize(new ECGenParameterSpec(curveName)); + } + + if (cli.hasOption("ecdsa.time-source")) { + if (!lib.setNativeTimingType(cli.getOptionValue("ecdsa.time-source"))) { + System.err.println("Couldn't set native time source."); + return; + } + } + + PrintStream out; + if (cli.hasOption("ecdsa.output")) { + out = new PrintStream(FileUtil.openStream(cli.getOptionValues("ecdsa.output"))); + } else { + out = System.out; + } + + String timeUnit = "nano"; + if (!lib.getNativeTimingSupport().isEmpty()) { + timeUnit = lib.getNativeTimingUnit(); + } + + String hashAlgoOut = sigIdent.getHashAlgo() != null ? String.format("[%s]", sigIdent.getHashAlgo()) : ""; + out.println(String.format("index;signTime[%s];verifyTime[%s];data;pubW;privS;signature%s;nonce;verified", timeUnit, timeUnit, hashAlgoOut)); + + ECPrivateKey privkey = (ECPrivateKey) ECUtil.loadKey(EC_Consts.PARAMETER_S, cli.getOptionValue("ecdsa.named-private"), cli.getOptionValue("ecdsa.private"), spec); + ECPublicKey pubkey = (ECPublicKey) ECUtil.loadKey(EC_Consts.PARAMETER_W, cli.getOptionValue("ecdsa.named-public"), cli.getOptionValue("ecdsa.public"), spec); + + KeyPair one; + if (cli.hasOption("ecdsa.fixed")) { + one = kpg.genKeyPair(); + if (!cli.hasOption("ecdsa.named-private")) { + privkey = (ECPrivateKey) one.getPrivate(); + } + if (!cli.hasOption("ecdsa.named-public")) { + pubkey = (ECPublicKey) one.getPublic(); + } + } + + + int amount = Integer.parseInt(cli.getOptionValue("ecdsa.amount", "1")); + for (int i = 0; i < amount || amount == 0; ++i) { + if ((!cli.hasOption("ecdsa.named-private") || !cli.hasOption("ecdsa.named-public")) && !cli.hasOption("ecdsa.fixed")) { + one = kpg.genKeyPair(); + + if (!cli.hasOption("ecdsa.named-private")) { + privkey = (ECPrivateKey) one.getPrivate(); + } + if (!cli.hasOption("ecdsa.named-public")) { + pubkey = (ECPublicKey) one.getPublic(); + } + } + + sig.initSign(privkey); + sig.update(data); + + long signTime = -System.nanoTime(); + byte[] signature = sig.sign(); + signTime += System.nanoTime(); + if (!lib.getNativeTimingSupport().isEmpty()) { + signTime = lib.getLastNativeTiming(); + } + + sig.initVerify(pubkey); + sig.update(data); + + long verifyTime = -System.nanoTime(); + boolean verified = sig.verify(signature); + verifyTime += System.nanoTime(); + if (!lib.getNativeTimingSupport().isEmpty()) { + verifyTime = lib.getLastNativeTiming(); + } + + String pub = ByteUtil.bytesToHex(ECUtil.toX962Uncompressed(pubkey.getW(), pubkey.getParams()), false); + String priv = ByteUtil.bytesToHex(privkey.getS().toByteArray(), false); + String sign = ByteUtil.bytesToHex(signature, false); + String k = ""; + ECParameterSpec kSpec = spec; + if (kSpec == null) { + kSpec = privkey.getParams(); + } + if (kSpec != null) { + // Parse the types out of SignatureIdent. + String hashAlgo = sigIdent.getHashAlgo(); + String sigType = sigIdent.getSigType(); + if (sigType == null) { + sigType = sigIdent.toString(); + } + + BigInteger kValue = ECUtil.recoverSignatureNonce(signature, data, privkey.getS(), kSpec, hashAlgo, sigType); + if (kValue != null) { + k = ByteUtil.bytesToHex(kValue.toByteArray(), false); + } + } + out.println(String.format("%d;%d;%d;%s;%s;%s;%s;%s;%d", i, signTime, verifyTime, dataString, pub, priv, sign, k, verified ? 1 : 0)); + } + + if (cli.hasOption("ecdsa.output")) { + out.close(); + } + } + + /** + * + */ + private void generate() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, FileNotFoundException { + ProviderECLibrary lib = cfg.selected; + KeyPairGeneratorIdent ident = null; + String algo = cli.getOptionValue("generate.type", "EC"); + for (KeyPairGeneratorIdent kpIdent : lib.getKPGs()) { + if (kpIdent.contains(algo)) { + ident = kpIdent; + break; + } + } + if (ident == null) { + throw new NoSuchAlgorithmException(algo); + } + KeyPairGenerator kpg = ident.getInstance(lib.getProvider()); + if (cli.hasOption("generate.bits")) { + int bits = Integer.parseInt(cli.getOptionValue("generate.bits")); + kpg.initialize(bits); + } else if (cli.hasOption("generate.named-curve")) { + String curveName = cli.getOptionValue("generate.named-curve"); + EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, curveName); + if (curve == null) { + System.err.println("Curve not found: " + curveName); + return; + } + kpg.initialize(curve.toSpec()); + } else if (cli.hasOption("generate.curve-name")) { + String curveName = cli.getOptionValue("generate.curve-name"); + kpg.initialize(new ECGenParameterSpec(curveName)); + } + + if (cli.hasOption("generate.time-source")) { + if (!lib.setNativeTimingType(cli.getOptionValue("generate.time-source"))) { + System.err.println("Couldn't set native time source."); + return; + } + } + + String timeUnit = "nano"; + if (!lib.getNativeTimingSupport().isEmpty()) { + timeUnit = lib.getNativeTimingUnit(); + } + + PrintStream out; + if (cli.hasOption("generate.output")) { + out = new PrintStream(FileUtil.openStream(cli.getOptionValues("generate.output"))); + } else { + out = System.out; + } + + out.println(String.format("index;time[%s];pubW;privS", timeUnit)); + + int amount = Integer.parseInt(cli.getOptionValue("generate.amount", "1")); + for (int i = 0; i < amount || amount == 0; ++i) { + long elapsed = -System.nanoTime(); + KeyPair kp = kpg.genKeyPair(); + elapsed += System.nanoTime(); + if (!lib.getNativeTimingSupport().isEmpty()) { + elapsed = lib.getLastNativeTiming(); + } + ECPublicKey publicKey = (ECPublicKey) kp.getPublic(); + ECPrivateKey privateKey = (ECPrivateKey) kp.getPrivate(); + + String pub = ByteUtil.bytesToHex(ECUtil.toX962Uncompressed(publicKey.getW(), publicKey.getParams()), false); + String priv = ByteUtil.bytesToHex(privateKey.getS().toByteArray(), false); + out.println(String.format("%d;%d;%s;%s", i, elapsed, pub, priv)); + } + + if (cli.hasOption("generate.output")) { + out.close(); + } + } + + /** + * + */ + private void test() throws TestException, ParserConfigurationException { + TestWriter writer; + switch (cli.getOptionValue("test.format", "text").toLowerCase()) { + case "yaml": + case "yml": + writer = new YAMLTestWriter(System.out); + break; + case "xml": + writer = new XMLTestWriter(System.out); + break; + case "text": + default: + writer = new TextTestWriter(System.out); + break; + } + + StandaloneTestSuite suite; + + switch (cli.getArg(0).toLowerCase()) { + case "test-vectors": + suite = new StandaloneTestVectorSuite(writer, cfg, cli); + break; + case "wrong": + suite = new StandaloneWrongSuite(writer, cfg, cli); + break; + case "degenerate": + suite = new StandaloneDegenerateSuite(writer, cfg, cli); + break; + case "cofactor": + suite = new StandaloneCofactorSuite(writer, cfg, cli); + break; + case "composite": + suite = new StandaloneCompositeSuite(writer, cfg, cli); + break; + case "invalid": + suite = new StandaloneInvalidSuite(writer, cfg, cli); + break; + case "edge-cases": + suite = new StandaloneEdgeCasesSuite(writer, cfg, cli); + break; + case "signature": + suite = new StandaloneSignatureSuite(writer, cfg, cli); + break; + case "twist": + suite = new StandaloneTwistSuite(writer, cfg, cli); + break; + case "miscellaneous": + suite = new StandaloneMiscSuite(writer, cfg, cli); + break; + case "performance": + suite = new StandalonePerformanceSuite(writer, cfg, cli); + break; + case "default": + default: + suite = new StandaloneDefaultSuite(writer, cfg, cli); + } + + suite.run(); + } + + /** + * + */ + private void export() throws NoSuchAlgorithmException, IOException { + ProviderECLibrary lib = cfg.selected; + KeyPairGeneratorIdent ident = null; + String algo = cli.getOptionValue("export.type", "EC"); + for (KeyPairGeneratorIdent kpIdent : lib.getKPGs()) { + if (kpIdent.contains(algo)) { + ident = kpIdent; + break; + } + } + if (ident == null) { + throw new NoSuchAlgorithmException(algo); + } + KeyPairGenerator kpg = ident.getInstance(lib.getProvider()); + if (cli.hasOption("export.bits")) { + int bits = Integer.parseInt(cli.getOptionValue("export.bits")); + kpg.initialize(bits); + } + 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); + } + + public static void main(String[] args) { + ECTesterStandalone app = new ECTesterStandalone(); + app.run(args); + } + + + /** + * + */ + public static class Config { + private ProviderECLibrary[] libs; + public ProviderECLibrary selected = null; + public boolean color = false; + + public Config(ProviderECLibrary[] libs) { + this.libs = libs; + } + + boolean readOptions(TreeCommandLine cli) { + color = cli.hasOption("color"); + Colors.enabled = color; + + String next = cli.getNextName(); + + if (cli.isNext("generate") || cli.isNext("export") || cli.isNext("ecdh") || cli.isNext("ecdsa") || cli.isNext("test")) { + if (!cli.hasArg(-1)) { + System.err.println("Missing library name argument."); + return false; + } + + boolean hasBits = cli.hasOption(next + ".bits"); + boolean hasNamedCurve = cli.hasOption(next + ".named-curve"); + boolean hasCurveName = cli.hasOption(next + ".curve-name"); + if (hasBits ^ hasNamedCurve ? hasCurveName : hasBits) { + System.err.println("You can only specify bitsize or a named curve/curve name, nor both."); + return false; + } + + if (hasCurveName && (cli.hasOption(next + ".named-public") || cli.hasOption(next + ".named-private") || cli.hasOption(next + ".public") || cli.hasOption(next + ".private"))) { + System.err.println("Cannot specify key with a curve name switch, needs explicit parameteres."); + return false; + } + } + + if (!cli.isNext("list-data") && !cli.isNext("list-suites") && !cli.isNext("list-types")) { + String libraryName = cli.getArg(-1); + if (libraryName != null) { + List<ProviderECLibrary> matchedLibs = new LinkedList<>(); + for (ProviderECLibrary lib : libs) { + if (lib.isInitialized() && lib.name().toLowerCase().contains(libraryName.toLowerCase())) { + matchedLibs.add(lib); + } + } + if (matchedLibs.size() == 0) { + System.err.println("No library " + libraryName + " found."); + return false; + } else if (matchedLibs.size() > 1) { + System.err.println("Multiple matching libraries found: " + String.join(",", matchedLibs.stream().map(ECLibrary::name).collect(Collectors.toList()))); + return false; + } else { + selected = matchedLibs.get(0); + } + } + } + + if (cli.hasOption("test.format")) { + String fmt = cli.getOptionValue("test.format"); + String[] formats = new String[]{"text", "xml", "yaml", "yml"}; + if (!Arrays.asList(formats).contains(fmt.toLowerCase())) { + System.err.println("Invalid format specified."); + return false; + } + } + + if (cli.isNext("ecdh")) { + if ((cli.hasOption("ecdh.public") || cli.hasOption("ecdh.private")) && !cli.hasOption("ecdh.named-curve")) { + System.err.println("Need to specify a named curve when specifying public/private key in file."); + return false; + } + } + + if (cli.isNext("ecdsa")) { + if ((cli.hasOption("ecdsa.public") || cli.hasOption("ecdsa.private")) && !cli.hasOption("ecdsa.named-curve")) { + System.err.println("Need to specify a named curve when specifying public/private key in file."); + return false; + } + } + + if (cli.isNext("generate") || cli.isNext("ecdh") || cli.isNext("ecdsa")) { + if (cli.hasOption(next + ".time-source")) { + String source = cli.getOptionValue(next + ".time-source"); + if (!selected.getNativeTimingSupport().contains(source)) { + System.err.println(String.format("Time source %s unavailable for library %s.", source, selected.name())); + return false; + } + } + } + + return true; + } + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/consts/Ident.java b/standalone/src/main/java/cz/crcs/ectester/standalone/consts/Ident.java new file mode 100644 index 0000000..fcc811d --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/consts/Ident.java @@ -0,0 +1,88 @@ +package cz.crcs.ectester.standalone.consts; + +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.util.*; +import java.util.function.BiFunction; + +public abstract class Ident { + Set<String> idents; + String name; + + public Ident(String name, String... aliases) { + this.name = name; + this.idents = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); + this.idents.add(name); + this.idents.addAll(Arrays.asList(aliases)); + } + + public String getName() { + return name; + } + + public Set<String> getIdents() { + return Collections.unmodifiableSet(idents); + } + + public boolean contains(String other) { + return name.equals(other) || idents.contains(other); + } + + public boolean containsAny(List<String> others) { + for(String other : others) { + if(name.equals(other) || idents.contains(other)) { + return true; + } + } + return false; + } + + <T> T getInstance(BiFunction<String, Provider, T> getter, Provider provider) throws NoSuchAlgorithmException { + T instance = null; + try { + instance = getter.apply(name, provider); + } catch (Exception ignored) { + ignored.printStackTrace(); + } + + if (instance == null) { + for (String alias : idents) { + try { + instance = getter.apply(alias, provider); + if (instance != null) { + break; + } + } catch (Exception ignored) { + ignored.printStackTrace(); + } + } + } + + if (instance == null) { + throw new NoSuchAlgorithmException(name); + } + return instance; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof Ident)) { + return false; + } + Ident other = (Ident) obj; + return idents.equals(other.getIdents()); + } + + @Override + public int hashCode() { + return idents.hashCode() + 37; + } + + @Override + public String toString() { + return "(" + String.join(" | ", idents) + ")"; + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/consts/KeyAgreementIdent.java b/standalone/src/main/java/cz/crcs/ectester/standalone/consts/KeyAgreementIdent.java new file mode 100644 index 0000000..60c60e8 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/consts/KeyAgreementIdent.java @@ -0,0 +1,130 @@ +package cz.crcs.ectester.standalone.consts; + +import javax.crypto.KeyAgreement; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class KeyAgreementIdent extends Ident { + private boolean requiresKeyAlgo; + private String kdf; + private String algo; + + private static final List<KeyAgreementIdent> ALL = new LinkedList<>(); + + static { + //https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html + // Basic ECDH and ECDHC (plain/raw) + ALL.add(new KeyAgreementIdent("ECDH")); + ALL.add(new KeyAgreementIdent("ECDHC", "ECCDH")); + // ECDH and ECDHC with SHA as KDF, OIDs from RFC 3278 + ALL.add(new KeyAgreementIdent("ECDHwithSHA1KDF", true, "1.3.133.16.840.63.0.2")); + ALL.add(new KeyAgreementIdent("ECCDHwithSHA1KDF", true, "1.3.133.16.840.63.0.3")); + ALL.add(new KeyAgreementIdent("ECDHwithSHA224KDF", true, "1.3.132.1.11.0")); + ALL.add(new KeyAgreementIdent("ECCDHwithSHA224KDF", true, "1.3.132.1.14.0")); + ALL.add(new KeyAgreementIdent("ECDHwithSHA256KDF", true, "1.3.132.1.11.1")); + ALL.add(new KeyAgreementIdent("ECCDHwithSHA256KDF", true, "1.3.132.1.14.1")); + ALL.add(new KeyAgreementIdent("ECDHwithSHA384KDF", true, "1.3.132.1.11.2")); + ALL.add(new KeyAgreementIdent("ECCDHwithSHA384KDF", true, "1.3.132.1.14.2")); + ALL.add(new KeyAgreementIdent("ECDHwithSHA512KDF", true, "1.3.132.1.11.3")); + ALL.add(new KeyAgreementIdent("ECCDHwithSHA512KDF", true, "1.3.132.1.14.3")); + // Microsoft specific KDF + ALL.add(new KeyAgreementIdent("ECDHwithSHA1KDF(CNG)")); + ALL.add(new KeyAgreementIdent("ECDHwithSHA256KDF(CNG)")); + ALL.add(new KeyAgreementIdent("ECDHwithSHA384KDF(CNG)")); + ALL.add(new KeyAgreementIdent("ECDHwithSHA512KDF(CNG)")); + // CKDF requires custom AlgorithmParameterSpec (only BouncyCastle) + //ALL.add(new KeyAgreementIdent("ECDHwithSHA1CKDF", true)); + //ALL.add(new KeyAgreementIdent("ECCDHwithSHA1CKDF", true)); + //ALL.add(new KeyAgreementIdent("ECDHwithSHA256CKDF", true)); + //ALL.add(new KeyAgreementIdent("ECCDHwithSHA256CKDF", true)); + //ALL.add(new KeyAgreementIdent("ECDHwithSHA384CKDF", true)); + //ALL.add(new KeyAgreementIdent("ECCDHwithSHA384CKDF", true)); + //ALL.add(new KeyAgreementIdent("ECDHwithSHA512CKDF", true)); + //ALL.add(new KeyAgreementIdent("ECCDHwithSHA512CKDF", true)); + // ECMQV - Disable for now as it needs diferent params(too different from DH) + //ALL.add(new KeyAgreementIdent("ECMQV")); + //ALL.add(new KeyAgreementIdent("ECMQVwithSHA1KDF", true)); + //ALL.add(new KeyAgreementIdent("ECMQVwithSHA224KDF", true)); + //ALL.add(new KeyAgreementIdent("ECMQVwithSHA256KDF", true)); + //ALL.add(new KeyAgreementIdent("ECMQVwithSHA354KDF", true)); + //ALL.add(new KeyAgreementIdent("ECMQVwithSHA512KDF", true)); + //ALL.add(new KeyAgreementIdent("ECMQVwithSHA1CKDF", true, "1.3.133.16.840.63.0.16")); + //ALL.add(new KeyAgreementIdent("ECMQVwithSHA224CKDF", true, "1.3.132.1.15.0")); + //ALL.add(new KeyAgreementIdent("ECMQVwithSHA256CKDF", true, "1.3.132.1.15.1")); + //ALL.add(new KeyAgreementIdent("ECMQVwithSHA384CKDF", true, "1.3.132.1.15.2")); + //ALL.add(new KeyAgreementIdent("ECMQVwithSHA512CKDF", true, "1.3.132.1.15.3")); + // ECVKO - Disable for now as it needs diferent params(too different from DH) + //ALL.add(new KeyAgreementIdent("ECVKO", "ECGOST3410", "1.2.643.2.2.19", "GOST-3410-2001", "1.2.643.2.2.96")); + //ALL.add(new KeyAgreementIdent("ECVKO256", "ECGOST3410-2012-256", "1.2.643.7.1.1.6.1", "1.2.643.7.1.1.1.1")); + //ALL.add(new KeyAgreementIdent("ECVKO512", "ECGOST3410-2012-512", "1.2.643.7.1.1.6.2", "1.2.643.7.1.1.1.2")); + } + + public static KeyAgreementIdent get(String ident) { + for (KeyAgreementIdent ka : ALL) { + if (ka.getIdents().contains(ident)) { + return ka; + } + } + return null; + } + + public static List<KeyAgreementIdent> list() { + return Collections.unmodifiableList(ALL); + } + + private KeyAgreementIdent(String name, String... aliases) { + super(name, aliases); + if (name.contains("with")) { + int split = name.indexOf("with"); + this.algo = name.substring(0, split); + this.kdf = name.substring(split + 4); + } else { + for (String alias : aliases) { + if (alias.contains("with")) { + int split = alias.indexOf("with"); + this.algo = alias.substring(0, split); + this.kdf = alias.substring(split + 4); + break; + } + } + if (this.algo == null) { + this.algo = name; + } + } + } + + private KeyAgreementIdent(String name, boolean requiresKeyAlgo, String... aliases) { + this(name, aliases); + this.requiresKeyAlgo = requiresKeyAlgo; + } + + public boolean requiresKeyAlgo() { + return requiresKeyAlgo; + } + + public String getKdfAlgo() { + return kdf; + } + + public String getBaseAlgo() { + return algo; + } + + public KeyAgreement getInstance(Provider provider) throws NoSuchAlgorithmException { + KeyAgreement instance = getInstance((algorithm, provider1) -> { + try { + return KeyAgreement.getInstance(algorithm, provider1); + } catch (NoSuchAlgorithmException e) { + return null; + } + }, provider); + instance.getProvider(); + return instance; + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/consts/KeyPairGeneratorIdent.java b/standalone/src/main/java/cz/crcs/ectester/standalone/consts/KeyPairGeneratorIdent.java new file mode 100644 index 0000000..83eef75 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/consts/KeyPairGeneratorIdent.java @@ -0,0 +1,55 @@ +package cz.crcs.ectester.standalone.consts; + +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +public class KeyPairGeneratorIdent extends Ident { + private static final List<KeyPairGeneratorIdent> ALL = new LinkedList<>(); + + static { + ALL.add(new KeyPairGeneratorIdent("EC")); + ALL.add(new KeyPairGeneratorIdent("ECDH")); + ALL.add(new KeyPairGeneratorIdent("ECDSA")); + ALL.add(new KeyPairGeneratorIdent("ECDHC")); + ALL.add(new KeyPairGeneratorIdent("ECMQV")); + //ALL.add(new KeyPairGeneratorIdent("ECGOST3410")); + //ALL.add(new KeyPairGeneratorIdent("ECGOST3410-2012")); + // ECKCDSA? Botan provides. + ALL.add(new KeyPairGeneratorIdent("ECKCDSA")); + // ECGDSA? Botan provides. + ALL.add(new KeyPairGeneratorIdent("ECGDSA")); + } + + public static KeyPairGeneratorIdent get(String ident) { + for (KeyPairGeneratorIdent kg : ALL) { + if (kg.getIdents().contains(ident)) { + return kg; + } + } + return null; + } + + public static List<KeyPairGeneratorIdent> list() { + return Collections.unmodifiableList(ALL); + } + + public KeyPairGeneratorIdent(String name, String... aliases) { + super(name, aliases); + } + + public KeyPairGenerator getInstance(Provider provider) throws NoSuchAlgorithmException { + KeyPairGenerator instance = getInstance((algorithm, provider1) -> { + try { + return KeyPairGenerator.getInstance(algorithm, provider1); + } catch (NoSuchAlgorithmException e) { + return null; + } + }, provider); + instance.getProvider(); + return instance; + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/consts/SignatureIdent.java b/standalone/src/main/java/cz/crcs/ectester/standalone/consts/SignatureIdent.java new file mode 100644 index 0000000..c3913b7 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/consts/SignatureIdent.java @@ -0,0 +1,141 @@ +package cz.crcs.ectester.standalone.consts; + +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.Signature; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class SignatureIdent extends Ident { + private String hash; + private String sig; + + private static final List<SignatureIdent> ALL = new LinkedList<>(); + + static { + //https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html + // ECDSA + ALL.add(new SignatureIdent("ECDSA", "SHA1withECDSA", "ECDSAwithSHA1", "1.2.840.10045.4.1", "1.3.36.3.3.2.1")); + ALL.add(new SignatureIdent("NONEwithECDSA")); + ALL.add(new SignatureIdent("SHA224withECDSA", "SHA224/ECDSA", "1.2.840.10045.4.3.1")); + ALL.add(new SignatureIdent("SHA256withECDSA", "SHA256/ECDSA", "1.2.840.10045.4.3.2")); + ALL.add(new SignatureIdent("SHA384withECDSA", "SHA384/ECDSA", "1.2.840.10045.4.3.3")); + ALL.add(new SignatureIdent("SHA512withECDSA", "SHA512/ECDSA", "1.2.840.10045.4.3.4")); + ALL.add(new SignatureIdent("SHA3-224withECDSA", "SHA3-224/ECDSA", "2.16.840.1.101.3.4.3.9")); + ALL.add(new SignatureIdent("SHA3-256withECDSA", "SHA3-256/ECDSA", "2.16.840.1.101.3.4.3.10")); + ALL.add(new SignatureIdent("SHA3-384withECDSA", "SHA3-384/ECDSA", "2.16.840.1.101.3.4.3.11")); + ALL.add(new SignatureIdent("SHA3-512withECDSA", "SHA3-512/ECDSA", "2.16.840.1.101.3.4.3.12")); + ALL.add(new SignatureIdent("RIPEMD160withECDSA", "RIPEMD160/ECDSA", "1.3.36.3.3.2.2")); + // ECNR + ALL.add(new SignatureIdent("SHA1withECNR")); + ALL.add(new SignatureIdent("SHA224withECNR")); + ALL.add(new SignatureIdent("SHA256withECNR")); + ALL.add(new SignatureIdent("SHA512withECNR")); + // CVC-ECDSA + ALL.add(new SignatureIdent("SHA1withCVC-ECDSA", "SHA1/CVC-ECDSA", "0.4.0.127.0.7.2.2.2.2.1")); + ALL.add(new SignatureIdent("SHA224withCVC-ECDSA", "SHA224/CVC-ECDSA", "0.4.0.127.0.7.2.2.2.2.2")); + ALL.add(new SignatureIdent("SHA256withCVC-ECDSA", "SHA256/CVC-ECDSA", "0.4.0.127.0.7.2.2.2.2.3")); + ALL.add(new SignatureIdent("SHA384withCVC-ECDSA", "SHA384/CVC-ECDSA", "0.4.0.127.0.7.2.2.2.2.4")); + ALL.add(new SignatureIdent("SHA512withCVC-ECDSA", "SHA512/CVC-ECDSA", "0.4.0.127.0.7.2.2.2.2.5")); + // PLAIN-ECDSA + ALL.add(new SignatureIdent("SHA1withPLAIN-ECDSA", "SHA1/PLAIN-ECDSA", "0.4.0.127.0.7.1.1.4.1.1")); + ALL.add(new SignatureIdent("SHA224withPLAIN-ECDSA", "SHA224/PLAIN-ECDSA", "0.4.0.127.0.7.1.1.4.1.2")); + ALL.add(new SignatureIdent("SHA256withPLAIN-ECDSA", "SHA256/PLAIN-ECDSA", "0.4.0.127.0.7.1.1.4.1.3")); + ALL.add(new SignatureIdent("SHA384withPLAIN-ECDSA", "SHA384/PLAIN-ECDSA", "0.4.0.127.0.7.1.1.4.1.4")); + ALL.add(new SignatureIdent("SHA512withPLAIN-ECDSA", "SHA512/PLAIN-ECDSA", "0.4.0.127.0.7.1.1.4.1.5")); + ALL.add(new SignatureIdent("RIPEMD160withPLAIN-ECDSA", "RIPEMD160/PLAIN-ECDSA", "0.4.0.127.0.7.1.1.4.1.6")); + // ECGOST + ALL.add(new SignatureIdent("ECGOST3410", "ECGOST-3410", "GOST-3410-2001")); + ALL.add(new SignatureIdent("GOST3411withECGOST3410", "GOST3411/ECGOST3410", "1.2.643.2.2.3")); + ALL.add(new SignatureIdent("ECGOST3410-2012-256", "GOST-3410-2012-256")); + ALL.add(new SignatureIdent("GOST3411-2012-256withECGOST3410-2012-256", "GOST3411-2012-256/ECGOST3410-2012-2560", "1.2.643.7.1.1.3.2")); + ALL.add(new SignatureIdent("ECGOST3410-2012-512", "GOST-3410-2012-512")); + ALL.add(new SignatureIdent("GOST3411-2012-512withECGOST3410-2012-512", "GOST3411-2012-512/ECGOST3410-2012-5120", "1.2.643.7.1.1.3.3")); + ALL.add(new SignatureIdent("SM3withSM2")); + // ECDDSA (rfc6979?) + ALL.add(new SignatureIdent("ECDDSA", "SHA1withECDDSA", "SHA1withDETECDSA", "DETECDSA", "ECDETDSA")); + ALL.add(new SignatureIdent("SHA224withECDDSA", "SHA224withDETECDSA")); + ALL.add(new SignatureIdent("SHA256withECDDSA", "SHA256withDETECDSA")); + ALL.add(new SignatureIdent("SHA384withECDDSA", "SHA384withDETECDSA")); + ALL.add(new SignatureIdent("SHA512withECDDSA", "SHA512withDETECDSA")); + ALL.add(new SignatureIdent("SHA3-224withECDDSA", "SHA3-224withDETECDSA")); + ALL.add(new SignatureIdent("SHA3-256withECDDSA", "SHA3-256withDETECDSA")); + ALL.add(new SignatureIdent("SHA3-384withECDDSA", "SHA3-384withDETECDSA")); + ALL.add(new SignatureIdent("SHA3-512withECDDSA", "SHA3-512withDETECDSA")); + // ECKCDSA? Botan provides. + ALL.add(new SignatureIdent("ECKCDSA", "SHA1withECKCDSA", "1.2.410.200004.1.100.4.3")); + ALL.add(new SignatureIdent("NONEwithECKCDSA")); + ALL.add(new SignatureIdent("RIPEMD160withECKCDSA")); + ALL.add(new SignatureIdent("SHA224withECKCDSA", "1.2.410.200004.1.100.4.4")); + ALL.add(new SignatureIdent("SHA256withECKCDSA", "1.2.410.200004.1.100.4.5")); + ALL.add(new SignatureIdent("SHA384withECKCDSA")); + ALL.add(new SignatureIdent("SHA512withECKCDSA")); + // ECGDSA? Botan provides. + ALL.add(new SignatureIdent("ECGDSA", "SHA1withECGDSA", "1.3.36.3.3.2.5.4.2")); + ALL.add(new SignatureIdent("NONEwithECGDSA")); + ALL.add(new SignatureIdent("RIPEMD160withECGDSA", "1.3.36.3.3.2.5.4.1")); + ALL.add(new SignatureIdent("SHA224withECGDSA", "1.3.36.3.3.2.5.4.3")); + ALL.add(new SignatureIdent("SHA224withECGDSA", "1.3.36.3.3.2.5.4.4")); + ALL.add(new SignatureIdent("SHA384withECGDSA", "1.3.36.3.3.2.5.4.5")); + ALL.add(new SignatureIdent("SHA512withECGDSA", "1.3.36.3.3.2.5.4.6")); + } + + public static SignatureIdent get(String ident) { + for (SignatureIdent sig : ALL) { + if (sig.getIdents().contains(ident)) { + return sig; + } + } + return null; + } + + public static List<SignatureIdent> list() { + return Collections.unmodifiableList(ALL); + } + + private SignatureIdent(String name, String... aliases) { + super(name, aliases); + if (name.contains("with")) { + int split = name.indexOf("with"); + this.hash = name.substring(0, split); + this.sig = name.substring(split + 4); + } else { + for (String alias : aliases) { + if (alias.contains("with")) { + int split = alias.indexOf("with"); + this.hash = alias.substring(0, split); + this.sig = alias.substring(split + 4); + break; + } + } + } + } + + public Signature getInstance(Provider provider) throws NoSuchAlgorithmException { + Signature instance = getInstance((algorithm, provider1) -> { + try { + return Signature.getInstance(algorithm, provider1); + } catch (NoSuchAlgorithmException e) { + return null; + } + }, provider); + instance.getProvider(); + return instance; + } + + public String toString() { + return name; + } + + public String getHashAlgo() { + return hash; + } + + public String getSigType() { + return sig; + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/libs/BoringsslLib.java b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/BoringsslLib.java new file mode 100644 index 0000000..60ca5d9 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/BoringsslLib.java @@ -0,0 +1,19 @@ +package cz.crcs.ectester.standalone.libs; + +import java.security.Provider; +import java.util.Set; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class BoringsslLib extends NativeECLibrary { + public BoringsslLib() { + super("boringssl_provider", "lib_boringssl.so"); + } + + @Override + native Provider createProvider(); + + @Override + public native Set<String> getCurves(); +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/libs/BotanLib.java b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/BotanLib.java new file mode 100644 index 0000000..cd28791 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/BotanLib.java @@ -0,0 +1,20 @@ +package cz.crcs.ectester.standalone.libs; + +import java.security.Provider; +import java.util.Set; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class BotanLib extends NativeECLibrary { + + public BotanLib() { + super("botan_provider", "botan-2"); + } + + @Override + native Provider createProvider(); + + @Override + public native Set<String> getCurves(); +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/libs/BouncyCastleLib.java b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/BouncyCastleLib.java new file mode 100644 index 0000000..c6600f9 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/BouncyCastleLib.java @@ -0,0 +1,28 @@ +package cz.crcs.ectester.standalone.libs; + +import org.bouncycastle.jce.ECNamedCurveTable; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import java.util.Enumeration; +import java.util.Set; +import java.util.TreeSet; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class BouncyCastleLib extends ProviderECLibrary { + + public BouncyCastleLib() { + super(new BouncyCastleProvider()); + } + + @Override + public Set<String> getCurves() { + Set<String> result = new TreeSet<>(); + Enumeration names = ECNamedCurveTable.getNames(); + while (names.hasMoreElements()) { + result.add((String) names.nextElement()); + } + return result; + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/libs/CryptoppLib.java b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/CryptoppLib.java new file mode 100644 index 0000000..5112d7d --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/CryptoppLib.java @@ -0,0 +1,20 @@ +package cz.crcs.ectester.standalone.libs; + +import java.security.Provider; +import java.util.Set; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class CryptoppLib extends NativeECLibrary { + + public CryptoppLib() { + super("cryptopp_provider", "cryptopp"); + } + + @Override + native Provider createProvider(); + + @Override + public native Set<String> getCurves(); +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/libs/ECLibrary.java b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/ECLibrary.java new file mode 100644 index 0000000..0f81978 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/ECLibrary.java @@ -0,0 +1,26 @@ +package cz.crcs.ectester.standalone.libs; + +import cz.crcs.ectester.standalone.consts.KeyAgreementIdent; +import cz.crcs.ectester.standalone.consts.KeyPairGeneratorIdent; +import cz.crcs.ectester.standalone.consts.SignatureIdent; + +import java.util.Set; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public interface ECLibrary { + boolean initialize(); + + boolean isInitialized(); + + Set<String> getCurves(); + + Set<KeyAgreementIdent> getKAs(); + + Set<SignatureIdent> getSigs(); + + Set<KeyPairGeneratorIdent> getKPGs(); + + String name(); +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/libs/GcryptLib.java b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/GcryptLib.java new file mode 100644 index 0000000..a0a7fc8 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/GcryptLib.java @@ -0,0 +1,20 @@ +package cz.crcs.ectester.standalone.libs; + +import java.security.Provider; +import java.util.Set; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class GcryptLib extends NativeECLibrary { + + public GcryptLib() { + super("gcrypt_provider", "gcrypt", "gpg-error"); + } + + @Override + native Provider createProvider(); + + @Override + public native Set<String> getCurves(); +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/libs/IppcpLib.java b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/IppcpLib.java new file mode 100644 index 0000000..0dec0a2 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/IppcpLib.java @@ -0,0 +1,20 @@ +package cz.crcs.ectester.standalone.libs; + +import java.security.Provider; +import java.util.Set; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class IppcpLib extends NativeECLibrary { + + public IppcpLib() { + super("ippcp_provider", "lib_ippcp.so"); + } + + @Override + native Provider createProvider(); + + @Override + public native Set<String> getCurves(); +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/libs/LibresslLib.java b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/LibresslLib.java new file mode 100644 index 0000000..cee4e4d --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/LibresslLib.java @@ -0,0 +1,19 @@ +package cz.crcs.ectester.standalone.libs; + +import java.security.Provider; +import java.util.Set; + +/** + * @author Matěj Grabovský matej@mgrabovsky.net + */ +public class LibresslLib extends NativeECLibrary { + public LibresslLib() { + super("libressl_provider", "lib_libressl.so"); + } + + @Override + native Provider createProvider(); + + @Override + public native Set<String> getCurves(); +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/libs/MatrixsslLib.java b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/MatrixsslLib.java new file mode 100644 index 0000000..fcc13ea --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/MatrixsslLib.java @@ -0,0 +1,20 @@ +package cz.crcs.ectester.standalone.libs; + +import java.security.Provider; +import java.util.Set; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class MatrixsslLib extends NativeECLibrary { + + public MatrixsslLib() { + super("matrixssl_provider"); + } + + @Override + native Provider createProvider(); + + @Override + public native Set<String> getCurves(); +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/libs/MbedTLSLib.java b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/MbedTLSLib.java new file mode 100644 index 0000000..ace10d7 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/MbedTLSLib.java @@ -0,0 +1,20 @@ +package cz.crcs.ectester.standalone.libs; + +import java.security.Provider; +import java.util.Set; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class MbedTLSLib extends NativeECLibrary { + + public MbedTLSLib() { + super("mbedtls_provider", "mbedcrypto"); + } + + @Override + native Provider createProvider(); + + @Override + public native Set<String> getCurves(); +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/libs/MscngLib.java b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/MscngLib.java new file mode 100644 index 0000000..527a65b --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/MscngLib.java @@ -0,0 +1,20 @@ +package cz.crcs.ectester.standalone.libs; + +import java.security.Provider; +import java.util.Set; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class MscngLib extends NativeECLibrary { + + public MscngLib() { + super("mscng_provider", "bcrypt"); + } + + @Override + native Provider createProvider(); + + @Override + public native Set<String> getCurves(); +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/libs/NativeECLibrary.java b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/NativeECLibrary.java new file mode 100644 index 0000000..2f469aa --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/NativeECLibrary.java @@ -0,0 +1,93 @@ +package cz.crcs.ectester.standalone.libs; + +import cz.crcs.ectester.common.util.FileUtil; +import cz.crcs.ectester.standalone.ECTesterStandalone; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.security.Provider; +import java.util.Set; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class NativeECLibrary extends ProviderECLibrary { + private String resource; + private String[] requriements; + + + public NativeECLibrary(String resource, String... requirements) { + this.resource = resource; + this.requriements = requirements; + } + + @Override + public boolean initialize() { + try { + /* Determine what OS are we running on and use appropriate suffix and path. */ + String suffix = FileUtil.getLibSuffix(); + + /* Resolve and create the ECTester directories in appData. */ + Path libDir = FileUtil.getLibDir(); + Path libReqDir = FileUtil.getRequirementsDir(); + Path libPath = libDir.resolve(resource + "." + suffix); + + /* Write the shim. */ + boolean found = FileUtil.writeNewer(ECTesterStandalone.LIB_RESOURCE_DIR + resource + "." + suffix, libPath); + if (!found) { + //System.err.printf("Resource %s not found.\n", resource); + return false; + } + + /* Load the requirements, if they are bundled, write them in and load them. */ + try { + for (String requirement : requriements) { + if (requirement.endsWith(suffix)) { + /* The requirement is bundled, write it */ + Path reqPath = libReqDir.resolve(requirement); + found = FileUtil.writeNewer(ECTesterStandalone.LIB_RESOURCE_DIR + requirement, reqPath); + if (!found) { + //System.err.printf("Requirement %s not found for %s.\n", requirement, resource); + return false; + } + System.load(reqPath.toString()); + } else { + System.loadLibrary(requirement); + } + } + } catch (UnsatisfiedLinkError ule) { + System.err.println(resource); + ule.printStackTrace(); + return false; + } + + System.load(libPath.toString()); + + provider = createProvider(); + return super.initialize(); + } catch (IOException | UnsatisfiedLinkError ignored) { + System.err.println(resource); + ignored.printStackTrace(); + } + return false; + } + + + @Override + public native Set<String> getNativeTimingSupport(); + + @Override + public native boolean setNativeTimingType(String type); + + @Override + public native long getNativeTimingResolution(); + + @Override + public native String getNativeTimingUnit(); + + @Override + public native long getLastNativeTiming(); + + abstract Provider createProvider(); +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/libs/NettleLib.java b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/NettleLib.java new file mode 100644 index 0000000..00e3b39 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/NettleLib.java @@ -0,0 +1,20 @@ +package cz.crcs.ectester.standalone.libs; + +import java.security.Provider; +import java.util.Set; + +/** + * @author Michal Cech 445431@mail.muni.cz + */ +public class NettleLib extends NativeECLibrary { + + public NettleLib() { + super("nettle_provider", "nettle","hogweed", "gmp"); + } + + @Override + native Provider createProvider(); + + @Override + public native Set<String> getCurves(); +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/libs/OpensslLib.java b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/OpensslLib.java new file mode 100644 index 0000000..e558336 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/OpensslLib.java @@ -0,0 +1,19 @@ +package cz.crcs.ectester.standalone.libs; + +import java.security.Provider; +import java.util.Set; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class OpensslLib extends NativeECLibrary { + public OpensslLib() { + super("openssl_provider", "crypto"); + } + + @Override + native Provider createProvider(); + + @Override + public native Set<String> getCurves(); +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/libs/ProviderECLibrary.java b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/ProviderECLibrary.java new file mode 100644 index 0000000..dd8e49c --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/ProviderECLibrary.java @@ -0,0 +1,113 @@ +package cz.crcs.ectester.standalone.libs; + +import cz.crcs.ectester.standalone.consts.Ident; +import cz.crcs.ectester.standalone.consts.KeyAgreementIdent; +import cz.crcs.ectester.standalone.consts.KeyPairGeneratorIdent; +import cz.crcs.ectester.standalone.consts.SignatureIdent; + +import java.security.Provider; +import java.security.Security; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Function; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class ProviderECLibrary implements ECLibrary { + Provider provider; + private boolean initialized = false; + + public ProviderECLibrary() { + + } + + public ProviderECLibrary(Provider provider) { + this.provider = provider; + } + + @Override + public boolean initialize() { + try { + int result = Security.addProvider(provider); + if (result == -1) { + provider = Security.getProvider(provider.getName()); + } + initialized = true; + } catch (NullPointerException | SecurityException ignored) { + initialized = false; + } + return initialized; + } + + @Override + public boolean isInitialized() { + return initialized; + } + + private <T extends Ident> Set<T> getIdents(String type, Function<String, T> getter) { + Set<T> results = new HashSet<>(); + if (!initialized) { + return results; + } + + for (Provider.Service service : provider.getServices()) { + if (service.getType().equals(type)) { + T id = getter.apply(service.getAlgorithm()); + if (id != null) { + results.add(id); + } + } + } + return results; + } + + public Set<String> getNativeTimingSupport() { + return new HashSet<>(); + } + + public boolean setNativeTimingType(String type) { + return false; + } + + public long getNativeTimingResolution() { + return 0; + } + + public String getNativeTimingUnit() { + return null; + } + + public long getLastNativeTiming() { + return 0; + } + + @Override + public Set<KeyAgreementIdent> getKAs() { + return getIdents("KeyAgreement", KeyAgreementIdent::get); + } + + @Override + public Set<SignatureIdent> getSigs() { + return getIdents("Signature", SignatureIdent::get); + } + + @Override + public Set<KeyPairGeneratorIdent> getKPGs() { + return getIdents("KeyPairGenerator", KeyPairGeneratorIdent::get); + } + + @Override + public String name() { + return provider.getInfo(); + } + + public Provider getProvider() { + return provider; + } + + @Override + public String toString() { + return name(); + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/libs/SunECLib.java b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/SunECLib.java new file mode 100644 index 0000000..3aec842 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/SunECLib.java @@ -0,0 +1,28 @@ +package cz.crcs.ectester.standalone.libs; + +import sun.security.ec.SunEC; + +import java.util.Set; +import java.util.TreeSet; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class SunECLib extends ProviderECLibrary { + + public SunECLib() { + super(new SunEC()); + } + + @Override + public Set<String> getCurves() { + String curves = provider.get("AlgorithmParameters.EC SupportedCurves").toString(); + String[] split = curves.split("\\|"); + Set<String> result = new TreeSet<>(); + for (String curve : split) { + String body = curve.split(",")[0].substring(1); + result.add(body); + } + return result; + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/libs/TomcryptLib.java b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/TomcryptLib.java new file mode 100644 index 0000000..78db00e --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/TomcryptLib.java @@ -0,0 +1,20 @@ +package cz.crcs.ectester.standalone.libs; + +import java.security.Provider; +import java.util.Set; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class TomcryptLib extends NativeECLibrary { + + public TomcryptLib() { + super("tomcrypt_provider", "tommath", "tomcrypt"); + } + + @Override + native Provider createProvider(); + + @Override + public native Set<String> getCurves(); +} 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 new file mode 100644 index 0000000..b58eb91 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/WolfCryptLib.java @@ -0,0 +1,18 @@ +package cz.crcs.ectester.standalone.libs; + +import com.wolfssl.provider.jce.WolfCryptProvider; + +import java.util.HashSet; +import java.util.Set; + +public class WolfCryptLib extends ProviderECLibrary { + + public WolfCryptLib() { + super(new WolfCryptProvider()); + } + + @Override + public Set<String> getCurves() { + return new HashSet<>(); + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/libs/jni/NativeECPrivateKey.java b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/jni/NativeECPrivateKey.java new file mode 100644 index 0000000..81bd387 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/jni/NativeECPrivateKey.java @@ -0,0 +1,184 @@ +package cz.crcs.ectester.standalone.libs.jni; + +import cz.crcs.ectester.common.util.ByteUtil; +import org.bouncycastle.util.Arrays; + +import java.math.BigInteger; +import java.security.interfaces.ECPrivateKey; +import java.security.spec.ECParameterSpec; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +@SuppressWarnings("serial") +public abstract class NativeECPrivateKey implements ECPrivateKey { + private String algorithm; + private String format; + ECParameterSpec params; + + public NativeECPrivateKey(String algorithm, String format, ECParameterSpec params) { + this.algorithm = algorithm; + this.format = format; + this.params = params; + } + + @Override + public String getAlgorithm() { + return algorithm; + } + + @Override + public String getFormat() { + return format; + } + + @Override + public ECParameterSpec getParams() { + return params; + } + + public abstract byte[] getData(); + + @SuppressWarnings("serial") + private static class Raw extends NativeECPrivateKey { + byte[] keyData; + + public Raw(byte[] keyData, ECParameterSpec params) { + super("EC", "raw", params); + this.keyData = Arrays.clone(keyData); + } + + @Override + public BigInteger getS() { + return new BigInteger(1, keyData); + } + + @Override + public byte[] getEncoded() { + return Arrays.clone(keyData); + } + + public byte[] getData() { + return getEncoded(); + } + } + + @SuppressWarnings("serial") + public static class TomCrypt extends Raw { + public TomCrypt(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } + + @SuppressWarnings("serial") + public static class Botan extends Raw { + public Botan(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } + + @SuppressWarnings("serial") + public static class Cryptopp extends Raw { + public Cryptopp(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } + + @SuppressWarnings("serial") + public static class Openssl extends Raw { + public Openssl(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } + + @SuppressWarnings("serial") + public static class Boringssl extends Raw { + public Boringssl(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } + + @SuppressWarnings("serial") + public static class Gcrypt extends Raw { + public Gcrypt(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } + + @SuppressWarnings("serial") + public static class MbedTLS extends Raw { + public MbedTLS(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } + + @SuppressWarnings("serial") + public static class Ippcp extends Raw { + public Ippcp(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } + + @SuppressWarnings("serial") + public static class Matrixssl extends Raw { + public Matrixssl(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } + + @SuppressWarnings("serial") + public static class Libressl extends Raw { + public Libressl(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } + + @SuppressWarnings("serial") + public static class Mscng extends Raw { + // 0 -> implicit (meta = curveName UTF16, header = full); + // 1 -> explicit (meta = null, header = full); + // 2 -> nist (meta = null, header = full) + private int flag; + private byte[] meta = null; + private byte[] header; + private byte[] x; + private byte[] y; + + public Mscng(int flag, byte[] meta, byte[] header, byte[] x, byte[] y, byte[] keyData, ECParameterSpec params) { + super(keyData, params); + this.flag = flag; + this.meta = Arrays.clone(meta); + this.header = Arrays.clone(header); + this.x = Arrays.clone(x); + this.y = Arrays.clone(y); + } + + public int getFlag() { + return flag; + } + + public byte[] getMeta() { + return Arrays.clone(meta); + } + + public byte[] getHeader() { + return Arrays.clone(header); + } + + public byte[] getBlob() { + return ByteUtil.concatenate(header, x, y, keyData); + } + + @Override + public byte[] getData() { + return getBlob(); + } + } + + @SuppressWarnings("serial") + public static class Nettle extends Raw { + public Nettle(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/libs/jni/NativeECPublicKey.java b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/jni/NativeECPublicKey.java new file mode 100644 index 0000000..7a8de83 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/jni/NativeECPublicKey.java @@ -0,0 +1,185 @@ +package cz.crcs.ectester.standalone.libs.jni; + +import cz.crcs.ectester.common.util.ByteUtil; +import cz.crcs.ectester.common.util.ECUtil; +import org.bouncycastle.util.Arrays; + +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +@SuppressWarnings("serial") +public abstract class NativeECPublicKey implements ECPublicKey { + private String algorithm; + private String format; + ECParameterSpec params; + + public NativeECPublicKey(String algorithm, String format, ECParameterSpec params) { + this.algorithm = algorithm; + this.format = format; + this.params = params; + } + + @Override + public String getAlgorithm() { + return algorithm; + } + + @Override + public String getFormat() { + return format; + } + + @Override + public ECParameterSpec getParams() { + return params; + } + + public abstract byte[] getData(); + + @SuppressWarnings("serial") + private static class ANSIX962 extends NativeECPublicKey { + byte[] keyData; + + public ANSIX962(byte[] keyData, ECParameterSpec params) { + super("EC", "ANSI X9.62", params); + this.keyData = Arrays.clone(keyData); + } + + @Override + public ECPoint getW() { + return ECUtil.fromX962(keyData, params.getCurve()); + } + + @Override + public byte[] getEncoded() { + return Arrays.clone(keyData); + } + + public byte[] getData() { + return ECUtil.toX962Uncompressed(getW(), params); + } + } + + @SuppressWarnings("serial") + public static class TomCrypt extends ANSIX962 { + public TomCrypt(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } + + @SuppressWarnings("serial") + public static class Botan extends ANSIX962 { + public Botan(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } + + @SuppressWarnings("serial") + public static class Cryptopp extends ANSIX962 { + public Cryptopp(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } + + @SuppressWarnings("serial") + public static class Openssl extends ANSIX962 { + public Openssl(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } + + @SuppressWarnings("serial") + public static class Boringssl extends ANSIX962 { + public Boringssl(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } + + @SuppressWarnings("serial") + public static class Gcrypt extends ANSIX962 { + public Gcrypt(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } + + @SuppressWarnings("serial") + public static class MbedTLS extends ANSIX962 { + public MbedTLS(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } + + @SuppressWarnings("serial") + public static class Ippcp extends ANSIX962 { + public Ippcp(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } + + @SuppressWarnings("serial") + public static class Matrixssl extends ANSIX962 { + public Matrixssl(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } + + @SuppressWarnings("serial") + public static class Libressl extends ANSIX962 { + public Libressl(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } + + @SuppressWarnings("serial") + public static class Mscng extends ANSIX962 { + // 0 -> implicit (meta = curveName UTF16, header = full); + // 1 -> explicit (meta = null, header = full); + // 2 -> nist (meta = null, header = full) + private int flag; + private byte[] meta = null; + private byte[] header; + private byte[] x; + private byte[] y; + + public Mscng(int flag, byte[] meta, byte[] header, byte[] x, byte[] y, ECParameterSpec params) { + super(ByteUtil.concatenate(new byte[]{0x04}, x, y), params); + this.flag = flag; + this.meta = Arrays.clone(meta); + this.header = Arrays.clone(header); + this.x = Arrays.clone(x); + this.y = Arrays.clone(y); + } + + public int getFlag() { + return flag; + } + + public byte[] getMeta() { + return Arrays.clone(meta); + } + + public byte[] getHeader() { + return Arrays.clone(header); + } + + public byte[] getBlob() { + return ByteUtil.concatenate(header, x, y); + } + + @Override + public byte[] getData() { + return getBlob(); + } + } + + @SuppressWarnings("serial") + public static class Nettle extends ANSIX962 { + public Nettle(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } +} 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 new file mode 100644 index 0000000..1e68f78 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/jni/NativeKeyAgreementSpi.java @@ -0,0 +1,449 @@ +package cz.crcs.ectester.standalone.libs.jni; + +import cz.crcs.ectester.common.util.ECUtil; + +import javax.crypto.KeyAgreementSpi; +import javax.crypto.SecretKey; +import javax.crypto.ShortBufferException; +import java.security.*; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.InvalidParameterSpecException; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class NativeKeyAgreementSpi extends KeyAgreementSpi { + ECPrivateKey privateKey; + ECPublicKey publicKey; + AlgorithmParameterSpec params; + + @Override + protected void engineInit(Key key, SecureRandom random) throws InvalidKeyException { + if (!(key instanceof ECPrivateKey)) { + throw new InvalidKeyException + ("Key must be instance of ECPrivateKey"); + } + privateKey = (ECPrivateKey) key; + this.params = privateKey.getParams(); + } + + @Override + protected Key engineDoPhase(Key key, boolean lastPhase) throws InvalidKeyException, IllegalStateException { + if (privateKey == null) { + throw new IllegalStateException("Not initialized"); + } + if (publicKey != null) { + throw new IllegalStateException("Phase already executed"); + } + if (!lastPhase) { + throw new IllegalStateException + ("Only two party agreement supported, lastPhase must be true"); + } + if (!(key instanceof ECPublicKey)) { + throw new InvalidKeyException + ("Key must be an instance of ECPublicKey"); + } + publicKey = (ECPublicKey) key; + return null; + } + + @Override + protected int engineGenerateSecret(byte[] sharedSecret, int offset) throws IllegalStateException, ShortBufferException { + byte[] secret = engineGenerateSecret(); + if (sharedSecret.length < offset + secret.length) { + throw new ShortBufferException(); + } + System.arraycopy(secret, 0, sharedSecret, offset, secret.length); + return secret.length; + } + + private abstract static class SimpleKeyAgreementSpi extends NativeKeyAgreementSpi { + + @Override + protected void engineInit(Key key, AlgorithmParameterSpec params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { + if (!(params instanceof ECParameterSpec)) { + throw new InvalidAlgorithmParameterException(params.toString()); + } + engineInit(key, random); + this.params = params; + } + + private byte[] getPubkey() { + if (publicKey instanceof NativeECPublicKey) { + return ((NativeECPublicKey) publicKey).getData(); + } else { + return ECUtil.toX962Uncompressed(publicKey.getW(), ((ECParameterSpec) params)); + } + } + + private byte[] getPrivkey() { + if (privateKey instanceof NativeECPrivateKey) { + return ((NativeECPrivateKey) privateKey).getData(); + } else { + return ECUtil.toByteArray(privateKey.getS(), ((ECParameterSpec) params).getOrder().bitLength()); + } + } + + @Override + protected byte[] engineGenerateSecret() throws IllegalStateException { + return generateSecret(getPubkey(), getPrivkey(), (ECParameterSpec) params); + } + + abstract byte[] generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params); + + @Override + protected SecretKey engineGenerateSecret(String algorithm) throws IllegalStateException, NoSuchAlgorithmException, InvalidKeyException { + if (algorithm == null) { + throw new NoSuchAlgorithmException("Algorithm must not be null"); + } + return generateSecret(getPubkey(), getPrivkey(), (ECParameterSpec) params, algorithm); + } + + abstract SecretKey generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params, String algorithm); + } + + private abstract static class ExtendedKeyAgreementSpi extends NativeKeyAgreementSpi { + + @Override + protected void engineInit(Key key, AlgorithmParameterSpec params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { + if (!(params instanceof ECParameterSpec || params instanceof ECGenParameterSpec)) { + throw new InvalidAlgorithmParameterException(); + } + engineInit(key, random); + this.params = params; + } + + @Override + protected byte[] engineGenerateSecret() throws IllegalStateException { + return generateSecret(publicKey, privateKey, params); + } + + abstract byte[] generateSecret(ECPublicKey pubkey, ECPrivateKey privkey, AlgorithmParameterSpec params); + + @Override + protected SecretKey engineGenerateSecret(String algorithm) throws IllegalStateException, NoSuchAlgorithmException, InvalidKeyException { + if (algorithm == null) { + throw new NoSuchAlgorithmException("Algorithm must not be null"); + } + return generateSecret(publicKey, privateKey, params, algorithm); + } + + abstract SecretKey generateSecret(ECPublicKey pubkey, ECPrivateKey privkey, AlgorithmParameterSpec params, String algorithm); + } + + + public static class TomCrypt extends SimpleKeyAgreementSpi { + + @Override + native byte[] generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params); + + @Override + native SecretKey generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params, String algorithm); + } + + public abstract static class Botan extends SimpleKeyAgreementSpi { + private String type; + + public Botan(String type) { + this.type = type; + } + + @Override + native byte[] generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params); + + @Override + native SecretKey generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params, String algorithm); + } + + public static class BotanECDH extends Botan { + public BotanECDH() { + super("ECDH"); + } + } + + public static class BotanECDHwithSHA1KDF extends Botan { + public BotanECDHwithSHA1KDF() { + super("ECDHwithSHA1KDF"); + } + } + + public static class BotanECDHwithSHA224KDF extends Botan { + public BotanECDHwithSHA224KDF() { + super("ECDHwithSHA224KDF"); + } + } + + public static class BotanECDHwithSHA256KDF extends Botan { + public BotanECDHwithSHA256KDF() { + super("ECDHwithSHA256KDF"); + } + } + + public static class BotanECDHwithSHA384KDF extends Botan { + public BotanECDHwithSHA384KDF() { + super("ECDHwithSHA384KDF"); + } + } + + public static class BotanECDHwithSHA512KDF extends Botan { + public BotanECDHwithSHA512KDF() { + super("ECDHwithSHA512KDF"); + } + } + + public abstract static class Cryptopp extends SimpleKeyAgreementSpi { + private String type; + + public Cryptopp(String type) { + this.type = type; + } + + @Override + native byte[] generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params); + + @Override + native SecretKey generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params, String algorithm); + } + + public static class CryptoppECDH extends Cryptopp { + public CryptoppECDH() { + super("ECDH"); + } + } + + public abstract static class Openssl extends SimpleKeyAgreementSpi { + private String type; + + public Openssl(String type) { + this.type = type; + } + + @Override + native byte[] generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params); + + @Override + native SecretKey generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params, String algorithm); + } + + public static class OpensslECDH extends Openssl { + public OpensslECDH() { + super("ECDH"); + } + } + + public abstract static class Boringssl extends SimpleKeyAgreementSpi { + private String type; + + public Boringssl(String type) { + this.type = type; + } + + @Override + native byte[] generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params); + + @Override + native SecretKey generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params, String algorithm); + } + + public static class BoringsslECDH extends Boringssl { + public BoringsslECDH() { + super("ECDH"); + } + } + + public abstract static class Gcrypt extends SimpleKeyAgreementSpi { + private String type; + + public Gcrypt(String type) { + this.type = type; + } + + @Override + native byte[] generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params); + + @Override + native SecretKey generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params, String algorithm); + } + + public static class GcryptECDH extends Gcrypt { + public GcryptECDH() { + super("ECDH"); + } + } + + + public abstract static class Mscng extends ExtendedKeyAgreementSpi { + private String type; + + public Mscng(String type) { + this.type = type; + } + + @Override + native byte[] generateSecret(ECPublicKey pubkey, ECPrivateKey privkey, AlgorithmParameterSpec params); + + @Override + native SecretKey generateSecret(ECPublicKey pubkey, ECPrivateKey privkey, AlgorithmParameterSpec params, String algorithm); + } + + public static class MscngECDHwithSHA1KDF extends Mscng { + public MscngECDHwithSHA1KDF() { + super("ECDHwithSHA1KDF(CNG)"); + } + } + + public static class MscngECDHwithSHA256KDF extends Mscng { + public MscngECDHwithSHA256KDF() { + super("ECDHwithSHA256KDF(CNG)"); + } + } + + public static class MscngECDHwithSHA384KDF extends Mscng { + public MscngECDHwithSHA384KDF() { + super("ECDHwithSHA384KDF(CNG)"); + } + } + + public static class MscngECDHwithSHA512KDF extends Mscng { + public MscngECDHwithSHA512KDF() { + super("ECDHwithSHA512KDF(CNG)"); + } + } + + public abstract static class MbedTLS extends SimpleKeyAgreementSpi { + private String type; + + public MbedTLS(String type) { + this.type = type; + } + + @Override + native byte[] generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params); + + @Override + native SecretKey generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params, String algorithm); + } + + public static class MbedTLSECDH extends MbedTLS { + public MbedTLSECDH() { + super("ECDH"); + } + } + + public abstract static class Ippcp extends SimpleKeyAgreementSpi { + private String type; + + public Ippcp(String type) { + this.type = type; + } + + @Override + native byte[] generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params); + + @Override + native SecretKey generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params, String algorithm); + } + + public static class IppcpECDH extends Ippcp { + public IppcpECDH() { + super("ECDH"); + } + } + + public abstract static class Matrixssl extends SimpleKeyAgreementSpi { + private String type; + + public Matrixssl(String type) { + this.type = type; + } + + @Override + native byte[] generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params); + + @Override + native SecretKey generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params, String algorithm); + } + + public static class MatrixsslECDH extends Matrixssl { + public MatrixsslECDH() { + super("ECDH"); + } + } + + public abstract static class Libressl extends SimpleKeyAgreementSpi { + private String type; + + public Libressl(String type) { + this.type = type; + } + + @Override + native byte[] generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params); + + @Override + native SecretKey generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params, String algorithm); + } + + public abstract static class Nettle extends SimpleKeyAgreementSpi { + private String type; + + public Nettle(String type) { + this.type = type; + } + + @Override + byte[] generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params) { + try { + AlgorithmParameters tmp = AlgorithmParameters.getInstance("EC"); + tmp.init(params); + ECGenParameterSpec spec = tmp.getParameterSpec(ECGenParameterSpec.class); + switch (spec.getName()) { + case "1.2.840.10045.3.1.7": + spec = new ECGenParameterSpec("secp256r1"); + break; + case "1.2.840.10045.3.1.1": + spec = new ECGenParameterSpec("secp192r1"); + break; + case "1.3.132.0.33": + spec = new ECGenParameterSpec("secp224r1"); + break; + case "1.3.132.0.34": + spec = new ECGenParameterSpec("secp384r1"); + break; + case "1.3.132.0.35": + spec = new ECGenParameterSpec("secp521r1"); + break; + default: + return null; + + } + return generateSecret(pubkey, privkey, spec); + + } catch (NoSuchAlgorithmException | InvalidParameterSpecException e) { + e.printStackTrace(); + return null; + } + } + + native byte[] generateSecret(byte[] pubkey, byte[] privkey, ECGenParameterSpec params); + + @Override + native SecretKey generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params, String algorithm); + } + + public static class NettleECDH extends Nettle { + public NettleECDH() { + super("ECDH"); + } + } + public static class LibresslECDH extends Libressl { + public LibresslECDH() { + super("ECDH"); + } + } + +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/libs/jni/NativeKeyPairGeneratorSpi.java b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/jni/NativeKeyPairGeneratorSpi.java new file mode 100644 index 0000000..636f423 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/jni/NativeKeyPairGeneratorSpi.java @@ -0,0 +1,367 @@ +package cz.crcs.ectester.standalone.libs.jni; + +import cz.crcs.ectester.common.ec.EC_Curve; +import cz.crcs.ectester.data.EC_Store; + +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; +import java.security.AlgorithmParameters; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.InvalidParameterSpecException; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class NativeKeyPairGeneratorSpi extends KeyPairGeneratorSpi { + private int keysize; + private SecureRandom random; + private AlgorithmParameterSpec params; + private boolean useKeysize; + private boolean useParams; + + public static final int DEFAULT_KEYSIZE = 256; + + @Override + public void initialize(int keysize, SecureRandom random) { + if (!keysizeSupported(keysize)) { + throw new InvalidParameterException("Keysize " + keysize + " not supported."); + } + this.keysize = keysize; + this.random = random; + this.useKeysize = true; + this.useParams = false; + } + + @Override + public void initialize(AlgorithmParameterSpec params, SecureRandom random) throws InvalidAlgorithmParameterException { + if (!paramsSupported(params)) { + throw new InvalidAlgorithmParameterException("Not supported."); + } + this.params = params; + this.random = random; + this.useParams = true; + this.useKeysize = false; + } + + @Override + public KeyPair generateKeyPair() { + if (!useKeysize && !useParams) { + if (keysizeSupported(DEFAULT_KEYSIZE)) { + initialize(DEFAULT_KEYSIZE, new SecureRandom()); + } + } + + if (useKeysize) { + return generate(keysize, random); + } else if (useParams) { + return generate(params, random); + } else { + throw new IllegalStateException("Uninitialized KeyPair."); + } + } + + abstract boolean keysizeSupported(int keysize); + + abstract boolean paramsSupported(AlgorithmParameterSpec params); + + abstract KeyPair generate(int keysize, SecureRandom random); + + abstract KeyPair generate(AlgorithmParameterSpec params, SecureRandom random); + + + public static class TomCrypt extends NativeKeyPairGeneratorSpi { + + public TomCrypt() { + } + + @Override + native boolean keysizeSupported(int keysize); + + @Override + native boolean paramsSupported(AlgorithmParameterSpec params); + + @Override + native KeyPair generate(int keysize, SecureRandom random); + + @Override + native KeyPair generate(AlgorithmParameterSpec params, SecureRandom random); + } + + public static abstract class Botan extends NativeKeyPairGeneratorSpi { + private String type; + + public Botan(String type) { + this.type = type; + } + + @Override + native boolean keysizeSupported(int keysize); + + @Override + native boolean paramsSupported(AlgorithmParameterSpec params); + + @Override + native KeyPair generate(int keysize, SecureRandom random); + + @Override + native KeyPair generate(AlgorithmParameterSpec params, SecureRandom random); + } + + public static class BotanECDH extends Botan { + + public BotanECDH() { + super("ECDH"); + } + } + + public static class BotanECDSA extends Botan { + + public BotanECDSA() { + super("ECDSA"); + } + } + + public static class BotanECKCDSA extends Botan { + + public BotanECKCDSA() { + super("ECKCDSA"); + } + } + + public static class BotanECGDSA extends Botan { + + public BotanECGDSA() { + super("ECGDSA"); + } + } + + public static abstract class Cryptopp extends NativeKeyPairGeneratorSpi { + private String type; + + public Cryptopp(String type) { + this.type = type; + } + + @Override + native boolean keysizeSupported(int keysize); + + @Override + native boolean paramsSupported(AlgorithmParameterSpec params); + + @Override + native KeyPair generate(int keysize, SecureRandom random); + + @Override + native KeyPair generate(AlgorithmParameterSpec params, SecureRandom random); + } + + public static class CryptoppECDH extends Cryptopp { + + public CryptoppECDH() { + super("ECDH"); + } + } + + public static class CryptoppECDSA extends Cryptopp { + + public CryptoppECDSA() { + super("ECDSA"); + } + } + + public static class Openssl extends NativeKeyPairGeneratorSpi { + public Openssl() { + initialize(256, new SecureRandom()); + } + + @Override + native boolean keysizeSupported(int keysize); + + @Override + native boolean paramsSupported(AlgorithmParameterSpec params); + + @Override + native KeyPair generate(int keysize, SecureRandom random); + + @Override + native KeyPair generate(AlgorithmParameterSpec params, SecureRandom random); + } + + public static class Boringssl extends NativeKeyPairGeneratorSpi { + public Boringssl() { + initialize(256, new SecureRandom()); + } + + @Override + native boolean keysizeSupported(int keysize); + + @Override + native boolean paramsSupported(AlgorithmParameterSpec params); + + @Override + native KeyPair generate(int keysize, SecureRandom random); + + @Override + native KeyPair generate(AlgorithmParameterSpec params, SecureRandom random); + } + + public static class Gcrypt extends NativeKeyPairGeneratorSpi { + + public Gcrypt() { + } + + @Override + native boolean keysizeSupported(int keysize); + + @Override + native boolean paramsSupported(AlgorithmParameterSpec params); + + @Override + native KeyPair generate(int keysize, SecureRandom random); + + @Override + native KeyPair generate(AlgorithmParameterSpec params, SecureRandom random); + } + + public static abstract class Mscng extends NativeKeyPairGeneratorSpi { + private String type; + + public Mscng(String type) { + this.type = type; + } + + @Override + native boolean keysizeSupported(int keysize); + + @Override + native boolean paramsSupported(AlgorithmParameterSpec params); + + @Override + native KeyPair generate(int keysize, SecureRandom random); + + @Override + native KeyPair generate(AlgorithmParameterSpec params, SecureRandom random); + } + + public static class MscngECDH extends Mscng { + + public MscngECDH() { + super("ECDH"); + } + } + + public static class MscngECDSA extends Mscng { + + public MscngECDSA() { + super("ECDSA"); + } + } + + public static class MbedTLS extends NativeKeyPairGeneratorSpi { + + public MbedTLS() { + initialize(256, new SecureRandom()); + } + + @Override + native boolean keysizeSupported(int keysize); + + @Override + native boolean paramsSupported(AlgorithmParameterSpec params); + + @Override + native KeyPair generate(int keysize, SecureRandom random); + + @Override + native KeyPair generate(AlgorithmParameterSpec params, SecureRandom random); + } + + public static class Ippcp extends NativeKeyPairGeneratorSpi { + + public Ippcp() { + initialize(256, new SecureRandom()); + } + + @Override + native boolean keysizeSupported(int keysize); + + @Override + native boolean paramsSupported(AlgorithmParameterSpec params); + + @Override + native KeyPair generate(int keysize, SecureRandom random); + + @Override + native KeyPair generate(AlgorithmParameterSpec params, SecureRandom random); + } + + public static class Matrixssl extends NativeKeyPairGeneratorSpi { + + public Matrixssl() { + initialize(256, new SecureRandom()); + } + + @Override + native boolean keysizeSupported(int keysize); + + @Override + native boolean paramsSupported(AlgorithmParameterSpec params); + + @Override + native KeyPair generate(int keysize, SecureRandom random); + + @Override + native KeyPair generate(AlgorithmParameterSpec params, SecureRandom random); + } + + public static class Libressl extends NativeKeyPairGeneratorSpi { + + public Libressl() { + initialize(256, new SecureRandom()); + } + + @Override + native boolean keysizeSupported(int keysize); + + @Override + native boolean paramsSupported(AlgorithmParameterSpec params); + + @Override + native KeyPair generate(int keysize, SecureRandom random); + + @Override + native KeyPair generate(AlgorithmParameterSpec params, SecureRandom random); + } + + public static class Nettle extends NativeKeyPairGeneratorSpi { + public Nettle() { + initialize(256, new SecureRandom()); + } + + @Override + native boolean keysizeSupported(int keysize); + + @Override + native boolean paramsSupported(AlgorithmParameterSpec params); + + @Override + native KeyPair generate(int keysize, SecureRandom random); + + @Override + KeyPair generate(AlgorithmParameterSpec params, SecureRandom random) { + if (params instanceof ECGenParameterSpec) { + String curveName = ((ECGenParameterSpec) params).getName(); + if (curveName.contains("secp")) { + curveName = "secg/" + curveName; + } + EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, curveName); + ECParameterSpec spec = curve.toSpec(); + return generate(params, random, spec); + } + return null; + } + + native KeyPair generate(AlgorithmParameterSpec params, SecureRandom random, AlgorithmParameterSpec spec); + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/libs/jni/NativeProvider.java b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/jni/NativeProvider.java new file mode 100644 index 0000000..e036937 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/jni/NativeProvider.java @@ -0,0 +1,159 @@ +package cz.crcs.ectester.standalone.libs.jni; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.Provider; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +@SuppressWarnings("serial") +public abstract class NativeProvider extends Provider { + + public NativeProvider(String name, double version, String info) { + super(name, version, info); + + if (System.getSecurityManager() == null) { + setup(); + } else { + AccessController.doPrivileged((PrivilegedAction<Object>) () -> { + setup(); + return null; + }); + } + } + + abstract void setup(); + + @SuppressWarnings("serial") + public static class TomCrypt extends NativeProvider { + + public TomCrypt(String name, double version, String info) { + super(name, version, info); + } + + @Override + native void setup(); + } + + @SuppressWarnings("serial") + public static class Botan extends NativeProvider { + + public Botan(String name, double version, String info) { + super(name, version, info); + } + + @Override + native void setup(); + } + + @SuppressWarnings("serial") + public static class Cryptopp extends NativeProvider { + + public Cryptopp(String name, double version, String info) { + super(name, version, info); + } + + @Override + native void setup(); + } + + @SuppressWarnings("serial") + public static class Openssl extends NativeProvider { + + public Openssl(String name, double version, String info) { + super(name, version, info); + } + + @Override + native void setup(); + } + + @SuppressWarnings("serial") + public static class Boringssl extends NativeProvider { + + public Boringssl(String name, double version, String info) { + super(name, version, info); + } + + @Override + native void setup(); + } + + @SuppressWarnings("serial") + public static class Gcrypt extends NativeProvider { + + public Gcrypt(String name, double version, String info) { + super(name, version, info); + } + + @Override + native void setup(); + } + + @SuppressWarnings("serial") + public static class Mscng extends NativeProvider { + + public Mscng(String name, double version, String info) { + super(name, version, info); + } + + @Override + native void setup(); + } + + @SuppressWarnings("serial") + public static class MbedTLS extends NativeProvider { + + public MbedTLS(String name, double version, String info) { + super(name, version, info); + } + + @Override + native void setup(); + } + + @SuppressWarnings("serial") + public static class Ippcp extends NativeProvider { + + public Ippcp(String name, double version, String info) { + super(name, version, info); + } + + @Override + native void setup(); + } + + @SuppressWarnings("serial") + public static class Matrixssl extends NativeProvider { + + public Matrixssl(String name, double version, String info) { + super(name, version, info); + } + + @Override + native void setup(); + } + + @SuppressWarnings("serial") + public static class Libressl extends NativeProvider { + + public Libressl(String name, double version, String info) { + super(name, version, info); + } + + @Override + native void setup(); + } + + @SuppressWarnings("serial") + public static class Nettle extends NativeProvider { + + public Nettle(String name, double version, String info) { + super(name, version, info); + } + + @Override + native void setup(); + } +} 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 new file mode 100644 index 0000000..d6e814c --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/libs/jni/NativeSignatureSpi.java @@ -0,0 +1,657 @@ +package cz.crcs.ectester.standalone.libs.jni; + +import cz.crcs.ectester.common.util.ECUtil; + +import java.io.ByteArrayOutputStream; +import java.security.*; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.InvalidParameterSpecException; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class NativeSignatureSpi extends SignatureSpi { + ECPublicKey verifyKey; + ECPrivateKey signKey; + ECParameterSpec params; + + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + + @Override + protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { + if (!(publicKey instanceof ECPublicKey)) { + throw new InvalidKeyException + ("Key must be an instance of ECPublicKey"); + } + verifyKey = (ECPublicKey) publicKey; + params = verifyKey.getParams(); + buffer.reset(); + } + + @Override + protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException { + if (!(privateKey instanceof ECPrivateKey)) { + throw new InvalidKeyException + ("Key must be an instance of ECPrivateKey"); + } + signKey = (ECPrivateKey) privateKey; + params = signKey.getParams(); + buffer.reset(); + } + + @Override + protected void engineUpdate(byte b) throws SignatureException { + buffer.write(b); + } + + @Override + protected void engineUpdate(byte[] b, int off, int len) throws SignatureException { + buffer.write(b, off, len); + } + + + @Override + @Deprecated + protected void engineSetParameter(String param, Object value) throws InvalidParameterException { + throw new UnsupportedOperationException("setParameter() not supported"); + } + + @Override + @Deprecated + protected Object engineGetParameter(String param) throws InvalidParameterException { + throw new UnsupportedOperationException("getParameter() not supported"); + } + + private abstract static class SimpleSignatureSpi extends NativeSignatureSpi { + + @Override + protected byte[] engineSign() throws SignatureException { + byte[] privkey; + if (signKey instanceof NativeECPrivateKey) { + privkey = ((NativeECPrivateKey) signKey).getData(); + } else { + privkey = ECUtil.toByteArray(signKey.getS(), params.getOrder().bitLength()); + } + return sign(buffer.toByteArray(), privkey, params); + } + + @Override + protected boolean engineVerify(byte[] sigBytes) throws SignatureException { + byte[] pubkey; + if (verifyKey instanceof NativeECPublicKey) { + pubkey = ((NativeECPublicKey) verifyKey).getData(); + } else { + pubkey = ECUtil.toX962Uncompressed(verifyKey.getW(), params); + } + return verify(sigBytes, buffer.toByteArray(), pubkey, params); + } + + abstract byte[] sign(byte[] data, byte[] privkey, ECParameterSpec params); + + abstract boolean verify(byte[] signature, byte[] data, byte[] pubkey, ECParameterSpec params); + } + + private abstract static class ExtendedSignatureSpi extends NativeSignatureSpi { + + @Override + protected byte[] engineSign() throws SignatureException { + return sign(buffer.toByteArray(), signKey, params); + } + + @Override + protected boolean engineVerify(byte[] sigBytes) throws SignatureException { + return verify(sigBytes, buffer.toByteArray(), verifyKey, params); + } + + abstract byte[] sign(byte[] data, ECPrivateKey privkey, ECParameterSpec params); + + abstract boolean verify(byte[] signature, byte[] data, ECPublicKey pubkey, ECParameterSpec params); + } + + public static class TomCryptRaw extends SimpleSignatureSpi { + + @Override + native byte[] sign(byte[] data, byte[] privkey, ECParameterSpec params); + + @Override + native boolean verify(byte[] signature, byte[] data, byte[] pubkey, ECParameterSpec params); + } + + public abstract static class Botan extends SimpleSignatureSpi { + private String type; + + public Botan(String type) { + this.type = type; + } + + @Override + native byte[] sign(byte[] data, byte[] privkey, ECParameterSpec params); + + @Override + native boolean verify(byte[] signature, byte[] data, byte[] pubkey, ECParameterSpec params); + } + + public static class BotanECDSAwithNONE extends Botan { + + public BotanECDSAwithNONE() { + super("NONEwithECDSA"); + } + } + + public static class BotanECDSAwithSHA1 extends Botan { + + public BotanECDSAwithSHA1() { + super("SHA1withECDSA"); + } + } + + public static class BotanECDSAwithSHA224 extends Botan { + + public BotanECDSAwithSHA224() { + super("SHA224withECDSA"); + } + } + + public static class BotanECDSAwithSHA256 extends Botan { + + public BotanECDSAwithSHA256() { + super("SHA256withECDSA"); + } + } + + public static class BotanECDSAwithSHA384 extends Botan { + + public BotanECDSAwithSHA384() { + super("SHA384withECDSA"); + } + } + + public static class BotanECDSAwithSHA512 extends Botan { + + public BotanECDSAwithSHA512() { + super("SHA512withECDSA"); + } + } + + public static class BotanECKCDSAwithNONE extends Botan { + + public BotanECKCDSAwithNONE() { + super("NONEwithECKCDSA"); + } + } + + public static class BotanECKCDSAwithSHA1 extends Botan { + + public BotanECKCDSAwithSHA1() { + super("SHA1withECKCDSA"); + } + } + + public static class BotanECKCDSAwithSHA224 extends Botan { + + public BotanECKCDSAwithSHA224() { + super("SHA224withECKCDSA"); + } + } + + public static class BotanECKCDSAwithSHA256 extends Botan { + + public BotanECKCDSAwithSHA256() { + super("SHA256withECKCDSA"); + } + } + + public static class BotanECKCDSAwithSHA384 extends Botan { + + public BotanECKCDSAwithSHA384() { + super("SHA384withECKCDSA"); + } + } + + public static class BotanECKCDSAwithSHA512 extends Botan { + + public BotanECKCDSAwithSHA512() { + super("SHA512withECKCDSA"); + } + } + + public static class BotanECGDSAwithNONE extends Botan { + + public BotanECGDSAwithNONE() { + super("NONEwithECGDSA"); + } + } + + public static class BotanECGDSAwithSHA1 extends Botan { + + public BotanECGDSAwithSHA1() { + super("SHA1withECGDSA"); + } + } + + public static class BotanECGDSAwithSHA224 extends Botan { + + public BotanECGDSAwithSHA224() { + super("SHA224withECGDSA"); + } + } + + public static class BotanECGDSAwithSHA256 extends Botan { + + public BotanECGDSAwithSHA256() { + super("SHA256withECGDSA"); + } + } + + public static class BotanECGDSAwithSHA384 extends Botan { + + public BotanECGDSAwithSHA384() { + super("SHA384withECGDSA"); + } + } + + public static class BotanECGDSAwithSHA512 extends Botan { + + public BotanECGDSAwithSHA512() { + super("SHA512withECGDSA"); + } + } + + public abstract static class Cryptopp extends SimpleSignatureSpi { + private String type; + + public Cryptopp(String type) { + this.type = type; + } + + @Override + native byte[] sign(byte[] data, byte[] privkey, ECParameterSpec params); + + @Override + native boolean verify(byte[] signature, byte[] data, byte[] pubkey, ECParameterSpec params); + } + + public static class CryptoppECDSAwithSHA1 extends Cryptopp { + + public CryptoppECDSAwithSHA1() { + super("SHA1withECDSA"); + } + } + + public static class CryptoppECDSAwithSHA224 extends Cryptopp { + + public CryptoppECDSAwithSHA224() { + super("SHA224withECDSA"); + } + } + + public static class CryptoppECDSAwithSHA256 extends Cryptopp { + + public CryptoppECDSAwithSHA256() { + super("SHA256withECDSA"); + } + } + + public static class CryptoppECDSAwithSHA384 extends Cryptopp { + + public CryptoppECDSAwithSHA384() { + super("SHA384withECDSA"); + } + } + + public static class CryptoppECDSAwithSHA512 extends Cryptopp { + + public CryptoppECDSAwithSHA512() { + super("SHA512withECDSA"); + } + } + + public abstract static class Openssl extends SimpleSignatureSpi { + private String type; + + public Openssl(String type) { + this.type = type; + } + + @Override + native byte[] sign(byte[] data, byte[] privkey, ECParameterSpec params); + + @Override + native boolean verify(byte[] signature, byte[] data, byte[] pubkey, ECParameterSpec params); + } + + public static class OpensslECDSAwithNONE extends Openssl { + + public OpensslECDSAwithNONE() { + super("NONEwithECDSA"); + } + } + + public abstract static class Boringssl extends SimpleSignatureSpi { + private String type; + + public Boringssl(String type) { + this.type = type; + } + + @Override + native byte[] sign(byte[] data, byte[] privkey, ECParameterSpec params); + + @Override + native boolean verify(byte[] signature, byte[] data, byte[] pubkey, ECParameterSpec params); + } + + public static class BoringsslECDSAwithNONE extends Boringssl { + + public BoringsslECDSAwithNONE() { + super("NONEwithECDSA"); + } + } + + public abstract static class Gcrypt extends SimpleSignatureSpi { + private String type; + + public Gcrypt(String type) { + this.type = type; + } + + @Override + native byte[] sign(byte[] data, byte[] privkey, ECParameterSpec params); + + @Override + native boolean verify(byte[] signature, byte[] data, byte[] pubkey, ECParameterSpec params); + } + + public static class GcryptECDSAwithNONE extends Gcrypt { + + public GcryptECDSAwithNONE() { + super("NONEwithECDSA"); + } + } + + public static class GcryptECDSAwithSHA1 extends Gcrypt { + + public GcryptECDSAwithSHA1() { + super("SHA1withECDSA"); + } + } + + public static class GcryptECDSAwithSHA224 extends Gcrypt { + + public GcryptECDSAwithSHA224() { + super("SHA224withECDSA"); + } + } + + public static class GcryptECDSAwithSHA256 extends Gcrypt { + + public GcryptECDSAwithSHA256() { + super("SHA256withECDSA"); + } + } + + public static class GcryptECDSAwithSHA384 extends Gcrypt { + + public GcryptECDSAwithSHA384() { + super("SHA384withECDSA"); + } + } + + public static class GcryptECDSAwithSHA512 extends Gcrypt { + + public GcryptECDSAwithSHA512() { + super("SHA512withECDSA"); + } + } + + public static class GcryptECDDSAwithSHA1 extends Gcrypt { + + public GcryptECDDSAwithSHA1() { + super("SHA1withECDDSA"); + } + } + + public static class GcryptECDDSAwithSHA224 extends Gcrypt { + + public GcryptECDDSAwithSHA224() { + super("SHA224withECDDSA"); + } + } + + public static class GcryptECDDSAwithSHA256 extends Gcrypt { + + public GcryptECDDSAwithSHA256() { + super("SHA256withECDDSA"); + } + } + + public static class GcryptECDDSAwithSHA384 extends Gcrypt { + + public GcryptECDDSAwithSHA384() { + super("SHA384withECDDSA"); + } + } + + public static class GcryptECDDSAwithSHA512 extends Gcrypt { + + public GcryptECDDSAwithSHA512() { + super("SHA512withECDDSA"); + } + } + + public abstract static class MbedTLS extends SimpleSignatureSpi { + private String type; + + public MbedTLS(String type) { + this.type = type; + } + + @Override + native byte[] sign(byte[] data, byte[] privkey, ECParameterSpec params); + + @Override + native boolean verify(byte[] signature, byte[] data, byte[] pubkey, ECParameterSpec params); + } + + public static class MbedTLSECDSAwithNONE extends MbedTLS { + + public MbedTLSECDSAwithNONE() { + super("NONEwithECDSA"); + } + } + + public abstract static class Ippcp extends SimpleSignatureSpi { + private String type; + + public Ippcp(String type) { + this.type = type; + } + + @Override + native byte[] sign(byte[] data, byte[] privkey, ECParameterSpec params); + + @Override + native boolean verify(byte[] signature, byte[] data, byte[] pubkey, ECParameterSpec params); + } + + public static class IppcpECDSAwithNONE extends Ippcp { + + public IppcpECDSAwithNONE() { + super("NONEwithECDSA"); + } + } + + public abstract static class Libressl extends SimpleSignatureSpi { + private String type; + + public Libressl(String type) { + this.type = type; + } + + @Override + native byte[] sign(byte[] data, byte[] privkey, ECParameterSpec params); + + @Override + native boolean verify(byte[] signature, byte[] data, byte[] pubkey, ECParameterSpec params); + } + + public static class LibresslECDSAwithNONE extends Libressl { + + public LibresslECDSAwithNONE() { + super("NONEwithECDSA"); + } + } + + public abstract static class Matrixssl extends SimpleSignatureSpi { + private String type; + + public Matrixssl(String type) { + this.type = type; + } + + @Override + native byte[] sign(byte[] data, byte[] privkey, ECParameterSpec params); + + @Override + native boolean verify(byte[] signature, byte[] data, byte[] pubkey, ECParameterSpec params); + } + + public static class MatrixsslECDSAwithNONE extends Matrixssl { + + public MatrixsslECDSAwithNONE() { + super("NONEwithECDSA"); + } + } + + public abstract static class Mscng extends ExtendedSignatureSpi { + private String type; + + public Mscng(String type) { + this.type = type; + } + + @Override + native byte[] sign(byte[] data, ECPrivateKey privkey, ECParameterSpec params); + + @Override + native boolean verify(byte[] signature, byte[] data, ECPublicKey pubkey, ECParameterSpec params); + } + + public static class MscngECDSAwithSHA1 extends Mscng { + + public MscngECDSAwithSHA1() { + super("SHA1withECDSA"); + } + } + + public static class MscngECDSAwithSHA256 extends Mscng { + + public MscngECDSAwithSHA256() { + super("SHA256withECDSA"); + } + } + + public static class MscngECDSAwithSHA384 extends Mscng { + + public MscngECDSAwithSHA384() { + super("SHA384withECDSA"); + } + } + + public static class MscngECDSAwithSHA512 extends Mscng { + + public MscngECDSAwithSHA512() { + super("SHA512withECDSA"); + } + } + + public abstract static class Nettle extends SimpleSignatureSpi { + private String type; + + public Nettle(String type) { + this.type = type; + } + + @Override + byte[] sign(byte[] data, byte[] privKey, ECParameterSpec params) { + try { + AlgorithmParameters tmp = AlgorithmParameters.getInstance("EC"); + tmp.init(params); + ECGenParameterSpec spec = tmp.getParameterSpec(ECGenParameterSpec.class); + switch (spec.getName()) { + case "1.2.840.10045.3.1.7": + spec = new ECGenParameterSpec("secp256r1"); + break; + case "1.2.840.10045.3.1.1": + spec = new ECGenParameterSpec("secp192r1"); + break; + case "1.3.132.0.33": + spec = new ECGenParameterSpec("secp224r1"); + break; + case "1.3.132.0.34": + spec = new ECGenParameterSpec("secp384r1"); + break; + case "1.3.132.0.35": + spec = new ECGenParameterSpec("secp521r1"); + break; + default: + return null; + + } + return sign(data, privKey, spec); + + } catch (NoSuchAlgorithmException | InvalidParameterSpecException e) { + e.printStackTrace(); + return null; + } + } + + native byte[] sign(byte[] data, byte[] privKey, ECGenParameterSpec params); + + @Override + boolean verify(byte[] signature, byte[] data, byte[] pubkey, ECParameterSpec params) { + try { + AlgorithmParameters tmp = AlgorithmParameters.getInstance("EC"); + tmp.init(params); + ECGenParameterSpec spec = tmp.getParameterSpec(ECGenParameterSpec.class); + switch (spec.getName()) { + case "1.2.840.10045.3.1.7": + spec = new ECGenParameterSpec("secp256r1"); + break; + case "1.2.840.10045.3.1.1": + spec = new ECGenParameterSpec("secp192r1"); + break; + case "1.3.132.0.33": + spec = new ECGenParameterSpec("secp224r1"); + break; + case "1.3.132.0.34": + spec = new ECGenParameterSpec("secp384r1"); + break; + case "1.3.132.0.35": + spec = new ECGenParameterSpec("secp521r1"); + break; + default: + return false; + } + return verify(signature, data, pubkey, spec); + + } catch (NoSuchAlgorithmException | InvalidParameterSpecException e) { + e.printStackTrace(); + return false; + } + } + + native boolean verify(byte[] signature, byte[] data, byte[] pubkey, ECGenParameterSpec params); + } + + public static class NettleECDSAwithNONE extends Nettle { + + public NettleECDSAwithNONE() { + super("NONEwithECDSA"); + } + } + +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/output/TextTestWriter.java b/standalone/src/main/java/cz/crcs/ectester/standalone/output/TextTestWriter.java new file mode 100644 index 0000000..d7be4dc --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/output/TextTestWriter.java @@ -0,0 +1,56 @@ +package cz.crcs.ectester.standalone.output; + +import cz.crcs.ectester.common.cli.Colors; +import cz.crcs.ectester.common.output.BaseTextTestWriter; +import cz.crcs.ectester.common.test.TestSuite; +import cz.crcs.ectester.common.test.Testable; +import cz.crcs.ectester.standalone.ECTesterStandalone; +import cz.crcs.ectester.standalone.test.base.StandaloneTestable; +import cz.crcs.ectester.standalone.test.suites.StandaloneTestSuite; + +import java.io.PrintStream; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class TextTestWriter extends BaseTextTestWriter { + public TextTestWriter(PrintStream output) { + super(output); + } + + private String causeString(Object cause) { + if (cause == null) { + return ""; + } else if (cause instanceof Exception) { + Exception ex = ((Exception) cause); + return " -> " + ex.getClass().getCanonicalName() + " : " + ex.getMessage(); + } else { + return cause.toString(); + } + } + + @Override + protected String testableString(Testable t) { + if (t instanceof StandaloneTestable) { + StandaloneTestable<?> testable = (StandaloneTestable) t; + Enum<?> stage = testable.getStage(); + String stageName = stage.name(); + String exception = causeString(testable.getException()); + String errorCause = causeString(testable.errorCause()); + return String.format("[%d/%d] %s %s %s", stage.ordinal() + 1, stage.getClass().getEnumConstants().length, stageName, exception, errorCause); + } + return ""; + } + + @Override + protected String deviceString(TestSuite suite) { + if (suite instanceof StandaloneTestSuite) { + StandaloneTestSuite standaloneSuite = (StandaloneTestSuite) suite; + StringBuilder sb = new StringBuilder(); + sb.append("═══ ").append(Colors.underline("ECTester version:")).append(" ").append(ECTesterStandalone.VERSION).append(System.lineSeparator()); + sb.append("═══ ").append(Colors.underline("Library:")).append(" ").append(standaloneSuite.getLibrary().name()).append(System.lineSeparator()); + return sb.toString(); + } + return ""; + } +}
\ No newline at end of file diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/output/XMLTestWriter.java b/standalone/src/main/java/cz/crcs/ectester/standalone/output/XMLTestWriter.java new file mode 100644 index 0000000..812634f --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/output/XMLTestWriter.java @@ -0,0 +1,156 @@ +package cz.crcs.ectester.standalone.output; + +import cz.crcs.ectester.common.output.BaseXMLTestWriter; +import cz.crcs.ectester.common.test.TestSuite; +import cz.crcs.ectester.common.test.Testable; +import cz.crcs.ectester.common.util.ByteUtil; +import cz.crcs.ectester.standalone.ECTesterStandalone; +import cz.crcs.ectester.standalone.test.base.KeyAgreementTestable; +import cz.crcs.ectester.standalone.test.base.KeyGeneratorTestable; +import cz.crcs.ectester.standalone.test.base.SignatureTestable; +import cz.crcs.ectester.standalone.test.base.StandaloneTestable; +import cz.crcs.ectester.standalone.test.suites.StandaloneTestSuite; +import org.w3c.dom.Element; + +import javax.xml.parsers.ParserConfigurationException; +import java.io.OutputStream; +import java.security.PrivateKey; +import java.security.PublicKey; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class XMLTestWriter extends BaseXMLTestWriter { + + public XMLTestWriter(OutputStream output) throws ParserConfigurationException { + super(output); + } + + private Element pkeyElement(PublicKey pkey) { + Element pubkey = doc.createElement("pubkey"); + if (pkey == null) { + return pubkey; + } + pubkey.setAttribute("algorithm", pkey.getAlgorithm()); + pubkey.setAttribute("format", pkey.getFormat()); + pubkey.setTextContent(ByteUtil.bytesToHex(pkey.getEncoded())); + return pubkey; + } + + private Element skeyElement(PrivateKey skey) { + Element privkey = doc.createElement("privkey"); + if (skey == null) { + return privkey; + } + privkey.setAttribute("algorithm", skey.getAlgorithm()); + privkey.setAttribute("format", skey.getFormat()); + privkey.setTextContent(ByteUtil.bytesToHex(skey.getEncoded())); + return privkey; + } + + private Element kaElement(KeyAgreementTestable kat) { + Element katElem = doc.createElement("key-agreement"); + katElem.setAttribute("algo", kat.getKa().getAlgorithm()); + + Element secret = doc.createElement("secret"); + secret.setTextContent(ByteUtil.bytesToHex(kat.getSecret())); + katElem.appendChild(secret); + + PublicKey pkey = kat.getPublicKey(); + Element pubkey = pkeyElement(pkey); + katElem.appendChild(pubkey); + + PrivateKey skey = kat.getPrivateKey(); + Element privkey = skeyElement(skey); + katElem.appendChild(privkey); + + return katElem; + } + + private Element kgtElement(KeyGeneratorTestable kgt) { + Element kgtElem = doc.createElement("key-pair-generator"); + kgtElem.setAttribute("algo", kgt.getKpg().getAlgorithm()); + + Element keyPair = doc.createElement("key-pair"); + if (kgt.getKeyPair() != null) { + PublicKey pkey = kgt.getKeyPair().getPublic(); + Element pubkey = pkeyElement(pkey); + keyPair.appendChild(pubkey); + + PrivateKey skey = kgt.getKeyPair().getPrivate(); + Element privkey = skeyElement(skey); + keyPair.appendChild(privkey); + } + + kgtElem.appendChild(keyPair); + return kgtElem; + } + + private Element sigElement(SignatureTestable sig) { + Element sigElem = doc.createElement("signature"); + sigElem.setAttribute("verified", sig.getVerified() ? "true" : "false"); + sigElem.setAttribute("algo", sig.getSig().getAlgorithm()); + + Element raw = doc.createElement("raw"); + raw.setTextContent(ByteUtil.bytesToHex(sig.getSignature())); + sigElem.appendChild(raw); + + return sigElem; + } + + private Element stageElement(StandaloneTestable<?> t) { + Element result = doc.createElement("stage"); + result.setTextContent(t.getStage().name()); + return result; + } + + private String causeObject(Object cause) { + if (cause == null) { + return ""; + } else if (cause instanceof Exception) { + Exception ex = ((Exception) cause); + return ex.getClass().getCanonicalName() + " : " + ex.getMessage(); + } else { + return cause.toString(); + } + } + + @Override + protected Element testableElement(Testable t) { + Element result = doc.createElement("test"); + if (t instanceof StandaloneTestable) { + StandaloneTestable<?> testable = (StandaloneTestable) t; + if (t instanceof KeyGeneratorTestable) { + result.setAttribute("type", "key-pair-generator"); + result.appendChild(kgtElement((KeyGeneratorTestable) t)); + } else if (t instanceof KeyAgreementTestable) { + result.setAttribute("type", "key-agreement"); + result.appendChild(kaElement((KeyAgreementTestable) t)); + } else if (t instanceof SignatureTestable) { + result.setAttribute("type", "signature"); + result.appendChild(sigElement((SignatureTestable) t)); + } + result.appendChild(stageElement(testable)); + Element exception = doc.createElement("exception"); + exception.setTextContent(causeObject(testable.getException()) + causeObject(testable.errorCause())); + result.appendChild(exception); + } + return result; + } + + @Override + protected Element deviceElement(TestSuite suite) { + if (suite instanceof StandaloneTestSuite) { + StandaloneTestSuite standaloneSuite = (StandaloneTestSuite) suite; + Element result = doc.createElement("device"); + result.setAttribute("type", "library"); + result.setAttribute("ectester", ECTesterStandalone.VERSION); + + Element name = doc.createElement("name"); + name.setTextContent(standaloneSuite.getLibrary().name()); + result.appendChild(name); + return result; + } + return null; + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/output/YAMLTestWriter.java b/standalone/src/main/java/cz/crcs/ectester/standalone/output/YAMLTestWriter.java new file mode 100644 index 0000000..ee8a199 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/output/YAMLTestWriter.java @@ -0,0 +1,124 @@ +package cz.crcs.ectester.standalone.output; + +import cz.crcs.ectester.common.output.BaseYAMLTestWriter; +import cz.crcs.ectester.common.test.TestSuite; +import cz.crcs.ectester.common.test.Testable; +import cz.crcs.ectester.common.util.ByteUtil; +import cz.crcs.ectester.standalone.ECTesterStandalone; +import cz.crcs.ectester.standalone.test.base.KeyAgreementTestable; +import cz.crcs.ectester.standalone.test.base.KeyGeneratorTestable; +import cz.crcs.ectester.standalone.test.base.SignatureTestable; +import cz.crcs.ectester.standalone.test.base.StandaloneTestable; +import cz.crcs.ectester.standalone.test.suites.StandaloneTestSuite; + +import java.io.PrintStream; +import java.security.Key; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class YAMLTestWriter extends BaseYAMLTestWriter { + public YAMLTestWriter(PrintStream output) { + super(output); + } + + private Map<String, Object> keyObject(Key key) { + Map<String, Object> kObject = new LinkedHashMap<>(); + if (key == null) { + return kObject; + } + kObject.put("algo", key.getAlgorithm()); + kObject.put("format", key.getFormat()); + kObject.put("raw", ByteUtil.bytesToHex(key.getEncoded())); + return kObject; + } + + private Map<String, Object> kaObject(KeyAgreementTestable kat) { + Map<String, Object> katObject = new LinkedHashMap<>(); + katObject.put("algo", kat.getKa().getAlgorithm()); + katObject.put("secret", ByteUtil.bytesToHex(kat.getSecret())); + + PublicKey pkey = kat.getPublicKey(); + katObject.put("pubkey", keyObject(pkey)); + + PrivateKey skey = kat.getPrivateKey(); + katObject.put("privkey", keyObject(skey)); + return katObject; + } + + private Map<String, Object> kgtObject(KeyGeneratorTestable kgt) { + Map<String, Object> kgtObject = new LinkedHashMap<>(); + kgtObject.put("algo", kgt.getKpg().getAlgorithm()); + + Map<String, Object> keypair = new LinkedHashMap<>(); + if (kgt.getKeyPair() != null) { + PublicKey pkey = kgt.getKeyPair().getPublic(); + Map<String, Object> pubObject = keyObject(pkey); + keypair.put("pubkey", pubObject); + + PrivateKey skey = kgt.getKeyPair().getPrivate(); + Map<String, Object> privObject = keyObject(skey); + keypair.put("privkey", privObject); + } + + kgtObject.put("keypair", keypair); + return kgtObject; + } + + private Map<String, Object> sigObject(SignatureTestable sig) { + Map<String, Object> sigObject = new LinkedHashMap<>(); + sigObject.put("algo", sig.getSig().getAlgorithm()); + sigObject.put("verified", sig.getVerified()); + sigObject.put("raw", ByteUtil.bytesToHex(sig.getSignature())); + return sigObject; + } + + private String causeObject(Object cause) { + if (cause == null) { + return ""; + } else if (cause instanceof Exception) { + Exception ex = ((Exception) cause); + return ex.getClass().getCanonicalName() + " : " + ex.getMessage(); + } else { + return cause.toString(); + } + } + + @Override + protected Map<String, Object> testableObject(Testable t) { + Map<String, Object> result = new LinkedHashMap<>(); + if (t instanceof StandaloneTestable) { + StandaloneTestable<?> testable = (StandaloneTestable) t; + if (t instanceof KeyGeneratorTestable) { + result.put("type", "key-pair-generator"); + result.put("key-pair-generator", kgtObject((KeyGeneratorTestable) t)); + } else if (t instanceof KeyAgreementTestable) { + result.put("type", "key-agreement"); + result.put("key-agreement", kaObject((KeyAgreementTestable) t)); + } else if (t instanceof SignatureTestable) { + result.put("type", "signature"); + result.put("signature", sigObject((SignatureTestable) t)); + } + result.put("stage", testable.getStage().name()); + result.put("exception", causeObject(testable.getException()) + causeObject(testable.errorCause())); + } + return result; + } + + @Override + protected Map<String, Object> deviceObject(TestSuite suite) { + if (suite instanceof StandaloneTestSuite) { + StandaloneTestSuite standaloneSuite = (StandaloneTestSuite) suite; + Map<String, Object> result = new LinkedHashMap<>(); + result.put("type", "library"); + result.put("ectester", ECTesterStandalone.VERSION); + result.put("name", standaloneSuite.getLibrary().name()); + return result; + } + return null; + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/test/base/KeyAgreementTest.java b/standalone/src/main/java/cz/crcs/ectester/standalone/test/base/KeyAgreementTest.java new file mode 100644 index 0000000..fd48212 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/test/base/KeyAgreementTest.java @@ -0,0 +1,59 @@ +package cz.crcs.ectester.standalone.test.base; + +import cz.crcs.ectester.common.test.Result; +import cz.crcs.ectester.common.test.SimpleTest; +import cz.crcs.ectester.common.test.TestCallback; + +import java.util.Arrays; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class KeyAgreementTest extends SimpleTest<KeyAgreementTestable> { + private KeyAgreementTest(KeyAgreementTestable ka, TestCallback<KeyAgreementTestable> callback) { + super(ka, callback); + } + + public static KeyAgreementTest match(KeyAgreementTestable ka, byte[] expectedSecret) { + return new KeyAgreementTest(ka, new TestCallback<KeyAgreementTestable>() { + @Override + public Result apply(KeyAgreementTestable ka) { + if (Arrays.equals(ka.getSecret(), expectedSecret)) { + return new Result(Result.Value.SUCCESS, "The KeyAgreement result matched the expected derived secret."); + } else { + return new Result(Result.Value.FAILURE, "The KeyAgreement result did not match the expected derived secret."); + } + } + }); + } + + public static KeyAgreementTest expect(KeyAgreementTestable ka, Result.ExpectedValue expected) { + return new KeyAgreementTest(ka, new TestCallback<KeyAgreementTestable>() { + @Override + public Result apply(KeyAgreementTestable keyAgreementTestable) { + Result.Value value = Result.Value.fromExpected(expected, keyAgreementTestable.ok(), keyAgreementTestable.error()); + return new Result(value, value.description()); + } + }); + } + + public static KeyAgreementTest expectError(KeyAgreementTestable ka, Result.ExpectedValue expected) { + return new KeyAgreementTest(ka, new TestCallback<KeyAgreementTestable>() { + @Override + public Result apply(KeyAgreementTestable keyAgreementTestable) { + Result.Value value = Result.Value.fromExpected(expected, keyAgreementTestable.ok(), false); + return new Result(value, value.description()); + } + }); + } + + public static KeyAgreementTest function(KeyAgreementTestable ka, TestCallback<KeyAgreementTestable> callback) { + return new KeyAgreementTest(ka, callback); + } + + @Override + public String getDescription() { + String keyAlgo = testable.getKeyAlgorithm() == null ? "" : " (" + testable.getKeyAlgorithm() + ")"; + return "KeyAgreement " + testable.getKa().getAlgorithm() + keyAlgo; + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/test/base/KeyAgreementTestable.java b/standalone/src/main/java/cz/crcs/ectester/standalone/test/base/KeyAgreementTestable.java new file mode 100644 index 0000000..7fd1c5a --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/test/base/KeyAgreementTestable.java @@ -0,0 +1,179 @@ +package cz.crcs.ectester.standalone.test.base; + +import javax.crypto.KeyAgreement; +import javax.crypto.SecretKey; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.ECParameterSpec; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class KeyAgreementTestable extends StandaloneTestable<KeyAgreementTestable.KeyAgreementStage> { + private KeyAgreement ka; + private ECPrivateKey privateKey; + private ECPublicKey publicKey; + private KeyGeneratorTestable kgtPrivate; + private KeyGeneratorTestable kgtPublic; + private AlgorithmParameterSpec spec; + private String keyAlgo; + private byte[] secret; + private SecretKey derived; + + public KeyAgreementTestable(KeyAgreement ka, ECPrivateKey privateKey, ECPublicKey publicKey) { + this.ka = ka; + this.privateKey = privateKey; + this.publicKey = publicKey; + } + + public KeyAgreementTestable(KeyAgreement ka, ECPrivateKey privateKey, ECPublicKey publicKey, String keyAlgo) { + this(ka, privateKey, publicKey); + this.keyAlgo = keyAlgo; + } + + public KeyAgreementTestable(KeyAgreement ka, ECPrivateKey privateKey, ECPublicKey publicKey, ECParameterSpec spec) { + this(ka, privateKey, publicKey); + this.spec = spec; + } + + public KeyAgreementTestable(KeyAgreement ka, ECPrivateKey privateKey, ECPublicKey publicKey, ECParameterSpec spec, String keyAlgo) { + this(ka, privateKey, publicKey, spec); + this.keyAlgo = keyAlgo; + } + + public KeyAgreementTestable(KeyAgreement ka, KeyGeneratorTestable kgt, ECPrivateKey privateKey, ECParameterSpec spec) { + this(ka, privateKey, null, spec); + this.kgtPublic = kgt; + } + + public KeyAgreementTestable(KeyAgreement ka, KeyGeneratorTestable kgt, ECPrivateKey privateKey, ECParameterSpec spec, String keyAlgo) { + this(ka, kgt, privateKey, spec); + this.keyAlgo = keyAlgo; + } + + public KeyAgreementTestable(KeyAgreement ka, ECPublicKey publicKey, KeyGeneratorTestable kgt, ECParameterSpec spec) { + this(ka, null, publicKey, spec); + this.kgtPrivate = kgt; + } + + public KeyAgreementTestable(KeyAgreement ka, ECPublicKey publicKey, KeyGeneratorTestable kgt, ECParameterSpec spec, String keyAlgo) { + this(ka, publicKey, kgt, spec); + this.keyAlgo = keyAlgo; + } + + public KeyAgreementTestable(KeyAgreement ka, KeyGeneratorTestable privKgt, KeyGeneratorTestable pubKgt, ECParameterSpec spec) { + this(ka, (ECPrivateKey) null, null, spec); + this.kgtPrivate = privKgt; + this.kgtPublic = pubKgt; + } + + public KeyAgreementTestable(KeyAgreement ka, KeyGeneratorTestable privKgt, KeyGeneratorTestable pubKgt, ECParameterSpec spec, String keyAlgo) { + this(ka, privKgt, pubKgt, spec); + this.keyAlgo = keyAlgo; + } + + public String getKeyAlgorithm() { + return keyAlgo; + } + + public KeyAgreement getKa() { + return ka; + } + + public ECPublicKey getPublicKey() { + return publicKey; + } + + public ECPrivateKey getPrivateKey() { + return privateKey; + } + + public byte[] getSecret() { + if (!hasRun) { + return null; + } + return secret; + } + + public SecretKey getDerivedKey() { + if (!hasRun) { + return null; + } + return derived; + } + + @Override + public void run() { + try { + stage = KeyAgreementStage.GetPrivate; + if (kgtPrivate != null) { + privateKey = (ECPrivateKey) kgtPrivate.getKeyPair().getPrivate(); + } + + stage = KeyAgreementStage.GetPublic; + if (kgtPublic != null) { + publicKey = (ECPublicKey) kgtPublic.getKeyPair().getPublic(); + } + + stage = KeyAgreementStage.Init; + try { + if (spec != null) { + ka.init(privateKey, spec); + } else { + ka.init(privateKey); + } + } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { + failOnException(e); + return; + } + + stage = KeyAgreementStage.DoPhase; + try { + ka.doPhase(publicKey, true); + } catch (IllegalStateException | InvalidKeyException e) { + failOnException(e); + return; + } + + stage = KeyAgreementStage.GenerateSecret; + try { + if (keyAlgo != null) { + derived = ka.generateSecret(keyAlgo); + secret = derived.getEncoded(); + } else { + secret = ka.generateSecret(); + } + } catch (IllegalStateException | UnsupportedOperationException e) { + failOnException(e); + return; + } + + ok = true; + } catch (Exception ex) { + ok = false; + error = true; + errorCause = ex; + } + hasRun = true; + } + + @Override + public void reset() { + super.reset(); + try { + ka = KeyAgreement.getInstance(ka.getAlgorithm(), ka.getProvider()); + } catch (NoSuchAlgorithmException e) { } + } + + public enum KeyAgreementStage { + GetPrivate, + GetPublic, + Init, + DoPhase, + GenerateSecret + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/test/base/KeyGeneratorTest.java b/standalone/src/main/java/cz/crcs/ectester/standalone/test/base/KeyGeneratorTest.java new file mode 100644 index 0000000..32f82cb --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/test/base/KeyGeneratorTest.java @@ -0,0 +1,43 @@ +package cz.crcs.ectester.standalone.test.base; + +import cz.crcs.ectester.common.test.Result; +import cz.crcs.ectester.common.test.SimpleTest; +import cz.crcs.ectester.common.test.TestCallback; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class KeyGeneratorTest extends SimpleTest<KeyGeneratorTestable> { + private KeyGeneratorTest(KeyGeneratorTestable kg, TestCallback<KeyGeneratorTestable> callback) { + super(kg, callback); + } + + public static KeyGeneratorTest expect(KeyGeneratorTestable kg, Result.ExpectedValue expected) { + return new KeyGeneratorTest(kg, new TestCallback<KeyGeneratorTestable>() { + @Override + public Result apply(KeyGeneratorTestable keyGenerationTestable) { + Result.Value value = Result.Value.fromExpected(expected, keyGenerationTestable.ok(), keyGenerationTestable.error()); + return new Result(value, value.description()); + } + }); + } + + public static KeyGeneratorTest expectError(KeyGeneratorTestable kg, Result.ExpectedValue expected) { + return new KeyGeneratorTest(kg, new TestCallback<KeyGeneratorTestable>() { + @Override + public Result apply(KeyGeneratorTestable keyGenerationTestable) { + Result.Value value = Result.Value.fromExpected(expected, keyGenerationTestable.ok(), false); + return new Result(value, value.description()); + } + }); + } + + public static KeyGeneratorTest function(KeyGeneratorTestable ka, TestCallback<KeyGeneratorTestable> callback) { + return new KeyGeneratorTest(ka, callback); + } + + @Override + public String getDescription() { + return "KeyPairGenerator " + testable.getKpg().getAlgorithm(); + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/test/base/KeyGeneratorTestable.java b/standalone/src/main/java/cz/crcs/ectester/standalone/test/base/KeyGeneratorTestable.java new file mode 100644 index 0000000..c05d6e3 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/test/base/KeyGeneratorTestable.java @@ -0,0 +1,70 @@ +package cz.crcs.ectester.standalone.test.base; + +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.spec.ECParameterSpec; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class KeyGeneratorTestable extends StandaloneTestable<KeyGeneratorTestable.KeyGeneratorStage> { + private KeyPair kp; + private KeyPairGenerator kpg; + private int keysize = 0; + private ECParameterSpec spec = null; + + public KeyGeneratorTestable(KeyPairGenerator kpg) { + this.kpg = kpg; + } + + public KeyGeneratorTestable(KeyPairGenerator kpg, int keysize) { + this.kpg = kpg; + this.keysize = keysize; + } + + public KeyGeneratorTestable(KeyPairGenerator kpg, ECParameterSpec spec) { + this.kpg = kpg; + this.spec = spec; + } + + public KeyPairGenerator getKpg() { + return kpg; + } + + public KeyPair getKeyPair() { + return kp; + } + + @Override + public void run() { + try { + stage = KeyGeneratorStage.Init; + try { + if (spec != null) { + kpg.initialize(spec); + } else if (keysize != 0) { + kpg.initialize(keysize); + } + } catch (InvalidAlgorithmParameterException e) { + failOnException(e); + return; + } + + stage = KeyGeneratorStage.GenKeyPair; + kp = kpg.genKeyPair(); + + ok = true; + } catch (Exception ex) { + ok = false; + error = true; + errorCause = ex; + } + hasRun = true; + } + + public enum KeyGeneratorStage { + Init, + GenKeyPair + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/test/base/PerformanceTest.java b/standalone/src/main/java/cz/crcs/ectester/standalone/test/base/PerformanceTest.java new file mode 100644 index 0000000..258ca12 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/test/base/PerformanceTest.java @@ -0,0 +1,109 @@ +package cz.crcs.ectester.standalone.test.base; + +import cz.crcs.ectester.common.test.BaseTestable; +import cz.crcs.ectester.common.test.Result; +import cz.crcs.ectester.common.test.SimpleTest; +import cz.crcs.ectester.common.test.TestCallback; + +import java.util.Arrays; + +/** + * @author David Hofman + */ +public class PerformanceTest extends SimpleTest<BaseTestable> { + private long[] times; + private long mean; + private long median; + private long mode; + private final int count; + private final String desc; + + private PerformanceTest(BaseTestable testable, int count, String desc) { + super(testable, new TestCallback<BaseTestable>() { + @Override + public Result apply(BaseTestable testable) { + return new Result(Result.Value.SUCCESS); + } + }); + this.count = count; + this.desc = desc; + } + + public static PerformanceTest repeat(BaseTestable testable, int count) { + return new PerformanceTest(testable, count, null); + } + + public static PerformanceTest repeat(BaseTestable testable, String desc, int count) { + return new PerformanceTest(testable, count, desc); + } + + @Override + public String getDescription() { + String rest = String.format("Mean = %d ns, Median = %d ns, Mode = %d ns", mean, median, mode); + return (desc == null ? rest : desc + " (" + rest + ")"); + } + + @Override + protected void runSelf() { + + times = new long[count]; + for (int i = 0; i < count; ++i) { + times[i] = measureTime(); + } + + mean = Arrays.stream(times).sum() / count; + + long[] sorted = times.clone(); + Arrays.sort(sorted); + if (count % 2 == 0) { + median = (sorted[(count / 2) - 1] + sorted[count / 2]) / 2; + } else { + median = sorted[count / 2]; + } + + long max_occurrences = 0; + int i = 0; + while (i < count) { + long current_value = sorted[i]; + long current_occurrences = 0; + while (i < count && sorted[i] == current_value) { + i++; + current_occurrences++; + } + if (current_occurrences > max_occurrences) { + max_occurrences = current_occurrences; + mode = current_value; + } + } + result = callback.apply(testable); + } + + public long getCount() { + return count; + } + + public long[] getTimes() { + return times; + } + + public long getMean() { + return mean; + } + + public long getMedian() { + return median; + } + + public long getMode() { + return mode; + } + + private long measureTime() { + if(testable.hasRun()) { + testable.reset(); + } + long startTime = System.nanoTime(); + testable.run(); + return System.nanoTime() - startTime; + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/test/base/SignatureTest.java b/standalone/src/main/java/cz/crcs/ectester/standalone/test/base/SignatureTest.java new file mode 100644 index 0000000..a817691 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/test/base/SignatureTest.java @@ -0,0 +1,43 @@ +package cz.crcs.ectester.standalone.test.base; + +import cz.crcs.ectester.common.test.Result; +import cz.crcs.ectester.common.test.SimpleTest; +import cz.crcs.ectester.common.test.TestCallback; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class SignatureTest extends SimpleTest<SignatureTestable> { + private SignatureTest(SignatureTestable sig, TestCallback<SignatureTestable> callback) { + super(sig, callback); + } + + public static SignatureTest expect(SignatureTestable kg, Result.ExpectedValue expected) { + return new SignatureTest(kg, new TestCallback<SignatureTestable>() { + @Override + public Result apply(SignatureTestable signatureTestable) { + Result.Value value = Result.Value.fromExpected(expected, signatureTestable.ok(), signatureTestable.error()); + return new Result(value, value.description()); + } + }); + } + + public static SignatureTest expectError(SignatureTestable kg, Result.ExpectedValue expected) { + return new SignatureTest(kg, new TestCallback<SignatureTestable>() { + @Override + public Result apply(SignatureTestable signatureTestable) { + Result.Value value = Result.Value.fromExpected(expected, signatureTestable.ok(), false); + return new Result(value, value.description()); + } + }); + } + + public static SignatureTest function(SignatureTestable ka, TestCallback<SignatureTestable> callback) { + return new SignatureTest(ka, callback); + } + + @Override + public String getDescription() { + return "Signature " + testable.getSig().getAlgorithm(); + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/test/base/SignatureTestable.java b/standalone/src/main/java/cz/crcs/ectester/standalone/test/base/SignatureTestable.java new file mode 100644 index 0000000..fe81b10 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/test/base/SignatureTestable.java @@ -0,0 +1,143 @@ +package cz.crcs.ectester.standalone.test.base; + +import java.security.InvalidKeyException; +import java.security.SecureRandom; +import java.security.Signature; +import java.security.SignatureException; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class SignatureTestable extends StandaloneTestable<SignatureTestable.SignatureStage> { + private Signature sig; + private ECPrivateKey signKey; + private ECPublicKey verifyKey; + private KeyGeneratorTestable kgt; + private byte[] data; + private byte[] signature; + private boolean verified; + + public SignatureTestable(Signature sig, ECPrivateKey signKey, ECPublicKey verifyKey, byte[] data) { + this.sig = sig; + this.signKey = signKey; + this.verifyKey = verifyKey; + this.data = data; + if (data == null) { + SecureRandom random = new SecureRandom(); + this.data = new byte[64]; + random.nextBytes(this.data); + } + } + + public SignatureTestable(Signature sig, ECPublicKey verifyKey, byte[] data, byte[] signature) { + this.sig = sig; + this.verifyKey = verifyKey; + this.data = data; + this.signature = signature; + } + + public SignatureTestable(Signature sig, KeyGeneratorTestable kgt, byte[] data) { + this(sig, (ECPrivateKey) null, null, data); + this.kgt = kgt; + } + + public Signature getSig() { + return sig; + } + + public byte[] getData() { + return data; + } + + public byte[] getSignature() { + return signature; + } + + public boolean getVerified() { + return verified; + } + + @Override + public void run() { + try { + stage = SignatureStage.GetKeys; + if (kgt != null) { + signKey = (ECPrivateKey) kgt.getKeyPair().getPrivate(); + verifyKey = (ECPublicKey) kgt.getKeyPair().getPublic(); + } + + if(signKey != null) { + stage = SignatureStage.InitSign; + try { + sig.initSign(signKey); + } catch (InvalidKeyException e) { + failOnException(e); + return; + } + + stage = SignatureStage.UpdateSign; + try { + sig.update(data); + } catch (SignatureException e) { + failOnException(e); + return; + } + + stage = SignatureStage.Sign; + try { + signature = sig.sign(); + } catch (SignatureException e) { + failOnException(e); + return; + } + + ok = true; + } + + if (verifyKey != null) { + stage = SignatureStage.InitVerify; + try { + sig.initVerify(verifyKey); + } catch (InvalidKeyException e) { + failOnException(e); + return; + } + + stage = SignatureStage.UpdateVerify; + try { + sig.update(data); + } catch (SignatureException e) { + failOnException(e); + return; + } + + stage = SignatureStage.Verify; + try { + verified = sig.verify(signature); + } catch (SignatureException e) { + failOnException(e); + return; + } + + ok = verified; + } + } catch (Exception ex) { + ok = false; + error = true; + errorCause = ex; + } + hasRun = true; + } + + public enum SignatureStage { + GetKeys, + InitSign, + UpdateSign, + Sign, + InitVerify, + UpdateVerify, + Verify + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/test/base/StandaloneTestable.java b/standalone/src/main/java/cz/crcs/ectester/standalone/test/base/StandaloneTestable.java new file mode 100644 index 0000000..47bffc1 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/test/base/StandaloneTestable.java @@ -0,0 +1,25 @@ +package cz.crcs.ectester.standalone.test.base; + +import cz.crcs.ectester.common.test.BaseTestable; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class StandaloneTestable<T extends Enum<T>> extends BaseTestable { + protected T stage; + protected Exception exception; + + public T getStage() { + return stage; + } + + public Exception getException() { + return exception; + } + + protected void failOnException(Exception ex) { + ok = false; + hasRun = true; + exception = ex; + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneCofactorSuite.java b/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneCofactorSuite.java new file mode 100644 index 0000000..52b0fbf --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneCofactorSuite.java @@ -0,0 +1,111 @@ +package cz.crcs.ectester.standalone.test.suites; + +import cz.crcs.ectester.common.cli.TreeCommandLine; +import cz.crcs.ectester.common.ec.EC_Curve; +import cz.crcs.ectester.common.ec.EC_Key; +import cz.crcs.ectester.common.output.TestWriter; +import cz.crcs.ectester.common.test.CompoundTest; +import cz.crcs.ectester.common.test.Result; +import cz.crcs.ectester.common.test.Test; +import cz.crcs.ectester.common.util.ECUtil; +import cz.crcs.ectester.data.EC_Store; +import cz.crcs.ectester.standalone.ECTesterStandalone; +import cz.crcs.ectester.standalone.consts.KeyAgreementIdent; +import cz.crcs.ectester.standalone.consts.KeyPairGeneratorIdent; +import cz.crcs.ectester.standalone.test.base.KeyAgreementTest; +import cz.crcs.ectester.standalone.test.base.KeyAgreementTestable; +import cz.crcs.ectester.standalone.test.base.KeyGeneratorTest; +import cz.crcs.ectester.standalone.test.base.KeyGeneratorTestable; + +import javax.crypto.KeyAgreement; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECParameterSpec; +import java.util.*; + +/** + * @author David Hofman + */ +public class StandaloneCofactorSuite extends StandaloneTestSuite { + public StandaloneCofactorSuite(TestWriter writer, ECTesterStandalone.Config cfg, TreeCommandLine cli) { + super(writer, cfg, cli, "cofactor", "The cofactor test suite tests whether the library correctly rejects points on the curve", + "but not in the subgroup generated by the generator (so of small order, dividing the cofactor) during ECDH.", + "Supports options:", "\t - gt/kpg-type", "\t - kt/ka-type (select multiple types by separating them with commas)"); + } + + @Override + protected void runTests() throws Exception { + String kpgAlgo = cli.getOptionValue("test.kpg-type"); + String kaAlgo = cli.getOptionValue("test.ka-type"); + List<String> kaTypes = kaAlgo != null ? Arrays.asList(kaAlgo.split(",")) : new ArrayList<>(); + + KeyPairGeneratorIdent kpgIdent; + if (kpgAlgo == null) { + // try EC, if not, fail with: need to specify kpg algo. + Optional<KeyPairGeneratorIdent> kpgIdentOpt = cfg.selected.getKPGs().stream() + .filter((ident) -> ident.contains("EC")) + .findFirst(); + if (kpgIdentOpt.isPresent()) { + kpgIdent = kpgIdentOpt.get(); + } else { + System.err.println("The default KeyPairGenerator algorithm type of \"EC\" was not found. Need to specify a type."); + return; + } + } else { + // try the specified, if not, fail with: wrong kpg algo/not found. + Optional<KeyPairGeneratorIdent> kpgIdentOpt = cfg.selected.getKPGs().stream() + .filter((ident) -> ident.contains(kpgAlgo)) + .findFirst(); + if (kpgIdentOpt.isPresent()) { + kpgIdent = kpgIdentOpt.get(); + } else { + System.err.println("The KeyPairGenerator algorithm type of \"" + kpgAlgo + "\" was not found."); + return; + } + } + + Map<String, EC_Key.Public> pubkeys = EC_Store.getInstance().getObjects(EC_Key.Public.class, "cofactor"); + Map<EC_Curve, List<EC_Key.Public>> curveList = EC_Store.mapKeyToCurve(pubkeys.values()); + for (Map.Entry<EC_Curve, List<EC_Key.Public>> e : curveList.entrySet()) { + EC_Curve curve = e.getKey(); + List<EC_Key.Public> keys = e.getValue(); + + KeyPairGenerator kpg = kpgIdent.getInstance(cfg.selected.getProvider()); + ECParameterSpec spec = curve.toSpec(); + KeyGeneratorTestable kgt = new KeyGeneratorTestable(kpg, spec); + + Test generate = KeyGeneratorTest.expectError(kgt, Result.ExpectedValue.ANY); + runTest(generate); + KeyPair kp = kgt.getKeyPair(); + if(kp == null) { + Test generateFail = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Generating KeyPair has failed on " + curve.getId() + ". " + "KeyAgreement tests will be skipped.", generate); + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Cofactor test of " + curve.getId() + ".", generateFail)); + continue; + } + Test generateSuccess = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Generate keypair.", generate); + ECPrivateKey ecpriv = (ECPrivateKey) kp.getPrivate(); + + List<Test> allKaTests = new LinkedList<>(); + for (KeyAgreementIdent kaIdent : cfg.selected.getKAs()) { + if (kaAlgo == null || kaIdent.containsAny(kaTypes)) { + List<Test> specificKaTests = new LinkedList<>(); + for (EC_Key.Public pub : keys) { + ECPublicKey ecpub = ECUtil.toPublicKey(pub); + KeyAgreement ka = kaIdent.getInstance(cfg.selected.getProvider()); + KeyAgreementTestable testable = new KeyAgreementTestable(ka, ecpriv, ecpub); + Test keyAgreement = KeyAgreementTest.expectError(testable, Result.ExpectedValue.FAILURE); + specificKaTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, pub.getId() + " cofactor key test.", keyAgreement)); + } + allKaTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Perform " + kaIdent.getName() + " with public points on non-generator subgroup.", specificKaTests.toArray(new Test[0]))); + } + } + if(allKaTests.isEmpty()) { + allKaTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "None of the specified key agreement types is supported by the library.")); + } + Test tests = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Do tests.", allKaTests.toArray(new Test[0])); + doTest(CompoundTest.greedyAllTry(Result.ExpectedValue.SUCCESS, "Cofactor test of " + curve.getId() + ".", generateSuccess, tests)); + } + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneCompositeSuite.java b/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneCompositeSuite.java new file mode 100644 index 0000000..c59d864 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneCompositeSuite.java @@ -0,0 +1,210 @@ +package cz.crcs.ectester.standalone.test.suites; + +import cz.crcs.ectester.common.cli.TreeCommandLine; +import cz.crcs.ectester.common.ec.EC_Curve; +import cz.crcs.ectester.common.ec.EC_Key; +import cz.crcs.ectester.common.output.TestWriter; +import cz.crcs.ectester.common.test.CompoundTest; +import cz.crcs.ectester.common.test.Result; +import cz.crcs.ectester.common.test.Test; +import cz.crcs.ectester.common.util.ECUtil; +import cz.crcs.ectester.data.EC_Store; +import cz.crcs.ectester.standalone.ECTesterStandalone; +import cz.crcs.ectester.standalone.consts.KeyAgreementIdent; +import cz.crcs.ectester.standalone.consts.KeyPairGeneratorIdent; +import cz.crcs.ectester.standalone.consts.SignatureIdent; +import cz.crcs.ectester.standalone.test.base.*; + +import javax.crypto.KeyAgreement; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.Signature; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECParameterSpec; +import java.util.*; + +/** + * @author David Hofman + */ +public class StandaloneCompositeSuite extends StandaloneTestSuite { + private String kpgAlgo; + private String kaAlgo; + private String sigAlgo; + private List<String> kaTypes; + private List<String> sigTypes; + + public StandaloneCompositeSuite(TestWriter writer, ECTesterStandalone.Config cfg, TreeCommandLine cli) { + super(writer, cfg, cli, "composite", "The composite suite runs ECDH over curves with composite order.", + "Various types of compositeness is tested: smooth numbers, Carmichael pseudo-prime, prime square, product of two large primes.", + "Supports options:", + "\t - gt/kpg-type", + "\t - kt/ka-type (select multiple types by separating them with commas)", + "\t - st/sig-type (select multiple types by separating them with commas)"); + } + + @Override + protected void runTests() throws Exception { + kpgAlgo = cli.getOptionValue("test.kpg-type"); + kaAlgo = cli.getOptionValue("test.ka-type"); + sigAlgo = cli.getOptionValue("test.sig-type"); + kaTypes = kaAlgo != null ? Arrays.asList(kaAlgo.split(",")) : new ArrayList<>(); + sigTypes = sigAlgo != null ? Arrays.asList(sigAlgo.split(",")) : new ArrayList<>(); + + KeyPairGeneratorIdent kpgIdent; + if (kpgAlgo == null) { + // try EC, if not, fail with: need to specify kpg algo. + Optional<KeyPairGeneratorIdent> kpgIdentOpt = cfg.selected.getKPGs().stream() + .filter((ident) -> ident.contains("EC")) + .findFirst(); + if (kpgIdentOpt.isPresent()) { + kpgIdent = kpgIdentOpt.get(); + } else { + System.err.println("The default KeyPairGenerator algorithm type of \"EC\" was not found. Need to specify a type."); + return; + } + } else { + // try the specified, if not, fail with: wrong kpg algo/not found. + Optional<KeyPairGeneratorIdent> kpgIdentOpt = cfg.selected.getKPGs().stream() + .filter((ident) -> ident.contains(kpgAlgo)) + .findFirst(); + if (kpgIdentOpt.isPresent()) { + kpgIdent = kpgIdentOpt.get(); + } else { + System.err.println("The KeyPairGenerator algorithm type of \"" + kpgAlgo + "\" was not found."); + return; + } + } + KeyPairGenerator kpg = kpgIdent.getInstance(cfg.selected.getProvider()); + + Map<String, EC_Key.Public> keys = EC_Store.getInstance().getObjects(EC_Key.Public.class, "composite"); + Map<EC_Curve, List<EC_Key.Public>> mappedKeys = EC_Store.mapKeyToCurve(keys.values()); + for (Map.Entry<EC_Curve, List<EC_Key.Public>> curveKeys : mappedKeys.entrySet()) { + EC_Curve curve = curveKeys.getKey(); + ECParameterSpec spec = curve.toSpec(); + + //Generate KeyPair + KeyGeneratorTestable kgt = new KeyGeneratorTestable(kpg, spec); + Test generate = KeyGeneratorTest.expectError(kgt, Result.ExpectedValue.ANY); + runTest(generate); + KeyPair kp = kgt.getKeyPair(); + if(kp == null) { + Test generateFail = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Generating KeyPair has failed on " + curve.getId() + ". " + "KeyAgreement tests will be skipped.", generate); + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Composite test of " + curve.getId() + ".", generateFail)); + continue; + } + Test generateSuccess = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Generate keypair.", generate); + ECPrivateKey ecpriv = (ECPrivateKey) kp.getPrivate(); + + //Perform KeyAgreement tests + List<Test> allKaTests = new LinkedList<>(); + for (KeyAgreementIdent kaIdent : cfg.selected.getKAs()) { + if (kaAlgo == null || kaIdent.containsAny(kaTypes)) { + List<Test> specificKaTests = new LinkedList<>(); + for (EC_Key.Public pub : curveKeys.getValue()) { + ECPublicKey ecpub = ECUtil.toPublicKey(pub); + KeyAgreement ka = kaIdent.getInstance(cfg.selected.getProvider()); + KeyAgreementTestable testable = new KeyAgreementTestable(ka, ecpriv ,ecpub); + Test keyAgreement = KeyAgreementTest.expectError(testable, Result.ExpectedValue.FAILURE); + specificKaTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Composite test of " + curve.getId() + ", with generated private key, " + pub.getDesc(), keyAgreement)); + } + allKaTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Perform " + kaIdent.getName() + " with various public points.", specificKaTests.toArray(new Test[0]))); + } + } + if(allKaTests.isEmpty()) { + allKaTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "None of the specified key agreement types is supported by the library.")); + } + Test tests = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Do tests.", allKaTests.toArray(new Test[0])); + doTest(CompoundTest.greedyAllTry(Result.ExpectedValue.SUCCESS, "Composite test of " + curve.getId() + ".", generateSuccess, tests)); + } + + + Map<String, EC_Curve> results = EC_Store.getInstance().getObjects(EC_Curve.class, "composite"); + Map<String, List<EC_Curve>> groups = EC_Store.mapToPrefix(results.values()); + /* Test the whole curves with both keypairs generated by the library(no small-order public points provided). + */ + List<EC_Curve> wholeCurves = groups.entrySet().stream().filter((e) -> e.getKey().equals("whole")).findFirst().get().getValue(); + testGroup(wholeCurves, kpg, "Composite generator order", Result.ExpectedValue.FAILURE); + + /* Also test having a G of small order, so small R. + */ + List<EC_Curve> smallRCurves = groups.entrySet().stream().filter((e) -> e.getKey().equals("small")).findFirst().get().getValue(); + testGroup(smallRCurves, kpg, "Small generator order", Result.ExpectedValue.FAILURE); + + /* Test increasingly larger prime R, to determine where/if the behavior changes. + */ + List<EC_Curve> varyingCurves = groups.entrySet().stream().filter((e) -> e.getKey().equals("varying")).findFirst().get().getValue(); + testGroup(varyingCurves, kpg, null, Result.ExpectedValue.ANY); + + /* Also test having a G of large but composite order, R = p * q, + */ + List<EC_Curve> pqCurves = groups.entrySet().stream().filter((e) -> e.getKey().equals("pq")).findFirst().get().getValue(); + testGroup(pqCurves, kpg, null, Result.ExpectedValue.ANY); + + /* Also test having G or large order being a Carmichael pseudoprime, R = p * q * r, + */ + List<EC_Curve> ppCurves = groups.entrySet().stream().filter((e) -> e.getKey().equals("pp")).findFirst().get().getValue(); + testGroup(ppCurves, kpg, "Generator order = Carmichael pseudo-prime", Result.ExpectedValue.ANY); + + /* Also test rg0 curves. + */ + List<EC_Curve> rg0Curves = groups.entrySet().stream().filter((e) -> e.getKey().equals("rg0")).findFirst().get().getValue(); + testGroup(rg0Curves, kpg, null, Result.ExpectedValue.ANY); + } + + private void testGroup(List<EC_Curve> curves, KeyPairGenerator kpg, String testName, Result.ExpectedValue dhValue) throws Exception { + for (EC_Curve curve : curves) { + String description; + if (testName == null) { + description = curve.getDesc() + " test of " + curve.getId() + "."; + } else { + description = testName + " test of " + curve.getId() + "."; + } + + //generate KeyPair + KeyGeneratorTestable kgt = new KeyGeneratorTestable(kpg, curve.toSpec()); + Test generate = KeyGeneratorTest.expectError(kgt, Result.ExpectedValue.ANY); + runTest(generate); + KeyPair kp = kgt.getKeyPair(); + if(kp == null) { + Test generateFail = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Generating KeyPair has failed on " + curve.getId() + + ". " + " Other tests will be skipped.", generate); + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, description, generateFail)); + continue; + } + Test generateSuccess = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Generate keypair.", generate); + ECPrivateKey ecpriv = (ECPrivateKey) kp.getPrivate(); + ECPublicKey ecpub = (ECPublicKey) kp.getPublic(); + + //perform KeyAgreement tests + List<Test> kaTests = new LinkedList<>(); + for (KeyAgreementIdent kaIdent : cfg.selected.getKAs()) { + if (kaAlgo == null || kaIdent.containsAny(kaTypes)) { + KeyAgreement ka = kaIdent.getInstance(cfg.selected.getProvider()); + KeyAgreementTestable testable = new KeyAgreementTestable(ka, ecpriv, ecpub); + kaTests.add(KeyAgreementTest.expectError(testable, dhValue)); + } + } + if(kaTests.isEmpty()) { + kaTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "None of the specified KeyAgreement types is supported by the library.")); + } + + //perform Signature tests + List<Test> sigTests = new LinkedList<>(); + for (SignatureIdent sigIdent : cfg.selected.getSigs()) { + if (sigAlgo == null || sigIdent.containsAny(sigTypes)) { + Signature sig = sigIdent.getInstance(cfg.selected.getProvider()); + SignatureTestable testable = new SignatureTestable(sig, ecpriv, ecpub, null); + sigTests.add(SignatureTest.expectError(testable, dhValue)); + } + } + if(sigTests.isEmpty()) { + sigTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "None of the specified Signature types is supported by the library.")); + } + + Test performKeyAgreements = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Perform specified KeyAgreements.", kaTests.toArray(new Test[0])); + Test performSignatures = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Perform specified Signatures.", sigTests.toArray(new Test[0])); + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, description, generateSuccess, performKeyAgreements, performSignatures)); + } + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneDefaultSuite.java b/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneDefaultSuite.java new file mode 100644 index 0000000..1c14ecc --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneDefaultSuite.java @@ -0,0 +1,108 @@ +package cz.crcs.ectester.standalone.test.suites; + +import cz.crcs.ectester.common.cli.TreeCommandLine; +import cz.crcs.ectester.common.ec.EC_Curve; +import cz.crcs.ectester.common.output.TestWriter; +import cz.crcs.ectester.common.test.Result; +import cz.crcs.ectester.data.EC_Store; +import cz.crcs.ectester.standalone.ECTesterStandalone; +import cz.crcs.ectester.standalone.consts.KeyAgreementIdent; +import cz.crcs.ectester.standalone.consts.KeyPairGeneratorIdent; +import cz.crcs.ectester.standalone.consts.SignatureIdent; +import cz.crcs.ectester.standalone.test.base.*; + +import javax.crypto.KeyAgreement; +import java.security.KeyPairGenerator; +import java.security.Signature; +import java.security.spec.ECParameterSpec; +import java.util.Optional; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class StandaloneDefaultSuite extends StandaloneTestSuite { + + public StandaloneDefaultSuite(TestWriter writer, ECTesterStandalone.Config cfg, TreeCommandLine cli) { + super(writer, cfg, cli, "default", "The default test suite run basic support of ECDH and ECDSA.", "Supports options:", "\t - gt/kpg-type", "\t - kt/ka-type", "\t - st/sig-type", "\t - key-type"); + } + + @Override + protected void runTests() throws Exception { + String kpgAlgo = cli.getOptionValue("test.kpg-type"); + String kaAlgo = cli.getOptionValue("test.ka-type"); + String sigAlgo = cli.getOptionValue("test.sig-type"); + String keyAlgo = cli.getOptionValue("test.key-type", "AES"); + + + KeyPairGeneratorIdent kpgIdent; + if (kpgAlgo == null) { + // try EC, if not, fail with: need to specify kpg algo. + Optional<KeyPairGeneratorIdent> kpgIdentOpt = cfg.selected.getKPGs().stream() + .filter((ident) -> ident.contains("EC")) + .findFirst(); + if (kpgIdentOpt.isPresent()) { + kpgIdent = kpgIdentOpt.get(); + } else { + System.err.println("The default KeyPairGenerator algorithm type of \"EC\" was not found. Need to specify a type."); + return; + } + } else { + // try the specified, if not, fail with: wrong kpg algo/not found. + Optional<KeyPairGeneratorIdent> kpgIdentOpt = cfg.selected.getKPGs().stream() + .filter((ident) -> ident.contains(kpgAlgo)) + .findFirst(); + if (kpgIdentOpt.isPresent()) { + kpgIdent = kpgIdentOpt.get(); + } else { + System.err.println("The KeyPairGenerator algorithm type of \"" + kpgAlgo + "\" was not found."); + return; + } + } + + KeyPairGenerator kpg = kpgIdent.getInstance(cfg.selected.getProvider()); + + KeyGeneratorTestable kgtOne; + KeyGeneratorTestable kgtOther; + ECParameterSpec spec = null; + if (cli.hasOption("test.bits")) { + int bits = Integer.parseInt(cli.getOptionValue("test.bits")); + kgtOne = new KeyGeneratorTestable(kpg, bits); + kgtOther = new KeyGeneratorTestable(kpg, bits); + } else if (cli.hasOption("test.named-curve")) { + String curveName = cli.getOptionValue("test.named-curve"); + EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, curveName); + if (curve == null) { + System.err.println("Curve not found: " + curveName); + return; + } + spec = curve.toSpec(); + kgtOne = new KeyGeneratorTestable(kpg, spec); + kgtOther = new KeyGeneratorTestable(kpg, spec); + } else { + kgtOne = new KeyGeneratorTestable(kpg); + kgtOther = new KeyGeneratorTestable(kpg); + } + + doTest(KeyGeneratorTest.expect(kgtOne, Result.ExpectedValue.SUCCESS)); + doTest(KeyGeneratorTest.expect(kgtOther, Result.ExpectedValue.SUCCESS)); + + for (KeyAgreementIdent kaIdent : cfg.selected.getKAs()) { + if (kaAlgo == null || kaIdent.contains(kaAlgo)) { + KeyAgreement ka = kaIdent.getInstance(cfg.selected.getProvider()); + KeyAgreementTestable testable; + if (kaIdent.requiresKeyAlgo()) { + testable = new KeyAgreementTestable(ka, kgtOne, kgtOther, spec, keyAlgo); + } else { + testable = new KeyAgreementTestable(ka, kgtOne, kgtOther, spec); + } + doTest(KeyAgreementTest.expect(testable, Result.ExpectedValue.SUCCESS)); + } + } + for (SignatureIdent sigIdent : cfg.selected.getSigs()) { + if (sigAlgo == null || sigIdent.contains(sigAlgo)) { + Signature sig = sigIdent.getInstance(cfg.selected.getProvider()); + doTest(SignatureTest.expect(new SignatureTestable(sig, kgtOne, null), Result.ExpectedValue.SUCCESS)); + } + } + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneDegenerateSuite.java b/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneDegenerateSuite.java new file mode 100644 index 0000000..9ab8a39 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneDegenerateSuite.java @@ -0,0 +1,121 @@ +package cz.crcs.ectester.standalone.test.suites; + +import cz.crcs.ectester.common.cli.TreeCommandLine; +import cz.crcs.ectester.common.ec.EC_Curve; +import cz.crcs.ectester.common.ec.EC_Key; +import cz.crcs.ectester.common.output.TestWriter; +import cz.crcs.ectester.common.test.CompoundTest; +import cz.crcs.ectester.common.test.Result; +import cz.crcs.ectester.common.test.Test; +import cz.crcs.ectester.common.util.ECUtil; +import cz.crcs.ectester.data.EC_Store; +import cz.crcs.ectester.standalone.ECTesterStandalone; +import cz.crcs.ectester.standalone.consts.KeyAgreementIdent; +import cz.crcs.ectester.standalone.consts.KeyPairGeneratorIdent; +import cz.crcs.ectester.standalone.test.base.KeyAgreementTest; +import cz.crcs.ectester.standalone.test.base.KeyAgreementTestable; +import cz.crcs.ectester.standalone.test.base.KeyGeneratorTest; +import cz.crcs.ectester.standalone.test.base.KeyGeneratorTestable; + +import javax.crypto.KeyAgreement; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECParameterSpec; +import java.util.*; + +/** + * @author David Hofman + */ +public class StandaloneDegenerateSuite extends StandaloneTestSuite { + public StandaloneDegenerateSuite(TestWriter writer, ECTesterStandalone.Config cfg, TreeCommandLine cli) { + super(writer, cfg, cli, "degenerate", "The degenerate suite tests whether the library rejects points outside of the curve during ECDH.", + "The tested points lie on a part of the plane for which some Edwards, Hessian and Huff form addition formulas degenerate into exponentiation in the base finite field.", + "Supports options:", "\t - gt/kpg-type", "\t - kt/ka-type (select multiple types by separating them with commas)"); + } + + @Override + protected void runTests() throws Exception { + String kpgAlgo = cli.getOptionValue("test.kpg-type"); + String kaAlgo = cli.getOptionValue("test.ka-type"); + List<String> kaTypes = kaAlgo != null ? Arrays.asList(kaAlgo.split(",")) : new ArrayList<>(); + + KeyPairGeneratorIdent kpgIdent; + if (kpgAlgo == null) { + // try EC, if not, fail with: need to specify kpg algo. + Optional<KeyPairGeneratorIdent> kpgIdentOpt = cfg.selected.getKPGs().stream() + .filter((ident) -> ident.contains("EC")) + .findFirst(); + if (kpgIdentOpt.isPresent()) { + kpgIdent = kpgIdentOpt.get(); + } else { + System.err.println("The default KeyPairGenerator algorithm type of \"EC\" was not found. Need to specify a type."); + return; + } + } else { + // try the specified, if not, fail with: wrong kpg algo/not found. + Optional<KeyPairGeneratorIdent> kpgIdentOpt = cfg.selected.getKPGs().stream() + .filter((ident) -> ident.contains(kpgAlgo)) + .findFirst(); + if (kpgIdentOpt.isPresent()) { + kpgIdent = kpgIdentOpt.get(); + } else { + System.err.println("The KeyPairGenerator algorithm type of \"" + kpgAlgo + "\" was not found."); + return; + } + } + + Map<String, EC_Key.Public> pubkeys = EC_Store.getInstance().getObjects(EC_Key.Public.class, "degenerate"); + Map<EC_Curve, List<EC_Key.Public>> curveList = EC_Store.mapKeyToCurve(pubkeys.values()); + for (Map.Entry<EC_Curve, List<EC_Key.Public>> e : curveList.entrySet()) { + EC_Curve curve = e.getKey(); + List<EC_Key.Public> keys = e.getValue(); + + KeyPairGenerator kpg = kpgIdent.getInstance(cfg.selected.getProvider()); + ECParameterSpec spec = curve.toSpec(); + KeyGeneratorTestable kgt = new KeyGeneratorTestable(kpg, spec); + + Test generateSuccess; + Test generate = KeyGeneratorTest.expectError(kgt, Result.ExpectedValue.ANY); + runTest(generate); + KeyPair kp = kgt.getKeyPair(); + if(kp != null) { + generateSuccess = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Generate keypair.", generate); + } else { //If KeyPair generation fails, try generating it on a default curve instead. Use this key only if it has the same domain parameters as our public key. + KeyGeneratorTestable kgtOnDefaultCurve = new KeyGeneratorTestable(kpg, curve.getBits()); + Test generateOnDefaultCurve = KeyGeneratorTest.expectError(kgtOnDefaultCurve, Result.ExpectedValue.ANY); + runTest(generateOnDefaultCurve); + kp = kgtOnDefaultCurve.getKeyPair(); + if(kp != null && ECUtil.equalKeyPairParameters((ECPrivateKey) kp.getPrivate(), ECUtil.toPublicKey(keys.get(0)))) { + generateSuccess = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Generate keypair.", generateOnDefaultCurve); + } else { + Test generateFail = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Generating KeyPair has failed on " + curve.getId() + ". " + "KeyAgreement tests will be skipped.", generate); + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Degenerate curve test of " + curve.getId() + ".", generateFail)); + continue; + } + } + ECPrivateKey ecpriv = (ECPrivateKey) kp.getPrivate(); + + List<Test> allKaTests = new LinkedList<>(); + for (KeyAgreementIdent kaIdent : cfg.selected.getKAs()) { + if (kaAlgo == null || kaIdent.containsAny(kaTypes)) { + List<Test> specificKaTests = new LinkedList<>(); + for (EC_Key.Public pub : keys) { + ECPublicKey ecpub = ECUtil.toPublicKey(pub); + KeyAgreement ka = kaIdent.getInstance(cfg.selected.getProvider()); + KeyAgreementTestable testable = new KeyAgreementTestable(ka, ecpriv, ecpub); + Test keyAgreement = KeyAgreementTest.expectError(testable, Result.ExpectedValue.FAILURE); + specificKaTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, pub.getId() + " degenerate key test.", keyAgreement)); + } + allKaTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Perform " + kaIdent.getName() + " with degenerate public points..", specificKaTests.toArray(new Test[0]))); + } + } + if(allKaTests.isEmpty()) { + allKaTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "None of the specified key agreement types is supported by the library.")); + } + Test tests = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Do tests.", allKaTests.toArray(new Test[0])); + doTest(CompoundTest.greedyAllTry(Result.ExpectedValue.SUCCESS, "Degenerate curve test of " + curve.getId() + ".", generateSuccess, tests)); + } + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneEdgeCasesSuite.java b/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneEdgeCasesSuite.java new file mode 100644 index 0000000..1900bea --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneEdgeCasesSuite.java @@ -0,0 +1,313 @@ +package cz.crcs.ectester.standalone.test.suites; + +import cz.crcs.ectester.common.cli.TreeCommandLine; +import cz.crcs.ectester.common.ec.*; +import cz.crcs.ectester.common.output.TestWriter; +import cz.crcs.ectester.common.test.CompoundTest; +import cz.crcs.ectester.common.test.Result; +import cz.crcs.ectester.common.test.Test; +import cz.crcs.ectester.common.test.TestCallback; +import cz.crcs.ectester.common.util.ByteUtil; +import cz.crcs.ectester.common.util.ECUtil; +import cz.crcs.ectester.data.EC_Store; +import cz.crcs.ectester.standalone.ECTesterStandalone; +import cz.crcs.ectester.standalone.consts.KeyAgreementIdent; +import cz.crcs.ectester.standalone.consts.KeyPairGeneratorIdent; +import cz.crcs.ectester.standalone.test.base.KeyAgreementTest; +import cz.crcs.ectester.standalone.test.base.KeyAgreementTestable; +import cz.crcs.ectester.standalone.test.base.KeyGeneratorTest; +import cz.crcs.ectester.standalone.test.base.KeyGeneratorTestable; + +import javax.crypto.KeyAgreement; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECParameterSpec; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author David Hofman + */ +public class StandaloneEdgeCasesSuite extends StandaloneTestSuite { + KeyAgreementIdent kaIdent; + + public StandaloneEdgeCasesSuite(TestWriter writer, ECTesterStandalone.Config cfg, TreeCommandLine cli) { + super(writer, cfg, cli, "edge-cases", "The edge-cases test suite tests various inputs to ECDH which may cause an implementation to achieve a certain edge-case state during it.", + "Some of the data is from the google/Wycheproof project. Tests include CVE-2017-10176 and CVE-2017-8932.", + "Also tests values of the private key and public key that would trigger the OpenSSL modular multiplication bug on the P-256 curve.", + "Various edge private key values are also tested.", + "Supports options:", + "\t - gt/kpg-type", + "\t - kt/ka-type"); + } + + @Override + protected void runTests() throws Exception { + String kaAlgo = cli.getOptionValue("test.ka-type"); + String kpgAlgo = cli.getOptionValue("test.kpg-type"); + + if (kaAlgo == null) { + // try ECDH, if not, fail with: need to specify ka algo. + Optional<KeyAgreementIdent> kaIdentOpt = cfg.selected.getKAs().stream() + .filter((ident) -> ident.contains("ECDH")) + .findFirst(); + if (kaIdentOpt.isPresent()) { + kaIdent = kaIdentOpt.get(); + } else { + System.err.println("The default KeyAgreement algorithm type of \"ECDH\" was not found. Need to specify a type."); + return; + } + } else { + // try the specified, if not, fail with: wrong ka algo/not found. + Optional<KeyAgreementIdent> kaIdentOpt = cfg.selected.getKAs().stream() + .filter((ident) -> ident.contains(kaAlgo)) + .findFirst(); + if (kaIdentOpt.isPresent()) { + kaIdent = kaIdentOpt.get(); + } else { + System.err.println("The KeyAgreement algorithm type of \"" + kaAlgo + "\" was not found."); + return; + } + } + + KeyPairGeneratorIdent kpgIdent; + if (kpgAlgo == null) { + // try EC, if not, fail with: need to specify kpg algo. + Optional<KeyPairGeneratorIdent> kpgIdentOpt = cfg.selected.getKPGs().stream() + .filter((ident) -> ident.contains("EC")) + .findFirst(); + if (kpgIdentOpt.isPresent()) { + kpgIdent = kpgIdentOpt.get(); + } else { + System.err.println("The default KeyPairGenerator algorithm type of \"EC\" was not found. Need to specify a type."); + return; + } + } else { + // try the specified, if not, fail with: wrong kpg algo/not found. + Optional<KeyPairGeneratorIdent> kpgIdentOpt = cfg.selected.getKPGs().stream() + .filter((ident) -> ident.contains(kpgAlgo)) + .findFirst(); + if (kpgIdentOpt.isPresent()) { + kpgIdent = kpgIdentOpt.get(); + } else { + System.err.println("The KeyPairGenerator algorithm type of \"" + kpgAlgo + "\" was not found."); + return; + } + } + KeyPairGenerator kpg = kpgIdent.getInstance(cfg.selected.getProvider()); + + Map<String, EC_KAResult> results = EC_Store.getInstance().getObjects(EC_KAResult.class, "wycheproof"); + Map<String, List<EC_KAResult>> groups = EC_Store.mapToPrefix(results.values()); + for (Map.Entry<String, List<EC_KAResult>> e : groups.entrySet()) { + String description = null; + switch (e.getKey()) { + case "addsub": + description = "Tests for addition-subtraction chains."; + break; + case "cve_2017_10176": + description = "Tests for CVE-2017-10176."; + break; + case "cve_2017_8932": + description = "Tests for CVE-2017-8932."; + break; + } + + List<Test> groupTests = new LinkedList<>(); + Map<EC_Curve, List<EC_KAResult>> curveList = EC_Store.mapResultToCurve(e.getValue()); + for (Map.Entry<EC_Curve, List<EC_KAResult>> c : curveList.entrySet()) { + EC_Curve curve = c.getKey(); + + List<Test> curveTests = new LinkedList<>(); + List<EC_KAResult> values = c.getValue(); + for (EC_KAResult value : values) { + String id = value.getId(); + String privkeyId = value.getOneKey(); + String pubkeyId = value.getOtherKey(); + ECPrivateKey ecpriv = ECUtil.toPrivateKey(EC_Store.getInstance().getObject(EC_Key.Private.class, privkeyId)); + ECPublicKey ecpub = ECUtil.toPublicKey(EC_Store.getInstance().getObject(EC_Key.Public.class, pubkeyId)); + + KeyAgreement ka = kaIdent.getInstance(cfg.selected.getProvider()); + KeyAgreementTestable testable = new KeyAgreementTestable(ka, ecpriv, ecpub); + Test ecdh = KeyAgreementTest.match(testable, value.getData(0)); + Test one = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Test " + id + ".", ecdh); + curveTests.add(one); + } + groupTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Tests on " + curve.getId() + ".", curveTests.toArray(new Test[0]))); + } + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, description, groupTests.toArray(new Test[0]))); + } + + { + EC_KAResult openssl_bug = EC_Store.getInstance().getObject(EC_KAResult.class, "misc", "openssl-bug"); + ECPrivateKey ecpriv = ECUtil.toPrivateKey(EC_Store.getInstance().getObject(EC_Key.Private.class, openssl_bug.getOtherKey())); + ECPublicKey ecpub = ECUtil.toPublicKey(EC_Store.getInstance().getObject(EC_Key.Public.class, openssl_bug.getOneKey())); + KeyAgreement ka = kaIdent.getInstance(cfg.selected.getProvider()); + KeyAgreementTestable testable = new KeyAgreementTestable(ka, ecpriv, ecpub); + Test ecdh = KeyAgreementTest.function(testable, new TestCallback<KeyAgreementTestable>() { + @Override + public Result apply(KeyAgreementTestable testable) { + if (!testable.ok()) { + return new Result(Result.Value.FAILURE, "ECDH was unsuccessful."); + } + if (ByteUtil.compareBytes(testable.getSecret(), 0, openssl_bug.getData(0), 0, testable.getSecret().length)) { + return new Result(Result.Value.FAILURE, "OpenSSL bug is present, derived secret matches example."); + } + return new Result(Result.Value.SUCCESS); + } + }); + + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Test OpenSSL modular reduction bug.", ecdh)); + } + + Map<String, EC_Curve> curveMap = EC_Store.getInstance().getObjects(EC_Curve.class, "secg"); + List<EC_Curve> curves = curveMap.entrySet().stream().filter((e) -> + e.getKey().endsWith("r1") && e.getValue().getField() == javacard.security.KeyPair.ALG_EC_FP).map(Map.Entry::getValue).collect(Collectors.toList()); + curves.add(EC_Store.getInstance().getObject(EC_Curve.class, "cofactor/cofactor128p2")); + curves.add(EC_Store.getInstance().getObject(EC_Curve.class, "cofactor/cofactor160p4")); + Random rand = new Random(); + for (EC_Curve curve : curves) { + ECParameterSpec spec = curve.toSpec(); + + //generate KeyPair + KeyGeneratorTestable kgt = new KeyGeneratorTestable(kpg, spec); + Test generate = KeyGeneratorTest.expectError(kgt, Result.ExpectedValue.ANY); + runTest(generate); + KeyPair kp = kgt.getKeyPair(); + if (kp == null) { + Test generateFail = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Generating KeyPair has failed on " + curve.getId() + + ". " + " Other tests will be skipped.", generate); + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Tests with edge-case private key values over" + curve.getId() + ".", generateFail)); + continue; + } + Test generateSuccess = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Generate KeyPair.", generate); + ECPublicKey ecpub = (ECPublicKey) kp.getPublic(); + + //perform ECDH tests + Test zeroS = ecdhTest(ecpub, BigInteger.ZERO, spec, "ECDH with S = 0.", Result.ExpectedValue.FAILURE); + Test oneS = ecdhTest(ecpub, BigInteger.ONE, spec, "ECDH with S = 1.", Result.ExpectedValue.FAILURE); + + byte[] rParam = curve.getParam(EC_Consts.PARAMETER_R)[0]; + BigInteger R = new BigInteger(1, rParam); + BigInteger smaller = new BigInteger(curve.getBits(), rand).mod(R); + BigInteger diff = R.divide(BigInteger.valueOf(10)); + BigInteger randDiff = new BigInteger(diff.bitLength(), rand).mod(diff); + BigInteger larger = R.add(randDiff); + BigInteger full = BigInteger.valueOf(1).shiftLeft(R.bitLength() - 1).subtract(BigInteger.ONE); + + BigInteger alternate = full; + for (int i = 0; i < R.bitLength(); i += 2) { + alternate = alternate.clearBit(i); + } + + BigInteger alternateOther = alternate.xor(full); + BigInteger rm1 = R.subtract(BigInteger.ONE); + BigInteger rp1 = R.add(BigInteger.ONE); + + Test alternateS = ecdhTest(ecpub, alternate, spec, "ECDH with S = 101010101...01010.", Result.ExpectedValue.SUCCESS); + Test alternateOtherS = ecdhTest(ecpub, alternateOther, spec, "ECDH with S = 010101010...10101.", Result.ExpectedValue.SUCCESS); + Test fullS = ecdhTest(ecpub, full, spec, "ECDH with S = 111111111...11111 (but < r).", Result.ExpectedValue.SUCCESS); + Test smallerS = ecdhTest(ecpub, smaller, spec, "ECDH with S < r.", Result.ExpectedValue.SUCCESS); + Test exactS = ecdhTest(ecpub, R, spec, "ECDH with S = r.", Result.ExpectedValue.FAILURE); + Test largeS = ecdhTest(ecpub, larger, spec, "ECDH with S > r.", Result.ExpectedValue.ANY); + Test rm1S = ecdhTest(ecpub, rm1, spec, "ECDH with S = r - 1.", Result.ExpectedValue.SUCCESS); + Test rp1S = ecdhTest(ecpub, rp1, spec, "ECDH with S = r + 1.", Result.ExpectedValue.ANY); + + byte[] k = curve.getParam(EC_Consts.PARAMETER_K)[0]; + BigInteger K = new BigInteger(1, k); + BigInteger kr = K.multiply(R); + BigInteger krm1 = kr.subtract(BigInteger.ONE); + BigInteger krp1 = kr.add(BigInteger.ONE); + + Result.ExpectedValue kExpected = K.equals(BigInteger.ONE) ? Result.ExpectedValue.SUCCESS : Result.ExpectedValue.FAILURE; + + Test krS /*ONE!*/ = ecdhTest(ecpub, kr, spec, "ECDH with S = k * r.", Result.ExpectedValue.FAILURE); + Test krm1S = ecdhTest(ecpub, krm1, spec, "ECDH with S = (k * r) - 1.", kExpected); + Test krp1S = ecdhTest(ecpub, krp1, spec, "ECDH with S = (k * r) + 1.", Result.ExpectedValue.ANY); + + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Tests with edge-case private key values over " + curve.getId() + ".", + generateSuccess, zeroS, oneS, alternateS, alternateOtherS, fullS, smallerS, exactS, largeS, rm1S, rp1S, krS, krm1S, krp1S)); + } + + EC_Curve secp160r1 = EC_Store.getInstance().getObject(EC_Curve.class, "secg/secp160r1"); + ECParameterSpec spec = secp160r1.toSpec(); + byte[] pData = secp160r1.getParam(EC_Consts.PARAMETER_FP)[0]; + BigInteger p = new BigInteger(1, pData); + byte[] rData = secp160r1.getParam(EC_Consts.PARAMETER_R)[0]; + BigInteger r = new BigInteger(1, rData); + + BigInteger range = r.subtract(p); + BigInteger deviation = range.divide(BigInteger.valueOf(5)); + BigDecimal dev = new BigDecimal(deviation); + BigDecimal smallDev = new BigDecimal(10000); + int n = 10; + BigInteger[] rs = new BigInteger[n]; + BigInteger[] ps = new BigInteger[n]; + BigInteger[] zeros = new BigInteger[n]; + for (int i = 0; i < n; ++i) { + double sample; + do { + sample = rand.nextGaussian(); + } while (sample >= -1 && sample <= 1); + BigInteger where = dev.multiply(new BigDecimal(sample)).toBigInteger(); + rs[i] = where.add(r); + ps[i] = where.add(p); + zeros[i] = smallDev.multiply(new BigDecimal(sample)).toBigInteger().abs(); + } + Arrays.sort(rs); + Arrays.sort(ps); + Arrays.sort(zeros); + + //generate KeyPair + KeyGeneratorTestable kgt = new KeyGeneratorTestable(kpg, spec); + Test generate = KeyGeneratorTest.expectError(kgt, Result.ExpectedValue.ANY); + runTest(generate); + KeyPair kp = kgt.getKeyPair(); + if(kp == null) { + Test generateFail = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Generating KeyPair has failed on " + + secp160r1.getBits() + "b secp160r1." + " Other tests will be skipped.", generate); + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Test private key values near zero, near p and near/larger than the order on" + secp160r1.getId() + ".", generateFail)); + return; + } + Test generateSuccess = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Generate KeyPair.", generate); + ECPublicKey ecpub = (ECPublicKey) kp.getPublic(); + + //perform ECDH tests + Test[] zeroTests = new Test[n]; + int i = 0; + for (BigInteger nearZero : zeros) { + zeroTests[i++] = ecdhTest(ecpub, nearZero, spec, nearZero.toString(16), Result.ExpectedValue.SUCCESS); + } + Test zeroTest = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Near zero.", zeroTests); + + Test[] pTests = new Test[n]; + i = 0; + for (BigInteger nearP : ps) { + pTests[i++] = ecdhTest(ecpub, nearP, spec, nearP.toString(16) + (nearP.compareTo(p) > 0 ? " (>p)" : " (<=p)"), Result.ExpectedValue.SUCCESS); + } + Test pTest = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Near p.", pTests); + + Test[] rTests = new Test[n]; + i = 0; + for (BigInteger nearR : rs) { + if (nearR.compareTo(r) >= 0) { + rTests[i++] = ecdhTest(ecpub, nearR, spec, nearR.toString(16) + " (>=r)", Result.ExpectedValue.FAILURE); + } else { + rTests[i++] = ecdhTest(ecpub, nearR, spec, nearR.toString(16) + " (<r)", Result.ExpectedValue.SUCCESS); + } + } + Test rTest = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Near r.", rTests); + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Test private key values near zero, near p and near/larger than the order.", generateSuccess, zeroTest, pTest, rTest)); + } + + private Test ecdhTest(ECPublicKey pub, BigInteger SParam, ECParameterSpec spec, String desc, Result.ExpectedValue expect) throws NoSuchAlgorithmException { + ECPrivateKey priv = new RawECPrivateKey(SParam, spec); + KeyAgreement ka = kaIdent.getInstance(cfg.selected.getProvider()); + KeyAgreementTestable testable = new KeyAgreementTestable(ka, priv, pub); + return CompoundTest.all(Result.ExpectedValue.SUCCESS, desc, KeyAgreementTest.expectError(testable, expect)); + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneInvalidSuite.java b/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneInvalidSuite.java new file mode 100644 index 0000000..ace8945 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneInvalidSuite.java @@ -0,0 +1,120 @@ +package cz.crcs.ectester.standalone.test.suites; + +import cz.crcs.ectester.common.cli.TreeCommandLine; +import cz.crcs.ectester.common.ec.EC_Curve; +import cz.crcs.ectester.common.ec.EC_Key; +import cz.crcs.ectester.common.output.TestWriter; +import cz.crcs.ectester.common.test.CompoundTest; +import cz.crcs.ectester.common.test.Result; +import cz.crcs.ectester.common.test.Test; +import cz.crcs.ectester.common.util.ECUtil; +import cz.crcs.ectester.data.EC_Store; +import cz.crcs.ectester.standalone.ECTesterStandalone; +import cz.crcs.ectester.standalone.consts.KeyAgreementIdent; +import cz.crcs.ectester.standalone.consts.KeyPairGeneratorIdent; +import cz.crcs.ectester.standalone.test.base.KeyAgreementTest; +import cz.crcs.ectester.standalone.test.base.KeyAgreementTestable; +import cz.crcs.ectester.standalone.test.base.KeyGeneratorTest; +import cz.crcs.ectester.standalone.test.base.KeyGeneratorTestable; + +import javax.crypto.KeyAgreement; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECParameterSpec; +import java.util.*; + +/** + * @author David Hofman + */ +public class StandaloneInvalidSuite extends StandaloneTestSuite { + public StandaloneInvalidSuite(TestWriter writer, ECTesterStandalone.Config cfg, TreeCommandLine cli) { + super(writer, cfg, cli, "invalid", "The invalid curve suite tests whether the library rejects points outside of the curve during ECDH.", + "Supports options:", "\t - gt/kpg-type", "\t - kt/ka-type (select multiple types by separating them with commas)"); + } + + @Override + protected void runTests() throws Exception { + String kpgAlgo = cli.getOptionValue("test.kpg-type"); + String kaAlgo = cli.getOptionValue("test.ka-type"); + List<String> kaTypes = kaAlgo != null ? Arrays.asList(kaAlgo.split(",")) : new ArrayList<>(); + + KeyPairGeneratorIdent kpgIdent; + if (kpgAlgo == null) { + // try EC, if not, fail with: need to specify kpg algo. + Optional<KeyPairGeneratorIdent> kpgIdentOpt = cfg.selected.getKPGs().stream() + .filter((ident) -> ident.contains("EC")) + .findFirst(); + if (kpgIdentOpt.isPresent()) { + kpgIdent = kpgIdentOpt.get(); + } else { + System.err.println("The default KeyPairGenerator algorithm type of \"EC\" was not found. Need to specify a type."); + return; + } + } else { + // try the specified, if not, fail with: wrong kpg algo/not found. + Optional<KeyPairGeneratorIdent> kpgIdentOpt = cfg.selected.getKPGs().stream() + .filter((ident) -> ident.contains(kpgAlgo)) + .findFirst(); + if (kpgIdentOpt.isPresent()) { + kpgIdent = kpgIdentOpt.get(); + } else { + System.err.println("The KeyPairGenerator algorithm type of \"" + kpgAlgo + "\" was not found."); + return; + } + } + + Map<String, EC_Key.Public> pubkeys = EC_Store.getInstance().getObjects(EC_Key.Public.class, "invalid"); + Map<EC_Curve, List<EC_Key.Public>> curveList = EC_Store.mapKeyToCurve(pubkeys.values()); + for (Map.Entry<EC_Curve, List<EC_Key.Public>> e : curveList.entrySet()) { + EC_Curve curve = e.getKey(); + List<EC_Key.Public> keys = e.getValue(); + + KeyPairGenerator kpg = kpgIdent.getInstance(cfg.selected.getProvider()); + ECParameterSpec spec = curve.toSpec(); + KeyGeneratorTestable kgt = new KeyGeneratorTestable(kpg, spec); + + Test generateSuccess; + Test generate = KeyGeneratorTest.expectError(kgt, Result.ExpectedValue.ANY); + runTest(generate); + KeyPair kp = kgt.getKeyPair(); + if(kp != null) { + generateSuccess = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Generate keypair.", generate); + } else { //If KeyPair generation fails, try generating it on a default curve instead. Use this key only if it has the same domain parameters as our public key. + KeyGeneratorTestable kgtOnDefaultCurve = new KeyGeneratorTestable(kpg, curve.getBits()); + Test generateOnDefaultCurve = KeyGeneratorTest.expectError(kgtOnDefaultCurve, Result.ExpectedValue.ANY); + runTest(generateOnDefaultCurve); + kp = kgtOnDefaultCurve.getKeyPair(); + if(kp != null && ECUtil.equalKeyPairParameters((ECPrivateKey) kp.getPrivate(), ECUtil.toPublicKey(keys.get(0)))) { + generateSuccess = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Generate keypair.", generateOnDefaultCurve); + } else { + Test generateFail = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Generating KeyPair has failed on " + curve.getId() + ". " + "KeyAgreement tests will be skipped.", generate); + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Invalid curve test of " + curve.getId() + ".", generateFail)); + continue; + } + } + ECPrivateKey ecpriv = (ECPrivateKey) kp.getPrivate(); + + List<Test> allKaTests = new LinkedList<>(); + for (KeyAgreementIdent kaIdent : cfg.selected.getKAs()) { + if (kaAlgo == null || kaIdent.containsAny(kaTypes)) { + List<Test> specificKaTests = new LinkedList<>(); + for (EC_Key.Public pub : keys) { + ECPublicKey ecpub = ECUtil.toPublicKey(pub); + KeyAgreement ka = kaIdent.getInstance(cfg.selected.getProvider()); + KeyAgreementTestable testable = new KeyAgreementTestable(ka, ecpriv, ecpub); + Test keyAgreement = KeyAgreementTest.expectError(testable, Result.ExpectedValue.FAILURE); + specificKaTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, pub.getId() + " invalid key test.", keyAgreement)); + } + allKaTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Perform " + kaIdent.getName() + " with invalid public points.", specificKaTests.toArray(new Test[0]))); + } + } + if(allKaTests.isEmpty()) { + allKaTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "None of the specified key agreement types is supported by the library.")); + } + Test tests = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Do tests.", allKaTests.toArray(new Test[0])); + doTest(CompoundTest.greedyAllTry(Result.ExpectedValue.SUCCESS, "Invalid curve test of " + curve.getId() + ".", generateSuccess, tests)); + } + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneMiscSuite.java b/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneMiscSuite.java new file mode 100644 index 0000000..f3a10eb --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneMiscSuite.java @@ -0,0 +1,150 @@ +package cz.crcs.ectester.standalone.test.suites; + +import cz.crcs.ectester.common.cli.TreeCommandLine; +import cz.crcs.ectester.common.ec.EC_Curve; +import cz.crcs.ectester.common.output.TestWriter; +import cz.crcs.ectester.common.test.CompoundTest; +import cz.crcs.ectester.common.test.Result; +import cz.crcs.ectester.common.test.Test; +import cz.crcs.ectester.data.EC_Store; +import cz.crcs.ectester.standalone.ECTesterStandalone; +import cz.crcs.ectester.standalone.consts.KeyAgreementIdent; +import cz.crcs.ectester.standalone.consts.KeyPairGeneratorIdent; +import cz.crcs.ectester.standalone.consts.SignatureIdent; +import cz.crcs.ectester.standalone.test.base.*; + +import javax.crypto.KeyAgreement; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.Signature; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.util.*; + +/** + * @author David Hofman + */ +public class StandaloneMiscSuite extends StandaloneTestSuite { + private String kpgAlgo; + private String kaAlgo; + private String sigAlgo; + private List<String> kaTypes; + private List<String> sigTypes; + + public StandaloneMiscSuite(TestWriter writer, ECTesterStandalone.Config cfg, TreeCommandLine cli) { + super(writer, cfg, cli, "miscellaneous", "Some miscellaneous tests, tries ECDH and ECDSA over supersingular curves, anomalous curves,", + "Barreto-Naehrig curves with small embedding degree and CM discriminant, MNT curves,", + "some Montgomery curves transformed to short Weierstrass form and Curve25519 transformed to short Weierstrass form.", + "Supports options:", + "\t - gt/kpg-type", + "\t - kt/ka-type (select multiple types by separating them with commas)", + "\t - st/sig-type (select multiple types by separating them with commas)"); + } + + @Override + protected void runTests() throws Exception { + kpgAlgo = cli.getOptionValue("test.kpg-type"); + kaAlgo = cli.getOptionValue("test.ka-type"); + sigAlgo = cli.getOptionValue("test.sig-type"); + + kaTypes = kaAlgo != null ? Arrays.asList(kaAlgo.split(",")) : new ArrayList<>(); + sigTypes = sigAlgo != null ? Arrays.asList(sigAlgo.split(",")) : new ArrayList<>(); + + KeyPairGeneratorIdent kpgIdent; + if (kpgAlgo == null) { + // try EC, if not, fail with: need to specify kpg algo. + Optional<KeyPairGeneratorIdent> kpgIdentOpt = cfg.selected.getKPGs().stream() + .filter((ident) -> ident.contains("EC")) + .findFirst(); + if (kpgIdentOpt.isPresent()) { + kpgIdent = kpgIdentOpt.get(); + } else { + System.err.println("The default KeyPairGenerator algorithm type of \"EC\" was not found. Need to specify a type."); + return; + } + } else { + // try the specified, if not, fail with: wrong kpg algo/not found. + Optional<KeyPairGeneratorIdent> kpgIdentOpt = cfg.selected.getKPGs().stream() + .filter((ident) -> ident.contains(kpgAlgo)) + .findFirst(); + if (kpgIdentOpt.isPresent()) { + kpgIdent = kpgIdentOpt.get(); + } else { + System.err.println("The KeyPairGenerator algorithm type of \"" + kpgAlgo + "\" was not found."); + return; + } + } + KeyPairGenerator kpg = kpgIdent.getInstance(cfg.selected.getProvider()); + + Map<String, EC_Curve> anCurves = EC_Store.getInstance().getObjects(EC_Curve.class, "anomalous"); + Map<String, EC_Curve> ssCurves = EC_Store.getInstance().getObjects(EC_Curve.class, "supersingular"); + Map<String, EC_Curve> bnCurves = EC_Store.getInstance().getObjects(EC_Curve.class, "Barreto-Naehrig"); + Map<String, EC_Curve> mntCurves = EC_Store.getInstance().getObjects(EC_Curve.class, "MNT"); + List<EC_Curve> mCurves = new ArrayList<>(); + mCurves.add(EC_Store.getInstance().getObject(EC_Curve.class, "other", "M-221")); + mCurves.add(EC_Store.getInstance().getObject(EC_Curve.class, "other", "M-383")); + mCurves.add(EC_Store.getInstance().getObject(EC_Curve.class, "other", "M-511")); + EC_Curve curve25519 = EC_Store.getInstance().getObject(EC_Curve.class, "other", "Curve25519"); + + testCurves(anCurves.values(), "anomalous", kpg, Result.ExpectedValue.FAILURE); + testCurves(ssCurves.values(), "supersingular", kpg, Result.ExpectedValue.FAILURE); + testCurves(bnCurves.values(), "Barreto-Naehrig", kpg, Result.ExpectedValue.SUCCESS); + testCurves(mntCurves.values(), "MNT", kpg, Result.ExpectedValue.SUCCESS); + testCurves(mCurves, "Montgomery", kpg, Result.ExpectedValue.SUCCESS); + testCurve(curve25519, "Montgomery", kpg, Result.ExpectedValue.SUCCESS); + } + + private void testCurve(EC_Curve curve, String catName, KeyPairGenerator kpg, Result.ExpectedValue expected) throws NoSuchAlgorithmException { + //generate KeyPair + KeyGeneratorTestable kgt = new KeyGeneratorTestable(kpg, curve.toSpec()); + Test generate = KeyGeneratorTest.expectError(kgt, Result.ExpectedValue.ANY); + runTest(generate); + KeyPair kp = kgt.getKeyPair(); + if(kp == null) { + Test generateFail = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Generating KeyPair has failed on " + curve.getId() + + ". " + " Other tests will be skipped.", generate); + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Tests over " + curve.getBits() + "b " + catName + " curve: " + curve.getId() + ".", generateFail)); + return; + } + Test generateSuccess = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Generate keypair.", generate); + ECPrivateKey ecpriv = (ECPrivateKey) kp.getPrivate(); + ECPublicKey ecpub = (ECPublicKey) kp.getPublic(); + + //perform KeyAgreement tests + List<Test> kaTests = new LinkedList<>(); + for (KeyAgreementIdent kaIdent : cfg.selected.getKAs()) { + if (kaAlgo == null || kaIdent.containsAny(kaTypes)) { + KeyAgreement ka = kaIdent.getInstance(cfg.selected.getProvider()); + KeyAgreementTestable testable = new KeyAgreementTestable(ka, ecpriv, ecpub); + kaTests.add(KeyAgreementTest.expectError(testable, expected)); + } + } + if(kaTests.isEmpty()) { + kaTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "None of the specified KeyAgreement types is supported by the library.")); + } + + //perform Signature tests + List<Test> sigTests = new LinkedList<>(); + for (SignatureIdent sigIdent : cfg.selected.getSigs()) { + if (sigAlgo == null || sigIdent.containsAny(sigTypes)) { + Signature sig = sigIdent.getInstance(cfg.selected.getProvider()); + SignatureTestable testable = new SignatureTestable(sig, ecpriv, ecpub, null); + sigTests.add(SignatureTest.expectError(testable, expected)); + } + } + if(sigTests.isEmpty()) { + sigTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "None of the specified Signature types is supported by the library.")); + } + + Test performKeyAgreements = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Perform specified KeyAgreements.", kaTests.toArray(new Test[0])); + Test performSignatures = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Perform specified Signatures.", sigTests.toArray(new Test[0])); + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Tests over " + curve.getBits() + "b " + catName + " curve: " + curve.getId() + ".", generateSuccess, performKeyAgreements, performSignatures)); + } + + private void testCurves(Collection<EC_Curve> curves, String catName, KeyPairGenerator kpg, Result.ExpectedValue expected) throws NoSuchAlgorithmException { + for (EC_Curve curve : curves) { + testCurve(curve, catName, kpg, expected); + } + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandalonePerformanceSuite.java b/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandalonePerformanceSuite.java new file mode 100644 index 0000000..dd50862 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandalonePerformanceSuite.java @@ -0,0 +1,142 @@ +package cz.crcs.ectester.standalone.test.suites; + +import cz.crcs.ectester.common.cli.TreeCommandLine; +import cz.crcs.ectester.common.ec.EC_Curve; +import cz.crcs.ectester.common.output.TestWriter; +import cz.crcs.ectester.common.test.CompoundTest; +import cz.crcs.ectester.common.test.Result; +import cz.crcs.ectester.common.test.Test; +import cz.crcs.ectester.data.EC_Store; +import cz.crcs.ectester.standalone.ECTesterStandalone; +import cz.crcs.ectester.standalone.consts.KeyAgreementIdent; +import cz.crcs.ectester.standalone.consts.KeyPairGeneratorIdent; +import cz.crcs.ectester.standalone.consts.SignatureIdent; +import cz.crcs.ectester.standalone.test.base.*; + +import javax.crypto.KeyAgreement; +import java.security.KeyPairGenerator; +import java.security.Signature; +import java.security.interfaces.ECPrivateKey; +import java.security.spec.ECParameterSpec; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author David Hofman + */ +public class StandalonePerformanceSuite extends StandaloneTestSuite { + private final int count = 100; + + public StandalonePerformanceSuite(TestWriter writer, ECTesterStandalone.Config cfg, TreeCommandLine cli) { + super(writer, cfg, cli, "performance", "The performance test suite measures performance of KeyPair generation, KeyAgreement and Signature operations.", + "Supports options:", + "\t - gt/kpg-type (select multiple types by separating them with commas)", + "\t - kt/ka-type (select multiple types by separating them with commas)", + "\t - st/sig-type (select multiple types by separating them with commas)", + "\t - key-type"); + } + + @Override + protected void runTests() throws Exception { + String kpgAlgo = cli.getOptionValue("test.kpg-type"); + String kaAlgo = cli.getOptionValue("test.ka-type"); + String sigAlgo = cli.getOptionValue("test.sig-type"); + String keyAlgo = cli.getOptionValue("test.key-type", "AES"); + + List<String> kpgTypes = kpgAlgo != null ? Arrays.asList(kpgAlgo.split(",")) : new ArrayList<>(); + List<String> kaTypes = kaAlgo != null ? Arrays.asList(kaAlgo.split(",")) : new ArrayList<>(); + List<String> sigTypes = sigAlgo != null ? Arrays.asList(sigAlgo.split(",")) : new ArrayList<>(); + + List<KeyPairGeneratorIdent> kpgIdents = new LinkedList<>(); + if (kpgAlgo == null) { + // try EC, if not, fail with: need to specify kpg algo. + Optional<KeyPairGeneratorIdent> kpgIdentOpt = cfg.selected.getKPGs().stream() + .filter((ident) -> ident.contains("EC")) + .findFirst(); + if (kpgIdentOpt.isPresent()) { + kpgIdents.add(kpgIdentOpt.get()); + } else { + System.err.println("The default KeyPairGenerator algorithm type of \"EC\" was not found. Need to specify a type."); + return; + } + } else { + // try the specified, if not, fail with: wrong kpg algo/not found. + kpgIdents = cfg.selected.getKPGs().stream() + .filter((ident) -> ident.containsAny(kpgTypes)).collect(Collectors.toList()); + if (kpgIdents.isEmpty()) { + System.err.println("No KeyPairGenerator algorithms of specified types were found."); + return; + } + } + + KeyGeneratorTestable kgtOne = null; + KeyGeneratorTestable kgtOther = null; + ECParameterSpec spec = null; + List<Test> kpgTests = new LinkedList<>(); + for(KeyPairGeneratorIdent kpgIdent : kpgIdents) { + KeyPairGenerator kpg = kpgIdent.getInstance(cfg.selected.getProvider()); + if (cli.hasOption("test.bits")) { + int bits = Integer.parseInt(cli.getOptionValue("test.bits")); + kgtOne = new KeyGeneratorTestable(kpg, bits); + kgtOther = new KeyGeneratorTestable(kpg, bits); + } else if (cli.hasOption("test.named-curve")) { + String curveName = cli.getOptionValue("test.named-curve"); + EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, curveName); + if (curve == null) { + System.err.println("Curve not found: " + curveName); + return; + } + spec = curve.toSpec(); + kgtOne = new KeyGeneratorTestable(kpg, spec); + kgtOther = new KeyGeneratorTestable(kpg, spec); + } else { + kgtOne = new KeyGeneratorTestable(kpg); + kgtOther = new KeyGeneratorTestable(kpg); + } + kpgTests.add(PerformanceTest.repeat(kgtOne, kpgIdent.getName(), count)); + } + runTest(KeyGeneratorTest.expect(kgtOther, Result.ExpectedValue.SUCCESS)); + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "KeyPairGenerator performance tests", kpgTests.toArray(new Test[0]))); + + List<Test> kaTests = new LinkedList<>(); + for (KeyAgreementIdent kaIdent : cfg.selected.getKAs()) { + if (kaAlgo == null || kaIdent.containsAny(kaTypes)) { + KeyAgreement ka = kaIdent.getInstance(cfg.selected.getProvider()); + KeyAgreementTestable testable; + if (kaIdent.requiresKeyAlgo()) { + testable = new KeyAgreementTestable(ka, kgtOne, kgtOther, spec, keyAlgo); + } else { + testable = new KeyAgreementTestable(ka, kgtOne, kgtOther, spec); + } + kaTests.add(PerformanceTest.repeat(testable, kaIdent.getName(), count)); + } + } + if(kaTests.isEmpty()) { + kaTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "None of the specified KeyAgreement types is supported by the library.")); + } + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "KeyAgreement performance tests", kaTests.toArray(new Test[0]))); + + List<Test> sigTests = new LinkedList<>(); + List<Test> sigTestsNoVerification = new LinkedList<>(); + for (SignatureIdent sigIdent : cfg.selected.getSigs()) { + if (sigAlgo == null || sigIdent.containsAny(sigTypes)) { + Signature sig = sigIdent.getInstance(cfg.selected.getProvider()); + sigTests.add(PerformanceTest.repeat(new SignatureTestable(sig, kgtOne, null), sigIdent.getName(),count)); + if(kgtOne.getKeyPair() != null) { + ECPrivateKey signKey = (ECPrivateKey) kgtOne.getKeyPair().getPrivate(); + sigTestsNoVerification.add(PerformanceTest.repeat(new SignatureTestable(sig, signKey, null, null), sigIdent.getName(), count)); + } + } + } + if(sigTestsNoVerification.isEmpty() & !sigTests.isEmpty()) { + sigTestsNoVerification.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Signature tests with no verification require a successfully generated private key.")); + } + if(sigTests.isEmpty()) { + sigTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "None of the specified Signature types is supported by the library.")); + sigTestsNoVerification.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "None of the specified Signature types is supported by the library.")); + } + Test signAndVerify = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Sign and verify", sigTests.toArray(new Test[0])); + Test signOnly = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Sign only, no verification", sigTestsNoVerification.toArray(new Test[0])); + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Signature performance tests", signAndVerify, signOnly)); + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneSignatureSuite.java b/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneSignatureSuite.java new file mode 100644 index 0000000..94e810e --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneSignatureSuite.java @@ -0,0 +1,87 @@ +package cz.crcs.ectester.standalone.test.suites; + +import cz.crcs.ectester.common.cli.TreeCommandLine; +import cz.crcs.ectester.common.ec.EC_Key; +import cz.crcs.ectester.common.ec.EC_SigResult; +import cz.crcs.ectester.common.output.TestWriter; +import cz.crcs.ectester.common.test.CompoundTest; +import cz.crcs.ectester.common.test.Result; +import cz.crcs.ectester.common.util.ECUtil; +import cz.crcs.ectester.data.EC_Store; +import cz.crcs.ectester.standalone.ECTesterStandalone; +import cz.crcs.ectester.standalone.consts.SignatureIdent; +import cz.crcs.ectester.standalone.test.base.SignatureTest; +import cz.crcs.ectester.standalone.test.base.SignatureTestable; + +import java.security.NoSuchAlgorithmException; +import java.security.Signature; +import java.security.interfaces.ECPublicKey; +import java.util.*; + +/** + * @author David Hofman + */ +public class StandaloneSignatureSuite extends StandaloneTestSuite { + public StandaloneSignatureSuite(TestWriter writer, ECTesterStandalone.Config cfg, TreeCommandLine cli) { + super(writer, cfg, cli, "signature", "The signature test suite tests verifying various malformed and well-formed but invalid ECDSA signatures.", + "Supports options:", "\t - st/sig-type"); + } + + @Override + protected void runTests() throws Exception { + String sigAlgo = cli.getOptionValue("test.sig-type"); + + SignatureIdent sigIdent; + if (sigAlgo == null) { + // try ECDSA, if not, fail with: need to specify sig algo. + Optional<SignatureIdent> sigIdentOpt = cfg.selected.getSigs().stream() + .filter((ident) -> ident.contains("ECDSA")) + .findFirst(); + if (sigIdentOpt.isPresent()) { + sigIdent = sigIdentOpt.get(); + } else { + System.err.println("The default Signature algorithm type of \"ECDSA\" was not found. Need to specify a type."); + return; + } + } else { + // try the specified, if not, fail with: wrong sig algo/not found. + Optional<SignatureIdent> sigIdentOpt = cfg.selected.getSigs().stream() + .filter((ident) -> ident.contains(sigAlgo)) + .findFirst(); + if (sigIdentOpt.isPresent()) { + sigIdent = sigIdentOpt.get(); + } else { + System.err.println("The Signature algorithm type of \"" + sigAlgo + "\" was not found."); + return; + } + } + + Map<String, EC_SigResult> results = EC_Store.getInstance().getObjects(EC_SigResult.class, "wrong"); + Map<String, List<EC_SigResult>> groups = EC_Store.mapToPrefix(results.values()); + + List<EC_SigResult> nok = groups.entrySet().stream().filter((e) -> e.getKey().equals("nok")).findFirst().get().getValue(); + + byte[] data = "Some stuff that is not the actual data".getBytes(); + for (EC_SigResult sig : nok) { + ecdsaTest(sig, sigIdent, Result.ExpectedValue.FAILURE, data); + } + + List<EC_SigResult> ok = groups.entrySet().stream().filter((e) -> e.getKey().equals("ok")).findFirst().get().getValue(); + for (EC_SigResult sig : ok) { + ecdsaTest(sig, sigIdent, Result.ExpectedValue.SUCCESS, null); + } + } + + private void ecdsaTest(EC_SigResult sig, SignatureIdent sigIdent, Result.ExpectedValue expected, byte[] defaultData) throws NoSuchAlgorithmException { + ECPublicKey ecpub = ECUtil.toPublicKey(EC_Store.getInstance().getObject(EC_Key.Public.class, sig.getVerifyKey())); + + byte[] data = sig.getSigData(); + if (data == null) { + data = defaultData; + } + + Signature signature = sigIdent.getInstance(cfg.selected.getProvider()); + SignatureTestable testable = new SignatureTestable(signature, ecpub, data, sig.getData(0)); + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "ECDSA test of " + sig.getId() + ".", SignatureTest.expectError(testable, expected))); + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneTestSuite.java b/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneTestSuite.java new file mode 100644 index 0000000..e4e0013 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneTestSuite.java @@ -0,0 +1,25 @@ +package cz.crcs.ectester.standalone.test.suites; + +import cz.crcs.ectester.common.cli.TreeCommandLine; +import cz.crcs.ectester.common.output.TestWriter; +import cz.crcs.ectester.common.test.TestSuite; +import cz.crcs.ectester.standalone.ECTesterStandalone; +import cz.crcs.ectester.standalone.libs.ProviderECLibrary; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class StandaloneTestSuite extends TestSuite { + TreeCommandLine cli; + ECTesterStandalone.Config cfg; + + public StandaloneTestSuite(TestWriter writer, ECTesterStandalone.Config cfg, TreeCommandLine cli, String name, String... description) { + super(writer, name, description); + this.cfg = cfg; + this.cli = cli; + } + + public ProviderECLibrary getLibrary() { + return cfg.selected; + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneTestVectorSuite.java b/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneTestVectorSuite.java new file mode 100644 index 0000000..1e1889c --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneTestVectorSuite.java @@ -0,0 +1,63 @@ +package cz.crcs.ectester.standalone.test.suites; + +import cz.crcs.ectester.common.cli.TreeCommandLine; +import cz.crcs.ectester.common.ec.*; +import cz.crcs.ectester.common.output.TestWriter; +import cz.crcs.ectester.common.test.CompoundTest; +import cz.crcs.ectester.common.test.Result; +import cz.crcs.ectester.common.util.ECUtil; +import cz.crcs.ectester.data.EC_Store; +import cz.crcs.ectester.standalone.ECTesterStandalone; +import cz.crcs.ectester.standalone.consts.KeyAgreementIdent; +import cz.crcs.ectester.standalone.test.base.KeyAgreementTest; +import cz.crcs.ectester.standalone.test.base.KeyAgreementTestable; + +import javax.crypto.KeyAgreement; +import java.io.IOException; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.util.Map; + +/** + * @author David Hofman + */ +public class StandaloneTestVectorSuite extends StandaloneTestSuite { + + public StandaloneTestVectorSuite(TestWriter writer, ECTesterStandalone.Config cfg, TreeCommandLine cli) { + super(writer, cfg, cli, "test-vectors", "The test-vectors suite contains a collection of test vectors which test basic ECDH correctness."); + } + + @Override + protected void runTests() throws Exception { + Map<String, EC_KAResult> results = EC_Store.getInstance().getObjects(EC_KAResult.class, "test"); + for (EC_KAResult result : results.values()) { + if(!"DH_PLAIN".equals(result.getKA())) { + continue; + } + + EC_Params onekey = EC_Store.getInstance().getObject(EC_Keypair.class, result.getOneKey()); + if (onekey == null) { + onekey = EC_Store.getInstance().getObject(EC_Key.Private.class, result.getOneKey()); + } + EC_Params otherkey = EC_Store.getInstance().getObject(EC_Keypair.class, result.getOtherKey()); + if (otherkey == null) { + otherkey = EC_Store.getInstance().getObject(EC_Key.Public.class, result.getOtherKey()); + } + if (onekey == null || otherkey == null) { + throw new IOException("Test vector keys couldn't be located."); + } + + ECPrivateKey privkey = onekey instanceof EC_Keypair ? + (ECPrivateKey) ECUtil.toKeyPair((EC_Keypair) onekey).getPrivate() : + ECUtil.toPrivateKey((EC_Key.Private) onekey); + ECPublicKey pubkey = otherkey instanceof EC_Keypair ? + (ECPublicKey) ECUtil.toKeyPair((EC_Keypair) otherkey).getPublic() : + ECUtil.toPublicKey((EC_Key.Public) otherkey); + + KeyAgreementIdent kaIdent = KeyAgreementIdent.get("ECDH"); + KeyAgreement ka = kaIdent.getInstance(cfg.selected.getProvider()); + KeyAgreementTestable testable = new KeyAgreementTestable(ka, privkey, pubkey); + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Test vector " + result.getId(), KeyAgreementTest.match(testable, result.getData(0)))); + } + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneTwistSuite.java b/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneTwistSuite.java new file mode 100644 index 0000000..f182952 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneTwistSuite.java @@ -0,0 +1,120 @@ +package cz.crcs.ectester.standalone.test.suites; + +import cz.crcs.ectester.common.cli.TreeCommandLine; +import cz.crcs.ectester.common.ec.EC_Curve; +import cz.crcs.ectester.common.ec.EC_Key; +import cz.crcs.ectester.common.output.TestWriter; +import cz.crcs.ectester.common.test.CompoundTest; +import cz.crcs.ectester.common.test.Result; +import cz.crcs.ectester.common.test.Test; +import cz.crcs.ectester.common.util.ECUtil; +import cz.crcs.ectester.data.EC_Store; +import cz.crcs.ectester.standalone.ECTesterStandalone; +import cz.crcs.ectester.standalone.consts.KeyAgreementIdent; +import cz.crcs.ectester.standalone.consts.KeyPairGeneratorIdent; +import cz.crcs.ectester.standalone.test.base.KeyAgreementTest; +import cz.crcs.ectester.standalone.test.base.KeyAgreementTestable; +import cz.crcs.ectester.standalone.test.base.KeyGeneratorTest; +import cz.crcs.ectester.standalone.test.base.KeyGeneratorTestable; + +import javax.crypto.KeyAgreement; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECParameterSpec; +import java.util.*; + +/** + * @author David Hofman + */ +public class StandaloneTwistSuite extends StandaloneTestSuite { + public StandaloneTwistSuite(TestWriter writer, ECTesterStandalone.Config cfg, TreeCommandLine cli) { + super(writer, cfg, cli, "twist", "The twist test suite tests whether the library correctly rejects points on the quadratic twist of the curve during ECDH.", + "Supports options:", "\t - gt/kpg-type", "\t - kt/ka-type (select multiple types by separating them with commas)"); + } + + @Override + protected void runTests() throws Exception { + String kpgAlgo = cli.getOptionValue("test.kpg-type"); + String kaAlgo = cli.getOptionValue("test.ka-type"); + List<String> kaTypes = kaAlgo != null ? Arrays.asList(kaAlgo.split(",")) : new ArrayList<>(); + + KeyPairGeneratorIdent kpgIdent; + if (kpgAlgo == null) { + // try EC, if not, fail with: need to specify kpg algo. + Optional<KeyPairGeneratorIdent> kpgIdentOpt = cfg.selected.getKPGs().stream() + .filter((ident) -> ident.contains("EC")) + .findFirst(); + if (kpgIdentOpt.isPresent()) { + kpgIdent = kpgIdentOpt.get(); + } else { + System.err.println("The default KeyPairGenerator algorithm type of \"EC\" was not found. Need to specify a type."); + return; + } + } else { + // try the specified, if not, fail with: wrong kpg algo/not found. + Optional<KeyPairGeneratorIdent> kpgIdentOpt = cfg.selected.getKPGs().stream() + .filter((ident) -> ident.contains(kpgAlgo)) + .findFirst(); + if (kpgIdentOpt.isPresent()) { + kpgIdent = kpgIdentOpt.get(); + } else { + System.err.println("The KeyPairGenerator algorithm type of \"" + kpgAlgo + "\" was not found."); + return; + } + } + + Map<String, EC_Key.Public> pubkeys = EC_Store.getInstance().getObjects(EC_Key.Public.class, "twist"); + Map<EC_Curve, List<EC_Key.Public>> curveList = EC_Store.mapKeyToCurve(pubkeys.values()); + for (Map.Entry<EC_Curve, List<EC_Key.Public>> e : curveList.entrySet()) { + EC_Curve curve = e.getKey(); + List<EC_Key.Public> keys = e.getValue(); + + KeyPairGenerator kpg = kpgIdent.getInstance(cfg.selected.getProvider()); + ECParameterSpec spec = curve.toSpec(); + KeyGeneratorTestable kgt = new KeyGeneratorTestable(kpg, spec); + + Test generateSuccess; + Test generate = KeyGeneratorTest.expectError(kgt, Result.ExpectedValue.ANY); + runTest(generate); + KeyPair kp = kgt.getKeyPair(); + if(kp != null) { + generateSuccess = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Generate keypair.", generate); + } else { //If KeyPair generation fails, try generating it on a default curve instead. Use this key only if it has the same domain parameters as our public key. + KeyGeneratorTestable kgtOnDefaultCurve = new KeyGeneratorTestable(kpg, curve.getBits()); + Test generateOnDefaultCurve = KeyGeneratorTest.expectError(kgtOnDefaultCurve, Result.ExpectedValue.ANY); + runTest(generateOnDefaultCurve); + kp = kgtOnDefaultCurve.getKeyPair(); + if(kp != null && ECUtil.equalKeyPairParameters((ECPrivateKey) kp.getPrivate(), ECUtil.toPublicKey(keys.get(0)))) { + generateSuccess = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Generate keypair.", generateOnDefaultCurve); + } else { + Test generateFail = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Generating KeyPair has failed on " + curve.getId() + ". " + "KeyAgreement tests will be skipped.", generate); + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Twist test of " + curve.getId() + ".", generateFail)); + continue; + } + } + ECPrivateKey ecpriv = (ECPrivateKey) kp.getPrivate(); + + List<Test> allKaTests = new LinkedList<>(); + for (KeyAgreementIdent kaIdent : cfg.selected.getKAs()) { + if (kaAlgo == null || kaIdent.containsAny(kaTypes)) { + List<Test> specificKaTests = new LinkedList<>(); + for (EC_Key.Public pub : keys) { + ECPublicKey ecpub = ECUtil.toPublicKey(pub); + KeyAgreement ka = kaIdent.getInstance(cfg.selected.getProvider()); + KeyAgreementTestable testable = new KeyAgreementTestable(ka, ecpriv, ecpub); + Test keyAgreement = KeyAgreementTest.expectError(testable, Result.ExpectedValue.FAILURE); + specificKaTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, pub.getId() + " twist key test.", keyAgreement)); + } + allKaTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Perform " + kaIdent.getName() + " with public points on twist.", specificKaTests.toArray(new Test[0]))); + } + } + if(allKaTests.isEmpty()) { + allKaTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "None of the specified key agreement types is supported by the library.")); + } + Test tests = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Do tests.", allKaTests.toArray(new Test[0])); + doTest(CompoundTest.greedyAllTry(Result.ExpectedValue.SUCCESS, "Twist test of " + curve.getId() + ".", generateSuccess, tests)); + } + } +} diff --git a/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneWrongSuite.java b/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneWrongSuite.java new file mode 100644 index 0000000..a457a33 --- /dev/null +++ b/standalone/src/main/java/cz/crcs/ectester/standalone/test/suites/StandaloneWrongSuite.java @@ -0,0 +1,343 @@ +package cz.crcs.ectester.standalone.test.suites; + +import cz.crcs.ectester.common.cli.TreeCommandLine; +import cz.crcs.ectester.common.ec.*; +import cz.crcs.ectester.common.output.TestWriter; +import cz.crcs.ectester.common.test.CompoundTest; +import cz.crcs.ectester.common.test.Result; +import cz.crcs.ectester.common.test.Test; +import cz.crcs.ectester.common.util.ByteUtil; +import cz.crcs.ectester.common.util.ECUtil; +import cz.crcs.ectester.data.EC_Store; +import cz.crcs.ectester.standalone.ECTesterStandalone; +import cz.crcs.ectester.standalone.consts.KeyAgreementIdent; +import cz.crcs.ectester.standalone.consts.KeyPairGeneratorIdent; +import cz.crcs.ectester.standalone.test.base.KeyAgreementTest; +import cz.crcs.ectester.standalone.test.base.KeyAgreementTestable; +import cz.crcs.ectester.standalone.test.base.KeyGeneratorTest; +import cz.crcs.ectester.standalone.test.base.KeyGeneratorTestable; + +import javax.crypto.KeyAgreement; +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.*; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author David Hofman + */ +public class StandaloneWrongSuite extends StandaloneTestSuite { + private KeyAgreementIdent kaIdent; + private KeyPairGenerator kpg; + + public StandaloneWrongSuite(TestWriter writer, ECTesterStandalone.Config cfg, TreeCommandLine cli) { + super(writer, cfg, cli, "wrong", "The wrong curve suite tests whether the library rejects domain parameters which are not curves.", + "Supports options:", + "\t - gt/kpg-type", + "\t - kt/ka-type", + "\t - skip (place this option before the library name to skip tests that can potentially cause a freeze)"); + } + + + @Override + protected void runTests() throws Exception { + String kpgAlgo = cli.getOptionValue("test.kpg-type"); + String kaAlgo = cli.getOptionValue("test.ka-type"); + boolean skip = cli.getArg(1).equalsIgnoreCase("-skip"); + + KeyPairGeneratorIdent kpgIdent; + if (kpgAlgo == null) { + // try EC, if not, fail with: need to specify kpg algo. + Optional<KeyPairGeneratorIdent> kpgIdentOpt = cfg.selected.getKPGs().stream() + .filter((ident) -> ident.contains("EC")) + .findFirst(); + if (kpgIdentOpt.isPresent()) { + kpgIdent = kpgIdentOpt.get(); + } else { + System.err.println("The default KeyPairGenerator algorithm type of \"EC\" was not found. Need to specify a type."); + return; + } + } else { + // try the specified, if not, fail with: wrong kpg algo/not found. + Optional<KeyPairGeneratorIdent> kpgIdentOpt = cfg.selected.getKPGs().stream() + .filter((ident) -> ident.contains(kpgAlgo)) + .findFirst(); + if (kpgIdentOpt.isPresent()) { + kpgIdent = kpgIdentOpt.get(); + } else { + System.err.println("The KeyPairGenerator algorithm type of \"" + kpgAlgo + "\" was not found."); + return; + } + } + kpg = kpgIdent.getInstance(cfg.selected.getProvider()); + + if (kaAlgo == null) { + // try ECDH, if not, fail with: need to specify ka algo. + Optional<KeyAgreementIdent> kaIdentOpt = cfg.selected.getKAs().stream() + .filter((ident) -> ident.contains("ECDH")) + .findFirst(); + if (kaIdentOpt.isPresent()) { + kaIdent = kaIdentOpt.get(); + } else { + System.err.println("The default KeyAgreement algorithm type of \"ECDH\" was not found. Need to specify a type."); + return; + } + } else { + // try the specified, if not, fail with: wrong ka algo/not found. + Optional<KeyAgreementIdent> kaIdentOpt = cfg.selected.getKAs().stream() + .filter((ident) -> ident.contains(kaAlgo)) + .findFirst(); + if (kaIdentOpt.isPresent()) { + kaIdent = kaIdentOpt.get(); + } else { + System.err.println("The KeyAgreement algorithm type of \"" + kaAlgo + "\" was not found."); + return; + } + } + + /* Just do the default run on the wrong curves. + * These should generally fail, the curves aren't curves. + */ + if(!skip) { + Map<String, EC_Curve> wrongCurves = EC_Store.getInstance().getObjects(EC_Curve.class, "wrong"); + for (Map.Entry<String, EC_Curve> e : wrongCurves.entrySet()) { + + EC_Curve curve = e.getValue(); + ECParameterSpec spec = curve.toSpec(); + String type = curve.getField() == javacard.security.KeyPair.ALG_EC_FP ? "FP" : "F2M"; + + //try generating a keypair + KeyGeneratorTestable kgt = new KeyGeneratorTestable(kpg, spec); + Test generate = KeyGeneratorTest.expectError(kgt, Result.ExpectedValue.ANY); + runTest(generate); + KeyPair kp = kgt.getKeyPair(); + if (kp == null) { + Test generateFail = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Generating KeyPair has failed on " + curve.getId() + ".", generate); + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Wrong curve test of " + curve.getBits() + + "b " + type + ". " + curve.getDesc(), generateFail)); + continue; + } + Test generateSuccess = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Generate keypair.", generate); + ECPrivateKey ecpriv = (ECPrivateKey) kp.getPrivate(); + ECPublicKey ecpub = (ECPublicKey) kp.getPublic(); + + KeyAgreement ka = kaIdent.getInstance(cfg.selected.getProvider()); + KeyAgreementTestable testable = new KeyAgreementTestable(ka, ecpriv, ecpub); + Test ecdh = KeyAgreementTest.expectError(testable, Result.ExpectedValue.FAILURE); + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Wrong curve test of " + curve.getBits() + + "b " + type + ". " + curve.getDesc(), generateSuccess, ecdh)); + } + } + + /* + * Do some interesting tests with corrupting the custom curves. + * For prime field: + * - p = 0 + * - p = 1 + * - p is a square of a prime + * - p is a composite q * s with q, s primes + * - TODO: p divides discriminant + */ + Map<String, EC_Curve> curveMap = EC_Store.getInstance().getObjects(EC_Curve.class, "secg"); + List<EC_Curve> curves = curveMap.entrySet().stream().filter((e) -> e.getKey().endsWith("r1") && + e.getValue().getField() == javacard.security.KeyPair.ALG_EC_FP).map(Map.Entry::getValue).collect(Collectors.toList()); + Random r = new Random(); + for (EC_Curve curve : curves) { + short bits = curve.getBits(); + final byte[] originalp = curve.getParam(EC_Consts.PARAMETER_FP)[0]; + + curve.setParam(EC_Consts.PARAMETER_FP, new byte[][]{ ByteUtil.hexToBytes("0")}); + Test prime0 = ecdhTest(toCustomSpec(curve),"ECDH with p = 0."); + + curve.setParam(EC_Consts.PARAMETER_FP, new byte[][]{ ByteUtil.hexToBytes("1")}); + Test prime1 = ecdhTest(toCustomSpec(curve),"ECDH with p = 1."); + + short keyHalf = (short) (bits / 2); + BigInteger prime = new BigInteger(keyHalf, 50, r); + BigInteger primePow = prime.pow(2); + byte[] primePowBytes = ECUtil.toByteArray(primePow, bits); + curve.setParam(EC_Consts.PARAMETER_FP, new byte[][]{primePowBytes}); + + Test primePower = ecdhTest(toCustomSpec(curve), "ECDH with p = q^2."); + + BigInteger q = new BigInteger(keyHalf, r); + BigInteger s = new BigInteger(keyHalf, r); + BigInteger compositeValue = q.multiply(s); + byte[] compositeBytes = ECUtil.toByteArray(compositeValue, bits); + curve.setParam(EC_Consts.PARAMETER_FP, new byte[][]{compositeBytes}); + + Test composite = ecdhTest(toCustomSpec(curve), "ECDH with p = q * s."); + + Test wrongPrime = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Tests with corrupted prime parameter.", prime0 , prime1, primePower, composite ); + + curve.setParam(EC_Consts.PARAMETER_FP, new byte[][] {originalp}); + final byte[][] originalG = curve.getParam(EC_Consts.PARAMETER_G); + + byte[] Gx = new BigInteger(curve.getBits(), r).toByteArray(); + byte[] Gy = new BigInteger(curve.getBits(), r).toByteArray(); + curve.setParam(EC_Consts.PARAMETER_G, new byte[][] {Gx, Gy}); + Test fullRandomG = ecdhTest(toCustomSpec(curve), "ECDH with G = random data."); + + final BigInteger originalBigp = new BigInteger(1, originalp); + byte[] smallerGx = new BigInteger(curve.getBits(), r).mod(originalBigp).toByteArray(); + byte[] smallerGy = new BigInteger(curve.getBits(), r).mod(originalBigp).toByteArray(); + curve.setParam(EC_Consts.PARAMETER_G, new byte[][] {smallerGx, smallerGy}); + Test randomG = ecdhTest(toCustomSpec(curve), "ECDH with G = random data mod p."); + + curve.setParam(EC_Consts.PARAMETER_G, new byte[][] {ByteUtil.hexToBytes("0"), ByteUtil.hexToBytes("0")}); + Test zeroG = ecdhTest(toCustomSpec(curve), "ECDH with G = infinity."); + + Test wrongG = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Tests with corrupted G parameter.", fullRandomG, randomG, zeroG); + + curve.setParam(EC_Consts.PARAMETER_G, originalG); + final byte[] originalR = curve.getParam(EC_Consts.PARAMETER_R)[0]; + final BigInteger originalBigR = new BigInteger(1, originalR); + + List<Test> allRTests = new LinkedList<>(); + if(!skip) { + byte[] RZero = new byte[]{(byte) 0}; + curve.setParam(EC_Consts.PARAMETER_R, new byte[][]{RZero}); + allRTests.add(ecdhTest(toCustomSpec(curve), "ECDH with R = 0.")); + + + byte[] ROne = new byte[]{(byte) 1}; + curve.setParam(EC_Consts.PARAMETER_R, new byte[][]{ROne}); + allRTests.add(ecdhTest(toCustomSpec(curve), "ECDH with R = 1.")); + } + + BigInteger prevPrimeR; + do { + prevPrimeR = BigInteger.probablePrime(originalBigR.bitLength() - 1, r); + } while (prevPrimeR.compareTo(originalBigR) >= 0); + byte[] prevRBytes = ECUtil.toByteArray(prevPrimeR, bits); + curve.setParam(EC_Consts.PARAMETER_R, new byte[][] {prevRBytes}); + allRTests.add(ecdhTest(toCustomSpec(curve), "ECDH with R = some prime (but [r]G != infinity) smaller than original R.")); + + BigInteger nextPrimeR = originalBigR.nextProbablePrime(); + byte[] nextRBytes = ECUtil.toByteArray(nextPrimeR, bits); + curve.setParam(EC_Consts.PARAMETER_R, new byte[][]{nextRBytes}); + allRTests.add(ecdhTest(toCustomSpec(curve), "ECDH with R = some prime (but [r]G != infinity) larger than original R.")); + + byte[] nonprimeRBytes = nextRBytes.clone(); + nonprimeRBytes[nonprimeRBytes.length - 1] ^= 1; + curve.setParam(EC_Consts.PARAMETER_R, new byte[][] {nonprimeRBytes} ); + allRTests.add(ecdhTest(toCustomSpec(curve), "ECDH with R = some composite (but [r]G != infinity).")); + + Test wrongR = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Tests with corrupted R parameter.", allRTests.toArray(new Test[0])); + + curve.setParam(EC_Consts.PARAMETER_R, new byte[][] {originalR}); + + byte[] kRaw = new byte[]{(byte) 0xff}; + curve.setParam(EC_Consts.PARAMETER_K, new byte[][] {kRaw}); + Test bigK = ecdhTest(toCustomSpec(curve), "ECDH with big K."); + + byte[] kZero = new byte[]{(byte) 0}; + curve.setParam(EC_Consts.PARAMETER_K, new byte[][]{kZero}); + Test zeroK = ecdhTest(toCustomSpec(curve), "ECDH with K = 0."); + + Test wrongK = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Tests with corrupted K parameter.", bigK, zeroK); + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Tests of " + bits + "b " + "FP", wrongPrime, wrongG, wrongR , wrongK)); + } + + + /* + * For binary field: + * - e1 = e2 = e3 = 0 + * - e1, e2 or e3 is larger than m. + */ + curveMap = EC_Store.getInstance().getObjects(EC_Curve.class, "secg"); + curves = curveMap.entrySet().stream().filter((e) -> e.getKey().endsWith("r1") && + e.getValue().getField() == EC_Consts.ALG_EC_F2M).map(Map.Entry::getValue).collect(Collectors.toList()); + for (EC_Curve curve : curves) { + short bits = curve.getBits(); + byte[][] coeffBytes; + + + coeffBytes = new byte[][]{ + ByteUtil.shortToBytes(bits), + ByteUtil.shortToBytes((short) 0), + ByteUtil.shortToBytes((short) 0), + ByteUtil.shortToBytes((short) 0)}; + curve.setParam(EC_Consts.PARAMETER_F2M, coeffBytes); + Test coeff0 = ecdhTest(toCustomSpec(curve), "ECDH with wrong field polynomial: x^"); + + short e1 = (short) (2 * bits); + short e2 = (short) (3 * bits); + short e3 = (short) (4 * bits); + coeffBytes = new byte[][]{ + ByteUtil.shortToBytes(bits), + ByteUtil.shortToBytes(e1), + ByteUtil.shortToBytes(e2), + ByteUtil.shortToBytes(e3)}; + curve.setParam(EC_Consts.PARAMETER_F2M, coeffBytes); + Test coeffLarger = ecdhTest(toCustomSpec(curve), "ECDH with wrong field poly, powers larger than " + bits); + + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Tests with corrupted field polynomial parameter over " + curve.getBits() + "b F2M", coeff0, coeffLarger)); + } + } + + private Test ecdhTest(ECParameterSpec spec, String desc) throws NoSuchAlgorithmException { + //generate KeyPair + KeyGeneratorTestable kgt = new KeyGeneratorTestable(kpg, spec); + Test generate = KeyGeneratorTest.expectError(kgt, Result.ExpectedValue.FAILURE); + runTest(generate); + KeyPair kp = kgt.getKeyPair(); + if(kp == null) { + return CompoundTest.all(Result.ExpectedValue.SUCCESS, desc, generate); + } + ECPublicKey pub = (ECPublicKey) kp.getPublic(); + ECPrivateKey priv = (ECPrivateKey) kp.getPrivate(); + + //perform ECDH + KeyAgreement ka = kaIdent.getInstance(cfg.selected.getProvider()); + KeyAgreementTestable testable = new KeyAgreementTestable(ka, priv, pub); + Test ecdh = KeyAgreementTest.expect(testable, Result.ExpectedValue.FAILURE); + return CompoundTest.all(Result.ExpectedValue.SUCCESS, desc, generate, ecdh); + } + + //constructs EllipticCurve from EC_Curve even if the parameters of the curve are wrong + private EllipticCurve toCustomCurve(EC_Curve curve) { + ECField field; + if (curve.getField() == javacard.security.KeyPair.ALG_EC_FP) { + field = new CustomECFieldFp(new BigInteger(1, curve.getData(0))); + } else { + byte[][] fieldData = curve.getParam(EC_Consts.PARAMETER_F2M); + int m = ByteUtil.getShort(fieldData[0], 0); + int e1 = ByteUtil.getShort(fieldData[1], 0); + int e2 = ByteUtil.getShort(fieldData[2], 0); + int e3 = ByteUtil.getShort(fieldData[3], 0); + int[] powers; + if (e2 == 0 && e3 == 0) { + powers = new int[]{e1}; + } else { + powers = new int[]{e1, e2, e3}; + } + field = new CustomECFieldF2m(m, powers); + } + + BigInteger a = new BigInteger(1, curve.getParam(EC_Consts.PARAMETER_A)[0]); + BigInteger b = new BigInteger(1, curve.getParam(EC_Consts.PARAMETER_B)[0]); + + return new CustomEllipticCurve(field, a, b); + } + + //constructs ECParameterSpec from EC_Curve even if the parameters of the curve are wrong + private ECParameterSpec toCustomSpec(EC_Curve curve) { + EllipticCurve customCurve = toCustomCurve(curve); + + byte[][] G = curve.getParam(EC_Consts.PARAMETER_G); + BigInteger gx = new BigInteger(1, G[0]); + BigInteger gy = new BigInteger(1, G[1]); + ECPoint generator = new ECPoint(gx, gy); + + BigInteger n = new BigInteger(1, curve.getParam(EC_Consts.PARAMETER_R)[0]); + + int h = new BigInteger(1, curve.getParam(EC_Consts.PARAMETER_K)[0]).intValue(); + return new CustomECParameterSpec(customCurve, generator, n, h); + } +} |
