aboutsummaryrefslogtreecommitdiff
path: root/src/cz/crcs/ectester
diff options
context:
space:
mode:
Diffstat (limited to 'src/cz/crcs/ectester')
-rw-r--r--src/cz/crcs/ectester/applet/ECKeyGenerator.java32
-rw-r--r--src/cz/crcs/ectester/applet/ECKeyTester.java49
-rw-r--r--src/cz/crcs/ectester/applet/ECTesterApplet.java7
-rw-r--r--src/cz/crcs/ectester/applet/ECUtil.java17
-rw-r--r--src/cz/crcs/ectester/data/EC_Store.java8
-rw-r--r--src/cz/crcs/ectester/reader/ECTester.java666
-rw-r--r--src/cz/crcs/ectester/reader/Response.java106
-rw-r--r--src/cz/crcs/ectester/reader/Test.java81
-rw-r--r--src/cz/crcs/ectester/reader/TestSuite.java167
-rw-r--r--src/cz/crcs/ectester/reader/Util.java82
-rw-r--r--src/cz/crcs/ectester/reader/ec/EC_Curve.java10
-rw-r--r--src/cz/crcs/ectester/reader/ec/EC_Data.java40
-rw-r--r--src/cz/crcs/ectester/reader/ec/EC_KAResult.java13
-rw-r--r--src/cz/crcs/ectester/reader/ec/EC_Key.java10
-rw-r--r--src/cz/crcs/ectester/reader/ec/EC_Keypair.java1
-rw-r--r--src/cz/crcs/ectester/reader/ec/EC_Params.java28
16 files changed, 854 insertions, 463 deletions
diff --git a/src/cz/crcs/ectester/applet/ECKeyGenerator.java b/src/cz/crcs/ectester/applet/ECKeyGenerator.java
index b412370..a36bd0c 100644
--- a/src/cz/crcs/ectester/applet/ECKeyGenerator.java
+++ b/src/cz/crcs/ectester/applet/ECKeyGenerator.java
@@ -39,8 +39,8 @@ public class ECKeyGenerator {
}
public short clearPair(KeyPair keypair, byte key) {
- sw = ISO7816.SW_NO_ERROR;
try {
+ sw = ECUtil.nullCheck(keypair);
if ((key & EC_Consts.KEY_PUBLIC) != 0) keypair.getPublic().clearKey();
if ((key & EC_Consts.KEY_PRIVATE) != 0) keypair.getPrivate().clearKey();
} catch (CardRuntimeException ce) {
@@ -54,8 +54,8 @@ public class ECKeyGenerator {
* @return
*/
public short generatePair(KeyPair keypair) {
- sw = ISO7816.SW_NO_ERROR;
try {
+ sw = ECUtil.nullCheck(keypair);
keypair.genKeyPair();
} catch (CardRuntimeException ce) {
sw = ce.getReason();
@@ -155,11 +155,12 @@ public class ECKeyGenerator {
* @return
*/
public short setParameter(KeyPair keypair, byte key, short param, byte[] data, short offset, short length) {
- sw = ISO7816.SW_NO_ERROR;
- ECPublicKey ecPublicKey = (ECPublicKey) keypair.getPublic();
- ECPrivateKey ecPrivateKey = (ECPrivateKey) keypair.getPrivate();
-
try {
+ sw = ECUtil.nullCheck(keypair);
+
+ ECPublicKey ecPublicKey = (ECPublicKey) keypair.getPublic();
+ ECPrivateKey ecPrivateKey = (ECPrivateKey) keypair.getPrivate();
+
switch (param) {
case EC_Consts.PARAMETER_FP:
if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setFieldFP(data, offset, length);
@@ -174,8 +175,11 @@ public class ECKeyGenerator {
short i1 = Util.makeShort(data[(short) (offset + 2)], data[(short) (offset + 3)]);
short i2 = Util.makeShort(data[(short) (offset + 4)], data[(short) (offset + 5)]);
short i3 = Util.makeShort(data[(short) (offset + 6)], data[(short) (offset + 7)]);
- if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setFieldF2M(i1, i2, i3);
- if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setFieldF2M(i1, i2, i3);
+// if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setFieldF2M(i1, i2, i3);
+// if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setFieldF2M(i1, i2, i3);
+ // TODO fix this
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setFieldF2M(i3, i2, i1);
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setFieldF2M(i3, i2, i1);
} else {
sw = ISO7816.SW_UNKNOWN;
}
@@ -275,12 +279,12 @@ public class ECKeyGenerator {
* @return length of data written
*/
public short exportParameter(KeyPair keypair, byte key, short param, byte[] outputBuffer, short outputOffset) {
- sw = ISO7816.SW_NO_ERROR;
- ECPublicKey ecPublicKey = (ECPublicKey) keypair.getPublic();
- ECPrivateKey ecPrivateKey = (ECPrivateKey) keypair.getPrivate();
-
short length = 0;
try {
+ sw = ECUtil.nullCheck(keypair);
+ ECPublicKey ecPublicKey = (ECPublicKey) keypair.getPublic();
+ ECPrivateKey ecPrivateKey = (ECPrivateKey) keypair.getPrivate();
+
switch (param) {
case EC_Consts.PARAMETER_FP:
if ((key & EC_Consts.KEY_PUBLIC) != 0) length = ecPublicKey.getField(outputBuffer, outputOffset);
@@ -385,8 +389,10 @@ public class ECKeyGenerator {
* @return sw
*/
public short copyCurve(KeyPair from, KeyPair to, short params, byte[] buffer, short offset) {
- sw = ISO7816.SW_NO_ERROR;
try {
+ sw = ECUtil.nullCheck(from);
+ sw = ECUtil.nullCheck(to);
+
short param = EC_Consts.PARAMETER_FP;
while (param <= EC_Consts.PARAMETER_K) {
short masked = (short) (param & params);
diff --git a/src/cz/crcs/ectester/applet/ECKeyTester.java b/src/cz/crcs/ectester/applet/ECKeyTester.java
index 64f3024..c4b58e0 100644
--- a/src/cz/crcs/ectester/applet/ECKeyTester.java
+++ b/src/cz/crcs/ectester/applet/ECKeyTester.java
@@ -4,10 +4,7 @@ package cz.crcs.ectester.applet;
import javacard.framework.CardRuntimeException;
import javacard.framework.ISO7816;
import javacard.framework.Util;
-import javacard.security.ECPrivateKey;
-import javacard.security.ECPublicKey;
-import javacard.security.KeyAgreement;
-import javacard.security.Signature;
+import javacard.security.*;
/**
* Class capable of testing ECDH/C and ECDSA.
@@ -53,11 +50,15 @@ public class ECKeyTester {
return sw;
}
- private short testKA(KeyAgreement ka, ECPrivateKey privateKey, byte[] pubkeyBuffer, short pubkeyOffset, short pubkeyLength, byte[] outputBuffer, short outputOffset) {
- sw = ISO7816.SW_NO_ERROR;
+ private short testKA(KeyAgreement ka, KeyPair privatePair, KeyPair publicPair, byte[] pubkeyBuffer, short pubkeyOffset, byte[] outputBuffer, short outputOffset, byte corruption) {
short length = 0;
try {
- ka.init(privateKey);
+ sw = ECUtil.nullCheck(privatePair);
+ sw = ECUtil.nullCheck(publicPair);
+
+ ka.init(privatePair.getPrivate());
+ short pubkeyLength = ((ECPublicKey) publicPair.getPublic()).getW(pubkeyBuffer, pubkeyOffset);
+ pubkeyLength = EC_Consts.corruptParameter(corruption, pubkeyBuffer, pubkeyOffset, pubkeyLength);
length = ka.generateSecret(pubkeyBuffer, pubkeyOffset, pubkeyLength, outputBuffer, outputOffset);
} catch (CardRuntimeException ce) {
sw = ce.getReason();
@@ -66,12 +67,12 @@ public class ECKeyTester {
}
/**
- * Tests ECDH secret generation with given {@code privateKey} and {@code publicKey}.
+ * Tests ECDH secret generation with keys from given {@code privatePair} and {@code publicPair}.
* Uses {@code pubkeyBuffer} at {@code pubkeyOffset} for computations.
* Output should equal with ECDHC output.
*
- * @param privateKey
- * @param publicKey
+ * @param privatePair
+ * @param publicPair
* @param pubkeyBuffer
* @param pubkeyOffset
* @param outputBuffer
@@ -79,19 +80,17 @@ public class ECKeyTester {
* @param corruption
* @return derived secret length
**/
- public short testECDH(ECPrivateKey privateKey, ECPublicKey publicKey, byte[] pubkeyBuffer, short pubkeyOffset, byte[] outputBuffer, short outputOffset, byte corruption) {
- short length = publicKey.getW(pubkeyBuffer, pubkeyOffset);
- length = EC_Consts.corruptParameter(corruption, pubkeyBuffer, pubkeyOffset, length);
- return testKA(ecdhKeyAgreement, privateKey, pubkeyBuffer, pubkeyOffset, length, outputBuffer, outputOffset);
+ public short testECDH(KeyPair privatePair, KeyPair publicPair, byte[] pubkeyBuffer, short pubkeyOffset, byte[] outputBuffer, short outputOffset, byte corruption) {
+ return testKA(ecdhKeyAgreement, privatePair, publicPair, pubkeyBuffer, pubkeyOffset, outputBuffer, outputOffset, corruption);
}
/**
- * Tests ECDHC secret generation with given {@code privateKey} and {@code publicKey}.
+ * Tests ECDHC secret generation with keys from given {@code privatePair} and {@code publicPair}.
* Uses {@code pubkeyBuffer} at {@code pubkeyOffset} for computations.
* Output should equal to ECDH output.
*
- * @param privateKey
- * @param publicKey
+ * @param privatePair
+ * @param publicPair
* @param pubkeyBuffer
* @param pubkeyOffset
* @param outputBuffer
@@ -99,16 +98,14 @@ public class ECKeyTester {
* @param corruption
* @return derived secret length
*/
- public short testECDHC(ECPrivateKey privateKey, ECPublicKey publicKey, byte[] pubkeyBuffer, short pubkeyOffset, byte[] outputBuffer, short outputOffset, byte corruption) {
- short length = publicKey.getW(pubkeyBuffer, pubkeyOffset);
- length = EC_Consts.corruptParameter(corruption, pubkeyBuffer, pubkeyOffset, length);
- return testKA(ecdhcKeyAgreement, privateKey, pubkeyBuffer, pubkeyOffset, length, outputBuffer, outputOffset);
+ public short testECDHC(KeyPair privatePair, KeyPair publicPair, byte[] pubkeyBuffer, short pubkeyOffset, byte[] outputBuffer, short outputOffset, byte corruption) {
+ return testKA(ecdhcKeyAgreement, privatePair, publicPair, pubkeyBuffer, pubkeyOffset, outputBuffer, outputOffset, corruption);
}
/**
*
- * @param privateKey
- * @param publicKey
+ * @param privatePair
+ * @param publicPair
* @param pubkeyBuffer
* @param pubkeyOffset
* @param outputBuffer
@@ -116,12 +113,12 @@ public class ECKeyTester {
* @param corruption
* @return
*/
- public short testKA(ECPrivateKey privateKey, ECPublicKey publicKey, byte[] pubkeyBuffer, short pubkeyOffset, byte[] outputBuffer, short outputOffset, byte corruption) {
- short ecdhLength = testECDH(privateKey, publicKey, pubkeyBuffer, pubkeyOffset, outputBuffer, outputOffset, corruption);
+ public short testECDH_ECDHC(KeyPair privatePair, KeyPair publicPair, byte[] pubkeyBuffer, short pubkeyOffset, byte[] outputBuffer, short outputOffset, byte corruption) {
+ short ecdhLength = testECDH(privatePair, publicPair, pubkeyBuffer, pubkeyOffset, outputBuffer, outputOffset, corruption);
if (sw != ISO7816.SW_NO_ERROR) {
return ecdhLength;
}
- short ecdhcLength = testECDHC(privateKey, publicKey, pubkeyBuffer, pubkeyOffset, outputBuffer, (short) (outputOffset + ecdhLength), corruption);
+ short ecdhcLength = testECDHC(privatePair, publicPair, pubkeyBuffer, pubkeyOffset, outputBuffer, (short) (outputOffset + ecdhLength), corruption);
short length = (short) (ecdhLength + ecdhcLength);
if (sw != ISO7816.SW_NO_ERROR) {
return length;
diff --git a/src/cz/crcs/ectester/applet/ECTesterApplet.java b/src/cz/crcs/ectester/applet/ECTesterApplet.java
index 98f59c4..f802233 100644
--- a/src/cz/crcs/ectester/applet/ECTesterApplet.java
+++ b/src/cz/crcs/ectester/applet/ECTesterApplet.java
@@ -64,6 +64,7 @@ public class ECTesterApplet extends Applet {
// STATUS WORDS
public static final short SW_SIG_VERIFY_FAIL = (short) 0x0ee1;
public static final short SW_DH_DHC_MISMATCH = (short) 0x0ee2;
+ public static final short SW_KEYPAIR_NULL = (short) 0x0ee3;
private static final short ARRAY_LENGTH = (short) 0xff;
@@ -551,13 +552,13 @@ public class ECTesterApplet extends Applet {
short secretLength = 0;
switch (type) {
case EC_Consts.KA_ECDH:
- secretLength = keyTester.testECDH((ECPrivateKey) priv.getPrivate(), (ECPublicKey) pub.getPublic(), ramArray, (short) 0, ramArray2, (short) 0, corruption);
+ secretLength = keyTester.testECDH(priv, pub, ramArray, (short) 0, ramArray2, (short) 0, corruption);
break;
case EC_Consts.KA_ECDHC:
- secretLength = keyTester.testECDHC((ECPrivateKey) priv.getPrivate(), (ECPublicKey) pub.getPublic(), ramArray, (short) 0, ramArray2, (short) 0, corruption);
+ secretLength = keyTester.testECDHC(priv, pub, ramArray, (short) 0, ramArray2, (short) 0, corruption);
break;
case EC_Consts.KA_BOTH:
- secretLength = keyTester.testKA((ECPrivateKey) priv.getPrivate(), (ECPublicKey) pub.getPublic(), ramArray, (short) 0, ramArray2, (short) 0, corruption);
+ secretLength = keyTester.testECDH_ECDHC(priv, pub, ramArray, (short) 0, ramArray2, (short) 0, corruption);
break;
default:
ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED);
diff --git a/src/cz/crcs/ectester/applet/ECUtil.java b/src/cz/crcs/ectester/applet/ECUtil.java
new file mode 100644
index 0000000..e7e4a8a
--- /dev/null
+++ b/src/cz/crcs/ectester/applet/ECUtil.java
@@ -0,0 +1,17 @@
+package cz.crcs.ectester.applet;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.security.KeyPair;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class ECUtil {
+
+ static short nullCheck(KeyPair keyPair) {
+ if (keyPair == null)
+ ISOException.throwIt(ECTesterApplet.SW_KEYPAIR_NULL);
+ return ISO7816.SW_NO_ERROR;
+ }
+}
diff --git a/src/cz/crcs/ectester/data/EC_Store.java b/src/cz/crcs/ectester/data/EC_Store.java
index 0571cd8..ccd3586 100644
--- a/src/cz/crcs/ectester/data/EC_Store.java
+++ b/src/cz/crcs/ectester/data/EC_Store.java
@@ -151,12 +151,13 @@ public class EC_Store {
}
short bitsize = Short.parseShort(bits.getTextContent());
- EC_Curve curve = new EC_Curve(bitsize, alg, descs);
+ EC_Curve curve = new EC_Curve(id.getTextContent(), bitsize, alg, descs);
InputStream csv = parseDataElement(dir, curveElem);
if (!curve.readCSV(csv)) {
throw new IOException("Invalid csv data.");
}
+ csv.close();
objMap.put(id.getTextContent(), curve);
} else {
@@ -233,12 +234,13 @@ public class EC_Store {
kab = EC_Consts.KA_ECDHC;
}
- EC_KAResult kaResult = new EC_KAResult(kab, curve.getTextContent(), onekey.getTextContent(), otherkey.getTextContent(), descs);
+ EC_KAResult kaResult = new EC_KAResult(id.getTextContent(), kab, curve.getTextContent(), onekey.getTextContent(), otherkey.getTextContent(), descs);
InputStream csv = parseDataElement(dir, elem);
if (!kaResult.readCSV(csv)) {
throw new IOException("Invalid csv data.");
}
+ csv.close();
objMap.put(id.getTextContent(), kaResult);
} else {
@@ -276,6 +278,8 @@ public class EC_Store {
if (!result.readCSV(csv)) {
throw new IOException("Invalid CSV data.");
}
+ csv.close();
+
return result;
}
diff --git a/src/cz/crcs/ectester/reader/ECTester.java b/src/cz/crcs/ectester/reader/ECTester.java
index 6965d14..8afda17 100644
--- a/src/cz/crcs/ectester/reader/ECTester.java
+++ b/src/cz/crcs/ectester/reader/ECTester.java
@@ -44,47 +44,7 @@ public class ECTester {
private CardMngr cardManager;
private DirtyLogger systemOutLogger;
private EC_Store dataStore;
-
- //Options
- private int optBits;
- private boolean optAll;
- private boolean optPrimeField = false;
- private boolean optBinaryField = false;
-
- private String optNamedCurve = null;
- private String optCurveFile = null;
- private boolean optCustomCurve = false;
-
- private boolean optAnyPublic = false;
- private String optNamedPublic = null;
- private String optPublic = null;
-
- private boolean optAnyPrivate = false;
- private String optNamedPrivate = null;
- private String optPrivate = null;
-
- private boolean optAnyKey = false;
- private String optNamedKey = null;
- private String optKey = null;
-
- private boolean optAnyKeypart = false;
-
- private String optLog = null;
-
- private boolean optVerbose = false;
- private String optInput = null;
- private String optOutput = null;
- private boolean optFresh = false;
- private boolean optSimulate = false;
-
- //Action-related options
- private String optListNamed;
- private String optTestSuite;
- private int optGenerateAmount;
- private int optECDHCount;
- private byte optECDHKA;
- private int optECDSACount;
-
+ private Config cfg;
private Options opts = new Options();
private static final String CLI_HEADER = "\nECTester, a javacard Elliptic Curve Cryptograhy support tester/utility.\n\n";
@@ -104,8 +64,10 @@ public class ECTester {
help();
return;
}
+ cfg = new Config();
+
//if not, read other options first, into attributes, then do action
- if (!readOptions(cli)) {
+ if (!cfg.readOptions(cli)) {
return;
}
@@ -117,10 +79,10 @@ public class ECTester {
}
//init CardManager
- cardManager = new CardMngr(optVerbose, optSimulate);
+ cardManager = new CardMngr(cfg.verbose, cfg.simulate);
//connect or simulate connection
- if (optSimulate) {
+ if (cfg.simulate) {
if (!cardManager.prepareLocalSimulatorApplet(AID, INSTALL_DATA, ECTesterApplet.class)) {
System.err.println("Failed to establish a simulator.");
System.exit(1);
@@ -133,7 +95,7 @@ public class ECTester {
cardManager.send(SELECT_ECTESTERAPPLET);
}
- systemOutLogger = new DirtyLogger(optLog, true);
+ systemOutLogger = new DirtyLogger(cfg.log, true);
//do action
if (cli.hasOption("export")) {
@@ -208,7 +170,7 @@ public class ECTester {
* -dh / --ecdh [count]
* -dhc / --ecdhc [count]
* -dsa / --ecdsa [count]
- * -ln / --list-named
+ * -ln / --list-named [obj]
*
* Options:
* -b / --bit-size <b> // -a / --all
@@ -292,193 +254,25 @@ public class ECTester {
}
/**
- * Reads and validates options, also sets defaults.
- *
- * @param cli cli object, with parsed args
- * @return whether the options are valid.
- */
- private boolean readOptions(CommandLine cli) {
- optBits = Integer.parseInt(cli.getOptionValue("bit-size", "0"));
- optAll = cli.hasOption("all");
- optPrimeField = cli.hasOption("fp");
- optBinaryField = cli.hasOption("f2m");
-
- optNamedCurve = cli.getOptionValue("named-curve");
- optCustomCurve = cli.hasOption("custom");
- optCurveFile = cli.getOptionValue("curve");
-
- optNamedPublic = cli.getOptionValue("named-public");
- optPublic = cli.getOptionValue("public");
- optAnyPublic = (optPublic != null) || (optNamedPublic != null);
-
- optNamedPrivate = cli.getOptionValue("named-private");
- optPrivate = cli.getOptionValue("private");
- optAnyPrivate = (optPrivate != null) || (optNamedPrivate != null);
-
- optNamedKey = cli.getOptionValue("named-key");
- optKey = cli.getOptionValue("key");
- optAnyKey = (optKey != null) || (optNamedKey != null);
- optAnyKeypart = optAnyKey || optAnyPublic || optAnyPrivate;
-
- if (cli.hasOption("log")) {
- optLog = cli.getOptionValue("log", String.format("ECTESTER_log_%d.log", System.currentTimeMillis() / 1000));
- }
-
- optVerbose = cli.hasOption("verbose");
- optInput = cli.getOptionValue("input");
- optOutput = cli.getOptionValue("output");
- optFresh = cli.hasOption("fresh");
- optSimulate = cli.hasOption("simulate");
-
- if (cli.hasOption("list-named")) {
- optListNamed = cli.getOptionValue("list-named");
- return true;
- }
-
- if ((optKey != null || optNamedKey != null) && (optPublic != null || optPrivate != null || optNamedPublic != null || optNamedPrivate != null)) {
- System.err.print("Can only specify the whole key with --key/--named-key or pubkey and privkey with --public/--named-public and --private/--named-private.");
- return false;
- }
- if (optBits < 0) {
- System.err.println("Bit-size must not be negative.");
- return false;
- }
- if (optBits == 0 && !optAll) {
- System.err.println("You must specify either bit-size with -b or all bit-sizes with -a.");
- return false;
- }
-
- if (optKey != null && optNamedKey != null || optPublic != null && optNamedPublic != null || optPrivate != null && optNamedPrivate != null) {
- System.err.println("You cannot specify both a named key and a key file.");
- return false;
- }
-
- if (cli.hasOption("export")) {
- if (optPrimeField == optBinaryField) {
- System.err.print("Need to specify field with -fp or -f2m. (not both)");
- return false;
- }
- if (optAnyKeypart) {
- System.err.println("Keys should not be specified when exporting curve params.");
- return false;
- }
- if (optNamedCurve != null || optCustomCurve || optCurveFile != null) {
- System.err.println("Specifying a curve for curve export makes no sense.");
- return false;
- }
- if (optOutput == null) {
- System.err.println("You have to specify an output file for curve parameter export.");
- return false;
- }
- if (optAll) {
- System.err.println("You have to specify curve bit-size with -b");
- return false;
- }
-
- } else if (cli.hasOption("generate")) {
- if (optPrimeField == optBinaryField) {
- System.err.print("Need to specify field with -fp or -f2m. (not both)");
- return false;
- }
- if (optAnyKeypart) {
- System.err.println("Keys should not be specified when generating keys.");
- return false;
- }
- if (optOutput == null) {
- System.err.println("You have to specify an output file for the key generation process.");
- return false;
- }
- if (optAll) {
- System.err.println("You have to specify curve bit-size with -b");
- return false;
- }
-
- optGenerateAmount = Integer.parseInt(cli.getOptionValue("generate", "0"));
- if (optGenerateAmount < 0) {
- System.err.println("Amount of keys generated cant be negative.");
- return false;
- }
- } else if (cli.hasOption("test")) {
- if (!optBinaryField && !optPrimeField) {
- optBinaryField = true;
- optPrimeField = true;
- }
-
- optTestSuite = cli.getOptionValue("test", "default").toLowerCase();
- String[] tests = new String[]{"default", "nonprime", "invalid", "test-vectors", "wrong"};
- List<String> testsList = Arrays.asList(tests);
- if (!testsList.contains(optTestSuite)) {
- System.err.println("Unknown test case. Should be one of: " + Arrays.toString(tests));
- return false;
- }
-
- } else if (cli.hasOption("ecdh") || cli.hasOption("ecdhc")) {
- if (optPrimeField == optBinaryField) {
- System.err.print("Need to specify field with -fp or -f2m. (not both)");
- return false;
- }
- if (optAll) {
- System.err.println("You have to specify curve bit-size with -b");
- return false;
- }
-
- if (cli.hasOption("ecdh")) {
- optECDHCount = Integer.parseInt(cli.getOptionValue("ecdh", "1"));
- optECDHKA = EC_Consts.KA_ECDH;
- } else if (cli.hasOption("ecdhc")) {
- optECDHCount = Integer.parseInt(cli.getOptionValue("ecdhc", "1"));
- optECDHKA = EC_Consts.KA_ECDHC;
- }
- if (optECDHCount <= 0) {
- System.err.println("ECDH count cannot be <= 0.");
- return false;
- }
-
- } else if (cli.hasOption("ecdsa")) {
- if (optPrimeField == optBinaryField) {
- System.err.print("Need to specify field with -fp or -f2m. (but not both)");
- return false;
- }
- if (optAll) {
- System.err.println("You have to specify curve bit-size with -b");
- return false;
- }
-
- if ((optAnyPublic) != (optAnyPrivate) && !optAnyKey) {
- System.err.println("You cannot only specify a part of a keypair.");
- return false;
- }
-
- optECDSACount = Integer.parseInt(cli.getOptionValue("ecdsa", "1"));
- if (optECDSACount <= 0) {
- System.err.println("ECDSA count cannot be <= 0.");
- return false;
- }
- }
-
- return true;
- }
-
- /**
* List categories and named curves.
*/
private void list() {
Map<String, EC_Category> categories = dataStore.getCategories();
- if (optListNamed == null) {
+ if (cfg.listNamed == null) {
// print all categories, briefly
for (EC_Category cat : categories.values()) {
System.out.println(cat);
}
- } else if (categories.containsKey(optListNamed)) {
+ } else if (categories.containsKey(cfg.listNamed)) {
// print given category
- System.out.println(categories.get(optListNamed));
+ System.out.println(categories.get(cfg.listNamed));
} else {
// print given object
- EC_Data object = dataStore.getObject(EC_Data.class, optListNamed);
+ EC_Data object = dataStore.getObject(EC_Data.class, cfg.listNamed);
if (object != null) {
System.out.println(object);
} else {
- System.err.println("Named object " + optListNamed + " not found!");
+ System.err.println("Named object " + cfg.listNamed + " not found!");
}
}
}
@@ -499,17 +293,17 @@ public class ECTester {
* @throws IOException if an IO error occurs when writing to key file.
*/
private void export() throws CardException, IOException {
- byte keyClass = optPrimeField ? KeyPair.ALG_EC_FP : KeyPair.ALG_EC_F2M;
+ byte keyClass = cfg.primeField ? KeyPair.ALG_EC_FP : KeyPair.ALG_EC_F2M;
List<Response> sent = new LinkedList<>();
- sent.add(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_LOCAL, (short) optBits, keyClass).send());
+ sent.add(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_LOCAL, (short) cfg.bits, keyClass).send());
sent.add(new Command.Clear(cardManager, ECTesterApplet.KEYPAIR_LOCAL).send());
sent.add(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL).send());
// Cofactor generally isn't set on the default curve parameters on cards,
// since its not necessary for ECDH, only ECDHC which not many cards implement
// TODO: check if its assumend to be == 1?
- short domainAll = optPrimeField ? EC_Consts.PARAMETERS_DOMAIN_FP : EC_Consts.PARAMETERS_DOMAIN_F2M;
+ short domainAll = cfg.primeField ? EC_Consts.PARAMETERS_DOMAIN_FP : EC_Consts.PARAMETERS_DOMAIN_F2M;
short domain = (short) (domainAll ^ EC_Consts.PARAMETER_K);
Response.Export export = new Command.Export(cardManager, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.KEY_PUBLIC, domainAll).send();
if (!export.successful()) {
@@ -521,7 +315,7 @@ public class ECTester {
EC_Params exported = new EC_Params(domain, export.getParams());
- FileOutputStream out = new FileOutputStream(optOutput);
+ FileOutputStream out = new FileOutputStream(cfg.output);
exported.writeCSV(out);
out.close();
}
@@ -533,18 +327,18 @@ public class ECTester {
* @throws IOException if an IO error occurs when writing to key file.
*/
private void generate() throws CardException, IOException {
- byte keyClass = optPrimeField ? KeyPair.ALG_EC_FP : KeyPair.ALG_EC_F2M;
+ byte keyClass = cfg.primeField ? KeyPair.ALG_EC_FP : KeyPair.ALG_EC_F2M;
- new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_LOCAL, (short) optBits, keyClass).send();
- List<Command> curve = prepareCurve(ECTesterApplet.KEYPAIR_LOCAL, (short) optBits, keyClass);
+ new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_LOCAL, (short) cfg.bits, keyClass).send();
+ List<Command> curve = prepareCurve(ECTesterApplet.KEYPAIR_LOCAL, (short) cfg.bits, keyClass);
- FileWriter keysFile = new FileWriter(optOutput);
+ FileWriter keysFile = new FileWriter(cfg.output);
keysFile.write("index;time;pubW;privS\n");
int generated = 0;
int retry = 0;
- while (generated < optGenerateAmount || optGenerateAmount == 0) {
- if (optFresh || generated == 0) {
+ while (generated < cfg.generateAmount || cfg.generateAmount == 0) {
+ if (cfg.fresh || generated == 0) {
Command.sendAll(curve);
}
@@ -583,19 +377,20 @@ public class ECTester {
*/
private void test() throws IOException, CardException {
List<Command> commands = new LinkedList<>();
+ TestSuite suite = null;
- if (optTestSuite.equals("default")) {
+ if (cfg.testSuite.equals("default")) {
commands.add(new Command.Support(cardManager));
- if (optNamedCurve != null) {
- if (optPrimeField) {
- commands.addAll(testCurves(optNamedCurve, KeyPair.ALG_EC_FP));
+ if (cfg.namedCurve != null) {
+ if (cfg.primeField) {
+ commands.addAll(testCurves(cfg.namedCurve, KeyPair.ALG_EC_FP));
}
- if (optBinaryField) {
- commands.addAll(testCurves(optNamedCurve, KeyPair.ALG_EC_F2M));
+ if (cfg.binaryField) {
+ commands.addAll(testCurves(cfg.namedCurve, KeyPair.ALG_EC_F2M));
}
} else {
- if (optAll) {
- if (optPrimeField) {
+ if (cfg.all) {
+ if (cfg.primeField) {
//iterate over prime curve sizes used: EC_Consts.FP_SIZES
for (short keyLength : EC_Consts.FP_SIZES) {
commands.add(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, keyLength, KeyPair.ALG_EC_FP));
@@ -604,7 +399,7 @@ public class ECTester {
commands.add(new Command.Cleanup(cardManager));
}
}
- if (optBinaryField) {
+ if (cfg.binaryField) {
//iterate over binary curve sizes used: EC_Consts.F2M_SIZES
for (short keyLength : EC_Consts.F2M_SIZES) {
commands.add(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, keyLength, KeyPair.ALG_EC_F2M));
@@ -614,60 +409,31 @@ public class ECTester {
}
}
} else {
- if (optPrimeField) {
- commands.add(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, (short) optBits, KeyPair.ALG_EC_FP));
- commands.addAll(prepareCurve(ECTesterApplet.KEYPAIR_BOTH, (short) optBits, KeyPair.ALG_EC_FP));
+ if (cfg.primeField) {
+ commands.add(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, (short) cfg.bits, KeyPair.ALG_EC_FP));
+ commands.addAll(prepareCurve(ECTesterApplet.KEYPAIR_BOTH, (short) cfg.bits, KeyPair.ALG_EC_FP));
commands.addAll(testCurve());
commands.add(new Command.Cleanup(cardManager));
}
- if (optBinaryField) {
- commands.add(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, (short) optBits, KeyPair.ALG_EC_F2M));
- commands.addAll(prepareCurve(ECTesterApplet.KEYPAIR_BOTH, (short) optBits, KeyPair.ALG_EC_F2M));
+ if (cfg.binaryField) {
+ commands.add(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, (short) cfg.bits, KeyPair.ALG_EC_F2M));
+ commands.addAll(prepareCurve(ECTesterApplet.KEYPAIR_BOTH, (short) cfg.bits, KeyPair.ALG_EC_F2M));
commands.addAll(testCurve());
commands.add(new Command.Cleanup(cardManager));
}
}
}
- } else if (optTestSuite.equals("test-vectors")) {
+ } else if (cfg.testSuite.equals("test-vectors")) {
/* 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 (optNamedCurve != null && !(result.getCurve().startsWith(optNamedCurve) || result.getCurve().equals(optNamedCurve))) {
- continue;
- }
- if (curve.getBits() != optBits && !optAll) {
- 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 not located");
- }
-
- commands.add(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()));
- commands.add(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()));
- commands.add(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_BOTH));
- commands.add(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.CURVE_external, EC_Consts.PARAMETER_S, onekey.flatten(EC_Consts.PARAMETER_S)));
- commands.add(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, EC_Consts.PARAMETER_W, otherkey.flatten(EC_Consts.PARAMETER_W)));
- commands.add(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_TRUE, EC_Consts.CORRUPTION_NONE, result.getKA()));
- //TODO add compare with result.getParam(0);
- commands.add(new Command.Cleanup(cardManager));
- }
+ suite = new TestSuite.TestVectors(dataStore, cfg);
} else {
// These tests are dangerous, prompt before them.
- System.out.println("The test you selected (" + optTestSuite + ") is potentially dangerous.");
+ 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.print("Do you want to proceed? (y/n): ");
Scanner in = new Scanner(System.in);
@@ -677,62 +443,40 @@ public class ECTester {
}
in.close();
- if (optTestSuite.equals("wrong")) {
+ if (cfg.testSuite.equals("wrong")) {
/* Just do the default tests on the wrong curves.
* These should generally fail, the curves aren't safe.
*/
- if (optPrimeField) {
- commands.addAll(testCurves(optTestSuite, KeyPair.ALG_EC_FP));
+ if (cfg.primeField) {
+ commands.addAll(testCurves(cfg.testSuite, KeyPair.ALG_EC_FP));
}
- if (optBinaryField) {
- commands.addAll(testCurves(optTestSuite, KeyPair.ALG_EC_F2M));
+ if (cfg.binaryField) {
+ commands.addAll(testCurves(cfg.testSuite, KeyPair.ALG_EC_F2M));
}
- } else if (optTestSuite.equals("nonprime")) {
+ } else if (cfg.testSuite.equals("nonprime")) {
/* Do the default tests with the public keys set to provided nonprime keys.
* 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, "nonprime");
- for (EC_Key key : keys.values()) {
- EC_Curve curve = dataStore.getObject(EC_Curve.class, key.getCurve());
- if ((curve.getBits() == optBits || optAll)) {
- commands.add(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()));
- commands.add(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()));
- commands.add(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL));
- commands.add(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, key.getParams(), key.flatten()));
- commands.add(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, EC_Consts.KA_ECDH));
- commands.add(new Command.Cleanup(cardManager));
- }
- }
- } else if (optTestSuite.equals("invalid")) {
+ suite = new TestSuite.NonPrime(dataStore, cfg);
+ } else if (cfg.testSuite.equals("invalid")) {
/* 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");
- for (EC_Key.Public key : pubkeys.values()) {
- EC_Curve curve = dataStore.getObject(EC_Curve.class, key.getCurve());
- if (optNamedCurve != null && !(key.getCurve().startsWith(optNamedCurve) || key.getCurve().equals(optNamedCurve))) {
- continue;
- }
- if (curve.getBits() != optBits && !optAll) {
- continue;
- }
- commands.add(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()));
- commands.add(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()));
- commands.add(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL));
- commands.add(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, key.getParams(), key.flatten()));
- commands.add(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, EC_Consts.KA_BOTH));
- //commands.add(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, EC_Consts.KA_ECDHC));
- commands.add(new Command.Cleanup(cardManager));
-
- }
+ suite = new TestSuite.Invalid(dataStore, cfg);
}
}
List<Response> test = Command.sendAll(commands);
- systemOutLogger.println(Response.toString(test, optTestSuite));
+ if (suite != null) {
+ List<Test> tests = suite.run(cardManager);
+ for (Test t : tests) {
+ System.out.println(t);
+ }
+ }
+ systemOutLogger.println(Response.toString(test, cfg.testSuite));
}
@@ -743,34 +487,34 @@ public class ECTester {
* @throws IOException if an IO error occurs when writing to key file.
*/
private void ecdh() throws IOException, CardException {
- byte keyClass = optPrimeField ? KeyPair.ALG_EC_FP : KeyPair.ALG_EC_F2M;
+ byte keyClass = cfg.primeField ? KeyPair.ALG_EC_FP : KeyPair.ALG_EC_F2M;
List<Response> prepare = new LinkedList<>();
- prepare.add(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, (short) optBits, keyClass).send());
- prepare.addAll(Command.sendAll(prepareCurve(ECTesterApplet.KEYPAIR_BOTH, (short) optBits, keyClass)));
+ prepare.add(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, (short) cfg.bits, keyClass).send());
+ prepare.addAll(Command.sendAll(prepareCurve(ECTesterApplet.KEYPAIR_BOTH, (short) cfg.bits, keyClass)));
systemOutLogger.println(Response.toString(prepare));
- byte pubkey = (optAnyPublic || optAnyKey) ? ECTesterApplet.KEYPAIR_REMOTE : ECTesterApplet.KEYPAIR_LOCAL;
- byte privkey = (optAnyPrivate || optAnyKey) ? ECTesterApplet.KEYPAIR_REMOTE : ECTesterApplet.KEYPAIR_LOCAL;
+ byte pubkey = (cfg.anyPublicKey || cfg.anyKey) ? ECTesterApplet.KEYPAIR_REMOTE : ECTesterApplet.KEYPAIR_LOCAL;
+ byte privkey = (cfg.anyPrivateKey || cfg.anyKey) ? ECTesterApplet.KEYPAIR_REMOTE : ECTesterApplet.KEYPAIR_LOCAL;
List<Command> generate = new LinkedList<>();
generate.add(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_BOTH));
- if (optAnyPublic || optAnyPrivate || optAnyKey) {
+ if (cfg.anyPublicKey || cfg.anyPrivateKey || cfg.anyKey) {
generate.add(prepareKey(ECTesterApplet.KEYPAIR_REMOTE));
}
FileWriter out = null;
- if (optOutput != null) {
- out = new FileWriter(optOutput);
+ if (cfg.output != null) {
+ out = new FileWriter(cfg.output);
out.write("index;time;secret\n");
}
int retry = 0;
int done = 0;
- while (done < optECDHCount) {
+ while (done < cfg.ECDHCount) {
List<Response> ecdh = Command.sendAll(generate);
- Response.ECDH perform = new Command.ECDH(cardManager, pubkey, privkey, ECTesterApplet.EXPORT_TRUE, EC_Consts.CORRUPTION_NONE, optECDHKA).send();
+ Response.ECDH perform = new Command.ECDH(cardManager, pubkey, privkey, ECTesterApplet.EXPORT_TRUE, EC_Consts.CORRUPTION_NONE, cfg.ECDHKA).send();
ecdh.add(perform);
systemOutLogger.println(Response.toString(ecdh));
@@ -804,38 +548,38 @@ public class ECTester {
private void ecdsa() throws CardException, IOException {
//read file, if asked to sign
byte[] data = null;
- if (optInput != null) {
- File in = new File(optInput);
+ if (cfg.input != null) {
+ File in = new File(cfg.input);
long len = in.length();
if (len == 0) {
- throw new FileNotFoundException(optInput);
+ throw new FileNotFoundException(cfg.input);
}
data = Files.readAllBytes(in.toPath());
}
Command generate;
- if (optAnyKeypart) {
+ if (cfg.anyKeypart) {
generate = prepareKey(ECTesterApplet.KEYPAIR_LOCAL);
} else {
generate = new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL);
}
- byte keyClass = optPrimeField ? KeyPair.ALG_EC_FP : KeyPair.ALG_EC_F2M;
+ byte keyClass = cfg.primeField ? KeyPair.ALG_EC_FP : KeyPair.ALG_EC_F2M;
List<Response> prepare = new LinkedList<>();
- prepare.add(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_LOCAL, (short) optBits, keyClass).send());
- prepare.addAll(Command.sendAll(prepareCurve(ECTesterApplet.KEYPAIR_LOCAL, (short) optBits, keyClass)));
+ prepare.add(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_LOCAL, (short) cfg.bits, keyClass).send());
+ prepare.addAll(Command.sendAll(prepareCurve(ECTesterApplet.KEYPAIR_LOCAL, (short) cfg.bits, keyClass)));
systemOutLogger.println(Response.toString(prepare));
FileWriter out = null;
- if (optOutput != null) {
- out = new FileWriter(optOutput);
+ if (cfg.output != null) {
+ out = new FileWriter(cfg.output);
out.write("index;time;signature\n");
}
int retry = 0;
int done = 0;
- while (done < optECDSACount) {
+ while (done < cfg.ECDSACount) {
List<Response> ecdsa = new LinkedList<>();
ecdsa.add(generate.send());
@@ -873,14 +617,14 @@ public class ECTester {
private List<Command> prepareCurve(byte keyPair, short keyLength, byte keyClass) throws IOException {
List<Command> commands = new ArrayList<>();
- if (optCustomCurve) {
+ if (cfg.customCurve) {
// Set custom curve (one of the SECG curves embedded applet-side)
short domainParams = keyClass == KeyPair.ALG_EC_FP ? EC_Consts.PARAMETERS_DOMAIN_FP : EC_Consts.PARAMETERS_DOMAIN_F2M;
commands.add(new Command.Set(cardManager, keyPair, EC_Consts.getCurve(keyLength, keyClass), domainParams, null));
- } else if (optNamedCurve != null) {
+ } else if (cfg.namedCurve != null) {
// Set a named curve.
- // parse optNamedCurve -> cat / id | cat | id
- EC_Curve curve = dataStore.getObject(EC_Curve.class, optNamedCurve);
+ // parse cfg.namedCurve -> cat / id | cat | id
+ EC_Curve curve = dataStore.getObject(EC_Curve.class, cfg.namedCurve);
if (curve == null) {
throw new IOException("Curve could no be found.");
}
@@ -893,11 +637,11 @@ public class ECTester {
throw new IOException("Couldn't read named curve data.");
}
commands.add(new Command.Set(cardManager, keyPair, EC_Consts.CURVE_external, curve.getParams(), external));
- } else if (optCurveFile != null) {
+ } else if (cfg.curveFile != null) {
// Set curve loaded from a file
- EC_Curve curve = new EC_Curve(keyLength, keyClass);
+ EC_Curve curve = new EC_Curve(null, keyLength, keyClass);
- FileInputStream in = new FileInputStream(optCurveFile);
+ FileInputStream in = new FileInputStream(cfg.curveFile);
curve.readCSV(in);
in.close();
@@ -927,17 +671,17 @@ public class ECTester {
short params = EC_Consts.PARAMETERS_NONE;
byte[] data = null;
- if (optKey != null || optNamedKey != null) {
+ if (cfg.key != null || cfg.namedKey != null) {
params |= EC_Consts.PARAMETERS_KEYPAIR;
EC_Params keypair;
- if (optKey != null) {
+ if (cfg.key != null) {
keypair = new EC_Params(EC_Consts.PARAMETERS_KEYPAIR);
- FileInputStream in = new FileInputStream(optKey);
+ FileInputStream in = new FileInputStream(cfg.key);
keypair.readCSV(in);
in.close();
} else {
- keypair = dataStore.getObject(EC_Keypair.class, optNamedKey);
+ keypair = dataStore.getObject(EC_Keypair.class, cfg.namedKey);
}
data = keypair.flatten();
@@ -946,19 +690,19 @@ public class ECTester {
}
}
- if (optPublic != null || optNamedPublic != null) {
+ if (cfg.publicKey != null || cfg.namedPublicKey != null) {
params |= EC_Consts.PARAMETER_W;
EC_Params pub;
- if (optPublic != null) {
+ if (cfg.publicKey != null) {
pub = new EC_Params(EC_Consts.PARAMETER_W);
- FileInputStream in = new FileInputStream(optPublic);
+ FileInputStream in = new FileInputStream(cfg.publicKey);
pub.readCSV(in);
in.close();
} else {
- pub = dataStore.getObject(EC_Key.Public.class, optNamedPublic);
+ pub = dataStore.getObject(EC_Key.Public.class, cfg.namedPublicKey);
if (pub == null) {
- pub = dataStore.getObject(EC_Keypair.class, optNamedPublic);
+ pub = dataStore.getObject(EC_Keypair.class, cfg.namedPublicKey);
}
}
@@ -968,19 +712,19 @@ public class ECTester {
}
data = pubkey;
}
- if (optPrivate != null || optNamedPrivate != null) {
+ if (cfg.privateKey != null || cfg.namedPrivateKey != null) {
params |= EC_Consts.PARAMETER_S;
EC_Params priv;
- if (optPrivate != null) {
+ if (cfg.privateKey != null) {
priv = new EC_Params(EC_Consts.PARAMETER_S);
- FileInputStream in = new FileInputStream(optPrivate);
+ FileInputStream in = new FileInputStream(cfg.privateKey);
priv.readCSV(in);
in.close();
} else {
- priv = dataStore.getObject(EC_Key.Public.class, optNamedPrivate);
+ priv = dataStore.getObject(EC_Key.Public.class, cfg.namedPrivateKey);
if (priv == null) {
- priv = dataStore.getObject(EC_Keypair.class, optNamedPrivate);
+ priv = dataStore.getObject(EC_Keypair.class, cfg.namedPrivateKey);
}
}
@@ -1022,7 +766,7 @@ public class ECTester {
return commands;
for (Map.Entry<String, EC_Curve> entry : curves.entrySet()) {
EC_Curve curve = entry.getValue();
- if (curve.getField() == field && (curve.getBits() == optBits || optAll)) {
+ if (curve.getField() == field && (curve.getBits() == cfg.bits || cfg.all)) {
commands.add(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), field));
commands.add(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()));
commands.addAll(testCurve());
@@ -1037,4 +781,214 @@ public class ECTester {
ECTester app = new ECTester();
app.run(args);
}
+
+ public static class Config {
+
+ //Options
+ public int bits;
+ public boolean all;
+ public boolean primeField = false;
+ public boolean binaryField = false;
+
+ public String namedCurve;
+ public String curveFile;
+ public boolean customCurve = false;
+
+ public boolean anyPublicKey = false;
+ public String namedPublicKey;
+ public String publicKey;
+
+ public boolean anyPrivateKey = false;
+ public String namedPrivateKey;
+ public String privateKey;
+
+ public boolean anyKey = false;
+ public String namedKey;
+ public String key;
+
+ public boolean anyKeypart = false;
+
+ public String log;
+
+ public boolean verbose = false;
+ public String input;
+ public String output;
+ public boolean fresh = false;
+ public boolean simulate = false;
+
+ //Action-related ions
+ public String listNamed;
+ public String testSuite;
+ public int generateAmount;
+ public int ECDHCount;
+ public byte ECDHKA;
+ public int ECDSACount;
+
+ /**
+ * Reads and validates options, also sets defaults.
+ *
+ * @param cli cli object, with parsed args
+ * @return whether the options are valid.
+ */
+ public boolean readOptions(CommandLine cli) {
+ bits = Integer.parseInt(cli.getOptionValue("bit-size", "0"));
+ all = cli.hasOption("all");
+ primeField = cli.hasOption("fp");
+ binaryField = cli.hasOption("f2m");
+
+ namedCurve = cli.getOptionValue("named-curve");
+ customCurve = cli.hasOption("custom");
+ curveFile = cli.getOptionValue("curve");
+
+ namedPublicKey = cli.getOptionValue("named-public");
+ publicKey = cli.getOptionValue("public");
+ anyPublicKey = (publicKey != null) || (namedPublicKey != null);
+
+ namedPrivateKey = cli.getOptionValue("named-private");
+ privateKey = cli.getOptionValue("private");
+ anyPrivateKey = (privateKey != null) || (namedPrivateKey != null);
+
+ namedKey = cli.getOptionValue("named-key");
+ key = cli.getOptionValue("key");
+ anyKey = (key != null) || (namedKey != null);
+ anyKeypart = anyKey || anyPublicKey || anyPrivateKey;
+
+ if (cli.hasOption("log")) {
+ log = cli.getOptionValue("log", String.format("ECTESTER_log_%d.log", System.currentTimeMillis() / 1000));
+ }
+
+ verbose = cli.hasOption("verbose");
+ input = cli.getOptionValue("input");
+ output = cli.getOptionValue("output");
+ fresh = cli.hasOption("fresh");
+ simulate = cli.hasOption("simulate");
+
+ if (cli.hasOption("list-named")) {
+ listNamed = cli.getOptionValue("list-named");
+ return true;
+ }
+
+ if ((key != null || namedKey != null) && (anyPublicKey || anyPrivateKey)) {
+ System.err.print("Can only specify the whole key with --key/--named-key or pubkey and privkey with --public/--named-public and --private/--named-private.");
+ return false;
+ }
+ if (bits < 0) {
+ System.err.println("Bit-size must not be negative.");
+ return false;
+ }
+ if (bits == 0 && !all) {
+ System.err.println("You must specify either bit-size with -b or all bit-sizes with -a.");
+ return false;
+ }
+
+ if (key != null && namedKey != null || publicKey != null && namedPublicKey != null || privateKey != null && namedPrivateKey != null) {
+ System.err.println("You cannot specify both a named key and a key file.");
+ return false;
+ }
+
+ if (cli.hasOption("export")) {
+ if (primeField == binaryField) {
+ System.err.print("Need to specify field with -fp or -f2m. (not both)");
+ return false;
+ }
+ if (anyKeypart) {
+ System.err.println("Keys should not be specified when exporting curve params.");
+ return false;
+ }
+ if (namedCurve != null || customCurve || curveFile != null) {
+ System.err.println("Specifying a curve for curve export makes no sense.");
+ return false;
+ }
+ if (output == null) {
+ System.err.println("You have to specify an output file for curve parameter export.");
+ return false;
+ }
+ if (all) {
+ 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)");
+ return false;
+ }
+ if (anyKeypart) {
+ System.err.println("Keys should not be specified when generating keys.");
+ return false;
+ }
+ if (output == null) {
+ System.err.println("You have to specify an output file for the key generation process.");
+ return false;
+ }
+ if (all) {
+ System.err.println("You have to specify curve bit-size with -b");
+ return false;
+ }
+
+ generateAmount = Integer.parseInt(cli.getOptionValue("generate", "0"));
+ if (generateAmount < 0) {
+ System.err.println("Amount of keys generated cant be negative.");
+ return false;
+ }
+ } else if (cli.hasOption("test")) {
+ if (!binaryField && primeField) {
+ binaryField = true;
+ primeField = true;
+ }
+
+ testSuite = cli.getOptionValue("test", "default").toLowerCase();
+ String[] tests = new String[]{"default", "nonprime", "invalid", "test-vectors", "wrong"};
+ List<String> testsList = Arrays.asList(tests);
+ if (!testsList.contains(testSuite)) {
+ System.err.println("Unknown test case. Should be one of: " + Arrays.toString(tests));
+ return false;
+ }
+
+ } else if (cli.hasOption("ecdh") || cli.hasOption("ecdhc")) {
+ if (primeField == binaryField) {
+ System.err.print("Need to specify field with -fp or -f2m. (not both)");
+ return false;
+ }
+ if (all) {
+ System.err.println("You have to specify curve bit-size with -b");
+ 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) {
+ System.err.println("ECDH count cannot be <= 0.");
+ return false;
+ }
+
+ } else if (cli.hasOption("ecdsa")) {
+ if (primeField == binaryField) {
+ System.err.print("Need to specify field with -fp or -f2m. (but not both)");
+ return false;
+ }
+ if (all) {
+ System.err.println("You have to specify curve bit-size with -b");
+ return false;
+ }
+
+ if ((anyPublicKey) != (anyPrivateKey) && !anyKey) {
+ System.err.println("You cannot only specify a part of a keypair.");
+ return false;
+ }
+
+ ECDSACount = Integer.parseInt(cli.getOptionValue("ecdsa", "1"));
+ if (ECDSACount <= 0) {
+ System.err.println("ECDSA count cannot be <= 0.");
+ return false;
+ }
+ }
+ return true;
+ }
+ }
}
diff --git a/src/cz/crcs/ectester/reader/Response.java b/src/cz/crcs/ectester/reader/Response.java
index ee27260..e5b8c1c 100644
--- a/src/cz/crcs/ectester/reader/Response.java
+++ b/src/cz/crcs/ectester/reader/Response.java
@@ -2,6 +2,7 @@ package cz.crcs.ectester.reader;
import cz.crcs.ectester.applet.ECTesterApplet;
import cz.crcs.ectester.applet.EC_Consts;
+import cz.crcs.ectester.reader.ec.EC_Curve;
import javacard.framework.ISO7816;
import javacard.security.KeyPair;
@@ -12,11 +13,13 @@ import java.util.List;
* @author Jan Jancar johny@neuromancer.sk
*/
public abstract class Response {
+
private ResponseAPDU resp;
private long time;
private short[] sws;
private int numSW = 0;
private byte[][] params;
+ //TODO replace params with EC_Data?
private boolean success = true;
protected Response(ResponseAPDU response, long time) {
@@ -40,9 +43,14 @@ public abstract class Response {
if (sw != ISO7816.SW_NO_ERROR) {
success = false;
}
+ } else {
+ success = false;
}
}
+ if ((short) resp.getSW() != ISO7816.SW_NO_ERROR)
+ success = false;
+
//try to parse numParams..
params = new byte[numParams][];
for (int i = 0; i < numParams; i++) {
@@ -70,16 +78,8 @@ public abstract class Response {
return time;
}
- public int getNaturalSW() {
- return resp.getSW();
- }
-
- public short getSW1() {
- return sws[0];
- }
-
- public short getSW2() {
- return sws[1];
+ public short getNaturalSW() {
+ return (short) resp.getSW();
}
public short getSW(int index) {
@@ -90,15 +90,15 @@ public abstract class Response {
return numSW;
}
- protected boolean hasParam(int index) {
+ public boolean hasParam(int index) {
return params.length >= index + 1 && params[index] != null;
}
- protected int getParamLength(int index) {
+ public int getParamLength(int index) {
return params[index].length;
}
- protected byte[] getParam(int index) {
+ public byte[] getParam(int index) {
return params[index];
}
@@ -117,27 +117,32 @@ public abstract class Response {
@Override
public abstract String toString();
+ public String toString(String inner) {
+ StringBuilder suffix = new StringBuilder();
+ for (int j = 0; j < getNumSW(); ++j) {
+ suffix.append(" ").append(Util.getSWString(getSW(j)));
+ }
+ return String.format("%-62s:%4d ms : %s", inner, time / 1000000, suffix);
+ }
+
public static String toString(List<Response> responses) {
return toString(responses, null);
}
- public static String toString(List<Response> responses, String prefix) {
+ public static String toString(List<Response> responses, String prefix) {
if (prefix != null)
prefix += " | ";
StringBuilder out = new StringBuilder();
for (int i = 0; i < responses.size(); ++i) {
Response r = responses.get(i);
- String message = r.toString();
- String suffix = "";
- for (int j = 0; j < r.getNumSW(); ++j) {
- suffix += " " + Util.getSWString(r.getSW(j));
- }
-
if (prefix != null)
out.append(prefix);
- out.append(String.format("%-62s:%4d ms : %s", message, r.time / 1000000, suffix));
+ String message = r.toString();
+ String full = r.toString(message);
+
+ out.append(full);
if (i < responses.size() - 1) {
out.append("\n");
}
@@ -149,6 +154,7 @@ public abstract class Response {
*
*/
public static class Allocate extends Response {
+
private byte keyPair;
private short keyLength;
private byte keyClass;
@@ -174,11 +180,15 @@ public abstract class Response {
} else {
key = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair";
}
- return String.format("Allocated %s %db %s", key, keyLength, field);
+ return super.toString(String.format("Allocated %s %db %s", key, keyLength, field));
}
}
+ /**
+ *
+ */
public static class Clear extends Response {
+
private byte keyPair;
protected Clear(ResponseAPDU response, long time, byte keyPair) {
@@ -199,7 +209,7 @@ public abstract class Response {
} else {
key = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair";
}
- return String.format("Cleared %s", key);
+ return super.toString(String.format("Cleared %s", key));
}
}
@@ -207,6 +217,7 @@ public abstract class Response {
*
*/
public static class Set extends Response {
+
private byte keyPair;
private byte curve;
private short parameters;
@@ -238,13 +249,24 @@ public abstract class Response {
name = "custom";
break;
}
+ String what = "";
+ if (parameters == EC_Consts.PARAMETERS_DOMAIN_F2M || parameters == EC_Consts.PARAMETERS_DOMAIN_FP) {
+ what = "curve";
+ } else if (parameters == EC_Consts.PARAMETER_W) {
+ what = "pubkey";
+ } else if (parameters == EC_Consts.PARAMETER_S) {
+ what = "privkey";
+ } else if (parameters == EC_Consts.PARAMETERS_KEYPAIR) {
+ what = "keypair";
+ }
+
String pair;
if (keyPair == ECTesterApplet.KEYPAIR_BOTH) {
pair = "both keypairs";
} else {
pair = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair";
}
- return String.format("Set %s curve parameters on %s", name, pair);
+ return super.toString(String.format("Set %s %s parameters on %s", name, what, pair));
}
}
@@ -253,6 +275,7 @@ public abstract class Response {
*
*/
public static class Corrupt extends Response {
+
private byte keyPair;
private byte key;
private short params;
@@ -282,7 +305,7 @@ public abstract class Response {
} else {
pair = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair";
}
- return String.format("Corrupted params of %s, %s", pair, corrupt);
+ return super.toString(String.format("Corrupted params of %s, %s", pair, corrupt));
}
}
@@ -290,6 +313,7 @@ public abstract class Response {
*
*/
public static class Generate extends Response {
+
private byte keyPair;
protected Generate(ResponseAPDU response, long time, byte keyPair) {
@@ -310,7 +334,7 @@ public abstract class Response {
} else {
key = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair";
}
- return String.format("Generated %s", key);
+ return super.toString(String.format("Generated %s", key));
}
}
@@ -319,6 +343,7 @@ public abstract class Response {
*
*/
public static class Export extends Response {
+
private byte keyPair;
private byte key;
private short parameters;
@@ -419,7 +444,7 @@ public abstract class Response {
} else {
pair = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair";
}
- return String.format("Exported params from %s of %s", source, pair);
+ return super.toString(String.format("Exported params from %s of %s", source, pair));
}
}
@@ -427,6 +452,7 @@ public abstract class Response {
*
*/
public static class ECDH extends Response {
+
private byte pubkey;
private byte privkey;
private byte export;
@@ -452,18 +478,13 @@ public abstract class Response {
return getParam(0);
}
+ public int secretLength() {
+ return getParamLength(0);
+ }
+
@Override
public String toString() {
- String algo = "";
- if ((type & EC_Consts.KA_ECDH) != 0) {
- algo += "ECDH";
- }
- if (type == EC_Consts.KA_BOTH) {
- algo += "+";
- }
- if ((type & EC_Consts.KA_ECDHC) != 0) {
- algo += "ECDHC";
- }
+ String algo = Util.getKA(type);
String pub = pubkey == ECTesterApplet.KEYPAIR_LOCAL ? "local" : "remote";
String priv = privkey == ECTesterApplet.KEYPAIR_LOCAL ? "local" : "remote";
@@ -474,7 +495,7 @@ public abstract class Response {
} else {
validity = Util.getCorruption(corruption);
}
- return String.format("%s of %s pubkey and %s privkey(%s point)", algo, pub, priv, validity);
+ return super.toString(String.format("%s of %s pubkey and %s privkey(%s point)", algo, pub, priv, validity));
}
}
@@ -482,6 +503,7 @@ public abstract class Response {
*
*/
public static class ECDSA extends Response {
+
private byte keyPair;
private byte export;
private byte[] raw;
@@ -507,7 +529,7 @@ public abstract class Response {
public String toString() {
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 super.toString(String.format("ECDSA with %s keypair(%s data)", key, data));
}
}
@@ -524,7 +546,7 @@ public abstract class Response {
@Override
public String toString() {
- return "Requested JCSystem object deletion";
+ return super.toString("Requested JCSystem object deletion");
}
}
@@ -537,12 +559,12 @@ public abstract class Response {
protected Support(ResponseAPDU response, long time) {
super(response, time);
- parse(3,0);
+ parse(3, 0);
}
@Override
public String toString() {
- return "Support of ECDH, ECDHC, ECDSA";
+ return super.toString("Support of ECDH, ECDHC, ECDSA");
}
}
}
diff --git a/src/cz/crcs/ectester/reader/Test.java b/src/cz/crcs/ectester/reader/Test.java
new file mode 100644
index 0000000..78efef5
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/Test.java
@@ -0,0 +1,81 @@
+package cz.crcs.ectester.reader;
+
+import javax.smartcardio.CardException;
+import java.util.function.BiFunction;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class Test {
+ private boolean hasRun = false;
+ private BiFunction<Command, Response, Result> callback;
+ private Result result;
+ private Result expected;
+ private Command command;
+ private Response response;
+
+ public Test(Command command, Result expected) {
+ this.command = command;
+ this.expected = expected;
+ }
+
+ public Test(Command command, Result expected, BiFunction<Command, Response, Result> callback) {
+ this(command, expected);
+ this.callback = callback;
+ }
+
+ public Command getCommand() {
+ return command;
+ }
+
+ public Response getResponse() {
+ if (!hasRun) {
+ return null;
+ }
+ return response;
+ }
+
+ public Result getResult() {
+ if (!hasRun) {
+ return null;
+ }
+ return result;
+ }
+
+ public Result getExpected() {
+ return expected;
+ }
+
+ public boolean ok() {
+ return result == expected || expected == Result.ANY;
+ }
+
+ public void run() throws CardException {
+ response = command.send();
+ if (callback != null) {
+ result = callback.apply(command, response);
+ } else {
+ if (response.successful()) {
+ result = Result.SUCCESS;
+ } else {
+ result = Result.FAILURE;
+ }
+ }
+ hasRun = true;
+ }
+
+ @Override
+ public String toString() {
+ if (hasRun) {
+ return (ok() ? "OK " : "NOK") + " " + response.toString();
+ } else {
+ return "";
+ }
+ }
+
+ public enum Result {
+ SUCCESS,
+ FAILURE,
+ ANY
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/TestSuite.java b/src/cz/crcs/ectester/reader/TestSuite.java
new file mode 100644
index 0000000..892e853
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/TestSuite.java
@@ -0,0 +1,167 @@
+package cz.crcs.ectester.reader;
+
+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.ec.*;
+
+import javax.smartcardio.CardException;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public abstract class TestSuite {
+
+ EC_Store dataStore;
+ ECTester.Config cfg;
+ String name;
+ boolean hasRun;
+ List<Test> tests = new LinkedList<>();
+
+ public TestSuite(EC_Store dataStore, ECTester.Config cfg, String name) {
+ this.dataStore = dataStore;
+ this.cfg = cfg;
+ this.name = name;
+ }
+
+ public List<Test> run(CardMngr cardManager) throws IOException, CardException {
+ for (Test t : tests) {
+ t.run();
+ System.out.println(t);
+ }
+ hasRun = true;
+ return tests;
+ }
+
+ public List<Test> getTests() {
+ return Collections.unmodifiableList(tests);
+ }
+
+ public boolean hasRun() {
+ return hasRun;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public static class Default extends TestSuite {
+
+ public Default(EC_Store dataStore, ECTester.Config cfg) {
+ super(dataStore, cfg, "default");
+ }
+
+ @Override
+ public List<Test> run(CardMngr cardManager) {
+ return null;
+ }
+ }
+
+ public static class TestVectors extends TestSuite {
+ public TestVectors(EC_Store dataStore, ECTester.Config cfg) {
+ super(dataStore, cfg, "test");
+ }
+
+ @Override
+ public List<Test> run(CardMngr cardManager) throws IOException, CardException {
+
+ 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;
+ }
+ 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.");
+ }
+
+ tests.add(new Test(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), Test.Result.SUCCESS));
+ tests.add(new Test(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), Test.Result.SUCCESS));
+ //tests.add(new Test(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_BOTH), Test.Result.SUCCESS));
+ tests.add(new Test(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.CURVE_external, EC_Consts.PARAMETER_S, onekey.flatten(EC_Consts.PARAMETER_S)), Test.Result.SUCCESS));
+ tests.add(new Test(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, EC_Consts.PARAMETER_W, otherkey.flatten(EC_Consts.PARAMETER_W)), Test.Result.SUCCESS));
+ tests.add(new Test(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_TRUE, EC_Consts.CORRUPTION_NONE, result.getKA()), Test.Result.SUCCESS, (command, response) -> {
+ Response.ECDH dh = (Response.ECDH) response;
+ if (!dh.successful() || !dh.hasSecret())
+ return Test.Result.FAILURE;
+ if (!Util.compareBytes(dh.getSecret(), 0, result.getParam(0), 0, dh.secretLength())) {
+ return Test.Result.FAILURE;
+ }
+ return Test.Result.SUCCESS;
+ }));
+ tests.add(new Test(new Command.Cleanup(cardManager), Test.Result.ANY));
+
+ }
+ return super.run(cardManager);
+ }
+ }
+
+ public static class NonPrime extends TestSuite {
+
+
+ public NonPrime(EC_Store dataStore, ECTester.Config cfg) {
+ super(dataStore, cfg, "nonprime");
+ }
+
+ @Override
+ public List<Test> run(CardMngr cardManager) throws IOException, CardException {
+ Map<String, EC_Key> keys = dataStore.getObjects(EC_Key.class, "nonprime");
+ for (EC_Key key : keys.values()) {
+ EC_Curve curve = dataStore.getObject(EC_Curve.class, key.getCurve());
+ if ((curve.getBits() == cfg.bits || cfg.all)) {
+ tests.add(new Test(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), Test.Result.SUCCESS));
+ tests.add(new Test(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), Test.Result.ANY));
+ tests.add(new Test(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL), Test.Result.ANY));
+ tests.add(new Test(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, key.getParams(), key.flatten()), Test.Result.ANY));
+ tests.add(new Test(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, EC_Consts.KA_ECDH), Test.Result.FAILURE));
+ tests.add(new Test(new Command.Cleanup(cardManager), Test.Result.ANY));
+ }
+ }
+ return super.run(cardManager);
+ }
+ }
+
+ public static class Invalid extends TestSuite {
+
+ public Invalid(EC_Store dataStore, ECTester.Config cfg) {
+ super(dataStore, cfg, "invalid");
+ }
+
+ @Override
+ public List<Test> run(CardMngr cardManager) throws IOException, CardException {
+ Map<String, EC_Key.Public> pubkeys = dataStore.getObjects(EC_Key.Public.class, "invalid");
+ 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;
+ }
+ tests.add(new Test(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), Test.Result.SUCCESS));
+ tests.add(new Test(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), Test.Result.SUCCESS));
+ tests.add(new Test(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL), Test.Result.SUCCESS));
+ tests.add(new Test(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, key.getParams(), key.flatten()), Test.Result.ANY));
+ tests.add(new Test(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, EC_Consts.KA_BOTH), Test.Result.FAILURE));
+ tests.add(new Test(new Command.Cleanup(cardManager), Test.Result.ANY));
+ }
+ return super.run(cardManager);
+ }
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/Util.java b/src/cz/crcs/ectester/reader/Util.java
index 41a7821..3a6a8ae 100644
--- a/src/cz/crcs/ectester/reader/Util.java
+++ b/src/cz/crcs/ectester/reader/Util.java
@@ -22,6 +22,25 @@ public class Util {
array[offset] = (byte) ((value >> 8) & 0xFF);
}
+ public static boolean compareBytes(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 false;
+ }
+ }
+ return true;
+ }
+
+ 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);
}
@@ -103,6 +122,47 @@ public class Util {
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:
+ return "ECTesterApplet";
+ default:
+ return "?";
+ }
+ }
+
public static String getSWString(short sw) {
if (sw == ISO7816.SW_NO_ERROR) {
return "OK\t(0x9000)";
@@ -193,6 +253,12 @@ public class Util {
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;
default:
str = "unknown";
break;
@@ -229,9 +295,23 @@ public class Util {
corrupt = "INFINITY";
break;
default:
- corrupt = "UNKNOWN";
+ corrupt = "unknown";
break;
}
return corrupt;
}
+
+ public static String getKA(byte ka) {
+ String algo = "";
+ if ((ka & EC_Consts.KA_ECDH) != 0) {
+ algo += "ECDH";
+ }
+ if (ka == EC_Consts.KA_BOTH) {
+ algo += "+";
+ }
+ if ((ka & EC_Consts.KA_ECDHC) != 0) {
+ algo += "ECDHC";
+ }
+ return algo;
+ }
}
diff --git a/src/cz/crcs/ectester/reader/ec/EC_Curve.java b/src/cz/crcs/ectester/reader/ec/EC_Curve.java
index 9b783ec..7d056a5 100644
--- a/src/cz/crcs/ectester/reader/ec/EC_Curve.java
+++ b/src/cz/crcs/ectester/reader/ec/EC_Curve.java
@@ -7,25 +7,27 @@ import javacard.security.KeyPair;
* @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(short bits, byte field, String desc) {
+ 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;
}
diff --git a/src/cz/crcs/ectester/reader/ec/EC_Data.java b/src/cz/crcs/ectester/reader/ec/EC_Data.java
index c55b99d..49b5316 100644
--- a/src/cz/crcs/ectester/reader/ec/EC_Data.java
+++ b/src/cz/crcs/ectester/reader/ec/EC_Data.java
@@ -13,11 +13,12 @@ import java.util.regex.Pattern;
* @author Jan Jancar johny@neuromancer.sk
*/
public class EC_Data {
- private static final Pattern hex = Pattern.compile("(0x|0X)?[a-fA-F\\d]+");
-
+ String id;
int count;
byte[][] data;
+ private static final Pattern HEX = Pattern.compile("(0x|0X)?[a-fA-F\\d]+");
+
EC_Data() {
}
@@ -31,6 +32,20 @@ public class EC_Data {
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;
}
@@ -117,16 +132,29 @@ public class EC_Data {
return false;
}
for (String param : data) {
- if (!hex.matcher(param).matches()) {
+ if (!HEX.matcher(param).matches()) {
return false;
}
}
return readHex(data.toArray(new String[data.size()]));
}
- public boolean readBytes(byte[] data) {
- //TODO
- return false;
+ 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 {
diff --git a/src/cz/crcs/ectester/reader/ec/EC_KAResult.java b/src/cz/crcs/ectester/reader/ec/EC_KAResult.java
index 1385f12..9e92fd9 100644
--- a/src/cz/crcs/ectester/reader/ec/EC_KAResult.java
+++ b/src/cz/crcs/ectester/reader/ec/EC_KAResult.java
@@ -1,6 +1,6 @@
package cz.crcs.ectester.reader.ec;
-import cz.crcs.ectester.applet.EC_Consts;
+import cz.crcs.ectester.reader.Util;
/**
* @author Jan Jancar johny@neuromancer.sk
@@ -22,8 +22,13 @@ public class EC_KAResult extends EC_Data {
this.otherKey = otherKey;
}
- public EC_KAResult(byte ka, String curve, String oneKey, String otherKey, String desc) {
+ 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;
}
@@ -49,8 +54,8 @@ public class EC_KAResult extends EC_Data {
@Override
public String toString() {
- String agreement = ka == EC_Consts.KA_ECDH ? "ECDH" : "ECDHC";
- return agreement + " result over " + curve + ", " + oneKey + " + " + otherKey + (desc == null ? "" : ": " + desc);
+ String algo = Util.getKA(ka);
+ return 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
index ad846b8..85fd652 100644
--- a/src/cz/crcs/ectester/reader/ec/EC_Key.java
+++ b/src/cz/crcs/ectester/reader/ec/EC_Key.java
@@ -20,6 +20,16 @@ public class EC_Key extends EC_Params {
this.desc = desc;
}
+ private EC_Key(String id, short mask, String curve) {
+ this(mask, curve);
+ this.id = id;
+ }
+
+ private EC_Key(String id, short mask, String curve, String desc) {
+ this(mask, curve, desc);
+ this.id = id;
+ }
+
public String getCurve() {
return curve;
}
diff --git a/src/cz/crcs/ectester/reader/ec/EC_Keypair.java b/src/cz/crcs/ectester/reader/ec/EC_Keypair.java
index bf87e9e..4da7218 100644
--- a/src/cz/crcs/ectester/reader/ec/EC_Keypair.java
+++ b/src/cz/crcs/ectester/reader/ec/EC_Keypair.java
@@ -6,7 +6,6 @@ import cz.crcs.ectester.applet.EC_Consts;
* @author Jan Jancar johny@neuromancer.sk
*/
public class EC_Keypair extends EC_Params {
-
private String curve;
private String desc;
diff --git a/src/cz/crcs/ectester/reader/ec/EC_Params.java b/src/cz/crcs/ectester/reader/ec/EC_Params.java
index 00747b1..cee9c16 100644
--- a/src/cz/crcs/ectester/reader/ec/EC_Params.java
+++ b/src/cz/crcs/ectester/reader/ec/EC_Params.java
@@ -4,6 +4,7 @@ import cz.crcs.ectester.applet.EC_Consts;
import cz.crcs.ectester.reader.Util;
import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
@@ -25,6 +26,16 @@ public class EC_Params extends EC_Data {
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;
}
@@ -67,8 +78,14 @@ public class EC_Params extends EC_Data {
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], data[i + 2], data[i + 3]);
- if (param.length != 8)
+ 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) {
@@ -112,9 +129,6 @@ public class EC_Params extends EC_Data {
if (masked == EC_Consts.PARAMETER_F2M) {
//split into m, e1, e2, e3
- if (param.length != 8) {
- throw new RuntimeException("PARAMETER_F2M length is not 8.(should be)");
- }
for (int i = 0; i < 4; ++i) {
out.add(String.format("%04x", Util.getShort(param, i * 2)));
}
@@ -136,4 +150,8 @@ public class EC_Params extends EC_Data {
return out.toArray(new String[out.size()]);
}
+ @Override
+ public String toString() {
+ return String.join(",", expand());
+ }
}