aboutsummaryrefslogtreecommitdiff
path: root/src/cz/crcs/ectester/reader
diff options
context:
space:
mode:
authorJ08nY2017-01-17 22:35:02 +0100
committerJ08nY2017-01-17 22:35:02 +0100
commitb99a1484397cc1b64a4798fa4a73b0782d99f18e (patch)
tree53e58d9ef7ce7f00e13e9c7271964454abfbdc25 /src/cz/crcs/ectester/reader
parente113197a41fc1961a9649cb3a96a18d7a9eab58f (diff)
downloadECTester-b99a1484397cc1b64a4798fa4a73b0782d99f18e.tar.gz
ECTester-b99a1484397cc1b64a4798fa4a73b0782d99f18e.tar.zst
ECTester-b99a1484397cc1b64a4798fa4a73b0782d99f18e.zip
reader: implemented ecdh and ecdsa actions, also added some docs
- ecdh and ecdsa instructions now work, although without output to file, yet. - added MIT license headers to main classes
Diffstat (limited to 'src/cz/crcs/ectester/reader')
-rw-r--r--src/cz/crcs/ectester/reader/CardMngr.java32
-rw-r--r--src/cz/crcs/ectester/reader/ECTester.java178
-rw-r--r--src/cz/crcs/ectester/reader/ParamReader.java5
-rw-r--r--src/cz/crcs/ectester/reader/Util.java35
4 files changed, 183 insertions, 67 deletions
diff --git a/src/cz/crcs/ectester/reader/CardMngr.java b/src/cz/crcs/ectester/reader/CardMngr.java
index aab7da9..9cdd055 100644
--- a/src/cz/crcs/ectester/reader/CardMngr.java
+++ b/src/cz/crcs/ectester/reader/CardMngr.java
@@ -2,11 +2,11 @@ package cz.crcs.ectester.reader;
import com.licel.jcardsim.io.CAD;
import com.licel.jcardsim.io.JavaxSmartCardInterface;
-import java.util.List;
-import java.util.Scanner;
import javacard.framework.AID;
import javax.smartcardio.*;
+import java.util.List;
+import java.util.Scanner;
/**
* @author Petr Svenda petr@svenda.com
@@ -16,16 +16,16 @@ public class CardMngr {
private CardTerminal terminal = null;
private CardChannel channel = null;
private Card card = null;
-
+
// Simulator related attributes
private CAD cad = null;
private JavaxSmartCardInterface simulator = null;
private boolean simulate = false;
-
+
private final byte selectCM[] = {
- (byte) 0x00, (byte) 0xa4, (byte) 0x04, (byte) 0x00, (byte) 0x07, (byte) 0xa0, (byte) 0x00, (byte) 0x00,
- (byte) 0x00, (byte) 0x18, (byte) 0x43, (byte) 0x4d};
+ (byte) 0x00, (byte) 0xa4, (byte) 0x04, (byte) 0x00, (byte) 0x07, (byte) 0xa0, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x18, (byte) 0x43, (byte) 0x4d};
public static final byte OFFSET_CLA = 0x00;
public static final byte OFFSET_INS = 0x01;
@@ -42,7 +42,7 @@ public class CardMngr {
this(false);
}
- public CardMngr(boolean simulate) {
+ public CardMngr(boolean simulate) {
this.simulate = simulate;
}
@@ -70,14 +70,14 @@ public class CardMngr {
//reset the card
System.out.println(Util.bytesToHex(card.getATR().getBytes()));
-
+
cardFound = true;
}
}
return cardFound;
}
-
+
public boolean connectToCardSelect() throws CardException {
if (simulate)
return true;
@@ -113,7 +113,7 @@ public class CardMngr {
terminal = terminalList.get(answ);
}
}
-
+
if (terminal != null) {
card = terminal.connect("*");
System.out.println("card: " + card);
@@ -190,15 +190,15 @@ public class CardMngr {
apdu[OFFSET_LC] = (byte) 0x00;
ResponseAPDU resp = send(apdu);
-
- System.out.println("Response: " + Integer.toHexString(resp.getSW()));
-
+
+ System.out.println("Response: " + Integer.toHexString(resp.getSW()));
+
if (resp.getSW() != 0x6D00) { // Note: 0x6D00 is SW_INS_NOT_SUPPORTED
// something?
}
}
}
-
+
public static List<CardTerminal> getReaderList() {
try {
TerminalFactory factory = TerminalFactory.getDefault();
@@ -242,14 +242,14 @@ public class CardMngr {
CommandAPDU commandAPDU = new CommandAPDU(apdu);
return sendAPDU(commandAPDU);
}
-
+
public boolean prepareLocalSimulatorApplet(byte[] appletAIDArray, byte[] installData, Class appletClass) {
System.setProperty("com.licel.jcardsim.terminal.type", "2");
cad = new CAD(System.getProperties());
simulator = (JavaxSmartCardInterface) cad.getCardInterface();
AID appletAID = new AID(appletAIDArray, (short) 0, (byte) appletAIDArray.length);
- AID appletAIDRes = simulator.installApplet(appletAID, appletClass, installData, (short) 0, (byte) installData.length);
+ AID appletAIDRes = simulator.installApplet(appletAID, appletClass, installData, (short) 0, (byte) installData.length);
return simulator.selectApplet(appletAID);
}
diff --git a/src/cz/crcs/ectester/reader/ECTester.java b/src/cz/crcs/ectester/reader/ECTester.java
index 6d1be42..1ee0015 100644
--- a/src/cz/crcs/ectester/reader/ECTester.java
+++ b/src/cz/crcs/ectester/reader/ECTester.java
@@ -1,3 +1,24 @@
+/*
+ * Copyright (c) 2016-2017 Petr Svenda <petr@svenda.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
package cz.crcs.ectester.reader;
import cz.crcs.ectester.applet.ECTesterApplet;
@@ -8,14 +29,17 @@ import org.apache.commons.cli.*;
import javax.smartcardio.CardException;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
+import java.io.File;
import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
+import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
/**
+ * Reader part of ECTester, a tool for testing Elliptic curve support on javacards.
+ *
* @author Petr Svenda petr@svenda.com
* @author Jan Jancar johny@neuromancer.sk
*/
@@ -23,7 +47,6 @@ public class ECTester {
private CardMngr cardManager = null;
private DirtyLogger systemOutLogger = null;
- private FileOutputStream outputFile = null;
//Options
private int optBits;
@@ -56,7 +79,7 @@ public class ECTester {
private static final byte[] ALLOCATE = {
(byte) 0xB0,
(byte) 0x5a, //INS ALLOCATE
- (byte) 0x00, //P1 *byte keypair
+ (byte) 0x00, //P1 *byte keyPair
(byte) 0x00, //P2
(byte) 0x03, //LC
(byte) 0x00, //DATA *short keyLength
@@ -67,7 +90,7 @@ public class ECTester {
private static final byte[] SET = {
(byte) 0xB0,
(byte) 0x5B, //INS SET
- (byte) 0x00, //P1 *byte keypair
+ (byte) 0x00, //P1 *byte keyPair
(byte) 0x00, //P2 *byte export
(byte) 0x06, //LC
(byte) 0x00, //DATA *byte curve
@@ -82,7 +105,7 @@ public class ECTester {
private static final byte[] GENERATE = {
(byte) 0xB0,
(byte) 0x5C, //INS GENERATE
- (byte) 0x00, //P1 *byte keypair
+ (byte) 0x00, //P1 *byte keyPair
(byte) 0x00, //P2 *byte export
(byte) 0x00 //LC
};
@@ -90,7 +113,7 @@ public class ECTester {
private static final byte[] ECDH = {
(byte) 0xB0,
(byte) 0x5D, //INS ECDH
- (byte) 0x00, //P1 *byte keypair
+ (byte) 0x00, //P1 *byte keyPair
(byte) 0x00, //P2 *byte export
(byte) 0x01, //LC
(byte) 0x00 //DATA *byte valid
@@ -99,7 +122,7 @@ public class ECTester {
private static final byte[] ECDSA = {
(byte) 0xB0,
(byte) 0x5E, //INS ECDSA
- (byte) 0x00, //P1 *byte keypair
+ (byte) 0x00, //P1 *byte keyPair
(byte) 0x00, //P2 *byte export
(byte) 0x00, //LC
//DATA [*short dataLength, byte[] data]
@@ -177,6 +200,13 @@ public class ECTester {
}
}
+ /**
+ * Parses command-line options.
+ *
+ * @param args cli arguments
+ * @return parsed CommandLine object
+ * @throws ParseException if there are any problems encountered while parsing the command line tokens
+ */
private CommandLine parseArgs(String[] args) throws ParseException {
/*
* Actions:
@@ -221,7 +251,7 @@ public class ECTester {
opts.addOption(Option.builder("f2m").longOpt("binary-field").desc("Use binary field curve.").build());
opts.addOption(Option.builder("pub").longOpt("public").desc("Use public key from file [pubkey_file] (wx,wy).").hasArg().argName("pubkey_file").build());
opts.addOption(Option.builder("priv").longOpt("private").desc("Use private key from file [privkey_file] (s).").hasArg().argName("privkey_file").build());
- opts.addOption(Option.builder("k").longOpt("key").desc("Use keypair from fileĀ [key_file] (wx,wy,s).").hasArg().argName("key_file").build());
+ opts.addOption(Option.builder("k").longOpt("key").desc("Use keyPair from fileĀ [key_file] (wx,wy,s).").hasArg().argName("key_file").build());
opts.addOption(Option.builder("o").longOpt("output").desc("Output into file [output_file].").hasArg().argName("output_file").build());
opts.addOption(Option.builder("l").longOpt("log").desc("Log output into file [log_file].").hasArg().argName("log_file").optionalArg(true).build());
opts.addOption(Option.builder("s").longOpt("simulate").desc("Simulate a card with jcardsim instead of using a terminal.").build());
@@ -301,6 +331,19 @@ public class ECTester {
}
} else if (cli.hasOption("ecdsa")) {
+ if (optPrimeField == optBinaryField) {
+ System.err.print("Need to specify field with -fp or -f2m. (not both)");
+ return false;
+ }
+ if (optAll) {
+ System.err.println("You have to specify curve bit-size with -b");
+ return false;
+ }
+ if ((optPublic == null) != (optPrivate == null)) {
+ System.err.println("You have cannot only specify a part of a keypair.");
+ return false;
+ }
+
optECDSASign = cli.getOptionValue("ecdsa");
}
@@ -316,9 +359,10 @@ public class ECTester {
}
/**
- * Generates EC keypairs and outputs them to output file.
- * @throws CardException
- * @throws IOException
+ * Generates EC keyPairs and outputs them to output file.
+ *
+ * @throws CardException if APDU transmission fails
+ * @throws IOException if an IO error occurs when writing to key file.
*/
private void generate() throws CardException, IOException {
byte keyClass = optPrimeField ? KeyPair.ALG_EC_FP : KeyPair.ALG_EC_F2M;
@@ -360,9 +404,10 @@ public class ECTester {
}
/**
- * Tests
+ * Tests Elliptic curve support for a given curve/curves.
*/
private void test() {
+ //TODO
if (optAll) {
if (optPrimeField) {
//iterate over prime curve sizes used: EC_Consts.FP_SIZES
@@ -404,9 +449,10 @@ public class ECTester {
}
/**
+ * Performs ECDH key exchange.
*
- * @throws IOException
- * @throws CardException
+ * @throws CardException if APDU transmission fails
+ * @throws IOException if an IO error occurs when writing to key file.
*/
private void ecdh() throws IOException, CardException {
byte keyClass = optPrimeField ? KeyPair.ALG_EC_FP : KeyPair.ALG_EC_F2M;
@@ -425,24 +471,55 @@ public class ECTester {
CommandAPDU ecdh = insECDH(ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_ECDH, (byte) 0);
ResponseAPDU response = cardManager.send(ecdh);
- //TODO output ecdh
+ //TODO print response SWs/error codes
+ //TODO output to file
}
/**
+ * Performs ECDSA signature, on random or provided data.
+ *
+ * @throws CardException if APDU transmission fails
+ * @throws IOException if an IO error occurs when writing to key file.
*/
- private void ecdsa() {
- //TODO
+ private void ecdsa() throws CardException, IOException {
+ byte keyClass = optPrimeField ? KeyPair.ALG_EC_FP : KeyPair.ALG_EC_F2M;
+ CommandAPDU[] curve = prepareCurve(ECTesterApplet.KEYPAIR_LOCAL, (short) optBits, keyClass);
+ cardManager.send(curve);
+
+ if (optKey != null || (optPublic != null && optPrivate != null)) {
+ CommandAPDU set = prepareKey(ECTesterApplet.KEYPAIR_LOCAL);
+ cardManager.send(set);
+ } else {
+ CommandAPDU generate = insGenerate(ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_NONE);
+ cardManager.send(generate);
+ }
+
+ //read file, if asked to sign
+ byte[] data = null;
+ if (optECDSASign != null) {
+ File in = new File(optECDSASign);
+ long len = in.length();
+ if (len == 0) {
+ throw new FileNotFoundException("File " + optECDSASign + " not found.");
+ }
+ data = Files.readAllBytes(in.toPath());
+ }
+
+ CommandAPDU ecdsa = insECDSA(ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_SIG, data);
+ ResponseAPDU response = cardManager.send(ecdsa);
+ //TODO print response SWs/error codes
+ //TODO output to file
}
/**
* Creates the INS_ALLOCATE instruction.
*
- * @param keyPair
- * @param keyLength
- * @param keyClass
+ * @param keyPair which keyPair to use, local/remote (KEYPAIR_* | ...)
+ * @param keyLength key length to set
+ * @param keyClass key class to allocate
* @return apdu to send
*/
- private CommandAPDU insAllocate(byte keyPair, short keyLength, byte keyClass) throws CardException {
+ private CommandAPDU insAllocate(byte keyPair, short keyLength, byte keyClass) {
byte[] data = new byte[]{0, 0, keyClass};
Util.setShort(data, 0, keyLength);
@@ -452,13 +529,13 @@ public class ECTester {
/**
* Creates the INS_SET instruction.
*
- * @param keyPair
- * @param export
- * @param curve
- * @param params
- * @param corrupted
- * @param corruption
- * @param external
+ * @param keyPair which keyPair to set params on, local/remote (KEYPAIR_* || ...)
+ * @param export whether to export set params from keyPair
+ * @param curve curve to set (EC_Consts.CURVE_*)
+ * @param params parameters to set (EC_Consts.PARAMETER_* | ...)
+ * @param corrupted parameters to corrupt (EC_Consts.PARAMETER_* | ...)
+ * @param corruption corruption type (EC_Consts.CORRUPTION_*)
+ * @param external external curve data, can be null
* @return apdu to send
*/
private CommandAPDU insSet(byte keyPair, byte export, byte curve, short params, short corrupted, byte corruption, byte[] external) {
@@ -478,8 +555,8 @@ public class ECTester {
/**
* Creates the INS_GENERATE instruction.
*
- * @param keyPair
- * @param export
+ * @param keyPair which keyPair to generate, local/remote (KEYPAIR_* || ...)
+ * @param export whether to export generated keys from keyPair
* @return apdu to send
*/
private CommandAPDU insGenerate(byte keyPair, byte export) {
@@ -489,10 +566,10 @@ public class ECTester {
/**
* Creates the INS_ECDH instruction.
*
- * @param pubkey
- * @param privkey
- * @param export
- * @param invalid
+ * @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 invalid whether to invalidate the pubkey before ECDH
* @return apdu to send
*/
private CommandAPDU insECDH(byte pubkey, byte privkey, byte export, byte invalid) {
@@ -504,9 +581,9 @@ public class ECTester {
/**
* Creates the INS_ECDSA instruction.
*
- * @param keyPair
- * @param export
- * @param raw
+ * @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.
* @return apdu to send
*/
private CommandAPDU insECDSA(byte keyPair, byte export, byte[] raw) {
@@ -521,14 +598,13 @@ public class ECTester {
}
/**
- * @param keyPair
- * @param keyLength
- * @param keyClass
- * @return
- * @throws CardException
- * @throws FileNotFoundException
+ * @param keyPair which keyPair/s (local/remote) to set curve domain parameters on
+ * @param keyLength key length to allocate
+ * @param keyClass key class to allocate
+ * @return an array of CommandAPDUs to send in order to prepare the keypair/s.
+ * @throws IOException if curve file cannot be found/opened
*/
- private CommandAPDU[] prepareCurve(byte keyPair, short keyLength, byte keyClass) throws CardException, IOException {
+ private CommandAPDU[] prepareCurve(byte keyPair, short keyLength, byte keyClass) throws IOException {
List<CommandAPDU> commands = new ArrayList<>();
commands.add(insAllocate(keyPair, keyLength, keyClass));
@@ -548,11 +624,11 @@ public class ECTester {
}
/**
- * @param keypair
- * @return
- * @throws IOException
+ * @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
*/
- private CommandAPDU prepareKey(byte keypair) throws IOException {
+ private CommandAPDU prepareKey(byte keyPair) throws IOException {
short params = EC_Consts.PARAMETERS_NONE;
byte[] data = null;
if (optKey != null) {
@@ -570,9 +646,13 @@ public class ECTester {
}
if (data == null && params != EC_Consts.PARAMETERS_NONE) {
+ /*
+ TODO: this is not correct, in case (optPublic != null) and (optPrivate != null),
+ only one can actually load(return not null from ParamReader.flatten) and an exception will not be thrown
+ */
throw new IOException("Couldn't read the key file correctly.");
}
- return insSet(keypair, ECTesterApplet.EXPORT_NONE, EC_Consts.CURVE_external, params, EC_Consts.PARAMETERS_NONE, EC_Consts.CORRUPTION_NONE, data);
+ return insSet(keyPair, ECTesterApplet.EXPORT_NONE, EC_Consts.CURVE_external, params, EC_Consts.PARAMETERS_NONE, EC_Consts.CORRUPTION_NONE, data);
}
public static void main(String[] args) {
diff --git a/src/cz/crcs/ectester/reader/ParamReader.java b/src/cz/crcs/ectester/reader/ParamReader.java
index 5232326..ca14d2d 100644
--- a/src/cz/crcs/ectester/reader/ParamReader.java
+++ b/src/cz/crcs/ectester/reader/ParamReader.java
@@ -9,6 +9,7 @@ import java.util.Scanner;
import java.util.regex.Pattern;
/**
+ *
* @author Jan Jancar johny@neuromancer.sk
*/
public class ParamReader {
@@ -18,7 +19,7 @@ public class ParamReader {
* Flattens params read from String[] data into a byte[] with their lengths prepended as short entries.
* @param params (EC_Consts.PARAMETER_* | ...)
* @param data data read by readString, readFile, readResource
- * @return byte[] with params flattened
+ * @return byte[] with params flattened, or null
*/
public static byte[] flatten(short params, String[] data) {
if (!validate(data)) {
@@ -42,7 +43,7 @@ public class ParamReader {
if (masked == EC_Consts.PARAMETER_G || masked == EC_Consts.PARAMETER_W) {
//read another param (the y coord) and put into X962 format.
byte[] y = parse(data[i + 1]);
- param = Util.concatenate(new byte[]{4}, param, y);
+ param = Util.concatenate(new byte[]{4}, param, y);//<- ugly but works!
i++;
}
if (param.length == 0)
diff --git a/src/cz/crcs/ectester/reader/Util.java b/src/cz/crcs/ectester/reader/Util.java
index aa0dfd5..1464728 100644
--- a/src/cz/crcs/ectester/reader/Util.java
+++ b/src/cz/crcs/ectester/reader/Util.java
@@ -1,6 +1,12 @@
package cz.crcs.ectester.reader;
+import cz.crcs.ectester.applet.ECTesterApplet;
+import javacard.framework.ISO7816;
+import javacard.security.CryptoException;
+
/**
+ * Utility class, some byte/hex manipulation, convenient byte[] methods.
+ *
* @author Petr Svenda petr@svenda.com
* @author Jan Jancar johny@neuromancer.sk
*/
@@ -79,4 +85,33 @@ public class Util {
}
return out;
}
+
+ public static String getPrintError(short code) {
+ if (code == ISO7816.SW_NO_ERROR) {
+ return "OK\t(0x9000)";
+ } else {
+ String codeStr = "unknown";
+ switch (code) {
+ case CryptoException.ILLEGAL_VALUE:
+ codeStr = "ILLEGAL_VALUE";
+ break;
+ case CryptoException.UNINITIALIZED_KEY:
+ codeStr = "UNINITIALIZED_KEY";
+ break;
+ case CryptoException.NO_SUCH_ALGORITHM:
+ codeStr = "NO_SUCH_ALG";
+ break;
+ case CryptoException.INVALID_INIT:
+ codeStr = "INVALID_INIT";
+ break;
+ case CryptoException.ILLEGAL_USE:
+ codeStr = "ILLEGAL_USE";
+ break;
+ case ECTesterApplet.SW_SIG_VERIFY_FAIL:
+ codeStr = "SIG_VERIFY_FAIL";
+ break;
+ }
+ return String.format("fail\t(%s,\t0x%4x)", codeStr, code);
+ }
+ }
}