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/response/Response.java | 45 ++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'src/cz/crcs/ectester/reader/response/Response.java') diff --git a/src/cz/crcs/ectester/reader/response/Response.java b/src/cz/crcs/ectester/reader/response/Response.java index 4814e41..b0cd0f8 100644 --- a/src/cz/crcs/ectester/reader/response/Response.java +++ b/src/cz/crcs/ectester/reader/response/Response.java @@ -79,6 +79,10 @@ public abstract class Response { return resp; } + public byte[] getData() { + return resp.getData(); + } + public long getDuration() { return time; } @@ -424,4 +428,45 @@ public abstract class Response { parse(1, 0); } } + + /** + * + */ + public static class GetInfo extends Response { + private short base; + private short jcVersion; + private short cleanupSupport; + + public GetInfo(ResponseAPDU response, String description, long time) { + super(response, description, time); + + parse(1, 1); + int offset = 2 + 2 + getParamLength(0); + byte[] data = getData(); + base = ByteUtil.getShort(data, offset); + offset += 2; + jcVersion = ByteUtil.getShort(data, offset); + offset += 2; + cleanupSupport = ByteUtil.getShort(data, offset); + } + + public String getVersion() { + return new String(getParam(0)); + } + + public short getBase() { + return base; + } + + public float getJavaCardVersion() { + byte major = (byte) (jcVersion >> 8); + byte minor = (byte) (jcVersion & 0xff); + int minorSize = (int) Math.ceil(Math.log10(minor)); + return (major + ((float) (minor) / (minorSize * 10))); + } + + public boolean getCleanupSupport() { + return cleanupSupport == 1; + } + } } -- 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/response/Response.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 cb18c136e443acb20b44c7add47a9e349d635543 Mon Sep 17 00:00:00 2001 From: J08nY Date: Wed, 14 Nov 2018 16:35:50 +0100 Subject: Fix long response APDU handling and exported params handling. --- src/cz/crcs/ectester/reader/CardMngr.java | 2 +- src/cz/crcs/ectester/reader/response/Response.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/cz/crcs/ectester/reader/response/Response.java') diff --git a/src/cz/crcs/ectester/reader/CardMngr.java b/src/cz/crcs/ectester/reader/CardMngr.java index 921a9c8..e6835dd 100644 --- a/src/cz/crcs/ectester/reader/CardMngr.java +++ b/src/cz/crcs/ectester/reader/CardMngr.java @@ -331,7 +331,7 @@ public class CardMngr { if (responseAPDU.getSW1() == (byte) 0x61) { CommandAPDU apduToSend = new CommandAPDU((byte) 0x00, (byte) 0xC0, (byte) 0x00, (byte) 0x00, - responseAPDU.getSW1()); + responseAPDU.getSW2()); responseAPDU = channel.transmit(apduToSend); if (verbose) diff --git a/src/cz/crcs/ectester/reader/response/Response.java b/src/cz/crcs/ectester/reader/response/Response.java index afac1bc..6f42ba1 100644 --- a/src/cz/crcs/ectester/reader/response/Response.java +++ b/src/cz/crcs/ectester/reader/response/Response.java @@ -308,7 +308,7 @@ public abstract class Response { if (pair == keyPair && param == mask) { return index; } - if ((parameters & mask) != 0 && (pair & keyPair) != 0) { + if ((parameters & mask) != 0 && (pair & this.keyPair) != 0) { if (mask == EC_Consts.PARAMETER_W) { if ((key & EC_Consts.KEY_PUBLIC) != 0) index++; -- 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/response/Response.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