summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJ08nY2018-05-02 20:08:49 +0200
committerJ08nY2018-05-02 20:08:49 +0200
commit87c4accbecc2f37a42c96e2bbc3c90618bfa2fdc (patch)
treef24d78af5e7a14e1a322a2b3978b0204bb0e5ec3
parent92c28a15e018e55b8f195993414f769ecf96a663 (diff)
downloadECTester-87c4accbecc2f37a42c96e2bbc3c90618bfa2fdc.tar.gz
ECTester-87c4accbecc2f37a42c96e2bbc3c90618bfa2fdc.tar.zst
ECTester-87c4accbecc2f37a42c96e2bbc3c90618bfa2fdc.zip
-rw-r--r--.gitignore3
-rw-r--r--src/cz/crcs/ectester/applet/EC_Consts.java11
-rw-r--r--src/cz/crcs/ectester/common/ec/EC_Curve.java8
-rw-r--r--src/cz/crcs/ectester/common/test/TestSuite.java4
-rw-r--r--src/cz/crcs/ectester/common/util/ByteUtil.java4
-rw-r--r--src/cz/crcs/ectester/common/util/CardUtil.java2
-rw-r--r--src/cz/crcs/ectester/reader/ECTesterReader.java7
-rw-r--r--src/cz/crcs/ectester/reader/test/CardCofactorSuite.java (renamed from src/cz/crcs/ectester/reader/test/CardCofactorTestSuite.java)4
-rw-r--r--src/cz/crcs/ectester/reader/test/CardCompressionSuite.java119
-rw-r--r--src/cz/crcs/ectester/reader/test/CardDefaultSuite.java18
-rw-r--r--src/cz/crcs/ectester/reader/test/PerformanceTest.java23
11 files changed, 184 insertions, 19 deletions
diff --git a/.gitignore b/.gitignore
index f03ac1b..aecbbec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,11 +11,14 @@
# Test runs in /dist
/dist/*.default
/dist/*.test-vectors
+/dist/*.compression
/dist/*.cofactor
/dist/*.composite
/dist/*.wrong
/dist/*.invalid
/dist/*.twist
+/dist/*.degenerate
+/dist/*.xml
# Built binaries in /src.
/src/**/*.a
diff --git a/src/cz/crcs/ectester/applet/EC_Consts.java b/src/cz/crcs/ectester/applet/EC_Consts.java
index 88a8bd6..c24283a 100644
--- a/src/cz/crcs/ectester/applet/EC_Consts.java
+++ b/src/cz/crcs/ectester/applet/EC_Consts.java
@@ -994,6 +994,7 @@ public class EC_Consts {
public static final short TRANSFORMATION_INCREMENT = (short) 0x40;
public static final short TRANSFORMATION_INFINITY = (short) 0x80;
public static final short TRANSFORMATION_COMPRESS = (short) 0x0100;
+ public static final short TRANSFORMATION_COMPRESS_HYBRID = (short) 0x0200;
// toX962 FORM types
public static final byte X962_UNCOMPRESSED = (byte) 0x00;
@@ -1386,6 +1387,7 @@ public class EC_Consts {
Util.arrayFillNonAtomic(buffer, offset, length, (byte) 0);
length = 1;
break;
+ case TRANSFORMATION_COMPRESS_HYBRID:
case TRANSFORMATION_COMPRESS:
if ((short) (length % 2) != 1) {
// an uncompressed point should have odd length (since 1 byte type, + 2 * coords)
@@ -1400,9 +1402,12 @@ public class EC_Consts {
buffer[offset] = 2;
}
- length = (short) (half + 1);
+ if (transformationPart == TRANSFORMATION_COMPRESS) {
+ length = (short) (half + 1);
+ } else {
+ buffer[offset] += 4;
+ }
break;
- //TODO: test hybrid form with not corresponding yBit (in first byte value) and y_value in the second half of the param
default:
ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED);
}
@@ -1427,7 +1432,7 @@ public class EC_Consts {
break;
case X962_HYBRID:
outputBuffer[offset] = 4;
- case X962_COMPRESSED:
+ case X962_COMPRESSED: /* fallthrough */
byte yLSB = yBuffer[(short) (yOffset + yLength)];
byte yBit = (byte) (yLSB & 0x01);
diff --git a/src/cz/crcs/ectester/common/ec/EC_Curve.java b/src/cz/crcs/ectester/common/ec/EC_Curve.java
index 173fd29..8338a93 100644
--- a/src/cz/crcs/ectester/common/ec/EC_Curve.java
+++ b/src/cz/crcs/ectester/common/ec/EC_Curve.java
@@ -54,7 +54,7 @@ public class EC_Curve extends EC_Params {
return "<" + getId() + "> " + (field == KeyPair.ALG_EC_FP ? "Prime" : "Binary") + " field Elliptic curve (" + String.valueOf(bits) + "b)" + (desc == null ? "" : ": " + desc);
}
- public ECParameterSpec toSpec() {
+ public EllipticCurve toCurve() {
ECField field;
if (this.field == KeyPair.ALG_EC_FP) {
field = new ECFieldFp(new BigInteger(1, getData(0)));
@@ -71,7 +71,11 @@ public class EC_Curve extends EC_Params {
BigInteger a = new BigInteger(1, getParam(EC_Consts.PARAMETER_A)[0]);
BigInteger b = new BigInteger(1, getParam(EC_Consts.PARAMETER_B)[0]);
- EllipticCurve curve = new EllipticCurve(field, a, b);
+ return new EllipticCurve(field, a, b);
+ }
+
+ public ECParameterSpec toSpec() {
+ EllipticCurve curve = toCurve();
byte[][] G = getParam(EC_Consts.PARAMETER_G);
BigInteger gx = new BigInteger(1, G[0]);
diff --git a/src/cz/crcs/ectester/common/test/TestSuite.java b/src/cz/crcs/ectester/common/test/TestSuite.java
index 3b3cd4b..9e08891 100644
--- a/src/cz/crcs/ectester/common/test/TestSuite.java
+++ b/src/cz/crcs/ectester/common/test/TestSuite.java
@@ -40,7 +40,7 @@ public abstract class TestSuite {
* @return The test that was run.
* @throws TestException
*/
- protected Test runTest(Test t) {
+ protected <T extends Test> T runTest(T t) {
running = t;
t.run();
running = null;
@@ -54,7 +54,7 @@ public abstract class TestSuite {
* @return The test that was run.
* @throws TestException
*/
- protected Test doTest(Test t) {
+ protected <T extends Test> T doTest(T t) {
runTest(t);
writer.outputTest(t);
return t;
diff --git a/src/cz/crcs/ectester/common/util/ByteUtil.java b/src/cz/crcs/ectester/common/util/ByteUtil.java
index 0de8dc7..daacabb 100644
--- a/src/cz/crcs/ectester/common/util/ByteUtil.java
+++ b/src/cz/crcs/ectester/common/util/ByteUtil.java
@@ -172,4 +172,8 @@ public class ByteUtil {
}
return out;
}
+
+ public static byte[] prependLength(byte[] data) {
+ return concatenate(ByteUtil.shortToBytes((short) data.length), data);
+ }
}
diff --git a/src/cz/crcs/ectester/common/util/CardUtil.java b/src/cz/crcs/ectester/common/util/CardUtil.java
index 2501e47..85a4a35 100644
--- a/src/cz/crcs/ectester/common/util/CardUtil.java
+++ b/src/cz/crcs/ectester/common/util/CardUtil.java
@@ -197,6 +197,8 @@ public class CardUtil {
return "INFINITY";
case EC_Consts.TRANSFORMATION_COMPRESS:
return "COMPRESSED";
+ case EC_Consts.TRANSFORMATION_COMPRESS_HYBRID:
+ return "HYBRID";
case EC_Consts.TRANSFORMATION_MAX:
return "MAX";
default:
diff --git a/src/cz/crcs/ectester/reader/ECTesterReader.java b/src/cz/crcs/ectester/reader/ECTesterReader.java
index 0a3031b..6098cd4 100644
--- a/src/cz/crcs/ectester/reader/ECTesterReader.java
+++ b/src/cz/crcs/ectester/reader/ECTesterReader.java
@@ -406,6 +406,9 @@ public class ECTesterReader {
case "test-vectors":
suite = new CardTestVectorSuite(writer, cfg, cardManager);
break;
+ case "compression":
+ suite = new CardCompressionSuite(writer, cfg, cardManager);
+ break;
default:
// These run are dangerous, prompt before them.
System.out.println("The test you selected (" + cfg.testSuite + ") is potentially dangerous.");
@@ -436,7 +439,7 @@ public class ECTesterReader {
suite = new CardTwistTestSuite(writer, cfg, cardManager);
break;
case "cofactor":
- suite = new CardCofactorTestSuite(writer, cfg, cardManager);
+ suite = new CardCofactorSuite(writer, cfg, cardManager);
break;
default:
System.err.println("Unknown test suite.");
@@ -767,7 +770,7 @@ public class ECTesterReader {
}
testSuite = cli.getOptionValue("test", "default").toLowerCase();
- String[] tests = new String[]{"default", "composite", "invalid", "degenerate", "test-vectors", "wrong", "twist", "cofactor"};
+ String[] tests = new String[]{"default", "composite", "compression", "invalid", "degenerate", "test-vectors", "wrong", "twist", "cofactor"};
if (!Arrays.asList(tests).contains(testSuite)) {
System.err.println("Unknown test suite " + testSuite + ". Should be one of: " + Arrays.toString(tests));
return false;
diff --git a/src/cz/crcs/ectester/reader/test/CardCofactorTestSuite.java b/src/cz/crcs/ectester/reader/test/CardCofactorSuite.java
index 8cf59e2..aca3371 100644
--- a/src/cz/crcs/ectester/reader/test/CardCofactorTestSuite.java
+++ b/src/cz/crcs/ectester/reader/test/CardCofactorSuite.java
@@ -22,8 +22,8 @@ import static cz.crcs.ectester.common.test.Result.ExpectedValue;
/**
* @author Jan Jancar johny@neuromancer.sk
*/
-public class CardCofactorTestSuite extends CardTestSuite {
- public CardCofactorTestSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) {
+public class CardCofactorSuite extends CardTestSuite {
+ public CardCofactorSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) {
super(writer, cfg, cardManager, "cofactor", "The cofactor test suite tests whether the card correctly rejects points on the curve but not in the subgroup generated by the generator during ECDH.");
}
diff --git a/src/cz/crcs/ectester/reader/test/CardCompressionSuite.java b/src/cz/crcs/ectester/reader/test/CardCompressionSuite.java
new file mode 100644
index 0000000..7300653
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/CardCompressionSuite.java
@@ -0,0 +1,119 @@
+package cz.crcs.ectester.reader.test;
+
+import cz.crcs.ectester.applet.ECTesterApplet;
+import cz.crcs.ectester.applet.EC_Consts;
+import cz.crcs.ectester.common.output.TestWriter;
+import cz.crcs.ectester.common.test.CompoundTest;
+import cz.crcs.ectester.common.test.Result;
+import cz.crcs.ectester.common.test.Test;
+import cz.crcs.ectester.common.util.ByteUtil;
+import cz.crcs.ectester.common.util.CardUtil;
+import cz.crcs.ectester.common.util.ECUtil;
+import cz.crcs.ectester.reader.CardMngr;
+import cz.crcs.ectester.reader.ECTesterReader;
+import cz.crcs.ectester.reader.command.Command;
+import cz.crcs.ectester.reader.response.Response;
+import javacard.security.KeyPair;
+
+import java.security.spec.ECPoint;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class CardCompressionSuite extends CardTestSuite {
+ public CardCompressionSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) {
+ super(writer, cfg, cardManager, "compression", "");
+ }
+
+ @Override
+ protected void runTests() throws Exception {
+ //iterate over default curve sizes
+ // for Fp
+ // - allocate, set custom curve, generate keypairs, -> export generated.
+ // - test ecdh with local and remote simply(no compression)
+ // - test local privkey, remote pubkey (compressed)
+ // - test local privkey, remote pubkey (hybrid)
+ // - test local privkey, remote pubkey (hybrid with wrong y)
+ // - test local privkey, remote pubkey (point at infinity)
+ if (cfg.primeField) {
+ runCompression(KeyPair.ALG_EC_FP);
+ }
+ // for F2m
+ // - allocate, set custom curve, generate keypairs, -> export generated.
+ // - test ecdh with local and remote simply(no compression)
+ // - test local privkey, remote pubkey (compressed)
+ // - test local privkey, remote pubkey (hybrid)
+ // - test local privkey, remote pubkey (hybrid with wrong y)
+ // - test local privkey, remote pubkey (point at infinity)
+ if (cfg.binaryField) {
+ runCompression(KeyPair.ALG_EC_F2M);
+ }
+ }
+
+ private void runCompression(byte field) throws Exception {
+ short[] keySizes = field == KeyPair.ALG_EC_FP ? EC_Consts.FP_SIZES : EC_Consts.F2M_SIZES;
+ short domain = field == KeyPair.ALG_EC_FP ? EC_Consts.PARAMETERS_DOMAIN_FP : EC_Consts.PARAMETERS_DOMAIN_F2M;
+
+ for (short keyLength : keySizes) {
+ String spec = keyLength + "b " + CardUtil.getKeyTypeString(field);
+
+ Test allocateFirst = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, keyLength, field), Result.ExpectedValue.SUCCESS));
+ if (!allocateFirst.ok()) {
+ doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "No support for " + spec + ".", allocateFirst));
+ continue;
+ }
+
+ List<Test> compressionTests = new LinkedList<>();
+ compressionTests.add(allocateFirst);
+ Test setCustom = runTest(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.getCurve(keyLength, field), domain, null), Result.ExpectedValue.SUCCESS));
+ Test genCustom = runTest(CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_BOTH), Result.ExpectedValue.SUCCESS));
+ compressionTests.add(setCustom);
+ compressionTests.add(genCustom);
+
+ Response.Export key = new Command.Export(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.KEY_PUBLIC, EC_Consts.PARAMETER_W).send();
+ byte[] pubkey = key.getParameter(ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.KEY_PUBLIC);
+ ECPoint pub;
+ try {
+ pub = ECUtil.fromX962(pubkey, null);
+ } catch (IllegalArgumentException iae) {
+ // TODO: use external SECG curves so we have them here.
+ doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "", compressionTests.toArray(new Test[0])));
+ continue;
+ }
+
+ List<Test> kaTests = new LinkedList<>();
+ for (byte kaType : EC_Consts.KA_TYPES) {
+ List<Test> thisTests = new LinkedList<>();
+ Test allocate = runTest(CommandTest.expect(new Command.AllocateKeyAgreement(this.card, kaType), Result.ExpectedValue.SUCCESS));
+ if (allocate.ok()) {
+ Test ka = runTest(CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, kaType), Result.ExpectedValue.SUCCESS));
+
+ thisTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "KeyAgreement setup and basic test.", allocate, ka));
+ if (ka.ok()) {
+ // tests of the good stuff
+ Test kaCompressed = CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_COMPRESS, kaType), Result.ExpectedValue.SUCCESS);
+ Test kaHybrid = CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_COMPRESS_HYBRID, kaType), Result.ExpectedValue.SUCCESS);
+ thisTests.add(CompoundTest.any(Result.ExpectedValue.SUCCESS, "Tests of compressed and hybrid form.", kaCompressed, kaHybrid));
+
+ // tests the bad stuff here
+ byte[] pubHybrid = ECUtil.toX962Hybrid(pub, keyLength);
+ pubHybrid[pubHybrid.length - 1] ^= 1;
+ byte[] pubHybridEncoded = ByteUtil.prependLength(pubHybrid);
+ Test kaBadHybrid = CommandTest.expect(new Command.ECDH_direct(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, kaType, pubHybridEncoded), Result.ExpectedValue.FAILURE);
+
+ byte[] pubInfinityEncoded = {0x01, 0x00};
+ Test kaBadInfinity = CommandTest.expect(new Command.ECDH_direct(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, kaType, pubInfinityEncoded), Result.ExpectedValue.FAILURE);
+ thisTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Tests of corrupted hybrid form and infinity.", kaBadHybrid, kaBadInfinity));
+ }
+ kaTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "KeyAgreement tests of " + CardUtil.getKATypeString(kaType) + ".", thisTests.toArray(new Test[0])));
+ }
+ }
+ compressionTests.addAll(kaTests);
+
+ doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Compression test of " + spec + ".", compressionTests.toArray(new Test[0])));
+ 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
index 611f5ec..4480962 100644
--- a/src/cz/crcs/ectester/reader/test/CardDefaultSuite.java
+++ b/src/cz/crcs/ectester/reader/test/CardDefaultSuite.java
@@ -14,9 +14,11 @@ import javacard.security.KeyPair;
import java.util.LinkedList;
import java.util.List;
+import java.util.Random;
import java.util.function.Function;
import static cz.crcs.ectester.common.test.Result.ExpectedValue;
+import static cz.crcs.ectester.common.test.Result.Value;
/**
* @author Jan Jancar johny@neuromancer.sk
@@ -70,9 +72,9 @@ public class CardDefaultSuite extends CardTestSuite {
String kaDesc = "Test of the " + CardUtil.getKATypeString(kaType) + " KeyAgreement.";
Function<Test[], Result> kaCallback = (tests) -> {
if (tests[1].ok() || tests[2].ok()) {
- return new Result(Result.Value.SUCCESS, "Some ECDH is supported.");
+ return new Result(Value.SUCCESS, "Some ECDH is supported.");
} else {
- return new Result(Result.Value.FAILURE, "ECDH failed.");
+ return new Result(Value.FAILURE, "ECDH failed.");
}
};
@@ -102,10 +104,18 @@ public class CardDefaultSuite extends CardTestSuite {
String signDesc = "Test of the " + CardUtil.getSigTypeString(sigType) + " signature.";
+ Random rand = new Random();
+ byte[] sigData = new byte[64];
+ rand.nextBytes(sigData);
+
Test compound;
if (expect.ok()) {
- Test perfTest = runTest(PerformanceTest.repeat(ecdsa, 10));
- compound = runTest(CompoundTest.all(ExpectedValue.SUCCESS, signDesc, allocate, expect, perfTest));
+ Command ecdsaSign = new Command.ECDSA_sign(this.card, ECTesterApplet.KEYPAIR_LOCAL, sigType, ECTesterApplet.EXPORT_TRUE, sigData);
+ PerformanceTest signTest = runTest(PerformanceTest.repeat("Sign", ecdsaSign, 10));
+ byte[] signature = signTest.getResponses()[0].getParam(0);
+ Command ecdsaVerify = new Command.ECDSA_verify(this.card, ECTesterApplet.KEYPAIR_LOCAL, sigType, sigData, signature);
+ PerformanceTest verifyTest = runTest(PerformanceTest.repeat("Verify", ecdsaVerify, 10));
+ compound = runTest(CompoundTest.all(ExpectedValue.SUCCESS, signDesc, allocate, expect, signTest, verifyTest));
} else {
compound = runTest(CompoundTest.all(ExpectedValue.SUCCESS, signDesc, allocate, expect));
}
diff --git a/src/cz/crcs/ectester/reader/test/PerformanceTest.java b/src/cz/crcs/ectester/reader/test/PerformanceTest.java
index 2e5f376..9abaadc 100644
--- a/src/cz/crcs/ectester/reader/test/PerformanceTest.java
+++ b/src/cz/crcs/ectester/reader/test/PerformanceTest.java
@@ -5,6 +5,7 @@ import cz.crcs.ectester.common.test.SimpleTest;
import cz.crcs.ectester.common.test.TestCallback;
import cz.crcs.ectester.common.test.TestException;
import cz.crcs.ectester.reader.command.Command;
+import cz.crcs.ectester.reader.response.Response;
import java.util.Arrays;
@@ -13,12 +14,14 @@ import java.util.Arrays;
*/
public class PerformanceTest extends SimpleTest<CommandTestable> {
private long[] times;
+ private Response[] responses;
private long mean;
private long median;
private long mode;
private int count;
+ private String desc;
- private PerformanceTest(CommandTestable testable, int count) {
+ private PerformanceTest(CommandTestable testable, int count, String desc) {
super(testable, new TestCallback<CommandTestable>() {
@Override
public Result apply(CommandTestable testable) {
@@ -26,23 +29,31 @@ public class PerformanceTest extends SimpleTest<CommandTestable> {
}
});
this.count = count;
+ this.desc = desc;
}
public static PerformanceTest repeat(Command cmd, int count) {
- return new PerformanceTest(new CommandTestable(cmd), count);
+ return new PerformanceTest(new CommandTestable(cmd), count, null);
+ }
+
+ public static PerformanceTest repeat(String desc, Command cmd, int count) {
+ return new PerformanceTest(new CommandTestable(cmd), count, desc);
}
@Override
public String getDescription() {
- return String.format("Mean = %d ns, Median = %d ns, Mode = %d ns", mean, median, mode);
+ String rest = String.format("Mean = %d ns, Median = %d ns, Mode = %d ns", mean, median, mode);
+ return (desc == null ? rest : desc + " (" + rest + ")");
}
@Override
protected void runSelf() {
times = new long[count];
+ responses = new Response[count];
for (int i = 0; i < count; ++i) {
testable.run();
- times[i] = testable.getResponse().getDuration();
+ responses[i] = testable.getResponse();
+ times[i] = responses[i].getDuration();
testable.reset();
}
@@ -81,6 +92,10 @@ public class PerformanceTest extends SimpleTest<CommandTestable> {
return testable.getCommand();
}
+ public Response[] getResponses() {
+ return responses;
+ }
+
public long[] getTimes() {
return times;
}