/* * Copyright (c) 2016-2017 Petr Svenda * * 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.reader; import cz.crcs.ectester.applet.ECTesterApplet; import cz.crcs.ectester.applet.EC_Consts; import cz.crcs.ectester.common.cli.CLITools; import cz.crcs.ectester.common.ec.EC_Params; import cz.crcs.ectester.common.output.*; import cz.crcs.ectester.common.test.TestException; import cz.crcs.ectester.common.test.TestRunner; import cz.crcs.ectester.common.util.ByteUtil; import cz.crcs.ectester.data.EC_Store; import cz.crcs.ectester.reader.command.Command; import cz.crcs.ectester.reader.output.*; import cz.crcs.ectester.reader.response.Response; import cz.crcs.ectester.reader.test.*; import javacard.security.KeyPair; import org.apache.commons.cli.*; import javax.smartcardio.CardException; import javax.xml.parsers.ParserConfigurationException; import java.io.*; import java.nio.file.Files; import java.util.*; import static cz.crcs.ectester.applet.ECTesterApplet.KeyAgreement_ALG_EC_SVDP_DH; /** * Reader part of ECTester, a tool for testing Elliptic curve support on javacards. * * @author Petr Svenda petr@svenda.com * @author Jan Jancar johny@neuromancer.sk * @version v0.1.0 */ public class ECTesterReader { private CardMngr cardManager; private OutputLogger logger; private TestWriter testWriter; private ResponseWriter respWriter; private EC_Store dataStore; private Config cfg; private Options opts = new Options(); private static final String VERSION = "v0.1.0"; private static final String DESCRIPTION = "ECTesterReader " + VERSION + ", a javacard Elliptic Curve Cryptography support tester/utility."; private static final String LICENSE = "MIT Licensed\nCopyright (c) 2016-2017 Petr Svenda "; private static final String CLI_HEADER = "\n" + DESCRIPTION + "\n\n"; private static final String CLI_FOOTER = "\n" + LICENSE; private static final byte[] SELECT_ECTESTERAPPLET = {(byte) 0x00, (byte) 0xa4, (byte) 0x04, (byte) 0x00, (byte) 0x0a, (byte) 0x45, (byte) 0x43, (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x65, (byte) 0x72, (byte) 0x30, (byte) 0x31}; private static final byte[] AID = {(byte) 0x45, (byte) 0x43, (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x65, (byte) 0x72, (byte) 0x30, (byte) 0x31}; private static final byte[] INSTALL_DATA = new byte[10]; private void run(String[] args) { try { CommandLine cli = parseArgs(args); //if help, print and quit if (cli.hasOption("help")) { CLITools.help("ECTesterReader.jar", CLI_HEADER, opts, CLI_FOOTER, true); return; } else if (cli.hasOption("version")) { CLITools.version(DESCRIPTION, LICENSE); return; } cfg = new Config(); //if not, read other options first, into attributes, then do action if (!cfg.readOptions(cli)) { return; } dataStore = new EC_Store(); //if list, print and quit if (cli.hasOption("list-named")) { CLITools.listNamed(dataStore, cli.getOptionValue("list-named")); return; } //init CardManager cardManager = new CardMngr(cfg.verbose, cfg.simulate); //connect or simulate connection if (cfg.simulate) { if (!cardManager.prepareLocalSimulatorApplet(AID, INSTALL_DATA, ECTesterApplet.class)) { System.err.println("Failed to establish a simulator."); System.exit(1); } } else { if (!cardManager.connectToCardSelect()) { System.err.println("Failed to connect to card."); System.exit(1); } cardManager.send(SELECT_ECTESTERAPPLET); } // Setup logger, testWriter and respWriter logger = new OutputLogger(true, cfg.log); if (cfg.format == null) { testWriter = new TextTestWriter(logger.getPrintStream()); } else { switch (cfg.format) { case "text": testWriter = new TextTestWriter(logger.getPrintStream()); break; case "xml": testWriter = new XMLTestWriter(logger.getOutputStream()); break; case "yaml": case "yml": testWriter = new YAMLTestWriter(logger.getPrintStream()); break; } } respWriter = new ResponseWriter(logger.getPrintStream()); //do action if (cli.hasOption("export")) { export(); } else if (cli.hasOption("generate")) { generate(); } else if (cli.hasOption("test")) { test(); } else if (cli.hasOption("ecdh") || cli.hasOption("ecdhc")) { ecdh(); } else if (cli.hasOption("ecdsa")) { ecdsa(); } //disconnect cardManager.disconnectFromCard(); logger.close(); } catch (MissingOptionException moex) { System.err.println("Missing required options, one of:"); for (Object opt : moex.getMissingOptions().toArray()) { if (opt instanceof OptionGroup) { for (Option o : ((OptionGroup) opt).getOptions()) { System.err.print("-" + o.getOpt()); if (o.hasLongOpt()) { System.err.print("\t/ --" + o.getLongOpt() + " "); } if (o.hasArg()) { if (o.hasOptionalArg()) { System.err.print("[" + o.getArgName() + "] "); } else { System.err.print("<" + o.getArgName() + "> "); } } if (o.getDescription() != null) { System.err.print("\t\t\t" + o.getDescription()); } System.err.println(); } } else if (opt instanceof String) { System.err.println(opt); } } } catch (MissingArgumentException maex) { System.err.println("Option, " + maex.getOption().getOpt() + " requires an argument: " + maex.getOption().getArgName()); } catch (NumberFormatException nfex) { System.err.println("Not a number. " + nfex.getMessage()); } catch (FileNotFoundException fnfe) { System.err.println("File " + fnfe.getMessage() + " not found."); } catch (ParseException | IOException ex) { System.err.println(ex.getMessage()); } catch (CardException | TestException ex) { if (logger != null) logger.println(ex.getMessage()); } catch (ParserConfigurationException e) { e.printStackTrace(); } finally { if (logger != null) logger.flush(); } } /** * Parses command-line options. * * @param args cli arguments * @return parsed CommandLine object * @throws ParseException if there are any problems encountered while parsing the command line tokens */ private CommandLine parseArgs(String[] args) throws ParseException { /* * Actions: * -V / --version * -h / --help * -e / --export * -g / --generate [amount] * -t / --test [test_suite] * -dh / --ecdh [count] * -dhc / --ecdhc [count] * -dsa / --ecdsa [count] * -ln / --list-named [obj] * * Options: * -b / --bit-size // -a / --all * * -fp / --prime-field * -f2m / --binary-field * * -u / --custom * -nc / --named-curve * -c / --curve field,a,b,gx,gy,r,k * * -pub / --public wx,wy * -npub / --named-public * * -priv / --private s * -npriv / --named-private * * -k / --key wx,wy,s * -nk / --named-key * * -v / --verbose * * -i / --input * -o / --output * --format * -l / --log [log_file] * * -f / --fresh * -s / --simulate * -y / --yes * -ka/ --ka-type */ OptionGroup actions = new OptionGroup(); actions.setRequired(true); actions.addOption(Option.builder("V").longOpt("version").desc("Print version info.").build()); actions.addOption(Option.builder("h").longOpt("help").desc("Print help.").build()); actions.addOption(Option.builder("ln").longOpt("list-named").desc("Print the list of supported named curves and keys.").hasArg().argName("what").optionalArg(true).build()); actions.addOption(Option.builder("e").longOpt("export").desc("Export the defaut curve parameters of the card(if any).").build()); actions.addOption(Option.builder("g").longOpt("generate").desc("Generate [amount] of EC keys.").hasArg().argName("amount").optionalArg(true).build()); actions.addOption(Option.builder("t").longOpt("test").desc("Test ECC support. [test_suite]:\n- default:\n- invalid:\n- wrong:\n- composite:\n- test-vectors:").hasArg().argName("test_suite").optionalArg(true).build()); actions.addOption(Option.builder("dh").longOpt("ecdh").desc("Do ECDH, [count] times.").hasArg().argName("count").optionalArg(true).build()); actions.addOption(Option.builder("dhc").longOpt("ecdhc").desc("Do ECDHC, [count] times.").hasArg().argName("count").optionalArg(true).build()); actions.addOption(Option.builder("dsa").longOpt("ecdsa").desc("Sign data with ECDSA, [count] times.").hasArg().argName("count").optionalArg(true).build()); opts.addOptionGroup(actions); OptionGroup size = new OptionGroup(); size.addOption(Option.builder("b").longOpt("bit-size").desc("Set curve size.").hasArg().argName("bits").build()); size.addOption(Option.builder("a").longOpt("all").desc("Test all curve sizes.").build()); opts.addOptionGroup(size); opts.addOption(Option.builder("fp").longOpt("prime-field").desc("Use a prime field.").build()); opts.addOption(Option.builder("f2m").longOpt("binary-field").desc("Use a binary field.").build()); OptionGroup curve = new OptionGroup(); curve.addOption(Option.builder("nc").longOpt("named-curve").desc("Use a named curve, from CurveDB: ").hasArg().argName("cat/id").build()); curve.addOption(Option.builder("c").longOpt("curve").desc("Use curve from file (field,a,b,gx,gy,r,k).").hasArg().argName("curve_file").build()); curve.addOption(Option.builder("u").longOpt("custom").desc("Use a custom curve (applet-side embedded, SECG curves).").build()); opts.addOptionGroup(curve); OptionGroup pub = new OptionGroup(); pub.addOption(Option.builder("npub").longOpt("named-public").desc("Use public key from KeyDB: ").hasArg().argName("cat/id").build()); pub.addOption(Option.builder("pub").longOpt("public").desc("Use public key from file (wx,wy).").hasArg().argName("pubkey_file").build()); opts.addOptionGroup(pub); OptionGroup priv = new OptionGroup(); priv.addOption(Option.builder("npriv").longOpt("named-private").desc("Use private key from KeyDB: ").hasArg().argName("cat/id").build()); priv.addOption(Option.builder("priv").longOpt("private").desc("Use private key from file (s).").hasArg().argName("privkey_file").build()); opts.addOptionGroup(priv); OptionGroup key = new OptionGroup(); key.addOption(Option.builder("nk").longOpt("named-key").desc("Use keyPair from KeyDB: ").hasArg().argName("cat/id").build()); key.addOption(Option.builder("k").longOpt("key").desc("Use keyPair from fileĀ  (wx,wy,s).").hasArg().argName("key_file").build()); opts.addOptionGroup(key); opts.addOption(Option.builder("i").longOpt("input").desc("Input from fileĀ , for ECDSA signing.").hasArg().argName("input_file").build()); opts.addOption(Option.builder("o").longOpt("output").desc("Output into file .").hasArg().argName("output_file").build()); opts.addOption(Option.builder("l").longOpt("log").desc("Log output into file [log_file].").hasArg().argName("log_file").optionalArg(true).build()); opts.addOption(Option.builder("v").longOpt("verbose").desc("Turn on verbose logging.").build()); opts.addOption(Option.builder().longOpt("format").desc("Output format to use. One of: text,yml,xml.").hasArg().argName("format").build()); opts.addOption(Option.builder("f").longOpt("fresh").desc("Generate fresh keys (set domain parameters before every generation).").build()); opts.addOption(Option.builder("s").longOpt("simulate").desc("Simulate a card with jcardsim instead of using a terminal.").build()); opts.addOption(Option.builder("y").longOpt("yes").desc("Accept all warnings and prompts.").build()); opts.addOption(Option.builder("ka").longOpt("ka-type").desc("Set KeyAgreement object [type], corresponds to JC.KeyAgreement constants.").hasArg().argName("type").optionalArg(true).build()); CommandLineParser parser = new DefaultParser(); return parser.parse(opts, args); } /** * Exports default card/simulation EC domain parameters to output file. * * @throws CardException if APDU transmission fails * @throws IOException if an IO error occurs when writing to key file. */ private void export() throws CardException, IOException { byte keyClass = cfg.primeField ? KeyPair.ALG_EC_FP : KeyPair.ALG_EC_F2M; List sent = new LinkedList<>(); sent.add(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_LOCAL, (short) cfg.bits, keyClass).send()); sent.add(new Command.Clear(cardManager, ECTesterApplet.KEYPAIR_LOCAL).send()); sent.add(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL).send()); // Cofactor generally isn't set on the default curve parameters on cards, // since its not necessary for ECDH, only ECDHC which not many cards implement // TODO: check if its assumend to be == 1? short domainAll = cfg.primeField ? EC_Consts.PARAMETERS_DOMAIN_FP : EC_Consts.PARAMETERS_DOMAIN_F2M; short domain = (short) (domainAll ^ EC_Consts.PARAMETER_K); Response.Export export = new Command.Export(cardManager, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.KEY_PUBLIC, domainAll).send(); if (!export.successful()) { export = new Command.Export(cardManager, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.KEY_PUBLIC, domain).send(); } sent.add(export); for (Response r : sent) { respWriter.outputResponse(r); } EC_Params exported = new EC_Params(domain, export.getParams()); FileOutputStream out = new FileOutputStream(cfg.output); exported.writeCSV(out); out.close(); } /** * Generates EC keyPairs and outputs them to output file. * * @throws CardException if APDU transmission fails * @throws IOException if an IO error occurs when writing to key file. */ private void generate() throws CardException, IOException { byte keyClass = cfg.primeField ? KeyPair.ALG_EC_FP : KeyPair.ALG_EC_F2M; new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_LOCAL, (short) cfg.bits, keyClass).send(); Command curve = Command.prepareCurve(cardManager, dataStore, cfg, ECTesterApplet.KEYPAIR_LOCAL, (short) cfg.bits, keyClass); FileWriter keysFile = new FileWriter(cfg.output); keysFile.write("index;time;pubW;privS\n"); int generated = 0; int retry = 0; while (generated < cfg.generateAmount || cfg.generateAmount == 0) { if ((cfg.fresh || generated == 0) && curve != null) { Response fresh = curve.send(); respWriter.outputResponse(fresh); } Command.Generate generate = new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL); Response.Generate response = generate.send(); long elapsed = response.getDuration(); Response.Export export = new Command.Export(cardManager, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.KEY_BOTH, EC_Consts.PARAMETERS_KEYPAIR).send(); if (!response.successful() || !export.successful()) { if (retry < 10) { retry++; continue; } else { System.err.println("Keys could not be generated."); break; } } respWriter.outputResponse(response); String pub = ByteUtil.bytesToHex(export.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_W), false); String priv = ByteUtil.bytesToHex(export.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_S), false); String line = String.format("%d;%d;%s;%s\n", generated, elapsed / 1000000, pub, priv); keysFile.write(line); keysFile.flush(); generated++; } Response cleanup = new Command.Cleanup(cardManager).send(); respWriter.outputResponse(cleanup); keysFile.close(); } /** * Tests Elliptic curve support for a given curve/curves. * * @throws CardException if APDU transmission fails * @throws IOException if an IO error occurs when writing to key file. */ private void test() throws IOException, TestException { CardTestSuite suite; switch (cfg.testSuite) { case "default": suite = new CardDefaultSuite(dataStore, cfg); break; case "test-vectors": suite = new CardTestVectorSuite(dataStore, cfg); break; default: // These tests are dangerous, prompt before them. System.out.println("The test you selected (" + cfg.testSuite + ") is potentially dangerous."); System.out.println("Some of these tests have caused temporary DoS of some cards."); if (!cfg.yes) { System.out.print("Do you want to proceed? (y/n): "); Scanner in = new Scanner(System.in); String confirmation = in.nextLine().toLowerCase(); if (!Arrays.asList("yes", "y").contains(confirmation)) { return; } in.close(); } switch (cfg.testSuite) { case "wrong": suite = new CardWrongCurvesSuite(dataStore, cfg); break; case "composite": suite = new CardCompositeCurvesSuite(dataStore, cfg); break; case "invalid": suite = new CardInvalidCurvesSuite(dataStore, cfg); break; default: System.err.println("Unknown test suite."); return; } break; } TestRunner runner = new TestRunner(suite, testWriter); suite.setup(cardManager); runner.run(); } /** * Performs ECDH key exchange. * * @throws CardException if APDU transmission fails * @throws IOException if an IO error occurs when writing to key file. */ private void ecdh() throws IOException, CardException { byte keyClass = cfg.primeField ? KeyPair.ALG_EC_FP : KeyPair.ALG_EC_F2M; List prepare = new LinkedList<>(); prepare.add(new Command.AllocateKeyAgreement(cardManager, cfg.kaType).send()); // Prepare KeyAgreement or required type prepare.add(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, (short) cfg.bits, keyClass).send()); Command curve = Command.prepareCurve(cardManager, dataStore, cfg, ECTesterApplet.KEYPAIR_BOTH, (short) cfg.bits, keyClass); if (curve != null) prepare.add(curve.send()); for (Response r : prepare) { respWriter.outputResponse(r); } byte pubkey = (cfg.anyPublicKey || cfg.anyKey) ? ECTesterApplet.KEYPAIR_REMOTE : ECTesterApplet.KEYPAIR_LOCAL; byte privkey = (cfg.anyPrivateKey || cfg.anyKey) ? ECTesterApplet.KEYPAIR_REMOTE : ECTesterApplet.KEYPAIR_LOCAL; List generate = new LinkedList<>(); generate.add(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_BOTH)); if (cfg.anyPublicKey || cfg.anyPrivateKey || cfg.anyKey) { generate.add(Command.prepareKey(cardManager, dataStore, cfg, ECTesterApplet.KEYPAIR_REMOTE)); } FileWriter out = null; if (cfg.output != null) { out = new FileWriter(cfg.output); out.write("index;time;secret\n"); } int retry = 0; int done = 0; while (done < cfg.ECDHCount) { List ecdh = Command.sendAll(generate); Response.ECDH perform = new Command.ECDH(cardManager, pubkey, privkey, ECTesterApplet.EXPORT_TRUE, EC_Consts.CORRUPTION_NONE, cfg.ECDHKA).send(); ecdh.add(perform); for (Response r : ecdh) { respWriter.outputResponse(r); } if (!perform.successful() || !perform.hasSecret()) { if (retry < 10) { ++retry; continue; } else { System.err.println("Couldn't obtain ECDH secret from card response."); break; } } if (out != null) { out.write(String.format("%d;%d;%s\n", done, perform.getDuration() / 1000000, ByteUtil.bytesToHex(perform.getSecret(), false))); } ++done; } Response cleanup = new Command.Cleanup(cardManager).send(); respWriter.outputResponse(cleanup); if (out != null) out.close(); } /** * Performs ECDSA signature, on random or provided data. * * @throws CardException if APDU transmission fails * @throws IOException if an IO error occurs when writing to key file. */ private void ecdsa() throws CardException, IOException { //read file, if asked to sign byte[] data = null; if (cfg.input != null) { File in = new File(cfg.input); long len = in.length(); if (len == 0) { throw new FileNotFoundException(cfg.input); } data = Files.readAllBytes(in.toPath()); } Command generate; if (cfg.anyKeypart) { generate = Command.prepareKey(cardManager, dataStore, cfg, ECTesterApplet.KEYPAIR_LOCAL); } else { generate = new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL); } byte keyClass = cfg.primeField ? KeyPair.ALG_EC_FP : KeyPair.ALG_EC_F2M; List prepare = new LinkedList<>(); prepare.add(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_LOCAL, (short) cfg.bits, keyClass).send()); Command curve = Command.prepareCurve(cardManager, dataStore, cfg, ECTesterApplet.KEYPAIR_LOCAL, (short) cfg.bits, keyClass); if (curve != null) prepare.add(curve.send()); for (Response r : prepare) { respWriter.outputResponse(r); } FileWriter out = null; if (cfg.output != null) { out = new FileWriter(cfg.output); out.write("index;time;signature\n"); } int retry = 0; int done = 0; while (done < cfg.ECDSACount) { List ecdsa = new LinkedList<>(); ecdsa.add(generate.send()); Response.ECDSA perform = new Command.ECDSA(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_TRUE, data).send(); ecdsa.add(perform); for (Response r : ecdsa) { respWriter.outputResponse(r); } if (!perform.successful() || !perform.hasSignature()) { if (retry < 10) { ++retry; continue; } else { System.err.println("Couldn't obtain ECDSA signature from card response."); break; } } if (out != null) { out.write(String.format("%d;%d;%s\n", done, perform.getDuration() / 1000000, ByteUtil.bytesToHex(perform.getSignature(), false))); } ++done; } Response cleanup = new Command.Cleanup(cardManager).send(); respWriter.outputResponse(cleanup); if (out != null) out.close(); } public static void main(String[] args) { ECTesterReader app = new ECTesterReader(); app.run(args); } public static class Config { //Options public int bits; public boolean all; public boolean primeField = false; public boolean binaryField = false; public byte kaType = KeyAgreement_ALG_EC_SVDP_DH; public String namedCurve; public String curveFile; public boolean customCurve = false; public boolean anyPublicKey = false; public String namedPublicKey; public String publicKey; public boolean anyPrivateKey = false; public String namedPrivateKey; public String privateKey; public boolean anyKey = false; public String namedKey; public String key; public boolean anyKeypart = false; public String log; public boolean verbose = false; public String input; public String output; public boolean fresh = false; public boolean simulate = false; public boolean yes = false; public String format; //Action-related options public String listNamed; public String testSuite; public int generateAmount; public int ECDHCount; public byte ECDHKA; public int ECDSACount; /** * Reads and validates options, also sets defaults. * * @param cli cli object, with parsed args * @return whether the options are valid. */ boolean readOptions(CommandLine cli) { bits = Integer.parseInt(cli.getOptionValue("bit-size", "0")); all = cli.hasOption("all"); primeField = cli.hasOption("fp"); binaryField = cli.hasOption("f2m"); kaType = Byte.parseByte(cli.getOptionValue("ka-type", "1")); namedCurve = cli.getOptionValue("named-curve"); customCurve = cli.hasOption("custom"); curveFile = cli.getOptionValue("curve"); namedPublicKey = cli.getOptionValue("named-public"); publicKey = cli.getOptionValue("public"); anyPublicKey = (publicKey != null) || (namedPublicKey != null); namedPrivateKey = cli.getOptionValue("named-private"); privateKey = cli.getOptionValue("private"); anyPrivateKey = (privateKey != null) || (namedPrivateKey != null); namedKey = cli.getOptionValue("named-key"); key = cli.getOptionValue("key"); anyKey = (key != null) || (namedKey != null); anyKeypart = anyKey || anyPublicKey || anyPrivateKey; if (cli.hasOption("log")) { log = cli.getOptionValue("log", String.format("ECTESTER_log_%d.log", System.currentTimeMillis() / 1000)); } verbose = cli.hasOption("verbose"); input = cli.getOptionValue("input"); output = cli.getOptionValue("output"); fresh = cli.hasOption("fresh"); simulate = cli.hasOption("simulate"); yes = cli.hasOption("yes"); if (cli.hasOption("list-named")) { listNamed = cli.getOptionValue("list-named"); return true; } format = cli.getOptionValue("format", "text"); String formats[] = new String[]{"text", "xml", "yaml", "yml"}; if (!Arrays.asList(formats).contains(format)) { System.err.println("Wrong output format " + format + ". Should be one of " + Arrays.toString(formats)); return false; } if ((key != null || namedKey != null) && (anyPublicKey || anyPrivateKey)) { System.err.print("Can only specify the whole key with --key/--named-key or pubkey and privkey with --public/--named-public and --private/--named-private."); return false; } if (bits < 0) { System.err.println("Bit-size must not be negative."); return false; } if (bits == 0 && !all) { System.err.println("You must specify either bit-size with -b or all bit-sizes with -a."); return false; } if (key != null && namedKey != null || publicKey != null && namedPublicKey != null || privateKey != null && namedPrivateKey != null) { System.err.println("You cannot specify both a named key and a key file."); return false; } if (cli.hasOption("export")) { if (primeField == binaryField) { System.err.print("Need to specify field with -fp or -f2m. (not both)"); return false; } if (anyKeypart) { System.err.println("Keys should not be specified when exporting curve params."); return false; } if (namedCurve != null || customCurve || curveFile != null) { System.err.println("Specifying a curve for curve export makes no sense."); return false; } if (output == null) { System.err.println("You have to specify an output file for curve parameter export."); return false; } if (all) { System.err.println("You have to specify curve bit-size with -b"); return false; } } else if (cli.hasOption("generate")) { if (primeField == binaryField) { System.err.print("Need to specify field with -fp or -f2m. (not both)"); return false; } if (anyKeypart) { System.err.println("Keys should not be specified when generating keys."); return false; } if (output == null) { System.err.println("You have to specify an output file for the key generation process."); return false; } if (all) { System.err.println("You have to specify curve bit-size with -b"); return false; } generateAmount = Integer.parseInt(cli.getOptionValue("generate", "0")); if (generateAmount < 0) { System.err.println("Amount of keys generated cant be negative."); return false; } } else if (cli.hasOption("test")) { if (!(binaryField || primeField)) { binaryField = true; primeField = true; } testSuite = cli.getOptionValue("test", "default").toLowerCase(); String[] tests = new String[]{"default", "composite", "invalid", "test-vectors", "wrong"}; if (!Arrays.asList(tests).contains(testSuite)) { System.err.println("Unknown test suite " + testSuite + ". Should be one of: " + Arrays.toString(tests)); return false; } } else if (cli.hasOption("ecdh") || cli.hasOption("ecdhc")) { if (primeField == binaryField) { System.err.print("Need to specify field with -fp or -f2m. (not both)"); return false; } if (all) { System.err.println("You have to specify curve bit-size with -b"); return false; } if (cli.hasOption("ecdh")) { ECDHCount = Integer.parseInt(cli.getOptionValue("ecdh", "1")); ECDHKA = EC_Consts.KA_ECDH; } else if (cli.hasOption("ecdhc")) { ECDHCount = Integer.parseInt(cli.getOptionValue("ecdhc", "1")); ECDHKA = EC_Consts.KA_ECDHC; } if (ECDHCount <= 0) { System.err.println("ECDH count cannot be <= 0."); return false; } } else if (cli.hasOption("ecdsa")) { if (primeField == binaryField) { System.err.print("Need to specify field with -fp or -f2m. (but not both)"); return false; } if (all) { System.err.println("You have to specify curve bit-size with -b"); return false; } if ((anyPublicKey) != (anyPrivateKey) && !anyKey) { System.err.println("You cannot only specify a part of a keypair."); return false; } ECDSACount = Integer.parseInt(cli.getOptionValue("ecdsa", "1")); if (ECDSACount <= 0) { System.err.println("ECDSA count cannot be <= 0."); return false; } } return true; } } }