aboutsummaryrefslogtreecommitdiff
path: root/src/cz/crcs/ectester/reader/test
diff options
context:
space:
mode:
authorJ08nY2017-11-10 12:56:47 +0100
committerJ08nY2017-11-10 12:56:47 +0100
commit588aa02a37ffd6e0ee2d164540023d1692109e89 (patch)
tree609505ab9461fa341bb468caf092af6a563dcc61 /src/cz/crcs/ectester/reader/test
parent631edcfe145a558dd645dae5d83f3825a74471b1 (diff)
parent4248ae9d9e3f6b79c9874c49ba901fba0f332211 (diff)
downloadECTester-588aa02a37ffd6e0ee2d164540023d1692109e89.tar.gz
ECTester-588aa02a37ffd6e0ee2d164540023d1692109e89.tar.zst
ECTester-588aa02a37ffd6e0ee2d164540023d1692109e89.zip
Merge branch 'devel'
Diffstat (limited to 'src/cz/crcs/ectester/reader/test')
-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/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
9 files changed, 782 insertions, 0 deletions
diff --git a/src/cz/crcs/ectester/reader/test/CompositeCurvesSuite.java b/src/cz/crcs/ectester/reader/test/CompositeCurvesSuite.java
new file mode 100644
index 0000000..8e7ca31
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/CompositeCurvesSuite.java
@@ -0,0 +1,53 @@
+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
new file mode 100644
index 0000000..736b7c5
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/DefaultSuite.java
@@ -0,0 +1,69 @@
+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
new file mode 100644
index 0000000..f61b695
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/InvalidCurvesSuite.java
@@ -0,0 +1,68 @@
+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/Result.java b/src/cz/crcs/ectester/reader/test/Result.java
new file mode 100644
index 0000000..82f0f32
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/Result.java
@@ -0,0 +1,94 @@
+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
new file mode 100644
index 0000000..022ad56
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/Test.java
@@ -0,0 +1,217 @@
+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
new file mode 100644
index 0000000..baab6a8
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/TestRunner.java
@@ -0,0 +1,29 @@
+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
new file mode 100644
index 0000000..f13317c
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/TestSuite.java
@@ -0,0 +1,135 @@
+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
new file mode 100644
index 0000000..ff46feb
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/TestVectorSuite.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.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
new file mode 100644
index 0000000..e9389b4
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/WrongCurvesSuite.java
@@ -0,0 +1,34 @@
+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));
+ }
+ }
+}