summaryrefslogtreecommitdiff
path: root/src/cz/crcs/ectester/reader
diff options
context:
space:
mode:
Diffstat (limited to 'src/cz/crcs/ectester/reader')
-rw-r--r--src/cz/crcs/ectester/reader/CardMngr.java179
-rw-r--r--src/cz/crcs/ectester/reader/ECTesterReader.java (renamed from src/cz/crcs/ectester/reader/ECTester.java)215
-rw-r--r--src/cz/crcs/ectester/reader/Util.java379
-rw-r--r--src/cz/crcs/ectester/reader/command/Command.java160
-rw-r--r--src/cz/crcs/ectester/reader/ec/EC_Category.java142
-rw-r--r--src/cz/crcs/ectester/reader/ec/EC_Curve.java52
-rw-r--r--src/cz/crcs/ectester/reader/ec/EC_Data.java200
-rw-r--r--src/cz/crcs/ectester/reader/ec/EC_KAResult.java63
-rw-r--r--src/cz/crcs/ectester/reader/ec/EC_Key.java83
-rw-r--r--src/cz/crcs/ectester/reader/ec/EC_Keypair.java41
-rw-r--r--src/cz/crcs/ectester/reader/ec/EC_Params.java151
-rw-r--r--src/cz/crcs/ectester/reader/output/OutputLogger.java60
-rw-r--r--src/cz/crcs/ectester/reader/output/ResponseWriter.java14
-rw-r--r--src/cz/crcs/ectester/reader/output/TeeOutputStream.java36
-rw-r--r--src/cz/crcs/ectester/reader/output/TestWriter.java15
-rw-r--r--src/cz/crcs/ectester/reader/output/TextTestWriter.java102
-rw-r--r--src/cz/crcs/ectester/reader/output/XMLTestWriter.java131
-rw-r--r--src/cz/crcs/ectester/reader/output/YAMLTestWriter.java115
-rw-r--r--src/cz/crcs/ectester/reader/response/Response.java78
-rw-r--r--src/cz/crcs/ectester/reader/test/CardCompositeCurvesSuite.java51
-rw-r--r--src/cz/crcs/ectester/reader/test/CardDefaultSuite.java94
-rw-r--r--src/cz/crcs/ectester/reader/test/CardInvalidCurvesSuite.java67
-rw-r--r--src/cz/crcs/ectester/reader/test/CardTestSuite.java24
-rw-r--r--src/cz/crcs/ectester/reader/test/CardTestVectorSuite.java83
-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/CommandTest.java76
-rw-r--r--src/cz/crcs/ectester/reader/test/CommandTestable.java44
-rw-r--r--src/cz/crcs/ectester/reader/test/CompositeCurvesSuite.java53
-rw-r--r--src/cz/crcs/ectester/reader/test/DefaultSuite.java69
-rw-r--r--src/cz/crcs/ectester/reader/test/InvalidCurvesSuite.java68
-rw-r--r--src/cz/crcs/ectester/reader/test/PerformanceTest.java103
-rw-r--r--src/cz/crcs/ectester/reader/test/Result.java94
-rw-r--r--src/cz/crcs/ectester/reader/test/Test.java217
-rw-r--r--src/cz/crcs/ectester/reader/test/TestRunner.java29
-rw-r--r--src/cz/crcs/ectester/reader/test/TestSuite.java135
-rw-r--r--src/cz/crcs/ectester/reader/test/TestVectorSuite.java83
-rw-r--r--src/cz/crcs/ectester/reader/test/WrongCurvesSuite.java34
38 files changed, 1190 insertions, 2470 deletions
diff --git a/src/cz/crcs/ectester/reader/CardMngr.java b/src/cz/crcs/ectester/reader/CardMngr.java
index e11bcb3..1e42c52 100644
--- a/src/cz/crcs/ectester/reader/CardMngr.java
+++ b/src/cz/crcs/ectester/reader/CardMngr.java
@@ -2,11 +2,12 @@ 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.ISO7816;
import javax.smartcardio.*;
-import java.util.List;
-import java.util.Scanner;
+import java.util.*;
/**
* @author Petr Svenda petr@svenda.com
@@ -79,7 +80,7 @@ public class CardMngr {
//reset the card
if (verbose)
- System.out.println(Util.bytesToHex(card.getATR().getBytes()));
+ System.out.println(ByteUtil.bytesToHex(card.getATR().getBytes()));
cardFound = true;
}
@@ -108,7 +109,7 @@ public class CardMngr {
try {
card = terminal.connect("*");
ATR atr = card.getATR();
- System.out.println(terminalIndex + " : " + terminal.getName() + " - " + Util.bytesToHex(atr.getBytes()));
+ System.out.println(terminalIndex + " : " + terminal.getName() + " - " + ByteUtil.bytesToHex(atr.getBytes()));
terminalIndex++;
} catch (CardException ex) {
ex.printStackTrace(System.out);
@@ -164,50 +165,138 @@ public class CardMngr {
}
}
- public byte[] getCPLCData() throws Exception {
- byte[] data;
+ // Functions for CPLC taken and modified from https://github.com/martinpaljak/GlobalPlatformPro
+ private static final byte CLA_GP = (byte) 0x80;
+ private static final byte ISO7816_INS_GET_DATA = (byte) 0xCA;
+ private static final byte[] FETCH_GP_CPLC_APDU = {CLA_GP, ISO7816_INS_GET_DATA, (byte) 0x9F, (byte) 0x7F, (byte) 0x00};
+ private static final byte[] FETCH_ISO_CPLC_APDU = {ISO7816.CLA_ISO7816, ISO7816_INS_GET_DATA, (byte) 0x9F, (byte) 0x7F, (byte) 0x00};
+ private static final byte[] FETCH_GP_CARDDATA_APDU = {CLA_GP, ISO7816_INS_GET_DATA, (byte) 0x00, (byte) 0x66, (byte) 0x00};
- // TODO: Modify to obtain CPLC data
- byte apdu[] = new byte[HEADER_LENGTH];
- apdu[OFFSET_CLA] = (byte) 0x00;
- apdu[OFFSET_INS] = (byte) 0x00;
- apdu[OFFSET_P1] = (byte) 0x00;
- apdu[OFFSET_P2] = (byte) 0x00;
- apdu[OFFSET_LC] = (byte) 0x00;
+ public byte[] fetchCPLC() throws CardException {
+ // Try CPLC via GP
+ ResponseAPDU resp = send(FETCH_GP_CPLC_APDU);
+ // If GP CLA fails, try with ISO
+ if (resp.getSW() == (ISO7816.SW_CLA_NOT_SUPPORTED & 0xffff)) {
+ resp = send(FETCH_ISO_CPLC_APDU);
+ }
+ if (resp.getSW() == (ISO7816.SW_NO_ERROR & 0xffff)) {
+ return resp.getData();
+ }
+ return null;
+ }
- ResponseAPDU resp = send(apdu);
- if (resp.getSW() != 0x9000) { // 0x9000 is "OK"
- System.err.println("Fail to obtain card's response data");
- data = null;
- } else {
- byte temp[] = resp.getBytes();
- data = new byte[temp.length - 2];
- System.arraycopy(temp, 0, data, 0, temp.length - 2);
- // Last two bytes are status word (also obtainable by resp.getSW())
- // Take a look at ISO7816_status_words.txt for common codes
+ public static final class CPLC {
+ public enum Field {
+ ICFabricator,
+ ICType,
+ OperatingSystemID,
+ OperatingSystemReleaseDate,
+ OperatingSystemReleaseLevel,
+ ICFabricationDate,
+ ICSerialNumber,
+ ICBatchIdentifier,
+ ICModuleFabricator,
+ ICModulePackagingDate,
+ ICCManufacturer,
+ ICEmbeddingDate,
+ ICPrePersonalizer,
+ ICPrePersonalizationEquipmentDate,
+ ICPrePersonalizationEquipmentID,
+ ICPersonalizer,
+ ICPersonalizationDate,
+ ICPersonalizationEquipmentID
}
- return data;
- }
+ private Map<Field, byte[]> values = new TreeMap<>();
- public void probeCardCommands() throws Exception {
- // TODO: modify to probe for instruction
- for (int i = 0; i <= 0; i++) {
- byte apdu[] = new byte[HEADER_LENGTH];
- apdu[OFFSET_CLA] = (byte) 0x00;
- apdu[OFFSET_INS] = (byte) 0x00;
- apdu[OFFSET_P1] = (byte) 0x00;
- apdu[OFFSET_P2] = (byte) 0x00;
- apdu[OFFSET_LC] = (byte) 0x00;
+ public CPLC(byte[] data) {
+ if (data == null) {
+ return;
+ }
+ if (data.length < 3 || data[2] != 0x2A) {
+ throw new IllegalArgumentException("CPLC must be 0x2A bytes long");
+ }
+ //offset = TLVUtils.skipTag(data, offset, (short)0x9F7F);
+ short offset = 3;
+ values.put(Field.ICFabricator, Arrays.copyOfRange(data, offset, offset + 2));
+ offset += 2;
+ values.put(Field.ICType, Arrays.copyOfRange(data, offset, offset + 2));
+ offset += 2;
+ values.put(Field.OperatingSystemID, Arrays.copyOfRange(data, offset, offset + 2));
+ offset += 2;
+ values.put(Field.OperatingSystemReleaseDate, Arrays.copyOfRange(data, offset, offset + 2));
+ offset += 2;
+ values.put(Field.OperatingSystemReleaseLevel, Arrays.copyOfRange(data, offset, offset + 2));
+ offset += 2;
+ values.put(Field.ICFabricationDate, Arrays.copyOfRange(data, offset, offset + 2));
+ offset += 2;
+ values.put(Field.ICSerialNumber, Arrays.copyOfRange(data, offset, offset + 4));
+ offset += 4;
+ values.put(Field.ICBatchIdentifier, Arrays.copyOfRange(data, offset, offset + 2));
+ offset += 2;
+ values.put(Field.ICModuleFabricator, Arrays.copyOfRange(data, offset, offset + 2));
+ offset += 2;
+ values.put(Field.ICModulePackagingDate, Arrays.copyOfRange(data, offset, offset + 2));
+ offset += 2;
+ values.put(Field.ICCManufacturer, Arrays.copyOfRange(data, offset, offset + 2));
+ offset += 2;
+ values.put(Field.ICEmbeddingDate, Arrays.copyOfRange(data, offset, offset + 2));
+ offset += 2;
+ values.put(Field.ICPrePersonalizer, Arrays.copyOfRange(data, offset, offset + 2));
+ offset += 2;
+ values.put(Field.ICPrePersonalizationEquipmentDate, Arrays.copyOfRange(data, offset, offset + 2));
+ offset += 2;
+ values.put(Field.ICPrePersonalizationEquipmentID, Arrays.copyOfRange(data, offset, offset + 4));
+ offset += 4;
+ values.put(Field.ICPersonalizer, Arrays.copyOfRange(data, offset, offset + 2));
+ offset += 2;
+ values.put(Field.ICPersonalizationDate, Arrays.copyOfRange(data, offset, offset + 2));
+ offset += 2;
+ values.put(Field.ICPersonalizationEquipmentID, Arrays.copyOfRange(data, offset, offset + 4));
+ offset += 4;
+ }
- ResponseAPDU resp = send(apdu);
+ public Map<Field, byte[]> values() {
+ return values;
+ }
+ }
- if (verbose)
- System.out.println("Response: " + Integer.toHexString(resp.getSW()));
+ public CPLC getCPLC() throws CardException {
+ byte[] data = fetchCPLC();
+ return new CPLC(data);
+ }
- if (resp.getSW() != 0x6D00) { // Note: 0x6D00 is SW_INS_NOT_SUPPORTED
- // something?
- }
+ public static String mapCPLCField(CPLC.Field field, byte[] value) {
+ switch (field) {
+ case ICFabricator:
+ String id = ByteUtil.bytesToHex(value, false);
+ String fabricatorName = "unknown";
+ if (id.equals("3060")) {
+ fabricatorName = "Renesas";
+ }
+ if (id.equals("4090")) {
+ fabricatorName = "Infineon";
+ }
+ if (id.equals("4180")) {
+ fabricatorName = "Atmel";
+ }
+ if (id.equals("4250")) {
+ fabricatorName = "Samsung";
+ }
+ if (id.equals("4790")) {
+ fabricatorName = "NXP";
+ }
+ return id + " (" + fabricatorName + ")";
+ default:
+ return ByteUtil.bytesToHex(value, false);
+ }
+ }
+
+ public ATR getATR() {
+ if (simulate) {
+ return new ATR(simulator.getATR());
+ } else {
+ return card.getATR();
}
}
@@ -226,7 +315,7 @@ public class CardMngr {
System.out.println(">>>>");
System.out.println(apdu);
- System.out.println(Util.bytesToHex(apdu.getBytes()));
+ System.out.println(ByteUtil.bytesToHex(apdu.getBytes()));
}
long elapsed = -System.nanoTime();
@@ -237,7 +326,7 @@ public class CardMngr {
if (verbose) {
System.out.println(responseAPDU);
- System.out.println(Util.bytesToHex(responseAPDU.getBytes()));
+ System.out.println(ByteUtil.bytesToHex(responseAPDU.getBytes()));
}
if (responseAPDU.getSW1() == (byte) 0x61) {
@@ -247,7 +336,7 @@ public class CardMngr {
responseAPDU = channel.transmit(apduToSend);
if (verbose)
- System.out.println(Util.bytesToHex(responseAPDU.getBytes()));
+ System.out.println(ByteUtil.bytesToHex(responseAPDU.getBytes()));
}
if (verbose) {
@@ -276,7 +365,7 @@ public class CardMngr {
if (verbose) {
System.out.println(">>>>");
System.out.println(apdu);
- System.out.println(Util.bytesToHex(apdu.getBytes()));
+ System.out.println(ByteUtil.bytesToHex(apdu.getBytes()));
}
ResponseAPDU response = simulator.transmitCommand(apdu);
@@ -284,7 +373,7 @@ public class CardMngr {
if (verbose) {
System.out.println(response);
- System.out.println(Util.bytesToHex(responseBytes));
+ System.out.println(ByteUtil.bytesToHex(responseBytes));
System.out.println("<<<<");
}
diff --git a/src/cz/crcs/ectester/reader/ECTester.java b/src/cz/crcs/ectester/reader/ECTesterReader.java
index 550e070..5e3a3fe 100644
--- a/src/cz/crcs/ectester/reader/ECTester.java
+++ b/src/cz/crcs/ectester/reader/ECTesterReader.java
@@ -23,12 +23,19 @@ package cz.crcs.ectester.reader;
import cz.crcs.ectester.applet.ECTesterApplet;
import cz.crcs.ectester.applet.EC_Consts;
+import cz.crcs.ectester.common.cli.CLITools;
+import cz.crcs.ectester.common.ec.EC_Params;
+import cz.crcs.ectester.common.output.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.data.EC_Store;
import cz.crcs.ectester.reader.command.Command;
-import cz.crcs.ectester.reader.ec.EC_Category;
-import cz.crcs.ectester.reader.ec.EC_Data;
-import cz.crcs.ectester.reader.ec.EC_Params;
-import cz.crcs.ectester.reader.output.*;
+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;
@@ -38,9 +45,13 @@ import javax.smartcardio.CardException;
import javax.xml.parsers.ParserConfigurationException;
import java.io.*;
import java.nio.file.Files;
-import java.util.*;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Scanner;
import static cz.crcs.ectester.applet.ECTesterApplet.KeyAgreement_ALG_EC_SVDP_DH;
+import static cz.crcs.ectester.applet.ECTesterApplet.Signature_ALG_ECDSA_SHA;
/**
* Reader part of ECTester, a tool for testing Elliptic curve support on javacards.
@@ -49,18 +60,15 @@ import static cz.crcs.ectester.applet.ECTesterApplet.KeyAgreement_ALG_EC_SVDP_DH
* @author Jan Jancar johny@neuromancer.sk
* @version v0.1.0
*/
-public class ECTester {
-
+public class ECTesterReader {
private CardMngr cardManager;
private OutputLogger logger;
- private TestWriter testWriter;
private ResponseWriter respWriter;
- private EC_Store dataStore;
private Config cfg;
private Options opts = new Options();
private static final String VERSION = "v0.1.0";
- private static final String DESCRIPTION = "ECTester " + VERSION + ", a javacard Elliptic Curve Cryptograhy support tester/utility.";
+ 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;
@@ -76,11 +84,10 @@ public class ECTester {
//if help, print and quit
if (cli.hasOption("help")) {
- help();
+ CLITools.help("ECTesterReader.jar", CLI_HEADER, opts, CLI_FOOTER, true);
return;
} else if (cli.hasOption("version")) {
- System.out.println(DESCRIPTION);
- System.out.println(LICENSE);
+ CLITools.version(DESCRIPTION, LICENSE);
return;
}
cfg = new Config();
@@ -90,10 +97,9 @@ public class ECTester {
return;
}
- dataStore = new EC_Store();
//if list, print and quit
if (cli.hasOption("list-named")) {
- list();
+ CLITools.listNamed(EC_Store.getInstance(), cli.getOptionValue("list-named"));
return;
}
@@ -116,22 +122,7 @@ public class ECTester {
// Setup logger, testWriter and respWriter
logger = new OutputLogger(true, cfg.log);
- if (cfg.format == null) {
- testWriter = new TextTestWriter(logger.getPrintStream());
- } else {
- switch (cfg.format) {
- case "text":
- testWriter = new TextTestWriter(logger.getPrintStream());
- break;
- case "xml":
- testWriter = new XMLTestWriter(logger.getOutputStream());
- break;
- case "yaml":
- case "yml":
- testWriter = new YAMLTestWriter(logger.getPrintStream());
- break;
- }
- }
+
respWriter = new ResponseWriter(logger.getPrintStream());
//do action
@@ -187,9 +178,10 @@ public class ECTester {
System.err.println("File " + fnfe.getMessage() + " not found.");
} catch (ParseException | IOException ex) {
System.err.println(ex.getMessage());
- } catch (CardException ex) {
+ } catch (CardException | TestException ex) {
if (logger != null)
logger.println(ex.getMessage());
+ ex.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} finally {
@@ -213,8 +205,7 @@ public class ECTester {
* -e / --export
* -g / --generate [amount]
* -t / --test [test_suite]
- * -dh / --ecdh [count]
- * -dhc / --ecdhc [count]
+ * -dh / --ecdh [count]]
* -dsa / --ecdsa [count]
* -ln / --list-named [obj]
*
@@ -248,6 +239,7 @@ public class ECTester {
* -s / --simulate
* -y / --yes
* -ka/ --ka-type <type>
+ * -sig/--sig-type <type>
*/
OptionGroup actions = new OptionGroup();
actions.setRequired(true);
@@ -256,9 +248,8 @@ public class ECTester {
actions.addOption(Option.builder("ln").longOpt("list-named").desc("Print the list of supported named curves and keys.").hasArg().argName("what").optionalArg(true).build());
actions.addOption(Option.builder("e").longOpt("export").desc("Export the defaut curve parameters of the card(if any).").build());
actions.addOption(Option.builder("g").longOpt("generate").desc("Generate [amount] of EC keys.").hasArg().argName("amount").optionalArg(true).build());
- actions.addOption(Option.builder("t").longOpt("test").desc("Test ECC support. [test_suite]:\n- default:\n- invalid:\n- wrong:\n- composite:\n- test-vectors:").hasArg().argName("test_suite").optionalArg(true).build());
- actions.addOption(Option.builder("dh").longOpt("ecdh").desc("Do ECDH, [count] times.").hasArg().argName("count").optionalArg(true).build());
- actions.addOption(Option.builder("dhc").longOpt("ecdhc").desc("Do ECDHC, [count] times.").hasArg().argName("count").optionalArg(true).build());
+ actions.addOption(Option.builder("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("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());
opts.addOptionGroup(actions);
@@ -303,45 +294,13 @@ public class ECTester {
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());
CommandLineParser parser = new DefaultParser();
return parser.parse(opts, args);
}
/**
- * Prints help.
- */
- private void help() {
- HelpFormatter help = new HelpFormatter();
- help.setOptionComparator(null);
- help.printHelp("ECTester.jar", CLI_HEADER, opts, CLI_FOOTER, true);
- }
-
- /**
- * List categories and named curves.
- */
- private void list() {
- Map<String, EC_Category> categories = dataStore.getCategories();
- if (cfg.listNamed == null) {
- // print all categories, briefly
- for (EC_Category cat : categories.values()) {
- System.out.println(cat);
- }
- } else if (categories.containsKey(cfg.listNamed)) {
- // print given category
- System.out.println(categories.get(cfg.listNamed));
- } else {
- // print given object
- EC_Data object = dataStore.getObject(EC_Data.class, cfg.listNamed);
- if (object != null) {
- System.out.println(object);
- } else {
- System.err.println("Named object " + cfg.listNamed + " not found!");
- }
- }
- }
-
- /**
* Exports default card/simulation EC domain parameters to output file.
*
* @throws CardException if APDU transmission fails
@@ -386,8 +345,9 @@ public class ECTester {
private void generate() throws CardException, IOException {
byte keyClass = cfg.primeField ? KeyPair.ALG_EC_FP : KeyPair.ALG_EC_F2M;
- new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_LOCAL, (short) cfg.bits, keyClass).send();
- Command curve = Command.prepareCurve(cardManager, dataStore, cfg, ECTesterApplet.KEYPAIR_LOCAL, (short) cfg.bits, keyClass);
+ Response allocate = new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_LOCAL, (short) cfg.bits, keyClass).send();
+ respWriter.outputResponse(allocate);
+ Command curve = Command.prepareCurve(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_LOCAL, (short) cfg.bits, keyClass);
FileWriter keysFile = new FileWriter(cfg.output);
keysFile.write("index;time;pubW;privS\n");
@@ -417,8 +377,8 @@ public class ECTester {
}
respWriter.outputResponse(response);
- String pub = Util.bytesToHex(export.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_W), false);
- String priv = Util.bytesToHex(export.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_S), false);
+ String pub = ByteUtil.bytesToHex(export.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_W), false);
+ String priv = ByteUtil.bytesToHex(export.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_S), false);
String line = String.format("%d;%d;%s;%s\n", generated, elapsed / 1000000, pub, priv);
keysFile.write(line);
keysFile.flush();
@@ -436,20 +396,38 @@ public class ECTester {
* @throws CardException if APDU transmission fails
* @throws IOException if an IO error occurs when writing to key file.
*/
- private void test() throws IOException, CardException {
- TestSuite suite;
+ 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;
+ }
+ }
+
+ CardTestSuite suite;
switch (cfg.testSuite) {
case "default":
- suite = new DefaultSuite(dataStore, cfg);
+ suite = new CardDefaultSuite(writer, cfg, cardManager);
break;
case "test-vectors":
- suite = new TestVectorSuite(dataStore, cfg);
+ suite = new CardTestVectorSuite(writer, cfg, cardManager);
break;
default:
- // These tests are dangerous, prompt before them.
+ // 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 tests have caused temporary DoS of some cards.");
+ System.out.println("Some of these run have caused temporary DoS of some cards.");
if (!cfg.yes) {
System.out.print("Do you want to proceed? (y/n): ");
Scanner in = new Scanner(System.in);
@@ -459,17 +437,18 @@ public class ECTester {
}
in.close();
}
-
-
switch (cfg.testSuite) {
case "wrong":
- suite = new WrongCurvesSuite(dataStore, cfg);
+ suite = new CardWrongCurvesSuite(writer, cfg, cardManager);
break;
case "composite":
- suite = new CompositeCurvesSuite(dataStore, cfg);
+ suite = new CardCompositeCurvesSuite(writer, cfg, cardManager);
break;
case "invalid":
- suite = new InvalidCurvesSuite(dataStore, cfg);
+ suite = new CardInvalidCurvesSuite(writer, cfg, cardManager);
+ break;
+ case "twist":
+ suite = new CardTwistTestSuite(writer, cfg, cardManager);
break;
default:
System.err.println("Unknown test suite.");
@@ -478,9 +457,7 @@ public class ECTester {
break;
}
- TestRunner runner = new TestRunner(suite, testWriter);
- suite.setup(cardManager);
- runner.run();
+ suite.run();
}
/**
@@ -492,9 +469,9 @@ public class ECTester {
private void ecdh() throws IOException, CardException {
byte keyClass = cfg.primeField ? KeyPair.ALG_EC_FP : KeyPair.ALG_EC_F2M;
List<Response> prepare = new LinkedList<>();
- prepare.add(new Command.AllocateKeyAgreement(cardManager, cfg.kaType).send()); // Prepare KeyAgreement or required type
+ 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, dataStore, cfg, ECTesterApplet.KEYPAIR_BOTH, (short) cfg.bits, keyClass);
+ Command curve = Command.prepareCurve(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_BOTH, (short) cfg.bits, keyClass);
if (curve != null)
prepare.add(curve.send());
@@ -508,21 +485,26 @@ public class ECTester {
List<Command> generate = new LinkedList<>();
generate.add(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_BOTH));
if (cfg.anyPublicKey || cfg.anyPrivateKey || cfg.anyKey) {
- generate.add(Command.prepareKey(cardManager, dataStore, cfg, ECTesterApplet.KEYPAIR_REMOTE));
+ generate.add(Command.prepareKey(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_REMOTE));
}
FileWriter out = null;
if (cfg.output != null) {
out = new FileWriter(cfg.output);
- out.write("index;time;secret\n");
+ out.write("index;time;pubW;privS;secret\n");
}
int retry = 0;
int done = 0;
- while (done < cfg.ECDHCount) {
+ while (done < cfg.ECKACount) {
List<Response> ecdh = Command.sendAll(generate);
- Response.ECDH perform = new Command.ECDH(cardManager, pubkey, privkey, ECTesterApplet.EXPORT_TRUE, EC_Consts.CORRUPTION_NONE, cfg.ECDHKA).send();
+ Response.Export export = new Command.Export(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.KEY_BOTH, EC_Consts.PARAMETERS_KEYPAIR).send();
+ ecdh.add(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);
@@ -539,7 +521,7 @@ public class ECTester {
}
if (out != null) {
- out.write(String.format("%d;%d;%s\n", done, perform.getDuration() / 1000000, Util.bytesToHex(perform.getSecret(), false)));
+ out.write(String.format("%d;%d;%s;%s;%s\n", done, perform.getDuration() / 1000000, ByteUtil.bytesToHex(pubkey_bytes, false), ByteUtil.bytesToHex(privkey_bytes, false), ByteUtil.bytesToHex(perform.getSecret(), false)));
}
++done;
@@ -571,15 +553,16 @@ public class ECTester {
Command generate;
if (cfg.anyKeypart) {
- generate = Command.prepareKey(cardManager, dataStore, cfg, ECTesterApplet.KEYPAIR_LOCAL);
+ generate = Command.prepareKey(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_LOCAL);
} else {
generate = new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL);
}
byte keyClass = cfg.primeField ? KeyPair.ALG_EC_FP : KeyPair.ALG_EC_F2M;
List<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, dataStore, cfg, ECTesterApplet.KEYPAIR_LOCAL, (short) cfg.bits, keyClass);
+ Command curve = Command.prepareCurve(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_LOCAL, (short) cfg.bits, keyClass);
if (curve != null)
prepare.add(curve.send());
@@ -599,7 +582,7 @@ public class ECTester {
List<Response> ecdsa = new LinkedList<>();
ecdsa.add(generate.send());
- Response.ECDSA perform = new Command.ECDSA(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_TRUE, data).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);
@@ -616,7 +599,7 @@ public class ECTester {
}
if (out != null) {
- out.write(String.format("%d;%d;%s\n", done, perform.getDuration() / 1000000, Util.bytesToHex(perform.getSignature(), false)));
+ out.write(String.format("%d;%d;%s\n", done, perform.getDuration() / 1000000, ByteUtil.bytesToHex(perform.getSignature(), false)));
}
++done;
@@ -629,18 +612,18 @@ public class ECTester {
}
public static void main(String[] args) {
- ECTester app = new ECTester();
+ ECTesterReader app = new ECTesterReader();
app.run(args);
}
public static class Config {
//Options
- public int bits;
+ public short bits;
public boolean all;
public boolean primeField = false;
public boolean binaryField = false;
- public byte kaType = KeyAgreement_ALG_EC_SVDP_DH;
+
public String namedCurve;
public String curveFile;
@@ -674,9 +657,10 @@ public class ECTester {
public String listNamed;
public String testSuite;
public int generateAmount;
- public int ECDHCount;
- public byte ECDHKA;
+ public int ECKACount;
+ public byte ECKAType = KeyAgreement_ALG_EC_SVDP_DH;
public int ECDSACount;
+ public byte ECDSAType = Signature_ALG_ECDSA_SHA;
/**
* Reads and validates options, also sets defaults.
@@ -685,11 +669,11 @@ public class ECTester {
* @return whether the options are valid.
*/
boolean readOptions(CommandLine cli) {
- bits = Integer.parseInt(cli.getOptionValue("bit-size", "0"));
+ bits = Short.parseShort(cli.getOptionValue("bit-size", "0"));
all = cli.hasOption("all");
primeField = cli.hasOption("fp");
binaryField = cli.hasOption("f2m");
- kaType = Byte.parseByte(cli.getOptionValue("ka-type", "1"));
+
namedCurve = cli.getOptionValue("named-curve");
customCurve = cli.hasOption("custom");
@@ -770,7 +754,6 @@ public class ECTester {
System.err.println("You have to specify curve bit-size with -b");
return false;
}
-
} else if (cli.hasOption("generate")) {
if (primeField == binaryField) {
System.err.print("Need to specify field with -fp or -f2m. (not both)");
@@ -801,13 +784,12 @@ public class ECTester {
}
testSuite = cli.getOptionValue("test", "default").toLowerCase();
- String[] tests = new String[]{"default", "composite", "invalid", "test-vectors", "wrong"};
+ String[] tests = new String[]{"default", "composite", "invalid", "test-vectors", "wrong", "twist"};
if (!Arrays.asList(tests).contains(testSuite)) {
System.err.println("Unknown test suite " + testSuite + ". Should be one of: " + Arrays.toString(tests));
return false;
}
-
- } else if (cli.hasOption("ecdh") || cli.hasOption("ecdhc")) {
+ } else if (cli.hasOption("ecdh")) {
if (primeField == binaryField) {
System.err.print("Need to specify field with -fp or -f2m. (not both)");
return false;
@@ -817,18 +799,13 @@ public class ECTester {
return false;
}
- if (cli.hasOption("ecdh")) {
- ECDHCount = Integer.parseInt(cli.getOptionValue("ecdh", "1"));
- ECDHKA = EC_Consts.KA_ECDH;
- } else if (cli.hasOption("ecdhc")) {
- ECDHCount = Integer.parseInt(cli.getOptionValue("ecdhc", "1"));
- ECDHKA = EC_Consts.KA_ECDHC;
- }
- if (ECDHCount <= 0) {
+ ECKACount = Integer.parseInt(cli.getOptionValue("ecdh", "1"));
+ if (ECKACount <= 0) {
System.err.println("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)");
@@ -849,6 +826,8 @@ public class ECTester {
System.err.println("ECDSA count cannot be <= 0.");
return false;
}
+
+ ECDSAType = CardUtil.parseSigType(cli.getOptionValue("sig-type", "17"));
}
return true;
}
diff --git a/src/cz/crcs/ectester/reader/Util.java b/src/cz/crcs/ectester/reader/Util.java
deleted file mode 100644
index 4e1154b..0000000
--- a/src/cz/crcs/ectester/reader/Util.java
+++ /dev/null
@@ -1,379 +0,0 @@
-package cz.crcs.ectester.reader;
-
-import cz.crcs.ectester.applet.ECTesterApplet;
-import static cz.crcs.ectester.applet.ECTesterApplet.KeyAgreement_ALG_EC_PACE_GM;
-import static cz.crcs.ectester.applet.ECTesterApplet.KeyAgreement_ALG_EC_SVDP_DH;
-import static cz.crcs.ectester.applet.ECTesterApplet.KeyAgreement_ALG_EC_SVDP_DHC;
-import static cz.crcs.ectester.applet.ECTesterApplet.KeyAgreement_ALG_EC_SVDP_DHC_PLAIN;
-import static cz.crcs.ectester.applet.ECTesterApplet.KeyAgreement_ALG_EC_SVDP_DH_PLAIN;
-import static cz.crcs.ectester.applet.ECTesterApplet.KeyAgreement_ALG_EC_SVDP_DH_PLAIN_XY;
-import cz.crcs.ectester.applet.EC_Consts;
-import javacard.framework.ISO7816;
-import javacard.security.CryptoException;
-
-/**
- * Utility class, some byte/hex manipulation, convenient byte[] methods.
- *
- * @author Petr Svenda petr@svenda.com
- * @author Jan Jancar johny@neuromancer.sk
- */
-public class Util {
-
- public static short getShort(byte[] array, int offset) {
- return (short) (((array[offset] & 0xFF) << 8) | (array[offset + 1] & 0xFF));
- }
-
- public static void setShort(byte[] array, int offset, short value) {
- array[offset + 1] = (byte) (value & 0xFF);
- array[offset] = (byte) ((value >> 8) & 0xFF);
- }
-
- public static int diffBytes(byte[] one, int oneOffset, byte[] other, int otherOffset, int length) {
- for (int i = 0; i < length; ++i) {
- byte a = one[i + oneOffset];
- byte b = other[i + otherOffset];
- if (a != b) {
- return i;
- }
- }
- return length;
- }
-
- public static boolean compareBytes(byte[] one, int oneOffset, byte[] other, int otherOffset, int length) {
- return diffBytes(one, oneOffset, other, otherOffset, length) == length;
- }
-
- public static boolean allValue(byte[] array, byte value) {
- for (byte a : array) {
- if (a != value)
- return false;
- }
- return true;
- }
-
- public static byte[] hexToBytes(String hex) {
- return hexToBytes(hex, true);
- }
-
- public static byte[] hexToBytes(String hex, boolean bigEndian) {
- hex = hex.replace(" ", "");
- int len = hex.length();
- StringBuilder sb = new StringBuilder();
-
- if (len % 2 == 1) {
- sb.append("0");
- ++len;
- }
-
- if (bigEndian) {
- sb.append(hex);
- } else {
- for (int i = 0; i < len / 2; ++i) {
- if (sb.length() >= 2) {
- sb.insert(sb.length() - 2, hex.substring(2 * i, 2 * i + 2));
- } else {
- sb.append(hex.substring(2 * i, 2 * i + 2));
- }
-
- }
- }
-
- String data = sb.toString();
- byte[] result = new byte[len / 2];
- for (int i = 0; i < len; i += 2) {
- result[i / 2] = (byte) ((Character.digit(data.charAt(i), 16) << 4)
- + (Character.digit(data.charAt(i + 1), 16)));
- }
- return result;
- }
-
- public static String byteToHex(byte data) {
- return String.format("%02x", data);
- }
-
- public static String bytesToHex(byte[] data) {
- return bytesToHex(data, true);
- }
-
- public static String bytesToHex(byte[] data, boolean addSpace) {
- return bytesToHex(data, 0, data.length, addSpace);
- }
-
- public static String bytesToHex(byte[] data, int offset, int len) {
- return bytesToHex(data, offset, len, true);
- }
-
- public static String bytesToHex(byte[] data, int offset, int len, boolean addSpace) {
- StringBuilder buf = new StringBuilder();
- for (int i = offset; i < (offset + len); i++) {
- buf.append(byteToHex(data[i]));
- if (addSpace && i != (offset + len - 1)) {
- buf.append(" ");
- }
- }
- return (buf.toString());
- }
-
- public static byte[] concatenate(byte[]... arrays) {
- int len = 0;
- for (byte[] array : arrays) {
- if (array == null)
- continue;
- len += array.length;
- }
- byte[] out = new byte[len];
- int offset = 0;
- for (byte[] array : arrays) {
- if (array == null || array.length == 0)
- continue;
- System.arraycopy(array, 0, out, offset, array.length);
- offset += array.length;
- }
- return out;
- }
-
- public static String getSWSource(short sw) {
- switch (sw) {
- case ISO7816.SW_NO_ERROR:
- case ISO7816.SW_APPLET_SELECT_FAILED:
- case ISO7816.SW_BYTES_REMAINING_00:
- case ISO7816.SW_CLA_NOT_SUPPORTED:
- case ISO7816.SW_COMMAND_NOT_ALLOWED:
- case ISO7816.SW_CONDITIONS_NOT_SATISFIED:
- case ISO7816.SW_CORRECT_LENGTH_00:
- case ISO7816.SW_DATA_INVALID:
- case ISO7816.SW_FILE_FULL:
- case ISO7816.SW_FILE_INVALID:
- case ISO7816.SW_FILE_NOT_FOUND:
- case ISO7816.SW_FUNC_NOT_SUPPORTED:
- case ISO7816.SW_INCORRECT_P1P2:
- case ISO7816.SW_INS_NOT_SUPPORTED:
- case ISO7816.SW_LOGICAL_CHANNEL_NOT_SUPPORTED:
- case ISO7816.SW_RECORD_NOT_FOUND:
- case ISO7816.SW_SECURE_MESSAGING_NOT_SUPPORTED:
- case ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED:
- case ISO7816.SW_UNKNOWN:
- case ISO7816.SW_WARNING_STATE_UNCHANGED:
- case ISO7816.SW_WRONG_DATA:
- case ISO7816.SW_WRONG_LENGTH:
- case ISO7816.SW_WRONG_P1P2:
- return "ISO";
- case CryptoException.ILLEGAL_VALUE:
- case CryptoException.UNINITIALIZED_KEY:
- case CryptoException.NO_SUCH_ALGORITHM:
- case CryptoException.INVALID_INIT:
- case CryptoException.ILLEGAL_USE:
- return "CryptoException";
- case ECTesterApplet.SW_SIG_VERIFY_FAIL:
- case ECTesterApplet.SW_DH_DHC_MISMATCH:
- case ECTesterApplet.SW_KEYPAIR_NULL:
- case ECTesterApplet.SW_KA_NULL:
- case ECTesterApplet.SW_SIGNATURE_NULL:
- case ECTesterApplet.SW_OBJECT_NULL:
- return "ECTesterApplet";
- default:
- return "?";
- }
- }
-
- public static String getSW(short sw) {
- String str;
- switch (sw) {
- case ISO7816.SW_APPLET_SELECT_FAILED:
- str = "APPLET_SELECT_FAILED";
- break;
- case ISO7816.SW_BYTES_REMAINING_00:
- str = "BYTES_REMAINING";
- break;
- case ISO7816.SW_CLA_NOT_SUPPORTED:
- str = "CLA_NOT_SUPPORTED";
- break;
- case ISO7816.SW_COMMAND_NOT_ALLOWED:
- str = "COMMAND_NOT_ALLOWED";
- break;
- case ISO7816.SW_CONDITIONS_NOT_SATISFIED:
- str = "CONDITIONS_NOT_SATISFIED";
- break;
- case ISO7816.SW_CORRECT_LENGTH_00:
- str = "CORRECT_LENGTH";
- break;
- case ISO7816.SW_DATA_INVALID:
- str = "DATA_INVALID";
- break;
- case ISO7816.SW_FILE_FULL:
- str = "FILE_FULL";
- break;
- case ISO7816.SW_FILE_INVALID:
- str = "FILE_INVALID";
- break;
- case ISO7816.SW_FILE_NOT_FOUND:
- str = "FILE_NOT_FOUND";
- break;
- case ISO7816.SW_FUNC_NOT_SUPPORTED:
- str = "FUNC_NOT_SUPPORTED";
- break;
- case ISO7816.SW_INCORRECT_P1P2:
- str = "INCORRECT_P1P2";
- break;
- case ISO7816.SW_INS_NOT_SUPPORTED:
- str = "INS_NOT_SUPPORTED";
- break;
- case ISO7816.SW_LOGICAL_CHANNEL_NOT_SUPPORTED:
- str = "LOGICAL_CHANNEL_NOT_SUPPORTED";
- break;
- case ISO7816.SW_RECORD_NOT_FOUND:
- str = "RECORD_NOT_FOUND";
- break;
- case ISO7816.SW_SECURE_MESSAGING_NOT_SUPPORTED:
- str = "SECURE_MESSAGING_NOT_SUPPORTED";
- break;
- case ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED:
- str = "SECURITY_STATUS_NOT_SATISFIED";
- break;
- case ISO7816.SW_UNKNOWN:
- str = "UNKNOWN";
- break;
- case ISO7816.SW_WARNING_STATE_UNCHANGED:
- str = "WARNING_STATE_UNCHANGED";
- break;
- case ISO7816.SW_WRONG_DATA:
- str = "WRONG_DATA";
- break;
- case ISO7816.SW_WRONG_LENGTH:
- str = "WRONG_LENGTH";
- break;
- case ISO7816.SW_WRONG_P1P2:
- str = "WRONG_P1P2";
- break;
- case CryptoException.ILLEGAL_VALUE:
- str = "ILLEGAL_VALUE";
- break;
- case CryptoException.UNINITIALIZED_KEY:
- str = "UNINITIALIZED_KEY";
- break;
- case CryptoException.NO_SUCH_ALGORITHM:
- str = "NO_SUCH_ALG";
- break;
- case CryptoException.INVALID_INIT:
- str = "INVALID_INIT";
- break;
- case CryptoException.ILLEGAL_USE:
- str = "ILLEGAL_USE";
- break;
- case ECTesterApplet.SW_SIG_VERIFY_FAIL:
- str = "SIG_VERIFY_FAIL";
- break;
- case ECTesterApplet.SW_DH_DHC_MISMATCH:
- str = "DH_DHC_MISMATCH";
- break;
- case ECTesterApplet.SW_KEYPAIR_NULL:
- str = "KEYPAIR_NULL";
- break;
- case ECTesterApplet.SW_KA_NULL:
- str = "KA_NULL";
- break;
- case ECTesterApplet.SW_SIGNATURE_NULL:
- str = "SIGNATURE_NULL";
- break;
- case ECTesterApplet.SW_OBJECT_NULL:
- str = "OBJECT_NULL";
- break;
- default:
- str = "unknown";
- break;
- }
- return str;
- }
-
- public static String getSWString(short sw) {
- if (sw == ISO7816.SW_NO_ERROR) {
- return "OK (0x9000)";
- } else {
- String str = getSW(sw);
- return String.format("fail (%s, 0x%04x)", str, sw);
- }
- }
-
- public static String getCorruption(short corruptionType) {
- String corrupt;
- switch (corruptionType) {
- case EC_Consts.CORRUPTION_NONE:
- corrupt = "NONE";
- break;
- case EC_Consts.CORRUPTION_FIXED:
- corrupt = "FIXED";
- break;
- case EC_Consts.CORRUPTION_ONE:
- corrupt = "ONE";
- break;
- case EC_Consts.CORRUPTION_ZERO:
- corrupt = "ZERO";
- break;
- case EC_Consts.CORRUPTION_ONEBYTERANDOM:
- corrupt = "ONE_BYTE_RANDOM";
- break;
- case EC_Consts.CORRUPTION_FULLRANDOM:
- corrupt = "FULL_RANDOM";
- break;
- case EC_Consts.CORRUPTION_INCREMENT:
- corrupt = "INCREMENT";
- break;
- case EC_Consts.CORRUPTION_INFINITY:
- corrupt = "INFINITY";
- break;
- case EC_Consts.CORRUPTION_COMPRESS:
- corrupt = "COMPRESSED";
- break;
- case EC_Consts.CORRUPTION_MAX:
- corrupt = "MAX";
- break;
- default:
- corrupt = "unknown";
- break;
- }
- return corrupt;
- }
-
- public static String getKA(byte ka) {
- String algo = "";
- if ((ka & EC_Consts.KA_ECDH) != 0 || ka == EC_Consts.KA_ANY) {
- algo += "ECDH";
- }
- if (ka == EC_Consts.KA_BOTH) {
- algo += "+";
- } else if (ka == EC_Consts.KA_ANY) {
- algo += "/";
- }
- if ((ka & EC_Consts.KA_ECDHC) != 0 || ka == EC_Consts.KA_ANY) {
- algo += "ECDHC";
- }
- return algo;
- }
-
- public static String getKATypeString(byte kaType) {
- String kaTypeString = "unknown";
- switch (kaType) {
- case KeyAgreement_ALG_EC_SVDP_DH:
- kaTypeString = "ALG_EC_SVDP_DH";
- break;
- case KeyAgreement_ALG_EC_SVDP_DH_PLAIN:
- kaTypeString = "ALG_EC_SVDP_DH_PLAIN";
- break;
- case KeyAgreement_ALG_EC_PACE_GM:
- kaTypeString = "ALG_EC_PACE_GM";
- break;
- case KeyAgreement_ALG_EC_SVDP_DH_PLAIN_XY:
- kaTypeString = "ALG_EC_SVDP_DH_PLAIN_XY";
- break;
- case KeyAgreement_ALG_EC_SVDP_DHC:
- kaTypeString = "ALG_EC_SVDP_DHC";
- break;
- case KeyAgreement_ALG_EC_SVDP_DHC_PLAIN:
- kaTypeString = "ALG_EC_SVDP_DHC_PLAIN";
- break;
- default:
- kaTypeString = "unknown";
- }
- return kaTypeString;
- }
-}
diff --git a/src/cz/crcs/ectester/reader/command/Command.java b/src/cz/crcs/ectester/reader/command/Command.java
index 3c11456..5a6906c 100644
--- a/src/cz/crcs/ectester/reader/command/Command.java
+++ b/src/cz/crcs/ectester/reader/command/Command.java
@@ -2,15 +2,15 @@ package cz.crcs.ectester.reader.command;
import cz.crcs.ectester.applet.ECTesterApplet;
import cz.crcs.ectester.applet.EC_Consts;
+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.ECTester;
+import cz.crcs.ectester.reader.ECTesterReader;
import cz.crcs.ectester.reader.response.Response;
-import cz.crcs.ectester.reader.Util;
-import cz.crcs.ectester.reader.ec.EC_Curve;
-import cz.crcs.ectester.reader.ec.EC_Key;
-import cz.crcs.ectester.reader.ec.EC_Keypair;
-import cz.crcs.ectester.reader.ec.EC_Params;
+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;
@@ -54,7 +54,7 @@ public abstract class Command {
* @return a Command to send in order to prepare the curve on the keypairs.
* @throws IOException if curve file cannot be found/opened
*/
- public static Command prepareCurve(CardMngr cardManager, EC_Store dataStore, ECTester.Config cfg, byte keyPair, short keyLength, byte keyClass) throws IOException {
+ public static Command prepareCurve(CardMngr cardManager, EC_Store dataStore, ECTesterReader.Config cfg, byte keyPair, short keyLength, byte keyClass) throws IOException {
if (cfg.customCurve) {
// Set custom curve (one of the SECG curves embedded applet-side)
@@ -109,7 +109,7 @@ public abstract class Command {
* @return a CommandAPDU setting params loaded on the keyPair/s
* @throws IOException if any of the key files cannot be found/opened
*/
- public static Command prepareKey(CardMngr cardManager, EC_Store dataStore, ECTester.Config cfg, byte keyPair) throws IOException {
+ public static Command prepareKey(CardMngr cardManager, EC_Store dataStore, ECTesterReader.Config cfg, byte keyPair) throws IOException {
short params = EC_Consts.PARAMETERS_NONE;
byte[] data = null;
@@ -174,7 +174,7 @@ public abstract class Command {
if (privkey == null) {
throw new IOException("Couldn't read the private key file correctly.");
}
- data = Util.concatenate(data, privkey);
+ data = ByteUtil.concatenate(data, privkey);
}
return new Command.Set(cardManager, keyPair, EC_Consts.CURVE_external, params, data);
}
@@ -203,7 +203,7 @@ public abstract class Command {
this.keyClass = keyClass;
byte[] data = new byte[]{0, 0, keyClass};
- Util.setShort(data, 0, keyLength);
+ ByteUtil.setShort(data, 0, keyLength);
this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_ALLOCATE, keyPair, 0x00, data);
}
@@ -214,13 +214,19 @@ public abstract class Command {
elapsed += System.nanoTime();
return new Response.Allocate(response, elapsed, keyPair, keyLength, keyClass);
}
+
+ @Override
+ public String toString() {
+ return "Allocate";
+ }
}
-
- public static class AllocateKeyAgreement extends Command {
+ /**
+ *
+ */
+ public static class AllocateKeyAgreement extends Command {
private byte kaType;
-
/**
* Creates the INS_ALLOCATE_KA instruction.
*
@@ -241,7 +247,45 @@ public abstract class Command {
elapsed += System.nanoTime();
return new Response.AllocateKeyAgreement(response, elapsed, kaType);
}
- }
+
+ @Override
+ public String toString() {
+ return "AllocateKeyAgreement";
+ }
+ }
+
+ /**
+ *
+ */
+ public static class AllocateSignature extends Command {
+ private byte sigType;
+
+ /**
+ * Creates the INS_ALLOCATE_SIG instruction.
+ *
+ * @param cardManager cardManager to send APDU through
+ * @param sigType which type of Signature to use
+ */
+ public AllocateSignature(CardMngr cardManager, byte sigType) {
+ super(cardManager);
+ 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.AllocateSignature send() throws CardException {
+ long elapsed = -System.nanoTime();
+ ResponseAPDU response = cardManager.send(cmd);
+ elapsed += System.nanoTime();
+ return new Response.AllocateSignature(response, elapsed, sigType);
+ }
+
+ @Override
+ public String toString() {
+ return "AllocateSignature";
+ }
+ }
/**
*
@@ -267,6 +311,11 @@ public abstract class Command {
elapsed += System.nanoTime();
return new Response.Clear(response, elapsed, keyPair);
}
+
+ @Override
+ public String toString() {
+ return "Clear";
+ }
}
/**
@@ -296,7 +345,7 @@ public abstract class Command {
int len = external != null ? 2 + external.length : 2;
byte[] data = new byte[len];
- Util.setShort(data, 0, params);
+ ByteUtil.setShort(data, 0, params);
if (external != null) {
System.arraycopy(external, 0, data, 2, external.length);
}
@@ -311,6 +360,11 @@ public abstract class Command {
elapsed += System.nanoTime();
return new Response.Set(response, elapsed, keyPair, curve, params);
}
+
+ @Override
+ public String toString() {
+ return "Set";
+ }
}
/**
@@ -337,7 +391,7 @@ public abstract class Command {
this.corruption = corruption;
byte[] data = new byte[3];
- Util.setShort(data, 0, params);
+ ByteUtil.setShort(data, 0, params);
data[2] = corruption;
this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_CORRUPT, keyPair, key, data);
@@ -350,6 +404,11 @@ public abstract class Command {
elapsed += System.nanoTime();
return new Response.Corrupt(response, elapsed, keyPair, key, params, corruption);
}
+
+ @Override
+ public String toString() {
+ return "Corrupt";
+ }
}
/**
@@ -378,6 +437,11 @@ public abstract class Command {
elapsed += System.nanoTime();
return new Response.Generate(response, elapsed, keyPair);
}
+
+ @Override
+ public String toString() {
+ return "Generate";
+ }
}
/**
@@ -403,7 +467,7 @@ public abstract class Command {
this.params = params;
byte[] data = new byte[2];
- Util.setShort(data, 0, params);
+ ByteUtil.setShort(data, 0, params);
this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_EXPORT, keyPair, key, data);
}
@@ -415,6 +479,11 @@ public abstract class Command {
elapsed += System.nanoTime();
return new Response.Export(response, elapsed, keyPair, key, params);
}
+
+ @Override
+ public String toString() {
+ return "Export";
+ }
}
/**
@@ -446,7 +515,7 @@ public abstract class Command {
this.type = type;
byte[] data = new byte[]{export, 0,0, type};
- Util.setShort(data, 1, corruption);
+ ByteUtil.setShort(data, 1, corruption);
this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_ECDH, pubkey, privkey, data);
}
@@ -458,6 +527,11 @@ public abstract class Command {
elapsed += System.nanoTime();
return new Response.ECDH(response, elapsed, pubkey, privkey, export, corruption, type);
}
+
+ @Override
+ public String toString() {
+ return "ECDH";
+ }
}
/**
@@ -477,7 +551,7 @@ public abstract class Command {
* @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 type ECDH algorithm type (EC_Consts.KA_* | ...)
+ * @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) {
@@ -489,7 +563,7 @@ public abstract class Command {
this.pubkey = pubkey;
byte[] data = new byte[3 + pubkey.length];
- Util.setShort(data, 0, corruption);
+ ByteUtil.setShort(data, 0, corruption);
data[2] = type;
System.arraycopy(pubkey, 0, data, 3, pubkey.length);
@@ -503,10 +577,16 @@ public abstract class Command {
elapsed += System.nanoTime();
return new Response.ECDH(response, elapsed, ECTesterApplet.KEYPAIR_REMOTE, privkey, export, corruption, type);
}
+
+ @Override
+ public String toString() {
+ return "ECDH_direct";
+ }
}
public static class ECDSA extends Command {
private byte keyPair;
+ private byte sigType;
private byte export;
private byte[] raw;
@@ -515,20 +595,23 @@ public abstract class Command {
*
* @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(CardMngr cardManager, byte keyPair, byte export, byte[] raw) {
+ public ECDSA(CardMngr cardManager, byte keyPair, byte sigType, byte export, byte[] raw) {
super(cardManager);
this.keyPair = keyPair;
+ this.sigType = sigType;
this.export = export;
this.raw = raw;
int len = raw != null ? raw.length : 0;
- byte[] data = new byte[2 + len];
- Util.setShort(data, 0, (short) len);
+ byte[] data = new byte[3 + len];
+ data[0] = sigType;
+ ByteUtil.setShort(data, 1, (short) len);
if (raw != null) {
- System.arraycopy(raw, 0, data, 2, len);
+ System.arraycopy(raw, 0, data, 3, len);
}
this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_ECDSA, keyPair, export, data);
@@ -539,7 +622,12 @@ public abstract class Command {
long elapsed = -System.nanoTime();
ResponseAPDU response = cardManager.send(cmd);
elapsed += System.nanoTime();
- return new Response.ECDSA(response, elapsed, keyPair, export, raw);
+ return new Response.ECDSA(response, elapsed, keyPair, sigType, export, raw);
+ }
+
+ @Override
+ public String toString() {
+ return "ECDSA";
}
}
@@ -564,28 +652,10 @@ public abstract class Command {
elapsed += System.nanoTime();
return new Response.Cleanup(response, elapsed);
}
- }
-
- /**
- *
- */
- public static class Support extends Command {
-
- /**
- * @param cardManager cardManager to send APDU through
- */
- public Support(CardMngr cardManager) {
- super(cardManager);
-
- this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_SUPPORT, 0, 0);
- }
@Override
- public Response.Support send() throws CardException {
- long elapsed = -System.nanoTime();
- ResponseAPDU response = cardManager.send(cmd);
- elapsed += System.nanoTime();
- return new Response.Support(response, elapsed);
+ public String toString() {
+ return "Cleanup";
}
}
}
diff --git a/src/cz/crcs/ectester/reader/ec/EC_Category.java b/src/cz/crcs/ectester/reader/ec/EC_Category.java
deleted file mode 100644
index 41cbad8..0000000
--- a/src/cz/crcs/ectester/reader/ec/EC_Category.java
+++ /dev/null
@@ -1,142 +0,0 @@
-package cz.crcs.ectester.reader.ec;
-
-import java.util.Collections;
-import java.util.Map;
-import java.util.Objects;
-import java.util.TreeMap;
-
-/**
- * A category of EC_Data objects, has a name, description and represents a directory in
- * the cz.crcs.ectester.data package.
- * @author Jan Jancar johny@neuromancer.sk
- */
-public class EC_Category {
-
- private String name;
- private String directory;
- private String desc;
-
- private Map<String, EC_Data> objects;
-
-
- public EC_Category(String name, String directory) {
- this.name = name;
- this.directory = directory;
- }
-
- public EC_Category(String name, String directory, String desc) {
- this(name, directory);
- this.desc = desc;
- }
-
- public EC_Category(String name, String directory, String desc, Map<String, EC_Data> objects) {
- this(name, directory, desc);
- this.objects = objects;
- }
-
- public String getName() {
- return name;
- }
-
- public String getDirectory() {
- return directory;
- }
-
- public String getDesc() {
- return desc;
- }
-
- public Map<String, EC_Data> getObjects() {
- return Collections.unmodifiableMap(objects);
- }
-
- public <T extends EC_Data> Map<String, T> getObjects(Class<T> cls) {
- Map<String, T> objs = new TreeMap<>();
- for (Map.Entry<String, EC_Data> entry : objects.entrySet()) {
- if (cls.isInstance(entry.getValue())) {
- objs.put(entry.getKey(), cls.cast(entry.getValue()));
- }
- }
- return Collections.unmodifiableMap(objs);
- }
-
- public <T extends EC_Data> T getObject(Class<T> cls, String id) {
- EC_Data obj = objects.get(id);
- if (cls.isInstance(obj)) {
- return cls.cast(obj);
- } else {
- return null;
- }
- }
-
- @Override
- public String toString() {
- StringBuilder out = new StringBuilder();
- out.append("\t- ").append(name).append((desc == null || desc.equals("")) ? "" : ": " + desc);
- out.append(System.lineSeparator());
-
- Map<String, EC_Curve> curves = getObjects(EC_Curve.class);
- int size = curves.size();
- if (size > 0) {
- out.append("\t\tCurves: ");
- for (Map.Entry<String, EC_Curve> curve : curves.entrySet()) {
- out.append(curve.getKey());
- size--;
- if (size > 0)
- out.append(", ");
- }
- out.append(System.lineSeparator());
- }
-
- Map<String, EC_Key> keys = getObjects(EC_Key.class);
- size = keys.size();
- if (size > 0) {
- out.append("\t\tKeys: ");
- for (Map.Entry<String, EC_Key> key : keys.entrySet()) {
- out.append(key.getKey());
- size--;
- if (size > 0)
- out.append(", ");
- }
- out.append(System.lineSeparator());
- }
-
- Map<String, EC_Keypair> keypairs = getObjects(EC_Keypair.class);
- size = keypairs.size();
- if (size > 0) {
- out.append("\t\tKeypairs: ");
- for (Map.Entry<String, EC_Keypair> key : keypairs.entrySet()) {
- out.append(key.getKey());
- size--;
- if (size > 0)
- out.append(", ");
- }
- out.append(System.lineSeparator());
- }
-
- Map<String, EC_KAResult> results = getObjects(EC_KAResult.class);
- size = results.size();
- if (size > 0) {
- out.append("\t\tResults: ");
- for (Map.Entry<String, EC_KAResult> result : results.entrySet()) {
- out.append(result.getKey());
- size--;
- if (size > 0)
- out.append(", ");
- }
- out.append(System.lineSeparator());
- }
- return out.toString();
- }
-
- @Override
- public boolean equals(Object obj) {
- return obj instanceof EC_Category && Objects.equals(this.name, ((EC_Category) obj).name);
- }
-
- @Override
- public int hashCode() {
- return this.name.hashCode() ^ this.directory.hashCode();
- }
-
-}
diff --git a/src/cz/crcs/ectester/reader/ec/EC_Curve.java b/src/cz/crcs/ectester/reader/ec/EC_Curve.java
deleted file mode 100644
index cb4a2df..0000000
--- a/src/cz/crcs/ectester/reader/ec/EC_Curve.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package cz.crcs.ectester.reader.ec;
-
-import cz.crcs.ectester.applet.EC_Consts;
-import javacard.security.KeyPair;
-
-/**
- * An Elliptic curve, contains parameters Fp/F2M, A, B, G, R, (K)?.
- *
- * @author Jan Jancar johny@neuromancer.sk
- */
-public class EC_Curve extends EC_Params {
- private short bits;
- private byte field;
- private String desc;
-
- /**
- * @param bits
- * @param field KeyPair.ALG_EC_FP or KeyPair.ALG_EC_F2M
- */
- public EC_Curve(short bits, byte field) {
- super(field == KeyPair.ALG_EC_FP ? EC_Consts.PARAMETERS_DOMAIN_FP : EC_Consts.PARAMETERS_DOMAIN_F2M);
- this.bits = bits;
- this.field = field;
- }
-
- public EC_Curve(String id, short bits, byte field) {
- this(bits, field);
- this.id = id;
- }
-
- public EC_Curve(String id, short bits, byte field, String desc) {
- this(id, bits, field);
- this.desc = desc;
- }
-
- public short getBits() {
- return bits;
- }
-
- public byte getField() {
- return field;
- }
-
- public String getDesc() {
- return desc;
- }
-
- @Override
- public String toString() {
- return "<" + getId() + "> " + (field == KeyPair.ALG_EC_FP ? "Prime" : "Binary") + " field Elliptic curve (" + String.valueOf(bits) + "b)" + (desc == null ? "" : ": " + desc);
- }
-}
diff --git a/src/cz/crcs/ectester/reader/ec/EC_Data.java b/src/cz/crcs/ectester/reader/ec/EC_Data.java
deleted file mode 100644
index 0ceddef..0000000
--- a/src/cz/crcs/ectester/reader/ec/EC_Data.java
+++ /dev/null
@@ -1,200 +0,0 @@
-package cz.crcs.ectester.reader.ec;
-
-import cz.crcs.ectester.reader.Util;
-
-import java.io.*;
-import java.util.*;
-import java.util.regex.Pattern;
-
-/**
- * A list of byte arrays for holding EC data.
- *
- * The data can be read from a byte array via <code>readBytes()</code>, from a CSV via <code>readCSV()</code>.
- * The data can be exported to a byte array via <code>flatten()</code> or to a string array via <code>expand()</code>.
- * @author Jan Jancar johny@neuromancer.sk
- */
-public abstract class EC_Data {
- String id;
- int count;
- byte[][] data;
-
- private static final Pattern HEX = Pattern.compile("(0x|0X)?[a-fA-F\\d]+");
-
- EC_Data() {
- }
-
- EC_Data(int count) {
- this.count = count;
- this.data = new byte[count][];
- }
-
- EC_Data(byte[][] data) {
- this.count = data.length;
- this.data = data;
- }
-
- EC_Data(String id, int count) {
- this(count);
- this.id = id;
- }
-
- EC_Data(String id, byte[][] data) {
- this(data);
- this.id = id;
- }
-
- public String getId() {
- return id;
- }
-
- public int getCount() {
- return count;
- }
-
- public byte[][] getData() {
- return data;
- }
-
- public boolean hasData() {
- return data != null;
- }
-
- public byte[] getParam(int index) {
- return data[index];
- }
-
- public byte[] flatten() {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- for (byte[] param : data) {
- byte[] length = new byte[2];
- Util.setShort(length, 0, (short) param.length);
-
- out.write(length, 0, 2);
- out.write(param, 0, param.length);
- }
-
- return out.toByteArray();
- }
-
- public String[] expand() {
- List<String> out = new ArrayList<>(count);
- for (byte[] param : data) {
- out.add(Util.bytesToHex(param, false));
- }
-
- return out.toArray(new String[out.size()]);
- }
-
- private static byte[] pad(byte[] data) {
- if (data.length == 1) {
- return new byte[]{(byte) 0, data[0]};
- } else if (data.length == 0 || data.length > 2) {
- return data;
- }
- return null;
- }
-
- private static byte[] parse(String param) {
- byte[] data;
- if (param.startsWith("0x") || param.startsWith("0X")) {
- data = Util.hexToBytes(param.substring(2));
- } else {
- data = Util.hexToBytes(param);
- }
- if (data == null)
- return new byte[0];
- if (data.length < 2)
- return pad(data);
- return data;
- }
-
- private boolean readHex(String[] hex) {
- if (hex.length != count) {
- return false;
- }
-
- for (int i = 0; i < count; ++i) {
- this.data[i] = parse(hex[i]);
- }
- return true;
- }
-
- public boolean readCSV(InputStream in) {
- Scanner s = new Scanner(in);
-
- s.useDelimiter(",|;");
- List<String> data = new LinkedList<>();
- while (s.hasNext()) {
- String field = s.next();
- data.add(field.replaceAll("\\s+", ""));
- }
-
- if (data.isEmpty()) {
- return false;
- }
- for (String param : data) {
- if (!HEX.matcher(param).matches()) {
- return false;
- }
- }
- return readHex(data.toArray(new String[data.size()]));
- }
-
- public boolean readBytes(byte[] bytes) {
- int offset = 0;
- for (int i = 0; i < count; i++) {
- if (bytes.length - offset < 2) {
- return false;
- }
- short paramLength = Util.getShort(bytes, offset);
- offset += 2;
- if (bytes.length < offset + paramLength) {
- return false;
- }
- data[i] = new byte[paramLength];
- System.arraycopy(bytes, offset, data[i], 0, paramLength);
- offset += paramLength;
- }
- return true;
- }
-
- public void writeCSV(OutputStream out) throws IOException {
- Writer w = new OutputStreamWriter(out);
- w.write(String.join(",", expand()));
- w.flush();
- }
-
- @Override
- public String toString() {
- return String.join(",", expand());
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof EC_Data) {
- EC_Data other = (EC_Data) obj;
- if (this.id != null || other.id != null) {
- return Objects.equals(this.id, other.id);
- }
-
- if (this.count != other.count)
- return false;
- for (int i = 0; i < this.count; ++i) {
- if (!Arrays.equals(this.data[i], other.data[i])) {
- return false;
- }
- }
- return true;
- } else {
- return false;
- }
- }
-
- @Override
- public int hashCode() {
- if (this.id != null) {
- return this.id.hashCode();
- }
- return Arrays.deepHashCode(this.data);
- }
-}
diff --git a/src/cz/crcs/ectester/reader/ec/EC_KAResult.java b/src/cz/crcs/ectester/reader/ec/EC_KAResult.java
deleted file mode 100644
index 4a67bbe..0000000
--- a/src/cz/crcs/ectester/reader/ec/EC_KAResult.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package cz.crcs.ectester.reader.ec;
-
-import cz.crcs.ectester.reader.Util;
-
-/**
- * A result of EC based Key agreement operation.
- *
- * @author Jan Jancar johny@neuromancer.sk
- */
-public class EC_KAResult extends EC_Data {
-
- private byte ka;
- private String curve;
- private String oneKey;
- private String otherKey;
-
- private String desc;
-
- public EC_KAResult(byte ka, String curve, String oneKey, String otherKey) {
- super(1);
- this.ka = ka;
- this.curve = curve;
- this.oneKey = oneKey;
- this.otherKey = otherKey;
- }
-
- public EC_KAResult(String id, byte ka, String curve, String oneKey, String otherKey) {
- this(ka, curve, oneKey, otherKey);
- this.id = id;
- }
-
- public EC_KAResult(String id, byte ka, String curve, String oneKey, String otherKey, String desc) {
- this(id, ka, curve, oneKey, otherKey);
- this.desc = desc;
- }
-
- public byte getKA() {
- return ka;
- }
-
- public String getCurve() {
- return curve;
- }
-
- public String getOneKey() {
- return oneKey;
- }
-
- public String getOtherKey() {
- return otherKey;
- }
-
- public String getDesc() {
- return desc;
- }
-
- @Override
- public String toString() {
- String algo = Util.getKA(ka);
- return "<" + getId() + "> " + algo + " result over " + curve + ", " + oneKey + " + " + otherKey + (desc == null ? "" : ": " + desc);
- }
-
-}
diff --git a/src/cz/crcs/ectester/reader/ec/EC_Key.java b/src/cz/crcs/ectester/reader/ec/EC_Key.java
deleted file mode 100644
index 5077d5b..0000000
--- a/src/cz/crcs/ectester/reader/ec/EC_Key.java
+++ /dev/null
@@ -1,83 +0,0 @@
-package cz.crcs.ectester.reader.ec;
-
-import cz.crcs.ectester.applet.EC_Consts;
-
-/**
- * An abstract-like EC key. Concrete implementations create a public and private keys.
- *
- * @author Jan Jancar johny@neuromancer.sk
- */
-public class EC_Key extends EC_Params {
-
- private String curve;
- private String desc;
-
- private EC_Key(short mask, String curve) {
- super(mask);
- this.curve = curve;
- }
-
- private EC_Key(short mask, String curve, String desc) {
- this(mask, curve);
- this.desc = desc;
- }
-
- private EC_Key(String id, short mask, String curve, String desc) {
- this(mask, curve, desc);
- this.id = id;
- }
-
- public String getCurve() {
- return curve;
- }
-
- public String getDesc() {
- return desc;
- }
-
- /**
- * An EC public key, contains the W parameter.
- */
- public static class Public extends EC_Key {
-
- public Public(String curve) {
- super(EC_Consts.PARAMETER_W, curve);
- }
-
- public Public(String curve, String desc) {
- super(EC_Consts.PARAMETER_W, curve, desc);
- }
-
- public Public(String id, String curve, String desc) {
- super(id, EC_Consts.PARAMETER_W, curve, desc);
- }
-
- @Override
- public String toString() {
- return "<" + getId() + "> EC Public key, over " + getCurve() + (getDesc() == null ? "" : ": " + getDesc());
- }
- }
-
- /**
- * An EC private key, contains the S parameter.
- */
- public static class Private extends EC_Key {
-
- public Private(String curve) {
- super(EC_Consts.PARAMETER_S, curve);
- }
-
- public Private(String curve, String desc) {
- super(EC_Consts.PARAMETER_S, curve, desc);
- }
-
- public Private(String id, String curve, String desc) {
- super(id, EC_Consts.PARAMETER_S, curve, desc);
- }
-
- @Override
- public String toString() {
- return "<" + getId() + "> EC Private key, over " + getCurve() + (getDesc() == null ? "" : ": " + getDesc());
- }
- }
-}
diff --git a/src/cz/crcs/ectester/reader/ec/EC_Keypair.java b/src/cz/crcs/ectester/reader/ec/EC_Keypair.java
deleted file mode 100644
index 2643346..0000000
--- a/src/cz/crcs/ectester/reader/ec/EC_Keypair.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package cz.crcs.ectester.reader.ec;
-
-import cz.crcs.ectester.applet.EC_Consts;
-
-/**
- * An EC keypair, contains both the W and S parameters.
- *
- * @author Jan Jancar johny@neuromancer.sk
- */
-public class EC_Keypair extends EC_Params {
- private String curve;
- private String desc;
-
- public EC_Keypair(String curve) {
- super(EC_Consts.PARAMETERS_KEYPAIR);
- this.curve = curve;
- }
-
- public EC_Keypair(String curve, String desc) {
- this(curve);
- this.desc = desc;
- }
-
- public EC_Keypair(String id, String curve, String desc) {
- this(curve, desc);
- this.id = id;
- }
-
- public String getCurve() {
- return curve;
- }
-
- public String getDesc() {
- return desc;
- }
-
- @Override
- public String toString() {
- return "<" + getId() + "> EC Keypair, over " + curve + (desc == null ? "" : ": " + desc);
- }
-}
diff --git a/src/cz/crcs/ectester/reader/ec/EC_Params.java b/src/cz/crcs/ectester/reader/ec/EC_Params.java
deleted file mode 100644
index 6fb164b..0000000
--- a/src/cz/crcs/ectester/reader/ec/EC_Params.java
+++ /dev/null
@@ -1,151 +0,0 @@
-package cz.crcs.ectester.reader.ec;
-
-import cz.crcs.ectester.applet.EC_Consts;
-import cz.crcs.ectester.reader.Util;
-
-import java.io.ByteArrayOutputStream;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A list of EC parameters, can contain a subset of the Fp/F2M, A, B, G, R, K, W, S parameters.
- *
- * The set of parameters is uniquely identified by a short bit string.
- * The parameters can be exported to a byte array via <code>flatten()</code> or to a comma delimited
- * string via <code>expand()</code>.
- * @author Jan Jancar johny@neuromancer.sk
- */
-public class EC_Params extends EC_Data {
- private short params;
-
- public EC_Params(short params) {
- this.params = params;
- this.count = numParams();
- this.data = new byte[this.count][];
- }
-
- public EC_Params(short params, byte[][] data) {
- this.params = params;
- this.count = data.length;
- this.data = data;
- }
-
- public EC_Params(String id, short params) {
- this(params);
- this.id = id;
- }
-
- public EC_Params(String id, short params, byte[][] data) {
- this(params, data);
- this.id = id;
- }
-
- public short getParams() {
- return params;
- }
-
- public boolean hasParam(short param) {
- return (params & param) != 0;
- }
-
- public int numParams() {
- short paramMask = EC_Consts.PARAMETER_FP;
- int num = 0;
- while (paramMask <= EC_Consts.PARAMETER_S) {
- if ((paramMask & params) != 0) {
- if (paramMask == EC_Consts.PARAMETER_F2M) {
- num += 3;
- }
- if (paramMask == EC_Consts.PARAMETER_W || paramMask == EC_Consts.PARAMETER_G) {
- num += 1;
- }
- ++num;
- }
- paramMask = (short) (paramMask << 1);
- }
- return num;
- }
-
- @Override
- public byte[] flatten() {
- return flatten(params);
- }
-
- public byte[] flatten(short params) {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- short paramMask = EC_Consts.PARAMETER_FP;
- int i = 0;
- while (paramMask <= EC_Consts.PARAMETER_S) {
- short masked = (short) (this.params & params & paramMask);
- short shallow = (short) (this.params & paramMask);
- if (masked != 0) {
- byte[] param = data[i];
- if (masked == EC_Consts.PARAMETER_F2M) {
- //add m, e_1, e_2, e_3
- param = Util.concatenate(param, data[i + 1]);
- if (!Util.allValue(data[i + 2], (byte) 0)) {
- param = Util.concatenate(param, data[i + 2]);
- }
- if (!Util.allValue(data[i + 3], (byte) 0)) {
- param = Util.concatenate(param, data[i + 3]);
- }
- if (!(param.length == 4 || param.length == 8))
- throw new RuntimeException("PARAMETER_F2M length is not 8.(should be)");
- }
- if (masked == EC_Consts.PARAMETER_G || masked == EC_Consts.PARAMETER_W) {
- //read another param (the y coord) and put into X962 format.
- byte[] y = data[i + 1];
- param = Util.concatenate(new byte[]{4}, param, y); //<- ugly but works!
- }
- if (param.length == 0)
- throw new RuntimeException("Empty parameter read?");
-
- //write length
- byte[] length = new byte[2];
- Util.setShort(length, 0, (short) param.length);
- out.write(length, 0, 2);
- //write data
- out.write(param, 0, param.length);
- }
- if (shallow == EC_Consts.PARAMETER_F2M) {
- i += 4;
- } else if (shallow == EC_Consts.PARAMETER_G || shallow == EC_Consts.PARAMETER_W) {
- i += 2;
- } else if (shallow != 0) {
- i++;
- }
- paramMask = (short) (paramMask << 1);
- }
-
- return (out.size() == 0) ? null : out.toByteArray();
- }
-
- @Override
- public String[] expand() {
- List<String> out = new ArrayList<>();
-
- short paramMask = EC_Consts.PARAMETER_FP;
- int index = 0;
- while (paramMask <= EC_Consts.PARAMETER_S) {
- short masked = (short) (params & paramMask);
- if (masked != 0) {
- byte[] param = data[index];
- if (masked == EC_Consts.PARAMETER_F2M) {
- for (int i = 0; i < 4; ++i) {
- out.add(Util.bytesToHex(data[index + i], false));
- }
- index += 4;
- } else if (masked == EC_Consts.PARAMETER_G || masked == EC_Consts.PARAMETER_W) {
- out.add(Util.bytesToHex(param, false));
- out.add(Util.bytesToHex(data[index + 1], false));
- index += 2;
- } else {
- out.add(Util.bytesToHex(param, false));
- index++;
- }
- }
- paramMask = (short) (paramMask << 1);
- }
- return out.toArray(new String[out.size()]);
- }
-}
diff --git a/src/cz/crcs/ectester/reader/output/OutputLogger.java b/src/cz/crcs/ectester/reader/output/OutputLogger.java
deleted file mode 100644
index bf47a1f..0000000
--- a/src/cz/crcs/ectester/reader/output/OutputLogger.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package cz.crcs.ectester.reader.output;
-
-import java.io.*;
-import java.util.LinkedList;
-import java.util.List;
-
-/**
- * @author Petr Svenda petr@svenda.com
- * @author Jan Jancar johny@neuromancer.sk
- */
-public class OutputLogger {
- private OutputStream out;
- private PrintStream print;
-
- public OutputLogger(boolean systemOut, String... filePaths) throws IOException {
- List<OutputStream> streams = new LinkedList<>();
- for (String filePath : filePaths) {
- if (filePath != null) {
- streams.add(new FileOutputStream(filePath));
- }
- }
- if (systemOut) {
- streams.add(System.out);
- }
- this.out = new TeeOutputStream(streams.toArray(new OutputStream[0]));
- this.print = new PrintStream(this.out);
- }
-
- public OutputLogger(String filePath) throws IOException {
- this(true, filePath);
- }
-
- public OutputStream getOutputStream() {
- return this.out;
- }
-
- public PrintStream getPrintStream() {
- return this.print;
- }
-
- public void println() {
- print.println();
- }
-
- public void println(String logLine) {
- print.println(logLine);
- }
-
- public void print(String logLine) {
- print.print(logLine);
- }
-
- public void flush() {
- print.flush();
- }
-
- public void close() {
- print.close();
- }
-}
diff --git a/src/cz/crcs/ectester/reader/output/ResponseWriter.java b/src/cz/crcs/ectester/reader/output/ResponseWriter.java
index c357233..85bf79a 100644
--- a/src/cz/crcs/ectester/reader/output/ResponseWriter.java
+++ b/src/cz/crcs/ectester/reader/output/ResponseWriter.java
@@ -1,6 +1,6 @@
package cz.crcs.ectester.reader.output;
-import cz.crcs.ectester.reader.Util;
+import cz.crcs.ectester.common.util.CardUtil;
import cz.crcs.ectester.reader.response.Response;
import java.io.PrintStream;
@@ -20,20 +20,24 @@ public class ResponseWriter {
for (int j = 0; j < r.getNumSW(); ++j) {
short sw = r.getSW(j);
if (sw != 0) {
- suffix.append(" ").append(Util.getSWString(sw));
+ suffix.append(" ").append(CardUtil.getSWString(sw));
}
}
if (suffix.length() == 0) {
- suffix.append(" [").append(Util.getSW(r.getNaturalSW())).append("]");
+ suffix.append(" [").append(CardUtil.getSW(r.getNaturalSW())).append(String.format(" 0x%04x", r.getNaturalSW())).append("]");
}
return String.format("%4d ms ┃ %s", r.getDuration() / 1000000, suffix);
}
- public void outputResponse(Response r) {
+ public String responseString(Response r) {
String out = "";
out += String.format("%-70s", r.getDescription()) + " ┃ ";
out += responseSuffix(r);
- output.println(out);
+ return out;
+ }
+
+ public void outputResponse(Response r) {
+ output.println(responseString(r));
output.flush();
}
}
diff --git a/src/cz/crcs/ectester/reader/output/TeeOutputStream.java b/src/cz/crcs/ectester/reader/output/TeeOutputStream.java
deleted file mode 100644
index 2a1af99..0000000
--- a/src/cz/crcs/ectester/reader/output/TeeOutputStream.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package cz.crcs.ectester.reader.output;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * @author Jan Jancar johny@neuromancer.sk
- */
-public class TeeOutputStream extends OutputStream {
- private OutputStream[] outputs;
-
- public TeeOutputStream(OutputStream... outputs) {
- this.outputs = outputs;
- }
-
- @Override
- public void write(int b) throws IOException {
- for (OutputStream out : outputs) {
- out.write(b);
- }
- }
-
- @Override
- public void flush() throws IOException {
- for (OutputStream out : outputs) {
- out.flush();
- }
- }
-
- @Override
- public void close() throws IOException {
- for (OutputStream out : outputs) {
- out.close();
- }
- }
-}
diff --git a/src/cz/crcs/ectester/reader/output/TestWriter.java b/src/cz/crcs/ectester/reader/output/TestWriter.java
deleted file mode 100644
index 74c76fb..0000000
--- a/src/cz/crcs/ectester/reader/output/TestWriter.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package cz.crcs.ectester.reader.output;
-
-import cz.crcs.ectester.reader.test.Test;
-import cz.crcs.ectester.reader.test.TestSuite;
-
-/**
- * @author Jan Jancar johny@neuromancer.sk
- */
-public interface TestWriter {
- void begin(TestSuite suite);
-
- void outputTest(Test t);
-
- void end();
-}
diff --git a/src/cz/crcs/ectester/reader/output/TextTestWriter.java b/src/cz/crcs/ectester/reader/output/TextTestWriter.java
index bcebcd5..eb52937 100644
--- a/src/cz/crcs/ectester/reader/output/TextTestWriter.java
+++ b/src/cz/crcs/ectester/reader/output/TextTestWriter.java
@@ -1,85 +1,57 @@
package cz.crcs.ectester.reader.output;
-import cz.crcs.ectester.reader.test.Test;
-import cz.crcs.ectester.reader.test.TestSuite;
+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.test.CardTestSuite;
+import cz.crcs.ectester.reader.test.CommandTestable;
+import javax.smartcardio.CardException;
import java.io.PrintStream;
+import java.util.Map;
/**
* @author Jan Jancar johny@neuromancer.sk
*/
-public class TextTestWriter implements TestWriter {
- private PrintStream output;
- private ResponseWriter respWriter;
-
- public static int BASE_WIDTH = 76;
+public class TextTestWriter extends BaseTextTestWriter {
+ private ResponseWriter writer;
public TextTestWriter(PrintStream output) {
- this.output = output;
- this.respWriter = new ResponseWriter(output);
+ super(output);
+ this.writer = new ResponseWriter(output);
}
@Override
- public void begin(TestSuite suite) {
- output.println("=== Running test suite: " + suite.getName() + " ===");
- output.println("=== " + suite.getDescription());
- }
-
- private String testString(Test t, int offset) {
- if (!t.hasRun()) {
- return null;
+ protected String testableString(Testable t) {
+ if (t instanceof CommandTestable) {
+ CommandTestable cmd = (CommandTestable) t;
+ return writer.responseSuffix(cmd.getResponse());
}
+ return "";
+ }
- StringBuilder out = new StringBuilder();
- if (t instanceof Test.Simple) {
- Test.Simple test = (Test.Simple) t;
- out.append(test.ok() ? "OK " : "NOK ");
- out.append("━ ");
- int width = BASE_WIDTH - (offset + out.length());
- String widthSpec = "%-" + String.valueOf(width) + "s";
- out.append(String.format(widthSpec, t.getDescription()));
- out.append(" ┃ ");
- out.append(String.format("%-9s", test.getResultValue().name()));
- out.append(" ┃ ");
- out.append(respWriter.responseSuffix(test.getResponse()));
- } else {
- Test.Compound test = (Test.Compound) t;
- out.append(test.ok() ? "OK " : "NOK ");
- out.append("┳ ");
- int width = BASE_WIDTH - (offset + out.length());
- String widthSpec = "%-" + String.valueOf(width) + "s";
- out.append(String.format(widthSpec, t.getDescription()));
- out.append(" ┃ ");
- out.append(String.format("%-9s", test.getResultValue().name()));
- out.append(" ┃ ");
- out.append(test.getResultCause());
- out.append(System.lineSeparator());
- Test[] tests = test.getTests();
- for (int i = 0; i < tests.length; ++i) {
- if (i == tests.length - 1) {
- out.append(" ┗ ");
- } else {
- out.append(" ┣ ");
- }
- out.append(testString(tests[i], offset + 6));
- if (i != tests.length - 1) {
- out.append(System.lineSeparator());
+ @Override
+ protected String deviceString(TestSuite suite) {
+ 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());
+ try {
+ CardMngr.CPLC cplc = cardSuite.getCard().getCPLC();
+ if (!cplc.values().isEmpty()) {
+ sb.append("═══ 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();
+ sb.append("═══ ").append(field.name()).append(": ").append(CardMngr.mapCPLCField(field, value));
+ }
}
+ } catch (CardException ignored) {
}
+ return sb.toString();
}
-
- return out.toString();
- }
-
- @Override
- public void outputTest(Test t) {
- if (!t.hasRun())
- return;
- output.println(testString(t, 0));
- output.flush();
- }
-
- @Override
- public void end() {
+ return "";
}
}
diff --git a/src/cz/crcs/ectester/reader/output/XMLTestWriter.java b/src/cz/crcs/ectester/reader/output/XMLTestWriter.java
index beb758c..d3674e8 100644
--- a/src/cz/crcs/ectester/reader/output/XMLTestWriter.java
+++ b/src/cz/crcs/ectester/reader/output/XMLTestWriter.java
@@ -1,55 +1,34 @@
package cz.crcs.ectester.reader.output;
-import cz.crcs.ectester.reader.Util;
+import cz.crcs.ectester.common.output.BaseXMLTestWriter;
+import cz.crcs.ectester.common.test.TestSuite;
+import cz.crcs.ectester.common.test.Testable;
+import cz.crcs.ectester.common.util.ByteUtil;
+import cz.crcs.ectester.reader.CardMngr;
import cz.crcs.ectester.reader.command.Command;
import cz.crcs.ectester.reader.response.Response;
-import cz.crcs.ectester.reader.test.Test;
-import cz.crcs.ectester.reader.test.TestSuite;
-import org.w3c.dom.Document;
+import cz.crcs.ectester.reader.test.CardTestSuite;
+import cz.crcs.ectester.reader.test.CommandTestable;
import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
+import javax.smartcardio.CardException;
import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
import java.io.OutputStream;
+import java.util.Map;
/**
* @author Jan Jancar johny@neuromancer.sk
*/
-public class XMLTestWriter implements TestWriter {
- private OutputStream output;
- private DocumentBuilder db;
- private Document doc;
- private Node root;
-
+public class XMLTestWriter extends BaseXMLTestWriter {
public XMLTestWriter(OutputStream output) throws ParserConfigurationException {
- this.output = output;
- this.db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
- }
-
- @Override
- public void begin(TestSuite suite) {
- doc = db.newDocument();
- Element rootElem = doc.createElement("testSuite");
- rootElem.setAttribute("name", suite.getName());
- rootElem.setAttribute("desc", suite.getDescription());
-
- root = rootElem;
- doc.appendChild(root);
+ super(output);
}
private Element commandElement(Command c) {
Element commandElem = doc.createElement("command");
Element apdu = doc.createElement("apdu");
- apdu.setTextContent(Util.bytesToHex(c.getAPDU().getBytes()));
+ apdu.setTextContent(ByteUtil.bytesToHex(c.getAPDU().getBytes()));
commandElem.appendChild(apdu);
return commandElem;
@@ -60,7 +39,7 @@ public class XMLTestWriter implements TestWriter {
responseElem.setAttribute("successful", r.successful() ? "true" : "false");
Element apdu = doc.createElement("apdu");
- apdu.setTextContent(Util.bytesToHex(r.getAPDU().getBytes()));
+ apdu.setTextContent(ByteUtil.bytesToHex(r.getAPDU().getBytes()));
responseElem.appendChild(apdu);
Element naturalSW = doc.createElement("natural-sw");
@@ -86,60 +65,50 @@ public class XMLTestWriter implements TestWriter {
return responseElem;
}
- private Element testElement(Test t) {
- Element testElem = doc.createElement("test");
+ @Override
+ protected Element testableElement(Testable t) {
+ if (t instanceof CommandTestable) {
+ CommandTestable cmd = (CommandTestable) t;
+ Element result = doc.createElement("test");
+ result.setAttribute("type", "command");
+ result.appendChild(commandElement(cmd.getCommand()));
+ result.appendChild(responseElement(cmd.getResponse()));
+ return result;
+ }
+ return null;
+ }
- if (t instanceof Test.Simple) {
- Test.Simple test = (Test.Simple) t;
- testElem.setAttribute("type", "simple");
- testElem.appendChild(commandElement(test.getCommand()));
- testElem.appendChild(responseElement(test.getResponse()));
- } else if (t instanceof Test.Compound) {
- Test.Compound test = (Test.Compound) t;
- testElem.setAttribute("type", "compound");
- for (Test innerTest : test.getTests()) {
- testElem.appendChild(testElement(innerTest));
+ private Element cplcElement(CardMngr card) {
+ Element result = doc.createElement("cplc");
+ try {
+ CardMngr.CPLC cplc = card.getCPLC();
+ if (!cplc.values().isEmpty()) {
+ for (Map.Entry<CardMngr.CPLC.Field, byte[]> entry : cplc.values().entrySet()) {
+ CardMngr.CPLC.Field field = entry.getKey();
+ byte[] value = entry.getValue();
+ Element keyVal = doc.createElement(field.name());
+ keyVal.setTextContent(ByteUtil.bytesToHex(value, false));
+ result.appendChild(keyVal);
+ }
}
+ } catch (CardException ignored) {
}
-
- Element description = doc.createElement("desc");
- description.setTextContent(t.getDescription());
- testElem.appendChild(description);
-
- Element result = doc.createElement("result");
- Element ok = doc.createElement("ok");
- ok.setTextContent(String.valueOf(t.ok()));
- Element value = doc.createElement("value");
- value.setTextContent(t.getResultValue().name());
- Element cause = doc.createElement("cause");
- cause.setTextContent(t.getResultCause());
- result.appendChild(ok);
- result.appendChild(value);
- result.appendChild(cause);
- testElem.appendChild(result);
-
- return testElem;
+ return result;
}
@Override
- public void outputTest(Test t) {
- if (!t.hasRun())
- return;
- root.appendChild(testElement(t));
- }
+ protected Element deviceElement(TestSuite suite) {
+ if (suite instanceof CardTestSuite) {
+ CardTestSuite cardSuite = (CardTestSuite) suite;
+ Element result = doc.createElement("device");
+ result.setAttribute("type", "card");
+ result.appendChild(cplcElement(cardSuite.getCard()));
- @Override
- public void end() {
- try {
- DOMSource domSource = new DOMSource(doc);
- StreamResult result = new StreamResult(output);
- TransformerFactory tf = TransformerFactory.newInstance();
- Transformer transformer = tf.newTransformer();
- transformer.setOutputProperty(OutputKeys.INDENT, "yes");
- transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
- transformer.transform(domSource, result);
- } catch (TransformerException e) {
- e.printStackTrace();
+ Element atr = doc.createElement("ATR");
+ atr.setTextContent(ByteUtil.bytesToHex(cardSuite.getCard().getATR().getBytes(), false));
+ result.appendChild(atr);
+ return result;
}
+ return null;
}
} \ No newline at end of file
diff --git a/src/cz/crcs/ectester/reader/output/YAMLTestWriter.java b/src/cz/crcs/ectester/reader/output/YAMLTestWriter.java
index 3b2b72d..199f2c0 100644
--- a/src/cz/crcs/ectester/reader/output/YAMLTestWriter.java
+++ b/src/cz/crcs/ectester/reader/output/YAMLTestWriter.java
@@ -1,13 +1,16 @@
package cz.crcs.ectester.reader.output;
-import cz.crcs.ectester.reader.Util;
+import cz.crcs.ectester.common.output.BaseYAMLTestWriter;
+import cz.crcs.ectester.common.test.TestSuite;
+import cz.crcs.ectester.common.test.Testable;
+import cz.crcs.ectester.common.util.ByteUtil;
+import cz.crcs.ectester.reader.CardMngr;
import cz.crcs.ectester.reader.command.Command;
import cz.crcs.ectester.reader.response.Response;
-import cz.crcs.ectester.reader.test.Test;
-import cz.crcs.ectester.reader.test.TestSuite;
-import org.yaml.snakeyaml.DumperOptions;
-import org.yaml.snakeyaml.Yaml;
+import cz.crcs.ectester.reader.test.CardTestSuite;
+import cz.crcs.ectester.reader.test.CommandTestable;
+import javax.smartcardio.CardException;
import java.io.PrintStream;
import java.util.HashMap;
import java.util.LinkedList;
@@ -17,39 +20,21 @@ import java.util.Map;
/**
* @author Jan Jancar johny@neuromancer.sk
*/
-public class YAMLTestWriter implements TestWriter {
- private PrintStream output;
- private Map<String, Object> testRun;
- private Map<String, String> testSuite;
- private List<Object> tests;
-
+public class YAMLTestWriter extends BaseYAMLTestWriter {
public YAMLTestWriter(PrintStream output) {
- this.output = output;
- }
-
- @Override
- public void begin(TestSuite suite) {
- output.println("---");
- testRun = new HashMap<>();
- testSuite = new HashMap<>();
- tests = new LinkedList<>();
- testSuite.put("name", suite.getName());
- testSuite.put("desc", suite.getDescription());
-
- testRun.put("suite", testSuite);
- testRun.put("tests", tests);
+ super(output);
}
private Map<String, Object> commandObject(Command c) {
Map<String, Object> commandObj = new HashMap<>();
- commandObj.put("apdu", Util.bytesToHex(c.getAPDU().getBytes()));
+ commandObj.put("apdu", ByteUtil.bytesToHex(c.getAPDU().getBytes()));
return commandObj;
}
private Map<String, Object> responseObject(Response r) {
Map<String, Object> responseObj = new HashMap<>();
responseObj.put("successful", r.successful());
- responseObj.put("apdu", Util.bytesToHex(r.getAPDU().getBytes()));
+ responseObj.put("apdu", ByteUtil.bytesToHex(r.getAPDU().getBytes()));
responseObj.put("natural_sw", Short.toUnsignedInt(r.getNaturalSW()));
List<Integer> sws = new LinkedList<>();
for (int i = 0; i < r.getNumSW(); ++i) {
@@ -61,53 +46,45 @@ public class YAMLTestWriter implements TestWriter {
return responseObj;
}
- private Map<String, Object> testObject(Test t) {
- Map<String, Object> testObj = new HashMap<>();
-
- if (t instanceof Test.Simple) {
- Test.Simple test = (Test.Simple) t;
- testObj.put("type", "simple");
- testObj.put("command", commandObject(test.getCommand()));
- testObj.put("response", responseObject(test.getResponse()));
- } else if (t instanceof Test.Compound) {
- Test.Compound test = (Test.Compound) t;
- testObj.put("type", "compound");
- List<Map<String, Object>> tests = new LinkedList<>();
- for (Test innerTest : test.getTests()) {
- tests.add(testObject(innerTest));
- }
- testObj.put("tests", tests);
+ @Override
+ protected Map<String, Object> testableObject(Testable t) {
+ if (t instanceof CommandTestable) {
+ CommandTestable cmd = (CommandTestable) t;
+ Map<String, Object> result = new HashMap<>();
+ result.put("type", "command");
+ result.put("command", commandObject(cmd.getCommand()));
+ result.put("response", responseObject(cmd.getResponse()));
+ return result;
}
-
- testObj.put("desc", t.getDescription());
- Map<String, Object> result = new HashMap<>();
- result.put("ok", t.ok());
- result.put("value", t.getResultValue().name());
- result.put("cause", t.getResultCause());
- testObj.put("result", result);
-
- return testObj;
+ return null;
}
- @Override
- public void outputTest(Test t) {
- if (!t.hasRun())
- return;
- tests.add(testObject(t));
+ private Map<String, Object> cplcObject(CardMngr card) {
+ Map<String, Object> result = new HashMap<>();
+ try {
+ CardMngr.CPLC cplc = card.getCPLC();
+ if (!cplc.values().isEmpty()) {
+ for (Map.Entry<CardMngr.CPLC.Field, byte[]> entry : cplc.values().entrySet()) {
+ CardMngr.CPLC.Field field = entry.getKey();
+ byte[] value = entry.getValue();
+ result.put(field.name(), ByteUtil.bytesToHex(value, false));
+ }
+ }
+ } catch (CardException ignored) {
+ }
+ return result;
}
@Override
- public void end() {
- DumperOptions options = new DumperOptions();
- options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
- options.setPrettyFlow(true);
- Yaml yaml = new Yaml(options);
-
- Map<String, Object> result = new HashMap<>();
- result.put("testRun", testRun);
- String out = yaml.dump(result);
-
- output.println(out);
- output.println("---");
+ protected Map<String, Object> deviceObject(TestSuite suite) {
+ if (suite instanceof CardTestSuite) {
+ CardTestSuite cardSuite = (CardTestSuite) suite;
+ Map<String, Object> result = new HashMap<>();
+ result.put("type", "card");
+ result.put("cplc", cplcObject(cardSuite.getCard()));
+ result.put("ATR", ByteUtil.bytesToHex(cardSuite.getCard().getATR().getBytes(), false));
+ return result;
+ }
+ return null;
}
}
diff --git a/src/cz/crcs/ectester/reader/response/Response.java b/src/cz/crcs/ectester/reader/response/Response.java
index 4abfd14..cbed3b2 100644
--- a/src/cz/crcs/ectester/reader/response/Response.java
+++ b/src/cz/crcs/ectester/reader/response/Response.java
@@ -2,8 +2,8 @@ package cz.crcs.ectester.reader.response;
import cz.crcs.ectester.applet.ECTesterApplet;
import cz.crcs.ectester.applet.EC_Consts;
-import cz.crcs.ectester.reader.Util;
-import cz.crcs.ectester.reader.ec.EC_Data;
+import cz.crcs.ectester.common.util.ByteUtil;
+import cz.crcs.ectester.common.util.CardUtil;
import javacard.framework.ISO7816;
import javacard.security.KeyPair;
@@ -13,7 +13,6 @@ import javax.smartcardio.ResponseAPDU;
* @author Jan Jancar johny@neuromancer.sk
*/
public abstract class Response {
-
private ResponseAPDU resp;
private long time;
private short[] sws;
@@ -37,7 +36,7 @@ public abstract class Response {
//parse SWs in response
for (int i = 0; i < numSW; ++i) {
if (getLength() >= (offset + 2)) {
- short sw = Util.getShort(data, offset);
+ short sw = ByteUtil.getShort(data, offset);
offset += 2;
sws[i] = sw;
if (sw != ISO7816.SW_NO_ERROR) {
@@ -63,7 +62,7 @@ public abstract class Response {
error = true;
break;
}
- short paramLength = Util.getShort(data, offset);
+ short paramLength = ByteUtil.getShort(data, offset);
offset += 2;
if (data.length < offset + paramLength) {
error = true;
@@ -88,6 +87,10 @@ public abstract class Response {
return (short) resp.getSW();
}
+ public short[] getSWs() {
+ return sws;
+ }
+
public short getSW(int index) {
return sws[index];
}
@@ -130,24 +133,44 @@ public abstract class Response {
*
*/
public static class AllocateKeyAgreement extends Response {
- byte kaType;
+ private byte kaType;
public AllocateKeyAgreement(ResponseAPDU response, long time, byte kaType) {
super(response, time);
this.kaType = kaType;
- parse(2, 0);
+ parse(1, 0);
}
@Override
public String getDescription() {
- return String.format("Allocated KeyAgreement(%s) object", Util.getKATypeString(this.kaType));
+ return String.format("Allocated KeyAgreement(%s) object", CardUtil.getKATypeString(this.kaType));
}
+ }
+
+ /**
+ *
+ */
+ public static class AllocateSignature extends Response {
+ private byte sigType;
+
+ public AllocateSignature(ResponseAPDU response, long time, byte sigType) {
+ super(response, time);
+ this.sigType = sigType;
+ parse(1, 0);
+ }
+
+ @Override
+ public String getDescription() {
+ return String.format("Allocated Signature(%s) object", CardUtil.getSigTypeString(this.sigType));
+ }
}
+ /**
+ *
+ */
public static class Allocate extends Response {
-
private byte keyPair;
private short keyLength;
private byte keyClass;
@@ -181,7 +204,6 @@ public abstract class Response {
*
*/
public static class Clear extends Response {
-
private byte keyPair;
public Clear(ResponseAPDU response, long time, byte keyPair) {
@@ -210,7 +232,6 @@ public abstract class Response {
*
*/
public static class Set extends Response {
-
private byte keyPair;
private byte curve;
private short parameters;
@@ -268,7 +289,6 @@ public abstract class Response {
*
*/
public static class Corrupt extends Response {
-
private byte keyPair;
private byte key;
private short params;
@@ -290,7 +310,7 @@ public abstract class Response {
@Override
public String getDescription() {
- String corrupt = Util.getCorruption(corruption);
+ String corrupt = CardUtil.getCorruption(corruption);
String pair;
if (keyPair == ECTesterApplet.KEYPAIR_BOTH) {
@@ -306,7 +326,6 @@ public abstract class Response {
*
*/
public static class Generate extends Response {
-
private byte keyPair;
public Generate(ResponseAPDU response, long time, byte keyPair) {
@@ -336,7 +355,6 @@ public abstract class Response {
*
*/
public static class Export extends Response {
-
private byte keyPair;
private byte key;
private short parameters;
@@ -445,7 +463,6 @@ public abstract class Response {
*
*/
public static class ECDH extends Response {
-
private byte pubkey;
private byte privkey;
private byte export;
@@ -477,7 +494,7 @@ public abstract class Response {
@Override
public String getDescription() {
- String algo = Util.getKA(type);
+ String algo = CardUtil.getKATypeString(type);
String pub = pubkey == ECTesterApplet.KEYPAIR_LOCAL ? "local" : "remote";
String priv = privkey == ECTesterApplet.KEYPAIR_LOCAL ? "local" : "remote";
@@ -486,7 +503,7 @@ public abstract class Response {
if (corruption == EC_Consts.CORRUPTION_NONE) {
validity = "unchanged";
} else {
- validity = Util.getCorruption(corruption);
+ validity = CardUtil.getCorruption(corruption);
}
return String.format("%s of %s pubkey and %s privkey(%s point)", algo, pub, priv, validity);
}
@@ -496,14 +513,15 @@ public abstract class Response {
*
*/
public static class ECDSA extends Response {
-
private byte keyPair;
+ private byte sigType;
private byte export;
private byte[] raw;
- public ECDSA(ResponseAPDU response, long time, byte keyPair, byte export, byte[] raw) {
+ public ECDSA(ResponseAPDU response, long time, byte keyPair, byte sigType, byte export, byte[] raw) {
super(response, time);
this.keyPair = keyPair;
+ this.sigType = sigType;
this.export = export;
this.raw = raw;
@@ -520,9 +538,10 @@ public abstract class Response {
@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("ECDSA with %s keypair(%s data)", key, data);
+ return String.format("%s with %s keypair(%s data)", algo, key, data);
}
}
@@ -543,21 +562,4 @@ public abstract class Response {
}
}
-
- /**
- *
- */
- public static class Support extends Response {
-
- public Support(ResponseAPDU response, long time) {
- super(response, time);
-
- parse(3, 0);
- }
-
- @Override
- public String getDescription() {
- return "Support of ECDH, ECDHC, ECDSA allocation";
- }
- }
}
diff --git a/src/cz/crcs/ectester/reader/test/CardCompositeCurvesSuite.java b/src/cz/crcs/ectester/reader/test/CardCompositeCurvesSuite.java
new file mode 100644
index 0000000..a53806c
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/CardCompositeCurvesSuite.java
@@ -0,0 +1,51 @@
+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/CardDefaultSuite.java b/src/cz/crcs/ectester/reader/test/CardDefaultSuite.java
new file mode 100644
index 0000000..c3bd9c8
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/CardDefaultSuite.java
@@ -0,0 +1,94 @@
+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.Test;
+import cz.crcs.ectester.common.util.CardUtil;
+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.LinkedList;
+import java.util.List;
+
+import static cz.crcs.ectester.common.test.Result.ExpectedValue;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+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.");
+ }
+
+ @Override
+ protected void runTests() throws Exception {
+ if (cfg.primeField) {
+ runDefault(KeyPair.ALG_EC_FP);
+ }
+ if (cfg.binaryField) {
+ runDefault(KeyPair.ALG_EC_F2M);
+ }
+ }
+
+ private void runDefault(byte field) throws Exception {
+ short[] keySizes = field == KeyPair.ALG_EC_FP ? EC_Consts.FP_SIZES : EC_Consts.F2M_SIZES;
+ 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));
+ continue;
+ }
+ supportTests.add(key);
+
+ 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 genCustom = runTest(CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_BOTH), ExpectedValue.SUCCESS));
+ supportTests.add(genDefault);
+ supportTests.add(setCustom);
+ supportTests.add(genCustom);
+
+ 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);
+ 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;
+ if (ka.ok()) {
+ perfTest = runTest(PerformanceTest.repeat(ecdh, 10));
+ }
+ Test compound = runTest(CompoundTest.all(ExpectedValue.SUCCESS, "Test of the " + CardUtil.getKATypeString(kaType) + " KeyAgreement.", allocate, ka, kaCompressed, perfTest));
+ supportTests.add(compound);
+ } else {
+ runTest(allocate);
+ supportTests.add(allocate);
+ }
+ }
+ 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;
+ if (expect.ok()) {
+ perfTest = runTest(PerformanceTest.repeat(ecdsa, 10));
+ }
+ Test compound = runTest(CompoundTest.all(ExpectedValue.SUCCESS, "Test of the " + CardUtil.getSigTypeString(sigType) + " signature.", allocate, expect, perfTest));
+ supportTests.add(compound);
+ } else {
+ supportTests.add(allocate);
+ }
+ }
+ doTest(CompoundTest.all(ExpectedValue.SUCCESS, description + " Some.", supportTests.toArray(new Test[0])));
+ new Command.Cleanup(this.card).send();
+ }
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/test/CardInvalidCurvesSuite.java b/src/cz/crcs/ectester/reader/test/CardInvalidCurvesSuite.java
new file mode 100644
index 0000000..8424d45
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/CardInvalidCurvesSuite.java
@@ -0,0 +1,67 @@
+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/CardTestSuite.java b/src/cz/crcs/ectester/reader/test/CardTestSuite.java
new file mode 100644
index 0000000..0eccd16
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/CardTestSuite.java
@@ -0,0 +1,24 @@
+package cz.crcs.ectester.reader.test;
+
+import cz.crcs.ectester.common.output.TestWriter;
+import cz.crcs.ectester.common.test.TestSuite;
+import cz.crcs.ectester.reader.CardMngr;
+import cz.crcs.ectester.reader.ECTesterReader;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public abstract class CardTestSuite extends TestSuite {
+ ECTesterReader.Config cfg;
+ CardMngr card;
+
+ CardTestSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager, String name, String description) {
+ super(writer, name, description);
+ this.card = cardManager;
+ this.cfg = cfg;
+ }
+
+ public CardMngr getCard() {
+ return card;
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/test/CardTestVectorSuite.java b/src/cz/crcs/ectester/reader/test/CardTestVectorSuite.java
new file mode 100644
index 0000000..73c6621
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/CardTestVectorSuite.java
@@ -0,0 +1,83 @@
+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.*;
+import cz.crcs.ectester.common.output.TestWriter;
+import cz.crcs.ectester.common.test.*;
+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;
+import java.util.List;
+import java.util.Map;
+
+import static cz.crcs.ectester.common.test.Result.ExpectedValue;
+import static cz.crcs.ectester.common.test.Result.Value;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class CardTestVectorSuite extends CardTestSuite {
+
+ public CardTestVectorSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) {
+ super(writer, cfg, cardManager, "test", "The test-vectors suite contains a collection of test vectors which test basic ECDH correctness.");
+ }
+
+ @Override
+ protected void runTests() throws Exception {
+ /* Set original curves (secg/nist/brainpool). Set keypairs from test vectors.
+ * Do ECDH both ways, export and verify that the result is correct.
+ */
+ 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());
+ }
+ EC_Params otherkey = EC_Store.getInstance().getObject(EC_Keypair.class, result.getOtherKey());
+ if (otherkey == null) {
+ otherkey = EC_Store.getInstance().getObject(EC_Key.Public.class, result.getOtherKey());
+ }
+ if (onekey == null || otherkey == null) {
+ throw new IOException("Test vector keys couldn't be located.");
+ }
+ List<Test> testVector = new LinkedList<>();
+
+ testVector.add(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS));
+ 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>() {
+ @Override
+ public Result apply(CommandTestable testable) {
+ Response.ECDH dh = (Response.ECDH) testable.getResponse();
+ if (!dh.successful())
+ return new Result(Value.FAILURE, "ECDH was unsuccessful.");
+ if (!dh.hasSecret())
+ return new Result(Value.FAILURE, "ECDH response did not contain the derived secret.");
+ if (!ByteUtil.compareBytes(dh.getSecret(), 0, result.getData(0), 0, dh.secretLength())) {
+ int firstDiff = ByteUtil.diffBytes(dh.getSecret(), 0, result.getData(0), 0, dh.secretLength());
+ return new Result(Value.FAILURE, "ECDH derived secret does not match the test-vector, first difference was at byte " + String.valueOf(firstDiff) + ".");
+ }
+ 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();
+ }
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/test/CardTwistTestSuite.java b/src/cz/crcs/ectester/reader/test/CardTwistTestSuite.java
new file mode 100644
index 0000000..c43b234
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/CardTwistTestSuite.java
@@ -0,0 +1,62 @@
+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
new file mode 100644
index 0000000..cac8fab
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/CardWrongCurvesSuite.java
@@ -0,0 +1,58 @@
+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/CommandTest.java b/src/cz/crcs/ectester/reader/test/CommandTest.java
new file mode 100644
index 0000000..a08d820
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/CommandTest.java
@@ -0,0 +1,76 @@
+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.
+ */
+public class CommandTest extends SimpleTest<CommandTestable> {
+ private CommandTest(CommandTestable command, TestCallback<CommandTestable> callback) {
+ super(command, callback);
+ }
+
+ public static CommandTest function(CommandTestable command, TestCallback<CommandTestable> callback) {
+ return new CommandTest(command, callback);
+ }
+
+ public static CommandTest function(Command command, TestCallback<CommandTestable> callback) {
+ return function(new CommandTestable(command), callback);
+ }
+
+ public static CommandTest expect(CommandTestable command, Result.ExpectedValue expected, String ok, String nok) {
+ 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());
+ return new Result(resultValue, resultValue.ok() ? ok : nok);
+ }
+ });
+ }
+
+ public static CommandTest expect(Command command, Result.ExpectedValue expectedValue, String ok, String nok) {
+ return expect(new CommandTestable(command), expectedValue, ok, nok);
+ }
+
+ public static CommandTest expect(CommandTestable command, Result.ExpectedValue expected) {
+ return expect(command, expected, null, null);
+ }
+
+ public static CommandTest expect(Command command, Result.ExpectedValue expectedValue) {
+ return expect(command, expectedValue, null, null);
+ }
+
+ public Command getCommand() {
+ return testable.getCommand();
+ }
+
+ public Response getResponse() {
+ return testable.getResponse();
+ }
+
+ @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();
+ }
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/test/CommandTestable.java b/src/cz/crcs/ectester/reader/test/CommandTestable.java
new file mode 100644
index 0000000..3bb55bf
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/CommandTestable.java
@@ -0,0 +1,44 @@
+package cz.crcs.ectester.reader.test;
+
+import cz.crcs.ectester.common.test.BaseTestable;
+import cz.crcs.ectester.common.test.TestException;
+import cz.crcs.ectester.reader.command.Command;
+import cz.crcs.ectester.reader.response.Response;
+
+import javax.smartcardio.CardException;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class CommandTestable extends BaseTestable {
+ private Command command;
+ private Response response;
+
+ public CommandTestable(Command command) {
+ this.command = command;
+ }
+
+ public Command getCommand() {
+ return command;
+ }
+
+ public Response getResponse() {
+ return response;
+ }
+
+ @Override
+ public void run() throws TestException {
+ try {
+ response = command.send();
+ } catch (CardException e) {
+ throw new TestException(e);
+ }
+
+ hasRun = true;
+ if (response.error()) {
+ error = true;
+ } else if (response.successful()) {
+ ok = true;
+ }
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/test/CompositeCurvesSuite.java b/src/cz/crcs/ectester/reader/test/CompositeCurvesSuite.java
deleted file mode 100644
index 8e7ca31..0000000
--- a/src/cz/crcs/ectester/reader/test/CompositeCurvesSuite.java
+++ /dev/null
@@ -1,53 +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.data.EC_Store;
-import cz.crcs.ectester.reader.CardMngr;
-import cz.crcs.ectester.reader.ECTester;
-import cz.crcs.ectester.reader.command.Command;
-import cz.crcs.ectester.reader.ec.EC_Curve;
-import cz.crcs.ectester.reader.ec.EC_Key;
-import javacard.security.KeyPair;
-
-import java.util.Map;
-
-import static cz.crcs.ectester.reader.test.Result.ExpectedValue;
-
-/**
- * @author Jan Jancar johny@neuromancer.sk
- */
-public class CompositeCurvesSuite extends TestSuite {
-
- public CompositeCurvesSuite(EC_Store dataStore, ECTester.Config cfg) {
- super(dataStore, cfg, "composite", "The composite suite tests ECDH over curves with composite order. This should generally fail, as using such a curve is unsafe.");
- }
-
- @Override
- public void setup(CardMngr cardManager) {
- /* Do the default tests 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 = dataStore.getObjects(EC_Key.class, "composite");
- for (EC_Key key : keys.values()) {
- EC_Curve curve = dataStore.getObject(EC_Curve.class, key.getCurve());
- if (cfg.namedCurve != null && !(key.getCurve().startsWith(cfg.namedCurve) || key.getCurve().equals(cfg.namedCurve))) {
- continue;
- }
- 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)) {
- tests.add(new Test.Simple(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS));
- tests.add(new Test.Simple(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.ANY));
- tests.add(new Test.Simple(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL), ExpectedValue.ANY));
- Command ecdhCommand = new Command.ECDH_direct(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, EC_Consts.KA_ECDH, key.flatten());
- tests.add(new Test.Simple(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(new Test.Simple(new Command.Cleanup(cardManager), ExpectedValue.ANY));
- }
- }
- }
-}
diff --git a/src/cz/crcs/ectester/reader/test/DefaultSuite.java b/src/cz/crcs/ectester/reader/test/DefaultSuite.java
deleted file mode 100644
index 736b7c5..0000000
--- a/src/cz/crcs/ectester/reader/test/DefaultSuite.java
+++ /dev/null
@@ -1,69 +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.data.EC_Store;
-import cz.crcs.ectester.reader.CardMngr;
-import cz.crcs.ectester.reader.ECTester;
-import cz.crcs.ectester.reader.command.Command;
-import javacard.security.KeyPair;
-
-import java.io.IOException;
-
-import static cz.crcs.ectester.reader.test.Result.ExpectedValue;
-
-/**
- * @author Jan Jancar johny@neuromancer.sk
- */
-public class DefaultSuite extends TestSuite {
-
- public DefaultSuite(EC_Store dataStore, ECTester.Config cfg) {
- super(dataStore, cfg, "default", "The default test suite tests basic support of ECDH and ECDSA.");
- }
-
- @Override
- public void setup(CardMngr cardManager) throws IOException {
- tests.add(new Test.Simple(new Command.Support(cardManager), ExpectedValue.ANY));
- if (cfg.namedCurve != null) {
- String desc = "Default tests over the " + cfg.namedCurve + " curve category.";
- if (cfg.primeField) {
- tests.addAll(defaultCategoryTests(cardManager, cfg.namedCurve, KeyPair.ALG_EC_FP, ExpectedValue.SUCCESS, ExpectedValue.SUCCESS, ExpectedValue.SUCCESS, ExpectedValue.ANY, ExpectedValue.SUCCESS, desc));
- }
- if (cfg.binaryField) {
- tests.addAll(defaultCategoryTests(cardManager, cfg.namedCurve, KeyPair.ALG_EC_F2M, ExpectedValue.SUCCESS, ExpectedValue.SUCCESS, ExpectedValue.SUCCESS, ExpectedValue.ANY, ExpectedValue.SUCCESS, desc));
- }
- } else {
- if (cfg.all) {
- if (cfg.primeField) {
- //iterate over prime curve sizes used: EC_Consts.FP_SIZES
- for (short keyLength : EC_Consts.FP_SIZES) {
- defaultTests(cardManager, keyLength, KeyPair.ALG_EC_FP);
- }
- }
- if (cfg.binaryField) {
- //iterate over binary curve sizes used: EC_Consts.F2M_SIZES
- for (short keyLength : EC_Consts.F2M_SIZES) {
- defaultTests(cardManager, keyLength, KeyPair.ALG_EC_F2M);
- }
- }
- } else {
- if (cfg.primeField) {
- defaultTests(cardManager, (short) cfg.bits, KeyPair.ALG_EC_FP);
- }
-
- if (cfg.binaryField) {
- defaultTests(cardManager, (short) cfg.bits, KeyPair.ALG_EC_F2M);
- }
- }
- }
- }
-
- private void defaultTests(CardMngr cardManager, short keyLength, byte keyType) throws IOException {
- tests.add(new Test.Simple(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, keyLength, keyType), ExpectedValue.SUCCESS));
- Command curve = Command.prepareCurve(cardManager, dataStore, cfg, ECTesterApplet.KEYPAIR_BOTH, keyLength, keyType);
- if (curve != null)
- tests.add(new Test.Simple(curve, ExpectedValue.SUCCESS));
- tests.add(defaultCurveTests(cardManager, ExpectedValue.SUCCESS, ExpectedValue.SUCCESS, ExpectedValue.ANY, ExpectedValue.SUCCESS, "Default tests."));
- tests.add(new Test.Simple(new Command.Cleanup(cardManager), ExpectedValue.ANY));
- }
-}
diff --git a/src/cz/crcs/ectester/reader/test/InvalidCurvesSuite.java b/src/cz/crcs/ectester/reader/test/InvalidCurvesSuite.java
deleted file mode 100644
index f61b695..0000000
--- a/src/cz/crcs/ectester/reader/test/InvalidCurvesSuite.java
+++ /dev/null
@@ -1,68 +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.data.EC_Store;
-import cz.crcs.ectester.reader.CardMngr;
-import cz.crcs.ectester.reader.ECTester;
-import cz.crcs.ectester.reader.command.Command;
-import cz.crcs.ectester.reader.ec.EC_Curve;
-import cz.crcs.ectester.reader.ec.EC_Key;
-import javacard.security.KeyPair;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-
-import static cz.crcs.ectester.reader.test.Result.ExpectedValue;
-
-/**
- * @author Jan Jancar johny@neuromancer.sk
- */
-public class InvalidCurvesSuite extends TestSuite {
-
- public InvalidCurvesSuite(EC_Store dataStore, ECTester.Config cfg) {
- super(dataStore, cfg, "invalid", "The invalid curve suite tests whether the card rejects points outside of the curve during ECDH.");
- }
-
- @Override
- public void setup(CardMngr cardManager) throws IOException {
- /* 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 = dataStore.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 = dataStore.getObject(EC_Curve.class, key.getCurve());
- if (cfg.namedCurve != null && !(key.getCurve().startsWith(cfg.namedCurve) || key.getCurve().equals(cfg.namedCurve))) {
- continue;
- }
- 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();
-
- tests.add(new Test.Simple(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS));
- tests.add(new Test.Simple(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.SUCCESS));
- tests.add(new Test.Simple(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL), ExpectedValue.SUCCESS));
- List<Test> ecdhTests = new LinkedList<>();
- for (EC_Key.Public pub : keys) {
- Command ecdhCommand = new Command.ECDH_direct(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, EC_Consts.KA_ANY, pub.flatten());
- ecdhTests.add(new Test.Simple(ecdhCommand, ExpectedValue.FAILURE, "Card correctly rejected point on invalid curve." , "Card incorrectly accepted point on invalid curve."));
- }
- tests.add(Test.Compound.all(ExpectedValue.SUCCESS, "Invalid curve test of " + curve.getId(), ecdhTests.toArray(new Test[0])));
- tests.add(new Test.Simple(new Command.Cleanup(cardManager), ExpectedValue.ANY));
- }
- }
-}
diff --git a/src/cz/crcs/ectester/reader/test/PerformanceTest.java b/src/cz/crcs/ectester/reader/test/PerformanceTest.java
new file mode 100644
index 0000000..4a27bad
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/PerformanceTest.java
@@ -0,0 +1,103 @@
+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 java.util.Arrays;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class PerformanceTest extends SimpleTest<CommandTestable> {
+ private long[] times;
+ private long mean;
+ private long median;
+ private long mode;
+ private int count;
+
+ private PerformanceTest(CommandTestable testable, int count) {
+ super(testable, new TestCallback<CommandTestable>() {
+ @Override
+ public Result apply(CommandTestable testable) {
+ return new Result(Result.Value.SUCCESS);
+ }
+ });
+ this.count = count;
+ }
+
+ public static PerformanceTest repeat(Command cmd, int count) {
+ return new PerformanceTest(new CommandTestable(cmd), count);
+ }
+
+ @Override
+ public String getDescription() {
+ return String.format("Mean = %d ns, Median = %d ns, Mode = %d ns", mean, median, mode);
+ }
+
+ @Override
+ public void run() throws TestException {
+ if (hasRun)
+ return;
+
+ times = new long[count];
+ for (int i = 0; i < count; ++i) {
+ testable.run();
+ times[i] = testable.getResponse().getDuration();
+ testable.reset();
+ }
+
+ mean = Arrays.stream(times).sum() / count;
+
+ long[] sorted = times.clone();
+ Arrays.sort(sorted);
+ if (count % 2 == 0) {
+ median = (sorted[(count / 2) - 1] + sorted[count / 2]) / 2;
+ } else {
+ median = sorted[count / 2];
+ }
+
+ long max_occurences = 0;
+ int i = 0;
+ while (i < count) {
+ long current_value = sorted[i];
+ long current_occurences = 0;
+ while (i < count && sorted[i] == current_value) {
+ i++;
+ current_occurences++;
+ }
+ if (current_occurences > max_occurences) {
+ max_occurences = current_occurences;
+ mode = current_value;
+ }
+ }
+ hasRun = true;
+ result = callback.apply(testable);
+ }
+
+ public long getCount() {
+ return count;
+ }
+
+ public Command getCommand() {
+ return testable.getCommand();
+ }
+
+ public long[] getTimes() {
+ return times;
+ }
+
+ public long getMean() {
+ return mean;
+ }
+
+ public long getMedian() {
+ return median;
+ }
+
+ public long getMode() {
+ return mode;
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/test/Result.java b/src/cz/crcs/ectester/reader/test/Result.java
deleted file mode 100644
index 82f0f32..0000000
--- a/src/cz/crcs/ectester/reader/test/Result.java
+++ /dev/null
@@ -1,94 +0,0 @@
-package cz.crcs.ectester.reader.test;
-
-/**
- * @author Jan Jancar johny@neuromancer.sk
- */
-public class Result {
-
- private Value value;
- private String cause;
-
- public Result(Value value) {
- this.value = value;
- }
-
- public Result(Value value, String cause) {
- this(value);
- this.cause = cause;
- }
-
- public Value getValue() {
- return value;
- }
-
- public String getCause() {
- return cause;
- }
-
- public boolean ok() {
- return value.ok();
- }
-
- public boolean compareTo(Result other) {
- if (other == null) {
- return false;
- }
- return value == other.value;
- }
-
- public boolean compareTo(Value other) {
- if (other == null) {
- return false;
- }
- return value == other;
- }
-
- /**
- *
- */
- public enum Value {
- SUCCESS(true),
- FAILURE(false),
- UXSUCCESS(false),
- XFAILURE(true),
- ERROR(false);
-
- private boolean ok;
-
- Value(boolean ok) {
- this.ok = ok;
- }
-
- public static Value fromExpected(ExpectedValue expected, boolean successful) {
- switch (expected) {
- case SUCCESS:
- return successful ? SUCCESS : FAILURE;
- case FAILURE:
- return successful ? UXSUCCESS : XFAILURE;
- case ANY:
- return SUCCESS;
- }
- return SUCCESS;
- }
-
- public static Value fromExpected(ExpectedValue expected, boolean successful, boolean error) {
- if (error) {
- return ERROR;
- }
- return fromExpected(expected, successful);
- }
-
- public boolean ok() {
- return ok;
- }
- }
-
- /**
- *
- */
- public enum ExpectedValue {
- SUCCESS,
- FAILURE,
- ANY
- }
-}
diff --git a/src/cz/crcs/ectester/reader/test/Test.java b/src/cz/crcs/ectester/reader/test/Test.java
deleted file mode 100644
index 022ad56..0000000
--- a/src/cz/crcs/ectester/reader/test/Test.java
+++ /dev/null
@@ -1,217 +0,0 @@
-package cz.crcs.ectester.reader.test;
-
-import cz.crcs.ectester.reader.command.Command;
-import cz.crcs.ectester.reader.response.Response;
-
-import javax.smartcardio.CardException;
-import java.util.function.BiFunction;
-import java.util.function.Function;
-
-import static cz.crcs.ectester.reader.test.Result.ExpectedValue;
-import static cz.crcs.ectester.reader.test.Result.Value;
-
-/**
- * An abstract test that can be run and has a Result.
- *
- * @author Jan Jancar johny@neuromancer.sk
- */
-public abstract class Test {
- boolean hasRun = false;
- Result result;
-
- public Result getResult() {
- if (!hasRun) {
- return null;
- }
- return result;
- }
-
- public Value getResultValue() {
- if (!hasRun) {
- return null;
- }
- return result.getValue();
- }
-
- public String getResultCause() {
- if (!hasRun) {
- return null;
- }
- return result.getCause();
- }
-
- public boolean ok() {
- if (!hasRun) {
- return true;
- }
- return result.ok();
- }
-
- public abstract String getDescription();
-
- public boolean hasRun() {
- return hasRun;
- }
-
- public abstract void run() throws CardException;
-
- /**
- * A simple test that runs one Command to get and evaluate one Response
- * to get a Result and compare it with the expected one.
- */
- public static class Simple extends Test {
- private BiFunction<Command, Response, Result> callback;
- private Command command;
- private Response response;
-
- public Simple(Command command, BiFunction<Command, Response, Result> callback) {
- this.command = command;
- this.callback = callback;
- }
-
- public Simple(Command command, ExpectedValue expected, String ok, String nok) {
- this(command, (cmd, resp) -> {
- Value resultValue = Value.fromExpected(expected, resp.successful(), resp.error());
- return new Result(resultValue, resultValue.ok() ? ok : nok);
- });
- }
-
- public Simple(Command command, ExpectedValue expected) {
- this(command, expected, null, null);
- }
-
- public Command getCommand() {
- return command;
- }
-
- public Response getResponse() {
- return response;
- }
-
- @Override
- public void run() throws CardException {
- if (hasRun)
- return;
-
- response = command.send();
- if (callback != null) {
- result = callback.apply(command, response);
- } else {
- if (response.successful()) {
- result = new Result(Value.SUCCESS);
- } else {
- result = new Result(Value.FAILURE);
- }
- }
- hasRun = true;
- }
-
- @Override
- public String getDescription() {
- return response.getDescription();
- }
- }
-
- /**
- * A compound test that runs many Tests and has a Result dependent on all/some of their Results.
- */
- public static class Compound extends Test {
- private Function<Test[], Result> callback;
- private Test[] tests;
- private String description;
-
- private Compound(Function<Test[], Result> callback, Test... tests) {
- this.callback = callback;
- this.tests = tests;
- }
-
- private Compound(Function<Test[], Result> callback, String descripiton, Test... tests) {
- this(callback, tests);
- this.description = descripiton;
- }
-
- public static Compound function(Function<Test[], Result> callback, Test... tests) {
- return new Compound(callback, tests);
- }
-
- public static Compound function(Function<Test[], Result> callback, String description, Test... tests) {
- return new Compound(callback, description, tests);
- }
-
- public static Compound all(ExpectedValue what, Test... all) {
- return new Compound((tests) -> {
- for (Test test : tests) {
- if (!Value.fromExpected(what, test.ok()).ok()) {
- return new Result(Value.FAILURE, "At least one of the sub-tests did not have the expected result.");
- }
- }
- return new Result(Value.SUCCESS, "All sub-tests had the expected result.");
- }, all);
- }
-
- public static Compound all(ExpectedValue what, String description, Test... all) {
- Compound result = Compound.all(what, all);
- result.setDescription(description);
- return result;
- }
-
- public static Compound any(ExpectedValue what, Test... any) {
- return new Compound((tests) -> {
- for (Test test : tests) {
- if (Value.fromExpected(what, test.ok()).ok()) {
- return new Result(Value.SUCCESS, "At least one of the sub-tests did have the expected result.");
- }
- }
- return new Result(Value.FAILURE, "None of the sub-tests had the expected result.");
- }, any);
- }
-
- public static Compound any(ExpectedValue what, String description, Test... any) {
- Compound result = Compound.any(what, any);
- result.setDescription(description);
- return result;
- }
-
- public static Compound mask(ExpectedValue[] results, Test... masked) {
- return new Compound((tests) -> {
- for (int i = 0; i < results.length; ++i) {
- if (!Value.fromExpected(results[i], tests[i].ok()).ok()) {
- return new Result(Value.FAILURE, "At least one of the sub-tests did not match the result mask.");
- }
- }
- return new Result(Value.SUCCESS, "All sub-tests matched the expected mask.");
- }, masked);
- }
-
- public static Compound mask(ExpectedValue[] results, String description, Test... masked) {
- Compound result = Compound.mask(results, masked);
- result.setDescription(description);
- return result;
- }
-
- public Test[] getTests() {
- return tests;
- }
-
- @Override
- public void run() throws CardException {
- if (hasRun)
- return;
-
- for (Test test : tests) {
- test.run();
- }
- result = callback.apply(tests);
- this.hasRun = true;
- }
-
- public void setDescription(String description) {
- this.description = description;
- }
-
- @Override
- public String getDescription() {
- return description;
- }
- }
-}
diff --git a/src/cz/crcs/ectester/reader/test/TestRunner.java b/src/cz/crcs/ectester/reader/test/TestRunner.java
deleted file mode 100644
index baab6a8..0000000
--- a/src/cz/crcs/ectester/reader/test/TestRunner.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package cz.crcs.ectester.reader.test;
-
-import cz.crcs.ectester.reader.output.TestWriter;
-
-import javax.smartcardio.CardException;
-
-/**
- * @author Jan Jancar johny@neuromancer.sk
- */
-public class TestRunner {
- private TestSuite suite;
- private TestWriter writer;
-
- public TestRunner(TestSuite suite, TestWriter writer) {
- this.suite = suite;
- this.writer = writer;
- }
-
- public void run() throws CardException {
- writer.begin(suite);
- for (Test t : suite.getTests()) {
- if (!t.hasRun()) {
- t.run();
- writer.outputTest(t);
- }
- }
- writer.end();
- }
-}
diff --git a/src/cz/crcs/ectester/reader/test/TestSuite.java b/src/cz/crcs/ectester/reader/test/TestSuite.java
deleted file mode 100644
index f13317c..0000000
--- a/src/cz/crcs/ectester/reader/test/TestSuite.java
+++ /dev/null
@@ -1,135 +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.data.EC_Store;
-import cz.crcs.ectester.reader.CardMngr;
-import cz.crcs.ectester.reader.ECTester;
-import cz.crcs.ectester.reader.command.Command;
-import cz.crcs.ectester.reader.ec.EC_Curve;
-
-import java.io.IOException;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Function;
-
-import static cz.crcs.ectester.reader.test.Result.ExpectedValue;
-import static cz.crcs.ectester.reader.test.Result.Value;
-
-/**
- * @author Jan Jancar johny@neuromancer.sk
- */
-public abstract class TestSuite {
- EC_Store dataStore;
- ECTester.Config cfg;
- String name;
- String description;
- List<Test> tests = new LinkedList<>();
-
- TestSuite(EC_Store dataStore, ECTester.Config cfg, String name, String description) {
- this.dataStore = dataStore;
- this.cfg = cfg;
- this.name = name;
- this.description = description;
- }
-
- public abstract void setup(CardMngr cardManager) throws IOException;
-
- public List<Test> getTests() {
- return Collections.unmodifiableList(tests);
- }
-
- public String getName() {
- return name;
- }
-
- public String getDescription() {
- return description;
- }
-
- /**
- * @param cardManager cardManager to send APDU through
- * @param generateExpected expected result of the Generate command
- * @param ecdhExpected expected result of the ordinary ECDH command
- * @param ecdhCompressExpected expected result of ECDH with a compressed point
- * @param ecdsaExpected expected result of the ordinary ECDSA command
- * @param description compound test description
- * @return test to run
- */
- Test defaultCurveTests(CardMngr cardManager, ExpectedValue generateExpected, ExpectedValue ecdhExpected, ExpectedValue ecdhCompressExpected, ExpectedValue ecdsaExpected, String description) {
- List<Test> tests = new LinkedList<>();
-
- tests.add(new Test.Simple(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_BOTH), generateExpected));
- tests.add(new Test.Simple(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, EC_Consts.KA_ECDH), ecdhExpected));
- tests.add(new Test.Simple(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_COMPRESS, EC_Consts.KA_ECDH), ecdhExpected));
- tests.add(new Test.Simple(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_ONE, EC_Consts.KA_ECDH), ExpectedValue.FAILURE));
- tests.add(new Test.Simple(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_ZERO, EC_Consts.KA_ECDH), ExpectedValue.FAILURE));
- tests.add(new Test.Simple(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_MAX, EC_Consts.KA_ECDH), ExpectedValue.FAILURE));
- tests.add(new Test.Simple(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_FULLRANDOM, EC_Consts.KA_ECDH), ExpectedValue.FAILURE));
- tests.add(new Test.Simple(new Command.ECDSA(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, null), ecdsaExpected));
-
- return Test.Compound.function((testArray) -> {
- Function<ExpectedValue, String> shouldHave = (expected) -> {
- switch (expected) {
- case SUCCESS:
- return "succeeded";
- case FAILURE:
- return "failed";
- case ANY:
- default:
- return "";
- }
- };
-
- for (int i = 0; i < testArray.length; ++i) {
- Test t = testArray[i];
- if (!t.ok()) {
- if (i == 0) { // generate
- return new Result(Value.FAILURE, "The generation of a key should have " + shouldHave.apply(generateExpected) + ", but it did not.");
- } else if (i == 2) { // ecdh compress
- return new Result(Value.FAILURE, "The ECDH should have " + shouldHave.apply(ecdhExpected) + ", but it did not.");
- } else if (i == 1) { // ecdh normal
- return new Result(Value.FAILURE, "The ECDH of a compressed point should have " + shouldHave.apply(ecdhCompressExpected) + ", but it did not.");
- } else if (i <= 6) { // ecdh wrong, should fail
- return new Result(Value.FAILURE, "The ECDH of a corrupted point should have failed, but it did not.");
- } else { // ecdsa
- return new Result(Value.FAILURE, "The ECDSA should have " + shouldHave.apply(ecdsaExpected) + ", but it did not.");
- }
- }
- }
- return new Result(Value.SUCCESS);
- }, description, tests.toArray(new Test[0]));
- }
-
- /**
- * @param cardManager cardManager to send APDU through
- * @param category category to test
- * @param field field to test (KeyPair.ALG_EC_FP || KeyPair.ALG_EC_F2M)
- * @param setExpected expected result of the Set (curve) command
- * @param generateExpected expected result of the Generate command
- * @param ecdhExpected expected result of the ordinary ECDH command
- * @param ecdhCompressedExpected expected result of the ECDH command with a compressed point.
- * @param ecdsaExpected expected result of the ordinary ECDSA command
- * @param description compound test description
- * @return tests to run
- */
- List<Test> defaultCategoryTests(CardMngr cardManager, String category, byte field, ExpectedValue setExpected, ExpectedValue generateExpected, ExpectedValue ecdhExpected, ExpectedValue ecdhCompressedExpected, ExpectedValue ecdsaExpected, String description) {
- List<Test> tests = new LinkedList<>();
- Map<String, EC_Curve> curves = dataStore.getObjects(EC_Curve.class, category);
- if (curves == null)
- return tests;
- for (Map.Entry<String, EC_Curve> entry : curves.entrySet()) {
- EC_Curve curve = entry.getValue();
- if (curve.getField() == field && (curve.getBits() == cfg.bits || cfg.all)) {
- tests.add(new Test.Simple(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), field), ExpectedValue.SUCCESS));
- tests.add(new Test.Simple(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), setExpected));
- tests.add(defaultCurveTests(cardManager, generateExpected, ecdhExpected, ecdhCompressedExpected, ecdsaExpected, description));
- tests.add(new Test.Simple(new Command.Cleanup(cardManager), ExpectedValue.ANY));
- }
- }
-
- return tests;
- }
-}
diff --git a/src/cz/crcs/ectester/reader/test/TestVectorSuite.java b/src/cz/crcs/ectester/reader/test/TestVectorSuite.java
deleted file mode 100644
index ff46feb..0000000
--- a/src/cz/crcs/ectester/reader/test/TestVectorSuite.java
+++ /dev/null
@@ -1,83 +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.data.EC_Store;
-import cz.crcs.ectester.reader.CardMngr;
-import cz.crcs.ectester.reader.ECTester;
-import cz.crcs.ectester.reader.Util;
-import cz.crcs.ectester.reader.command.Command;
-import cz.crcs.ectester.reader.ec.*;
-import cz.crcs.ectester.reader.response.Response;
-import javacard.security.KeyPair;
-
-import java.io.IOException;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-
-import static cz.crcs.ectester.reader.test.Result.ExpectedValue;
-import static cz.crcs.ectester.reader.test.Result.Value;
-
-/**
- * @author Jan Jancar johny@neuromancer.sk
- */
-public class TestVectorSuite extends TestSuite {
-
- public TestVectorSuite(EC_Store dataStore, ECTester.Config cfg) {
- super(dataStore, cfg, "test", "The test-vectors suite contains a collection of test vectors which test basic ECDH correctness.");
- }
-
- @Override
- public void setup(CardMngr cardManager) throws IOException {
- /* Set original curves (secg/nist/brainpool). Set keypairs from test vectors.
- * Do ECDH both ways, export and verify that the result is correct.
- */
- Map<String, EC_KAResult> results = dataStore.getObjects(EC_KAResult.class, "test");
- for (EC_KAResult result : results.values()) {
- EC_Curve curve = dataStore.getObject(EC_Curve.class, result.getCurve());
- if (cfg.namedCurve != null && !(result.getCurve().startsWith(cfg.namedCurve) || result.getCurve().equals(cfg.namedCurve))) {
- continue;
- }
- 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 = dataStore.getObject(EC_Keypair.class, result.getOneKey());
- if (onekey == null) {
- onekey = dataStore.getObject(EC_Key.Private.class, result.getOneKey());
- }
- EC_Params otherkey = dataStore.getObject(EC_Keypair.class, result.getOtherKey());
- if (otherkey == null) {
- otherkey = dataStore.getObject(EC_Key.Public.class, result.getOtherKey());
- }
- if (onekey == null || otherkey == null) {
- throw new IOException("Test vector keys couldn't be located.");
- }
- List<Test> testVector = new LinkedList<>();
-
- testVector.add(new Test.Simple(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS));
- testVector.add(new Test.Simple(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.SUCCESS));
- //tests.add(new Test.Simple(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_BOTH), ExpectedValue.SUCCESS));
- testVector.add(new Test.Simple(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.CURVE_external, EC_Consts.PARAMETER_S, onekey.flatten(EC_Consts.PARAMETER_S)), ExpectedValue.SUCCESS));
- testVector.add(new Test.Simple(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, EC_Consts.PARAMETER_W, otherkey.flatten(EC_Consts.PARAMETER_W)), ExpectedValue.SUCCESS));
- testVector.add(new Test.Simple(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_TRUE, EC_Consts.CORRUPTION_NONE, result.getKA()), (command, response) -> {
- Response.ECDH dh = (Response.ECDH) response;
- if (!dh.successful())
- return new Result(Value.FAILURE, "ECDH was unsuccessful.");
- if (!dh.hasSecret())
- return new Result(Value.FAILURE, "ECDH response did not contain the derived secret.");
- if (!Util.compareBytes(dh.getSecret(), 0, result.getParam(0), 0, dh.secretLength())) {
- int firstDiff = Util.diffBytes(dh.getSecret(), 0, result.getParam(0), 0, dh.secretLength());
- return new Result(Value.FAILURE, "ECDH derived secret does not match the test, first difference was at byte " + String.valueOf(firstDiff) + ".");
- }
- return new Result(Value.SUCCESS);
- }));
- tests.add(Test.Compound.all(ExpectedValue.SUCCESS, "Test vector " + result.getId(), testVector.toArray(new Test[0])));
- tests.add(new Test.Simple(new Command.Cleanup(cardManager), ExpectedValue.ANY));
-
- }
- }
-}
diff --git a/src/cz/crcs/ectester/reader/test/WrongCurvesSuite.java b/src/cz/crcs/ectester/reader/test/WrongCurvesSuite.java
deleted file mode 100644
index e9389b4..0000000
--- a/src/cz/crcs/ectester/reader/test/WrongCurvesSuite.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package cz.crcs.ectester.reader.test;
-
-import cz.crcs.ectester.data.EC_Store;
-import cz.crcs.ectester.reader.CardMngr;
-import cz.crcs.ectester.reader.ECTester;
-import javacard.security.KeyPair;
-
-import java.io.IOException;
-
-import static cz.crcs.ectester.reader.test.Result.ExpectedValue;
-
-/**
- * @author Jan Jancar johny@neuromancer.sk
- */
-public class WrongCurvesSuite extends TestSuite {
-
- public WrongCurvesSuite(EC_Store dataStore, ECTester.Config cfg) {
- super(dataStore, cfg, "wrong", "The wrong curve suite tests whether the card rejects domain parameters which are not curves.");
- }
-
- @Override
- public void setup(CardMngr cardManager) throws IOException {
- /* Just do the default tests on the wrong curves.
- * These should generally fail, the curves aren't curves.
- */
- String desc = "Default tests over wrong curve params.";
- if (cfg.primeField) {
- tests.addAll(defaultCategoryTests(cardManager, cfg.testSuite, KeyPair.ALG_EC_FP, ExpectedValue.FAILURE, ExpectedValue.FAILURE, ExpectedValue.FAILURE, ExpectedValue.FAILURE, ExpectedValue.FAILURE, desc));
- }
- if (cfg.binaryField) {
- tests.addAll(defaultCategoryTests(cardManager, cfg.testSuite, KeyPair.ALG_EC_F2M, ExpectedValue.FAILURE, ExpectedValue.FAILURE, ExpectedValue.FAILURE, ExpectedValue.FAILURE, ExpectedValue.FAILURE, desc));
- }
- }
-}