aboutsummaryrefslogtreecommitdiff
path: root/src/cz/crcs/ectester/reader/ECTester.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/cz/crcs/ectester/reader/ECTester.java')
-rw-r--r--src/cz/crcs/ectester/reader/ECTester.java178
1 files changed, 129 insertions, 49 deletions
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) {