From e78bd5d010bd6ced2b71d83b88748f9cc8d98d5e Mon Sep 17 00:00:00 2001 From: J08nY Date: Fri, 13 Oct 2017 21:42:49 +0200 Subject: Reorganize reader packages. --- src/cz/crcs/ectester/reader/Command.java | 588 -------------------- src/cz/crcs/ectester/reader/ECTester.java | 3 + src/cz/crcs/ectester/reader/Response.java | 591 -------------------- src/cz/crcs/ectester/reader/Test.java | 82 --- src/cz/crcs/ectester/reader/TestSuite.java | 314 ----------- src/cz/crcs/ectester/reader/command/Command.java | 592 +++++++++++++++++++++ src/cz/crcs/ectester/reader/response/Response.java | 591 ++++++++++++++++++++ src/cz/crcs/ectester/reader/test/Test.java | 85 +++ src/cz/crcs/ectester/reader/test/TestSuite.java | 320 +++++++++++ 9 files changed, 1591 insertions(+), 1575 deletions(-) delete mode 100644 src/cz/crcs/ectester/reader/Command.java delete mode 100644 src/cz/crcs/ectester/reader/Response.java delete mode 100644 src/cz/crcs/ectester/reader/Test.java delete mode 100644 src/cz/crcs/ectester/reader/TestSuite.java create mode 100644 src/cz/crcs/ectester/reader/command/Command.java create mode 100644 src/cz/crcs/ectester/reader/response/Response.java create mode 100644 src/cz/crcs/ectester/reader/test/Test.java create mode 100644 src/cz/crcs/ectester/reader/test/TestSuite.java (limited to 'src') diff --git a/src/cz/crcs/ectester/reader/Command.java b/src/cz/crcs/ectester/reader/Command.java deleted file mode 100644 index cf05972..0000000 --- a/src/cz/crcs/ectester/reader/Command.java +++ /dev/null @@ -1,588 +0,0 @@ -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.EC_Curve; -import cz.crcs.ectester.reader.ec.EC_Key; -import cz.crcs.ectester.reader.ec.EC_Keypair; -import cz.crcs.ectester.reader.ec.EC_Params; -import javacard.security.KeyPair; - -import javax.smartcardio.CardException; -import javax.smartcardio.CommandAPDU; -import javax.smartcardio.ResponseAPDU; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/** - * @author Jan Jancar johny@neuromancer.sk - */ -public abstract class Command { - CommandAPDU cmd; - CardMngr cardManager; - - Command(CardMngr cardManager) { - this.cardManager = cardManager; - } - - public CommandAPDU getAPDU() { - return cmd; - } - - public abstract Response send() throws CardException; - - public static List sendAll(List commands) throws CardException { - List result = new ArrayList<>(); - for (Command cmd : commands) { - result.add(cmd.send()); - } - return result; - } - - - /** - * @param keyPair which keyPair/s (local/remote) to set curve domain parameters on - * @param keyLength key length to choose - * @param keyClass key class to choose - * @return a Command to send in order to prepare the curve on the keypairs. - * @throws IOException if curve file cannot be found/opened - */ - public static Command prepareCurve(CardMngr cardManager, EC_Store dataStore, ECTester.Config cfg, byte keyPair, short keyLength, byte keyClass) throws IOException { - - 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; - return new Command.Set(cardManager, keyPair, EC_Consts.getCurve(keyLength, keyClass), domainParams, null); - } else if (cfg.namedCurve != null) { - // Set a named curve. - // 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."); - } - if (curve.getBits() != keyLength) { - throw new IOException("Curve bits mismatch: " + curve.getBits() + " vs " + keyLength + " entered."); - } - if (curve.getField() != keyClass) { - throw new IOException("Curve field mismatch."); - } - - byte[] external = curve.flatten(); - if (external == null) { - throw new IOException("Couldn't read named curve data."); - } - return new Command.Set(cardManager, keyPair, EC_Consts.CURVE_external, curve.getParams(), external); - } else if (cfg.curveFile != null) { - // Set curve loaded from a file - EC_Curve curve = new EC_Curve(null, keyLength, keyClass); - - FileInputStream in = new FileInputStream(cfg.curveFile); - curve.readCSV(in); - in.close(); - - byte[] external = curve.flatten(); - if (external == null) { - throw new IOException("Couldn't read the curve file correctly."); - } - return new Command.Set(cardManager, keyPair, EC_Consts.CURVE_external, curve.getParams(), external); - } else { - // Set default curve - /* This command was generally causing problems for simulating on jcardsim. - * Since there, .clearKey() resets all the keys values, even the domain. - * This might break some other stuff.. But should not. - */ - //commands.add(new Command.Clear(cardManager, keyPair)); - return null; - } - } - - - /** - * @param keyPair which keyPair/s to set the key params on - * @return a CommandAPDU setting params loaded on the keyPair/s - * @throws IOException if any of the key files cannot be found/opened - */ - public static Command prepareKey(CardMngr cardManager, EC_Store dataStore, ECTester.Config cfg, byte keyPair) throws IOException { - short params = EC_Consts.PARAMETERS_NONE; - byte[] data = null; - - if (cfg.key != null || cfg.namedKey != null) { - params |= EC_Consts.PARAMETERS_KEYPAIR; - EC_Params keypair; - if (cfg.key != null) { - keypair = new EC_Params(EC_Consts.PARAMETERS_KEYPAIR); - - FileInputStream in = new FileInputStream(cfg.key); - keypair.readCSV(in); - in.close(); - } else { - keypair = dataStore.getObject(EC_Keypair.class, cfg.namedKey); - } - - data = keypair.flatten(); - if (data == null) { - throw new IOException("Couldn't read the key file correctly."); - } - } - - if (cfg.publicKey != null || cfg.namedPublicKey != null) { - params |= EC_Consts.PARAMETER_W; - EC_Params pub; - if (cfg.publicKey != null) { - pub = new EC_Params(EC_Consts.PARAMETER_W); - - FileInputStream in = new FileInputStream(cfg.publicKey); - pub.readCSV(in); - in.close(); - } else { - pub = dataStore.getObject(EC_Key.Public.class, cfg.namedPublicKey); - if (pub == null) { - pub = dataStore.getObject(EC_Keypair.class, cfg.namedPublicKey); - } - } - - byte[] pubkey = pub.flatten(EC_Consts.PARAMETER_W); - if (pubkey == null) { - throw new IOException("Couldn't read the public key file correctly."); - } - data = pubkey; - } - if (cfg.privateKey != null || cfg.namedPrivateKey != null) { - params |= EC_Consts.PARAMETER_S; - EC_Params priv; - if (cfg.privateKey != null) { - priv = new EC_Params(EC_Consts.PARAMETER_S); - - FileInputStream in = new FileInputStream(cfg.privateKey); - priv.readCSV(in); - in.close(); - } else { - priv = dataStore.getObject(EC_Key.Public.class, cfg.namedPrivateKey); - if (priv == null) { - priv = dataStore.getObject(EC_Keypair.class, cfg.namedPrivateKey); - } - } - - byte[] privkey = priv.flatten(EC_Consts.PARAMETER_S); - if (privkey == null) { - throw new IOException("Couldn't read the private key file correctly."); - } - data = Util.concatenate(data, privkey); - } - return new Command.Set(cardManager, keyPair, EC_Consts.CURVE_external, params, data); - } - - - /** - * - */ - public static class Allocate extends Command { - private byte keyPair; - private short keyLength; - private byte keyClass; - - /** - * Creates the INS_ALLOCATE instruction. - * - * @param cardManager cardManager to send APDU through - * @param keyPair which keyPair to use, local/remote (KEYPAIR_* | ...) - * @param keyLength key length to set - * @param keyClass key class to allocate - */ - protected Allocate(CardMngr cardManager, byte keyPair, short keyLength, byte keyClass) { - super(cardManager); - this.keyPair = keyPair; - this.keyLength = keyLength; - this.keyClass = keyClass; - - byte[] data = new byte[]{0, 0, keyClass}; - Util.setShort(data, 0, keyLength); - this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_ALLOCATE, keyPair, 0x00, data); - } - - @Override - public Response.Allocate send() throws CardException { - long elapsed = -System.nanoTime(); - ResponseAPDU response = cardManager.send(cmd); - elapsed += System.nanoTime(); - return new Response.Allocate(response, elapsed, keyPair, keyLength, keyClass); - } - } - - public static class AllocateKeyAgreement extends Command { - - private byte kaType; - - - /** - * Creates the INS_ALLOCATE_KA instruction. - * - * @param cardManager cardManager to send APDU through - * @param kaType which type of KeyAgreement to use - */ - protected AllocateKeyAgreement(CardMngr cardManager, byte kaType) { - super(cardManager); - this.kaType = kaType; - byte[] data = new byte[]{kaType}; - this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_ALLOCATE_KA, 0x00, 0x00, data); - } - - @Override - public Response.AllocateKeyAgreement send() throws CardException { - long elapsed = -System.nanoTime(); - ResponseAPDU response = cardManager.send(cmd); - elapsed += System.nanoTime(); - return new Response.AllocateKeyAgreement(response, elapsed, kaType); - } - } - - /** - * - */ - public static class Clear extends Command { - private byte keyPair; - - /** - * @param cardManager cardManager to send APDU through - * @param keyPair which keyPair clear, local/remote (KEYPAIR_* || ...) - */ - protected Clear(CardMngr cardManager, byte keyPair) { - super(cardManager); - this.keyPair = keyPair; - - this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_CLEAR, keyPair, 0x00); - } - - @Override - public Response.Clear send() throws CardException { - long elapsed = -System.nanoTime(); - ResponseAPDU response = cardManager.send(cmd); - elapsed += System.nanoTime(); - return new Response.Clear(response, elapsed, keyPair); - } - } - - /** - * - */ - public static class Set extends Command { - private byte keyPair; - private byte curve; - private short params; - private byte[] external; - - /** - * Creates the INS_SET instruction. - * - * @param cardManager cardManager to send APDU through - * @param keyPair which keyPair to set params on, local/remote (KEYPAIR_* || ...) - * @param curve curve to set (EC_Consts.CURVE_*) - * @param params parameters to set (EC_Consts.PARAMETER_* | ...) - * @param external external curve data, can be null - */ - protected Set(CardMngr cardManager, byte keyPair, byte curve, short params, byte[] external) { - super(cardManager); - this.keyPair = keyPair; - this.curve = curve; - this.params = params; - this.external = external; - - int len = external != null ? 2 + external.length : 2; - byte[] data = new byte[len]; - Util.setShort(data, 0, params); - if (external != null) { - System.arraycopy(external, 0, data, 2, external.length); - } - - this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_SET, keyPair, curve, data); - } - - @Override - public Response.Set send() throws CardException { - long elapsed = -System.nanoTime(); - ResponseAPDU response = cardManager.send(cmd); - elapsed += System.nanoTime(); - return new Response.Set(response, elapsed, keyPair, curve, params); - } - } - - /** - * - */ - public static class Corrupt extends Command { - private byte keyPair; - private byte key; - private short params; - private byte corruption; - - /** - * @param cardManager cardManager to send APDU through - * @param keyPair which keyPair to corrupt, local/remote (KEYPAIR_* || ...) - * @param key key to corrupt (EC_Consts.KEY_* | ...) - * @param params parameters to corrupt (EC_Consts.PARAMETER_* | ...) - * @param corruption corruption type (EC_Consts.CORRUPTION_*) - */ - protected Corrupt(CardMngr cardManager, byte keyPair, byte key, short params, byte corruption) { - super(cardManager); - this.keyPair = keyPair; - this.key = key; - this.params = params; - this.corruption = corruption; - - byte[] data = new byte[3]; - Util.setShort(data, 0, params); - data[2] = corruption; - - this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_CORRUPT, keyPair, key, data); - } - - @Override - public Response.Corrupt send() throws CardException { - long elapsed = -System.nanoTime(); - ResponseAPDU response = cardManager.send(cmd); - elapsed += System.nanoTime(); - return new Response.Corrupt(response, elapsed, keyPair, key, params, corruption); - } - } - - /** - * - */ - public static class Generate extends Command { - private byte keyPair; - - /** - * Creates the INS_GENERATE instruction. - * - * @param cardManager cardManager to send APDU through - * @param keyPair which keyPair to generate, local/remote (KEYPAIR_* || ...) - */ - protected Generate(CardMngr cardManager, byte keyPair) { - super(cardManager); - this.keyPair = keyPair; - - this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_GENERATE, keyPair, 0); - } - - @Override - public Response.Generate send() throws CardException { - long elapsed = -System.nanoTime(); - ResponseAPDU response = cardManager.send(cmd); - elapsed += System.nanoTime(); - return new Response.Generate(response, elapsed, keyPair); - } - } - - /** - * - */ - public static class Export extends Command { - private byte keyPair; - private byte key; - private short params; - - /** - * Creates the INS_EXPORT instruction. - * - * @param cardManager cardManager to send APDU through - * @param keyPair keyPair to export from (KEYPAIR_* | ...) - * @param key key to export from (EC_Consts.KEY_* | ...) - * @param params params to export (EC_Consts.PARAMETER_* | ...) - */ - protected Export(CardMngr cardManager, byte keyPair, byte key, short params) { - super(cardManager); - this.keyPair = keyPair; - this.key = key; - this.params = params; - - byte[] data = new byte[2]; - Util.setShort(data, 0, params); - - this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_EXPORT, keyPair, key, data); - } - - @Override - public Response.Export send() throws CardException { - long elapsed = -System.nanoTime(); - ResponseAPDU response = cardManager.send(cmd); - elapsed += System.nanoTime(); - return new Response.Export(response, elapsed, keyPair, key, params); - } - } - - /** - * - */ - public static class ECDH extends Command { - private byte pubkey; - private byte privkey; - private byte export; - private short corruption; - private byte type; - - /** - * Creates the INS_ECDH instruction. - * - * @param cardManager cardManager to send APDU through - * @param pubkey keyPair to use for public key, (KEYPAIR_LOCAL || KEYPAIR_REMOTE) - * @param privkey keyPair to use for private key, (KEYPAIR_LOCAL || KEYPAIR_REMOTE) - * @param export whether to export ECDH secret - * @param corruption whether to invalidate the pubkey before ECDH (EC_Consts.CORRUPTION_* | ...) - * @param type ECDH algorithm type (EC_Consts.KA_* | ...) - */ - protected ECDH(CardMngr cardManager, byte pubkey, byte privkey, byte export, short corruption, byte type) { - super(cardManager); - this.pubkey = pubkey; - this.privkey = privkey; - this.export = export; - this.corruption = corruption; - this.type = type; - - byte[] data = new byte[]{export, 0,0, type}; - Util.setShort(data, 1, corruption); - - this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_ECDH, pubkey, privkey, data); - } - - @Override - public Response.ECDH send() throws CardException { - long elapsed = -System.nanoTime(); - ResponseAPDU response = cardManager.send(cmd); - elapsed += System.nanoTime(); - return new Response.ECDH(response, elapsed, pubkey, privkey, export, corruption, type); - } - } - - /** - * - */ - public static class ECDH_direct extends Command { - private byte privkey; - private byte export; - private short corruption; - private byte type; - private byte[] pubkey; - - /** - * Creates the INS_ECDH_DIRECT instruction. - * - * @param cardManager cardManager to send APDU through - * @param privkey keyPair to use for private key, (KEYPAIR_LOCAL || KEYPAIR_REMOTE) - * @param export whether to export ECDH secret - * @param corruption whether to invalidate the pubkey before ECDH (EC_Consts.CORRUPTION_* | ...) - * @param type ECDH algorithm type (EC_Consts.KA_* | ...) - * @param pubkey pubkey data to do ECDH with. - */ - protected ECDH_direct(CardMngr cardManager, byte privkey, byte export, short corruption, byte type, byte[] pubkey) { - super(cardManager); - this.privkey = privkey; - this.export = export; - this.corruption = corruption; - this.type = type; - this.pubkey = pubkey; - - byte[] data = new byte[3 + pubkey.length]; - Util.setShort(data, 0, corruption); - data[2] = type; - System.arraycopy(pubkey, 0, data, 3, pubkey.length); - - this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_ECDH_DIRECT, privkey, export, data); - } - - @Override - public Response.ECDH send() throws CardException { - long elapsed = -System.nanoTime(); - ResponseAPDU response = cardManager.send(cmd); - elapsed += System.nanoTime(); - return new Response.ECDH(response, elapsed, ECTesterApplet.KEYPAIR_REMOTE, privkey, export, corruption, type); - } - } - - public static class ECDSA extends Command { - private byte keyPair; - private byte export; - private byte[] raw; - - /** - * Creates the INS_ECDSA instruction. - * - * @param cardManager cardManager to send APDU through - * @param keyPair keyPair to use for signing and verification (KEYPAIR_LOCAL || KEYPAIR_REMOTE) - * @param export whether to export ECDSA signature - * @param raw data to sign, can be null, in which case random data is signed. - */ - protected ECDSA(CardMngr cardManager, byte keyPair, byte export, byte[] raw) { - super(cardManager); - this.keyPair = keyPair; - this.export = export; - this.raw = raw; - - int len = raw != null ? raw.length : 0; - byte[] data = new byte[2 + len]; - Util.setShort(data, 0, (short) len); - if (raw != null) { - System.arraycopy(raw, 0, data, 2, len); - } - - this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_ECDSA, keyPair, export, data); - } - - @Override - public Response.ECDSA send() throws CardException { - long elapsed = -System.nanoTime(); - ResponseAPDU response = cardManager.send(cmd); - elapsed += System.nanoTime(); - return new Response.ECDSA(response, elapsed, keyPair, export, raw); - } - } - - /** - * - */ - public static class Cleanup extends Command { - - /** - * @param cardManager cardManager to send APDU through - */ - protected Cleanup(CardMngr cardManager) { - super(cardManager); - - this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_CLEANUP, 0, 0); - } - - @Override - public Response.Cleanup send() throws CardException { - long elapsed = -System.nanoTime(); - ResponseAPDU response = cardManager.send(cmd); - elapsed += System.nanoTime(); - return new Response.Cleanup(response, elapsed); - } - } - - /** - * - */ - public static class Support extends Command { - - /** - * @param cardManager cardManager to send APDU through - */ - protected Support(CardMngr cardManager) { - super(cardManager); - - this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_SUPPORT, 0, 0); - } - - @Override - public Response.Support send() throws CardException { - long elapsed = -System.nanoTime(); - ResponseAPDU response = cardManager.send(cmd); - elapsed += System.nanoTime(); - return new Response.Support(response, elapsed); - } - } -} - diff --git a/src/cz/crcs/ectester/reader/ECTester.java b/src/cz/crcs/ectester/reader/ECTester.java index ea2cbe4..a43c945 100644 --- a/src/cz/crcs/ectester/reader/ECTester.java +++ b/src/cz/crcs/ectester/reader/ECTester.java @@ -25,9 +25,12 @@ import cz.crcs.ectester.applet.ECTesterApplet; import static cz.crcs.ectester.applet.ECTesterApplet.KeyAgreement_ALG_EC_SVDP_DH; import cz.crcs.ectester.applet.EC_Consts; import cz.crcs.ectester.data.EC_Store; +import cz.crcs.ectester.reader.command.Command; import cz.crcs.ectester.reader.ec.EC_Category; import cz.crcs.ectester.reader.ec.EC_Data; import cz.crcs.ectester.reader.ec.EC_Params; +import cz.crcs.ectester.reader.response.Response; +import cz.crcs.ectester.reader.test.TestSuite; import javacard.security.KeyPair; import org.apache.commons.cli.*; diff --git a/src/cz/crcs/ectester/reader/Response.java b/src/cz/crcs/ectester/reader/Response.java deleted file mode 100644 index 3df956e..0000000 --- a/src/cz/crcs/ectester/reader/Response.java +++ /dev/null @@ -1,591 +0,0 @@ -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; - -import javax.smartcardio.ResponseAPDU; -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) { - this.resp = response; - this.time = time; - } - - protected void parse(int numSW, int numParams) { - this.numSW = numSW; - this.sws = new short[numSW]; - - byte[] data = resp.getData(); - int offset = 0; - - //parse SWs in response - for (int i = 0; i < numSW; ++i) { - if (getLength() >= (offset + 2)) { - short sw = Util.getShort(data, offset); - offset += 2; - sws[i] = sw; - 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++) { - if (data.length - offset < 2) { - success = false; - break; - } - short paramLength = Util.getShort(data, offset); - offset += 2; - if (data.length < offset + paramLength) { - success = false; - break; - } - params[i] = new byte[paramLength]; - System.arraycopy(data, offset, params[i], 0, paramLength); - offset += paramLength; - } - } - - public ResponseAPDU getAPDU() { - return resp; - } - - public long getDuration() { - return time; - } - - public short getNaturalSW() { - return (short) resp.getSW(); - } - - public short getSW(int index) { - return sws[index]; - } - - public int getNumSW() { - return numSW; - } - - public boolean hasParam(int index) { - return params.length >= index + 1 && params[index] != null; - } - - public int getParamLength(int index) { - return params[index].length; - } - - public byte[] getParam(int index) { - return params[index]; - } - - public byte[][] getParams() { - return params; - } - - public int getLength() { - return resp.getNr(); - } - - public boolean successful() { - return this.success; - } - - @Override - public abstract String toString(); - - public String toString(String inner) { - StringBuilder suffix = new StringBuilder(); - for (int j = 0; j < getNumSW(); ++j) { - short sw = getSW(j); - if (sw != 0) { - suffix.append(" ").append(Util.getSWString(sw)); - } - } - if (suffix.length() == 0) { - suffix.append(" [").append(Util.getSW(getNaturalSW())).append("]"); - } - return String.format("%-62s:%4d ms : %s", inner, time / 1000000, suffix); - } - - public static String toString(List responses) { - return toString(responses, null); - } - - public static String toString(List responses, String prefix) { - if (prefix != null) - prefix += " | "; - StringBuilder out = new StringBuilder(); - for (int i = 0; i < responses.size(); ++i) { - Response r = responses.get(i); - - if (prefix != null) - out.append(prefix); - - String message = r.toString(); - out.append(message); - if (i < responses.size() - 1) { - out.append("\n"); - } - } - return out.toString(); - } - - - /** - * - */ - public static class AllocateKeyAgreement extends Response { - byte kaType; - protected AllocateKeyAgreement(ResponseAPDU response, long time, byte kaType) { - super(response, time); - this.kaType = kaType; - - parse(2, 0); - } - - @Override - public String toString() { - return super.toString(String.format("Allocate KeyAgreement(%s) object", Util.getKATypeString(this.kaType))); - } - - } - - public static class Allocate extends Response { - - private byte keyPair; - private short keyLength; - private byte keyClass; - - protected Allocate(ResponseAPDU response, long time, byte keyPair, short keyLength, byte keyClass) { - super(response, time); - this.keyPair = keyPair; - this.keyLength = keyLength; - this.keyClass = keyClass; - - int pairs = 0; - if ((keyPair & ECTesterApplet.KEYPAIR_LOCAL) != 0) pairs++; - if ((keyPair & ECTesterApplet.KEYPAIR_REMOTE) != 0) pairs++; - parse(pairs, 0); - } - - @Override - public String toString() { - String field = keyClass == KeyPair.ALG_EC_FP ? "ALG_EC_FP" : "ALG_EC_F2M"; - String key; - if (keyPair == ECTesterApplet.KEYPAIR_BOTH) { - key = "both keypairs"; - } else { - key = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair"; - } - 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) { - super(response, time); - this.keyPair = keyPair; - - int pairs = 0; - if ((keyPair & ECTesterApplet.KEYPAIR_LOCAL) != 0) pairs++; - if ((keyPair & ECTesterApplet.KEYPAIR_REMOTE) != 0) pairs++; - parse(pairs, 0); - } - - @Override - public String toString() { - String key; - if (keyPair == ECTesterApplet.KEYPAIR_BOTH) { - key = "both keypairs"; - } else { - key = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair"; - } - return super.toString(String.format("Cleared %s", key)); - } - } - - /** - * - */ - public static class Set extends Response { - - private byte keyPair; - private byte curve; - private short parameters; - - protected Set(ResponseAPDU response, long time, byte keyPair, byte curve, short parameters) { - super(response, time); - this.keyPair = keyPair; - this.curve = curve; - this.parameters = parameters; - - int pairs = 0; - if ((keyPair & ECTesterApplet.KEYPAIR_LOCAL) != 0) pairs++; - if ((keyPair & ECTesterApplet.KEYPAIR_REMOTE) != 0) pairs++; - - parse(pairs, 0); - } - - @Override - public String toString() { - String name; - switch (curve) { - case EC_Consts.CURVE_default: - name = "default"; - break; - case EC_Consts.CURVE_external: - name = "external"; - break; - default: - 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 super.toString(String.format("Set %s %s parameters on %s", name, what, pair)); - } - - } - - /** - * - */ - public static class Corrupt extends Response { - - private byte keyPair; - private byte key; - private short params; - private byte corruption; - - protected Corrupt(ResponseAPDU response, long time, byte keyPair, byte key, short params, byte corruption) { - super(response, time); - this.keyPair = keyPair; - this.key = key; - this.params = params; - this.corruption = corruption; - - int pairs = 0; - if ((keyPair & ECTesterApplet.KEYPAIR_LOCAL) != 0) pairs++; - if ((keyPair & ECTesterApplet.KEYPAIR_REMOTE) != 0) pairs++; - - parse(pairs, 0); - } - - @Override - public String toString() { - String corrupt = Util.getCorruption(corruption); - - String pair; - if (keyPair == ECTesterApplet.KEYPAIR_BOTH) { - pair = "both keypairs"; - } else { - pair = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair"; - } - return super.toString(String.format("Corrupted params of %s, %s", pair, corrupt)); - } - } - - /** - * - */ - public static class Generate extends Response { - - private byte keyPair; - - protected Generate(ResponseAPDU response, long time, byte keyPair) { - super(response, time); - this.keyPair = keyPair; - - int generated = 0; - if ((keyPair & ECTesterApplet.KEYPAIR_LOCAL) != 0) generated++; - if ((keyPair & ECTesterApplet.KEYPAIR_REMOTE) != 0) generated++; - parse(generated, 0); - } - - @Override - public String toString() { - String key; - if (keyPair == ECTesterApplet.KEYPAIR_BOTH) { - key = "both keypairs"; - } else { - key = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair"; - } - return super.toString(String.format("Generated %s", key)); - } - - } - - /** - * - */ - public static class Export extends Response { - - private byte keyPair; - private byte key; - private short parameters; - - protected Export(ResponseAPDU response, long time, byte keyPair, byte key, short parameters) { - super(response, time); - this.keyPair = keyPair; - this.key = key; - this.parameters = parameters; - - int exported = 0; - if ((keyPair & ECTesterApplet.KEYPAIR_LOCAL) != 0) exported++; - if ((keyPair & ECTesterApplet.KEYPAIR_REMOTE) != 0) exported++; - int keys = 0; - if ((key & EC_Consts.KEY_PUBLIC) != 0) keys++; - if ((key & EC_Consts.KEY_PRIVATE) != 0) keys++; - int paramCount = 0; - short mask = EC_Consts.PARAMETER_FP; - while (mask <= EC_Consts.PARAMETER_K) { - if ((mask & parameters) != 0) { - paramCount++; - } - mask = (short) (mask << 1); - } - int other = 0; - if ((key & EC_Consts.KEY_PUBLIC) != 0 && (parameters & EC_Consts.PARAMETER_W) != 0) other++; - if ((key & EC_Consts.KEY_PRIVATE) != 0 && (parameters & EC_Consts.PARAMETER_S) != 0) other++; - - parse(exported, exported * keys * paramCount + exported * other); - } - - private int getIndex(byte keyPair, short param) { - byte pair = ECTesterApplet.KEYPAIR_LOCAL; - int index = 0; - while (pair <= ECTesterApplet.KEYPAIR_REMOTE) { - short mask = EC_Consts.PARAMETER_FP; - while (mask <= EC_Consts.PARAMETER_S) { - if (pair == keyPair && param == mask) { - return index; - } - if ((parameters & mask) != 0 && (pair & keyPair) != 0) { - if (mask == EC_Consts.PARAMETER_W) { - if ((key & EC_Consts.KEY_PUBLIC) != 0) - index++; - } else if (mask == EC_Consts.PARAMETER_S) { - if ((key & EC_Consts.KEY_PRIVATE) != 0) - index++; - } else { - index++; - } - } - mask = (short) (mask << 1); - } - - pair = (byte) (pair << 1); - } - return -1; - } - - public boolean hasParameters(byte keyPair, short params) { - if ((keyPair & this.keyPair) == 0 || (params ^ parameters) != 0) { - return false; - } - short param = EC_Consts.PARAMETER_FP; - while (param <= EC_Consts.PARAMETER_S) { - short masked = (short) (param & params); - if (masked != 0 && !hasParameter(keyPair, masked)) { - return false; - } - param = (short) (param << 1); - } - return true; - } - - public boolean hasParameter(byte keyPair, short param) { - if ((keyPair & this.keyPair) == 0 || (parameters & param) == 0) { - return false; - } - int index = getIndex(keyPair, param); - return index != -1 && hasParam(index); - } - - public byte[] getParameter(byte keyPair, short param) { - return getParam(getIndex(keyPair, param)); - } - - @Override - public String toString() { - String source; - if (key == EC_Consts.KEY_BOTH) { - source = "both keys"; - } else { - source = ((key == EC_Consts.KEY_PUBLIC) ? "public" : "private") + " key"; - } - String pair; - if (keyPair == ECTesterApplet.KEYPAIR_BOTH) { - pair = "both keypairs"; - } else { - pair = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair"; - } - return super.toString(String.format("Exported params from %s of %s", source, pair)); - } - } - - /** - * - */ - public static class ECDH extends Response { - - private byte pubkey; - private byte privkey; - private byte export; - private short corruption; - private byte type; - - protected ECDH(ResponseAPDU response, long time, byte pubkey, byte privkey, byte export, short corruption, byte type) { - super(response, time); - this.pubkey = pubkey; - this.privkey = privkey; - this.export = export; - this.corruption = corruption; - this.type = type; - - parse(1, (export == ECTesterApplet.EXPORT_TRUE) ? 1 : 0); - } - - public boolean hasSecret() { - return hasParam(0); - } - - public byte[] getSecret() { - return getParam(0); - } - - public int secretLength() { - return getParamLength(0); - } - - @Override - public String toString() { - String algo = Util.getKA(type); - - String pub = pubkey == ECTesterApplet.KEYPAIR_LOCAL ? "local" : "remote"; - String priv = privkey == ECTesterApplet.KEYPAIR_LOCAL ? "local" : "remote"; - - String validity; - if (corruption == EC_Consts.CORRUPTION_NONE) { - validity = "unchanged"; - } else { - validity = Util.getCorruption(corruption); - } - return super.toString(String.format("%s of %s pubkey and %s privkey(%s point)", algo, pub, priv, validity)); - } - } - - /** - * - */ - public static class ECDSA extends Response { - - private byte keyPair; - private byte export; - private byte[] raw; - - protected ECDSA(ResponseAPDU response, long time, byte keyPair, byte export, byte[] raw) { - super(response, time); - this.keyPair = keyPair; - this.export = export; - this.raw = raw; - - parse(1, (export == ECTesterApplet.EXPORT_TRUE) ? 1 : 0); - } - - public boolean hasSignature() { - return hasParam(0); - } - - public byte[] getSignature() { - return getParam(0); - } - - @Override - public String toString() { - String key = keyPair == ECTesterApplet.KEYPAIR_LOCAL ? "local" : "remote"; - String data = raw == null ? "random" : "provided"; - return super.toString(String.format("ECDSA with %s keypair(%s data)", key, data)); - } - } - - /** - * - */ - public static class Cleanup extends Response { - - protected Cleanup(ResponseAPDU response, long time) { - super(response, time); - - parse(1, 0); - } - - @Override - public String toString() { - return super.toString("Requested JCSystem object deletion"); - } - - } - - /** - * - */ - public static class Support extends Response { - - protected Support(ResponseAPDU response, long time) { - super(response, time); - - parse(3, 0); - } - - @Override - public String toString() { - 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 deleted file mode 100644 index 157e360..0000000 --- a/src/cz/crcs/ectester/reader/Test.java +++ /dev/null @@ -1,82 +0,0 @@ -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 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 callback) { - this(command, expected); - this.callback = callback; - } - - public Command getCommand() { - return command; - } - - public Response getResponse() { - 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; - } - - public boolean hasRun() { - return hasRun; - } - - @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 deleted file mode 100644 index 24b72a3..0000000 --- a/src/cz/crcs/ectester/reader/TestSuite.java +++ /dev/null @@ -1,314 +0,0 @@ -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 javacard.security.KeyPair; - -import javax.smartcardio.CardException; -import java.io.IOException; -import java.util.*; - -/** - * @author Jan Jancar johny@neuromancer.sk - */ -public abstract class TestSuite { - - EC_Store dataStore; - ECTester.Config cfg; - DirtyLogger systemOut; - String name; - List tests = new LinkedList<>(); - - TestSuite(EC_Store dataStore, ECTester.Config cfg, DirtyLogger systemOut, String name) { - this.dataStore = dataStore; - this.cfg = cfg; - this.systemOut = systemOut; - this.name = name; - } - - public List run(CardMngr cardManager) throws CardException, IOException { - for (Test t : tests) { - if (!t.hasRun()) { - t.run(); - systemOut.println(t.toString()); - } - } - return tests; - } - - public List getTests() { - return Collections.unmodifiableList(tests); - } - - public String getName() { - return name; - } - - /** - * @param cardManager cardManager to send APDU through - * @param generateExpected expected result of the Generate command - * @param ecdhExpected expected result of the ordinary ECDH command - * @param ecdsaExpected expected result of the ordinary ECDSA command - * @return tests to run - */ - List testCurve(CardMngr cardManager, Test.Result generateExpected, Test.Result ecdhExpected, Test.Result ecdsaExpected) { - List tests = new LinkedList<>(); - - tests.add(new Test(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_BOTH), generateExpected)); - tests.add(new Test(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, EC_Consts.KA_ECDH), ecdhExpected)); - tests.add(new Test(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_COMPRESS, EC_Consts.KA_ECDH), ecdhExpected)); - tests.add(new Test(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_ONE, EC_Consts.KA_ECDH), Test.Result.FAILURE)); - tests.add(new Test(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_ZERO, EC_Consts.KA_ECDH), Test.Result.FAILURE)); - tests.add(new Test(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_MAX, EC_Consts.KA_ECDH), Test.Result.FAILURE)); - tests.add(new Test(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_FULLRANDOM, EC_Consts.KA_ECDH), Test.Result.FAILURE)); - tests.add(new Test(new Command.ECDSA(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, null), ecdsaExpected)); - - return tests; - } - - /** - * @param cardManager cardManager to send APDU through - * @param category category to test - * @param field field to test (KeyPair.ALG_EC_FP || KeyPair.ALG_EC_F2M) - * @param setExpected expected result of the Set (curve) command - * @param generateExpected expected result of the Generate command - * @param ecdhExpected expected result of the ordinary ECDH command - * @param ecdsaExpected expected result of the ordinary ECDSA command - * @return tests to run - */ - List testCategory(CardMngr cardManager, String category, byte field, Test.Result setExpected, Test.Result generateExpected, Test.Result ecdhExpected, Test.Result ecdsaExpected) { - List tests = new LinkedList<>(); - Map curves = dataStore.getObjects(EC_Curve.class, category); - if (curves == null) - return tests; - for (Map.Entry entry : curves.entrySet()) { - EC_Curve curve = entry.getValue(); - if (curve.getField() == field && (curve.getBits() == cfg.bits || cfg.all)) { - tests.add(new Test(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), field), Test.Result.SUCCESS)); - tests.add(new Test(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), setExpected)); - tests.addAll(testCurve(cardManager, generateExpected, ecdhExpected, ecdsaExpected)); - tests.add(new Test(new Command.Cleanup(cardManager), Test.Result.ANY)); - } - } - - return tests; - } - - public static class Default extends TestSuite { - - public Default(EC_Store dataStore, ECTester.Config cfg, DirtyLogger systemOut) { - super(dataStore, cfg, systemOut, "default"); - } - - @Override - public List run(CardMngr cardManager) throws IOException, CardException { - tests.add(new Test(new Command.Support(cardManager), Test.Result.ANY)); - if (cfg.namedCurve != null) { - if (cfg.primeField) { - tests.addAll(testCategory(cardManager, cfg.namedCurve, KeyPair.ALG_EC_FP, Test.Result.SUCCESS, Test.Result.SUCCESS, Test.Result.SUCCESS, Test.Result.SUCCESS)); - } - if (cfg.binaryField) { - tests.addAll(testCategory(cardManager, cfg.namedCurve, KeyPair.ALG_EC_F2M, Test.Result.SUCCESS, Test.Result.SUCCESS, Test.Result.SUCCESS, Test.Result.SUCCESS)); - } - } else { - if (cfg.all) { - if (cfg.primeField) { - //iterate over prime curve sizes used: EC_Consts.FP_SIZES - for (short keyLength : EC_Consts.FP_SIZES) { - defaultTests(cardManager, keyLength, KeyPair.ALG_EC_FP); - } - } - if (cfg.binaryField) { - //iterate over binary curve sizes used: EC_Consts.F2M_SIZES - for (short keyLength : EC_Consts.F2M_SIZES) { - defaultTests(cardManager, keyLength, KeyPair.ALG_EC_F2M); - } - } - } else { - if (cfg.primeField) { - defaultTests(cardManager, (short) cfg.bits, KeyPair.ALG_EC_FP); - } - - if (cfg.binaryField) { - defaultTests(cardManager, (short) cfg.bits, KeyPair.ALG_EC_F2M); - } - } - } - return super.run(cardManager); - } - - private void defaultTests(CardMngr cardManager, short keyLength, byte keyType) throws IOException { - tests.add(new Test(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, keyLength, keyType), Test.Result.SUCCESS)); - Command curve = Command.prepareCurve(cardManager, dataStore, cfg, ECTesterApplet.KEYPAIR_BOTH, keyLength, keyType); - if (curve != null) - tests.add(new Test(curve, Test.Result.SUCCESS)); - tests.addAll(testCurve(cardManager, Test.Result.SUCCESS, Test.Result.SUCCESS, Test.Result.SUCCESS)); - tests.add(new Test(new Command.Cleanup(cardManager), Test.Result.ANY)); - } - } - - public static class TestVectors extends TestSuite { - - public TestVectors(EC_Store dataStore, ECTester.Config cfg, DirtyLogger systemOut) { - super(dataStore, cfg, systemOut, "test"); - } - - @Override - public List run(CardMngr cardManager) throws IOException, CardException { - /* Set original curves (secg/nist/brainpool). Set keypairs from test vectors. - * Do ECDH both ways, export and verify that the result is correct. - */ - Map results = dataStore.getObjects(EC_KAResult.class, "test"); - for (EC_KAResult result : results.values()) { - EC_Curve curve = dataStore.getObject(EC_Curve.class, result.getCurve()); - if (cfg.namedCurve != null && !(result.getCurve().startsWith(cfg.namedCurve) || result.getCurve().equals(cfg.namedCurve))) { - continue; - } - if (curve.getBits() != cfg.bits && !cfg.all) { - continue; - } - if (curve.getField() == KeyPair.ALG_EC_FP && !cfg.primeField || curve.getField() == KeyPair.ALG_EC_F2M && !cfg.binaryField) { - continue; - } - EC_Params onekey = dataStore.getObject(EC_Keypair.class, result.getOneKey()); - if (onekey == null) { - onekey = dataStore.getObject(EC_Key.Private.class, result.getOneKey()); - } - EC_Params otherkey = dataStore.getObject(EC_Keypair.class, result.getOtherKey()); - if (otherkey == null) { - otherkey = dataStore.getObject(EC_Key.Public.class, result.getOtherKey()); - } - if (onekey == null || otherkey == null) { - throw new IOException("Test vector keys couldn't be located."); - } - - 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 Composite extends TestSuite { - - public Composite(EC_Store dataStore, ECTester.Config cfg, DirtyLogger systemOut) { - super(dataStore, cfg, systemOut, "composite"); - } - - @Override - public List run(CardMngr cardManager) throws IOException, CardException { - /* Do the default tests with the public keys set to provided smallorder keys - * over composite order curves. Essentially small subgroup attacks. - * These should fail, the curves aren't safe so that if the computation with - * a small order public key succeeds the private key modulo the public key order - * is revealed. - */ - Map keys = dataStore.getObjects(EC_Key.class, "composite"); - for (EC_Key key : keys.values()) { - EC_Curve curve = dataStore.getObject(EC_Curve.class, key.getCurve()); - if (cfg.namedCurve != null && !(key.getCurve().startsWith(cfg.namedCurve) || key.getCurve().equals(cfg.namedCurve))) { - continue; - } - if (curve.getField() == KeyPair.ALG_EC_FP && !cfg.primeField || curve.getField() == KeyPair.ALG_EC_F2M && !cfg.binaryField) { - continue; - } - if ((curve.getBits() == cfg.bits || cfg.all)) { - tests.add(new Test(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.ECDH_direct(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, EC_Consts.KA_ECDH, key.flatten()), 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, DirtyLogger systemOut) { - super(dataStore, cfg, systemOut, "invalid"); - } - - @Override - public List run(CardMngr cardManager) throws CardException, IOException { - /* Set original curves (secg/nist/brainpool). Generate local. - * Try ECDH with invalid public keys of increasing (or decreasing) order. - */ - Map pubkeys = dataStore.getObjects(EC_Key.Public.class, "invalid"); - Map> curves = new HashMap<>(); - for (EC_Key.Public key : pubkeys.values()) { - EC_Curve curve = dataStore.getObject(EC_Curve.class, key.getCurve()); - if (cfg.namedCurve != null && !(key.getCurve().startsWith(cfg.namedCurve) || key.getCurve().equals(cfg.namedCurve))) { - continue; - } - if (curve.getBits() != cfg.bits && !cfg.all) { - continue; - } - if (curve.getField() == KeyPair.ALG_EC_FP && !cfg.primeField || curve.getField() == KeyPair.ALG_EC_F2M && !cfg.binaryField) { - continue; - } - List keys = curves.getOrDefault(curve, new LinkedList<>()); - keys.add(key); - curves.putIfAbsent(curve, keys); - } - for (Map.Entry> e : curves.entrySet()) { - EC_Curve curve = e.getKey(); - List keys = e.getValue(); - - 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)); - for (EC_Key.Public pub : keys) { - // tests.add(new Test(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, pub.getParams(), pub.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_ANY), Test.Result.FAILURE)); - tests.add(new Test(new Command.ECDH_direct(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, EC_Consts.KA_ANY, pub.flatten()), Test.Result.FAILURE)); - } - tests.add(new Test(new Command.Cleanup(cardManager), Test.Result.ANY)); - } - - return super.run(cardManager); - } - } - - public static class Wrong extends TestSuite { - - public Wrong(EC_Store dataStore, ECTester.Config cfg, DirtyLogger systemOut) { - super(dataStore, cfg, systemOut, "wrong"); - } - - @Override - public List run(CardMngr cardManager) throws CardException, IOException { - /* Just do the default tests on the wrong curves. - * These should generally fail, the curves aren't curves. - */ - if (cfg.primeField) { - tests.addAll(testCategory(cardManager, cfg.testSuite, KeyPair.ALG_EC_FP, Test.Result.FAILURE, Test.Result.FAILURE, Test.Result.FAILURE, Test.Result.FAILURE)); - } - if (cfg.binaryField) { - tests.addAll(testCategory(cardManager, cfg.testSuite, KeyPair.ALG_EC_F2M, Test.Result.FAILURE, Test.Result.FAILURE, Test.Result.FAILURE, Test.Result.FAILURE)); - } - return super.run(cardManager); - } - } -} diff --git a/src/cz/crcs/ectester/reader/command/Command.java b/src/cz/crcs/ectester/reader/command/Command.java new file mode 100644 index 0000000..bab78c9 --- /dev/null +++ b/src/cz/crcs/ectester/reader/command/Command.java @@ -0,0 +1,592 @@ +package cz.crcs.ectester.reader.command; + +import cz.crcs.ectester.applet.ECTesterApplet; +import cz.crcs.ectester.applet.EC_Consts; +import cz.crcs.ectester.data.EC_Store; +import cz.crcs.ectester.reader.CardMngr; +import cz.crcs.ectester.reader.ECTester; +import cz.crcs.ectester.reader.response.Response; +import cz.crcs.ectester.reader.Util; +import cz.crcs.ectester.reader.ec.EC_Curve; +import cz.crcs.ectester.reader.ec.EC_Key; +import cz.crcs.ectester.reader.ec.EC_Keypair; +import cz.crcs.ectester.reader.ec.EC_Params; +import javacard.security.KeyPair; + +import javax.smartcardio.CardException; +import javax.smartcardio.CommandAPDU; +import javax.smartcardio.ResponseAPDU; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class Command { + CommandAPDU cmd; + CardMngr cardManager; + + Command(CardMngr cardManager) { + this.cardManager = cardManager; + } + + public CommandAPDU getAPDU() { + return cmd; + } + + public abstract Response send() throws CardException; + + public static List sendAll(List commands) throws CardException { + List result = new ArrayList<>(); + for (Command cmd : commands) { + result.add(cmd.send()); + } + return result; + } + + + /** + * @param keyPair which keyPair/s (local/remote) to set curve domain parameters on + * @param keyLength key length to choose + * @param keyClass key class to choose + * @return a Command to send in order to prepare the curve on the keypairs. + * @throws IOException if curve file cannot be found/opened + */ + public static Command prepareCurve(CardMngr cardManager, EC_Store dataStore, ECTester.Config cfg, byte keyPair, short keyLength, byte keyClass) throws IOException { + + 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; + return new Command.Set(cardManager, keyPair, EC_Consts.getCurve(keyLength, keyClass), domainParams, null); + } else if (cfg.namedCurve != null) { + // Set a named curve. + // 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."); + } + if (curve.getBits() != keyLength) { + throw new IOException("Curve bits mismatch: " + curve.getBits() + " vs " + keyLength + " entered."); + } + if (curve.getField() != keyClass) { + throw new IOException("Curve field mismatch."); + } + + byte[] external = curve.flatten(); + if (external == null) { + throw new IOException("Couldn't read named curve data."); + } + return new Command.Set(cardManager, keyPair, EC_Consts.CURVE_external, curve.getParams(), external); + } else if (cfg.curveFile != null) { + // Set curve loaded from a file + EC_Curve curve = new EC_Curve(null, keyLength, keyClass); + + FileInputStream in = new FileInputStream(cfg.curveFile); + curve.readCSV(in); + in.close(); + + byte[] external = curve.flatten(); + if (external == null) { + throw new IOException("Couldn't read the curve file correctly."); + } + return new Command.Set(cardManager, keyPair, EC_Consts.CURVE_external, curve.getParams(), external); + } else { + // Set default curve + /* This command was generally causing problems for simulating on jcardsim. + * Since there, .clearKey() resets all the keys values, even the domain. + * This might break some other stuff.. But should not. + */ + //commands.add(new Command.Clear(cardManager, keyPair)); + return null; + } + } + + + /** + * @param keyPair which keyPair/s to set the key params on + * @return a CommandAPDU setting params loaded on the keyPair/s + * @throws IOException if any of the key files cannot be found/opened + */ + public static Command prepareKey(CardMngr cardManager, EC_Store dataStore, ECTester.Config cfg, byte keyPair) throws IOException { + short params = EC_Consts.PARAMETERS_NONE; + byte[] data = null; + + if (cfg.key != null || cfg.namedKey != null) { + params |= EC_Consts.PARAMETERS_KEYPAIR; + EC_Params keypair; + if (cfg.key != null) { + keypair = new EC_Params(EC_Consts.PARAMETERS_KEYPAIR); + + FileInputStream in = new FileInputStream(cfg.key); + keypair.readCSV(in); + in.close(); + } else { + keypair = dataStore.getObject(EC_Keypair.class, cfg.namedKey); + } + + data = keypair.flatten(); + if (data == null) { + throw new IOException("Couldn't read the key file correctly."); + } + } + + if (cfg.publicKey != null || cfg.namedPublicKey != null) { + params |= EC_Consts.PARAMETER_W; + EC_Params pub; + if (cfg.publicKey != null) { + pub = new EC_Params(EC_Consts.PARAMETER_W); + + FileInputStream in = new FileInputStream(cfg.publicKey); + pub.readCSV(in); + in.close(); + } else { + pub = dataStore.getObject(EC_Key.Public.class, cfg.namedPublicKey); + if (pub == null) { + pub = dataStore.getObject(EC_Keypair.class, cfg.namedPublicKey); + } + } + + byte[] pubkey = pub.flatten(EC_Consts.PARAMETER_W); + if (pubkey == null) { + throw new IOException("Couldn't read the public key file correctly."); + } + data = pubkey; + } + if (cfg.privateKey != null || cfg.namedPrivateKey != null) { + params |= EC_Consts.PARAMETER_S; + EC_Params priv; + if (cfg.privateKey != null) { + priv = new EC_Params(EC_Consts.PARAMETER_S); + + FileInputStream in = new FileInputStream(cfg.privateKey); + priv.readCSV(in); + in.close(); + } else { + priv = dataStore.getObject(EC_Key.Public.class, cfg.namedPrivateKey); + if (priv == null) { + priv = dataStore.getObject(EC_Keypair.class, cfg.namedPrivateKey); + } + } + + byte[] privkey = priv.flatten(EC_Consts.PARAMETER_S); + if (privkey == null) { + throw new IOException("Couldn't read the private key file correctly."); + } + data = Util.concatenate(data, privkey); + } + return new Command.Set(cardManager, keyPair, EC_Consts.CURVE_external, params, data); + } + + + /** + * + */ + public static class Allocate extends Command { + private byte keyPair; + private short keyLength; + private byte keyClass; + + /** + * Creates the INS_ALLOCATE instruction. + * + * @param cardManager cardManager to send APDU through + * @param keyPair which keyPair to use, local/remote (KEYPAIR_* | ...) + * @param keyLength key length to set + * @param keyClass key class to allocate + */ + public Allocate(CardMngr cardManager, byte keyPair, short keyLength, byte keyClass) { + super(cardManager); + this.keyPair = keyPair; + this.keyLength = keyLength; + this.keyClass = keyClass; + + byte[] data = new byte[]{0, 0, keyClass}; + Util.setShort(data, 0, keyLength); + this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_ALLOCATE, keyPair, 0x00, data); + } + + @Override + public Response.Allocate send() throws CardException { + long elapsed = -System.nanoTime(); + ResponseAPDU response = cardManager.send(cmd); + elapsed += System.nanoTime(); + return new Response.Allocate(response, elapsed, keyPair, keyLength, keyClass); + } + } + + public static class AllocateKeyAgreement extends Command { + + private byte kaType; + + + /** + * Creates the INS_ALLOCATE_KA instruction. + * + * @param cardManager cardManager to send APDU through + * @param kaType which type of KeyAgreement to use + */ + public AllocateKeyAgreement(CardMngr cardManager, byte kaType) { + super(cardManager); + this.kaType = kaType; + byte[] data = new byte[]{kaType}; + this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_ALLOCATE_KA, 0x00, 0x00, data); + } + + @Override + public Response.AllocateKeyAgreement send() throws CardException { + long elapsed = -System.nanoTime(); + ResponseAPDU response = cardManager.send(cmd); + elapsed += System.nanoTime(); + return new Response.AllocateKeyAgreement(response, elapsed, kaType); + } + } + + /** + * + */ + public static class Clear extends Command { + private byte keyPair; + + /** + * @param cardManager cardManager to send APDU through + * @param keyPair which keyPair clear, local/remote (KEYPAIR_* || ...) + */ + public Clear(CardMngr cardManager, byte keyPair) { + super(cardManager); + this.keyPair = keyPair; + + this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_CLEAR, keyPair, 0x00); + } + + @Override + public Response.Clear send() throws CardException { + long elapsed = -System.nanoTime(); + ResponseAPDU response = cardManager.send(cmd); + elapsed += System.nanoTime(); + return new Response.Clear(response, elapsed, keyPair); + } + } + + /** + * + */ + public static class Set extends Command { + private byte keyPair; + private byte curve; + private short params; + private byte[] external; + + /** + * Creates the INS_SET instruction. + * + * @param cardManager cardManager to send APDU through + * @param keyPair which keyPair to set params on, local/remote (KEYPAIR_* || ...) + * @param curve curve to set (EC_Consts.CURVE_*) + * @param params parameters to set (EC_Consts.PARAMETER_* | ...) + * @param external external curve data, can be null + */ + public Set(CardMngr cardManager, byte keyPair, byte curve, short params, byte[] external) { + super(cardManager); + this.keyPair = keyPair; + this.curve = curve; + this.params = params; + this.external = external; + + int len = external != null ? 2 + external.length : 2; + byte[] data = new byte[len]; + Util.setShort(data, 0, params); + if (external != null) { + System.arraycopy(external, 0, data, 2, external.length); + } + + this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_SET, keyPair, curve, data); + } + + @Override + public Response.Set send() throws CardException { + long elapsed = -System.nanoTime(); + ResponseAPDU response = cardManager.send(cmd); + elapsed += System.nanoTime(); + return new Response.Set(response, elapsed, keyPair, curve, params); + } + } + + /** + * + */ + public static class Corrupt extends Command { + private byte keyPair; + private byte key; + private short params; + private byte corruption; + + /** + * @param cardManager cardManager to send APDU through + * @param keyPair which keyPair to corrupt, local/remote (KEYPAIR_* || ...) + * @param key key to corrupt (EC_Consts.KEY_* | ...) + * @param params parameters to corrupt (EC_Consts.PARAMETER_* | ...) + * @param corruption corruption type (EC_Consts.CORRUPTION_*) + */ + public Corrupt(CardMngr cardManager, byte keyPair, byte key, short params, byte corruption) { + super(cardManager); + this.keyPair = keyPair; + this.key = key; + this.params = params; + this.corruption = corruption; + + byte[] data = new byte[3]; + Util.setShort(data, 0, params); + data[2] = corruption; + + this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_CORRUPT, keyPair, key, data); + } + + @Override + public Response.Corrupt send() throws CardException { + long elapsed = -System.nanoTime(); + ResponseAPDU response = cardManager.send(cmd); + elapsed += System.nanoTime(); + return new Response.Corrupt(response, elapsed, keyPair, key, params, corruption); + } + } + + /** + * + */ + public static class Generate extends Command { + private byte keyPair; + + /** + * Creates the INS_GENERATE instruction. + * + * @param cardManager cardManager to send APDU through + * @param keyPair which keyPair to generate, local/remote (KEYPAIR_* || ...) + */ + public Generate(CardMngr cardManager, byte keyPair) { + super(cardManager); + this.keyPair = keyPair; + + this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_GENERATE, keyPair, 0); + } + + @Override + public Response.Generate send() throws CardException { + long elapsed = -System.nanoTime(); + ResponseAPDU response = cardManager.send(cmd); + elapsed += System.nanoTime(); + return new Response.Generate(response, elapsed, keyPair); + } + } + + /** + * + */ + public static class Export extends Command { + private byte keyPair; + private byte key; + private short params; + + /** + * Creates the INS_EXPORT instruction. + * + * @param cardManager cardManager to send APDU through + * @param keyPair keyPair to export from (KEYPAIR_* | ...) + * @param key key to export from (EC_Consts.KEY_* | ...) + * @param params params to export (EC_Consts.PARAMETER_* | ...) + */ + public Export(CardMngr cardManager, byte keyPair, byte key, short params) { + super(cardManager); + this.keyPair = keyPair; + this.key = key; + this.params = params; + + byte[] data = new byte[2]; + Util.setShort(data, 0, params); + + this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_EXPORT, keyPair, key, data); + } + + @Override + public Response.Export send() throws CardException { + long elapsed = -System.nanoTime(); + ResponseAPDU response = cardManager.send(cmd); + elapsed += System.nanoTime(); + return new Response.Export(response, elapsed, keyPair, key, params); + } + } + + /** + * + */ + public static class ECDH extends Command { + private byte pubkey; + private byte privkey; + private byte export; + private short corruption; + private byte type; + + /** + * Creates the INS_ECDH instruction. + * + * @param cardManager cardManager to send APDU through + * @param pubkey keyPair to use for public key, (KEYPAIR_LOCAL || KEYPAIR_REMOTE) + * @param privkey keyPair to use for private key, (KEYPAIR_LOCAL || KEYPAIR_REMOTE) + * @param export whether to export ECDH secret + * @param corruption whether to invalidate the pubkey before ECDH (EC_Consts.CORRUPTION_* | ...) + * @param type ECDH algorithm type (EC_Consts.KA_* | ...) + */ + public ECDH(CardMngr cardManager, byte pubkey, byte privkey, byte export, short corruption, byte type) { + super(cardManager); + this.pubkey = pubkey; + this.privkey = privkey; + this.export = export; + this.corruption = corruption; + this.type = type; + + byte[] data = new byte[]{export, 0,0, type}; + Util.setShort(data, 1, corruption); + + this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_ECDH, pubkey, privkey, data); + } + + @Override + public Response.ECDH send() throws CardException { + long elapsed = -System.nanoTime(); + ResponseAPDU response = cardManager.send(cmd); + elapsed += System.nanoTime(); + return new Response.ECDH(response, elapsed, pubkey, privkey, export, corruption, type); + } + } + + /** + * + */ + public static class ECDH_direct extends Command { + private byte privkey; + private byte export; + private short corruption; + private byte type; + private byte[] pubkey; + + /** + * Creates the INS_ECDH_DIRECT instruction. + * + * @param cardManager cardManager to send APDU through + * @param privkey keyPair to use for private key, (KEYPAIR_LOCAL || KEYPAIR_REMOTE) + * @param export whether to export ECDH secret + * @param corruption whether to invalidate the pubkey before ECDH (EC_Consts.CORRUPTION_* | ...) + * @param type ECDH algorithm type (EC_Consts.KA_* | ...) + * @param pubkey pubkey data to do ECDH with. + */ + public ECDH_direct(CardMngr cardManager, byte privkey, byte export, short corruption, byte type, byte[] pubkey) { + super(cardManager); + this.privkey = privkey; + this.export = export; + this.corruption = corruption; + this.type = type; + this.pubkey = pubkey; + + byte[] data = new byte[3 + pubkey.length]; + Util.setShort(data, 0, corruption); + data[2] = type; + System.arraycopy(pubkey, 0, data, 3, pubkey.length); + + this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_ECDH_DIRECT, privkey, export, data); + } + + @Override + public Response.ECDH send() throws CardException { + long elapsed = -System.nanoTime(); + ResponseAPDU response = cardManager.send(cmd); + elapsed += System.nanoTime(); + return new Response.ECDH(response, elapsed, ECTesterApplet.KEYPAIR_REMOTE, privkey, export, corruption, type); + } + } + + public static class ECDSA extends Command { + private byte keyPair; + private byte export; + private byte[] raw; + + /** + * Creates the INS_ECDSA instruction. + * + * @param cardManager cardManager to send APDU through + * @param keyPair keyPair to use for signing and verification (KEYPAIR_LOCAL || KEYPAIR_REMOTE) + * @param export whether to export ECDSA signature + * @param raw data to sign, can be null, in which case random data is signed. + */ + public ECDSA(CardMngr cardManager, byte keyPair, byte export, byte[] raw) { + super(cardManager); + this.keyPair = keyPair; + this.export = export; + this.raw = raw; + + int len = raw != null ? raw.length : 0; + byte[] data = new byte[2 + len]; + Util.setShort(data, 0, (short) len); + if (raw != null) { + System.arraycopy(raw, 0, data, 2, len); + } + + this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_ECDSA, keyPair, export, data); + } + + @Override + public Response.ECDSA send() throws CardException { + long elapsed = -System.nanoTime(); + ResponseAPDU response = cardManager.send(cmd); + elapsed += System.nanoTime(); + return new Response.ECDSA(response, elapsed, keyPair, export, raw); + } + } + + /** + * + */ + public static class Cleanup extends Command { + + /** + * @param cardManager cardManager to send APDU through + */ + public Cleanup(CardMngr cardManager) { + super(cardManager); + + this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_CLEANUP, 0, 0); + } + + @Override + public Response.Cleanup send() throws CardException { + long elapsed = -System.nanoTime(); + ResponseAPDU response = cardManager.send(cmd); + elapsed += System.nanoTime(); + return new Response.Cleanup(response, elapsed); + } + } + + /** + * + */ + public static class Support extends Command { + + /** + * @param cardManager cardManager to send APDU through + */ + public Support(CardMngr cardManager) { + super(cardManager); + + this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_SUPPORT, 0, 0); + } + + @Override + public Response.Support send() throws CardException { + long elapsed = -System.nanoTime(); + ResponseAPDU response = cardManager.send(cmd); + elapsed += System.nanoTime(); + return new Response.Support(response, elapsed); + } + } +} + diff --git a/src/cz/crcs/ectester/reader/response/Response.java b/src/cz/crcs/ectester/reader/response/Response.java new file mode 100644 index 0000000..59b24fc --- /dev/null +++ b/src/cz/crcs/ectester/reader/response/Response.java @@ -0,0 +1,591 @@ +package cz.crcs.ectester.reader.response; + +import cz.crcs.ectester.applet.ECTesterApplet; +import cz.crcs.ectester.applet.EC_Consts; +import cz.crcs.ectester.reader.Util; +import javacard.framework.ISO7816; +import javacard.security.KeyPair; + +import javax.smartcardio.ResponseAPDU; +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; + + public Response(ResponseAPDU response, long time) { + this.resp = response; + this.time = time; + } + + public void parse(int numSW, int numParams) { + this.numSW = numSW; + this.sws = new short[numSW]; + + byte[] data = resp.getData(); + int offset = 0; + + //parse SWs in response + for (int i = 0; i < numSW; ++i) { + if (getLength() >= (offset + 2)) { + short sw = Util.getShort(data, offset); + offset += 2; + sws[i] = sw; + 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++) { + if (data.length - offset < 2) { + success = false; + break; + } + short paramLength = Util.getShort(data, offset); + offset += 2; + if (data.length < offset + paramLength) { + success = false; + break; + } + params[i] = new byte[paramLength]; + System.arraycopy(data, offset, params[i], 0, paramLength); + offset += paramLength; + } + } + + public ResponseAPDU getAPDU() { + return resp; + } + + public long getDuration() { + return time; + } + + public short getNaturalSW() { + return (short) resp.getSW(); + } + + public short getSW(int index) { + return sws[index]; + } + + public int getNumSW() { + return numSW; + } + + public boolean hasParam(int index) { + return params.length >= index + 1 && params[index] != null; + } + + public int getParamLength(int index) { + return params[index].length; + } + + public byte[] getParam(int index) { + return params[index]; + } + + public byte[][] getParams() { + return params; + } + + public int getLength() { + return resp.getNr(); + } + + public boolean successful() { + return this.success; + } + + @Override + public abstract String toString(); + + public String toString(String inner) { + StringBuilder suffix = new StringBuilder(); + for (int j = 0; j < getNumSW(); ++j) { + short sw = getSW(j); + if (sw != 0) { + suffix.append(" ").append(Util.getSWString(sw)); + } + } + if (suffix.length() == 0) { + suffix.append(" [").append(Util.getSW(getNaturalSW())).append("]"); + } + return String.format("%-62s:%4d ms : %s", inner, time / 1000000, suffix); + } + + public static String toString(List responses) { + return toString(responses, null); + } + + public static String toString(List responses, String prefix) { + if (prefix != null) + prefix += " | "; + StringBuilder out = new StringBuilder(); + for (int i = 0; i < responses.size(); ++i) { + Response r = responses.get(i); + + if (prefix != null) + out.append(prefix); + + String message = r.toString(); + out.append(message); + if (i < responses.size() - 1) { + out.append("\n"); + } + } + return out.toString(); + } + + + /** + * + */ + public static class AllocateKeyAgreement extends Response { + byte kaType; + public AllocateKeyAgreement(ResponseAPDU response, long time, byte kaType) { + super(response, time); + this.kaType = kaType; + + parse(2, 0); + } + + @Override + public String toString() { + return super.toString(String.format("Allocate KeyAgreement(%s) object", Util.getKATypeString(this.kaType))); + } + + } + + public static class Allocate extends Response { + + private byte keyPair; + private short keyLength; + private byte keyClass; + + public Allocate(ResponseAPDU response, long time, byte keyPair, short keyLength, byte keyClass) { + super(response, time); + this.keyPair = keyPair; + this.keyLength = keyLength; + this.keyClass = keyClass; + + int pairs = 0; + if ((keyPair & ECTesterApplet.KEYPAIR_LOCAL) != 0) pairs++; + if ((keyPair & ECTesterApplet.KEYPAIR_REMOTE) != 0) pairs++; + parse(pairs, 0); + } + + @Override + public String toString() { + String field = keyClass == KeyPair.ALG_EC_FP ? "ALG_EC_FP" : "ALG_EC_F2M"; + String key; + if (keyPair == ECTesterApplet.KEYPAIR_BOTH) { + key = "both keypairs"; + } else { + key = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair"; + } + return super.toString(String.format("Allocated %s %db %s", key, keyLength, field)); + } + } + + /** + * + */ + public static class Clear extends Response { + + private byte keyPair; + + public Clear(ResponseAPDU response, long time, byte keyPair) { + super(response, time); + this.keyPair = keyPair; + + int pairs = 0; + if ((keyPair & ECTesterApplet.KEYPAIR_LOCAL) != 0) pairs++; + if ((keyPair & ECTesterApplet.KEYPAIR_REMOTE) != 0) pairs++; + parse(pairs, 0); + } + + @Override + public String toString() { + String key; + if (keyPair == ECTesterApplet.KEYPAIR_BOTH) { + key = "both keypairs"; + } else { + key = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair"; + } + return super.toString(String.format("Cleared %s", key)); + } + } + + /** + * + */ + public static class Set extends Response { + + private byte keyPair; + private byte curve; + private short parameters; + + public Set(ResponseAPDU response, long time, byte keyPair, byte curve, short parameters) { + super(response, time); + this.keyPair = keyPair; + this.curve = curve; + this.parameters = parameters; + + int pairs = 0; + if ((keyPair & ECTesterApplet.KEYPAIR_LOCAL) != 0) pairs++; + if ((keyPair & ECTesterApplet.KEYPAIR_REMOTE) != 0) pairs++; + + parse(pairs, 0); + } + + @Override + public String toString() { + String name; + switch (curve) { + case EC_Consts.CURVE_default: + name = "default"; + break; + case EC_Consts.CURVE_external: + name = "external"; + break; + default: + 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 super.toString(String.format("Set %s %s parameters on %s", name, what, pair)); + } + + } + + /** + * + */ + public static class Corrupt extends Response { + + private byte keyPair; + private byte key; + private short params; + private byte corruption; + + public Corrupt(ResponseAPDU response, long time, byte keyPair, byte key, short params, byte corruption) { + super(response, time); + this.keyPair = keyPair; + this.key = key; + this.params = params; + this.corruption = corruption; + + int pairs = 0; + if ((keyPair & ECTesterApplet.KEYPAIR_LOCAL) != 0) pairs++; + if ((keyPair & ECTesterApplet.KEYPAIR_REMOTE) != 0) pairs++; + + parse(pairs, 0); + } + + @Override + public String toString() { + String corrupt = Util.getCorruption(corruption); + + String pair; + if (keyPair == ECTesterApplet.KEYPAIR_BOTH) { + pair = "both keypairs"; + } else { + pair = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair"; + } + return super.toString(String.format("Corrupted params of %s, %s", pair, corrupt)); + } + } + + /** + * + */ + public static class Generate extends Response { + + private byte keyPair; + + public Generate(ResponseAPDU response, long time, byte keyPair) { + super(response, time); + this.keyPair = keyPair; + + int generated = 0; + if ((keyPair & ECTesterApplet.KEYPAIR_LOCAL) != 0) generated++; + if ((keyPair & ECTesterApplet.KEYPAIR_REMOTE) != 0) generated++; + parse(generated, 0); + } + + @Override + public String toString() { + String key; + if (keyPair == ECTesterApplet.KEYPAIR_BOTH) { + key = "both keypairs"; + } else { + key = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair"; + } + return super.toString(String.format("Generated %s", key)); + } + + } + + /** + * + */ + public static class Export extends Response { + + private byte keyPair; + private byte key; + private short parameters; + + public Export(ResponseAPDU response, long time, byte keyPair, byte key, short parameters) { + super(response, time); + this.keyPair = keyPair; + this.key = key; + this.parameters = parameters; + + int exported = 0; + if ((keyPair & ECTesterApplet.KEYPAIR_LOCAL) != 0) exported++; + if ((keyPair & ECTesterApplet.KEYPAIR_REMOTE) != 0) exported++; + int keys = 0; + if ((key & EC_Consts.KEY_PUBLIC) != 0) keys++; + if ((key & EC_Consts.KEY_PRIVATE) != 0) keys++; + int paramCount = 0; + short mask = EC_Consts.PARAMETER_FP; + while (mask <= EC_Consts.PARAMETER_K) { + if ((mask & parameters) != 0) { + paramCount++; + } + mask = (short) (mask << 1); + } + int other = 0; + if ((key & EC_Consts.KEY_PUBLIC) != 0 && (parameters & EC_Consts.PARAMETER_W) != 0) other++; + if ((key & EC_Consts.KEY_PRIVATE) != 0 && (parameters & EC_Consts.PARAMETER_S) != 0) other++; + + parse(exported, exported * keys * paramCount + exported * other); + } + + private int getIndex(byte keyPair, short param) { + byte pair = ECTesterApplet.KEYPAIR_LOCAL; + int index = 0; + while (pair <= ECTesterApplet.KEYPAIR_REMOTE) { + short mask = EC_Consts.PARAMETER_FP; + while (mask <= EC_Consts.PARAMETER_S) { + if (pair == keyPair && param == mask) { + return index; + } + if ((parameters & mask) != 0 && (pair & keyPair) != 0) { + if (mask == EC_Consts.PARAMETER_W) { + if ((key & EC_Consts.KEY_PUBLIC) != 0) + index++; + } else if (mask == EC_Consts.PARAMETER_S) { + if ((key & EC_Consts.KEY_PRIVATE) != 0) + index++; + } else { + index++; + } + } + mask = (short) (mask << 1); + } + + pair = (byte) (pair << 1); + } + return -1; + } + + public boolean hasParameters(byte keyPair, short params) { + if ((keyPair & this.keyPair) == 0 || (params ^ parameters) != 0) { + return false; + } + short param = EC_Consts.PARAMETER_FP; + while (param <= EC_Consts.PARAMETER_S) { + short masked = (short) (param & params); + if (masked != 0 && !hasParameter(keyPair, masked)) { + return false; + } + param = (short) (param << 1); + } + return true; + } + + public boolean hasParameter(byte keyPair, short param) { + if ((keyPair & this.keyPair) == 0 || (parameters & param) == 0) { + return false; + } + int index = getIndex(keyPair, param); + return index != -1 && hasParam(index); + } + + public byte[] getParameter(byte keyPair, short param) { + return getParam(getIndex(keyPair, param)); + } + + @Override + public String toString() { + String source; + if (key == EC_Consts.KEY_BOTH) { + source = "both keys"; + } else { + source = ((key == EC_Consts.KEY_PUBLIC) ? "public" : "private") + " key"; + } + String pair; + if (keyPair == ECTesterApplet.KEYPAIR_BOTH) { + pair = "both keypairs"; + } else { + pair = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair"; + } + return super.toString(String.format("Exported params from %s of %s", source, pair)); + } + } + + /** + * + */ + public static class ECDH extends Response { + + private byte pubkey; + private byte privkey; + private byte export; + private short corruption; + private byte type; + + public ECDH(ResponseAPDU response, long time, byte pubkey, byte privkey, byte export, short corruption, byte type) { + super(response, time); + this.pubkey = pubkey; + this.privkey = privkey; + this.export = export; + this.corruption = corruption; + this.type = type; + + parse(1, (export == ECTesterApplet.EXPORT_TRUE) ? 1 : 0); + } + + public boolean hasSecret() { + return hasParam(0); + } + + public byte[] getSecret() { + return getParam(0); + } + + public int secretLength() { + return getParamLength(0); + } + + @Override + public String toString() { + String algo = Util.getKA(type); + + String pub = pubkey == ECTesterApplet.KEYPAIR_LOCAL ? "local" : "remote"; + String priv = privkey == ECTesterApplet.KEYPAIR_LOCAL ? "local" : "remote"; + + String validity; + if (corruption == EC_Consts.CORRUPTION_NONE) { + validity = "unchanged"; + } else { + validity = Util.getCorruption(corruption); + } + return super.toString(String.format("%s of %s pubkey and %s privkey(%s point)", algo, pub, priv, validity)); + } + } + + /** + * + */ + public static class ECDSA extends Response { + + private byte keyPair; + private byte export; + private byte[] raw; + + public ECDSA(ResponseAPDU response, long time, byte keyPair, byte export, byte[] raw) { + super(response, time); + this.keyPair = keyPair; + this.export = export; + this.raw = raw; + + parse(1, (export == ECTesterApplet.EXPORT_TRUE) ? 1 : 0); + } + + public boolean hasSignature() { + return hasParam(0); + } + + public byte[] getSignature() { + return getParam(0); + } + + @Override + public String toString() { + String key = keyPair == ECTesterApplet.KEYPAIR_LOCAL ? "local" : "remote"; + String data = raw == null ? "random" : "provided"; + return super.toString(String.format("ECDSA with %s keypair(%s data)", key, data)); + } + } + + /** + * + */ + public static class Cleanup extends Response { + + public Cleanup(ResponseAPDU response, long time) { + super(response, time); + + parse(1, 0); + } + + @Override + public String toString() { + return super.toString("Requested JCSystem object deletion"); + } + + } + + /** + * + */ + public static class Support extends Response { + + public Support(ResponseAPDU response, long time) { + super(response, time); + + parse(3, 0); + } + + @Override + public String toString() { + return super.toString("Support of ECDH, ECDHC, ECDSA"); + } + } +} diff --git a/src/cz/crcs/ectester/reader/test/Test.java b/src/cz/crcs/ectester/reader/test/Test.java new file mode 100644 index 0000000..e22e0a4 --- /dev/null +++ b/src/cz/crcs/ectester/reader/test/Test.java @@ -0,0 +1,85 @@ +package cz.crcs.ectester.reader.test; + +import cz.crcs.ectester.reader.command.Command; +import cz.crcs.ectester.reader.response.Response; + +import javax.smartcardio.CardException; +import java.util.function.BiFunction; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class Test { + private boolean hasRun = false; + private BiFunction 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 callback) { + this(command, expected); + this.callback = callback; + } + + public Command getCommand() { + return command; + } + + public Response getResponse() { + 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; + } + + public boolean hasRun() { + return hasRun; + } + + @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/test/TestSuite.java b/src/cz/crcs/ectester/reader/test/TestSuite.java new file mode 100644 index 0000000..2f52f38 --- /dev/null +++ b/src/cz/crcs/ectester/reader/test/TestSuite.java @@ -0,0 +1,320 @@ +package cz.crcs.ectester.reader.test; + +import cz.crcs.ectester.applet.ECTesterApplet; +import cz.crcs.ectester.applet.EC_Consts; +import cz.crcs.ectester.data.EC_Store; +import cz.crcs.ectester.reader.CardMngr; +import cz.crcs.ectester.reader.DirtyLogger; +import cz.crcs.ectester.reader.ECTester; +import cz.crcs.ectester.reader.Util; +import cz.crcs.ectester.reader.command.Command; +import cz.crcs.ectester.reader.ec.*; +import cz.crcs.ectester.reader.response.Response; +import javacard.security.KeyPair; + +import javax.smartcardio.CardException; +import java.io.IOException; +import java.util.*; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class TestSuite { + + EC_Store dataStore; + ECTester.Config cfg; + DirtyLogger systemOut; + String name; + List tests = new LinkedList<>(); + + TestSuite(EC_Store dataStore, ECTester.Config cfg, DirtyLogger systemOut, String name) { + this.dataStore = dataStore; + this.cfg = cfg; + this.systemOut = systemOut; + this.name = name; + } + + public List run(CardMngr cardManager) throws CardException, IOException { + for (Test t : tests) { + if (!t.hasRun()) { + t.run(); + systemOut.println(t.toString()); + } + } + return tests; + } + + public List getTests() { + return Collections.unmodifiableList(tests); + } + + public String getName() { + return name; + } + + /** + * @param cardManager cardManager to send APDU through + * @param generateExpected expected result of the Generate command + * @param ecdhExpected expected result of the ordinary ECDH command + * @param ecdsaExpected expected result of the ordinary ECDSA command + * @return tests to run + */ + List testCurve(CardMngr cardManager, Test.Result generateExpected, Test.Result ecdhExpected, Test.Result ecdsaExpected) { + List tests = new LinkedList<>(); + + tests.add(new Test(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_BOTH), generateExpected)); + tests.add(new Test(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, EC_Consts.KA_ECDH), ecdhExpected)); + tests.add(new Test(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_COMPRESS, EC_Consts.KA_ECDH), ecdhExpected)); + tests.add(new Test(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_ONE, EC_Consts.KA_ECDH), Test.Result.FAILURE)); + tests.add(new Test(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_ZERO, EC_Consts.KA_ECDH), Test.Result.FAILURE)); + tests.add(new Test(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_MAX, EC_Consts.KA_ECDH), Test.Result.FAILURE)); + tests.add(new Test(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_FULLRANDOM, EC_Consts.KA_ECDH), Test.Result.FAILURE)); + tests.add(new Test(new Command.ECDSA(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, null), ecdsaExpected)); + + return tests; + } + + /** + * @param cardManager cardManager to send APDU through + * @param category category to test + * @param field field to test (KeyPair.ALG_EC_FP || KeyPair.ALG_EC_F2M) + * @param setExpected expected result of the Set (curve) command + * @param generateExpected expected result of the Generate command + * @param ecdhExpected expected result of the ordinary ECDH command + * @param ecdsaExpected expected result of the ordinary ECDSA command + * @return tests to run + */ + List testCategory(CardMngr cardManager, String category, byte field, Test.Result setExpected, Test.Result generateExpected, Test.Result ecdhExpected, Test.Result ecdsaExpected) { + List tests = new LinkedList<>(); + Map curves = dataStore.getObjects(EC_Curve.class, category); + if (curves == null) + return tests; + for (Map.Entry entry : curves.entrySet()) { + EC_Curve curve = entry.getValue(); + if (curve.getField() == field && (curve.getBits() == cfg.bits || cfg.all)) { + tests.add(new Test(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), field), Test.Result.SUCCESS)); + tests.add(new Test(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), setExpected)); + tests.addAll(testCurve(cardManager, generateExpected, ecdhExpected, ecdsaExpected)); + tests.add(new Test(new Command.Cleanup(cardManager), Test.Result.ANY)); + } + } + + return tests; + } + + public static class Default extends TestSuite { + + public Default(EC_Store dataStore, ECTester.Config cfg, DirtyLogger systemOut) { + super(dataStore, cfg, systemOut, "default"); + } + + @Override + public List run(CardMngr cardManager) throws IOException, CardException { + tests.add(new Test(new Command.Support(cardManager), Test.Result.ANY)); + if (cfg.namedCurve != null) { + if (cfg.primeField) { + tests.addAll(testCategory(cardManager, cfg.namedCurve, KeyPair.ALG_EC_FP, Test.Result.SUCCESS, Test.Result.SUCCESS, Test.Result.SUCCESS, Test.Result.SUCCESS)); + } + if (cfg.binaryField) { + tests.addAll(testCategory(cardManager, cfg.namedCurve, KeyPair.ALG_EC_F2M, Test.Result.SUCCESS, Test.Result.SUCCESS, Test.Result.SUCCESS, Test.Result.SUCCESS)); + } + } else { + if (cfg.all) { + if (cfg.primeField) { + //iterate over prime curve sizes used: EC_Consts.FP_SIZES + for (short keyLength : EC_Consts.FP_SIZES) { + defaultTests(cardManager, keyLength, KeyPair.ALG_EC_FP); + } + } + if (cfg.binaryField) { + //iterate over binary curve sizes used: EC_Consts.F2M_SIZES + for (short keyLength : EC_Consts.F2M_SIZES) { + defaultTests(cardManager, keyLength, KeyPair.ALG_EC_F2M); + } + } + } else { + if (cfg.primeField) { + defaultTests(cardManager, (short) cfg.bits, KeyPair.ALG_EC_FP); + } + + if (cfg.binaryField) { + defaultTests(cardManager, (short) cfg.bits, KeyPair.ALG_EC_F2M); + } + } + } + return super.run(cardManager); + } + + private void defaultTests(CardMngr cardManager, short keyLength, byte keyType) throws IOException { + tests.add(new Test(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, keyLength, keyType), Test.Result.SUCCESS)); + Command curve = Command.prepareCurve(cardManager, dataStore, cfg, ECTesterApplet.KEYPAIR_BOTH, keyLength, keyType); + if (curve != null) + tests.add(new Test(curve, Test.Result.SUCCESS)); + tests.addAll(testCurve(cardManager, Test.Result.SUCCESS, Test.Result.SUCCESS, Test.Result.SUCCESS)); + tests.add(new Test(new Command.Cleanup(cardManager), Test.Result.ANY)); + } + } + + public static class TestVectors extends TestSuite { + + public TestVectors(EC_Store dataStore, ECTester.Config cfg, DirtyLogger systemOut) { + super(dataStore, cfg, systemOut, "test"); + } + + @Override + public List run(CardMngr cardManager) throws IOException, CardException { + /* Set original curves (secg/nist/brainpool). Set keypairs from test vectors. + * Do ECDH both ways, export and verify that the result is correct. + */ + Map results = dataStore.getObjects(EC_KAResult.class, "test"); + for (EC_KAResult result : results.values()) { + EC_Curve curve = dataStore.getObject(EC_Curve.class, result.getCurve()); + if (cfg.namedCurve != null && !(result.getCurve().startsWith(cfg.namedCurve) || result.getCurve().equals(cfg.namedCurve))) { + continue; + } + if (curve.getBits() != cfg.bits && !cfg.all) { + continue; + } + if (curve.getField() == KeyPair.ALG_EC_FP && !cfg.primeField || curve.getField() == KeyPair.ALG_EC_F2M && !cfg.binaryField) { + continue; + } + EC_Params onekey = dataStore.getObject(EC_Keypair.class, result.getOneKey()); + if (onekey == null) { + onekey = dataStore.getObject(EC_Key.Private.class, result.getOneKey()); + } + EC_Params otherkey = dataStore.getObject(EC_Keypair.class, result.getOtherKey()); + if (otherkey == null) { + otherkey = dataStore.getObject(EC_Key.Public.class, result.getOtherKey()); + } + if (onekey == null || otherkey == null) { + throw new IOException("Test vector keys couldn't be located."); + } + + 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 Composite extends TestSuite { + + public Composite(EC_Store dataStore, ECTester.Config cfg, DirtyLogger systemOut) { + super(dataStore, cfg, systemOut, "composite"); + } + + @Override + public List run(CardMngr cardManager) throws IOException, CardException { + /* Do the default tests with the public keys set to provided smallorder keys + * over composite order curves. Essentially small subgroup attacks. + * These should fail, the curves aren't safe so that if the computation with + * a small order public key succeeds the private key modulo the public key order + * is revealed. + */ + Map keys = dataStore.getObjects(EC_Key.class, "composite"); + for (EC_Key key : keys.values()) { + EC_Curve curve = dataStore.getObject(EC_Curve.class, key.getCurve()); + if (cfg.namedCurve != null && !(key.getCurve().startsWith(cfg.namedCurve) || key.getCurve().equals(cfg.namedCurve))) { + continue; + } + if (curve.getField() == KeyPair.ALG_EC_FP && !cfg.primeField || curve.getField() == KeyPair.ALG_EC_F2M && !cfg.binaryField) { + continue; + } + if ((curve.getBits() == cfg.bits || cfg.all)) { + tests.add(new Test(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.ECDH_direct(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, EC_Consts.KA_ECDH, key.flatten()), 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, DirtyLogger systemOut) { + super(dataStore, cfg, systemOut, "invalid"); + } + + @Override + public List run(CardMngr cardManager) throws CardException, IOException { + /* Set original curves (secg/nist/brainpool). Generate local. + * Try ECDH with invalid public keys of increasing (or decreasing) order. + */ + Map pubkeys = dataStore.getObjects(EC_Key.Public.class, "invalid"); + Map> curves = new HashMap<>(); + for (EC_Key.Public key : pubkeys.values()) { + EC_Curve curve = dataStore.getObject(EC_Curve.class, key.getCurve()); + if (cfg.namedCurve != null && !(key.getCurve().startsWith(cfg.namedCurve) || key.getCurve().equals(cfg.namedCurve))) { + continue; + } + if (curve.getBits() != cfg.bits && !cfg.all) { + continue; + } + if (curve.getField() == KeyPair.ALG_EC_FP && !cfg.primeField || curve.getField() == KeyPair.ALG_EC_F2M && !cfg.binaryField) { + continue; + } + List keys = curves.getOrDefault(curve, new LinkedList<>()); + keys.add(key); + curves.putIfAbsent(curve, keys); + } + for (Map.Entry> e : curves.entrySet()) { + EC_Curve curve = e.getKey(); + List keys = e.getValue(); + + 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)); + for (EC_Key.Public pub : keys) { + // tests.add(new Test(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, pub.getParams(), pub.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_ANY), Test.Result.FAILURE)); + tests.add(new Test(new Command.ECDH_direct(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, EC_Consts.KA_ANY, pub.flatten()), Test.Result.FAILURE)); + } + tests.add(new Test(new Command.Cleanup(cardManager), Test.Result.ANY)); + } + + return super.run(cardManager); + } + } + + public static class Wrong extends TestSuite { + + public Wrong(EC_Store dataStore, ECTester.Config cfg, DirtyLogger systemOut) { + super(dataStore, cfg, systemOut, "wrong"); + } + + @Override + public List run(CardMngr cardManager) throws CardException, IOException { + /* Just do the default tests on the wrong curves. + * These should generally fail, the curves aren't curves. + */ + if (cfg.primeField) { + tests.addAll(testCategory(cardManager, cfg.testSuite, KeyPair.ALG_EC_FP, Test.Result.FAILURE, Test.Result.FAILURE, Test.Result.FAILURE, Test.Result.FAILURE)); + } + if (cfg.binaryField) { + tests.addAll(testCategory(cardManager, cfg.testSuite, KeyPair.ALG_EC_F2M, Test.Result.FAILURE, Test.Result.FAILURE, Test.Result.FAILURE, Test.Result.FAILURE)); + } + return super.run(cardManager); + } + } +} -- cgit v1.2.3-70-g09d2