aboutsummaryrefslogtreecommitdiff
path: root/src/cz/crcs/ectester/reader
diff options
context:
space:
mode:
authorJ08nY2018-07-29 18:34:58 +0200
committerJ08nY2018-07-29 18:34:58 +0200
commitd24630d759bb16f715564ab80a5d4447f57d03f2 (patch)
treec30699e723a8ed31ea354be7e76eb16c1b681f04 /src/cz/crcs/ectester/reader
parentcb6c6b8b1274fe5a340c4317a4b015ea0ef15396 (diff)
parent07d0c8947ef0d0f4c0ae01c1d8699d24a892752d (diff)
downloadECTester-d24630d759bb16f715564ab80a5d4447f57d03f2.tar.gz
ECTester-d24630d759bb16f715564ab80a5d4447f57d03f2.tar.zst
ECTester-d24630d759bb16f715564ab80a5d4447f57d03f2.zip
Merge branch 'devel'
Diffstat (limited to 'src/cz/crcs/ectester/reader')
-rw-r--r--src/cz/crcs/ectester/reader/CardMngr.java11
-rw-r--r--src/cz/crcs/ectester/reader/ECTesterReader.java361
-rw-r--r--src/cz/crcs/ectester/reader/command/Command.java406
-rw-r--r--src/cz/crcs/ectester/reader/output/FileTestWriter.java53
-rw-r--r--src/cz/crcs/ectester/reader/output/TextTestWriter.java13
-rw-r--r--src/cz/crcs/ectester/reader/output/XMLTestWriter.java13
-rw-r--r--src/cz/crcs/ectester/reader/output/YAMLTestWriter.java24
-rw-r--r--src/cz/crcs/ectester/reader/response/Response.java204
-rw-r--r--src/cz/crcs/ectester/reader/test/CardCofactorSuite.java77
-rw-r--r--src/cz/crcs/ectester/reader/test/CardCompositeCurvesSuite.java51
-rw-r--r--src/cz/crcs/ectester/reader/test/CardCompositeSuite.java116
-rw-r--r--src/cz/crcs/ectester/reader/test/CardCompressionSuite.java122
-rw-r--r--src/cz/crcs/ectester/reader/test/CardDefaultSuite.java89
-rw-r--r--src/cz/crcs/ectester/reader/test/CardDegenerateSuite.java59
-rw-r--r--src/cz/crcs/ectester/reader/test/CardEdgeCasesSuite.java189
-rw-r--r--src/cz/crcs/ectester/reader/test/CardInvalidCurvesSuite.java67
-rw-r--r--src/cz/crcs/ectester/reader/test/CardInvalidSuite.java81
-rw-r--r--src/cz/crcs/ectester/reader/test/CardMiscSuite.java60
-rw-r--r--src/cz/crcs/ectester/reader/test/CardTestSuite.java2
-rw-r--r--src/cz/crcs/ectester/reader/test/CardTestVectorSuite.java20
-rw-r--r--src/cz/crcs/ectester/reader/test/CardTwistSuite.java75
-rw-r--r--src/cz/crcs/ectester/reader/test/CardTwistTestSuite.java62
-rw-r--r--src/cz/crcs/ectester/reader/test/CardWrongCurvesSuite.java58
-rw-r--r--src/cz/crcs/ectester/reader/test/CardWrongSuite.java227
-rw-r--r--src/cz/crcs/ectester/reader/test/CommandTest.java18
-rw-r--r--src/cz/crcs/ectester/reader/test/CommandTestable.java2
-rw-r--r--src/cz/crcs/ectester/reader/test/PerformanceTest.java29
27 files changed, 1768 insertions, 721 deletions
diff --git a/src/cz/crcs/ectester/reader/CardMngr.java b/src/cz/crcs/ectester/reader/CardMngr.java
index 1e42c52..921a9c8 100644
--- a/src/cz/crcs/ectester/reader/CardMngr.java
+++ b/src/cz/crcs/ectester/reader/CardMngr.java
@@ -1,9 +1,9 @@
package cz.crcs.ectester.reader;
-import com.licel.jcardsim.io.CAD;
import com.licel.jcardsim.io.JavaxSmartCardInterface;
import cz.crcs.ectester.common.util.ByteUtil;
import javacard.framework.AID;
+import javacard.framework.Applet;
import javacard.framework.ISO7816;
import javax.smartcardio.*;
@@ -19,7 +19,6 @@ public class CardMngr {
private Card card = null;
// Simulator related attributes
- private CAD cad = null;
private JavaxSmartCardInterface simulator = null;
private boolean simulate = false;
@@ -351,13 +350,11 @@ public class CardMngr {
return sendAPDU(commandAPDU);
}
- public boolean prepareLocalSimulatorApplet(byte[] appletAIDArray, byte[] installData, Class appletClass) {
- System.setProperty("com.licel.jcardsim.terminal.type", "2");
- cad = new CAD(System.getProperties());
- simulator = (JavaxSmartCardInterface) cad.getCardInterface();
+ public boolean prepareLocalSimulatorApplet(byte[] appletAIDArray, byte[] installData, Class<? extends Applet> appletClass) {
+ simulator = new JavaxSmartCardInterface();
AID appletAID = new AID(appletAIDArray, (short) 0, (byte) appletAIDArray.length);
- AID appletAIDRes = simulator.installApplet(appletAID, appletClass, installData, (short) 0, (byte) installData.length);
+ simulator.installApplet(appletAID, appletClass, installData, (short) 0, (byte) installData.length);
return simulator.selectApplet(appletAID);
}
diff --git a/src/cz/crcs/ectester/reader/ECTesterReader.java b/src/cz/crcs/ectester/reader/ECTesterReader.java
index 5e3a3fe..4a7d779 100644
--- a/src/cz/crcs/ectester/reader/ECTesterReader.java
+++ b/src/cz/crcs/ectester/reader/ECTesterReader.java
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 2016-2017 Petr Svenda <petr@svenda.com>
+ * ECTester, tool for testing Elliptic curve cryptography implementations.
+ * Copyright (c) 2016-2018 Petr Svenda <petr@svenda.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,18 +25,17 @@ 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_Params;
import cz.crcs.ectester.common.output.OutputLogger;
import cz.crcs.ectester.common.output.TestWriter;
-import cz.crcs.ectester.common.test.TestException;
import cz.crcs.ectester.common.util.ByteUtil;
import cz.crcs.ectester.common.util.CardUtil;
+import cz.crcs.ectester.common.util.FileUtil;
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.output.TextTestWriter;
-import cz.crcs.ectester.reader.output.XMLTestWriter;
-import cz.crcs.ectester.reader.output.YAMLTestWriter;
import cz.crcs.ectester.reader.response.Response;
import cz.crcs.ectester.reader.test.*;
import javacard.security.KeyPair;
@@ -44,21 +44,24 @@ import org.apache.commons.cli.*;
import javax.smartcardio.CardException;
import javax.xml.parsers.ParserConfigurationException;
import java.io.*;
+import java.net.URL;
+import java.net.URLClassLoader;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
+import java.util.jar.Manifest;
-import static cz.crcs.ectester.applet.ECTesterApplet.KeyAgreement_ALG_EC_SVDP_DH;
-import static cz.crcs.ectester.applet.ECTesterApplet.Signature_ALG_ECDSA_SHA;
+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.1.0
+ * @version v0.2.0
*/
public class ECTesterReader {
private CardMngr cardManager;
@@ -67,21 +70,40 @@ public class ECTesterReader {
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 <petr@svenda.com>";
- private static final String CLI_HEADER = "\n" + DESCRIPTION + "\n\n";
- private static final String CLI_FOOTER = "\n" + LICENSE;
+ public static final String VERSION = "v0.2.0";
+ 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 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[] INSTALL_DATA = new byte[10];
+ static {
+ URLClassLoader cl = (URLClassLoader) ECTesterReader.class.getClassLoader();
+ try {
+ URL url = cl.findResource("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);
@@ -90,10 +112,9 @@ public class ECTesterReader {
CLITools.version(DESCRIPTION, LICENSE);
return;
}
- cfg = new Config();
- //if not, read other options first, into attributes, then do action
- if (!cfg.readOptions(cli)) {
+ //if opts failed, quit
+ if (!optsOk) {
return;
}
@@ -103,18 +124,23 @@ public class ECTesterReader {
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(AID, INSTALL_DATA, ECTesterApplet.class)) {
- System.err.println("Failed to establish a simulator.");
+ System.err.println(Colors.error("Failed to establish a simulator."));
System.exit(1);
}
} else {
if (!cardManager.connectToCardSelect()) {
- System.err.println("Failed to connect to card.");
+ System.err.println(Colors.error("Failed to connect to card."));
System.exit(1);
}
cardManager.send(SELECT_ECTESTERAPPLET);
@@ -143,7 +169,7 @@ public class ECTesterReader {
logger.close();
} catch (MissingOptionException moex) {
- System.err.println("Missing required options, one of:");
+ 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()) {
@@ -171,14 +197,14 @@ public class ECTesterReader {
}
}
} catch (MissingArgumentException maex) {
- System.err.println("Option, " + maex.getOption().getOpt() + " requires an argument: " + maex.getOption().getArgName());
+ System.err.println(Colors.error("Option, " + maex.getOption().getOpt() + " requires an argument: " + maex.getOption().getArgName()));
} catch (NumberFormatException nfex) {
- System.err.println("Not a number. " + nfex.getMessage());
+ System.err.println(Colors.error("Not a number. " + nfex.getMessage()));
} catch (FileNotFoundException fnfe) {
- System.err.println("File " + fnfe.getMessage() + " not found.");
+ System.err.println(Colors.error("File " + fnfe.getMessage() + " not found."));
} catch (ParseException | IOException ex) {
- System.err.println(ex.getMessage());
- } catch (CardException | TestException ex) {
+ System.err.println(Colors.error(ex.getMessage()));
+ } catch (CardException ex) {
if (logger != null)
logger.println(ex.getMessage());
ex.printStackTrace();
@@ -236,10 +262,12 @@ public class ECTesterReader {
* -l / --log [log_file]
*
* -f / --fresh
+ * --cleanup
* -s / --simulate
* -y / --yes
* -ka/ --ka-type <type>
* -sig/--sig-type <type>
+ * -C / --color
*/
OptionGroup actions = new OptionGroup();
actions.setRequired(true);
@@ -247,10 +275,11 @@ public class ECTesterReader {
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- twist:\n- wrong:\n- composite:\n- test-vectors:").hasArg().argName("test_suite").optionalArg(true).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- 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("ls").longOpt("list-suites").desc("List supported test suites.").build());
opts.addOptionGroup(actions);
@@ -284,22 +313,45 @@ public class ECTesterReader {
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>.").hasArg().argName("output_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("f").longOpt("fresh").desc("Generate fresh keys (set domain parameters before every generation).").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("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 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);
+ }
+ }
+ }
+
/**
* Exports default card/simulation EC domain parameters to output file.
*
@@ -310,17 +362,17 @@ public class ECTesterReader {
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, (short) cfg.bits, keyClass).send());
+ sent.add(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_LOCAL, 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();
+ 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);
@@ -328,11 +380,19 @@ public class ECTesterReader {
for (Response r : sent) {
respWriter.outputResponse(r);
}
+ if (cfg.cleanup) {
+ Response cleanup = new Command.Cleanup(cardManager).send();
+ respWriter.outputResponse(cleanup);
+ }
- EC_Params exported = new EC_Params(domain, export.getParams());
-
- FileOutputStream out = new FileOutputStream(cfg.output);
- exported.writeCSV(out);
+ 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();
}
@@ -345,11 +405,11 @@ public class ECTesterReader {
private void generate() throws CardException, IOException {
byte keyClass = cfg.primeField ? KeyPair.ALG_EC_FP : KeyPair.ALG_EC_F2M;
- Response allocate = new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_LOCAL, (short) cfg.bits, keyClass).send();
+ Response allocate = new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_LOCAL, cfg.bits, keyClass).send();
respWriter.outputResponse(allocate);
- Command curve = Command.prepareCurve(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_LOCAL, (short) cfg.bits, keyClass);
+ Command curve = Command.prepareCurve(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_LOCAL, cfg.bits, keyClass);
- FileWriter keysFile = new FileWriter(cfg.output);
+ OutputStreamWriter keysFile = FileUtil.openFiles(cfg.outputs);
keysFile.write("index;time;pubW;privS\n");
int generated = 0;
@@ -363,6 +423,7 @@ public class ECTesterReader {
Command.Generate generate = new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL);
Response.Generate response = generate.send();
long elapsed = response.getDuration();
+ respWriter.outputResponse(response);
Response.Export export = new Command.Export(cardManager, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.KEY_BOTH, EC_Consts.PARAMETERS_KEYPAIR).send();
@@ -371,11 +432,10 @@ public class ECTesterReader {
retry++;
continue;
} else {
- System.err.println("Keys could not be generated.");
+ System.err.println(Colors.error("Keys could not be generated/exported."));
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);
@@ -384,8 +444,10 @@ public class ECTesterReader {
keysFile.flush();
generated++;
}
- Response cleanup = new Command.Cleanup(cardManager).send();
- respWriter.outputResponse(cleanup);
+ if (cfg.cleanup) {
+ Response cleanup = new Command.Cleanup(cardManager).send();
+ respWriter.outputResponse(cleanup);
+ }
keysFile.close();
}
@@ -393,27 +455,10 @@ public class ECTesterReader {
/**
* 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.
+ * @throws IOException if an IO error occurs
*/
- private void test() throws IOException, TestException, ParserConfigurationException {
- TestWriter writer = null;
- if (cfg.format == null) {
- writer = new TextTestWriter(logger.getPrintStream());
- } else {
- switch (cfg.format) {
- case "text":
- writer = new TextTestWriter(logger.getPrintStream());
- break;
- case "xml":
- writer = new XMLTestWriter(logger.getOutputStream());
- break;
- case "yaml":
- case "yml":
- writer = new YAMLTestWriter(logger.getPrintStream());
- break;
- }
- }
+ private void test() throws ParserConfigurationException, IOException {
+ TestWriter writer = new FileTestWriter(cfg.format, true, cfg.outputs);
CardTestSuite suite;
@@ -424,10 +469,17 @@ public class ECTesterReader {
case "test-vectors":
suite = new CardTestVectorSuite(writer, cfg, cardManager);
break;
+ case "compression":
+ suite = new CardCompressionSuite(writer, cfg, cardManager);
+ break;
+ case "misc":
+ case "miscellaneous":
+ suite = new CardMiscSuite(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 DoS of some cards.");
+ 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);
@@ -439,25 +491,34 @@ public class ECTesterReader {
}
switch (cfg.testSuite) {
case "wrong":
- suite = new CardWrongCurvesSuite(writer, cfg, cardManager);
+ suite = new CardWrongSuite(writer, cfg, cardManager);
break;
case "composite":
- suite = new CardCompositeCurvesSuite(writer, cfg, cardManager);
+ suite = new CardCompositeSuite(writer, cfg, cardManager);
break;
case "invalid":
- suite = new CardInvalidCurvesSuite(writer, cfg, cardManager);
+ suite = new CardInvalidSuite(writer, cfg, cardManager);
+ break;
+ case "degenerate":
+ suite = new CardDegenerateSuite(writer, cfg, cardManager);
break;
case "twist":
- suite = new CardTwistTestSuite(writer, cfg, cardManager);
+ 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("Unknown test suite.");
+ System.err.println(Colors.error("Unknown test suite."));
return;
}
break;
}
- suite.run();
+ suite.run(cfg.testFrom, cfg.testTo);
}
/**
@@ -470,8 +531,8 @@ public class ECTesterReader {
byte keyClass = cfg.primeField ? KeyPair.ALG_EC_FP : KeyPair.ALG_EC_F2M;
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, (short) cfg.bits, keyClass).send());
- Command curve = Command.prepareCurve(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_BOTH, (short) cfg.bits, keyClass);
+ prepare.add(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, cfg.bits, keyClass).send());
+ Command curve = Command.prepareCurve(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_BOTH, cfg.bits, keyClass);
if (curve != null)
prepare.add(curve.send());
@@ -488,9 +549,9 @@ public class ECTesterReader {
generate.add(Command.prepareKey(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_REMOTE));
}
- FileWriter out = null;
- if (cfg.output != null) {
- out = new FileWriter(cfg.output);
+ OutputStreamWriter out = null;
+ if (cfg.outputs != null) {
+ out = FileUtil.openFiles(cfg.outputs);
out.write("index;time;pubW;privS;secret\n");
}
@@ -498,24 +559,24 @@ public class ECTesterReader {
int done = 0;
while (done < cfg.ECKACount) {
List<Response> ecdh = Command.sendAll(generate);
+ for (Response r : ecdh) {
+ respWriter.outputResponse(r);
+ }
Response.Export export = new Command.Export(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.KEY_BOTH, EC_Consts.PARAMETERS_KEYPAIR).send();
- ecdh.add(export);
+ respWriter.outputResponse(export);
byte pubkey_bytes[] = export.getParameter(pubkey, EC_Consts.PARAMETER_W);
byte privkey_bytes[] = export.getParameter(privkey, EC_Consts.PARAMETER_S);
- Response.ECDH perform = new Command.ECDH(cardManager, pubkey, privkey, ECTesterApplet.EXPORT_TRUE, EC_Consts.CORRUPTION_NONE, cfg.ECKAType).send();
- ecdh.add(perform);
- for (Response r : ecdh) {
- respWriter.outputResponse(r);
- }
+ Response.ECDH perform = new Command.ECDH(cardManager, pubkey, privkey, ECTesterApplet.EXPORT_TRUE, EC_Consts.TRANSFORMATION_NONE, cfg.ECKAType).send();
+ respWriter.outputResponse(perform);
if (!perform.successful() || !perform.hasSecret()) {
if (retry < 10) {
++retry;
continue;
} else {
- System.err.println("Couldn't obtain ECDH secret from card response.");
+ System.err.println(Colors.error("Couldn't obtain ECDH secret from card response."));
break;
}
}
@@ -526,8 +587,10 @@ public class ECTesterReader {
++done;
}
- Response cleanup = new Command.Cleanup(cardManager).send();
- respWriter.outputResponse(cleanup);
+ if (cfg.cleanup) {
+ Response cleanup = new Command.Cleanup(cardManager).send();
+ respWriter.outputResponse(cleanup);
+ }
if (out != null)
out.close();
@@ -561,8 +624,8 @@ public class ECTesterReader {
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, (short) cfg.bits, keyClass).send());
- Command curve = Command.prepareCurve(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_LOCAL, (short) cfg.bits, keyClass);
+ 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);
if (curve != null)
prepare.add(curve.send());
@@ -570,30 +633,25 @@ public class ECTesterReader {
respWriter.outputResponse(r);
}
- FileWriter out = null;
- if (cfg.output != null) {
- out = new FileWriter(cfg.output);
+ OutputStreamWriter out = FileUtil.openFiles(cfg.outputs);
+ if (out != null) {
out.write("index;time;signature\n");
}
int retry = 0;
int done = 0;
while (done < cfg.ECDSACount) {
- List<Response> ecdsa = new LinkedList<>();
- ecdsa.add(generate.send());
+ respWriter.outputResponse(generate.send());
Response.ECDSA perform = new Command.ECDSA(cardManager, ECTesterApplet.KEYPAIR_LOCAL, cfg.ECDSAType, ECTesterApplet.EXPORT_TRUE, data).send();
- ecdsa.add(perform);
- for (Response r : ecdsa) {
- respWriter.outputResponse(r);
- }
+ respWriter.outputResponse(perform);
if (!perform.successful() || !perform.hasSignature()) {
if (retry < 10) {
++retry;
continue;
} else {
- System.err.println("Couldn't obtain ECDSA signature from card response.");
+ System.err.println(Colors.error("Couldn't obtain ECDSA signature from card response."));
break;
}
}
@@ -604,9 +662,10 @@ public class ECTesterReader {
++done;
}
- Response cleanup = new Command.Cleanup(cardManager).send();
- respWriter.outputResponse(cleanup);
-
+ if (cfg.cleanup) {
+ Response cleanup = new Command.Cleanup(cardManager).send();
+ respWriter.outputResponse(cleanup);
+ }
if (out != null)
out.close();
}
@@ -647,15 +706,19 @@ public class ECTesterReader {
public boolean verbose = false;
public String input;
- public String output;
+ public String[] outputs;
public boolean fresh = false;
+ 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;
@@ -698,83 +761,82 @@ public class ECTesterReader {
verbose = cli.hasOption("verbose");
input = cli.getOptionValue("input");
- output = cli.getOptionValue("output");
+ outputs = cli.getOptionValues("output");
fresh = cli.hasOption("fresh");
+ cleanup = cli.hasOption("cleanup");
simulate = cli.hasOption("simulate");
yes = cli.hasOption("yes");
+ color = cli.hasOption("color");
+ Colors.enabled = color;
if (cli.hasOption("list-named")) {
listNamed = cli.getOptionValue("list-named");
return true;
}
- format = cli.getOptionValue("format", "text");
+ format = cli.getOptionValue("format");
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));
+ 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("Can only specify the whole key with --key/--named-key or pubkey and privkey with --public/--named-public and --private/--named-private.");
+ 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("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.");
+ 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("You cannot specify both a named key and a key file.");
+ 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("Need to specify field with -fp or -f2m. (not both)");
+ System.err.print(Colors.error("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.");
+ System.err.println(Colors.error("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.");
+ System.err.println(Colors.error("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.");
+ if (outputs == null) {
+ System.err.println(Colors.error("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");
+ 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("Need to specify field with -fp or -f2m. (not both)");
+ System.err.print(Colors.error("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.");
+ System.err.println(Colors.error("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.");
+ if (outputs == null) {
+ System.err.println(Colors.error("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");
+ 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("Amount of keys generated cant be negative.");
+ System.err.println(Colors.error("Amount of keys generated cant be negative."));
return false;
}
} else if (cli.hasOption("test")) {
@@ -783,47 +845,74 @@ public class ECTesterReader {
primeField = true;
}
- testSuite = cli.getOptionValue("test", "default").toLowerCase();
- String[] tests = new String[]{"default", "composite", "invalid", "test-vectors", "wrong", "twist"};
+ 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"};
if (!Arrays.asList(tests).contains(testSuite)) {
- System.err.println("Unknown test suite " + testSuite + ". Should be one of: " + Arrays.toString(tests));
+ System.err.println(Colors.error("Unknown test suite " + testSuite + ". Should be one of: " + Arrays.toString(tests)));
return false;
}
} else if (cli.hasOption("ecdh")) {
if (primeField == binaryField) {
- System.err.print("Need to specify field with -fp or -f2m. (not both)");
+ System.err.print(Colors.error("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");
+ 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("ECDH count cannot be <= 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("Need to specify field with -fp or -f2m. (but not both)");
+ System.err.print(Colors.error("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");
+ 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("You cannot only specify a part of a keypair.");
+ 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("ECDSA count cannot be <= 0.");
+ System.err.println(Colors.error("ECDSA count cannot be <= 0."));
return false;
}
diff --git a/src/cz/crcs/ectester/reader/command/Command.java b/src/cz/crcs/ectester/reader/command/Command.java
index 5a6906c..858b05f 100644
--- a/src/cz/crcs/ectester/reader/command/Command.java
+++ b/src/cz/crcs/ectester/reader/command/Command.java
@@ -2,15 +2,16 @@ package cz.crcs.ectester.reader.command;
import cz.crcs.ectester.applet.ECTesterApplet;
import cz.crcs.ectester.applet.EC_Consts;
+import cz.crcs.ectester.common.ec.EC_Curve;
+import cz.crcs.ectester.common.ec.EC_Key;
+import cz.crcs.ectester.common.ec.EC_Keypair;
+import cz.crcs.ectester.common.ec.EC_Params;
import cz.crcs.ectester.common.util.ByteUtil;
+import cz.crcs.ectester.common.util.CardUtil;
import cz.crcs.ectester.data.EC_Store;
import cz.crcs.ectester.reader.CardMngr;
import cz.crcs.ectester.reader.ECTesterReader;
import cz.crcs.ectester.reader.response.Response;
-import cz.crcs.ectester.common.ec.EC_Curve;
-import cz.crcs.ectester.common.ec.EC_Key;
-import cz.crcs.ectester.common.ec.EC_Keypair;
-import cz.crcs.ectester.common.ec.EC_Params;
import javacard.security.KeyPair;
import javax.smartcardio.CardException;
@@ -24,7 +25,7 @@ import java.util.List;
/**
* @author Jan Jancar johny@neuromancer.sk
*/
-public abstract class Command {
+public abstract class Command implements Cloneable {
CommandAPDU cmd;
CardMngr cardManager;
@@ -46,6 +47,13 @@ public abstract class Command {
return result;
}
+ public abstract String getDescription();
+
+ @Override
+ protected Command clone() throws CloneNotSupportedException {
+ return (Command) super.clone();
+ }
+
/**
* @param keyPair which keyPair/s (local/remote) to set curve domain parameters on
@@ -179,111 +187,117 @@ public abstract class Command {
return new Command.Set(cardManager, keyPair, EC_Consts.CURVE_external, params, data);
}
-
/**
*
*/
- public static class Allocate extends Command {
- private byte keyPair;
- private short keyLength;
- private byte keyClass;
+ public static class AllocateKeyAgreement extends Command {
+ private byte kaType;
/**
- * Creates the INS_ALLOCATE instruction.
+ * Creates the INS_ALLOCATE_KA instruction.
*
* @param cardManager cardManager to send APDU through
- * @param keyPair which keyPair to use, local/remote (KEYPAIR_* | ...)
- * @param keyLength key length to set
- * @param keyClass key class to allocate
+ * @param kaType which type of KeyAgreement to use
*/
- public Allocate(CardMngr cardManager, byte keyPair, short keyLength, byte keyClass) {
+ public AllocateKeyAgreement(CardMngr cardManager, byte kaType) {
super(cardManager);
- this.keyPair = keyPair;
- this.keyLength = keyLength;
- this.keyClass = keyClass;
-
- byte[] data = new byte[]{0, 0, keyClass};
- ByteUtil.setShort(data, 0, keyLength);
- this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_ALLOCATE, keyPair, 0x00, data);
+ this.kaType = kaType;
+ byte[] data = new byte[]{kaType};
+ this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_ALLOCATE_KA, 0x00, 0x00, data);
}
@Override
- public Response.Allocate send() throws CardException {
+ public Response.AllocateKeyAgreement send() throws CardException {
long elapsed = -System.nanoTime();
ResponseAPDU response = cardManager.send(cmd);
elapsed += System.nanoTime();
- return new Response.Allocate(response, elapsed, keyPair, keyLength, keyClass);
+ return new Response.AllocateKeyAgreement(response, getDescription(), elapsed, kaType);
}
@Override
- public String toString() {
- return "Allocate";
+ public String getDescription() {
+ return String.format("Allocate KeyAgreement(%s) object", CardUtil.getKATypeString(kaType));
}
}
/**
*
*/
- public static class AllocateKeyAgreement extends Command {
- private byte kaType;
+ public static class AllocateSignature extends Command {
+ private byte sigType;
/**
- * Creates the INS_ALLOCATE_KA instruction.
+ * Creates the INS_ALLOCATE_SIG instruction.
*
* @param cardManager cardManager to send APDU through
- * @param kaType which type of KeyAgreement to use
+ * @param sigType which type of Signature to use
*/
- public AllocateKeyAgreement(CardMngr cardManager, byte kaType) {
+ public AllocateSignature(CardMngr cardManager, byte sigType) {
super(cardManager);
- this.kaType = kaType;
- byte[] data = new byte[]{kaType};
- this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_ALLOCATE_KA, 0x00, 0x00, data);
+ this.sigType = sigType;
+ byte[] data = new byte[]{sigType};
+ this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_ALLOCATE_SIG, 0x00, 0x00, data);
}
@Override
- public Response.AllocateKeyAgreement send() throws CardException {
+ public Response.AllocateSignature send() throws CardException {
long elapsed = -System.nanoTime();
ResponseAPDU response = cardManager.send(cmd);
elapsed += System.nanoTime();
- return new Response.AllocateKeyAgreement(response, elapsed, kaType);
+ return new Response.AllocateSignature(response, getDescription(), elapsed, sigType);
}
@Override
- public String toString() {
- return "AllocateKeyAgreement";
+ public String getDescription() {
+ return String.format("Allocate Signature(%s) object", CardUtil.getSigTypeString(sigType));
}
}
/**
*
*/
- public static class AllocateSignature extends Command {
- private byte sigType;
+ public static class Allocate extends Command {
+ private byte keyPair;
+ private short keyLength;
+ private byte keyClass;
/**
- * Creates the INS_ALLOCATE_SIG instruction.
+ * Creates the INS_ALLOCATE instruction.
*
* @param cardManager cardManager to send APDU through
- * @param sigType which type of Signature to use
+ * @param keyPair which keyPair to use, local/remote (KEYPAIR_* | ...)
+ * @param keyLength key length to set
+ * @param keyClass key class to allocate
*/
- public AllocateSignature(CardMngr cardManager, byte sigType) {
+ public Allocate(CardMngr cardManager, byte keyPair, short keyLength, byte keyClass) {
super(cardManager);
- this.sigType = sigType;
- byte[] data = new byte[]{sigType};
- this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_ALLOCATE_SIG, 0x00, 0x00, data);
+ this.keyPair = keyPair;
+ this.keyLength = keyLength;
+ this.keyClass = keyClass;
+
+ byte[] data = new byte[]{0, 0, keyClass};
+ ByteUtil.setShort(data, 0, keyLength);
+ this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_ALLOCATE, keyPair, 0x00, data);
}
@Override
- public Response.AllocateSignature send() throws CardException {
+ public Response.Allocate send() throws CardException {
long elapsed = -System.nanoTime();
ResponseAPDU response = cardManager.send(cmd);
elapsed += System.nanoTime();
- return new Response.AllocateSignature(response, elapsed, sigType);
+ return new Response.Allocate(response, getDescription(), elapsed, keyPair, keyLength, keyClass);
}
@Override
- public String toString() {
- return "AllocateSignature";
+ public String getDescription() {
+ String field = keyClass == KeyPair.ALG_EC_FP ? "ALG_EC_FP" : "ALG_EC_F2M";
+ String key;
+ if (keyPair == ECTesterApplet.KEYPAIR_BOTH) {
+ key = "both keypairs";
+ } else {
+ key = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair";
+ }
+ return String.format("Allocate %s %db %s", key, keyLength, field);
}
}
@@ -309,12 +323,18 @@ public abstract class Command {
long elapsed = -System.nanoTime();
ResponseAPDU response = cardManager.send(cmd);
elapsed += System.nanoTime();
- return new Response.Clear(response, elapsed, keyPair);
+ return new Response.Clear(response, getDescription(), elapsed, keyPair);
}
@Override
- public String toString() {
- return "Clear";
+ public String getDescription() {
+ String key;
+ if (keyPair == ECTesterApplet.KEYPAIR_BOTH) {
+ key = "both keypairs";
+ } else {
+ key = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair";
+ }
+ return String.format("Clear %s", key);
}
}
@@ -358,56 +378,85 @@ public abstract class Command {
long elapsed = -System.nanoTime();
ResponseAPDU response = cardManager.send(cmd);
elapsed += System.nanoTime();
- return new Response.Set(response, elapsed, keyPair, curve, params);
+ return new Response.Set(response, getDescription(), elapsed, keyPair, curve, params);
}
@Override
- public String toString() {
- return "Set";
+ public String getDescription() {
+ String name;
+ switch (curve) {
+ case EC_Consts.CURVE_default:
+ name = "default";
+ break;
+ case EC_Consts.CURVE_external:
+ name = "external";
+ break;
+ default:
+ name = "custom";
+ break;
+ }
+ String what = CardUtil.getParameterString(params);
+
+ String pair;
+ if (keyPair == ECTesterApplet.KEYPAIR_BOTH) {
+ pair = "both keypairs";
+ } else {
+ pair = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair";
+ }
+ return String.format("Set %s %s parameters on %s", name, what, pair);
}
}
/**
*
*/
- public static class Corrupt extends Command {
+ public static class Transform extends Command {
private byte keyPair;
private byte key;
private short params;
- private byte corruption;
+ private short transformation;
/**
* @param cardManager cardManager to send APDU through
- * @param keyPair which keyPair to corrupt, local/remote (KEYPAIR_* || ...)
- * @param key key to corrupt (EC_Consts.KEY_* | ...)
- * @param params parameters to corrupt (EC_Consts.PARAMETER_* | ...)
- * @param corruption corruption type (EC_Consts.CORRUPTION_*)
+ * @param keyPair which keyPair to transform, local/remote (KEYPAIR_* || ...)
+ * @param key key to transform (EC_Consts.KEY_* | ...)
+ * @param params parameters to transform (EC_Consts.PARAMETER_* | ...)
+ * @param transformation transformation type (EC_Consts.TRANSFORMATION_*)
*/
- public Corrupt(CardMngr cardManager, byte keyPair, byte key, short params, byte corruption) {
+ public Transform(CardMngr cardManager, byte keyPair, byte key, short params, short transformation) {
super(cardManager);
this.keyPair = keyPair;
this.key = key;
this.params = params;
- this.corruption = corruption;
+ this.transformation = transformation;
- byte[] data = new byte[3];
+ byte[] data = new byte[4];
ByteUtil.setShort(data, 0, params);
- data[2] = corruption;
+ ByteUtil.setShort(data, 2, transformation);
- this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_CORRUPT, keyPair, key, data);
+ this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_TRANSFORM, keyPair, key, data);
}
@Override
- public Response.Corrupt send() throws CardException {
+ public Response.Transform send() throws CardException {
long elapsed = -System.nanoTime();
ResponseAPDU response = cardManager.send(cmd);
elapsed += System.nanoTime();
- return new Response.Corrupt(response, elapsed, keyPair, key, params, corruption);
+ return new Response.Transform(response, getDescription(), elapsed, keyPair, key, params, transformation);
}
@Override
- public String toString() {
- return "Corrupt";
+ public String getDescription() {
+ String stringParams = CardUtil.getParams(params);
+ String transform = CardUtil.getTransformation(transformation);
+
+ String pair;
+ if (keyPair == ECTesterApplet.KEYPAIR_BOTH) {
+ pair = "both keypairs";
+ } else {
+ pair = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair";
+ }
+ return String.format("Transform params %s of %s, %s", stringParams, pair, transform);
}
}
@@ -435,12 +484,18 @@ public abstract class Command {
long elapsed = -System.nanoTime();
ResponseAPDU response = cardManager.send(cmd);
elapsed += System.nanoTime();
- return new Response.Generate(response, elapsed, keyPair);
+ return new Response.Generate(response, getDescription(), elapsed, keyPair);
}
@Override
- public String toString() {
- return "Generate";
+ public String getDescription() {
+ String key;
+ if (keyPair == ECTesterApplet.KEYPAIR_BOTH) {
+ key = "both keypairs";
+ } else {
+ key = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair";
+ }
+ return String.format("Generate %s", key);
}
}
@@ -477,12 +532,26 @@ public abstract class Command {
long elapsed = -System.nanoTime();
ResponseAPDU response = cardManager.send(cmd);
elapsed += System.nanoTime();
- return new Response.Export(response, elapsed, keyPair, key, params);
+ return new Response.Export(response, getDescription(), elapsed, keyPair, key, params);
}
@Override
- public String toString() {
- return "Export";
+ public String getDescription() {
+ String what = CardUtil.getParameterString(params);
+
+ String source;
+ if (key == EC_Consts.KEY_BOTH) {
+ source = "both keys";
+ } else {
+ source = ((key == EC_Consts.KEY_PUBLIC) ? "public" : "private") + " key";
+ }
+ String pair;
+ if (keyPair == ECTesterApplet.KEYPAIR_BOTH) {
+ pair = "both keypairs";
+ } else {
+ pair = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair";
+ }
+ return String.format("Export %s params from %s of %s", what, source, pair);
}
}
@@ -493,7 +562,7 @@ public abstract class Command {
private byte pubkey;
private byte privkey;
private byte export;
- private short corruption;
+ private short transformation;
private byte type;
/**
@@ -503,19 +572,19 @@ public abstract class Command {
* @param pubkey keyPair to use for public key, (KEYPAIR_LOCAL || KEYPAIR_REMOTE)
* @param privkey keyPair to use for private key, (KEYPAIR_LOCAL || KEYPAIR_REMOTE)
* @param export whether to export ECDH secret
- * @param corruption whether to invalidate the pubkey before ECDH (EC_Consts.CORRUPTION_* | ...)
+ * @param transformation whether to transform the pubkey before ECDH (EC_Consts.TRANSFORMATION_* | ...)
* @param type ECDH algorithm type (EC_Consts.KA_* | ...)
*/
- public ECDH(CardMngr cardManager, byte pubkey, byte privkey, byte export, short corruption, byte type) {
+ public ECDH(CardMngr cardManager, byte pubkey, byte privkey, byte export, short transformation, byte type) {
super(cardManager);
this.pubkey = pubkey;
this.privkey = privkey;
this.export = export;
- this.corruption = corruption;
+ this.transformation = transformation;
this.type = type;
- byte[] data = new byte[]{export, 0,0, type};
- ByteUtil.setShort(data, 1, corruption);
+ byte[] data = new byte[]{export, 0, 0, type};
+ ByteUtil.setShort(data, 1, transformation);
this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_ECDH, pubkey, privkey, data);
}
@@ -525,12 +594,23 @@ public abstract class Command {
long elapsed = -System.nanoTime();
ResponseAPDU response = cardManager.send(cmd);
elapsed += System.nanoTime();
- return new Response.ECDH(response, elapsed, pubkey, privkey, export, corruption, type);
+ return new Response.ECDH(response, getDescription(), elapsed, pubkey, privkey, export, transformation, type);
}
@Override
- public String toString() {
- return "ECDH";
+ public String getDescription() {
+ String algo = CardUtil.getKATypeString(type);
+
+ String pub = pubkey == ECTesterApplet.KEYPAIR_LOCAL ? "local" : "remote";
+ String priv = privkey == ECTesterApplet.KEYPAIR_LOCAL ? "local" : "remote";
+
+ String validity;
+ if (transformation == EC_Consts.TRANSFORMATION_NONE) {
+ validity = "";
+ } else {
+ validity = String.format("(%s point)", CardUtil.getTransformation(transformation));
+ }
+ return String.format("%s of %s pubkey and %s privkey%s", algo, pub, priv, validity);
}
}
@@ -540,7 +620,7 @@ public abstract class Command {
public static class ECDH_direct extends Command {
private byte privkey;
private byte export;
- private short corruption;
+ private short transformation;
private byte type;
private byte[] pubkey;
@@ -550,20 +630,20 @@ public abstract class Command {
* @param cardManager cardManager to send APDU through
* @param privkey keyPair to use for private key, (KEYPAIR_LOCAL || KEYPAIR_REMOTE)
* @param export whether to export ECDH secret
- * @param corruption whether to invalidate the pubkey before ECDH (EC_Consts.CORRUPTION_* | ...)
+ * @param transformation whether to transform the pubkey before ECDH (EC_Consts.TRANSFORMATION_* | ...)
* @param type EC KeyAgreement type
* @param pubkey pubkey data to do ECDH with.
*/
- public ECDH_direct(CardMngr cardManager, byte privkey, byte export, short corruption, byte type, byte[] pubkey) {
+ public ECDH_direct(CardMngr cardManager, byte privkey, byte export, short transformation, byte type, byte[] pubkey) {
super(cardManager);
this.privkey = privkey;
this.export = export;
- this.corruption = corruption;
+ this.transformation = transformation;
this.type = type;
this.pubkey = pubkey;
byte[] data = new byte[3 + pubkey.length];
- ByteUtil.setShort(data, 0, corruption);
+ ByteUtil.setShort(data, 0, transformation);
data[2] = type;
System.arraycopy(pubkey, 0, data, 3, pubkey.length);
@@ -575,12 +655,22 @@ public abstract class Command {
long elapsed = -System.nanoTime();
ResponseAPDU response = cardManager.send(cmd);
elapsed += System.nanoTime();
- return new Response.ECDH(response, elapsed, ECTesterApplet.KEYPAIR_REMOTE, privkey, export, corruption, type);
+ return new Response.ECDH(response, getDescription(), elapsed, ECTesterApplet.KEYPAIR_REMOTE, privkey, export, transformation, type);
}
@Override
- public String toString() {
- return "ECDH_direct";
+ public String getDescription() {
+ String algo = CardUtil.getKATypeString(type);
+
+ String priv = privkey == ECTesterApplet.KEYPAIR_LOCAL ? "local" : "remote";
+
+ String validity;
+ if (transformation == EC_Consts.TRANSFORMATION_NONE) {
+ validity = "";
+ } else {
+ validity = String.format("(%s point)", CardUtil.getTransformation(transformation));
+ }
+ return String.format("%s of external pubkey and %s privkey%s", algo, priv, validity);
}
}
@@ -601,6 +691,10 @@ public abstract class Command {
*/
public ECDSA(CardMngr cardManager, byte keyPair, byte sigType, byte export, byte[] raw) {
super(cardManager);
+ if (keyPair == ECTesterApplet.KEYPAIR_BOTH) {
+ throw new IllegalArgumentException();
+ }
+
this.keyPair = keyPair;
this.sigType = sigType;
this.export = export;
@@ -622,12 +716,124 @@ public abstract class Command {
long elapsed = -System.nanoTime();
ResponseAPDU response = cardManager.send(cmd);
elapsed += System.nanoTime();
- return new Response.ECDSA(response, elapsed, keyPair, sigType, export, raw);
+ return new Response.ECDSA(response, getDescription(), elapsed, keyPair, sigType, export, raw);
+ }
+
+ @Override
+ public String getDescription() {
+ String algo = CardUtil.getSigTypeString(sigType);
+ String key = keyPair == ECTesterApplet.KEYPAIR_LOCAL ? "local" : "remote";
+ String data = raw == null ? "random" : "provided";
+ return String.format("%s with %s keypair(%s data)", algo, key, data);
+ }
+ }
+
+ public static class ECDSA_sign extends Command {
+ private byte keyPair;
+ private byte sigType;
+ private byte export;
+ private byte[] raw;
+
+ /**
+ * Creates the INS_ECDSA_SIGN instruction.
+ *
+ * @param cardManager cardManager to send APDU through
+ * @param keyPair keyPair to use for signing and verification (KEYPAIR_LOCAL || KEYPAIR_REMOTE)
+ * @param sigType Signature type to use
+ * @param export whether to export ECDSA signature
+ * @param raw data to sign, can be null, in which case random data is signed.
+ */
+ public ECDSA_sign(CardMngr cardManager, byte keyPair, byte sigType, byte export, byte[] raw) {
+ super(cardManager);
+ if (keyPair == ECTesterApplet.KEYPAIR_BOTH) {
+ throw new IllegalArgumentException();
+ }
+
+ this.keyPair = keyPair;
+ this.sigType = sigType;
+ this.export = export;
+ this.raw = raw;
+
+ int len = raw != null ? raw.length : 0;
+ byte[] data = new byte[3 + len];
+ data[0] = sigType;
+ ByteUtil.setShort(data, 1, (short) len);
+ if (raw != null) {
+ System.arraycopy(raw, 0, data, 3, len);
+ }
+
+ this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_ECDSA_SIGN, keyPair, export, data);
+ }
+
+ @Override
+ public Response.ECDSA send() throws CardException {
+ long elapsed = -System.nanoTime();
+ ResponseAPDU response = cardManager.send(cmd);
+ elapsed += System.nanoTime();
+ return new Response.ECDSA(response, getDescription(), elapsed, keyPair, sigType, export, raw);
+ }
+
+ @Override
+ public String getDescription() {
+ String algo = CardUtil.getSigTypeString(sigType);
+ String key = keyPair == ECTesterApplet.KEYPAIR_LOCAL ? "local" : "remote";
+ String data = raw == null ? "random" : "provided";
+ return String.format("%s signature with %s keypair(%s data)", algo, key, data);
+ }
+ }
+
+ public static class ECDSA_verify extends Command {
+ private byte keyPair;
+ private byte sigType;
+ private byte[] raw;
+ private byte[] signature;
+
+ /**
+ * Creates the INS_ECDSA_VERIFY instruction.
+ *
+ * @param cardManager cardManager to send APDU through
+ * @param keyPair keyPair to use for signing and verification (KEYPAIR_LOCAL || KEYPAIR_REMOTE)
+ * @param sigType Signature type to use
+ * @param raw data to sign
+ * @param signature signature data
+ */
+ public ECDSA_verify(CardMngr cardManager, byte keyPair, byte sigType, byte[] raw, byte[] signature) {
+ super(cardManager);
+ if (keyPair == ECTesterApplet.KEYPAIR_BOTH) {
+ throw new IllegalArgumentException();
+ }
+ if (raw == null || signature == null) {
+ throw new IllegalArgumentException();
+ }
+
+ this.keyPair = keyPair;
+ this.sigType = sigType;
+ this.raw = raw;
+ this.signature = signature;
+
+ byte[] data = new byte[4 + raw.length + signature.length];
+ ByteUtil.setShort(data, 0, (short) raw.length);
+ System.arraycopy(raw, 0, data, 2, raw.length);
+ ByteUtil.setShort(data, 2 + raw.length, (short) signature.length);
+ System.arraycopy(signature, 0, data, 2 + raw.length + 2, signature.length);
+
+ this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_ECDSA_VERIFY, keyPair, sigType, data);
+ }
+
+ @Override
+ public Response.ECDSA send() throws CardException {
+ long elapsed = -System.nanoTime();
+ ResponseAPDU response = cardManager.send(cmd);
+ elapsed += System.nanoTime();
+ return new Response.ECDSA(response, getDescription(), elapsed, keyPair, sigType, ECTesterApplet.EXPORT_FALSE, raw);
}
@Override
- public String toString() {
- return "ECDSA";
+ public String getDescription() {
+ String algo = CardUtil.getSigTypeString(sigType);
+ String key = keyPair == ECTesterApplet.KEYPAIR_LOCAL ? "local" : "remote";
+ String data = raw == null ? "random" : "provided";
+ return String.format("%s verification with %s keypair(%s data)", algo, key, data);
}
}
@@ -650,12 +856,12 @@ public abstract class Command {
long elapsed = -System.nanoTime();
ResponseAPDU response = cardManager.send(cmd);
elapsed += System.nanoTime();
- return new Response.Cleanup(response, elapsed);
+ return new Response.Cleanup(response, getDescription(), elapsed);
}
@Override
- public String toString() {
- return "Cleanup";
+ public String getDescription() {
+ return "Request JCSystem object deletion";
}
}
}
diff --git a/src/cz/crcs/ectester/reader/output/FileTestWriter.java b/src/cz/crcs/ectester/reader/output/FileTestWriter.java
new file mode 100644
index 0000000..e4ef9b8
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/output/FileTestWriter.java
@@ -0,0 +1,53 @@
+package cz.crcs.ectester.reader.output;
+
+import cz.crcs.ectester.common.output.TeeTestWriter;
+import cz.crcs.ectester.common.output.TestWriter;
+
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.PrintStream;
+import java.util.regex.Pattern;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class FileTestWriter extends TeeTestWriter {
+
+ private static final Pattern PREFIX = Pattern.compile("(text|xml|yaml|yml):.+");
+
+ public FileTestWriter(String defaultFormat, boolean systemOut, String[] files) throws ParserConfigurationException, FileNotFoundException {
+ int fLength = files == null ? 0 : files.length;
+ writers = new TestWriter[systemOut ? fLength + 1 : fLength];
+ if (systemOut) {
+ writers[0] = createWriter(defaultFormat, System.out);
+ }
+ for (int i = 0; i < fLength; ++i) {
+ String fName = files[i];
+ String format = null;
+ if (PREFIX.matcher(fName).matches()) {
+ String[] split = fName.split(":",2);
+ format = split[0];
+ fName = split[1];
+ }
+ writers[i + 1] = createWriter(format, new PrintStream(new FileOutputStream(fName)));
+ }
+ }
+
+ private TestWriter createWriter(String format, PrintStream out) throws ParserConfigurationException {
+ if (format == null) {
+ return new TextTestWriter(out);
+ }
+ switch (format) {
+ case "text":
+ return new TextTestWriter(out);
+ case "xml":
+ return new XMLTestWriter(out);
+ case "yaml":
+ case "yml":
+ return new YAMLTestWriter(out);
+ default:
+ return null;
+ }
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/output/TextTestWriter.java b/src/cz/crcs/ectester/reader/output/TextTestWriter.java
index eb52937..ad35012 100644
--- a/src/cz/crcs/ectester/reader/output/TextTestWriter.java
+++ b/src/cz/crcs/ectester/reader/output/TextTestWriter.java
@@ -1,10 +1,13 @@
package cz.crcs.ectester.reader.output;
+import cz.crcs.ectester.common.cli.Colors;
import cz.crcs.ectester.common.output.BaseTextTestWriter;
import cz.crcs.ectester.common.test.TestSuite;
import cz.crcs.ectester.common.test.Testable;
import cz.crcs.ectester.common.util.ByteUtil;
import cz.crcs.ectester.reader.CardMngr;
+import cz.crcs.ectester.reader.ECTesterReader;
+import cz.crcs.ectester.reader.response.Response;
import cz.crcs.ectester.reader.test.CardTestSuite;
import cz.crcs.ectester.reader.test.CommandTestable;
@@ -27,7 +30,10 @@ public class TextTestWriter extends BaseTextTestWriter {
protected String testableString(Testable t) {
if (t instanceof CommandTestable) {
CommandTestable cmd = (CommandTestable) t;
- return writer.responseSuffix(cmd.getResponse());
+ Response response = cmd.getResponse();
+ if (response != null) {
+ return writer.responseSuffix(response);
+ }
}
return "";
}
@@ -37,11 +43,12 @@ public class TextTestWriter extends BaseTextTestWriter {
if (suite instanceof CardTestSuite) {
CardTestSuite cardSuite = (CardTestSuite) suite;
StringBuilder sb = new StringBuilder();
- sb.append("═══ Card ATR: ").append(ByteUtil.bytesToHex(cardSuite.getCard().getATR().getBytes(), false)).append(System.lineSeparator());
+ sb.append("═══ ").append(Colors.underline("ECTester version:")).append(" ").append(ECTesterReader.VERSION).append(ECTesterReader.GIT_COMMIT).append(System.lineSeparator());
+ sb.append("═══ ").append(Colors.underline("Card ATR:")).append(" ").append(ByteUtil.bytesToHex(cardSuite.getCard().getATR().getBytes(), false)).append(System.lineSeparator());
try {
CardMngr.CPLC cplc = cardSuite.getCard().getCPLC();
if (!cplc.values().isEmpty()) {
- sb.append("═══ Card CPLC data:").append(System.lineSeparator());
+ sb.append("═══ ").append(Colors.underline("Card CPLC data:")).append(System.lineSeparator());
for (Map.Entry<CardMngr.CPLC.Field, byte[]> entry : cplc.values().entrySet()) {
CardMngr.CPLC.Field field = entry.getKey();
byte[] value = entry.getValue();
diff --git a/src/cz/crcs/ectester/reader/output/XMLTestWriter.java b/src/cz/crcs/ectester/reader/output/XMLTestWriter.java
index d3674e8..00cc6c6 100644
--- a/src/cz/crcs/ectester/reader/output/XMLTestWriter.java
+++ b/src/cz/crcs/ectester/reader/output/XMLTestWriter.java
@@ -5,6 +5,7 @@ import cz.crcs.ectester.common.test.TestSuite;
import cz.crcs.ectester.common.test.Testable;
import cz.crcs.ectester.common.util.ByteUtil;
import cz.crcs.ectester.reader.CardMngr;
+import cz.crcs.ectester.reader.ECTesterReader;
import cz.crcs.ectester.reader.command.Command;
import cz.crcs.ectester.reader.response.Response;
import cz.crcs.ectester.reader.test.CardTestSuite;
@@ -26,16 +27,27 @@ public class XMLTestWriter extends BaseXMLTestWriter {
private Element commandElement(Command c) {
Element commandElem = doc.createElement("command");
+ if (c == null) {
+ return commandElem;
+ }
Element apdu = doc.createElement("apdu");
apdu.setTextContent(ByteUtil.bytesToHex(c.getAPDU().getBytes()));
commandElem.appendChild(apdu);
+ Element description = doc.createElement("desc");
+ description.setTextContent(c.getDescription());
+ commandElem.appendChild(description);
+
return commandElem;
}
private Element responseElement(Response r) {
Element responseElem = doc.createElement("response");
+ if (r == null) {
+ return responseElem;
+ }
+
responseElem.setAttribute("successful", r.successful() ? "true" : "false");
Element apdu = doc.createElement("apdu");
@@ -102,6 +114,7 @@ public class XMLTestWriter extends BaseXMLTestWriter {
CardTestSuite cardSuite = (CardTestSuite) suite;
Element result = doc.createElement("device");
result.setAttribute("type", "card");
+ result.setAttribute("ectester", ECTesterReader.VERSION + ECTesterReader.GIT_COMMIT);
result.appendChild(cplcElement(cardSuite.getCard()));
Element atr = doc.createElement("ATR");
diff --git a/src/cz/crcs/ectester/reader/output/YAMLTestWriter.java b/src/cz/crcs/ectester/reader/output/YAMLTestWriter.java
index 199f2c0..080fa8b 100644
--- a/src/cz/crcs/ectester/reader/output/YAMLTestWriter.java
+++ b/src/cz/crcs/ectester/reader/output/YAMLTestWriter.java
@@ -5,6 +5,7 @@ import cz.crcs.ectester.common.test.TestSuite;
import cz.crcs.ectester.common.test.Testable;
import cz.crcs.ectester.common.util.ByteUtil;
import cz.crcs.ectester.reader.CardMngr;
+import cz.crcs.ectester.reader.ECTesterReader;
import cz.crcs.ectester.reader.command.Command;
import cz.crcs.ectester.reader.response.Response;
import cz.crcs.ectester.reader.test.CardTestSuite;
@@ -12,10 +13,7 @@ import cz.crcs.ectester.reader.test.CommandTestable;
import javax.smartcardio.CardException;
import java.io.PrintStream;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
/**
* @author Jan Jancar johny@neuromancer.sk
@@ -26,13 +24,20 @@ public class YAMLTestWriter extends BaseYAMLTestWriter {
}
private Map<String, Object> commandObject(Command c) {
- Map<String, Object> commandObj = new HashMap<>();
+ Map<String, Object> commandObj = new LinkedHashMap<>();
+ if (c == null) {
+ return commandObj;
+ }
commandObj.put("apdu", ByteUtil.bytesToHex(c.getAPDU().getBytes()));
+ commandObj.put("desc", c.getDescription());
return commandObj;
}
private Map<String, Object> responseObject(Response r) {
- Map<String, Object> responseObj = new HashMap<>();
+ Map<String, Object> responseObj = new LinkedHashMap<>();
+ if (r == null) {
+ return responseObj;
+ }
responseObj.put("successful", r.successful());
responseObj.put("apdu", ByteUtil.bytesToHex(r.getAPDU().getBytes()));
responseObj.put("natural_sw", Short.toUnsignedInt(r.getNaturalSW()));
@@ -50,7 +55,7 @@ public class YAMLTestWriter extends BaseYAMLTestWriter {
protected Map<String, Object> testableObject(Testable t) {
if (t instanceof CommandTestable) {
CommandTestable cmd = (CommandTestable) t;
- Map<String, Object> result = new HashMap<>();
+ Map<String, Object> result = new LinkedHashMap<>();
result.put("type", "command");
result.put("command", commandObject(cmd.getCommand()));
result.put("response", responseObject(cmd.getResponse()));
@@ -60,7 +65,7 @@ public class YAMLTestWriter extends BaseYAMLTestWriter {
}
private Map<String, Object> cplcObject(CardMngr card) {
- Map<String, Object> result = new HashMap<>();
+ Map<String, Object> result = new LinkedHashMap<>();
try {
CardMngr.CPLC cplc = card.getCPLC();
if (!cplc.values().isEmpty()) {
@@ -79,8 +84,9 @@ public class YAMLTestWriter extends BaseYAMLTestWriter {
protected Map<String, Object> deviceObject(TestSuite suite) {
if (suite instanceof CardTestSuite) {
CardTestSuite cardSuite = (CardTestSuite) suite;
- Map<String, Object> result = new HashMap<>();
+ Map<String, Object> result = new LinkedHashMap<>();
result.put("type", "card");
+ result.put("ectester", ECTesterReader.VERSION + ECTesterReader.GIT_COMMIT);
result.put("cplc", cplcObject(cardSuite.getCard()));
result.put("ATR", ByteUtil.bytesToHex(cardSuite.getCard().getATR().getBytes(), false));
return result;
diff --git a/src/cz/crcs/ectester/reader/response/Response.java b/src/cz/crcs/ectester/reader/response/Response.java
index cbed3b2..4814e41 100644
--- a/src/cz/crcs/ectester/reader/response/Response.java
+++ b/src/cz/crcs/ectester/reader/response/Response.java
@@ -3,9 +3,7 @@ package cz.crcs.ectester.reader.response;
import cz.crcs.ectester.applet.ECTesterApplet;
import cz.crcs.ectester.applet.EC_Consts;
import cz.crcs.ectester.common.util.ByteUtil;
-import cz.crcs.ectester.common.util.CardUtil;
import javacard.framework.ISO7816;
-import javacard.security.KeyPair;
import javax.smartcardio.ResponseAPDU;
@@ -20,9 +18,11 @@ public abstract class Response {
private byte[][] params;
private boolean success = true;
private boolean error = false;
+ private String description;
- public Response(ResponseAPDU response, long time) {
+ public Response(ResponseAPDU response, String description, long time) {
this.resp = response;
+ this.description = description;
this.time = time;
}
@@ -127,7 +127,9 @@ public abstract class Response {
return this.error;
}
- public abstract String getDescription();
+ public String getDescription() {
+ return description;
+ }
/**
*
@@ -135,17 +137,12 @@ public abstract class Response {
public static class AllocateKeyAgreement extends Response {
private byte kaType;
- public AllocateKeyAgreement(ResponseAPDU response, long time, byte kaType) {
- super(response, time);
+ public AllocateKeyAgreement(ResponseAPDU response, String description, long time, byte kaType) {
+ super(response, description, time);
this.kaType = kaType;
parse(1, 0);
}
-
- @Override
- public String getDescription() {
- return String.format("Allocated KeyAgreement(%s) object", CardUtil.getKATypeString(this.kaType));
- }
}
/**
@@ -154,17 +151,12 @@ public abstract class Response {
public static class AllocateSignature extends Response {
private byte sigType;
- public AllocateSignature(ResponseAPDU response, long time, byte sigType) {
- super(response, time);
+ public AllocateSignature(ResponseAPDU response, String description, long time, byte sigType) {
+ super(response, description, time);
this.sigType = sigType;
parse(1, 0);
}
-
- @Override
- public String getDescription() {
- return String.format("Allocated Signature(%s) object", CardUtil.getSigTypeString(this.sigType));
- }
}
/**
@@ -175,8 +167,8 @@ public abstract class Response {
private short keyLength;
private byte keyClass;
- public Allocate(ResponseAPDU response, long time, byte keyPair, short keyLength, byte keyClass) {
- super(response, time);
+ public Allocate(ResponseAPDU response, String description, long time, byte keyPair, short keyLength, byte keyClass) {
+ super(response, description, time);
this.keyPair = keyPair;
this.keyLength = keyLength;
this.keyClass = keyClass;
@@ -186,18 +178,6 @@ public abstract class Response {
if ((keyPair & ECTesterApplet.KEYPAIR_REMOTE) != 0) pairs++;
parse(pairs, 0);
}
-
- @Override
- public String getDescription() {
- String field = keyClass == KeyPair.ALG_EC_FP ? "ALG_EC_FP" : "ALG_EC_F2M";
- String key;
- if (keyPair == ECTesterApplet.KEYPAIR_BOTH) {
- key = "both keypairs";
- } else {
- key = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair";
- }
- return String.format("Allocated %s %db %s", key, keyLength, field);
- }
}
/**
@@ -206,8 +186,8 @@ public abstract class Response {
public static class Clear extends Response {
private byte keyPair;
- public Clear(ResponseAPDU response, long time, byte keyPair) {
- super(response, time);
+ public Clear(ResponseAPDU response, String description, long time, byte keyPair) {
+ super(response, description, time);
this.keyPair = keyPair;
int pairs = 0;
@@ -215,17 +195,6 @@ public abstract class Response {
if ((keyPair & ECTesterApplet.KEYPAIR_REMOTE) != 0) pairs++;
parse(pairs, 0);
}
-
- @Override
- public String getDescription() {
- String key;
- if (keyPair == ECTesterApplet.KEYPAIR_BOTH) {
- key = "both keypairs";
- } else {
- key = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair";
- }
- return String.format("Cleared %s", key);
- }
}
/**
@@ -236,8 +205,8 @@ public abstract class Response {
private byte curve;
private short parameters;
- public Set(ResponseAPDU response, long time, byte keyPair, byte curve, short parameters) {
- super(response, time);
+ public Set(ResponseAPDU response, String description, long time, byte keyPair, byte curve, short parameters) {
+ super(response, description, time);
this.keyPair = keyPair;
this.curve = curve;
this.parameters = parameters;
@@ -248,58 +217,23 @@ public abstract class Response {
parse(pairs, 0);
}
-
- @Override
- public String getDescription() {
- String name;
- switch (curve) {
- case EC_Consts.CURVE_default:
- name = "default";
- break;
- case EC_Consts.CURVE_external:
- name = "external";
- break;
- default:
- name = "custom";
- break;
- }
- String what = "";
- if (parameters == EC_Consts.PARAMETERS_DOMAIN_F2M || parameters == EC_Consts.PARAMETERS_DOMAIN_FP) {
- what = "curve";
- } else if (parameters == EC_Consts.PARAMETER_W) {
- what = "pubkey";
- } else if (parameters == EC_Consts.PARAMETER_S) {
- what = "privkey";
- } else if (parameters == EC_Consts.PARAMETERS_KEYPAIR) {
- what = "keypair";
- }
-
- String pair;
- if (keyPair == ECTesterApplet.KEYPAIR_BOTH) {
- pair = "both keypairs";
- } else {
- pair = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair";
- }
- return String.format("Set %s %s parameters on %s", name, what, pair);
- }
-
}
/**
*
*/
- public static class Corrupt extends Response {
+ public static class Transform extends Response {
private byte keyPair;
private byte key;
private short params;
- private byte corruption;
+ private short transformation;
- public Corrupt(ResponseAPDU response, long time, byte keyPair, byte key, short params, byte corruption) {
- super(response, time);
+ public Transform(ResponseAPDU response, String description, long time, byte keyPair, byte key, short params, short transformation) {
+ super(response, description, time);
this.keyPair = keyPair;
this.key = key;
this.params = params;
- this.corruption = corruption;
+ this.transformation = transformation;
int pairs = 0;
if ((keyPair & ECTesterApplet.KEYPAIR_LOCAL) != 0) pairs++;
@@ -307,19 +241,6 @@ public abstract class Response {
parse(pairs, 0);
}
-
- @Override
- public String getDescription() {
- String corrupt = CardUtil.getCorruption(corruption);
-
- String pair;
- if (keyPair == ECTesterApplet.KEYPAIR_BOTH) {
- pair = "both keypairs";
- } else {
- pair = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair";
- }
- return String.format("Corrupted params of %s, %s", pair, corrupt);
- }
}
/**
@@ -328,8 +249,8 @@ public abstract class Response {
public static class Generate extends Response {
private byte keyPair;
- public Generate(ResponseAPDU response, long time, byte keyPair) {
- super(response, time);
+ public Generate(ResponseAPDU response, String description, long time, byte keyPair) {
+ super(response, description, time);
this.keyPair = keyPair;
int generated = 0;
@@ -337,18 +258,6 @@ public abstract class Response {
if ((keyPair & ECTesterApplet.KEYPAIR_REMOTE) != 0) generated++;
parse(generated, 0);
}
-
- @Override
- public String getDescription() {
- String key;
- if (keyPair == ECTesterApplet.KEYPAIR_BOTH) {
- key = "both keypairs";
- } else {
- key = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair";
- }
- return String.format("Generated %s", key);
- }
-
}
/**
@@ -359,8 +268,8 @@ public abstract class Response {
private byte key;
private short parameters;
- public Export(ResponseAPDU response, long time, byte keyPair, byte key, short parameters) {
- super(response, time);
+ public Export(ResponseAPDU response, String description, long time, byte keyPair, byte key, short parameters) {
+ super(response, description, time);
this.keyPair = keyPair;
this.key = key;
this.parameters = parameters;
@@ -440,23 +349,6 @@ public abstract class Response {
public byte[] getParameter(byte keyPair, short param) {
return getParam(getIndex(keyPair, param));
}
-
- @Override
- public String getDescription() {
- String source;
- if (key == EC_Consts.KEY_BOTH) {
- source = "both keys";
- } else {
- source = ((key == EC_Consts.KEY_PUBLIC) ? "public" : "private") + " key";
- }
- String pair;
- if (keyPair == ECTesterApplet.KEYPAIR_BOTH) {
- pair = "both keypairs";
- } else {
- pair = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair";
- }
- return String.format("Exported params from %s of %s", source, pair);
- }
}
/**
@@ -466,15 +358,15 @@ public abstract class Response {
private byte pubkey;
private byte privkey;
private byte export;
- private short corruption;
+ private short transformation;
private byte type;
- public ECDH(ResponseAPDU response, long time, byte pubkey, byte privkey, byte export, short corruption, byte type) {
- super(response, time);
+ public ECDH(ResponseAPDU response, String description, long time, byte pubkey, byte privkey, byte export, short transformation, byte type) {
+ super(response, description, time);
this.pubkey = pubkey;
this.privkey = privkey;
this.export = export;
- this.corruption = corruption;
+ this.transformation = transformation;
this.type = type;
parse(1, (export == ECTesterApplet.EXPORT_TRUE) ? 1 : 0);
@@ -491,22 +383,6 @@ public abstract class Response {
public int secretLength() {
return getParamLength(0);
}
-
- @Override
- public String getDescription() {
- String algo = CardUtil.getKATypeString(type);
-
- String pub = pubkey == ECTesterApplet.KEYPAIR_LOCAL ? "local" : "remote";
- String priv = privkey == ECTesterApplet.KEYPAIR_LOCAL ? "local" : "remote";
-
- String validity;
- if (corruption == EC_Consts.CORRUPTION_NONE) {
- validity = "unchanged";
- } else {
- validity = CardUtil.getCorruption(corruption);
- }
- return String.format("%s of %s pubkey and %s privkey(%s point)", algo, pub, priv, validity);
- }
}
/**
@@ -518,8 +394,8 @@ public abstract class Response {
private byte export;
private byte[] raw;
- public ECDSA(ResponseAPDU response, long time, byte keyPair, byte sigType, byte export, byte[] raw) {
- super(response, time);
+ public ECDSA(ResponseAPDU response, String description, long time, byte keyPair, byte sigType, byte export, byte[] raw) {
+ super(response, description, time);
this.keyPair = keyPair;
this.sigType = sigType;
this.export = export;
@@ -535,14 +411,6 @@ public abstract class Response {
public byte[] getSignature() {
return getParam(0);
}
-
- @Override
- public String getDescription() {
- String algo = CardUtil.getSigTypeString(sigType);
- String key = keyPair == ECTesterApplet.KEYPAIR_LOCAL ? "local" : "remote";
- String data = raw == null ? "random" : "provided";
- return String.format("%s with %s keypair(%s data)", algo, key, data);
- }
}
/**
@@ -550,16 +418,10 @@ public abstract class Response {
*/
public static class Cleanup extends Response {
- public Cleanup(ResponseAPDU response, long time) {
- super(response, time);
+ public Cleanup(ResponseAPDU response, String description, long time) {
+ super(response, description, time);
parse(1, 0);
}
-
- @Override
- public String getDescription() {
- return "Requested JCSystem object deletion";
- }
-
}
}
diff --git a/src/cz/crcs/ectester/reader/test/CardCofactorSuite.java b/src/cz/crcs/ectester/reader/test/CardCofactorSuite.java
new file mode 100644
index 0000000..39024b8
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/CardCofactorSuite.java
@@ -0,0 +1,77 @@
+package cz.crcs.ectester.reader.test;
+
+import cz.crcs.ectester.applet.ECTesterApplet;
+import cz.crcs.ectester.applet.EC_Consts;
+import cz.crcs.ectester.common.ec.EC_Curve;
+import cz.crcs.ectester.common.ec.EC_Key;
+import cz.crcs.ectester.common.output.TestWriter;
+import cz.crcs.ectester.common.test.CompoundTest;
+import cz.crcs.ectester.common.test.Test;
+import cz.crcs.ectester.data.EC_Store;
+import cz.crcs.ectester.reader.CardMngr;
+import cz.crcs.ectester.reader.ECTesterReader;
+import cz.crcs.ectester.reader.command.Command;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+import static cz.crcs.ectester.common.test.Result.ExpectedValue;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class CardCofactorSuite extends CardTestSuite {
+ public CardCofactorSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) {
+ super(writer, cfg, cardManager, "cofactor", "The cofactor test suite tests whether the card correctly rejects points on the curve but not in the subgroup generated by the generator during ECDH.");
+ }
+
+ @Override
+ protected void runTests() throws Exception {
+ Map<String, EC_Key.Public> pubkeys = EC_Store.getInstance().getObjects(EC_Key.Public.class, "cofactor");
+ List<Map.Entry<EC_Curve, List<EC_Key.Public>>> curveList = EC_Store.mapKeyToCurve(pubkeys.values());
+ for (Map.Entry<EC_Curve, List<EC_Key.Public>> e : curveList) {
+ EC_Curve curve = e.getKey();
+ List<EC_Key.Public> keys = e.getValue();
+
+ Test allocate = CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS);
+ Test set = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.SUCCESS);
+ Test generate = CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_LOCAL), ExpectedValue.SUCCESS);
+
+ Test prepare = CompoundTest.all(ExpectedValue.SUCCESS, "Prepare and generate keypair on " + curve.getId() + ".", allocate, set, generate);
+
+ List<Test> ecdhTests = new LinkedList<>();
+ for (EC_Key.Public pub : keys) {
+ Command ecdhCommand = new Command.ECDH_direct(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH, pub.flatten());
+ ecdhTests.add(CommandTest.expect(ecdhCommand, ExpectedValue.FAILURE, "Card correctly rejected point on non-generator subgroup.", "Card incorrectly accepted point on non-generator subgroup."));
+ }
+ Test ecdh = CompoundTest.all(ExpectedValue.SUCCESS, "Perform ECDH with public points on non-generator subgroup.", ecdhTests.toArray(new Test[0]));
+
+ Random r = new Random();
+ byte[] raw = new byte[128];
+ byte[] sig = new byte[40];
+ r.nextBytes(raw);
+ r.nextBytes(sig);
+
+ List<Test> ecdsaTests = new LinkedList<>();
+ for (EC_Key.Public pub : keys) {
+ Command setCommand = new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, pub.getParams(), pub.flatten());
+ Test setTest = CommandTest.expect(setCommand, ExpectedValue.ANY);
+ Command ecdsaCommand = new Command.ECDSA_verify(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.Signature_ALG_ECDSA_SHA, raw, sig);
+ Test ecdsaTest = CommandTest.expect(ecdsaCommand, ExpectedValue.FAILURE);
+ ecdsaTests.add(CompoundTest.all(ExpectedValue.SUCCESS, "Verify random ECDSA signature by " + pub.getId() + ".", setTest, ecdsaTest));
+ }
+ Test ecdsa = CompoundTest.all(ExpectedValue.SUCCESS, "Verify random ECDSA signature by public points on non-generator subgroup.", ecdsaTests.toArray(new Test[0]));
+
+ Test tests = CompoundTest.all(ExpectedValue.SUCCESS, "Perform ECDH and ECDSA tests.", ecdh, ecdsa);
+
+ if (cfg.cleanup) {
+ Test cleanup = CommandTest.expect(new Command.Cleanup(this.card), ExpectedValue.SUCCESS);
+ doTest(CompoundTest.greedyAllTry(ExpectedValue.SUCCESS, "Cofactor test of " + curve.getId() + ".", prepare, tests, cleanup));
+ } else {
+ doTest(CompoundTest.greedyAllTry(ExpectedValue.SUCCESS, "Cofactor test of " + curve.getId() + ".", prepare, tests));
+ }
+ }
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/test/CardCompositeCurvesSuite.java b/src/cz/crcs/ectester/reader/test/CardCompositeCurvesSuite.java
deleted file mode 100644
index a53806c..0000000
--- a/src/cz/crcs/ectester/reader/test/CardCompositeCurvesSuite.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package cz.crcs.ectester.reader.test;
-
-import cz.crcs.ectester.applet.ECTesterApplet;
-import cz.crcs.ectester.applet.EC_Consts;
-import cz.crcs.ectester.common.ec.EC_Curve;
-import cz.crcs.ectester.common.ec.EC_Key;
-import cz.crcs.ectester.common.output.TestWriter;
-import cz.crcs.ectester.data.EC_Store;
-import cz.crcs.ectester.reader.CardMngr;
-import cz.crcs.ectester.reader.ECTesterReader;
-import cz.crcs.ectester.reader.command.Command;
-import javacard.security.KeyPair;
-
-import java.util.Map;
-
-import static cz.crcs.ectester.common.test.Result.ExpectedValue;
-
-/**
- * @author Jan Jancar johny@neuromancer.sk
- */
-public class CardCompositeCurvesSuite extends CardTestSuite {
-
- public CardCompositeCurvesSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) {
- super(writer, cfg, cardManager, "composite", "The composite suite run ECDH over curves with composite order. This should generally fail, as using such a curve is unsafe.");
- }
-
- @Override
- protected void runTests() throws Exception {
- /* Do the default run with the public keys set to provided smallorder keys
- * over composite order curves. Essentially small subgroup attacks.
- * These should fail, the curves aren't safe so that if the computation with
- * a small order public key succeeds the private key modulo the public key order
- * is revealed.
- */
- Map<String, EC_Key> keys = EC_Store.getInstance().getObjects(EC_Key.class, "composite");
- for (EC_Key key : keys.values()) {
- EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, key.getCurve());
- if (curve.getField() == KeyPair.ALG_EC_FP && !cfg.primeField || curve.getField() == KeyPair.ALG_EC_F2M && !cfg.binaryField) {
- continue;
- }
- if ((curve.getBits() == cfg.bits || cfg.all)) {
- doTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS));
- doTest(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.ANY));
- doTest(CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_LOCAL), ExpectedValue.ANY));
- Command ecdhCommand = new Command.ECDH_direct(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, ECTesterApplet.KeyAgreement_ALG_EC_SVDP_DH, key.flatten());
- doTest(CommandTest.expect(ecdhCommand, ExpectedValue.FAILURE, "Card correctly rejected to do ECDH over a composite order curve.", "Card incorrectly does ECDH over a composite order curve, leaks bits of private key."));
- new Command.Cleanup(this.card).send();
- }
- }
- }
-}
diff --git a/src/cz/crcs/ectester/reader/test/CardCompositeSuite.java b/src/cz/crcs/ectester/reader/test/CardCompositeSuite.java
new file mode 100644
index 0000000..ec56901
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/CardCompositeSuite.java
@@ -0,0 +1,116 @@
+package cz.crcs.ectester.reader.test;
+
+import cz.crcs.ectester.applet.ECTesterApplet;
+import cz.crcs.ectester.applet.EC_Consts;
+import cz.crcs.ectester.common.ec.EC_Curve;
+import cz.crcs.ectester.common.ec.EC_Key;
+import cz.crcs.ectester.common.output.TestWriter;
+import cz.crcs.ectester.common.test.CompoundTest;
+import cz.crcs.ectester.common.test.Test;
+import cz.crcs.ectester.common.util.CardUtil;
+import cz.crcs.ectester.data.EC_Store;
+import cz.crcs.ectester.reader.CardMngr;
+import cz.crcs.ectester.reader.ECTesterReader;
+import cz.crcs.ectester.reader.command.Command;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import static cz.crcs.ectester.common.test.Result.ExpectedValue;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class CardCompositeSuite extends CardTestSuite {
+
+ public CardCompositeSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) {
+ super(writer, cfg, cardManager, "composite", "The composite suite runs ECDH over curves with composite order. This should generally fail, as using such a curve is unsafe.");
+ }
+
+ @Override
+ protected void runTests() throws Exception {
+ /* Do the default run with the public keys set to provided smallorder keys
+ * over composite order curves. Essentially small subgroup attacks.
+ * These should fail, the curves aren't safe so that if the computation with
+ * a small order public key succeeds the private key modulo the public key order
+ * is revealed.
+ */
+ Map<String, EC_Key> keys = EC_Store.getInstance().getObjects(EC_Key.class, "composite");
+ List<Map.Entry<EC_Curve, List<EC_Key>>> mappedKeys = EC_Store.mapKeyToCurve(keys.values());
+ for (Map.Entry<EC_Curve, List<EC_Key>> curveKeys : mappedKeys) {
+ EC_Curve curve = curveKeys.getKey();
+ List<Test> tests = new LinkedList<>();
+ Test allocate = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_LOCAL, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS));
+ if (!allocate.ok()) {
+ doTest(CompoundTest.all(ExpectedValue.SUCCESS, "No support for " + curve.getBits() + "b " + CardUtil.getKeyTypeString(curve.getField()) + ".", allocate));
+ continue;
+ }
+ tests.add(allocate);
+ tests.add(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.ANY));
+ tests.add(CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_LOCAL), ExpectedValue.ANY));
+ for (EC_Key key : curveKeys.getValue()) {
+ Command ecdhCommand = new Command.ECDH_direct(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH, key.flatten());
+ Test ecdh = CommandTest.expect(ecdhCommand, ExpectedValue.FAILURE, "Card correctly rejected to do ECDH over a composite order curve.", "Card incorrectly does ECDH over a composite order curve, leaks bits of private key.");
+ tests.add(CompoundTest.greedyAllTry(ExpectedValue.SUCCESS, "Composite test of " + curve.getId() + ", " + key.getDesc(), ecdh));
+ }
+ doTest(CompoundTest.all(ExpectedValue.SUCCESS, "Composite test of " + curve.getId() + ".", tests.toArray(new Test[0])));
+ }
+
+
+ Map<String, EC_Curve> results = EC_Store.getInstance().getObjects(EC_Curve.class, "composite");
+ List<Map.Entry<String, List<EC_Curve>>> groupList = EC_Store.mapToPrefix(results.values());
+ /* Test the whole curves with both keypairs generated on card(no small-order public points provided).
+ */
+ List<EC_Curve> wholeCurves = groupList.stream().filter((e) -> e.getKey().equals("whole")).findFirst().get().getValue();
+ testGroup(wholeCurves, "Composite generator order", ExpectedValue.FAILURE, "Card rejected to do ECDH with composite order generator.", "Card did not reject to do ECDH with composite order generator.");
+
+ /* Also test having a G of small order, so small R.
+ */
+ List<EC_Curve> smallRCurves = groupList.stream().filter((e) -> e.getKey().equals("small")).findFirst().get().getValue();
+ testGroup(smallRCurves, "Small generator order", ExpectedValue.FAILURE, "Card correctly rejected to do ECDH over a small order generator.", "Card incorrectly does ECDH over a small order generator.");
+
+ /* Test increasingly larger prime R, to determine where/if card behavior changes.
+ */
+ List<EC_Curve> varyingCurves = groupList.stream().filter((e) -> e.getKey().equals("varying")).findFirst().get().getValue();
+ testGroup(varyingCurves, null, ExpectedValue.ANY, "", "");
+
+ /* Also test having a G of large but composite order, R = p * q,
+ */
+ List<EC_Curve> pqCurves = groupList.stream().filter((e) -> e.getKey().equals("pq")).findFirst().get().getValue();
+ testGroup(pqCurves, null, ExpectedValue.ANY, "", "");
+
+ /* Also test having G or large order being a Carmichael pseudoprime, R = p * q * r,
+ */
+ List<EC_Curve> ppCurves = groupList.stream().filter((e) -> e.getKey().equals("pp")).findFirst().get().getValue();
+ testGroup(ppCurves, "Generator order = Carmichael pseudoprime", ExpectedValue.ANY, "", "");
+
+ /* Also test rg0 curves.
+ */
+ List<EC_Curve> rg0Curves = groupList.stream().filter((e) -> e.getKey().equals("rg0")).findFirst().get().getValue();
+ testGroup(rg0Curves, null, ExpectedValue.ANY, "", "");
+ }
+
+ private void testGroup(List<EC_Curve> curves, String testName, ExpectedValue dhValue, String ok, String nok) throws Exception {
+ for (EC_Curve curve : curves) {
+ Test allocate = CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS);
+ Test set = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.ANY);
+ Test generate = CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_BOTH), ExpectedValue.ANY);
+ Test ecdh = CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH), dhValue, ok, nok);
+
+ String description;
+ if (testName == null) {
+ description = curve.getDesc() + " test of " + curve.getId() + ".";
+ } else {
+ description = testName + " test of " + curve.getId() + ".";
+ }
+ if (cfg.cleanup) {
+ Test cleanup = CommandTest.expect(new Command.Cleanup(this.card), ExpectedValue.SUCCESS);
+ doTest(CompoundTest.greedyAllTry(ExpectedValue.SUCCESS, description, allocate, set, generate, ecdh, cleanup));
+ } else {
+ doTest(CompoundTest.greedyAllTry(ExpectedValue.SUCCESS, description, allocate, set, generate, ecdh));
+ }
+ }
+
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/test/CardCompressionSuite.java b/src/cz/crcs/ectester/reader/test/CardCompressionSuite.java
new file mode 100644
index 0000000..5e8f600
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/CardCompressionSuite.java
@@ -0,0 +1,122 @@
+package cz.crcs.ectester.reader.test;
+
+import cz.crcs.ectester.applet.ECTesterApplet;
+import cz.crcs.ectester.applet.EC_Consts;
+import cz.crcs.ectester.common.output.TestWriter;
+import cz.crcs.ectester.common.test.CompoundTest;
+import cz.crcs.ectester.common.test.Result;
+import cz.crcs.ectester.common.test.Test;
+import cz.crcs.ectester.common.util.ByteUtil;
+import cz.crcs.ectester.common.util.CardUtil;
+import cz.crcs.ectester.common.util.ECUtil;
+import cz.crcs.ectester.reader.CardMngr;
+import cz.crcs.ectester.reader.ECTesterReader;
+import cz.crcs.ectester.reader.command.Command;
+import cz.crcs.ectester.reader.response.Response;
+import javacard.security.KeyPair;
+
+import java.security.spec.ECPoint;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class CardCompressionSuite extends CardTestSuite {
+ public CardCompressionSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) {
+ super(writer, cfg, cardManager, "compression", "The compression test suite tests cards support for compressed points in ECDH (as per ANSI X9.62).",
+ "It also tests for handling of bogus input by using the point at infinity and a hybrid point with the y coordinate corrupted.");
+ }
+
+ @Override
+ protected void runTests() throws Exception {
+ //iterate over default curve sizes
+ // for Fp
+ // - allocate, set custom curve, generate keypairs, -> export generated.
+ // - test ecdh with local and remote simply(no compression)
+ // - test local privkey, remote pubkey (compressed)
+ // - test local privkey, remote pubkey (hybrid)
+ // - test local privkey, remote pubkey (hybrid with wrong y)
+ // - test local privkey, remote pubkey (point at infinity)
+ if (cfg.primeField) {
+ runCompression(KeyPair.ALG_EC_FP);
+ }
+ // for F2m
+ // - allocate, set custom curve, generate keypairs, -> export generated.
+ // - test ecdh with local and remote simply(no compression)
+ // - test local privkey, remote pubkey (compressed)
+ // - test local privkey, remote pubkey (hybrid)
+ // - test local privkey, remote pubkey (hybrid with wrong y)
+ // - test local privkey, remote pubkey (point at infinity)
+ if (cfg.binaryField) {
+ runCompression(KeyPair.ALG_EC_F2M);
+ }
+ }
+
+ private void runCompression(byte field) throws Exception {
+ short[] keySizes = field == KeyPair.ALG_EC_FP ? EC_Consts.FP_SIZES : EC_Consts.F2M_SIZES;
+ short domain = field == KeyPair.ALG_EC_FP ? EC_Consts.PARAMETERS_DOMAIN_FP : EC_Consts.PARAMETERS_DOMAIN_F2M;
+
+ for (short keyLength : keySizes) {
+ String spec = keyLength + "b " + CardUtil.getKeyTypeString(field);
+
+ Test allocateFirst = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, keyLength, field), Result.ExpectedValue.SUCCESS));
+ if (!allocateFirst.ok()) {
+ doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "No support for " + spec + ".", allocateFirst));
+ continue;
+ }
+
+ List<Test> compressionTests = new LinkedList<>();
+ compressionTests.add(allocateFirst);
+ Test setCustom = runTest(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.getCurve(keyLength, field), domain, null), Result.ExpectedValue.SUCCESS));
+ Test genCustom = runTest(CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_BOTH), Result.ExpectedValue.SUCCESS));
+ compressionTests.add(setCustom);
+ compressionTests.add(genCustom);
+
+ Response.Export key = new Command.Export(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.KEY_PUBLIC, EC_Consts.PARAMETER_W).send();
+ byte[] pubkey = key.getParameter(ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.KEY_PUBLIC);
+ ECPoint pub;
+ try {
+ pub = ECUtil.fromX962(pubkey, null);
+ } catch (IllegalArgumentException iae) {
+ // TODO: use external SECG curves so we have them here.
+ doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "", compressionTests.toArray(new Test[0])));
+ continue;
+ }
+
+ List<Test> kaTests = new LinkedList<>();
+ for (byte kaType : EC_Consts.KA_TYPES) {
+ List<Test> thisTests = new LinkedList<>();
+ Test allocate = runTest(CommandTest.expect(new Command.AllocateKeyAgreement(this.card, kaType), Result.ExpectedValue.SUCCESS));
+ if (allocate.ok()) {
+ Test ka = runTest(CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, kaType), Result.ExpectedValue.SUCCESS));
+
+ thisTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "KeyAgreement setup and basic test.", allocate, ka));
+ if (ka.ok()) {
+ // tests of the good stuff
+ Test kaCompressed = CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_COMPRESS, kaType), Result.ExpectedValue.SUCCESS);
+ Test kaHybrid = CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_COMPRESS_HYBRID, kaType), Result.ExpectedValue.SUCCESS);
+ thisTests.add(CompoundTest.any(Result.ExpectedValue.SUCCESS, "Tests of compressed and hybrid form.", kaCompressed, kaHybrid));
+
+ // tests the bad stuff here
+ byte[] pubHybrid = ECUtil.toX962Hybrid(pub, keyLength);
+ pubHybrid[pubHybrid.length - 1] ^= 1;
+ byte[] pubHybridEncoded = ByteUtil.prependLength(pubHybrid);
+ Test kaBadHybrid = CommandTest.expect(new Command.ECDH_direct(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, kaType, pubHybridEncoded), Result.ExpectedValue.FAILURE);
+
+ byte[] pubInfinityEncoded = {0x01, 0x00};
+ Test kaBadInfinity = CommandTest.expect(new Command.ECDH_direct(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, kaType, pubInfinityEncoded), Result.ExpectedValue.FAILURE);
+ thisTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Tests of corrupted hybrid form and infinity.", kaBadHybrid, kaBadInfinity));
+ }
+ kaTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "KeyAgreement tests of " + CardUtil.getKATypeString(kaType) + ".", thisTests.toArray(new Test[0])));
+ }
+ }
+ compressionTests.addAll(kaTests);
+ if (cfg.cleanup) {
+ compressionTests.add(CommandTest.expect(new Command.Cleanup(this.card), Result.ExpectedValue.SUCCESS));
+ }
+
+ doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Compression test of " + spec + ".", compressionTests.toArray(new Test[0])));
+ }
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/test/CardDefaultSuite.java b/src/cz/crcs/ectester/reader/test/CardDefaultSuite.java
index c3bd9c8..fa9bfd0 100644
--- a/src/cz/crcs/ectester/reader/test/CardDefaultSuite.java
+++ b/src/cz/crcs/ectester/reader/test/CardDefaultSuite.java
@@ -4,6 +4,7 @@ import cz.crcs.ectester.applet.ECTesterApplet;
import cz.crcs.ectester.applet.EC_Consts;
import cz.crcs.ectester.common.output.TestWriter;
import cz.crcs.ectester.common.test.CompoundTest;
+import cz.crcs.ectester.common.test.Result;
import cz.crcs.ectester.common.test.Test;
import cz.crcs.ectester.common.util.CardUtil;
import cz.crcs.ectester.reader.CardMngr;
@@ -13,8 +14,13 @@ import javacard.security.KeyPair;
import java.util.LinkedList;
import java.util.List;
+import java.util.Random;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import static cz.crcs.ectester.common.test.Result.ExpectedValue;
+import static cz.crcs.ectester.common.test.Result.Value;
/**
* @author Jan Jancar johny@neuromancer.sk
@@ -22,7 +28,7 @@ import static cz.crcs.ectester.common.test.Result.ExpectedValue;
public class CardDefaultSuite extends CardTestSuite {
public CardDefaultSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) {
- super(writer, cfg, cardManager, "default", "The default test suite run basic support of ECDH and ECDSA.");
+ super(writer, cfg, cardManager, "default", "The default test suite tests basic support of ECDH and ECDSA.");
}
@Override
@@ -37,58 +43,99 @@ public class CardDefaultSuite extends CardTestSuite {
private void runDefault(byte field) throws Exception {
short[] keySizes = field == KeyPair.ALG_EC_FP ? EC_Consts.FP_SIZES : EC_Consts.F2M_SIZES;
+ short domain = field == KeyPair.ALG_EC_FP ? EC_Consts.PARAMETERS_DOMAIN_FP : EC_Consts.PARAMETERS_DOMAIN_F2M;
for (short keyLength : keySizes) {
- String description = "Tests of " + keyLength + "b " + (field == KeyPair.ALG_EC_FP ? "ALG_EC_FP" : "ALG_EC_F2M") + " support.";
List<Test> supportTests = new LinkedList<>();
- Test key = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, keyLength, field), ExpectedValue.SUCCESS));
- if (!key.ok()) {
- doTest(CompoundTest.all(ExpectedValue.SUCCESS, description + " None.", key));
+ Test allocateFirst = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, keyLength, field), ExpectedValue.SUCCESS));
+ if (!allocateFirst.ok()) {
+ doTest(CompoundTest.all(ExpectedValue.SUCCESS, "No support for " + keyLength + "b " + CardUtil.getKeyTypeString(field) + ".", allocateFirst));
continue;
}
- supportTests.add(key);
+ supportTests.add(allocateFirst);
Test genDefault = runTest(CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_BOTH), ExpectedValue.SUCCESS));
- Test setCustom = runTest(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.getCurve(keyLength, field), EC_Consts.PARAMETERS_DOMAIN_FP, null), ExpectedValue.SUCCESS));
+ Test allocateSecond = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, keyLength, field), ExpectedValue.SUCCESS));
+ Test setCustom = runTest(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.getCurve(keyLength, field), domain, null), ExpectedValue.SUCCESS));
Test genCustom = runTest(CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_BOTH), ExpectedValue.SUCCESS));
supportTests.add(genDefault);
+ supportTests.add(allocateSecond);
supportTests.add(setCustom);
supportTests.add(genCustom);
+ List<Test> kaTests = new LinkedList<>();
for (byte kaType : EC_Consts.KA_TYPES) {
Test allocate = runTest(CommandTest.expect(new Command.AllocateKeyAgreement(this.card, kaType), ExpectedValue.SUCCESS));
if (allocate.ok()) {
- Command ecdh = new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, kaType);
+ Command ecdh = new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, kaType);
Test ka = runTest(CommandTest.expect(ecdh, ExpectedValue.SUCCESS));
- Test kaCompressed = runTest(CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_COMPRESS, kaType), ExpectedValue.SUCCESS));
- Test perfTest = null;
+ Test kaCompressed = runTest(CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_COMPRESS, kaType), ExpectedValue.SUCCESS));
+
+ String kaDesc = "Test of the " + CardUtil.getKATypeString(kaType) + " KeyAgreement.";
+ Function<Test[], Result> kaCallback = (tests) -> {
+ if (tests[1].ok() || tests[2].ok()) {
+ return new Result(Value.SUCCESS, "Some ECDH is supported.");
+ } else {
+ return new Result(Value.FAILURE, "ECDH failed.");
+ }
+ };
+
+ Test compound;
if (ka.ok()) {
- perfTest = runTest(PerformanceTest.repeat(ecdh, 10));
+ Test perfTest = runTest(PerformanceTest.repeat(ecdh, 10));
+ compound = runTest(CompoundTest.function(kaCallback, kaDesc, allocate, ka, kaCompressed, perfTest));
+ } else {
+ compound = runTest(CompoundTest.function(kaCallback, kaDesc, allocate, ka, kaCompressed));
}
- Test compound = runTest(CompoundTest.all(ExpectedValue.SUCCESS, "Test of the " + CardUtil.getKATypeString(kaType) + " KeyAgreement.", allocate, ka, kaCompressed, perfTest));
- supportTests.add(compound);
+
+ kaTests.add(compound);
} else {
runTest(allocate);
- supportTests.add(allocate);
+ kaTests.add(allocate);
}
}
+ Test kaTest = runTest(CompoundTest.any(ExpectedValue.SUCCESS, "KeyAgreement tests.", kaTests.toArray(new Test[0])));
+ supportTests.add(kaTest);
+
+ List<Test> signTests = new LinkedList<>();
for (byte sigType : EC_Consts.SIG_TYPES) {
Test allocate = runTest(CommandTest.expect(new Command.AllocateSignature(this.card, sigType), ExpectedValue.SUCCESS));
if (allocate.ok()) {
Command ecdsa = new Command.ECDSA(this.card, ECTesterApplet.KEYPAIR_LOCAL, sigType, ECTesterApplet.EXPORT_FALSE, null);
Test expect = runTest(CommandTest.expect(ecdsa, ExpectedValue.SUCCESS));
- Test perfTest = null;
+
+ String signDesc = "Test of the " + CardUtil.getSigTypeString(sigType) + " signature.";
+
+ Random rand = new Random();
+ byte[] sigData = new byte[64];
+ rand.nextBytes(sigData);
+
+ Test compound;
if (expect.ok()) {
- perfTest = runTest(PerformanceTest.repeat(ecdsa, 10));
+ Command ecdsaSign = new Command.ECDSA_sign(this.card, ECTesterApplet.KEYPAIR_LOCAL, sigType, ECTesterApplet.EXPORT_TRUE, sigData);
+ PerformanceTest signTest = runTest(PerformanceTest.repeat("Sign", ecdsaSign, 10));
+ byte[] signature = signTest.getResponses()[0].getParam(0);
+ Command ecdsaVerify = new Command.ECDSA_verify(this.card, ECTesterApplet.KEYPAIR_LOCAL, sigType, sigData, signature);
+ PerformanceTest verifyTest = runTest(PerformanceTest.repeat("Verify", ecdsaVerify, 10));
+ compound = runTest(CompoundTest.all(ExpectedValue.SUCCESS, signDesc, allocate, expect, signTest, verifyTest));
+ } else {
+ compound = runTest(CompoundTest.all(ExpectedValue.SUCCESS, signDesc, allocate, expect));
}
- Test compound = runTest(CompoundTest.all(ExpectedValue.SUCCESS, "Test of the " + CardUtil.getSigTypeString(sigType) + " signature.", allocate, expect, perfTest));
- supportTests.add(compound);
+ signTests.add(compound);
} else {
- supportTests.add(allocate);
+ signTests.add(allocate);
}
}
- doTest(CompoundTest.all(ExpectedValue.SUCCESS, description + " Some.", supportTests.toArray(new Test[0])));
- new Command.Cleanup(this.card).send();
+ Test signTest = runTest(CompoundTest.any(ExpectedValue.SUCCESS, "Signature tests.", signTests.toArray(new Test[0])));
+ supportTests.add(signTest);
+ ExpectedValue[] testExpects = {ExpectedValue.SUCCESS, ExpectedValue.ANY, ExpectedValue.SUCCESS, ExpectedValue.SUCCESS, ExpectedValue.SUCCESS, ExpectedValue.SUCCESS, ExpectedValue.SUCCESS};
+ List<ExpectedValue> expects = Stream.of(testExpects).collect(Collectors.toList());
+ if (cfg.cleanup) {
+ supportTests.add(CommandTest.expect(new Command.Cleanup(this.card), Result.ExpectedValue.SUCCESS));
+ expects.add(ExpectedValue.ANY);
+ }
+
+ doTest(CompoundTest.mask(expects.toArray(new ExpectedValue[0]), "Tests of " + keyLength + "b " + CardUtil.getKeyTypeString(field) + " support.", supportTests.toArray(new Test[0])));
}
}
}
diff --git a/src/cz/crcs/ectester/reader/test/CardDegenerateSuite.java b/src/cz/crcs/ectester/reader/test/CardDegenerateSuite.java
new file mode 100644
index 0000000..064c6cb
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/CardDegenerateSuite.java
@@ -0,0 +1,59 @@
+package cz.crcs.ectester.reader.test;
+
+import cz.crcs.ectester.applet.ECTesterApplet;
+import cz.crcs.ectester.applet.EC_Consts;
+import cz.crcs.ectester.common.ec.EC_Curve;
+import cz.crcs.ectester.common.ec.EC_Key;
+import cz.crcs.ectester.common.output.TestWriter;
+import cz.crcs.ectester.common.test.CompoundTest;
+import cz.crcs.ectester.common.test.Result;
+import cz.crcs.ectester.common.test.Test;
+import cz.crcs.ectester.data.EC_Store;
+import cz.crcs.ectester.reader.CardMngr;
+import cz.crcs.ectester.reader.ECTesterReader;
+import cz.crcs.ectester.reader.command.Command;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class CardDegenerateSuite extends CardTestSuite {
+
+ public CardDegenerateSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) {
+ super(writer, cfg, cardManager, "degenerate", "The degenerate suite tests whether the card rejects points outside of the curve during ECDH.",
+ "The tested points lie on a part of the plane for which some Edwards, Hessian and Huff form addition formulas work.");
+ }
+
+ @Override
+ protected void runTests() throws Exception {
+ Map<String, EC_Key.Public> pubkeys = EC_Store.getInstance().getObjects(EC_Key.Public.class, "degenerate");
+ List<Map.Entry<EC_Curve, List<EC_Key.Public>>> curveList = EC_Store.mapKeyToCurve(pubkeys.values());
+ for (Map.Entry<EC_Curve, List<EC_Key.Public>> e : curveList) {
+ EC_Curve curve = e.getKey();
+ List<EC_Key.Public> keys = e.getValue();
+
+ Test allocate = CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), Result.ExpectedValue.SUCCESS);
+ Test set = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), Result.ExpectedValue.SUCCESS);
+ Test generate = CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_LOCAL), Result.ExpectedValue.SUCCESS);
+
+ Test prepare = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Prepare and generate keypair on " + curve.getId(), allocate, set, generate);
+
+ List<Test> ecdhTests = new LinkedList<>();
+ for (EC_Key.Public pub : keys) {
+ Command ecdhCommand = new Command.ECDH_direct(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH, pub.flatten());
+ ecdhTests.add(CommandTest.expect(ecdhCommand, Result.ExpectedValue.FAILURE, "Card correctly rejected point on degenerate curve.", "Card incorrectly accepted point on degenerate curve."));
+ }
+ Test ecdh = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Perform ECDH with degenerate public points", ecdhTests.toArray(new Test[0]));
+ if (cfg.cleanup) {
+ Test cleanup = CommandTest.expect(new Command.Cleanup(this.card), Result.ExpectedValue.SUCCESS);
+ doTest(CompoundTest.greedyAllTry(Result.ExpectedValue.SUCCESS, "Degenerate curve test of " + curve.getId(), prepare, ecdh, cleanup));
+ } else {
+ doTest(CompoundTest.greedyAllTry(Result.ExpectedValue.SUCCESS, "Degenerate curve test of " + curve.getId(), prepare, ecdh));
+ }
+
+ }
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/test/CardEdgeCasesSuite.java b/src/cz/crcs/ectester/reader/test/CardEdgeCasesSuite.java
new file mode 100644
index 0000000..dc489a0
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/CardEdgeCasesSuite.java
@@ -0,0 +1,189 @@
+package cz.crcs.ectester.reader.test;
+
+import cz.crcs.ectester.applet.ECTesterApplet;
+import cz.crcs.ectester.applet.EC_Consts;
+import cz.crcs.ectester.common.ec.EC_Curve;
+import cz.crcs.ectester.common.ec.EC_KAResult;
+import cz.crcs.ectester.common.ec.EC_Key;
+import cz.crcs.ectester.common.ec.EC_Params;
+import cz.crcs.ectester.common.output.TestWriter;
+import cz.crcs.ectester.common.test.CompoundTest;
+import cz.crcs.ectester.common.test.Result;
+import cz.crcs.ectester.common.test.Test;
+import cz.crcs.ectester.common.test.TestCallback;
+import cz.crcs.ectester.common.util.ByteUtil;
+import cz.crcs.ectester.common.util.ECUtil;
+import cz.crcs.ectester.data.EC_Store;
+import cz.crcs.ectester.reader.CardMngr;
+import cz.crcs.ectester.reader.ECTesterReader;
+import cz.crcs.ectester.reader.command.Command;
+import cz.crcs.ectester.reader.response.Response;
+import javacard.security.CryptoException;
+import javacard.security.KeyPair;
+
+import java.math.BigInteger;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.stream.Collectors;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class CardEdgeCasesSuite extends CardTestSuite {
+ public CardEdgeCasesSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) {
+ super(writer, cfg, cardManager, "edge-cases", "The edge-cases test suite tests various inputs to ECDH which may cause an implementation to achieve a certain edge-case state during it.",
+ "Some of the data is from the google/Wycheproof project. Tests include CVE-2017-10176 and CVE-2017-8932.",
+ "Various edge private key values are also tested.");
+ }
+
+ @Override
+ protected void runTests() throws Exception {
+ Map<String, EC_KAResult> results = EC_Store.getInstance().getObjects(EC_KAResult.class, "wycheproof");
+ List<Map.Entry<String, List<EC_KAResult>>> groupList = EC_Store.mapToPrefix(results.values());
+ for (Map.Entry<String, List<EC_KAResult>> e : groupList) {
+ String description = null;
+ switch (e.getKey()) {
+ case "addsub":
+ description = "Tests for addition-subtraction chains.";
+ break;
+ case "cve_2017_10176":
+ description = "Tests for CVE-2017-10176.";
+ break;
+ case "cve_2017_8932":
+ description = "Tests for CVE-2017-8932.";
+ break;
+ }
+
+ List<Test> groupTests = new LinkedList<>();
+ List<Map.Entry<EC_Curve, List<EC_KAResult>>> curveList = EC_Store.mapResultToCurve(e.getValue());
+ for (Map.Entry<EC_Curve, List<EC_KAResult>> c : curveList) {
+ EC_Curve curve = c.getKey();
+
+ List<Test> curveTests = new LinkedList<>();
+ Test allocate = CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), Result.ExpectedValue.SUCCESS);
+ Test set = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), Result.ExpectedValue.SUCCESS);
+ Test prepareCurve = CompoundTest.greedyAll(Result.ExpectedValue.SUCCESS, "Prepare curve", allocate, set);
+
+ List<EC_KAResult> values = c.getValue();
+ for (EC_KAResult value : values) {
+ String id = value.getId();
+ String privkeyId = value.getOneKey();
+ String pubkeyId = value.getOtherKey();
+
+ EC_Key.Private privkey = EC_Store.getInstance().getObject(EC_Key.Private.class, privkeyId);
+ EC_Key.Public pubkey = EC_Store.getInstance().getObject(EC_Key.Public.class, pubkeyId);
+
+ Test setPrivkey = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.CURVE_external, privkey.getParams(), privkey.flatten()), Result.ExpectedValue.SUCCESS);
+ Test setPubkey = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, pubkey.getParams(), pubkey.flatten()), Result.ExpectedValue.SUCCESS);
+ Test ecdhPreTest = CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH), Result.ExpectedValue.SUCCESS);
+ Test ecdh = CommandTest.function(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_TRUE, EC_Consts.TRANSFORMATION_NONE, value.getJavaCardKA()), new TestCallback<CommandTestable>() {
+ @Override
+ public Result apply(CommandTestable testable) {
+ Response.ECDH dh = (Response.ECDH) testable.getResponse();
+ if (dh.getSW(0) == CryptoException.NO_SUCH_ALGORITHM) {
+ return new Result(Result.Value.SUCCESS, "ECDH algorithm unsupported.");
+ }
+ if (!dh.successful())
+ return new Result(Result.Value.FAILURE, "ECDH was unsuccessful.");
+ if (!dh.hasSecret())
+ return new Result(Result.Value.FAILURE, "ECDH response did not contain the derived secret.");
+ if (!ByteUtil.compareBytes(dh.getSecret(), 0, value.getData(0), 0, dh.secretLength())) {
+ int firstDiff = ByteUtil.diffBytes(dh.getSecret(), 0, value.getData(0), 0, dh.secretLength());
+ System.err.println(ByteUtil.bytesToHex(dh.getSecret()));
+ System.err.println(ByteUtil.bytesToHex(value.getData(0)));
+ return new Result(Result.Value.FAILURE, "ECDH derived secret does not match the test-vector, first difference was at byte " + String.valueOf(firstDiff) + ".");
+ }
+ return new Result(Result.Value.SUCCESS);
+ }
+ });
+
+ Test prepare = CompoundTest.greedyAll(Result.ExpectedValue.SUCCESS, "Prepare", setPrivkey, setPubkey);
+ Test ka = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Do", ecdhPreTest, ecdh);
+
+ Test one = CompoundTest.greedyAllTry(Result.ExpectedValue.SUCCESS, "Test " + id + ".", prepare, ka);
+ curveTests.add(one);
+ }
+
+ Test curveTest = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Tests", curveTests.toArray(new Test[0]));
+ groupTests.add(CompoundTest.greedyAllTry(Result.ExpectedValue.SUCCESS, "Tests on " + curve.getId() + ".", prepareCurve, curveTest));
+ }
+ doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, description, groupTests.toArray(new Test[0])));
+ }
+
+ Map<String, EC_Curve> curveMap = EC_Store.getInstance().getObjects(EC_Curve.class, "secg");
+ List<EC_Curve> curves = curveMap.entrySet().stream().filter((e) -> e.getKey().endsWith("r1") && e.getValue().getField() == KeyPair.ALG_EC_FP).map(Map.Entry::getValue).collect(Collectors.toList());
+ Random rand = new Random();
+ for (EC_Curve curve : curves) {
+ Test key = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), KeyPair.ALG_EC_FP), Result.ExpectedValue.SUCCESS));
+ if (!key.ok()) {
+ doTest(CompoundTest.all(Result.ExpectedValue.FAILURE, "No support for " + curve.getBits() + "b ALG_EC_FP.", key));
+ continue;
+ }
+ Test set = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), Result.ExpectedValue.SUCCESS);
+ Test generate = CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_LOCAL), Result.ExpectedValue.SUCCESS);
+ Test setup = CompoundTest.all(Result.ExpectedValue.SUCCESS, "KeyPair setup.", key, set, generate);
+
+ Test zeroS = ecdhTest(new Command.Transform(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, EC_Consts.PARAMETER_S, EC_Consts.TRANSFORMATION_ZERO), "ECDH with S = 0.", Result.ExpectedValue.FAILURE, Result.ExpectedValue.FAILURE);
+ Test oneS = ecdhTest(new Command.Transform(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, EC_Consts.PARAMETER_S, EC_Consts.TRANSFORMATION_ONE), "ECDH with S = 1.", Result.ExpectedValue.FAILURE, Result.ExpectedValue.FAILURE);
+
+ byte[] r = curve.getParam(EC_Consts.PARAMETER_R)[0];
+ BigInteger R = new BigInteger(1, r);
+ BigInteger smaller = new BigInteger(curve.getBits(), rand).mod(R);
+ BigInteger diff = R.divide(BigInteger.valueOf(10));
+ BigInteger randDiff = new BigInteger(diff.bitLength(), rand).mod(diff);
+ BigInteger larger = R.add(randDiff);
+
+ EC_Params smallerParams = makeParams(smaller);
+ Test smallerS = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, smallerParams.getParams(), smallerParams.flatten()), "ECDH with S < r.", Result.ExpectedValue.SUCCESS, Result.ExpectedValue.SUCCESS);
+
+ EC_Params exactParams = makeParams(R);
+ Test exactS = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, exactParams.getParams(), exactParams.flatten()), "ECDH with S = r.", Result.ExpectedValue.FAILURE, Result.ExpectedValue.FAILURE);
+
+ EC_Params largerParams = makeParams(larger);
+ Test largerS = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, largerParams.getParams(), largerParams.flatten()), "ECDH with S > r.", Result.ExpectedValue.ANY, Result.ExpectedValue.ANY);
+
+ BigInteger rm1 = R.subtract(BigInteger.ONE);
+ BigInteger rp1 = R.add(BigInteger.ONE);
+
+ EC_Params rm1Params = makeParams(rm1);
+ Test rm1S = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, rm1Params.getParams(), rm1Params.flatten()), "ECDH with S = r - 1.", Result.ExpectedValue.SUCCESS, Result.ExpectedValue.SUCCESS);
+
+ EC_Params rp1Params = makeParams(rp1);
+ Test rp1S = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, rp1Params.getParams(), rp1Params.flatten()), "ECDH with S = r + 1.", Result.ExpectedValue.ANY, Result.ExpectedValue.ANY);
+
+ byte[] k = curve.getParam(EC_Consts.PARAMETER_K)[0];
+ BigInteger K = new BigInteger(1, k);
+ BigInteger kr = K.multiply(R);
+ BigInteger krm1 = kr.subtract(BigInteger.ONE);
+ BigInteger krp1 = kr.add(BigInteger.ONE);
+
+ EC_Params krParams = makeParams(kr);
+ Test krS /*ONE!*/ = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, krParams.getParams(), krParams.flatten()), "ECDH with S = k * r.", Result.ExpectedValue.FAILURE, Result.ExpectedValue.FAILURE);
+
+ EC_Params krm1Params = makeParams(krm1);
+ Test krm1S = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, krm1Params.getParams(), krm1Params.flatten()), "ECDH with S = (k * r) - 1.", Result.ExpectedValue.FAILURE, Result.ExpectedValue.FAILURE);
+
+ EC_Params krp1Params = makeParams(krp1);
+ Test krp1S = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, krp1Params.getParams(), krp1Params.flatten()), "ECDH with S = (k * r) + 1.", Result.ExpectedValue.FAILURE, Result.ExpectedValue.FAILURE);
+
+ doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Tests with edge-case private key values over " + curve.getId() + ".", setup, zeroS, oneS, smallerS, exactS, largerS, rm1S, rp1S, krS, krm1S, krp1S));
+ }
+ }
+
+ private Test ecdhTest(Command setPriv, String desc, Result.ExpectedValue setExpect, Result.ExpectedValue ecdhExpect) {
+ Test set = CommandTest.expect(setPriv, setExpect);
+ Test ecdh = CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_TRUE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH), ecdhExpect);
+
+ return CompoundTest.any(Result.ExpectedValue.SUCCESS, desc, set, ecdh);
+ }
+
+ private EC_Params makeParams(BigInteger s) {
+ return makeParams(ECUtil.toByteArray(s, s.bitLength()));
+ }
+
+ private EC_Params makeParams(byte[] s) {
+ return new EC_Params(EC_Consts.PARAMETER_S, new byte[][]{s});
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/test/CardInvalidCurvesSuite.java b/src/cz/crcs/ectester/reader/test/CardInvalidCurvesSuite.java
deleted file mode 100644
index 8424d45..0000000
--- a/src/cz/crcs/ectester/reader/test/CardInvalidCurvesSuite.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package cz.crcs.ectester.reader.test;
-
-import cz.crcs.ectester.applet.ECTesterApplet;
-import cz.crcs.ectester.applet.EC_Consts;
-import cz.crcs.ectester.common.ec.EC_Curve;
-import cz.crcs.ectester.common.ec.EC_Key;
-import cz.crcs.ectester.common.output.TestWriter;
-import cz.crcs.ectester.common.test.CompoundTest;
-import cz.crcs.ectester.common.test.Test;
-import cz.crcs.ectester.data.EC_Store;
-import cz.crcs.ectester.reader.CardMngr;
-import cz.crcs.ectester.reader.ECTesterReader;
-import cz.crcs.ectester.reader.command.Command;
-import javacard.security.KeyPair;
-
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-
-import static cz.crcs.ectester.common.test.Result.ExpectedValue;
-
-/**
- * @author Jan Jancar johny@neuromancer.sk
- */
-public class CardInvalidCurvesSuite extends CardTestSuite {
-
- public CardInvalidCurvesSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) {
- super(writer, cfg, cardManager, "invalid", "The invalid curve suite tests whether the card rejects points outside of the curve during ECDH.");
- }
-
- @Override
- protected void runTests() throws Exception {
- /* Set original curves (secg/nist/brainpool). Generate local.
- * Try ECDH with invalid public keys of increasing (or decreasing) order.
- */
- Map<String, EC_Key.Public> pubkeys = EC_Store.getInstance().getObjects(EC_Key.Public.class, "invalid");
- Map<EC_Curve, List<EC_Key.Public>> curves = new HashMap<>();
- for (EC_Key.Public key : pubkeys.values()) {
- EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, key.getCurve());
- if (curve.getBits() != cfg.bits && !cfg.all) {
- continue;
- }
- if (curve.getField() == KeyPair.ALG_EC_FP && !cfg.primeField || curve.getField() == KeyPair.ALG_EC_F2M && !cfg.binaryField) {
- continue;
- }
- List<EC_Key.Public> keys = curves.getOrDefault(curve, new LinkedList<>());
- keys.add(key);
- curves.putIfAbsent(curve, keys);
- }
- for (Map.Entry<EC_Curve, List<EC_Key.Public>> e : curves.entrySet()) {
- EC_Curve curve = e.getKey();
- List<EC_Key.Public> keys = e.getValue();
-
- doTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS));
- doTest(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.SUCCESS));
- doTest(CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_LOCAL), ExpectedValue.SUCCESS));
- List<Test> ecdhTests = new LinkedList<>();
- for (EC_Key.Public pub : keys) {
- Command ecdhCommand = new Command.ECDH_direct(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, ECTesterApplet.KeyAgreement_ALG_EC_SVDP_DH, pub.flatten());
- ecdhTests.add(CommandTest.expect(ecdhCommand, ExpectedValue.FAILURE, "Card correctly rejected point on invalid curve.", "Card incorrectly accepted point on invalid curve."));
- }
- doTest(CompoundTest.all(ExpectedValue.SUCCESS, "Invalid curve test of " + curve.getId(), ecdhTests.toArray(new Test[0])));
- new Command.Cleanup(this.card).send();
- }
- }
-}
diff --git a/src/cz/crcs/ectester/reader/test/CardInvalidSuite.java b/src/cz/crcs/ectester/reader/test/CardInvalidSuite.java
new file mode 100644
index 0000000..59a427f
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/CardInvalidSuite.java
@@ -0,0 +1,81 @@
+package cz.crcs.ectester.reader.test;
+
+import cz.crcs.ectester.applet.ECTesterApplet;
+import cz.crcs.ectester.applet.EC_Consts;
+import cz.crcs.ectester.common.ec.EC_Curve;
+import cz.crcs.ectester.common.ec.EC_Key;
+import cz.crcs.ectester.common.output.TestWriter;
+import cz.crcs.ectester.common.test.CompoundTest;
+import cz.crcs.ectester.common.test.Result;
+import cz.crcs.ectester.common.test.Test;
+import cz.crcs.ectester.data.EC_Store;
+import cz.crcs.ectester.reader.CardMngr;
+import cz.crcs.ectester.reader.ECTesterReader;
+import cz.crcs.ectester.reader.command.Command;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+import static cz.crcs.ectester.common.test.Result.ExpectedValue;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class CardInvalidSuite extends CardTestSuite {
+
+ public CardInvalidSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) {
+ super(writer, cfg, cardManager, "invalid", "The invalid curve suite tests whether the card rejects points outside of the curve during ECDH.");
+ }
+
+ @Override
+ protected void runTests() throws Exception {
+ /* Set original curves (secg/nist/brainpool). Generate local.
+ * Try ECDH with invalid public keys of increasing order.
+ */
+ Map<String, EC_Key.Public> pubkeys = EC_Store.getInstance().getObjects(EC_Key.Public.class, "invalid");
+ List<Map.Entry<EC_Curve, List<EC_Key.Public>>> curveList = EC_Store.mapKeyToCurve(pubkeys.values());
+ for (Map.Entry<EC_Curve, List<EC_Key.Public>> e : curveList) {
+ EC_Curve curve = e.getKey();
+ List<EC_Key.Public> keys = e.getValue();
+
+ Test allocate = CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS);
+ Test set = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.SUCCESS);
+ Test generate = CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_LOCAL), ExpectedValue.SUCCESS);
+
+ Test prepare = CompoundTest.all(ExpectedValue.SUCCESS, "Prepare and generate keypair on " + curve.getId(), allocate, set, generate);
+
+ List<Test> ecdhTests = new LinkedList<>();
+ for (EC_Key.Public pub : keys) {
+ Command ecdhCommand = new Command.ECDH_direct(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH, pub.flatten());
+ ecdhTests.add(CommandTest.expect(ecdhCommand, ExpectedValue.FAILURE, "Card correctly rejected point on invalid curve.", "Card incorrectly accepted point on invalid curve."));
+ }
+ Test ecdh = CompoundTest.all(ExpectedValue.SUCCESS, "Perform ECDH with invalid public points", ecdhTests.toArray(new Test[0]));
+
+ Random r = new Random();
+ byte[] raw = new byte[128];
+ byte[] sig = new byte[40];
+ r.nextBytes(raw);
+ r.nextBytes(sig);
+
+ List<Test> ecdsaTests = new LinkedList<>();
+ for (EC_Key.Public pub : keys) {
+ Command setCommand = new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, pub.getParams(), pub.flatten());
+ Test setTest = CommandTest.expect(setCommand, Result.ExpectedValue.ANY);
+ Command ecdsaCommand = new Command.ECDSA_verify(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.Signature_ALG_ECDSA_SHA, raw, sig);
+ Test ecdsaTest = CommandTest.expect(ecdsaCommand, Result.ExpectedValue.FAILURE);
+ ecdsaTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Verify random ECDSA signature by " + pub.getId(), setTest, ecdsaTest));
+ }
+ Test ecdsa = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Verify random ECDSA signature by invalid public points", ecdsaTests.toArray(new Test[0]));
+
+ Test tests = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Test ECDH and ECDSA with points on invalid curves.", ecdh, ecdsa);
+ if (cfg.cleanup) {
+ Test cleanup = CommandTest.expect(new Command.Cleanup(this.card), ExpectedValue.SUCCESS);
+ doTest(CompoundTest.greedyAllTry(ExpectedValue.SUCCESS, "Invalid curve test of " + curve.getId(), prepare, tests, cleanup));
+ } else {
+ doTest(CompoundTest.greedyAllTry(ExpectedValue.SUCCESS, "Invalid curve test of " + curve.getId(), prepare, tests));
+ }
+ }
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/test/CardMiscSuite.java b/src/cz/crcs/ectester/reader/test/CardMiscSuite.java
new file mode 100644
index 0000000..e568f67
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/CardMiscSuite.java
@@ -0,0 +1,60 @@
+package cz.crcs.ectester.reader.test;
+
+import cz.crcs.ectester.applet.ECTesterApplet;
+import cz.crcs.ectester.applet.EC_Consts;
+import cz.crcs.ectester.common.ec.EC_Curve;
+import cz.crcs.ectester.common.output.TestWriter;
+import cz.crcs.ectester.common.test.CompoundTest;
+import cz.crcs.ectester.common.test.Result;
+import cz.crcs.ectester.common.test.Test;
+import cz.crcs.ectester.common.util.CardUtil;
+import cz.crcs.ectester.data.EC_Store;
+import cz.crcs.ectester.reader.CardMngr;
+import cz.crcs.ectester.reader.ECTesterReader;
+import cz.crcs.ectester.reader.command.Command;
+
+import java.util.Map;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class CardMiscSuite extends CardTestSuite {
+
+ public CardMiscSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) {
+ super(writer, cfg, cardManager, "miscellaneous", "Some miscellaneous tests, tries ECDH and ECDSA over supersingular curves, anomalous curves and some Barreto-Naehrig curves with small embedding degree and CM discriminant.");
+ }
+
+ @Override
+ protected void runTests() throws Exception {
+ Map<String, EC_Curve> anCurves = EC_Store.getInstance().getObjects(EC_Curve.class, "anomalous");
+ Map<String, EC_Curve> ssCurves = EC_Store.getInstance().getObjects(EC_Curve.class, "supersingular");
+ Map<String, EC_Curve> bnCurves = EC_Store.getInstance().getObjects(EC_Curve.class, "Barreto-Naehrig");
+
+ testCurves(anCurves, "anomalous", Result.ExpectedValue.FAILURE);
+ testCurves(ssCurves, "supersingular", Result.ExpectedValue.FAILURE);
+ testCurves(bnCurves, "Barreto-Naehrig", Result.ExpectedValue.ANY);
+ }
+
+ private void testCurves(Map<String, EC_Curve> curves, String catName, Result.ExpectedValue expected) throws Exception {
+ for (EC_Curve curve : curves.values()) {
+ Test allocateFirst = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), Result.ExpectedValue.SUCCESS));
+ if (!allocateFirst.ok()) {
+ doTest(CompoundTest.all(Result.ExpectedValue.FAILURE, "No support for " + curve.getBits() + "b " + CardUtil.getKeyTypeString(curve.getField()) + ".", allocateFirst));
+ continue;
+ }
+
+ Test set = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), Result.ExpectedValue.SUCCESS);
+ Test generate = CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_BOTH), Result.ExpectedValue.ANY);
+ Test ka = CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH), expected);
+ Test sig = CommandTest.expect(new Command.ECDSA(this.card, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.Signature_ALG_ECDSA_SHA, ECTesterApplet.EXPORT_FALSE, null), expected);
+ Test perform = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Perform ECDH and ECDSA", ka, sig);
+
+ if (cfg.cleanup) {
+ Test cleanup = CommandTest.expect(new Command.Cleanup(this.card), Result.ExpectedValue.SUCCESS);
+ doTest(CompoundTest.greedyAll(Result.ExpectedValue.SUCCESS, "Tests over " + curve.getBits() + " " + catName + " curve: " + curve.getId() + ".", allocateFirst, set, generate, perform, cleanup));
+ } else {
+ doTest(CompoundTest.greedyAll(Result.ExpectedValue.SUCCESS, "Tests over " + curve.getBits() + " " + catName + " curve: " + curve.getId() + ".", allocateFirst, set, generate, perform));
+ }
+ }
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/test/CardTestSuite.java b/src/cz/crcs/ectester/reader/test/CardTestSuite.java
index 0eccd16..3578f9c 100644
--- a/src/cz/crcs/ectester/reader/test/CardTestSuite.java
+++ b/src/cz/crcs/ectester/reader/test/CardTestSuite.java
@@ -12,7 +12,7 @@ public abstract class CardTestSuite extends TestSuite {
ECTesterReader.Config cfg;
CardMngr card;
- CardTestSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager, String name, String description) {
+ CardTestSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager, String name, String... description) {
super(writer, name, description);
this.card = cardManager;
this.cfg = cfg;
diff --git a/src/cz/crcs/ectester/reader/test/CardTestVectorSuite.java b/src/cz/crcs/ectester/reader/test/CardTestVectorSuite.java
index 73c6621..052e480 100644
--- a/src/cz/crcs/ectester/reader/test/CardTestVectorSuite.java
+++ b/src/cz/crcs/ectester/reader/test/CardTestVectorSuite.java
@@ -4,14 +4,16 @@ import cz.crcs.ectester.applet.ECTesterApplet;
import cz.crcs.ectester.applet.EC_Consts;
import cz.crcs.ectester.common.ec.*;
import cz.crcs.ectester.common.output.TestWriter;
-import cz.crcs.ectester.common.test.*;
+import cz.crcs.ectester.common.test.CompoundTest;
+import cz.crcs.ectester.common.test.Result;
+import cz.crcs.ectester.common.test.Test;
+import cz.crcs.ectester.common.test.TestCallback;
import cz.crcs.ectester.common.util.ByteUtil;
import cz.crcs.ectester.data.EC_Store;
import cz.crcs.ectester.reader.CardMngr;
import cz.crcs.ectester.reader.ECTesterReader;
import cz.crcs.ectester.reader.command.Command;
import cz.crcs.ectester.reader.response.Response;
-import javacard.security.KeyPair;
import java.io.IOException;
import java.util.LinkedList;
@@ -38,12 +40,6 @@ public class CardTestVectorSuite extends CardTestSuite {
Map<String, EC_KAResult> results = EC_Store.getInstance().getObjects(EC_KAResult.class, "test");
for (EC_KAResult result : results.values()) {
EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, result.getCurve());
- if (curve.getBits() != cfg.bits && !cfg.all) {
- continue;
- }
- if (curve.getField() == KeyPair.ALG_EC_FP && !cfg.primeField || curve.getField() == KeyPair.ALG_EC_F2M && !cfg.binaryField) {
- continue;
- }
EC_Params onekey = EC_Store.getInstance().getObject(EC_Keypair.class, result.getOneKey());
if (onekey == null) {
onekey = EC_Store.getInstance().getObject(EC_Key.Private.class, result.getOneKey());
@@ -61,7 +57,7 @@ public class CardTestVectorSuite extends CardTestSuite {
testVector.add(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.SUCCESS));
testVector.add(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.CURVE_external, EC_Consts.PARAMETER_S, onekey.flatten(EC_Consts.PARAMETER_S)), ExpectedValue.SUCCESS));
testVector.add(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, EC_Consts.PARAMETER_W, otherkey.flatten(EC_Consts.PARAMETER_W)), ExpectedValue.SUCCESS));
- testVector.add(CommandTest.function(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_TRUE, EC_Consts.CORRUPTION_NONE, result.getJavaCardKA()), new TestCallback<CommandTestable>() {
+ testVector.add(CommandTest.function(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_TRUE, EC_Consts.TRANSFORMATION_NONE, result.getJavaCardKA()), new TestCallback<CommandTestable>() {
@Override
public Result apply(CommandTestable testable) {
Response.ECDH dh = (Response.ECDH) testable.getResponse();
@@ -76,8 +72,10 @@ public class CardTestVectorSuite extends CardTestSuite {
return new Result(Value.SUCCESS);
}
}));
- doTest(CompoundTest.all(ExpectedValue.SUCCESS, "Test vector " + result.getId(), testVector.toArray(new Test[0])));
- new Command.Cleanup(this.card).send();
+ if (cfg.cleanup) {
+ testVector.add(CommandTest.expect(new Command.Cleanup(this.card), ExpectedValue.SUCCESS));
+ }
+ doTest(CompoundTest.greedyAll(ExpectedValue.SUCCESS, "Test vector " + result.getId(), testVector.toArray(new Test[0])));
}
}
}
diff --git a/src/cz/crcs/ectester/reader/test/CardTwistSuite.java b/src/cz/crcs/ectester/reader/test/CardTwistSuite.java
new file mode 100644
index 0000000..1e1f5f3
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/CardTwistSuite.java
@@ -0,0 +1,75 @@
+package cz.crcs.ectester.reader.test;
+
+import cz.crcs.ectester.applet.ECTesterApplet;
+import cz.crcs.ectester.applet.EC_Consts;
+import cz.crcs.ectester.common.ec.EC_Curve;
+import cz.crcs.ectester.common.ec.EC_Key;
+import cz.crcs.ectester.common.output.TestWriter;
+import cz.crcs.ectester.common.test.CompoundTest;
+import cz.crcs.ectester.common.test.Result;
+import cz.crcs.ectester.common.test.Test;
+import cz.crcs.ectester.data.EC_Store;
+import cz.crcs.ectester.reader.CardMngr;
+import cz.crcs.ectester.reader.ECTesterReader;
+import cz.crcs.ectester.reader.command.Command;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class CardTwistSuite extends CardTestSuite {
+ public CardTwistSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) {
+ super(writer, cfg, cardManager, "twist", "The twist test suite tests whether the card correctly rejects points on the quadratic twist of the curve during ECDH.");
+ }
+
+ @Override
+ protected void runTests() throws Exception {
+ Map<String, EC_Key.Public> pubkeys = EC_Store.getInstance().getObjects(EC_Key.Public.class, "twist");
+ List<Map.Entry<EC_Curve, List<EC_Key.Public>>> curveList = EC_Store.mapKeyToCurve(pubkeys.values());
+ for (Map.Entry<EC_Curve, List<EC_Key.Public>> e : curveList) {
+ EC_Curve curve = e.getKey();
+ List<EC_Key.Public> keys = e.getValue();
+
+ Test allocate = CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), Result.ExpectedValue.SUCCESS);
+ Test set = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), Result.ExpectedValue.SUCCESS);
+ Test generate = CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_LOCAL), Result.ExpectedValue.SUCCESS);
+
+ Test prepare = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Prepare and generate keypair on " + curve.getId(), allocate, set, generate);
+
+ List<Test> ecdhTests = new LinkedList<>();
+ for (EC_Key.Public pub : keys) {
+ Command ecdhCommand = new Command.ECDH_direct(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH, pub.flatten());
+ ecdhTests.add(CommandTest.expect(ecdhCommand, Result.ExpectedValue.FAILURE, "Card correctly rejected point on twist.", "Card incorrectly accepted point on twist."));
+ }
+ Test ecdh = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Perform ECDH with public points on twist", ecdhTests.toArray(new Test[0]));
+
+ Random r = new Random();
+ byte[] raw = new byte[128];
+ byte[] sig = new byte[40];
+ r.nextBytes(raw);
+ r.nextBytes(sig);
+
+ List<Test> ecdsaTests = new LinkedList<>();
+ for (EC_Key.Public pub : keys) {
+ Command setCommand = new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, pub.getParams(), pub.flatten());
+ Test setTest = CommandTest.expect(setCommand, Result.ExpectedValue.ANY);
+ Command ecdsaCommand = new Command.ECDSA_verify(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.Signature_ALG_ECDSA_SHA, raw, sig);
+ Test ecdsaTest = CommandTest.expect(ecdsaCommand, Result.ExpectedValue.FAILURE);
+ ecdsaTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Verify random ECDSA signature by " + pub.getId(), setTest, ecdsaTest));
+ }
+ Test ecdsa = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Verify random ECDSA signature by public points on twist", ecdsaTests.toArray(new Test[0]));
+
+ Test tests = CompoundTest.all(Result.ExpectedValue.SUCCESS, ecdh, ecdsa);
+ if (cfg.cleanup) {
+ Test cleanup = CommandTest.expect(new Command.Cleanup(this.card), Result.ExpectedValue.SUCCESS);
+ doTest(CompoundTest.greedyAllTry(Result.ExpectedValue.SUCCESS, "Twist test of " + curve.getId(), prepare, tests, cleanup));
+ } else {
+ doTest(CompoundTest.greedyAllTry(Result.ExpectedValue.SUCCESS, "Twist test of " + curve.getId(), prepare, tests));
+ }
+ }
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/test/CardTwistTestSuite.java b/src/cz/crcs/ectester/reader/test/CardTwistTestSuite.java
deleted file mode 100644
index c43b234..0000000
--- a/src/cz/crcs/ectester/reader/test/CardTwistTestSuite.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package cz.crcs.ectester.reader.test;
-
-import cz.crcs.ectester.applet.ECTesterApplet;
-import cz.crcs.ectester.applet.EC_Consts;
-import cz.crcs.ectester.common.ec.EC_Curve;
-import cz.crcs.ectester.common.ec.EC_Key;
-import cz.crcs.ectester.common.output.TestWriter;
-import cz.crcs.ectester.common.test.CompoundTest;
-import cz.crcs.ectester.common.test.Result;
-import cz.crcs.ectester.common.test.Test;
-import cz.crcs.ectester.data.EC_Store;
-import cz.crcs.ectester.reader.CardMngr;
-import cz.crcs.ectester.reader.ECTesterReader;
-import cz.crcs.ectester.reader.command.Command;
-import javacard.security.KeyPair;
-
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-
-/**
- * @author Jan Jancar johny@neuromancer.sk
- */
-public class CardTwistTestSuite extends CardTestSuite {
- public CardTwistTestSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) {
- super(writer, cfg, cardManager, "twist", "The twist test suite tests whether the card correctly rejects points on the quadratic twist of the curve during ECDH.");
- }
-
- @Override
- protected void runTests() throws Exception {
- Map<String, EC_Key.Public> pubkeys = EC_Store.getInstance().getObjects(EC_Key.Public.class, "twist");
- Map<EC_Curve, List<EC_Key.Public>> curves = new HashMap<>();
- for (EC_Key.Public key : pubkeys.values()) {
- EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, key.getCurve());
- if (curve.getBits() != cfg.bits && !cfg.all) {
- continue;
- }
- if (curve.getField() == KeyPair.ALG_EC_FP && !cfg.primeField || curve.getField() == KeyPair.ALG_EC_F2M && !cfg.binaryField) {
- continue;
- }
- List<EC_Key.Public> keys = curves.getOrDefault(curve, new LinkedList<>());
- keys.add(key);
- curves.putIfAbsent(curve, keys);
- }
- for (Map.Entry<EC_Curve, List<EC_Key.Public>> e : curves.entrySet()) {
- EC_Curve curve = e.getKey();
- List<EC_Key.Public> keys = e.getValue();
-
- doTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), Result.ExpectedValue.SUCCESS));
- doTest(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), Result.ExpectedValue.SUCCESS));
- doTest(CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_LOCAL), Result.ExpectedValue.SUCCESS));
- List<Test> ecdhTests = new LinkedList<>();
- for (EC_Key.Public pub : keys) {
- Command ecdhCommand = new Command.ECDH_direct(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, ECTesterApplet.KeyAgreement_ALG_EC_SVDP_DH, pub.flatten());
- ecdhTests.add(CommandTest.expect(ecdhCommand, Result.ExpectedValue.FAILURE, "Card correctly rejected point on twist.", "Card incorrectly accepted point on twist."));
- }
- doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Twist test of " + curve.getId(), ecdhTests.toArray(new Test[0])));
- new Command.Cleanup(this.card).send();
- }
- }
-}
diff --git a/src/cz/crcs/ectester/reader/test/CardWrongCurvesSuite.java b/src/cz/crcs/ectester/reader/test/CardWrongCurvesSuite.java
deleted file mode 100644
index cac8fab..0000000
--- a/src/cz/crcs/ectester/reader/test/CardWrongCurvesSuite.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package cz.crcs.ectester.reader.test;
-
-import cz.crcs.ectester.applet.ECTesterApplet;
-import cz.crcs.ectester.applet.EC_Consts;
-import cz.crcs.ectester.common.ec.EC_Curve;
-import cz.crcs.ectester.common.output.TestWriter;
-import cz.crcs.ectester.common.test.CompoundTest;
-import cz.crcs.ectester.common.test.Result;
-import cz.crcs.ectester.common.test.Test;
-import cz.crcs.ectester.data.EC_Store;
-import cz.crcs.ectester.reader.CardMngr;
-import cz.crcs.ectester.reader.ECTesterReader;
-import cz.crcs.ectester.reader.command.Command;
-import javacard.security.KeyPair;
-
-import java.util.Map;
-
-/**
- * @author Jan Jancar johny@neuromancer.sk
- */
-public class CardWrongCurvesSuite extends CardTestSuite {
-
- public CardWrongCurvesSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) {
- super(writer, cfg, cardManager, "wrong", "The wrong curve suite run whether the card rejects domain parameters which are not curves.");
- }
-
- @Override
- protected void runTests() throws Exception {
- /* Just do the default run on the wrong curves.
- * These should generally fail, the curves aren't curves.
- */
- Map<String, EC_Curve> curves = EC_Store.getInstance().getObjects(EC_Curve.class, "wrong");
- for (Map.Entry<String, EC_Curve> e : curves.entrySet()) {
- EC_Curve curve = e.getValue();
- if (curve.getBits() != cfg.bits && !cfg.all) {
- continue;
- }
- if (curve.getField() == KeyPair.ALG_EC_FP && !cfg.primeField || curve.getField() == KeyPair.ALG_EC_F2M && !cfg.binaryField) {
- continue;
- }
- Test key = doTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), Result.ExpectedValue.SUCCESS));
- if (!key.ok()) {
- continue;
- }
- Test set = runTest(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), Result.ExpectedValue.SUCCESS));
- Test generate = runTest(CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_BOTH), Result.ExpectedValue.SUCCESS));
- doTest(CompoundTest.any(Result.ExpectedValue.FAILURE, "Set wrong curve and generate keypairs, should fail." ,set, generate));
-
- for (byte kaType : EC_Consts.KA_TYPES) {
- Test allocate = runTest(CommandTest.expect(new Command.AllocateKeyAgreement(this.card, kaType), Result.ExpectedValue.SUCCESS));
- if (allocate.ok()) {
- Test ka = runTest(CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, kaType), Result.ExpectedValue.FAILURE));
- doTest(CompoundTest.any(Result.ExpectedValue.FAILURE, "Allocate and perform KA, should fail.", allocate, ka));
- }
- }
- }
- }
-}
diff --git a/src/cz/crcs/ectester/reader/test/CardWrongSuite.java b/src/cz/crcs/ectester/reader/test/CardWrongSuite.java
new file mode 100644
index 0000000..8bc7c90
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/CardWrongSuite.java
@@ -0,0 +1,227 @@
+package cz.crcs.ectester.reader.test;
+
+import cz.crcs.ectester.applet.ECTesterApplet;
+import cz.crcs.ectester.applet.EC_Consts;
+import cz.crcs.ectester.common.ec.EC_Curve;
+import cz.crcs.ectester.common.ec.EC_Params;
+import cz.crcs.ectester.common.output.TestWriter;
+import cz.crcs.ectester.common.test.CompoundTest;
+import cz.crcs.ectester.common.test.Result;
+import cz.crcs.ectester.common.test.Test;
+import cz.crcs.ectester.common.util.ByteUtil;
+import cz.crcs.ectester.common.util.CardUtil;
+import cz.crcs.ectester.common.util.ECUtil;
+import cz.crcs.ectester.data.EC_Store;
+import cz.crcs.ectester.reader.CardMngr;
+import cz.crcs.ectester.reader.ECTesterReader;
+import cz.crcs.ectester.reader.command.Command;
+import javacard.security.KeyPair;
+
+import java.math.BigInteger;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+import static cz.crcs.ectester.common.test.Result.ExpectedValue;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class CardWrongSuite extends CardTestSuite {
+
+ public CardWrongSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) {
+ super(writer, cfg, cardManager, "wrong", "The wrong curve suite tests whether the card rejects domain parameters which are not curves.");
+ }
+
+ @Override
+ protected void runTests() throws Exception {
+ /* Just do the default run on the wrong curves.
+ * These should generally fail, the curves aren't curves.
+ */
+ Map<String, EC_Curve> curves = EC_Store.getInstance().getObjects(EC_Curve.class, "wrong");
+ for (Map.Entry<String, EC_Curve> e : curves.entrySet()) {
+ EC_Curve curve = e.getValue();
+ List<Test> tests = new LinkedList<>();
+ Test key = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS));
+ if (!key.ok()) {
+ doTest(CompoundTest.all(ExpectedValue.FAILURE, "No support for " + curve.getBits() + "b " + CardUtil.getKeyTypeString(curve.getField()), key));
+ continue;
+ }
+ tests.add(key);
+ Test set = runTest(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.FAILURE));
+ Test generate = runTest(CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_BOTH), ExpectedValue.FAILURE));
+ Test setup = runTest(CompoundTest.any(ExpectedValue.SUCCESS, "Set wrong curve and generate keypairs.", set, generate));
+ tests.add(setup);
+
+ for (byte kaType : EC_Consts.KA_TYPES) {
+ Test allocate = runTest(CommandTest.expect(new Command.AllocateKeyAgreement(this.card, kaType), ExpectedValue.SUCCESS));
+ if (allocate.ok()) {
+ Test ka = runTest(CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, kaType), ExpectedValue.FAILURE));
+ Test kaTest = runTest(CompoundTest.all(ExpectedValue.SUCCESS, "Allocate and perform KA.", allocate, ka));
+ tests.add(kaTest);
+ }
+ }
+ doTest(CompoundTest.function((tsts) -> {
+ for (int i = 0; i < tsts.length; ++i) {
+ if (i != 1 && !tsts[i].ok()) {
+ return new Result(Result.Value.FAILURE, "Some tests did not have the expected result.");
+ }
+ }
+ return new Result(Result.Value.SUCCESS, "All tests had the expected result.");
+ }, "Wrong curve test of " + curve.getBits() + "b " + CardUtil.getKeyTypeString(curve.getField()), tests.toArray(new Test[0])));
+ }
+ /*
+ * Do some interesting tests with corrupting the custom curves.
+ * For prime field:
+ * - p = 0
+ * - p = 1
+ * - p is a square of a prime
+ * - p is a composite q * s with q, s primes
+ * - TODO: p divides discriminant
+ */
+ Random r = new Random();
+ for (short keyLength : EC_Consts.FP_SIZES) {
+ byte curve = EC_Consts.getCurve(keyLength, KeyPair.ALG_EC_FP);
+ Test key = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, keyLength, KeyPair.ALG_EC_FP), ExpectedValue.SUCCESS));
+ if (!key.ok()) {
+ doTest(CompoundTest.all(ExpectedValue.FAILURE, "No support for " + keyLength + "b ALG_EC_FP.", key));
+ continue;
+ }
+ Test set = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, curve, EC_Consts.PARAMETERS_DOMAIN_FP, null), ExpectedValue.SUCCESS);
+ Test setup = CompoundTest.all(ExpectedValue.SUCCESS, "KeyPair setup.", key, set);
+
+ Test prime0 = ecdhTest(new Command.Transform(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.KEY_BOTH, EC_Consts.PARAMETER_FP, EC_Consts.TRANSFORMATION_ZERO), "Set p = 0.", "ECDH with p = 0.");
+ Test prime1 = ecdhTest(new Command.Transform(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.KEY_BOTH, EC_Consts.PARAMETER_FP, EC_Consts.TRANSFORMATION_ONE), "Set p = 1.", "ECDH with p = 1.");
+
+ short keyHalf = (short) (keyLength / 2);
+ BigInteger prime = new BigInteger(keyHalf, 50, r);
+ BigInteger primePow = prime.pow(2);
+ byte[] primePowBytes = ECUtil.toByteArray(primePow, keyLength);
+ EC_Params primePowData = new EC_Params(EC_Consts.PARAMETER_FP, new byte[][]{primePowBytes});
+ Test primePower = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, primePowData.getParams(), primePowData.flatten()), "Set p = square of a prime.", "ECDH with p = q^2.");
+
+ BigInteger q = new BigInteger(keyHalf, r);
+ BigInteger s = new BigInteger(keyHalf, r);
+ BigInteger compositeValue = q.multiply(s);
+ byte[] compositeBytes = ECUtil.toByteArray(compositeValue, keyLength);
+ EC_Params compositeData = new EC_Params(EC_Consts.PARAMETER_FP, new byte[][]{compositeBytes});
+ Test composite = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, compositeData.getParams(), compositeData.flatten()), "Set p = product of two primes.", "ECDH with p = q * s.");
+
+ Test wrongPrime = CompoundTest.all(ExpectedValue.SUCCESS, "Tests with corrupted prime parameter.", prime0, prime1, primePower, composite);
+
+ Test resetSetup = CompoundTest.all(ExpectedValue.SUCCESS, "Reset keypair.", set.clone());
+
+ Test randomG = ecdhTest(new Command.Transform(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.KEY_BOTH, EC_Consts.PARAMETER_G, (short) (EC_Consts.TRANSFORMATION_FULLRANDOM | EC_Consts.TRANSFORMATION_04_MASK)), "Set G = random non-point/point-like.", "ECDH with non-point G.");
+ Test fullRandomG = ecdhTest(new Command.Transform(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.KEY_BOTH, EC_Consts.PARAMETER_G, EC_Consts.TRANSFORMATION_FULLRANDOM), "Set G = random data.", "ECDH with G = random data.");
+ Test zeroG = ecdhTest(new Command.Transform(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.KEY_BOTH, EC_Consts.PARAMETER_G, EC_Consts.TRANSFORMATION_INFINITY), "Set G = inifnity.", "ECDH with G = infinity.");
+ Test wrongG = CompoundTest.all(ExpectedValue.SUCCESS, "Tests with corrupted G parameter.", randomG, fullRandomG, zeroG);
+
+ byte[] originalR = new byte[keyLength];
+ EC_Consts.getCurveParameter(curve, EC_Consts.PARAMETER_R, originalR, (short) 0);
+ BigInteger originalBigR = new BigInteger(1, originalR);
+
+ Test zeroR = ecdhTest(new Command.Transform(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, EC_Consts.PARAMETER_R, EC_Consts.TRANSFORMATION_ZERO), "Set R = 0.", "ECDH with R = 0.");
+ Test oneR = ecdhTest(new Command.Transform(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, EC_Consts.PARAMETER_R, EC_Consts.TRANSFORMATION_ONE), "Set R = 1.", "ECDH with R = 1.");
+
+ BigInteger prevPrimeR;
+ do {
+ prevPrimeR = BigInteger.probablePrime(originalBigR.bitLength() - 1, r);
+ } while (prevPrimeR.compareTo(originalBigR) >= 0);
+ byte[] prevRBytes = ECUtil.toByteArray(prevPrimeR, keyLength);
+ EC_Params prevRData = new EC_Params(EC_Consts.PARAMETER_R, new byte[][]{prevRBytes});
+ Test prevprimeWrongR = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, prevRData.getParams(), prevRData.flatten()), "Set R = some prime (but [r]G != infinity) smaller than original R.", "ECDH with wrong R, prevprime.");
+
+ BigInteger nextPrimeR = originalBigR.nextProbablePrime();
+ byte[] nextRBytes = ECUtil.toByteArray(nextPrimeR, keyLength);
+ EC_Params nextRData = new EC_Params(EC_Consts.PARAMETER_R, new byte[][]{nextRBytes});
+ Test nextprimeWrongR = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, nextRData.getParams(), nextRData.flatten()), "Set R = some prime (but [r]G != infinity) larger than original R.", "ECDH with wrong R, nextprime.");
+
+ byte[] nonprimeRBytes = nextRBytes.clone();
+ nonprimeRBytes[0] ^= 1;
+ EC_Params nonprimeWrongRData = new EC_Params(EC_Consts.PARAMETER_R, new byte[][]{nonprimeRBytes});
+ Test nonprimeWrongR = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, nonprimeWrongRData.getParams(), nonprimeWrongRData.flatten()), "Set R = some composite (but [r]G != infinity).", "ECDH with wrong R, composite.");
+
+ Test wrongR = CompoundTest.all(ExpectedValue.SUCCESS, "Tests with corrupted R parameter.", zeroR, oneR, prevprimeWrongR, nextprimeWrongR, nonprimeWrongR);
+
+ byte[] kRaw = new byte[]{(byte) 0xff};
+ EC_Params kData = new EC_Params(EC_Consts.PARAMETER_K, new byte[][]{kRaw});
+ Test bigK = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, kData.getParams(), kData.flatten()), "", "");
+
+ byte[] kZero = new byte[]{(byte) 0};
+ EC_Params kZeroData = new EC_Params(EC_Consts.PARAMETER_K, new byte[][]{kZero});
+ Test zeroK = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, kZeroData.getParams(), kZeroData.flatten()), "", "");
+
+ Test wrongK = CompoundTest.all(ExpectedValue.SUCCESS, "Tests with corrupted K parameter.", bigK, zeroK);
+
+ doTest(CompoundTest.all(ExpectedValue.SUCCESS, "Tests of " + keyLength + "b " + CardUtil.getKeyTypeString(KeyPair.ALG_EC_FP), setup, wrongPrime, resetSetup, wrongG, resetSetup.clone(), wrongR, resetSetup.clone(), wrongK, resetSetup.clone()));
+ }
+
+ /*
+ * For binary field:
+ * - e1, e2 or e3 is larger than m.
+ * - e1 = e2 = e3 = 0
+ */
+ for (short keyLength : EC_Consts.F2M_SIZES) {
+ byte curve = EC_Consts.getCurve(keyLength, KeyPair.ALG_EC_F2M);
+ Test key = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, keyLength, KeyPair.ALG_EC_F2M), ExpectedValue.SUCCESS));
+ if (!key.ok()) {
+ doTest(CompoundTest.all(ExpectedValue.FAILURE, "No support for " + keyLength + "b ALG_EC_F2M.", key));
+ continue;
+ }
+ Test set = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, curve, EC_Consts.PARAMETERS_DOMAIN_F2M, null), ExpectedValue.SUCCESS);
+ Test setup = CompoundTest.all(ExpectedValue.SUCCESS, "KeyPair setup.", key, set);
+
+ Test coeff0 = ecdhTest(new Command.Transform(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.KEY_BOTH, EC_Consts.PARAMETER_F2M, EC_Consts.TRANSFORMATION_ZERO), "Set e1 = e2 = e3 = 0.", "ECDH with wrong field polynomial: x^" + keyLength);
+
+ short e1 = (short) (2 * keyLength);
+ short e2 = (short) (3 * keyLength);
+ short e3 = (short) (4 * keyLength);
+ byte[][] coeffBytes = new byte[][]{
+ ByteUtil.shortToBytes(keyLength),
+ ByteUtil.shortToBytes(e1),
+ ByteUtil.shortToBytes(e2),
+ ByteUtil.shortToBytes(e3)};
+ EC_Params coeffParams = new EC_Params(EC_Consts.PARAMETER_F2M, coeffBytes);
+ Test coeffLarger = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, coeffParams.getParams(), coeffParams.flatten()), "Set e1=" + e1 + ", e2=" + e2 + ", e3=" + e3, "ECDH with wrong field poly, powers larger than " + keyLength);
+
+ Test wrong = CompoundTest.all(ExpectedValue.SUCCESS, "Tests with corrupted field polynomial parameter.", coeff0, coeffLarger);
+ doTest(CompoundTest.all(ExpectedValue.SUCCESS, "Tests of " + keyLength + "b " + CardUtil.getKeyTypeString(KeyPair.ALG_EC_F2M), setup, wrong));
+ }
+
+ /*
+ * TODO: tests for both Fp and F2m:
+ * - generator not on curve,
+ * - generator not on proper subgroup of curve(as specified by order/cofactor),
+ * - wrong order,
+ * - wrong cofactor.
+ */
+ }
+
+ private Test ecdhTest(Command setupCmd, String prepareDesc, String fullDesc) {
+ Test setup = CommandTest.expect(setupCmd, ExpectedValue.FAILURE);
+ Test generate = CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_BOTH), ExpectedValue.FAILURE);
+ Test preparePhase = CompoundTest.any(ExpectedValue.SUCCESS, prepareDesc, setup, generate);
+ Test allocateECDH = CommandTest.expect(new Command.AllocateKeyAgreement(this.card, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH), ExpectedValue.SUCCESS);
+ Test ecdh = CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH), ExpectedValue.FAILURE);
+
+
+ return CompoundTest.function((tests) -> {
+ if (preparePhase.ok() | !allocateECDH.ok() | ecdh.ok()) {
+ return new Result(Result.Value.SUCCESS, "All tests had the expected result.");
+ } else {
+ return new Result(Result.Value.FAILURE, "Some tests did not have the expected result.");
+ }
+ }, (tests) -> {
+ preparePhase.run();
+ if (preparePhase.ok()) {
+ return;
+ }
+ allocateECDH.run();
+ if (!allocateECDH.ok()) {
+ return;
+ }
+ ecdh.run();
+ }, fullDesc, preparePhase, allocateECDH, ecdh);
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/test/CommandTest.java b/src/cz/crcs/ectester/reader/test/CommandTest.java
index a08d820..d57dc17 100644
--- a/src/cz/crcs/ectester/reader/test/CommandTest.java
+++ b/src/cz/crcs/ectester/reader/test/CommandTest.java
@@ -3,13 +3,14 @@ package cz.crcs.ectester.reader.test;
import cz.crcs.ectester.common.test.Result;
import cz.crcs.ectester.common.test.SimpleTest;
import cz.crcs.ectester.common.test.TestCallback;
-import cz.crcs.ectester.common.test.TestException;
import cz.crcs.ectester.reader.command.Command;
import cz.crcs.ectester.reader.response.Response;
/**
* A simple test that runs one Command to get and evaluate one Response
* to get a Result and compare it with the expected one.
+ *
+ * @author Jan Jancar johny@neuromancer.sk
*/
public class CommandTest extends SimpleTest<CommandTestable> {
private CommandTest(CommandTestable command, TestCallback<CommandTestable> callback) {
@@ -28,8 +29,7 @@ public class CommandTest extends SimpleTest<CommandTestable> {
return new CommandTest(command, new TestCallback<CommandTestable>() {
@Override
public Result apply(CommandTestable commandTestable) {
- Response resp = commandTestable.getResponse();
- Result.Value resultValue = Result.Value.fromExpected(expected, resp.successful(), resp.error());
+ Result.Value resultValue = Result.Value.fromExpected(expected, commandTestable.ok(), commandTestable.error());
return new Result(resultValue, resultValue.ok() ? ok : nok);
}
});
@@ -56,21 +56,11 @@ public class CommandTest extends SimpleTest<CommandTestable> {
}
@Override
- public void run() throws TestException {
- if (hasRun)
- return;
-
- testable.run();
- result = callback.apply(testable);
- hasRun = true;
- }
-
- @Override
public String getDescription() {
if (hasRun) {
return testable.getResponse().getDescription();
} else {
- return testable.getCommand().toString();
+ return testable.getCommand().getDescription();
}
}
}
diff --git a/src/cz/crcs/ectester/reader/test/CommandTestable.java b/src/cz/crcs/ectester/reader/test/CommandTestable.java
index 3bb55bf..f670534 100644
--- a/src/cz/crcs/ectester/reader/test/CommandTestable.java
+++ b/src/cz/crcs/ectester/reader/test/CommandTestable.java
@@ -27,7 +27,7 @@ public class CommandTestable extends BaseTestable {
}
@Override
- public void run() throws TestException {
+ public void run() {
try {
response = command.send();
} catch (CardException e) {
diff --git a/src/cz/crcs/ectester/reader/test/PerformanceTest.java b/src/cz/crcs/ectester/reader/test/PerformanceTest.java
index 4a27bad..9abaadc 100644
--- a/src/cz/crcs/ectester/reader/test/PerformanceTest.java
+++ b/src/cz/crcs/ectester/reader/test/PerformanceTest.java
@@ -5,6 +5,7 @@ import cz.crcs.ectester.common.test.SimpleTest;
import cz.crcs.ectester.common.test.TestCallback;
import cz.crcs.ectester.common.test.TestException;
import cz.crcs.ectester.reader.command.Command;
+import cz.crcs.ectester.reader.response.Response;
import java.util.Arrays;
@@ -13,12 +14,14 @@ import java.util.Arrays;
*/
public class PerformanceTest extends SimpleTest<CommandTestable> {
private long[] times;
+ private Response[] responses;
private long mean;
private long median;
private long mode;
private int count;
+ private String desc;
- private PerformanceTest(CommandTestable testable, int count) {
+ private PerformanceTest(CommandTestable testable, int count, String desc) {
super(testable, new TestCallback<CommandTestable>() {
@Override
public Result apply(CommandTestable testable) {
@@ -26,26 +29,31 @@ public class PerformanceTest extends SimpleTest<CommandTestable> {
}
});
this.count = count;
+ this.desc = desc;
}
public static PerformanceTest repeat(Command cmd, int count) {
- return new PerformanceTest(new CommandTestable(cmd), count);
+ return new PerformanceTest(new CommandTestable(cmd), count, null);
+ }
+
+ public static PerformanceTest repeat(String desc, Command cmd, int count) {
+ return new PerformanceTest(new CommandTestable(cmd), count, desc);
}
@Override
public String getDescription() {
- return String.format("Mean = %d ns, Median = %d ns, Mode = %d ns", mean, median, mode);
+ String rest = String.format("Mean = %d ns, Median = %d ns, Mode = %d ns", mean, median, mode);
+ return (desc == null ? rest : desc + " (" + rest + ")");
}
@Override
- public void run() throws TestException {
- if (hasRun)
- return;
-
+ protected void runSelf() {
times = new long[count];
+ responses = new Response[count];
for (int i = 0; i < count; ++i) {
testable.run();
- times[i] = testable.getResponse().getDuration();
+ responses[i] = testable.getResponse();
+ times[i] = responses[i].getDuration();
testable.reset();
}
@@ -73,7 +81,6 @@ public class PerformanceTest extends SimpleTest<CommandTestable> {
mode = current_value;
}
}
- hasRun = true;
result = callback.apply(testable);
}
@@ -85,6 +92,10 @@ public class PerformanceTest extends SimpleTest<CommandTestable> {
return testable.getCommand();
}
+ public Response[] getResponses() {
+ return responses;
+ }
+
public long[] getTimes() {
return times;
}