diff options
Diffstat (limited to 'src/cz/crcs/ectester/reader/ECTesterReader.java')
| -rw-r--r-- | src/cz/crcs/ectester/reader/ECTesterReader.java | 184 |
1 files changed, 81 insertions, 103 deletions
diff --git a/src/cz/crcs/ectester/reader/ECTesterReader.java b/src/cz/crcs/ectester/reader/ECTesterReader.java index 7a9d3eb..cb64b4c 100644 --- a/src/cz/crcs/ectester/reader/ECTesterReader.java +++ b/src/cz/crcs/ectester/reader/ECTesterReader.java @@ -1,6 +1,7 @@ /* * ECTester, tool for testing Elliptic curve cryptography implementations. - * Copyright (c) 2016-2018 Petr Svenda <petr@svenda.com> + * Copyright (c) 2016-2019 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 @@ -29,10 +30,8 @@ import cz.crcs.ectester.common.cli.Colors; import cz.crcs.ectester.common.ec.EC_Curve; import cz.crcs.ectester.common.output.OutputLogger; import cz.crcs.ectester.common.output.TestWriter; -import cz.crcs.ectester.common.util.ByteUtil; -import cz.crcs.ectester.common.util.CardUtil; -import cz.crcs.ectester.common.util.ECUtil; -import cz.crcs.ectester.common.util.FileUtil; +import cz.crcs.ectester.common.util.Util; +import cz.crcs.ectester.common.util.*; import cz.crcs.ectester.data.EC_Store; import cz.crcs.ectester.reader.command.Command; import cz.crcs.ectester.reader.output.FileTestWriter; @@ -77,13 +76,13 @@ public class ECTesterReader { public static final String VERSION = "v0.3.2"; public static String GIT_COMMIT = ""; private static String DESCRIPTION; - private static String LICENSE = "MIT Licensed\nCopyright (c) 2016-2018 Petr Svenda <petr@svenda.com>"; + private static String LICENSE = "MIT Licensed\nCopyright © 2016-2019 Petr Svenda <petr@svenda.com>\nCopyright © 2016-2019 Jan Jancar <johny@neuromancer.sk>"; private static String CLI_HEADER; private static 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[] SELECT_PREFIX = {(byte) 0x00, (byte) 0xa4, (byte) 0x04, (byte) 0x00, (byte) 0x0c}; + private static final byte[] AID_221 = {(byte) 0x45, (byte) 0x43, (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x65, (byte) 0x72, (byte) 0x30, (byte) 0x33, (byte) 0x32, (byte) 0x62}; // VERSION v0.3.2 + private static final byte[] AID_222 = {(byte) 0x45, (byte) 0x43, (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x65, (byte) 0x72, (byte) 0x30, (byte) 0x33, (byte) 0x32, (byte) 0x78}; // VERSION v0.3.2 private static final byte[] INSTALL_DATA = new byte[10]; static { @@ -137,20 +136,27 @@ public class ECTesterReader { //connect or simulate connection if (cfg.simulate) { - if (!cardManager.prepareLocalSimulatorApplet(AID, INSTALL_DATA, ECTesterApplet.class)) { + if (!cardManager.prepareLocalSimulatorApplet(AID_221, INSTALL_DATA, ECTesterApplet.class)) { System.err.println(Colors.error("Failed to establish a simulator.")); System.exit(1); + } else { + cardManager.setChunking(true); } } else { if (!cardManager.connectToCardSelect()) { System.err.println(Colors.error("Failed to connect to card.")); System.exit(1); } - ResponseAPDU selectResp = cardManager.send(SELECT_ECTESTERAPPLET); + ResponseAPDU selectResp = cardManager.send(ByteUtil.concatenate(SELECT_PREFIX, AID_222)); if ((short) selectResp.getSW() != ISO7816.SW_NO_ERROR) { - System.err.println(Colors.error("Failed to select ECTester applet, is it installed?")); - cardManager.disconnectFromCard(); - System.exit(1); + selectResp = cardManager.send(ByteUtil.concatenate(SELECT_PREFIX, AID_221)); + if ((short) selectResp.getSW() != ISO7816.SW_NO_ERROR) { + System.err.println(Colors.error("Failed to select ECTester applet, is it installed?")); + cardManager.disconnectFromCard(); + System.exit(1); + } else { + cardManager.setChunking(true); + } } } @@ -163,6 +169,9 @@ public class ECTesterReader { Security.addProvider(new BouncyCastleProvider()); } catch (SecurityException | NoClassDefFoundError ignored) { } + // Make BouncyCastle more lenient when we work with signatures in ASN.1 DER format, + // cards sometimes are not fully compliant. + System.setProperty("org.bouncycastle.asn1.allow_unsafe_integer", "true"); //do action if (cli.hasOption("export")) { @@ -239,54 +248,6 @@ public class ECTesterReader { * @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]] - * -dsa / --ecdsa [count] - * -ln / --list-named [obj] - * -ls / --list-suites - * -nfo / --info - * - * Options: - * -b / --bit-size <b> // -a / --all - * - * -fp / --prime-field - * -f2m / --binary-field - * - * -u / --custom - * -nc / --named-curve <cat/id> - * -c / --curve <curve_file> field,a,b,gx,gy,r,k - * - * -pub / --public <pubkey_file> wx,wy - * -npub / --named-public <cat/id> - * - * -priv / --private <privkey_file> s - * -npriv / --named-private <cat/id> - * - * -k / --key <key_file> wx,wy,s - * -nk / --named-key <cat/id> - * - * -v / --verbose - * - * -i / --input <input_file> - * -o / --output <output_file> - * --format <format> - * -l / --log [log_file] - * - * -f / --fresh - * --cleanup - * -s / --simulate - * -y / --yes - * -ka/ --ka-type <type> - * -sig/--sig-type <type> - * -C / --color - * -to/ --test-options <opts> - */ OptionGroup actions = new OptionGroup(); actions.setRequired(true); actions.addOption(Option.builder("V").longOpt("version").desc("Print version info.").build()); @@ -338,10 +299,11 @@ public class ECTesterReader { opts.addOption(Option.builder().longOpt("fixed-public").desc("Generate public key only once, keep it for later ECDH.").build()); opts.addOption(Option.builder("f").longOpt("fresh").desc("Generate fresh keys (set domain parameters before every generation).").build()); opts.addOption(Option.builder().longOpt("time").desc("Output better timing values, by running command in dry run mode and normal mode, and subtracting the two.").build()); + opts.addOption(Option.builder().longOpt("time-unit").desc("Use given time unit in measurement, one of: milli, micro, nano.").hasArg().argName("unit").build()); opts.addOption(Option.builder().longOpt("cleanup").desc("Send the cleanup command trigerring JCSystem.requestObjectDeletion() after some operations.").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("to").longOpt("test-options").desc("Test options to use:\n- preset: Use preset semi-random private keys instead of generating keypairs on the cards when the test needs one.").hasArg().argName("options").build()); + opts.addOption(Option.builder("to").longOpt("test-options").desc("Test options to use:\n- preset: Use preset semi-random private keys (derived from curve) instead of generating keypairs on the cards when the test needs one.\n- random: Use fully random private keys instead of generating keypairs.").hasArg().argName("options").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()); opts.addOption(Option.builder("sig").longOpt("sig-type").desc("Set Signature object [type], corresponds to JC.Signature constants.").hasArg().argName("type").optionalArg(true).build()); @@ -380,11 +342,13 @@ public class ECTesterReader { private void info() throws CardException { Response.GetInfo info = new Command.GetInfo(cardManager).send(); - System.out.println(String.format("ECTester applet version: %s", info.getVersion())); - System.out.println(String.format("ECTester applet APDU support: %s", (info.getBase() == ECTesterApplet.BASE_221) ? "basic" : "extended length")); - System.out.println(String.format("JavaCard API version: %.1f", info.getJavaCardVersion())); - System.out.println(String.format("JavaCard supports system cleanup: %s", info.getCleanupSupport())); - System.out.println(String.format("Array sizes (apduBuf, ram, ram2, apduArr): %d %d %d %d", info.getApduBufferLength(), info.getRamArrayLength(), info.getRamArray2Length(), info.getApduArrayLength())); + System.out.println(String.format("Card ATR:\t\t\t\t%s", ByteUtil.bytesToHex(cardManager.getATR().getBytes(), false))); + System.out.println(String.format("Card protocol:\t\t\t\t%s", cardManager.getProtocol())); + System.out.println(String.format("ECTester applet version:\t\t%s", info.getVersion())); + System.out.println(String.format("ECTester applet APDU support:\t\t%s", (info.getBase() == ECTesterApplet.BASE_221) ? "basic" : "extended length")); + System.out.println(String.format("JavaCard API version:\t\t\t%.1f", info.getJavaCardVersion())); + System.out.println(String.format("JavaCard supports system cleanup:\t%s", info.getCleanupSupport())); + System.out.println(String.format("Array sizes (apduBuf,ram,ram2,apduArr):\t%d %d %d %d", info.getApduBufferLength(), info.getRamArrayLength(), info.getRamArray2Length(), info.getApduArrayLength())); } /** @@ -402,7 +366,7 @@ public class ECTesterReader { sent.add(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL).send()); // Also support exporting set parameters, to verify they are set correctly. - Command curve = Command.prepareCurve(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_LOCAL, cfg.bits, keyClass); + Command curve = Command.prepareCurve(cardManager, cfg, ECTesterApplet.KEYPAIR_LOCAL, cfg.bits, keyClass); if (curve != null) { sent.add(curve.send()); } @@ -445,13 +409,13 @@ public class ECTesterReader { */ private void generate() throws CardException, IOException { byte keyClass = cfg.primeField ? KeyPair.ALG_EC_FP : KeyPair.ALG_EC_F2M; - Command curve = Command.prepareCurve(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_LOCAL, cfg.bits, keyClass); + Command curve = Command.prepareCurve(cardManager, cfg, ECTesterApplet.KEYPAIR_LOCAL, cfg.bits, keyClass); Response allocate = new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_LOCAL, cfg.bits, keyClass).send(); respWriter.outputResponse(allocate); OutputStreamWriter keysFile = FileUtil.openFiles(cfg.outputs); - keysFile.write("index;genTime[milli];exportTime[milli];pubW;privS\n"); + keysFile.write(String.format("index;genTime[%s];exportTime[%s];pubW;privS\n", cfg.timeUnit, cfg.timeUnit)); int generated = 0; int retry = 0; @@ -485,7 +449,7 @@ public class ECTesterReader { 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;%d;%s;%s\n", generated, time / 1000000, export.getDuration() / 1000000, pub, priv); + String line = String.format("%d;%d;%d;%s;%s\n", generated, Util.convertTime(time, cfg.timeUnit), Util.convertTime(export.getDuration(), cfg.timeUnit), pub, priv); keysFile.write(line); keysFile.flush(); generated++; @@ -518,7 +482,6 @@ public class ECTesterReader { case "compression": suite = new CardCompressionSuite(writer, cfg, cardManager); break; - case "misc": case "miscellaneous": suite = new CardMiscSuite(writer, cfg, cardManager); break; @@ -578,7 +541,7 @@ public class ECTesterReader { */ private void ecdh() throws IOException, CardException { byte keyClass = cfg.primeField ? KeyPair.ALG_EC_FP : KeyPair.ALG_EC_F2M; - Command curve = Command.prepareCurve(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_BOTH, cfg.bits, keyClass); + Command curve = Command.prepareCurve(cardManager, cfg, ECTesterApplet.KEYPAIR_BOTH, cfg.bits, keyClass); List<Response> prepare = new LinkedList<>(); prepare.add(new Command.AllocateKeyAgreement(cardManager, cfg.ECKAType).send()); // Prepare KeyAgreement or required type prepare.add(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, cfg.bits, keyClass).send()); @@ -592,17 +555,17 @@ public class ECTesterReader { OutputStreamWriter out = null; if (cfg.outputs != null) { out = FileUtil.openFiles(cfg.outputs); - out.write("index;time[milli];pubW;privS;secret\n"); + out.write(String.format("index;time[%s];pubW;privS;secret\n", cfg.timeUnit)); } Response gen = new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_BOTH).send(); respWriter.outputResponse(gen); if (cfg.anyPublicKey || cfg.anyKey) { - Response prep = Command.prepareKey(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_REMOTE).send(); + Response prep = Command.prepareKey(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.PARAMETER_W).send(); respWriter.outputResponse(prep); } if (cfg.anyPrivateKey || cfg.anyKey) { - Response prep = Command.prepareKey(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_LOCAL).send(); + Response prep = Command.prepareKey(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_S).send(); respWriter.outputResponse(prep); } @@ -630,10 +593,12 @@ public class ECTesterReader { respWriter.outputResponse(regen); } - Response.Export export = new Command.Export(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.KEY_BOTH, EC_Consts.PARAMETERS_KEYPAIR).send(); - respWriter.outputResponse(export); - byte[] pubkey_bytes = export.getParameter(ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.PARAMETER_W); - byte[] privkey_bytes = export.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_S); + Response.Export exportRemote = new Command.Export(cardManager, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.KEY_PUBLIC, EC_Consts.PARAMETER_W).send(); + respWriter.outputResponse(exportRemote); + Response.Export exportLocal = new Command.Export(cardManager, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.KEY_PRIVATE, EC_Consts.PARAMETER_S).send(); + respWriter.outputResponse(exportLocal); + byte[] pubkey_bytes = exportRemote.getParameter(ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.PARAMETER_W); + byte[] privkey_bytes = exportLocal.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_S); Command.ECDH perform = new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_TRUE, EC_Consts.TRANSFORMATION_NONE, cfg.ECKAType); @@ -658,7 +623,7 @@ public class ECTesterReader { if (out != null) { time += result.getDuration(); - out.write(String.format("%d;%d;%s;%s;%s\n", done, time / 1000000, ByteUtil.bytesToHex(pubkey_bytes, false), ByteUtil.bytesToHex(privkey_bytes, false), ByteUtil.bytesToHex(result.getSecret(), false))); + out.write(String.format("%d;%d;%s;%s;%s\n", done, Util.convertTime(time, cfg.timeUnit), ByteUtil.bytesToHex(pubkey_bytes, false), ByteUtil.bytesToHex(privkey_bytes, false), ByteUtil.bytesToHex(result.getSecret(), false))); out.flush(); } @@ -697,7 +662,7 @@ public class ECTesterReader { Command generate; if (cfg.anyKeypart) { - generate = Command.prepareKey(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_LOCAL); + generate = Command.prepareKey(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETERS_KEYPAIR); } else { generate = new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL); } @@ -706,7 +671,7 @@ public class ECTesterReader { List<Response> prepare = new LinkedList<>(); prepare.add(new Command.AllocateSignature(cardManager, cfg.ECDSAType).send()); prepare.add(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_LOCAL, cfg.bits, keyClass).send()); - Command curve = Command.prepareCurve(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_LOCAL, cfg.bits, keyClass); + Command curve = Command.prepareCurve(cardManager, cfg, ECTesterApplet.KEYPAIR_LOCAL, cfg.bits, keyClass); if (curve != null) prepare.add(curve.send()); @@ -716,7 +681,7 @@ public class ECTesterReader { OutputStreamWriter out = FileUtil.openFiles(cfg.outputs); if (out != null) { - out.write("index;signTime[milli];verifyTime[milli];data;pubW;privS;signature;nonce;valid\n"); + out.write(String.format("index;signTime[%s];verifyTime[%s];data;pubW;privS;signature;nonce;valid\n", cfg.timeUnit, cfg.timeUnit)); } Command.Export export = new Command.Export(cardManager, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.KEY_BOTH, EC_Consts.PARAMETERS_KEYPAIR); @@ -780,7 +745,7 @@ public class ECTesterReader { String priv = ByteUtil.bytesToHex(exported.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_S), false); String dataString = (cfg.input != null) ? "" : ByteUtil.bytesToHex(data, false); BigInteger privkey = new BigInteger(1, exported.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_S)); - EC_Curve actualCurve = Command.findCurve(EC_Store.getInstance(), cfg, cfg.bits, keyClass); + EC_Curve actualCurve = Command.findCurve(cfg, cfg.bits, keyClass); String k = ""; if (actualCurve != null) { ECParameterSpec params = actualCurve.toSpec(); @@ -789,7 +754,7 @@ public class ECTesterReader { k = ByteUtil.bytesToHex(kValue.toByteArray(), false); } } - out.write(String.format("%d;%d;%d;%s;%s;%s;%s;%s;%d\n", done, signTime / 1000000, verifyTime / 1000000, dataString, pub, priv, ByteUtil.bytesToHex(signature, false), k, verifyResp.successful() ? 1 : 0)); + out.write(String.format("%d;%d;%d;%s;%s;%s;%s;%s;%d\n", done, Util.convertTime(signTime, cfg.timeUnit), Util.convertTime(verifyTime, cfg.timeUnit), dataString, pub, priv, ByteUtil.bytesToHex(signature, false), k, verifyResp.successful() ? 1 : 0)); out.flush(); } @@ -845,6 +810,7 @@ public class ECTesterReader { public String[] outputs; public boolean fresh = false; public boolean time = false; + public String timeUnit; public boolean cleanup = false; public boolean simulate = false; public boolean yes = false; @@ -911,6 +877,13 @@ public class ECTesterReader { color = cli.hasOption("color"); Colors.enabled = color; + timeUnit = cli.getOptionValue("time-unit", "micro"); + String[] times = new String[]{"milli", "micro", "nano"}; + if (!Arrays.asList(times).contains(timeUnit)) { + System.err.println(Colors.error("Wrong time unit " + timeUnit + ". Should be one of " + Arrays.toString(times))); + return false; + } + if (cli.hasOption("list-named")) { listNamed = cli.getOptionValue("list-named"); return true; @@ -1011,40 +984,45 @@ public class ECTesterReader { testFrom = 0; testTo = -1; } - + String[] tests = new String[]{"default", "composite", "compression", "invalid", "degenerate", "test-vectors", "wrong", "twist", "cofactor", "edge-cases", "miscellaneous", "signature"}; - String selected = null; - for (String test : tests) { - if (test.startsWith(testSuite)) { - if (selected != null) { - System.err.println(Colors.error("Test suite ambiguous " + test + " or " + selected + "?")); - return false; - } else { - selected = test; - } - } - } - if (selected == null) { + String selected = null; + for (String test : tests) { + if (test.startsWith(testSuite)) { + if (selected != null) { + System.err.println(Colors.error("Test suite ambiguous " + test + " or " + selected + "?")); + return false; + } else { + selected = test; + } + } + } + if (selected == null) { System.err.println(Colors.error("Unknown test suite " + testSuite + ". Should be one of: " + Arrays.toString(tests))); return false; } else { - testSuite = selected; + testSuite = selected; } String[] opts = cli.getOptionValue("test-options", "").split(","); - List<String> validOpts = Arrays.asList("preset"); + List<String> validOpts = Arrays.asList("preset", "random"); testOptions = new HashSet<>(); for (String opt : opts) { if (opt.equals("")) { continue; } if (!validOpts.contains(opt)) { - System.err.println(Colors.error("Unknown test option " + opt + ". Should be one of: " + "preset.")); + System.err.println(Colors.error("Unknown test option " + opt + ". Should be one of: " + Arrays.toString(validOpts.toArray()))); return false; } else { testOptions.add(opt); } } + + if (testOptions.contains("preset") && testOptions.contains("random")) { + System.err.println("Cannot have both preset and random option enabled."); + return false; + } } else if (cli.hasOption("ecdh")) { if (primeField == binaryField) { System.err.print(Colors.error("Need to specify field with -fp or -f2m. (not both)")); |
