From c676bacffd1305e1efc49b34d87ffd0a0a695ea7 Mon Sep 17 00:00:00 2001 From: J08nY Date: Tue, 6 Nov 2018 16:12:09 +0100 Subject: Backport applet to JavaCard 2.2.1 to support more cards. --- src/cz/crcs/ectester/reader/ECTesterReader.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/cz/crcs/ectester/reader/ECTesterReader.java') diff --git a/src/cz/crcs/ectester/reader/ECTesterReader.java b/src/cz/crcs/ectester/reader/ECTesterReader.java index 0c04453..c3c0e13 100644 --- a/src/cz/crcs/ectester/reader/ECTesterReader.java +++ b/src/cz/crcs/ectester/reader/ECTesterReader.java @@ -144,9 +144,8 @@ public class ECTesterReader { cardManager.send(SELECT_ECTESTERAPPLET); } - // Setup logger, testWriter and respWriter + // Setup logger and respWriter logger = new OutputLogger(true, cfg.log); - respWriter = new ResponseWriter(logger.getPrintStream()); //do action -- cgit v1.2.3-70-g09d2 From f0ae5fdc1ba778acc922d5269a5523a88ca97976 Mon Sep 17 00:00:00 2001 From: J08nY Date: Wed, 7 Nov 2018 11:20:06 +0100 Subject: Add --info command to get and output applet info. --- README.md | 157 ++++++++++++--------- src/cz/crcs/ectester/reader/ECTesterReader.java | 24 +++- src/cz/crcs/ectester/reader/response/Response.java | 7 +- 3 files changed, 120 insertions(+), 68 deletions(-) (limited to 'src/cz/crcs/ectester/reader/ECTesterReader.java') diff --git a/README.md b/README.md index 4112595..da272ce 100644 --- a/README.md +++ b/README.md @@ -34,71 +34,84 @@ See `java -jar ECTesterReader.jar -h` for more. ### Options ``` - -dsa,--ecdsa Sign data with ECDSA, [count] times. - -t,--test Test ECC support. [test_suite]: - - default: - - invalid: - - compression: - - twist: - - degenerate: - - cofactor: - - wrong: - - composite: - - test-vectors: - -dh,--ecdh Do EC KeyAgreement (ECDH...), [count] - times. - -e,--export Export the defaut curve parameters of - the card(if any). - -V,--version Print version info. - -ln,--list-named Print the list of supported named - curves and keys. - -h,--help Print help. - - -a,--all Test all curve sizes. - -b,--bit-size Set curve size. - - -fp,--prime-field Use a prime field. - -f2m,--binary-field Use a binary field. - - -c,--curve Use curve from file - (field,a,b,gx,gy,r,k). - -nc,--named-curve Use a named curve, from CurveDB: - - -u,--custom Use a custom curve (applet-side - embedded, SECG curves). - -npub,--named-public Use public key from KeyDB: - -pub,--public Use public key from file - (wx,wy). - -priv,--private Use private key from file - (s). - -npriv,--named-private Use private key from KeyDB: - -k,--key Use keyPair from file  - (wx,wy,s). - -nk,--named-key Use keyPair from KeyDB: - - -i,--input Input from file , for ECDSA - signing. - -o,--output Output into file . - -l,--log Log output into file [log_file]. - -v,--verbose Turn on verbose logging. - --format Output format to use. One of: - text,yml,xml. - -f,--fresh Generate fresh keys (set domain - parameters before every generation). - --cleanup Send the cleanup command trigerring - JCSystem.requestObjectDeletion() - after some operations. - -s,--simulate Simulate a card with jcardsim instead - of using a terminal. - -y,--yes Accept all warnings and prompts. - - -ka,--ka-type Set KeyAgreement object [type], - corresponds to JC.KeyAgreement - constants. - -sig,--sig-type Set Signature object [type], - corresponds to JC.Signature constants. - -C,--color Print stuff with color, requires ANSI - terminal. + -V,--version Print version info. + -h,--help Print help. + -ln,--list-named Print the list of supported named + curves and keys, (CurveDB and KeyDB). + -ls,--list-suites List supported test suites. + -e,--export Export the defaut curve parameters + of the card(if any). + -g,--generate Generate of EC keys. + -t,--test Test ECC support. Optionally specify + a test number to run only a part of + a test suite. : + - default + - compression + - invalid + - twist + - degenerate + - cofactor + - wrong + - signature + - composite + - test-vectors + - edge-cases + - miscellaneous + -dh,--ecdh Do EC KeyAgreement (ECDH...), + [count] times. + -dsa,--ecdsa Sign data with ECDSA, [count] times. + -nf,--info Get applet info. + + -b,--bit-size Set curve size. + -fp,--prime-field Use a prime field. + -f2m,--binary-field Use a binary field. + + -nc,--named-curve Use a named curve, from CurveDB: + + -c,--curve Use curve from file + (field,a,b,gx,gy,r,k). + -u,--custom Use a custom curve (applet-side + embedded, SECG curves). + + -npub,--named-public Use public key from KeyDB: + -pub,--public Use public key from file + (wx,wy). + + -npriv,--named-private Use private key from KeyDB: + -priv,--private Use private key from file + (s). + + -nk,--named-key Use KeyPair from KeyDB: + -k,--key Use KeyPair from file  + (wx,wy,s). + + -i,--input Input from file , for + ECDSA signing. + -o,--output Output into file . The + file can be prefixed by the format + (one of text,yml,xml), such as: + xml:. + -l,--log Log output into file [log_file]. + -v,--verbose Turn on verbose logging. + --format Output format to use. One of: + text,yml,xml. + + -f,--fresh Generate fresh keys (set domain + parameters before every generation). + --cleanup Send the cleanup command trigerring + JCSystem.requestObjectDeletion() + after some operations. + -s,--simulate Simulate a card with jcardsim + instead of using a terminal. + -y,--yes Accept all warnings and prompts. + -ka,--ka-type Set KeyAgreement object [type], + corresponds to JavaCard KeyAgreement + constants. + -sig,--sig-type Set Signature object [type], + corresponds to JavaCard Signature + constants. + -C,--color Print stuff with color, requires + ANSI terminal. ``` ### Actions @@ -160,6 +173,20 @@ For example: For more info about the curves and curve categories see [CURVES](docs/CURVES.md). +#### List test suites +`-ls / --list-suites` + +Lists the implemented test suites and gives their short description. + +#### Get applet info +`-nf / --info` + +Get and print ECTester applet info from an applet installed on a card. Outputs: + - ECTester applet version + - ECTester APDU support + - JavaCard API version + - JavaCard cleanup support + ### Example Snippet below shows running the default test suite while simulating(`-s`), so using JCardSim. diff --git a/src/cz/crcs/ectester/reader/ECTesterReader.java b/src/cz/crcs/ectester/reader/ECTesterReader.java index c3c0e13..1359dc2 100644 --- a/src/cz/crcs/ectester/reader/ECTesterReader.java +++ b/src/cz/crcs/ectester/reader/ECTesterReader.java @@ -37,10 +37,12 @@ import cz.crcs.ectester.reader.output.FileTestWriter; import cz.crcs.ectester.reader.output.ResponseWriter; import cz.crcs.ectester.reader.response.Response; import cz.crcs.ectester.reader.test.*; +import javacard.framework.ISO7816; import javacard.security.KeyPair; import org.apache.commons.cli.*; import javax.smartcardio.CardException; +import javax.smartcardio.ResponseAPDU; import javax.xml.parsers.ParserConfigurationException; import java.io.*; import java.net.URL; @@ -141,7 +143,12 @@ public class ECTesterReader { System.err.println(Colors.error("Failed to connect to card.")); System.exit(1); } - cardManager.send(SELECT_ECTESTERAPPLET); + ResponseAPDU selectResp = cardManager.send(SELECT_ECTESTERAPPLET); + if ((short) selectResp.getSW() != ISO7816.SW_NO_ERROR) { + System.err.println(Colors.error("Failed to select ECTester applet, is it installed?")); + cardManager.disconnectFromCard(); + System.exit(1); + } } // Setup logger and respWriter @@ -159,6 +166,8 @@ public class ECTesterReader { ecdh(); } else if (cli.hasOption("ecdsa")) { ecdsa(); + } else if (cli.hasOption("info")) { + info(); } //disconnect @@ -231,6 +240,8 @@ public class ECTesterReader { * -dh / --ecdh [count]] * -dsa / --ecdsa [count] * -ln / --list-named [obj] + * -ls / --list-suites + * -nfo / --info * * Options: * -b / --bit-size // -a / --all @@ -271,12 +282,13 @@ public class ECTesterReader { actions.addOption(Option.builder("V").longOpt("version").desc("Print version info.").build()); actions.addOption(Option.builder("h").longOpt("help").desc("Print help.").build()); actions.addOption(Option.builder("ln").longOpt("list-named").desc("Print the list of supported named curves and keys.").hasArg().argName("what").optionalArg(true).build()); + actions.addOption(Option.builder("ls").longOpt("list-suites").desc("List supported test suites.").build()); actions.addOption(Option.builder("e").longOpt("export").desc("Export the defaut curve parameters of the card(if any).").build()); actions.addOption(Option.builder("g").longOpt("generate").desc("Generate of EC keys.").hasArg().argName("amount").optionalArg(true).build()); actions.addOption(Option.builder("t").longOpt("test").desc("Test ECC support. Optionally specify a test number to run only a part of a test suite. :\n- default:\n- compression:\n- invalid:\n- twist:\n- degenerate:\n- cofactor:\n- wrong:\n- signature:\n- composite:\n- test-vectors:\n- edge-cases:\n- miscellaneous:").hasArg().argName("test_suite[:from[:to]]").optionalArg(true).build()); actions.addOption(Option.builder("dh").longOpt("ecdh").desc("Do EC KeyAgreement (ECDH...), [count] times.").hasArg().argName("count").optionalArg(true).build()); actions.addOption(Option.builder("dsa").longOpt("ecdsa").desc("Sign data with ECDSA, [count] times.").hasArg().argName("count").optionalArg(true).build()); - actions.addOption(Option.builder("ls").longOpt("list-suites").desc("List supported test suites.").build()); + actions.addOption(Option.builder("nf").longOpt("info").desc("Get applet info.").build()); opts.addOptionGroup(actions); @@ -346,6 +358,14 @@ public class ECTesterReader { } } + private void info() throws CardException { + Response.GetInfo info = new Command.GetInfo(cardManager).send(); + System.out.println(String.format("ECTester applet version: %s", info.getVersion())); + System.out.println(String.format("ECTester applet APDU support: %s", (info.getBase() == ECTesterApplet.BASE_221) ? "basic" : "extended length")); + System.out.println(String.format("JavaCard API version: %.1f", info.getJavaCardVersion())); + System.out.println(String.format("JavaCard supports system cleanup: %s", info.getCleanupSupport())); + } + /** * Exports default card/simulation EC domain parameters to output file. * diff --git a/src/cz/crcs/ectester/reader/response/Response.java b/src/cz/crcs/ectester/reader/response/Response.java index b0cd0f8..afac1bc 100644 --- a/src/cz/crcs/ectester/reader/response/Response.java +++ b/src/cz/crcs/ectester/reader/response/Response.java @@ -461,7 +461,12 @@ public abstract class Response { public float getJavaCardVersion() { byte major = (byte) (jcVersion >> 8); byte minor = (byte) (jcVersion & 0xff); - int minorSize = (int) Math.ceil(Math.log10(minor)); + int minorSize; + if (minor == 0) { + minorSize = 1; + } else { + minorSize = (int) Math.ceil(Math.log10(minor)); + } return (major + ((float) (minor) / (minorSize * 10))); } -- cgit v1.2.3-70-g09d2 From 9e157bc1d0a42848bb8f780f4f7b294ad535feda Mon Sep 17 00:00:00 2001 From: J08nY Date: Fri, 9 Nov 2018 19:42:35 +0100 Subject: FIx loading of F2m field, fix some utility functions. --- src/cz/crcs/ectester/applet/ECKeyGenerator.java | 9 ++++----- src/cz/crcs/ectester/reader/ECTesterReader.java | 4 ++-- src/cz/crcs/ectester/reader/command/Command.java | 9 +++++++++ 3 files changed, 15 insertions(+), 7 deletions(-) (limited to 'src/cz/crcs/ectester/reader/ECTesterReader.java') diff --git a/src/cz/crcs/ectester/applet/ECKeyGenerator.java b/src/cz/crcs/ectester/applet/ECKeyGenerator.java index 7c52e8f..4326752 100644 --- a/src/cz/crcs/ectester/applet/ECKeyGenerator.java +++ b/src/cz/crcs/ectester/applet/ECKeyGenerator.java @@ -204,11 +204,10 @@ public class ECKeyGenerator { short i1 = Util.getShort(data, (short) (offset + 2)); short i2 = Util.getShort(data, (short) (offset + 4)); short i3 = Util.getShort(data, (short) (offset + 6)); -// if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setFieldF2M(i1, i2, i3); -// if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setFieldF2M(i1, i2, i3); - // TODO: fix this, ^^ fails on jcardsim, but is up to spec - if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setFieldF2M(i3, i2, i1); - if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setFieldF2M(i3, i2, i1); + if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setFieldF2M(i1, i2, i3); + if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setFieldF2M(i1, i2, i3); + // if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setFieldF2M(i3, i2, i1); + // if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setFieldF2M(i3, i2, i1); } else { sw = ISO7816.SW_UNKNOWN; } diff --git a/src/cz/crcs/ectester/reader/ECTesterReader.java b/src/cz/crcs/ectester/reader/ECTesterReader.java index 1359dc2..2a74ea7 100644 --- a/src/cz/crcs/ectester/reader/ECTesterReader.java +++ b/src/cz/crcs/ectester/reader/ECTesterReader.java @@ -424,10 +424,10 @@ public class ECTesterReader { */ private void generate() throws CardException, IOException { byte keyClass = cfg.primeField ? KeyPair.ALG_EC_FP : KeyPair.ALG_EC_F2M; + Command curve = Command.prepareCurve(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_LOCAL, cfg.bits, keyClass); Response allocate = new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_LOCAL, cfg.bits, keyClass).send(); respWriter.outputResponse(allocate); - Command curve = Command.prepareCurve(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_LOCAL, cfg.bits, keyClass); OutputStreamWriter keysFile = FileUtil.openFiles(cfg.outputs); keysFile.write("index;time;pubW;privS\n"); @@ -552,10 +552,10 @@ public class ECTesterReader { */ private void ecdh() throws IOException, CardException { byte keyClass = cfg.primeField ? KeyPair.ALG_EC_FP : KeyPair.ALG_EC_F2M; + Command curve = Command.prepareCurve(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_BOTH, cfg.bits, keyClass); List prepare = new LinkedList<>(); prepare.add(new Command.AllocateKeyAgreement(cardManager, cfg.ECKAType).send()); // Prepare KeyAgreement or required type prepare.add(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, cfg.bits, keyClass).send()); - Command curve = Command.prepareCurve(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_BOTH, cfg.bits, keyClass); if (curve != null) prepare.add(curve.send()); diff --git a/src/cz/crcs/ectester/reader/command/Command.java b/src/cz/crcs/ectester/reader/command/Command.java index d2e08ee..7a3f3b0 100644 --- a/src/cz/crcs/ectester/reader/command/Command.java +++ b/src/cz/crcs/ectester/reader/command/Command.java @@ -133,6 +133,9 @@ public abstract class Command implements Cloneable { } else { keypair = dataStore.getObject(EC_Keypair.class, cfg.namedKey); } + if (keypair == null) { + throw new IOException("KeyPair not found."); + } data = keypair.flatten(); if (data == null) { @@ -155,6 +158,9 @@ public abstract class Command implements Cloneable { pub = dataStore.getObject(EC_Keypair.class, cfg.namedPublicKey); } } + if (pub == null) { + throw new IOException("Public key not found."); + } byte[] pubkey = pub.flatten(EC_Consts.PARAMETER_W); if (pubkey == null) { @@ -177,6 +183,9 @@ public abstract class Command implements Cloneable { priv = dataStore.getObject(EC_Keypair.class, cfg.namedPrivateKey); } } + if (priv == null) { + throw new IOException("Private key not found."); + } byte[] privkey = priv.flatten(EC_Consts.PARAMETER_S); if (privkey == null) { -- cgit v1.2.3-70-g09d2 From 25bd6fd367c067f9311cb04d5acb703d1e0bccc6 Mon Sep 17 00:00:00 2001 From: J08nY Date: Wed, 14 Nov 2018 23:43:31 +0100 Subject: Add ECDH validation tests using BouncyCastle. --- nbproject/reader/manifest.mf | 2 +- src/cz/crcs/ectester/applet/AppletBase.java | 8 ++ .../ectester/common/output/BaseTextTestWriter.java | 3 + src/cz/crcs/ectester/reader/ECTesterReader.java | 30 ++++-- .../ectester/reader/output/TextTestWriter.java | 1 + .../crcs/ectester/reader/output/XMLTestWriter.java | 18 ++++ .../ectester/reader/output/YAMLTestWriter.java | 6 ++ src/cz/crcs/ectester/reader/response/Response.java | 28 ++++++ .../ectester/reader/test/CardCofactorSuite.java | 2 +- .../ectester/reader/test/CardTestVectorSuite.java | 103 ++++++++++++++++++++- 10 files changed, 187 insertions(+), 14 deletions(-) (limited to 'src/cz/crcs/ectester/reader/ECTesterReader.java') diff --git a/nbproject/reader/manifest.mf b/nbproject/reader/manifest.mf index c2a00ee..4bb6334 100644 --- a/nbproject/reader/manifest.mf +++ b/nbproject/reader/manifest.mf @@ -1,4 +1,4 @@ Manifest-Version: 1.0 -Class-Path: lib/jcardsim-3.0.4-SNAPSHOT.jar lib/commons-cli-1.4.jar lib/snakeyaml-1.19.jar +Class-Path: lib/bcprov-jdk15on-1.58.jar lib/jcardsim-3.0.4-SNAPSHOT.jar lib/commons-cli-1.4.jar lib/snakeyaml-1.19.jar Main-Class: cz.crcs.ectester.reader.ECTesterReader diff --git a/src/cz/crcs/ectester/applet/AppletBase.java b/src/cz/crcs/ectester/applet/AppletBase.java index 24272c5..4e488b5 100644 --- a/src/cz/crcs/ectester/applet/AppletBase.java +++ b/src/cz/crcs/ectester/applet/AppletBase.java @@ -873,6 +873,14 @@ public abstract class AppletBase extends Applet { length += 2; Util.setShort(buffer, (short) (offset + length), (short) (JCSystem.isObjectDeletionSupported() ? 1 : 0)); length += 2; + Util.setShort(buffer, (short) (offset + length), (short) buffer.length); + length += 2; + Util.setShort(buffer, (short) (offset + length), (short) ramArray.length); + length += 2; + Util.setShort(buffer, (short) (offset + length), (short) ramArray2.length); + length += 2; + Util.setShort(buffer, (short) (offset + length), (short) apduArray.length); + length += 2; return length; } } diff --git a/src/cz/crcs/ectester/common/output/BaseTextTestWriter.java b/src/cz/crcs/ectester/common/output/BaseTextTestWriter.java index f60f8bb..85b32a4 100644 --- a/src/cz/crcs/ectester/common/output/BaseTextTestWriter.java +++ b/src/cz/crcs/ectester/common/output/BaseTextTestWriter.java @@ -106,6 +106,9 @@ public abstract class BaseTextTestWriter implements TestWriter { } else { SimpleTest test = (SimpleTest) t; out.append(testableString(test.getTestable())); + if (t.getResult().getCause() != null) { + out.append(" ┃ ").append(t.getResult().getCause().toString()); + } } return line + out.toString(); } diff --git a/src/cz/crcs/ectester/reader/ECTesterReader.java b/src/cz/crcs/ectester/reader/ECTesterReader.java index 2a74ea7..377e2ff 100644 --- a/src/cz/crcs/ectester/reader/ECTesterReader.java +++ b/src/cz/crcs/ectester/reader/ECTesterReader.java @@ -40,6 +40,7 @@ import cz.crcs.ectester.reader.test.*; import javacard.framework.ISO7816; import javacard.security.KeyPair; import org.apache.commons.cli.*; +import org.bouncycastle.jce.provider.BouncyCastleProvider; import javax.smartcardio.CardException; import javax.smartcardio.ResponseAPDU; @@ -48,6 +49,7 @@ import java.io.*; import java.net.URL; import java.net.URLClassLoader; import java.nio.file.Files; +import java.security.Security; import java.util.Arrays; import java.util.LinkedList; import java.util.List; @@ -145,9 +147,9 @@ public class ECTesterReader { } ResponseAPDU selectResp = cardManager.send(SELECT_ECTESTERAPPLET); if ((short) selectResp.getSW() != ISO7816.SW_NO_ERROR) { - System.err.println(Colors.error("Failed to select ECTester applet, is it installed?")); - cardManager.disconnectFromCard(); - System.exit(1); + System.err.println(Colors.error("Failed to select ECTester applet, is it installed?")); + cardManager.disconnectFromCard(); + System.exit(1); } } @@ -155,6 +157,12 @@ public class ECTesterReader { logger = new OutputLogger(true, cfg.log); respWriter = new ResponseWriter(logger.getPrintStream()); + // Try adding the BouncyCastleProvider, which might be used in some parts of ECTester. + try { + Security.addProvider(new BouncyCastleProvider()); + } catch (SecurityException | NoClassDefFoundError ignored) { + } + //do action if (cli.hasOption("export")) { export(); @@ -167,7 +175,7 @@ public class ECTesterReader { } else if (cli.hasOption("ecdsa")) { ecdsa(); } else if (cli.hasOption("info")) { - info(); + info(); } //disconnect @@ -282,7 +290,7 @@ public class ECTesterReader { actions.addOption(Option.builder("V").longOpt("version").desc("Print version info.").build()); actions.addOption(Option.builder("h").longOpt("help").desc("Print help.").build()); actions.addOption(Option.builder("ln").longOpt("list-named").desc("Print the list of supported named curves and keys.").hasArg().argName("what").optionalArg(true).build()); - actions.addOption(Option.builder("ls").longOpt("list-suites").desc("List supported test suites.").build()); + actions.addOption(Option.builder("ls").longOpt("list-suites").desc("List supported test suites.").build()); actions.addOption(Option.builder("e").longOpt("export").desc("Export the defaut curve parameters of the card(if any).").build()); actions.addOption(Option.builder("g").longOpt("generate").desc("Generate of EC keys.").hasArg().argName("amount").optionalArg(true).build()); actions.addOption(Option.builder("t").longOpt("test").desc("Test ECC support. Optionally specify a test number to run only a part of a test suite. :\n- default:\n- compression:\n- invalid:\n- twist:\n- degenerate:\n- cofactor:\n- wrong:\n- signature:\n- composite:\n- test-vectors:\n- edge-cases:\n- miscellaneous:").hasArg().argName("test_suite[:from[:to]]").optionalArg(true).build()); @@ -359,11 +367,12 @@ public class ECTesterReader { } private void info() throws CardException { - Response.GetInfo info = new Command.GetInfo(cardManager).send(); - System.out.println(String.format("ECTester applet version: %s", info.getVersion())); - System.out.println(String.format("ECTester applet APDU support: %s", (info.getBase() == ECTesterApplet.BASE_221) ? "basic" : "extended length")); - System.out.println(String.format("JavaCard API version: %.1f", info.getJavaCardVersion())); - System.out.println(String.format("JavaCard supports system cleanup: %s", info.getCleanupSupport())); + Response.GetInfo info = new Command.GetInfo(cardManager).send(); + System.out.println(String.format("ECTester applet version: %s", info.getVersion())); + System.out.println(String.format("ECTester applet APDU support: %s", (info.getBase() == ECTesterApplet.BASE_221) ? "basic" : "extended length")); + System.out.println(String.format("JavaCard API version: %.1f", info.getJavaCardVersion())); + System.out.println(String.format("JavaCard supports system cleanup: %s", info.getCleanupSupport())); + System.out.println(String.format("Array sizes (apduBuf, ram, ram2, apduArr): %d %d %d %d", info.getApduBufferLength(), info.getRamArrayLength(), info.getRamArray2Length(), info.getApduArrayLength())); } /** @@ -446,6 +455,7 @@ public class ECTesterReader { respWriter.outputResponse(response); Response.Export export = new Command.Export(cardManager, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.KEY_BOTH, EC_Consts.PARAMETERS_KEYPAIR).send(); + respWriter.outputResponse(export); if (!response.successful() || !export.successful()) { if (retry < 10) { diff --git a/src/cz/crcs/ectester/reader/output/TextTestWriter.java b/src/cz/crcs/ectester/reader/output/TextTestWriter.java index a755857..e89d403 100644 --- a/src/cz/crcs/ectester/reader/output/TextTestWriter.java +++ b/src/cz/crcs/ectester/reader/output/TextTestWriter.java @@ -51,6 +51,7 @@ public class TextTestWriter extends BaseTextTestWriter { sb.append("═══ ").append(Colors.underline("ECTester applet version:")).append(" ").append(info.getVersion()).append(info.getBase() == ECTesterApplet.BASE_221 ? "" : " (extended length)").append(System.lineSeparator()); sb.append("═══ ").append(Colors.underline("Card ATR:")).append(" ").append(ByteUtil.bytesToHex(cardSuite.getCard().getATR().getBytes(), false)).append(System.lineSeparator()); sb.append("═══ ").append(Colors.underline("JavaCard version:")).append(" ").append(info.getJavaCardVersion()).append(System.lineSeparator()); + sb.append("═══ ").append(Colors.underline("Array sizes (apduBuf, ram, ram2, apduArr):")).append(" ").append(String.format("%d %d %d %d", info.getApduBufferLength(), info.getRamArrayLength(), info.getRamArray2Length(), info.getApduArrayLength())).append(System.lineSeparator()); CardMngr.CPLC cplc = cardSuite.getCard().getCPLC(); if (!cplc.values().isEmpty()) { sb.append("═══ ").append(Colors.underline("Card CPLC data:")).append(System.lineSeparator()); diff --git a/src/cz/crcs/ectester/reader/output/XMLTestWriter.java b/src/cz/crcs/ectester/reader/output/XMLTestWriter.java index 8abdea5..9add072 100644 --- a/src/cz/crcs/ectester/reader/output/XMLTestWriter.java +++ b/src/cz/crcs/ectester/reader/output/XMLTestWriter.java @@ -116,6 +116,24 @@ public class XMLTestWriter extends BaseXMLTestWriter { result.setAttribute("javacard", String.format("%.1f", info.getJavaCardVersion())); result.setAttribute("base", String.format("%#x",info.getBase())); result.setAttribute("cleanup", String.valueOf(info.getCleanupSupport())); + Element arrays = doc.createElement("arrays"); + Element apduBuf = doc.createElement("length"); + apduBuf.setAttribute("name", "apduBuf"); + apduBuf.setTextContent(String.valueOf(info.getApduBufferLength())); + Element ramArray = doc.createElement("length"); + ramArray.setAttribute("name", "ramArray"); + ramArray.setTextContent(String.valueOf(info.getRamArrayLength())); + Element ramArray2 = doc.createElement("length"); + ramArray2.setAttribute("name", "ramArray2"); + ramArray2.setTextContent(String.valueOf(info.getRamArray2Length())); + Element apduArray = doc.createElement("length"); + apduArray.setAttribute("name", "apduArray"); + apduArray.setTextContent(String.valueOf(info.getApduArrayLength())); + arrays.appendChild(apduBuf); + arrays.appendChild(ramArray); + arrays.appendChild(ramArray2); + arrays.appendChild(apduArray); + result.appendChild(arrays); } catch (CardException ignored) { } return result; diff --git a/src/cz/crcs/ectester/reader/output/YAMLTestWriter.java b/src/cz/crcs/ectester/reader/output/YAMLTestWriter.java index 92de6c6..56ecb71 100644 --- a/src/cz/crcs/ectester/reader/output/YAMLTestWriter.java +++ b/src/cz/crcs/ectester/reader/output/YAMLTestWriter.java @@ -91,6 +91,12 @@ public class YAMLTestWriter extends BaseYAMLTestWriter { result.put("javacard", info.getJavaCardVersion()); result.put("base", info.getBase()); result.put("cleanup", info.getCleanupSupport()); + Map arrays = new LinkedHashMap<>(); + arrays.put("apduBuf", Short.toUnsignedInt(info.getApduBufferLength())); + arrays.put("ramArray", Short.toUnsignedInt(info.getRamArrayLength())); + arrays.put("ramArray2", Short.toUnsignedInt(info.getRamArray2Length())); + arrays.put("apduArray", Short.toUnsignedInt(info.getApduArrayLength())); + result.put("arrays", arrays); } catch (CardException ignored) { } return result; diff --git a/src/cz/crcs/ectester/reader/response/Response.java b/src/cz/crcs/ectester/reader/response/Response.java index 6f42ba1..235564e 100644 --- a/src/cz/crcs/ectester/reader/response/Response.java +++ b/src/cz/crcs/ectester/reader/response/Response.java @@ -436,6 +436,10 @@ public abstract class Response { private short base; private short jcVersion; private short cleanupSupport; + private short apduBufferLength; + private short ramArrayLength; + private short ramArray2Length; + private short apduArrayLength; public GetInfo(ResponseAPDU response, String description, long time) { super(response, description, time); @@ -448,6 +452,14 @@ public abstract class Response { jcVersion = ByteUtil.getShort(data, offset); offset += 2; cleanupSupport = ByteUtil.getShort(data, offset); + offset += 2; + apduBufferLength = ByteUtil.getShort(data, offset); + offset += 2; + ramArrayLength = ByteUtil.getShort(data, offset); + offset += 2; + ramArray2Length = ByteUtil.getShort(data, offset); + offset += 2; + apduArrayLength = ByteUtil.getShort(data, offset); } public String getVersion() { @@ -473,5 +485,21 @@ public abstract class Response { public boolean getCleanupSupport() { return cleanupSupport == 1; } + + public short getApduBufferLength() { + return apduBufferLength; + } + + public short getRamArrayLength() { + return ramArrayLength; + } + + public short getRamArray2Length() { + return ramArray2Length; + } + + public short getApduArrayLength() { + return apduArrayLength; + } } } diff --git a/src/cz/crcs/ectester/reader/test/CardCofactorSuite.java b/src/cz/crcs/ectester/reader/test/CardCofactorSuite.java index c8caa8b..172c8af 100644 --- a/src/cz/crcs/ectester/reader/test/CardCofactorSuite.java +++ b/src/cz/crcs/ectester/reader/test/CardCofactorSuite.java @@ -46,7 +46,7 @@ public class CardCofactorSuite extends CardTestSuite { for (EC_Key.Public pub : keys) { Test setPub = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, pub.getParams(), pub.flatten()), Result.ExpectedValue.FAILURE); Test ecdh = CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH), Result.ExpectedValue.FAILURE); - Test objectEcdh = CompoundTest.all(Result.ExpectedValue.SUCCESS, CardUtil.getKATypeString(EC_Consts.KeyAgreement_ALG_EC_SVDP_DH) + " test with degenerate pubkey.", setPub, ecdh); + Test objectEcdh = CompoundTest.any(Result.ExpectedValue.SUCCESS, CardUtil.getKATypeString(EC_Consts.KeyAgreement_ALG_EC_SVDP_DH) + " test with degenerate pubkey.", setPub, ecdh); Command ecdhCommand = new Command.ECDH_direct(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH, pub.flatten()); Test rawEcdh = CommandTest.expect(ecdhCommand, ExpectedValue.FAILURE, "Card correctly rejected point on non-generator subgroup.", "Card incorrectly accepted point on non-generator subgroup."); ecdhTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, pub.getId() + " cofactor key test.", objectEcdh, rawEcdh)); diff --git a/src/cz/crcs/ectester/reader/test/CardTestVectorSuite.java b/src/cz/crcs/ectester/reader/test/CardTestVectorSuite.java index fbdf103..3abcebb 100644 --- a/src/cz/crcs/ectester/reader/test/CardTestVectorSuite.java +++ b/src/cz/crcs/ectester/reader/test/CardTestVectorSuite.java @@ -9,16 +9,28 @@ import cz.crcs.ectester.common.test.Result; import cz.crcs.ectester.common.test.Test; import cz.crcs.ectester.common.test.TestCallback; import cz.crcs.ectester.common.util.ByteUtil; +import cz.crcs.ectester.common.util.CardUtil; +import cz.crcs.ectester.common.util.ECUtil; import cz.crcs.ectester.data.EC_Store; import cz.crcs.ectester.reader.CardMngr; import cz.crcs.ectester.reader.ECTesterReader; import cz.crcs.ectester.reader.command.Command; import cz.crcs.ectester.reader.response.Response; +import javacard.security.KeyPair; +import javax.crypto.KeyAgreement; import java.io.IOException; +import java.math.BigInteger; +import java.security.*; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPrivateKeySpec; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import static cz.crcs.ectester.common.test.Result.ExpectedValue; import static cz.crcs.ectester.common.test.Result.Value; @@ -52,8 +64,13 @@ public class CardTestVectorSuite extends CardTestSuite { throw new IOException("Test vector keys couldn't be located."); } List testVector = new LinkedList<>(); + Test allocate = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS)); + if (!allocate.ok()) { + doTest(CompoundTest.all(ExpectedValue.SUCCESS, "No support for " + curve.getBits() + "b " + CardUtil.getKeyTypeString(curve.getField()) + ".", allocate)); + continue; + } - testVector.add(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS)); + testVector.add(allocate); testVector.add(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.SUCCESS)); testVector.add(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.CURVE_external, EC_Consts.PARAMETER_S, onekey.flatten(EC_Consts.PARAMETER_S)), ExpectedValue.SUCCESS)); testVector.add(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, EC_Consts.PARAMETER_W, otherkey.flatten(EC_Consts.PARAMETER_W)), ExpectedValue.SUCCESS)); @@ -75,7 +92,89 @@ public class CardTestVectorSuite extends CardTestSuite { if (cfg.cleanup) { testVector.add(CommandTest.expect(new Command.Cleanup(this.card), ExpectedValue.ANY)); } - doTest(CompoundTest.greedyAll(ExpectedValue.SUCCESS, "Test vector " + result.getId(), testVector.toArray(new Test[0]))); + doTest(CompoundTest.greedyAll(ExpectedValue.SUCCESS, "Test vector " + result.getId() + ".", testVector.toArray(new Test[0]))); + } + + KeyAgreement ka; + KeyFactory kf; + MessageDigest md; + try { + ka = KeyAgreement.getInstance("ECDH", "BC"); + kf = KeyFactory.getInstance("ECDH", "BC"); + md = MessageDigest.getInstance("SHA1", "BC"); + } catch (NoSuchAlgorithmException | NoSuchProviderException ex) { + return; + } + + List testCurves = new ArrayList<>(); + testCurves.addAll(EC_Store.getInstance().getObjects(EC_Curve.class, "secg").values().stream().filter((curve) -> curve.getField() == KeyPair.ALG_EC_FP).collect(Collectors.toList())); + testCurves.addAll(EC_Store.getInstance().getObjects(EC_Curve.class, "brainpool").values().stream().filter((curve) -> curve.getField() == KeyPair.ALG_EC_FP).collect(Collectors.toList())); + for (EC_Curve curve : testCurves) { + List testVector = new LinkedList<>(); + Test allocate = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS)); + if (!allocate.ok()) { + doTest(CompoundTest.all(ExpectedValue.SUCCESS, "No support for " + curve.getBits() + "b " + CardUtil.getKeyTypeString(curve.getField()) + ".", allocate)); + continue; + } + testVector.add(allocate); + testVector.add(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.SUCCESS)); + testVector.add(CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_BOTH), ExpectedValue.SUCCESS)); + CommandTest export = CommandTest.expect(new Command.Export(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.KEY_BOTH, EC_Consts.PARAMETERS_KEYPAIR), ExpectedValue.ANY); + testVector.add(export); + TestCallback kaCallback = new TestCallback() { + @Override + public Result apply(CommandTestable testable) { + Response.ECDH ecdhData = (Response.ECDH) testable.getResponse(); + if (!ecdhData.successful()) + return new Result(Value.FAILURE, "ECDH was unsuccessful."); + if (!ecdhData.hasSecret()) { + return new Result(Value.FAILURE, "ECDH response did not contain the derived secret."); + } + byte[] secret = ecdhData.getSecret(); + Response.Export keyData = (Response.Export) export.getResponse(); + byte[] pkey = keyData.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_W); + byte[] skey = keyData.getParameter(ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.PARAMETER_S); + ECParameterSpec spec = curve.toSpec(); + ECPrivateKeySpec privKeySpec = new ECPrivateKeySpec(new BigInteger(1, skey), spec); + ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(ECUtil.fromX962(pkey, curve.toCurve()), spec); + PrivateKey privKey; + PublicKey pubkey; + try { + privKey = kf.generatePrivate(privKeySpec); + pubkey = kf.generatePublic(pubKeySpec); + ka.init(privKey); + ka.doPhase(pubkey, true); + byte[] rawDerived = ka.generateSecret(); + int fieldSize = (curve.getBits() + 7) / 8; + if (rawDerived.length < fieldSize) { + byte[] padded = new byte[fieldSize]; + System.arraycopy(rawDerived, 0, padded, fieldSize - rawDerived.length, rawDerived.length); + rawDerived = padded; + } + byte[] derived = md.digest(rawDerived); + if (secret.length != derived.length) { + if (secret.length < derived.length) { + return new Result(Value.FAILURE, String.format("Derived secret was shorter than expected: %d vs %d (expected).", secret.length, derived.length)); + } else { + return new Result(Value.FAILURE, String.format("Derived secret was longer than expected: %d vs %d (expected).", secret.length, derived.length)); + } + } + int diff = ByteUtil.diffBytes(derived, 0, secret, 0, secret.length); + if (diff == secret.length) { + return new Result(Value.SUCCESS, "Derived secret matched expected value."); + } else { + return new Result(Value.FAILURE, "Derived secret does not match expected value, first difference was at byte " + String.valueOf(diff) + "."); + } + } catch (InvalidKeySpecException | InvalidKeyException ex) { + return new Result(Value.SUCCESS, "Result could not be verified. " + ex.getMessage()); + } + } + }; + testVector.add(CommandTest.function(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_TRUE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH), kaCallback)); + if (cfg.cleanup) { + testVector.add(CommandTest.expect(new Command.Cleanup(this.card), ExpectedValue.ANY)); + } + doTest(CompoundTest.greedyAll(ExpectedValue.SUCCESS, "Validation test on " + curve.getId() + ".", testVector.toArray(new Test[0]))); } } } -- cgit v1.2.3-70-g09d2 From 105228a6e842df46ba8523fc5214b3a8833cbef0 Mon Sep 17 00:00:00 2001 From: J08nY Date: Sun, 18 Nov 2018 19:37:59 +0100 Subject: Output duration of keygen and export as well. --- src/cz/crcs/ectester/reader/ECTesterReader.java | 5 +- .../ectester/standalone/ECTesterStandalone.java | 6 +- util/plot_gen.py | 151 +++++++++++++-------- 3 files changed, 97 insertions(+), 65 deletions(-) (limited to 'src/cz/crcs/ectester/reader/ECTesterReader.java') diff --git a/src/cz/crcs/ectester/reader/ECTesterReader.java b/src/cz/crcs/ectester/reader/ECTesterReader.java index 377e2ff..abc7264 100644 --- a/src/cz/crcs/ectester/reader/ECTesterReader.java +++ b/src/cz/crcs/ectester/reader/ECTesterReader.java @@ -439,7 +439,7 @@ public class ECTesterReader { respWriter.outputResponse(allocate); OutputStreamWriter keysFile = FileUtil.openFiles(cfg.outputs); - keysFile.write("index;time;pubW;privS\n"); + keysFile.write("index;genTime;exportTime;pubW;privS\n"); int generated = 0; int retry = 0; @@ -451,7 +451,6 @@ public class ECTesterReader { Command.Generate generate = new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL); Response.Generate response = generate.send(); - long elapsed = response.getDuration(); respWriter.outputResponse(response); Response.Export export = new Command.Export(cardManager, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.KEY_BOTH, EC_Consts.PARAMETERS_KEYPAIR).send(); @@ -469,7 +468,7 @@ public class ECTesterReader { String pub = ByteUtil.bytesToHex(export.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_W), false); String priv = ByteUtil.bytesToHex(export.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_S), false); - String line = String.format("%d;%d;%s;%s\n", generated, elapsed / 1000000, pub, priv); + String line = String.format("%d;%d;%d;%s;%s\n", generated, response.getDuration() / 1000000, export.getDuration() / 1000000, pub, priv); keysFile.write(line); keysFile.flush(); generated++; diff --git a/src/cz/crcs/ectester/standalone/ECTesterStandalone.java b/src/cz/crcs/ectester/standalone/ECTesterStandalone.java index b6f5478..e75274d 100644 --- a/src/cz/crcs/ectester/standalone/ECTesterStandalone.java +++ b/src/cz/crcs/ectester/standalone/ECTesterStandalone.java @@ -325,7 +325,7 @@ public class ECTesterStandalone { kpg.initialize(spec); } - System.out.println("index;nanotime;pubW;privS;secret"); + System.out.println("index;time[nano];pubW;privS;secret"); int amount = Integer.parseInt(cli.getOptionValue("ecdh.amount", "1")); for (int i = 0; i < amount; ++i) { @@ -435,7 +435,7 @@ public class ECTesterStandalone { kpg.initialize(new ECGenParameterSpec(curveName)); } - System.out.println("index;data;signtime;verifytime;pubW;privS;signature;verified"); + System.out.println("index;data;signTime[nano];verifyTime[nano];pubW;privS;signature;verified"); int amount = Integer.parseInt(cli.getOptionValue("ecdsa.amount", "1")); for (int i = 0; i < amount; ++i) { @@ -499,7 +499,7 @@ public class ECTesterStandalone { String curveName = cli.getOptionValue("generate.curve-name"); kpg.initialize(new ECGenParameterSpec(curveName)); } - System.out.println("index;nanotime;pubW;privS"); + System.out.println("index;time[nano];pubW;privS"); int amount = Integer.parseInt(cli.getOptionValue("generate.amount", "1")); for (int i = 0; i < amount; ++i) { diff --git a/util/plot_gen.py b/util/plot_gen.py index 98d8261..f6cd8e4 100755 --- a/util/plot_gen.py +++ b/util/plot_gen.py @@ -13,19 +13,24 @@ import numpy as np import matplotlib.pyplot as plt -import matplotlib.ticker as ticker -import matplotlib.colors as colors -from operator import itemgetter +from matplotlib import ticker, colors from copy import deepcopy import argparse +def hw(i): + res = 0 + while i: + res += 1 + i &= i - 1 + return res + if __name__ == "__main__": parser = argparse.ArgumentParser(description="Plot results of ECTester key generation timing.") parser.add_argument("-o", "--output", dest="output", type=argparse.FileType("wb"), help="Write image to [file], do not display.", metavar="file") - parser.add_argument("--pub", dest="pub", action="store_true", help="Show public key scatter plot.") - parser.add_argument("--priv", dest="priv", action="store_true", help="Show private key scatter plot.") - parser.add_argument("--hist", dest="hist", action="store_true", help="Show histogram.") - parser.add_argument("--hw-hist", dest="hw_hist", action="store_true", help="Show Hamming weight 2D histogram (private key Hamming weight and generation time).") + parser.add_argument("--priv", dest="priv", action="store_true", help="Show private key MSB heatmap plot.") + parser.add_argument("--hist", dest="hist", action="store_true", help="Show keygen time histogram.") + parser.add_argument("--export-hist", dest="export_hist", action="store_true", help="Show export time histogram.") + parser.add_argument("--hw-hist", dest="hw_hist", action="store_true", help="Show Hamming weight heatmap (private key Hamming weight and keygen time).") parser.add_argument("--skip-first", dest="skip_first", action="store_true", help="Skip first entry, as it's usually a large outlier.") parser.add_argument("-t", "--title", dest="title", type=str, nargs="?", default="", help="What title to give the figure.") parser.add_argument("file", type=str, help="The file to plot(csv).") @@ -35,99 +40,127 @@ if __name__ == "__main__": with open(opts.file, "r") as f: header = f.readline() header_names = header.split(";") + if len(header_names) not in (4, 5): + print("Bad data?") + exit(1) - plots = [opts.priv, opts.pub, opts.hist, opts.hw_hist] + plots = [opts.priv, opts.hist, opts.export_hist, opts.hw_hist] n_plots = sum(plots) if n_plots == 0: - n_plots = 4 - plots = [True, True, True, True] + if len(header_names) == 4: + n_plots = 3 + else: + n_plots = 4 + plots = [True for _ in range(n_plots)] + + if plots[2] and len(header_names) != 5: + n_plots = n_plots - 1 + if n_plots == 0: + print("Nothing to plot.") + exit(1) + plots[2] = False hx = lambda x: int(x, 16) - data = np.genfromtxt(opts.file, delimiter=";", skip_header=1, converters={2: hx, 3: hx}, dtype=np.dtype([("index","u4"), ("time","u4"), ("pub", "O"), ("priv", "O")])) + if len(header_names) == 4: + data = np.genfromtxt(opts.file, delimiter=";", skip_header=1, converters={2: hx, 3: hx}, dtype=np.dtype([("index", "u4"), ("gen_time", "u4"), ("pub", "O"), ("priv", "O")])) + else: + data = np.genfromtxt(opts.file, delimiter=";", skip_header=1, converters={3: hx, 4: hx}, dtype=np.dtype([("index", "u4"), ("gen_time", "u4"), ("export_time", "u4"), ("pub", "O"), ("priv", "O")])) + if opts.skip_first: data = data[1:] - if "nano" in header_names[1]: - unit = r"$\mu s$" - time_data = map(lambda x: x[1]//1000, data) - else: - unit = r"ms" - time_data = map(itemgetter(1), data) - time_data = list(time_data) - priv_data = list(map(itemgetter(2), data)) - pub_data = list(map(itemgetter(3), data)) + gen_time_data = data["gen_time"] + export_time_data = None + if "export_time" in data.dtype.names: + export_time_data = data["export_time"] + pub_data = data["pub"] + priv_data = data["priv"] + + + gen_unit = "ms" + if header_names[1].endswith("[nano]"): + gen_unit = r"$\mu s$" + gen_time_data = list(map(lambda x: x[1]//1000, gen_time_data)) + export_unit = "ms" + if len(header_names) == 5 and header_names[2].endswith("[nano]"): + export_unit = r"$\mu s$" + export_time_data = list(map(lambda x: x[1]//1000, export_time_data)) plt.style.use("ggplot") fig = plt.figure() layout_kwargs = {} if opts.title is None: fig.suptitle(opts.file) - layout_kwargs["rect"] = [0, 0.02, 1, 0.98] + #layout_kwargs["rect"] = [0, 0.02, 1, 0.98] elif opts.title: fig.suptitle(opts.title) - layout_kwargs["rect"] = [0, 0.02, 1, 0.98] - fig.tight_layout(**layout_kwargs) + #layout_kwargs["rect"] = [0, 0.02, 1, 0.98] + fig.tight_layout(**layout_kwargs) + + max_gen_time = max(gen_time_data) + min_gen_time = min(gen_time_data) + bit_size = len(bin(max(priv_data))) - 2 + + cmap = deepcopy(plt.cm.plasma) + cmap.set_bad("black") plot_i = 1 if plots[0]: axe_private = fig.add_subplot(n_plots, 1, plot_i) - axe_private.scatter(time_data, priv_data, marker="x", s=10) - axe_private.set_ylabel("private key value\n(big endian)") - axe_private.set_xlabel("time ({})".format(unit)) + priv_msb = np.array(list(map(lambda x: x >> (bit_size - 8), priv_data)), dtype=np.dtype("u1")) + heatmap, xedges, yedges = np.histogram2d(priv_msb, gen_time_data, bins=[128, max_gen_time - min_gen_time]) + extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]] + axe_private.imshow(heatmap.T, extent=extent, aspect="auto", cmap=cmap, origin="low", interpolation="nearest", norm=colors.LogNorm()) + axe_private.set_xlabel("private key MSB value\n(big endian)") + axe_private.set_ylabel("time ({})".format(gen_unit)) plot_i += 1 if plots[1]: - axe_public = fig.add_subplot(n_plots, 1, plot_i) - axe_public.scatter(time_data, pub_data, marker="x", s=10) - axe_public.set_ylabel("public key value\n(big endian)") - axe_public.set_xlabel("time ({})".format(unit)) + axe_hist = fig.add_subplot(n_plots, 1, plot_i) + time_max = max(gen_time_data) + time_min = min(gen_time_data) + time_avg = np.average(gen_time_data) + time_median = np.median(gen_time_data) + axe_hist.hist(gen_time_data, bins=int((time_max - time_min)/1.2), log=True) + axe_hist.axvline(x=time_avg, alpha=0.7, linestyle="dotted", color="blue", label="avg = {}".format(time_avg)) + axe_hist.axvline(x=time_median, alpha=0.7, linestyle="dotted", color="green", label="median = {}".format(time_median)) + axe_hist.set_ylabel("count\n(log)") + axe_hist.set_xlabel("keygen time ({})".format(gen_unit)) + axe_hist.xaxis.set_major_locator(ticker.MultipleLocator()) + axe_hist.legend(loc="best") plot_i += 1 if plots[2]: axe_hist = fig.add_subplot(n_plots, 1, plot_i) - time_max = max(time_data) - time_avg = np.average(time_data) - time_median = np.median(time_data) - axe_hist.hist(time_data, bins=time_max//3, log=True) - axe_hist.axvline(x=time_avg, alpha=0.7, linestyle="dotted", color="red", label="avg = {}".format(time_avg)) + time_max = max(export_time_data) + time_min = min(export_time_data) + time_avg = np.average(export_time_data) + time_median = np.median(export_time_data) + axe_hist.hist(export_time_data, bins=int((time_max - time_min)/1.2), log=True) + axe_hist.axvline(x=time_avg, alpha=0.7, linestyle="dotted", color="blue", label="avg = {}".format(time_avg)) axe_hist.axvline(x=time_median, alpha=0.7, linestyle="dotted", color="green", label="median = {}".format(time_median)) axe_hist.set_ylabel("count\n(log)") - axe_hist.set_xlabel("time ({})".format(unit)) - axe_hist.xaxis.set_major_locator(ticker.MaxNLocator()) + axe_hist.set_xlabel("export time ({})".format(export_unit)) + axe_hist.xaxis.set_major_locator(ticker.MultipleLocator()) axe_hist.legend(loc="best") plot_i += 1 if plots[3]: - priv_bit_bins = {} - for i in range(len(data)): - skey = priv_data[i] - time = time_data[i] - skey_hw = 0 - while skey: - skey_hw += 1 - skey &= skey - 1 - if skey_hw in priv_bit_bins: - priv_bit_bins[skey_hw].append(time) - else: - priv_bit_bins[skey_hw] = [time] - priv_bit_x = [] - priv_bit_y = [] - for k,v in priv_bit_bins.items(): - priv_bit_x.extend([k] * len(v)) - priv_bit_y.extend(v) axe_priv_hist = fig.add_subplot(n_plots, 1, plot_i) - h, xe, ye = np.histogram2d(priv_bit_x, priv_bit_y, bins=[max(priv_bit_bins) - min(priv_bit_bins), (max(time_data) - min(time_data))//5]) - cmap = deepcopy(plt.cm.plasma) - cmap.set_bad("black") + priv_hw = np.array(list(map(hw, priv_data)), dtype=np.dtype("u2")) + h, xe, ye = np.histogram2d(priv_hw, gen_time_data, bins=[max(priv_hw) - min(priv_hw), max_gen_time - min_gen_time]) im = axe_priv_hist.imshow(h.T, origin="low", cmap=cmap, aspect="auto", extent=[xe[0], xe[-1], ye[0], ye[-1]], norm=colors.LogNorm()) + axe_priv_hist.axvline(x=bit_size//2, alpha=0.7, linestyle="dotted", color="white", label=str(bit_size//2) + " bits") axe_priv_hist.set_xlabel("private key Hamming weight") - axe_priv_hist.set_ylabel("time ({})".format(unit)) + axe_priv_hist.set_ylabel("time ({})".format(gen_unit)) + axe_priv_hist.legend(loc="best") fig.colorbar(im, ax=axe_priv_hist) if plot_i > 2: - fig.text(0.01, 0.02, "Data size: {}".format(len(time_data)), size="small") + fig.text(0.01, 0.02, "Data size: {}".format(len(gen_time_data)), size="small") if opts.output is None: + plt.tight_layout() plt.show() else: fig.set_size_inches(12, 10) -- cgit v1.2.3-70-g09d2