aboutsummaryrefslogtreecommitdiff
path: root/src/cz/crcs/ectester/reader/ECTesterReader.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/cz/crcs/ectester/reader/ECTesterReader.java')
-rw-r--r--src/cz/crcs/ectester/reader/ECTesterReader.java185
1 files changed, 152 insertions, 33 deletions
diff --git a/src/cz/crcs/ectester/reader/ECTesterReader.java b/src/cz/crcs/ectester/reader/ECTesterReader.java
index c94a544..e5e6061 100644
--- a/src/cz/crcs/ectester/reader/ECTesterReader.java
+++ b/src/cz/crcs/ectester/reader/ECTesterReader.java
@@ -26,10 +26,12 @@ import cz.crcs.ectester.applet.ECTesterApplet;
import cz.crcs.ectester.applet.EC_Consts;
import cz.crcs.ectester.common.cli.CLITools;
import cz.crcs.ectester.common.cli.Colors;
+import cz.crcs.ectester.common.ec.EC_Curve;
import cz.crcs.ectester.common.output.OutputLogger;
import cz.crcs.ectester.common.output.TestWriter;
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.common.util.FileUtil;
import cz.crcs.ectester.data.EC_Store;
import cz.crcs.ectester.reader.command.Command;
@@ -46,14 +48,13 @@ import javax.smartcardio.CardException;
import javax.smartcardio.ResponseAPDU;
import javax.xml.parsers.ParserConfigurationException;
import java.io.*;
+import java.math.BigInteger;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.security.Security;
-import java.util.Arrays;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Scanner;
+import java.security.spec.ECParameterSpec;
+import java.util.*;
import java.util.jar.Manifest;
import static cz.crcs.ectester.applet.EC_Consts.KeyAgreement_ALG_EC_SVDP_DH;
@@ -331,10 +332,15 @@ public class ECTesterReader {
opts.addOption(Option.builder("v").longOpt("verbose").desc("Turn on verbose logging.").build());
opts.addOption(Option.builder().longOpt("format").desc("Output format to use. One of: text,yml,xml.").hasArg().argName("format").build());
+ opts.addOption(Option.builder().longOpt("fixed").desc("Generate key(s) only once, keep them for later operations.").build());
+ opts.addOption(Option.builder().longOpt("fixed-private").desc("Generate private key only once, keep it for later ECDH.").build());
+ opts.addOption(Option.builder().longOpt("fixed-public").desc("Generate public key only once, keep it for later ECDH.").build());
opts.addOption(Option.builder("f").longOpt("fresh").desc("Generate fresh keys (set domain parameters before every generation).").build());
+ opts.addOption(Option.builder().longOpt("time").desc("Output better timing values, by running command in dry run mode and normal mode, and subtracting the two.").build());
opts.addOption(Option.builder().longOpt("cleanup").desc("Send the cleanup command trigerring JCSystem.requestObjectDeletion() after some operations.").build());
opts.addOption(Option.builder("s").longOpt("simulate").desc("Simulate a card with jcardsim instead of using a terminal.").build());
opts.addOption(Option.builder("y").longOpt("yes").desc("Accept all warnings and prompts.").build());
+ opts.addOption(Option.builder("to").longOpt("test-options").desc("Test options to use.").hasArg().argName("options").build());
opts.addOption(Option.builder("ka").longOpt("ka-type").desc("Set KeyAgreement object [type], corresponds to JC.KeyAgreement constants.").hasArg().argName("type").optionalArg(true).build());
opts.addOption(Option.builder("sig").longOpt("sig-type").desc("Set Signature object [type], corresponds to JC.Signature constants.").hasArg().argName("type").optionalArg(true).build());
@@ -364,6 +370,8 @@ public class ECTesterReader {
System.out.println("\t" + line);
}
}
+ System.out.println();
+ System.out.println("For more information, look at the documentation at https://github.com/crocs-muni/ECTester.");
}
private void info() throws CardException {
@@ -439,7 +447,7 @@ public class ECTesterReader {
respWriter.outputResponse(allocate);
OutputStreamWriter keysFile = FileUtil.openFiles(cfg.outputs);
- keysFile.write("index;genTime;exportTime;pubW;privS\n");
+ keysFile.write("index;genTime[milli];exportTime[milli];pubW;privS\n");
int generated = 0;
int retry = 0;
@@ -450,7 +458,12 @@ public class ECTesterReader {
}
Command.Generate generate = new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL);
+ long time = 0;
+ if (cfg.time) {
+ time = -Command.dryRunTime(cardManager, generate, 2, respWriter);
+ }
Response.Generate response = generate.send();
+ time += response.getDuration();
respWriter.outputResponse(response);
Response.Export export = new Command.Export(cardManager, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.KEY_BOTH, EC_Consts.PARAMETERS_KEYPAIR).send();
@@ -468,7 +481,7 @@ public class ECTesterReader {
String pub = ByteUtil.bytesToHex(export.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_W), false);
String priv = ByteUtil.bytesToHex(export.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_S), false);
- String line = String.format("%d;%d;%d;%s;%s\n", generated, response.getDuration() / 1000000, export.getDuration() / 1000000, pub, priv);
+ String line = String.format("%d;%d;%d;%s;%s\n", generated, time / 1000000, export.getDuration() / 1000000, pub, priv);
keysFile.write(line);
keysFile.flush();
generated++;
@@ -572,38 +585,63 @@ public class ECTesterReader {
respWriter.outputResponse(r);
}
- 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 (cfg.anyPublicKey || cfg.anyPrivateKey || cfg.anyKey) {
- generate.add(Command.prepareKey(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_REMOTE));
- }
-
OutputStreamWriter out = null;
if (cfg.outputs != null) {
out = FileUtil.openFiles(cfg.outputs);
- out.write("index;time;pubW;privS;secret\n");
+ out.write("index;time[milli];pubW;privS;secret\n");
+ }
+
+ Response gen = new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_BOTH).send();
+ respWriter.outputResponse(gen);
+ if (cfg.anyPublicKey || cfg.anyKey) {
+ Response prep = Command.prepareKey(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_REMOTE).send();
+ respWriter.outputResponse(prep);
+ }
+ if (cfg.anyPrivateKey || cfg.anyKey) {
+ Response prep = Command.prepareKey(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_LOCAL).send();
+ respWriter.outputResponse(prep);
+ }
+
+ byte kp = ECTesterApplet.KEYPAIR_BOTH;
+ if (cfg.fixedPrivate || cfg.anyPrivateKey) {
+ kp ^= ECTesterApplet.KEYPAIR_LOCAL;
+ }
+ if (cfg.fixedPublic || cfg.anyPublicKey) {
+ kp ^= ECTesterApplet.KEYPAIR_REMOTE;
+ }
+ if (cfg.fixedKey || cfg.anyKey) {
+ kp = 0;
+ }
+
+ Command generate = null;
+ if (kp != 0) {
+ generate = new Command.Generate(cardManager, kp);
}
int retry = 0;
int done = 0;
while (done < cfg.ECKACount) {
- List<Response> ecdh = Command.sendAll(generate);
- for (Response r : ecdh) {
- respWriter.outputResponse(r);
+ if (generate != null) {
+ Response regen = generate.send();
+ respWriter.outputResponse(regen);
}
Response.Export export = new Command.Export(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.KEY_BOTH, EC_Consts.PARAMETERS_KEYPAIR).send();
respWriter.outputResponse(export);
- byte pubkey_bytes[] = export.getParameter(pubkey, EC_Consts.PARAMETER_W);
- byte privkey_bytes[] = export.getParameter(privkey, EC_Consts.PARAMETER_S);
+ byte[] pubkey_bytes = export.getParameter(ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.PARAMETER_W);
+ byte[] privkey_bytes = export.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_S);
+
+ Command.ECDH perform = new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_TRUE, EC_Consts.TRANSFORMATION_NONE, cfg.ECKAType);
- Response.ECDH perform = new Command.ECDH(cardManager, pubkey, privkey, ECTesterApplet.EXPORT_TRUE, EC_Consts.TRANSFORMATION_NONE, cfg.ECKAType).send();
- respWriter.outputResponse(perform);
+ long time = 0;
+ if (cfg.time) {
+ time = -Command.dryRunTime(cardManager, perform, 2, respWriter);
+ }
+
+ Response.ECDH result = perform.send();
+ respWriter.outputResponse(result);
- if (!perform.successful() || !perform.hasSecret()) {
+ if (!result.successful() || !result.hasSecret()) {
if (retry < 10) {
++retry;
continue;
@@ -614,7 +652,9 @@ public class ECTesterReader {
}
if (out != null) {
- out.write(String.format("%d;%d;%s;%s;%s\n", done, perform.getDuration() / 1000000, ByteUtil.bytesToHex(pubkey_bytes, false), ByteUtil.bytesToHex(privkey_bytes, false), ByteUtil.bytesToHex(perform.getSecret(), false)));
+ time += result.getDuration();
+
+ out.write(String.format("%d;%d;%s;%s;%s\n", done, time / 1000000, ByteUtil.bytesToHex(pubkey_bytes, false), ByteUtil.bytesToHex(privkey_bytes, false), ByteUtil.bytesToHex(result.getSecret(), false)));
}
++done;
@@ -636,7 +676,7 @@ public class ECTesterReader {
*/
private void ecdsa() throws CardException, IOException {
//read file, if asked to sign
- byte[] data = null;
+ byte[] data;
if (cfg.input != null) {
File in = new File(cfg.input);
long len = in.length();
@@ -644,6 +684,10 @@ public class ECTesterReader {
throw new FileNotFoundException(cfg.input);
}
data = Files.readAllBytes(in.toPath());
+ } else {
+ Random rand = new Random();
+ data = new byte[32];
+ rand.nextBytes(data);
}
Command generate;
@@ -667,18 +711,56 @@ public class ECTesterReader {
OutputStreamWriter out = FileUtil.openFiles(cfg.outputs);
if (out != null) {
- out.write("index;time;signature\n");
+ out.write("index;signTime[milli];verifyTime[milli];data;pubW;privS;signature;nonce;valid\n");
+ }
+
+ Command.Export export = new Command.Export(cardManager, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.KEY_BOTH, EC_Consts.PARAMETERS_KEYPAIR);
+ Response.Export exported = null;
+ if (cfg.fixedKey) {
+ respWriter.outputResponse(generate.send());
+ exported = export.send();
+ respWriter.outputResponse(exported);
}
int retry = 0;
int done = 0;
while (done < cfg.ECDSACount) {
- respWriter.outputResponse(generate.send());
+ if (!cfg.fixedKey) {
+ respWriter.outputResponse(generate.send());
+ exported = export.send();
+ respWriter.outputResponse(exported);
+ }
+
+ Command.ECDSA_sign sign = new Command.ECDSA_sign(cardManager, ECTesterApplet.KEYPAIR_LOCAL, cfg.ECDSAType, ECTesterApplet.EXPORT_TRUE, data);
- Response.ECDSA perform = new Command.ECDSA(cardManager, ECTesterApplet.KEYPAIR_LOCAL, cfg.ECDSAType, ECTesterApplet.EXPORT_TRUE, data).send();
- respWriter.outputResponse(perform);
+ long signTime = 0;
+ if (cfg.time) {
+ signTime = -Command.dryRunTime(cardManager, sign, 2, respWriter);
+ }
- if (!perform.successful() || !perform.hasSignature()) {
+ Response.ECDSA signResp = sign.send();
+ signTime += signResp.getDuration();
+ respWriter.outputResponse(signResp);
+ if (!signResp.successful() || !signResp.hasSignature()) {
+ if (retry < 10) {
+ ++retry;
+ continue;
+ } else {
+ System.err.println(Colors.error("Couldn't obtain ECDSA signature from card response."));
+ break;
+ }
+ }
+ byte[] signature = signResp.getSignature();
+ Command.ECDSA_verify verify = new Command.ECDSA_verify(cardManager, ECTesterApplet.KEYPAIR_LOCAL, cfg.ECDSAType, data, signature);
+ long verifyTime = 0;
+ if (cfg.time) {
+ verifyTime = -Command.dryRunTime(cardManager, verify, 2, respWriter);
+ }
+ Response.ECDSA verifyResp = verify.send();
+ verifyTime += verifyResp.getDuration();
+ respWriter.outputResponse(verifyResp);
+
+ if (verifyResp.error()) {
if (retry < 10) {
++retry;
continue;
@@ -689,7 +771,20 @@ public class ECTesterReader {
}
if (out != null) {
- out.write(String.format("%d;%d;%s\n", done, perform.getDuration() / 1000000, ByteUtil.bytesToHex(perform.getSignature(), false)));
+ String pub = ByteUtil.bytesToHex(exported.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_W), false);
+ String priv = ByteUtil.bytesToHex(exported.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_S), false);
+ String dataString = (cfg.input != null) ? "" : ByteUtil.bytesToHex(data, false);
+ BigInteger privkey = new BigInteger(1, exported.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_S));
+ EC_Curve actualCurve = Command.findCurve(EC_Store.getInstance(), cfg, cfg.bits, keyClass);
+ String k = "";
+ if (actualCurve != null) {
+ ECParameterSpec params = actualCurve.toSpec();
+ BigInteger kValue = ECUtil.recoverSignatureNonce(signature, data, privkey, params, CardUtil.getSigHashAlgo(cfg.ECDSAType));
+ if (kValue != null) {
+ k = ByteUtil.bytesToHex(kValue.toByteArray(), false);
+ }
+ }
+ out.write(String.format("%d;%d;%d;%s;%s;%s;%s;%s;%d\n", done, signTime / 1000000, verifyTime / 1000000, dataString, pub, priv, ByteUtil.bytesToHex(signature, false), k, verifyResp.successful() ? 1 : 0));
}
++done;
@@ -733,6 +828,9 @@ public class ECTesterReader {
public String key;
public boolean anyKeypart = false;
+ public boolean fixedKey = false;
+ public boolean fixedPrivate = false;
+ public boolean fixedPublic = false;
public String log;
@@ -740,6 +838,7 @@ public class ECTesterReader {
public String input;
public String[] outputs;
public boolean fresh = false;
+ public boolean time = false;
public boolean cleanup = false;
public boolean simulate = false;
public boolean yes = false;
@@ -756,6 +855,7 @@ public class ECTesterReader {
public byte ECKAType = KeyAgreement_ALG_EC_SVDP_DH;
public int ECDSACount;
public byte ECDSAType = Signature_ALG_ECDSA_SHA;
+ public Set<String> testOptions;
/**
* Reads and validates options, also sets defaults.
@@ -786,6 +886,9 @@ public class ECTesterReader {
key = cli.getOptionValue("key");
anyKey = (key != null) || (namedKey != null);
anyKeypart = anyKey || anyPublicKey || anyPrivateKey;
+ fixedKey = cli.hasOption("fixed");
+ fixedPrivate = cli.hasOption("fixed-private");
+ fixedPublic = cli.hasOption("fixed-public");
if (cli.hasOption("log")) {
log = cli.getOptionValue("log", String.format("ECTESTER_log_%d.log", System.currentTimeMillis() / 1000));
@@ -795,6 +898,7 @@ public class ECTesterReader {
input = cli.getOptionValue("input");
outputs = cli.getOptionValues("output");
fresh = cli.hasOption("fresh");
+ time = cli.hasOption("time");
cleanup = cli.hasOption("cleanup");
simulate = cli.hasOption("simulate");
yes = cli.hasOption("yes");
@@ -807,7 +911,7 @@ public class ECTesterReader {
}
format = cli.getOptionValue("format");
- String formats[] = new String[]{"text", "xml", "yaml", "yml"};
+ String[] formats = new String[]{"text", "xml", "yaml", "yml"};
if (format != null && !Arrays.asList(formats).contains(format)) {
System.err.println(Colors.error("Wrong output format " + format + ". Should be one of " + Arrays.toString(formats)));
return false;
@@ -906,6 +1010,21 @@ public class ECTesterReader {
System.err.println(Colors.error("Unknown test suite " + testSuite + ". Should be one of: " + Arrays.toString(tests)));
return false;
}
+
+ String[] opts = cli.getOptionValue("test-options", "").split(",");
+ List<String> validOpts = Arrays.asList("preset");
+ testOptions = new HashSet<>();
+ for (String opt : opts) {
+ if (opt.equals("")) {
+ continue;
+ }
+ if (!validOpts.contains(opt)) {
+ System.err.println(Colors.error("Unknown test option " + opt + ". Should be one of: " + "preset."));
+ return false;
+ } else {
+ testOptions.add(opt);
+ }
+ }
} else if (cli.hasOption("ecdh")) {
if (primeField == binaryField) {
System.err.print(Colors.error("Need to specify field with -fp or -f2m. (not both)"));