aboutsummaryrefslogtreecommitdiff
path: root/src/cz/crcs/ectester/reader/ECTesterReader.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/cz/crcs/ectester/reader/ECTesterReader.java')
-rw-r--r--src/cz/crcs/ectester/reader/ECTesterReader.java1104
1 files changed, 0 insertions, 1104 deletions
diff --git a/src/cz/crcs/ectester/reader/ECTesterReader.java b/src/cz/crcs/ectester/reader/ECTesterReader.java
deleted file mode 100644
index fdfb4cb..0000000
--- a/src/cz/crcs/ectester/reader/ECTesterReader.java
+++ /dev/null
@@ -1,1104 +0,0 @@
-/*
- * ECTester, tool for testing Elliptic curve cryptography implementations.
- * 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
- * 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.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.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;
-import cz.crcs.ectester.reader.output.ResponseWriter;
-import cz.crcs.ectester.reader.response.Response;
-import cz.crcs.ectester.reader.test.*;
-import javacard.framework.ISO7816;
-import javacard.security.KeyPair;
-import org.apache.commons.cli.*;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-
-import javax.smartcardio.CardException;
-import javax.smartcardio.ResponseAPDU;
-import javax.xml.parsers.ParserConfigurationException;
-import java.io.*;
-import java.math.BigInteger;
-import java.net.URL;
-import java.nio.file.Files;
-import java.security.Security;
-import java.security.spec.ECParameterSpec;
-import java.util.*;
-import java.util.jar.Manifest;
-
-import static cz.crcs.ectester.applet.EC_Consts.KeyAgreement_ALG_EC_SVDP_DH;
-import static cz.crcs.ectester.applet.EC_Consts.Signature_ALG_ECDSA_SHA;
-
-/**
- * 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.3.3
- */
-public class ECTesterReader {
- private CardMngr cardManager;
- private OutputLogger logger;
- private ResponseWriter respWriter;
- private Config cfg;
-
- private Options opts = new Options();
- public static final String VERSION = "v0.3.3";
- public static String GIT_COMMIT = "";
- private static String DESCRIPTION;
- 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_PREFIX = {(byte) 0x00, (byte) 0xa4, (byte) 0x04, (byte) 0x00, (byte) 0x0c};
- private static final byte[] AID_PREFIX = {(byte) 0x45, (byte) 0x43, (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x65, (byte) 0x72};
- private static final byte[] AID_CURRENT_VERSION = {(byte) 0x30, (byte) 0x33, (byte) 0x33}; // VERSION v0.3.3
- private static final byte[] AID_SUFFIX_221 = {(byte) 0x62};
- private static final byte[] AID_SUFFIX_222 = {(byte) 0x78};
- private static final byte[] INSTALL_DATA = new byte[10];
- private static final int TRY_VERSIONS = 10;
-
- static {
- ClassLoader cl = ECTesterReader.class.getClassLoader();
- try {
- URL url = cl.getResource("META-INF/MANIFEST.MF");
- Manifest manifest = new Manifest(url.openStream());
- String commit = manifest.getMainAttributes().getValue("Git-Commit");
- GIT_COMMIT = (commit == null) ? "" : "(git " + commit + ")";
- } catch (Exception ignored) {
- }
-
- DESCRIPTION = "ECTesterReader " + VERSION + GIT_COMMIT + ", a javacard Elliptic Curve Cryptography support tester/utility.";
- CLI_HEADER = "\n" + DESCRIPTION + "\n\n";
- }
-
- private void run(String[] args) {
- try {
- CommandLine cli = parseArgs(args);
-
- cfg = new Config();
- boolean optsOk = cfg.readOptions(cli);
-
- //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;
- }
-
- //if opts failed, quit
- if (!optsOk) {
- return;
- }
-
- //if list, print and quit
- if (cli.hasOption("list-named")) {
- CLITools.listNamed(EC_Store.getInstance(), cli.getOptionValue("list-named"));
- return;
- }
-
- if (cli.hasOption("list-suites")) {
- listSuites();
- return;
- }
-
- //init CardManager
- cardManager = new CardMngr(cfg.verbose, cfg.simulate);
-
- //connect or simulate connection
- if (cfg.simulate) {
- if (!cardManager.prepareLocalSimulatorApplet(ByteUtil.concatenate(AID_PREFIX, AID_CURRENT_VERSION, AID_SUFFIX_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);
- }
- //Try the highest known version first
- byte[] versionByte = AID_CURRENT_VERSION.clone();
- boolean selected = false;
- for (int i = 0; i < TRY_VERSIONS; ++i) {
- byte[] select222 = ByteUtil.concatenate(SELECT_PREFIX, AID_PREFIX, versionByte, AID_SUFFIX_222);
- ResponseAPDU selectResp = cardManager.send(select222);
- if ((short) selectResp.getSW() != ISO7816.SW_NO_ERROR) {
- byte[] select221 = ByteUtil.concatenate(SELECT_PREFIX, AID_PREFIX, versionByte, AID_SUFFIX_221);
- selectResp = cardManager.send(select221);
- if ((short) selectResp.getSW() == ISO7816.SW_NO_ERROR) {
- cardManager.setChunking(true);
- selected = true;
- break;
- }
- } else {
- selected = true;
- break;
- }
- // Count down by versions
- if (versionByte[2] == 0x30) {
- if (versionByte[1] == 0x30) {
- if (versionByte[0] == 0x30) {
- break;
- } else {
- versionByte[0]--;
- versionByte[1] = 0x39;
- versionByte[2] = 0x39;
- }
- } else {
- versionByte[1]--;
- versionByte[2] = 0x39;
- }
- } else {
- versionByte[2]--;
- }
- }
- if (!selected) {
- System.err.println(Colors.error("Failed to select ECTester applet, is it installed?"));
- cardManager.disconnectFromCard();
- System.exit(1);
- }
- }
-
- // Setup logger and respWriter
- logger = new OutputLogger(true, cfg.log);
- respWriter = new ResponseWriter(logger.getPrintStream());
-
- // Try adding the BouncyCastleProvider, which might be used in some parts of ECTester.
- try {
- 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")) {
- 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();
- } else if (cli.hasOption("info")) {
- info();
- }
-
- //disconnect
- cardManager.disconnectFromCard();
- logger.close();
-
- } catch (MissingOptionException moex) {
- System.err.println(Colors.error("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(Colors.error("Option, " + maex.getOption().getOpt() + " requires an argument: " + maex.getOption().getArgName()));
- } catch (NumberFormatException nfex) {
- System.err.println(Colors.error("Not a number. " + nfex.getMessage()));
- } catch (FileNotFoundException fnfe) {
- System.err.println(Colors.error("File " + fnfe.getMessage() + " not found."));
- } catch (ParseException | IOException ex) {
- System.err.println(Colors.error(ex.getMessage()));
- } catch (CardException ex) {
- if (logger != null)
- logger.println(ex.getMessage());
- ex.printStackTrace();
- } 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 {
- 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("ls").longOpt("list-suites").desc("List supported test suites.").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. Optionally specify a test number to run only a part of a test suite. <test_suite>:\n- default\n- compression\n- invalid\n- twist\n- degenerate\n- cofactor\n- wrong\n- signature\n- composite\n- test-vectors\n- edge-cases\n- miscellaneous").hasArg().argName("test_suite[:from[:to]]").optionalArg(true).build());
- actions.addOption(Option.builder("dh").longOpt("ecdh").desc("Do EC KeyAgreement (ECDH...), [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());
- actions.addOption(Option.builder("nf").longOpt("info").desc("Get applet info.").build());
-
- opts.addOptionGroup(actions);
-
- opts.addOption(Option.builder("b").longOpt("bit-size").desc("Set curve size.").hasArg().argName("bits").build());
- 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: <cat/id>").hasArg().argName("cat/id").build());
- curve.addOption(Option.builder("c").longOpt("curve").desc("Use curve from file <curve_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: <cat/id>").hasArg().argName("cat/id").build());
- pub.addOption(Option.builder("pub").longOpt("public").desc("Use public key from file <pubkey_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: <cat/id>").hasArg().argName("cat/id").build());
- priv.addOption(Option.builder("priv").longOpt("private").desc("Use private key from file <privkey_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: <cat/id>").hasArg().argName("cat/id").build());
- key.addOption(Option.builder("k").longOpt("key").desc("Use keyPair from file <key_file> (wx,wy,s).").hasArg().argName("key_file").build());
- opts.addOptionGroup(key);
-
- opts.addOption(Option.builder("i").longOpt("input").desc("Input from file <input_file>, for ECDSA signing.").hasArg().argName("input_file").build());
- opts.addOption(Option.builder("o").longOpt("output").desc("Output into file <output_file>. The file can be prefixed by the format (one of text,yml,xml), such as: xml:<output_file>.").hasArgs().argName("output_file").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("kb").longOpt("key-builder").desc("Allocate KeyPair using KeyBuilder.").build());
- opts.addOption(Option.builder().longOpt("fixed").desc("Generate key(s) only once, keep them for later operations.").build());
- opts.addOption(Option.builder().longOpt("fixed-private").desc("Generate private key only once, keep it for later ECDH.").build());
- 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 (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());
- opts.addOption(Option.builder("C").longOpt("color").desc("Print stuff with color, requires ANSI terminal.").build());
-
- CommandLineParser parser = new DefaultParser();
- return parser.parse(opts, args);
- }
-
- private void listSuites() {
- CardTestSuite[] suites = new CardTestSuite[]{
- new CardDefaultSuite(null, null, null),
- new CardTestVectorSuite(null, null, null),
- new CardCompressionSuite(null, null, null),
- new CardWrongSuite(null, null, null),
- new CardDegenerateSuite(null, null, null),
- new CardCofactorSuite(null, null, null),
- new CardCompositeSuite(null, null, null),
- new CardInvalidSuite(null, null, null),
- new CardEdgeCasesSuite(null, null, null),
- new CardSignatureSuite(null, null, null),
- new CardTwistSuite(null, null, null),
- new CardMiscSuite(null, null, null)};
- for (CardTestSuite suite : suites) {
- System.out.println(" - " + Colors.bold(suite.getName()));
- for (String line : suite.getDescription()) {
- System.out.println("\t" + line);
- }
- if (suite.getOptions() != null) {
- System.out.println("\t" + Colors.underline("Options:") + " " + Arrays.toString(suite.getOptions()));
- }
- }
- System.out.println();
- System.out.println("For more information, look at the documentation at https://github.com/crocs-muni/ECTester.");
- }
-
- private void info() throws CardException {
- Response.GetInfo info = new Command.GetInfo(cardManager).send();
- 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()));
- }
-
- /**
- * 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<Response> sent = new LinkedList<>();
- sent.add(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_LOCAL, cfg.keyBuilder, cfg.bits, keyClass).send());
- //sent.add(new Command.Clear(cardManager, ECTesterApplet.KEYPAIR_LOCAL).send());
- 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, cfg, ECTesterApplet.KEYPAIR_LOCAL, cfg.bits, keyClass);
- if (curve != null) {
- sent.add(curve.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 domain = cfg.primeField ? EC_Consts.PARAMETERS_DOMAIN_FP : EC_Consts.PARAMETERS_DOMAIN_F2M;
- Response.Export export = new Command.Export(cardManager, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.KEY_PUBLIC, domain).send();
- if (!export.successful()) {
- domain = (short) (domain ^ EC_Consts.PARAMETER_K);
- export = new Command.Export(cardManager, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.KEY_PUBLIC, domain).send();
- }
- sent.add(export);
-
- for (Response r : sent) {
- respWriter.outputResponse(r);
- }
- if (cfg.cleanup) {
- Response cleanup = new Command.Cleanup(cardManager).send();
- respWriter.outputResponse(cleanup);
- }
-
- PrintStream out = new PrintStream(FileUtil.openStream(cfg.outputs));
- byte[][] params = export.getParams();
- for (int i = 0; i < params.length; ++i) {
- out.print(ByteUtil.bytesToHex(params[i], false));
- if (i != params.length - 1) {
- out.print(",");
- }
- }
- 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;
- Command curve = Command.prepareCurve(cardManager, cfg, ECTesterApplet.KEYPAIR_LOCAL, cfg.bits, keyClass);
-
- Response allocate = new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_LOCAL, cfg.keyBuilder, cfg.bits, keyClass).send();
- respWriter.outputResponse(allocate);
-
- OutputStreamWriter keysFile = FileUtil.openFiles(cfg.outputs);
- keysFile.write(String.format("index;genTime[%s];exportTime[%s];pubW;privS\n", cfg.timeUnit, cfg.timeUnit));
-
- 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);
- long time = 0;
- if (cfg.time) {
- time = -Command.dryRunTime(cardManager, generate, 2, respWriter);
- }
- Response.Generate response = generate.send();
- time += response.getDuration();
- respWriter.outputResponse(response);
-
- Response.Export export = new Command.Export(cardManager, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.KEY_BOTH, EC_Consts.PARAMETERS_KEYPAIR).send();
- respWriter.outputResponse(export);
-
- if (!response.successful() || !export.successful()) {
- if (retry < 10) {
- retry++;
- continue;
- } else {
- System.err.println(Colors.error("Keys could not be generated/exported."));
- break;
- }
- }
-
- 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, Util.convertTime(time, cfg.timeUnit), Util.convertTime(export.getDuration(), cfg.timeUnit), pub, priv);
- keysFile.write(line);
- keysFile.flush();
- generated++;
- }
- if (cfg.cleanup) {
- Response cleanup = new Command.Cleanup(cardManager).send();
- respWriter.outputResponse(cleanup);
- }
-
- keysFile.close();
- }
-
- /**
- * Tests Elliptic curve support for a given curve/curves.
- *
- * @throws IOException if an IO error occurs
- */
- private void test() throws ParserConfigurationException, IOException {
- TestWriter writer = new FileTestWriter(cfg.format, true, cfg.outputs);
-
- CardTestSuite suite;
-
- switch (cfg.testSuite) {
- case "default":
- suite = new CardDefaultSuite(writer, cfg, cardManager);
- break;
- case "test-vectors":
- suite = new CardTestVectorSuite(writer, cfg, cardManager);
- break;
- case "compression":
- suite = new CardCompressionSuite(writer, cfg, cardManager);
- break;
- case "miscellaneous":
- suite = new CardMiscSuite(writer, cfg, cardManager);
- break;
- case "signature":
- suite = new CardSignatureSuite(writer, cfg, cardManager);
- break;
- default:
- // These run are dangerous, prompt before them.
- System.out.println("The test you selected (" + cfg.testSuite + ") is potentially dangerous.");
- System.out.println("Some of these run have caused temporary(or even permanent) 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 CardWrongSuite(writer, cfg, cardManager);
- break;
- case "composite":
- suite = new CardCompositeSuite(writer, cfg, cardManager);
- break;
- case "invalid":
- suite = new CardInvalidSuite(writer, cfg, cardManager);
- break;
- case "degenerate":
- suite = new CardDegenerateSuite(writer, cfg, cardManager);
- break;
- case "twist":
- suite = new CardTwistSuite(writer, cfg, cardManager);
- break;
- case "cofactor":
- suite = new CardCofactorSuite(writer, cfg, cardManager);
- break;
- case "edge-cases":
- suite = new CardEdgeCasesSuite(writer, cfg, cardManager);
- break;
- default:
- System.err.println(Colors.error("Unknown test suite."));
- return;
- }
- break;
- }
-
- suite.run(cfg.testFrom, cfg.testTo);
- }
-
- /**
- * 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;
- 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.keyBuilder, cfg.bits, keyClass).send());
- if (curve != null)
- prepare.add(curve.send());
-
- for (Response r : prepare) {
- respWriter.outputResponse(r);
- }
-
- OutputStreamWriter out = null;
- if (cfg.outputs != null) {
- out = FileUtil.openFiles(cfg.outputs);
- out.write(String.format("index;time[%s];pubW;privS;secret[%s]\n", cfg.timeUnit, CardUtil.getKexHashName(cfg.ECKAType)));
- }
-
- 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, 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, EC_Consts.PARAMETER_S).send();
- respWriter.outputResponse(prep);
- }
-
- byte kp = ECTesterApplet.KEYPAIR_BOTH;
- if (cfg.fixedPrivate || cfg.anyPrivateKey) {
- kp ^= ECTesterApplet.KEYPAIR_LOCAL;
- }
- if (cfg.fixedPublic || cfg.anyPublicKey) {
- kp ^= ECTesterApplet.KEYPAIR_REMOTE;
- }
- if (cfg.fixedKey || cfg.anyKey) {
- kp = 0;
- }
-
- Command generate = null;
- if (kp != 0) {
- generate = new Command.Generate(cardManager, kp);
- }
-
- int retry = 0;
- int done = 0;
- while (done < cfg.ECKACount || cfg.ECKACount == 0) {
- if (generate != null) {
- Response regen = generate.send();
- respWriter.outputResponse(regen);
- }
-
- 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);
-
- long time = 0;
- if (cfg.time) {
- time = -Command.dryRunTime(cardManager, perform, 2, respWriter);
- }
-
- Response.ECDH result = perform.send();
- respWriter.outputResponse(result);
-
- if (!result.successful() || !result.hasSecret()) {
- if (retry < 10) {
- ++retry;
- continue;
- } else {
- System.err.println(Colors.error("Couldn't obtain ECDH secret from card response."));
- break;
- }
- }
-
- if (out != null) {
- time += result.getDuration();
-
- 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();
- }
-
- ++done;
- }
- if (cfg.cleanup) {
- 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;
- 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());
- } else {
- Random rand = new Random();
- data = new byte[32];
- rand.nextBytes(data);
- }
-
- Command generate;
- if (cfg.anyKeypart) {
- generate = Command.prepareKey(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETERS_KEYPAIR);
- } else {
- generate = new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL);
- }
-
- byte keyClass = cfg.primeField ? KeyPair.ALG_EC_FP : KeyPair.ALG_EC_F2M;
- List<Response> prepare = new LinkedList<>();
- prepare.add(new Command.AllocateSignature(cardManager, cfg.ECDSAType).send());
- prepare.add(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_LOCAL, cfg.keyBuilder, cfg.bits, keyClass).send());
- Command curve = Command.prepareCurve(cardManager, cfg, ECTesterApplet.KEYPAIR_LOCAL, cfg.bits, keyClass);
- if (curve != null)
- prepare.add(curve.send());
-
- for (Response r : prepare) {
- respWriter.outputResponse(r);
- }
-
- OutputStreamWriter out = FileUtil.openFiles(cfg.outputs);
- if (out != null) {
- out.write(String.format("index;signTime[%s];verifyTime[%s];data;pubW;privS;signature[%s];nonce;valid\n", cfg.timeUnit, cfg.timeUnit, CardUtil.getSigHashAlgo(cfg.ECDSAType)));
- }
-
- Command.Export export = new Command.Export(cardManager, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.KEY_BOTH, EC_Consts.PARAMETERS_KEYPAIR);
- Response.Export exported = null;
- if (cfg.fixedKey) {
- respWriter.outputResponse(generate.send());
- exported = export.send();
- respWriter.outputResponse(exported);
- }
-
- int retry = 0;
- int done = 0;
- while (done < cfg.ECDSACount || cfg.ECDSACount == 0) {
- if (!cfg.fixedKey) {
- respWriter.outputResponse(generate.send());
- exported = export.send();
- respWriter.outputResponse(exported);
- }
-
- Command.ECDSA_sign sign = new Command.ECDSA_sign(cardManager, ECTesterApplet.KEYPAIR_LOCAL, cfg.ECDSAType, ECTesterApplet.EXPORT_TRUE, data);
-
- long signTime = 0;
- if (cfg.time) {
- signTime = -Command.dryRunTime(cardManager, sign, 2, respWriter);
- }
-
- Response.ECDSA signResp = sign.send();
- signTime += signResp.getDuration();
- respWriter.outputResponse(signResp);
- if (!signResp.successful() || !signResp.hasSignature()) {
- if (retry < 10) {
- ++retry;
- continue;
- } else {
- System.err.println(Colors.error("Couldn't obtain ECDSA signature from card response."));
- break;
- }
- }
- byte[] signature = signResp.getSignature();
- Command.ECDSA_verify verify = new Command.ECDSA_verify(cardManager, ECTesterApplet.KEYPAIR_LOCAL, cfg.ECDSAType, data, signature);
- long verifyTime = 0;
- if (cfg.time) {
- verifyTime = -Command.dryRunTime(cardManager, verify, 2, respWriter);
- }
- Response.ECDSA verifyResp = verify.send();
- verifyTime += verifyResp.getDuration();
- respWriter.outputResponse(verifyResp);
-
- if (verifyResp.error()) {
- if (retry < 10) {
- ++retry;
- continue;
- } else {
- System.err.println(Colors.error("Couldn't obtain ECDSA signature from card response."));
- break;
- }
- }
-
- if (out != null) {
- String pub = ByteUtil.bytesToHex(exported.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_W), false);
- 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(cfg, cfg.bits, keyClass);
- String k = "";
- if (actualCurve != null) {
- ECParameterSpec params = actualCurve.toSpec();
- BigInteger kValue = ECUtil.recoverSignatureNonce(signature, data, privkey, params, CardUtil.getSigHashName(cfg.ECDSAType), "ECDSA");
- if (kValue != null) {
- k = ByteUtil.bytesToHex(kValue.toByteArray(), false);
- }
- }
- 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();
- }
-
- ++done;
- }
- if (cfg.cleanup) {
- 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 short bits;
- public boolean all;
- public boolean primeField = false;
- public boolean binaryField = false;
-
-
- 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 boolean fixedKey = false;
- public boolean fixedPrivate = false;
- public boolean fixedPublic = false;
- public byte keyBuilder;
-
- public String log;
-
- public boolean verbose = false;
- public String input;
- 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;
- public String format;
- public boolean color;
-
- //Action-related options
- public String listNamed;
- public String testSuite;
- public int testFrom;
- public int testTo;
- public int generateAmount;
- public int ECKACount;
- public byte ECKAType = KeyAgreement_ALG_EC_SVDP_DH;
- public int ECDSACount;
- public byte ECDSAType = Signature_ALG_ECDSA_SHA;
- public Set<String> testOptions;
-
- /**
- * 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 = Short.parseShort(cli.getOptionValue("bit-size", "0"));
- all = cli.hasOption("all");
- primeField = cli.hasOption("fp");
- binaryField = cli.hasOption("f2m");
-
-
- 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;
- fixedKey = cli.hasOption("fixed");
- fixedPrivate = cli.hasOption("fixed-private");
- fixedPublic = cli.hasOption("fixed-public");
- keyBuilder = cli.hasOption("key-builder") ? ECTesterApplet.BUILD_KEYBUILDER : ECTesterApplet.BUILD_KEYPAIR;
-
- 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");
- outputs = cli.getOptionValues("output");
- fresh = cli.hasOption("fresh");
- time = cli.hasOption("time");
- cleanup = cli.hasOption("cleanup");
- simulate = cli.hasOption("simulate");
- yes = cli.hasOption("yes");
- 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;
- }
-
- format = cli.getOptionValue("format");
- String[] formats = new String[]{"text", "xml", "yaml", "yml"};
- if (format != null && !Arrays.asList(formats).contains(format)) {
- System.err.println(Colors.error("Wrong output format " + format + ". Should be one of " + Arrays.toString(formats)));
- return false;
- }
-
- if ((key != null || namedKey != null) && (anyPublicKey || anyPrivateKey)) {
- System.err.print(Colors.error("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(Colors.error("Bit-size must not be negative."));
- return false;
- }
-
- if (key != null && namedKey != null || publicKey != null && namedPublicKey != null || privateKey != null && namedPrivateKey != null) {
- System.err.println(Colors.error("You cannot specify both a named key and a key file."));
- return false;
- }
-
- if (cli.hasOption("export")) {
- if (primeField == binaryField) {
- System.err.print(Colors.error("Need to specify field with -fp or -f2m. (not both)"));
- return false;
- }
- if (anyKeypart) {
- System.err.println(Colors.error("Keys should not be specified when exporting curve params."));
- return false;
- }
- if (outputs == null) {
- System.err.println(Colors.error("You have to specify an output file for curve parameter export."));
- return false;
- }
- if (all || bits == 0) {
- System.err.println(Colors.error("You have to specify curve bit-size with -b"));
- return false;
- }
- } else if (cli.hasOption("generate")) {
- if (primeField == binaryField) {
- System.err.print(Colors.error("Need to specify field with -fp or -f2m. (not both)"));
- return false;
- }
- if (anyKeypart) {
- System.err.println(Colors.error("Keys should not be specified when generating keys."));
- return false;
- }
- if (outputs == null) {
- System.err.println(Colors.error("You have to specify an output file for the key generation process."));
- return false;
- }
- if (all || bits == 0) {
- System.err.println(Colors.error("You have to specify curve bit-size with -b"));
- return false;
- }
-
- generateAmount = Integer.parseInt(cli.getOptionValue("generate", "0"));
- if (generateAmount < 0) {
- System.err.println(Colors.error("Amount of keys generated cant be negative."));
- return false;
- }
- } else if (cli.hasOption("test")) {
- if (!(binaryField || primeField)) {
- binaryField = true;
- primeField = true;
- }
-
- String suiteOpt = cli.getOptionValue("test", "default").toLowerCase();
- if (suiteOpt.contains(":")) {
- String[] parts = suiteOpt.split(":");
- testSuite = parts[0];
- try {
- testFrom = Integer.parseInt(parts[1]);
- } catch (NumberFormatException nfe) {
- System.err.println("Invalid test_from number: " + parts[1] + ".");
- return false;
- }
- if (parts.length == 3) {
- try {
- testTo = Integer.parseInt(parts[2]);
- } catch (NumberFormatException nfe) {
- System.err.println("Invalid test_to number: " + parts[2] + ".");
- return false;
- }
- } else if (parts.length != 2) {
- System.err.println("Invalid test suite selection.");
- return false;
- } else {
- testTo = -1;
- }
- } else {
- testSuite = suiteOpt;
- 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) {
- System.err.println(Colors.error("Unknown test suite " + testSuite + ". Should be one of: " + Arrays.toString(tests)));
- return false;
- } else {
- testSuite = selected;
- }
-
- String[] opts = cli.getOptionValue("test-options", "").split(",");
- 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: " + 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)"));
- return false;
- }
- if (all || bits == 0) {
- System.err.println(Colors.error("You have to specify curve bit-size with -b"));
- return false;
- }
-
- ECKACount = Integer.parseInt(cli.getOptionValue("ecdh", "1"));
- if (ECKACount < 0) {
- System.err.println(Colors.error("ECDH count cannot be < 0."));
- return false;
- }
-
- ECKAType = CardUtil.parseKAType(cli.getOptionValue("ka-type", "1"));
- } else if (cli.hasOption("ecdsa")) {
- if (primeField == binaryField) {
- System.err.print(Colors.error("Need to specify field with -fp or -f2m. (but not both)"));
- return false;
- }
- if (all || bits == 0) {
- System.err.println(Colors.error("You have to specify curve bit-size with -b"));
- return false;
- }
-
- if ((anyPublicKey) != (anyPrivateKey) && !anyKey) {
- System.err.println(Colors.error("You cannot only specify a part of a keypair."));
- return false;
- }
-
- ECDSACount = Integer.parseInt(cli.getOptionValue("ecdsa", "1"));
- if (ECDSACount < 0) {
- System.err.println(Colors.error("ECDSA count cannot be < 0."));
- return false;
- }
-
- ECDSAType = CardUtil.parseSigType(cli.getOptionValue("sig-type", "17"));
- }
- return true;
- }
- }
-}