aboutsummaryrefslogtreecommitdiff
path: root/src/cz/crcs/ectester/applet/ECTesterApplet.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/cz/crcs/ectester/applet/ECTesterApplet.java')
-rw-r--r--src/cz/crcs/ectester/applet/ECTesterApplet.java427
1 files changed, 427 insertions, 0 deletions
diff --git a/src/cz/crcs/ectester/applet/ECTesterApplet.java b/src/cz/crcs/ectester/applet/ECTesterApplet.java
new file mode 100644
index 0000000..b461688
--- /dev/null
+++ b/src/cz/crcs/ectester/applet/ECTesterApplet.java
@@ -0,0 +1,427 @@
+/*
+ * PACKAGEID: 4C6162616B417070
+ * APPLETID: 4C6162616B4170706C6574
+ */
+package cz.crcs.ectester.applet;
+
+import javacard.framework.*;
+import javacard.security.ECPrivateKey;
+import javacard.security.ECPublicKey;
+import javacard.security.KeyPair;
+import javacard.security.RandomData;
+
+/**
+ * @author Petr Svenda petr@svenda.com
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class ECTesterApplet extends Applet {
+
+ // MAIN INSTRUCTION CLASS
+ public static final byte CLA_ECTESTERAPPLET = (byte) 0xB0;
+
+ //INSTRUCTIONS
+ public static final byte INS_ALLOCATE = (byte) 0x5a;
+ public static final byte INS_SET = (byte) 0x5b;
+ public static final byte INS_GENERATE = (byte) 0x5c;
+ public static final byte INS_ECDH = (byte) 0x5d;
+ public static final byte INS_ECDSA = (byte) 0x5e;
+
+ //PARAMETERS for P1 and P2
+ public static final byte KEYPAIR_LOCAL = (byte) 0x01;
+ public static final byte KEYPAIR_REMOTE = (byte) 0x02;
+ public static final byte KEYPAIR_BOTH = KEYPAIR_LOCAL | KEYPAIR_REMOTE;
+ public static final byte EXPORT_PUBLIC = (byte) 0x04;
+ public static final byte EXPORT_PRIVATE = (byte) 0x08;
+ public static final byte EXPORT_BOTH = EXPORT_PUBLIC | EXPORT_PRIVATE;
+ public static final byte EXPORT_ECDH = (byte) 0x10;
+ public static final byte EXPORT_SIG = (byte) 0x20;
+
+ //STATUS WORDS
+ public static final short SW_SIG_VERIFY_FAIL = (short) 0x0ee1;
+
+
+ private static final short ARRAY_LENGTH = (short) 0xff;
+ // TEMPORARRY ARRAY IN RAM
+ private byte ramArray[] = null;
+ private byte ramArray2[] = null;
+ // PERSISTENT ARRAY IN EEPROM
+ private byte dataArray[] = null; // unused
+
+
+ private RandomData randomData = null;
+
+ private KeyPair localKeypair = null;
+ private KeyPair remoteKeypair = null;
+ private ECKeyTester keyTester = null;
+ private ECKeyGenerator keyGenerator = null;
+
+ protected ECTesterApplet(byte[] buffer, short offset, byte length) {
+ if (length > 9) {
+ /*
+ short dataOffset = offset;
+ // shift to privilege offset
+ dataOffset += (short) (1 + buffer[offset]);
+ // finally shift to Application specific offset
+ dataOffset += (short) (1 + buffer[dataOffset]);
+ // go to proprietary data
+ dataOffset++;
+ */
+
+ ramArray = JCSystem.makeTransientByteArray(ARRAY_LENGTH, JCSystem.CLEAR_ON_RESET);
+ ramArray2 = JCSystem.makeTransientByteArray(ARRAY_LENGTH, JCSystem.CLEAR_ON_RESET);
+
+ dataArray = new byte[ARRAY_LENGTH];
+ Util.arrayFillNonAtomic(dataArray, (short) 0, ARRAY_LENGTH, (byte) 0);
+
+ randomData = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
+ EC_Consts.randomData = randomData;
+
+ keyGenerator = new ECKeyGenerator();
+ keyTester = new ECKeyTester();
+ keyTester.allocateECDH();
+ keyTester.allocateECDHC();
+ keyTester.allocateECDSA();
+ }
+ register();
+ }
+
+ public static void install(byte[] bArray, short bOffset, byte bLength) throws ISOException {
+ // applet instance creation
+ new ECTesterApplet(bArray, bOffset, bLength);
+ }
+
+ public void process(APDU apdu) throws ISOException {
+ // get the APDU buffer
+ byte[] apduBuffer = apdu.getBuffer();
+
+ // ignore the applet select command dispached to the process
+ if (selectingApplet())
+ return;
+
+ if (apduBuffer[ISO7816.OFFSET_CLA] == CLA_ECTESTERAPPLET) {
+ switch (apduBuffer[ISO7816.OFFSET_INS]) {
+ case INS_ALLOCATE:
+ insAllocate(apdu);
+ break;
+ case INS_SET:
+ insSet(apdu);
+ break;
+ case INS_GENERATE:
+ insGenerate(apdu);
+ break;
+ case INS_ECDH:
+ insECDH(apdu);
+ break;
+ case INS_ECDSA:
+ insECDSA(apdu);
+ break;
+ default:
+ // The INS code is not supported by the dispatcher
+ ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
+ break;
+ }
+ } else ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
+ }
+
+ /**
+ * Allocate local and remote keypairs.
+ * returns allocate SWs
+ *
+ * @param apdu P1 = byte keypair (KEYPAIR_* | ...)
+ * P2 =
+ * DATA = short keyLength
+ * byte keyClass
+ */
+ private void insAllocate(APDU apdu) {
+ apdu.setIncomingAndReceive();
+ byte[] apdubuf = apdu.getBuffer();
+
+ byte keypair = apdubuf[ISO7816.OFFSET_P1];
+ short keyLength = Util.getShort(apdubuf, ISO7816.OFFSET_CDATA);
+ byte keyClass = apdubuf[ISO7816.OFFSET_CDATA + 2];
+
+ short len = allocate(keypair, keyLength, keyClass, apdubuf, (short) 0);
+
+ apdu.setOutgoingAndSend((short) 0, len);
+ }
+
+ /**
+ * @param keypair which keypair to use, local/remote (KEYPAIR_* | ...)
+ * @param keyLength key length to set
+ * @param keyClass key class to allocate
+ * @param buffer apdu buffer
+ * @param offset offset into apdu buffer
+ * @return length of data written to the buffer
+ */
+ private short allocate(byte keypair, short keyLength, byte keyClass, byte[] buffer, short offset) {
+ short length = 0;
+ if ((keypair & KEYPAIR_LOCAL) != 0) {
+ localKeypair = keyGenerator.allocatePair(keyClass, keyLength);
+ Util.setShort(buffer, offset, keyGenerator.getSW());
+ length += 2;
+ }
+
+ if ((keypair & KEYPAIR_REMOTE) != 0) {
+ remoteKeypair = keyGenerator.allocatePair(keyClass, keyLength);
+ Util.setShort(buffer, (short) (offset + length), keyGenerator.getSW());
+ length += 2;
+ }
+
+ return length;
+ }
+
+ /**
+ * Sets curve parameters on local and remote keypairs.
+ * returns setCurve SWs, set params if export
+ *
+ * @param apdu P1 = byte keypair (KEYPAIR_* | ...)
+ * P2 = byte export (EXPORT_* | KEYPAIR_*)
+ * DATA = byte curve (EC_Consts.CURVE_*)
+ * short params (EC_Consts.PARAMETER_* | ...)
+ * short corruptedParams (EC_Consts.PARAMETER_* | ...)
+ * byte corruptionType (EC_Consts.CORRUPTION_*)
+ * <p>
+ * if curveID = CURVE_EXTERNAL:
+ * [short param_length, byte[] param],
+ * for all params in params,
+ * in order: field,a,b,g,r,k,w,s
+ */
+ private void insSet(APDU apdu) {
+ apdu.setIncomingAndReceive();
+ byte[] apdubuf = apdu.getBuffer();
+
+ byte keypair = apdubuf[ISO7816.OFFSET_P1];
+ byte export = apdubuf[ISO7816.OFFSET_P2];
+ byte curve = apdubuf[ISO7816.OFFSET_CDATA];
+ short params = Util.getShort(apdubuf, (short) (ISO7816.OFFSET_CDATA + 1));
+ short corruptedParams = Util.getShort(apdubuf, (short) (ISO7816.OFFSET_CDATA + 3));
+ byte corruptionType = apdubuf[(short) (ISO7816.OFFSET_CDATA + 5)];
+
+ short len = 0;
+
+ if ((keypair & KEYPAIR_LOCAL) != 0)
+ len += set(localKeypair, curve, params, corruptedParams, corruptionType, apdubuf, (short) (ISO7816.OFFSET_CDATA + 6), (short) 0);
+ if ((keypair & KEYPAIR_REMOTE) != 0)
+ len += set(remoteKeypair, curve, params, corruptedParams, corruptionType, apdubuf, (short) (ISO7816.OFFSET_CDATA + 6), len);
+ if ((export & KEYPAIR_LOCAL) != 0)
+ len += export(localKeypair, export, params, apdubuf, len);
+ if ((export & KEYPAIR_REMOTE) != 0)
+ len += export(remoteKeypair, export, params, apdubuf, len);
+
+ apdu.setOutgoingAndSend((short) 0, len);
+ }
+
+ /**
+ * @param keypair KeyPair to set params on
+ * @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 buffer buffer to read params from and write sw to
+ * @param inOffset input offset in buffer
+ * @param outOffset output offset in buffer
+ * @return length of data written to the buffer
+ */
+ private short set(KeyPair keypair, byte curve, short params, short corrupted, byte corruption, byte[] buffer, short inOffset, short outOffset) {
+ short sw = ISO7816.SW_NO_ERROR;
+
+ switch (curve) {
+ case EC_Consts.CURVE_default:
+ //default, dont set anything
+ break;
+ case EC_Consts.CURVE_external:
+ //external
+ sw = keyGenerator.setExternalCurve(keypair, params, buffer, inOffset);
+ break;
+ default:
+ //custom
+ sw = keyGenerator.setCurve(keypair, curve, params, ramArray, (short) 0);
+ break;
+ }
+
+ if (sw == ISO7816.SW_NO_ERROR)
+ sw = keyGenerator.corruptCurve(keypair, corrupted, corruption, ramArray, (short) 0);
+ Util.setShort(buffer, outOffset, sw);
+ return 2;
+ }
+
+ /**
+ * Generates the local and remote keypairs.
+ * returns generate SWs, pubkey and privkey if export
+ *
+ * @param apdu P1 = byte keypair (KEYPAIR_* | ...)
+ * P2 = byte export (EXPORT_* | KEYPAIR_*)
+ */
+ private void insGenerate(APDU apdu) {
+ apdu.setIncomingAndReceive();
+ byte[] apdubuf = apdu.getBuffer();
+
+ byte keypair = apdubuf[ISO7816.OFFSET_P1];
+ byte export = apdubuf[ISO7816.OFFSET_P2];
+
+ short len = 0;
+ if ((keypair & KEYPAIR_LOCAL) != 0)
+ len += generate(localKeypair, apdubuf, (short) 0);
+ if ((keypair & KEYPAIR_REMOTE) != 0)
+ len += generate(remoteKeypair, apdubuf, len);
+ if ((export & KEYPAIR_LOCAL) != 0)
+ len += export(localKeypair, export, (short) (EC_Consts.PARAMETER_W | EC_Consts.PARAMETER_S), apdubuf, len);
+ if ((export & KEYPAIR_REMOTE) != 0)
+ len += export(remoteKeypair, export, (short) (EC_Consts.PARAMETER_W | EC_Consts.PARAMETER_S), apdubuf, len);
+
+ apdu.setOutgoingAndSend((short) 0, len);
+ }
+
+ /**
+ * @param keypair KeyPair to generate
+ * @param buffer buffer to write sw to
+ * @param offset output offset in buffer
+ * @return length of data written to the buffer
+ */
+ private short generate(KeyPair keypair, byte[] buffer, short offset) {
+ short sw = keyGenerator.generatePair(keypair);
+ Util.setShort(buffer, offset, sw);
+
+ return 2;
+ }
+
+ /**
+ * @param keypair KeyPair to export from
+ * @param export which key to export from (EXPORT_PUBLIC | EXPORT_PRIVATE)
+ * @param params which params to export (EC_Consts.PARAMETER_* | ...)
+ * @param buffer buffer to export params to
+ * @param offset output offset in buffer
+ * @return length of data written to the buffer
+ */
+ private short export(KeyPair keypair, byte export, short params, byte[] buffer, short offset) {
+ short length = 0;
+
+ if ((export & EXPORT_PUBLIC) != 0) {
+ //export params from public
+ length += keyGenerator.exportParameters(keypair, ECKeyGenerator.KEY_PUBLIC, params, buffer, offset);
+ }
+
+ if ((export & EXPORT_PRIVATE) != 0) {
+ //export params from private
+ length += keyGenerator.exportParameters(keypair, ECKeyGenerator.KEY_PRIVATE, params, buffer, (short) (offset + length));
+
+ }
+ return length;
+ }
+
+ /**
+ * Does ECDH, between the pubkey specified in P1(local/remote) and the privkey specified in P2(local/remote).
+ * returns deriveSecret SW, if export != 0 => short secretlen, byte[] secret
+ *
+ * @param apdu P1 = byte pubkey (KEYPAIR_*)
+ * P2 = byte privkey (KEYPAIR_*)
+ * DATA = byte export (EXPORT_ECDH || 0)
+ * byte invalid (00 = valid, !00 = invalid)
+ */
+ private void insECDH(APDU apdu) {
+ apdu.setIncomingAndReceive();
+ byte[] apdubuf = apdu.getBuffer();
+
+ byte pubkey = apdubuf[ISO7816.OFFSET_P1];
+ byte privkey = apdubuf[ISO7816.OFFSET_P2];
+ byte export = apdubuf[ISO7816.OFFSET_CDATA];
+ byte invalid = apdubuf[(short) (ISO7816.OFFSET_CDATA + 1)];
+
+ short len = ecdh(pubkey, privkey, export, invalid, apdubuf, (short) 0);
+
+ apdu.setOutgoingAndSend((short) 0, len);
+ }
+
+ /**
+ * @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
+ * @param buffer buffer to write sw to, and export ECDH secret if (export & EXPORT_ECDH) != 0
+ * @param offset output offset in buffer
+ * @return length of data written to the buffer
+ */
+ private short ecdh(byte pubkey, byte privkey, byte export, byte invalid, byte[] buffer, short offset) {
+ short length = 0;
+
+ KeyPair pub = ((pubkey & KEYPAIR_LOCAL) != 0) ? localKeypair : remoteKeypair;
+ KeyPair priv = ((privkey & KEYPAIR_LOCAL) != 0) ? localKeypair : remoteKeypair;
+
+ short secretLength;
+ if (invalid != 0) {
+ secretLength = keyTester.testECDH_invalidPoint((ECPrivateKey) priv.getPrivate(), (ECPublicKey) pub.getPublic(), ramArray, (short) 0, ramArray2, (short) 0);
+ } else {
+ secretLength = keyTester.testECDH_validPoint((ECPrivateKey) priv.getPrivate(), (ECPublicKey) pub.getPublic(), ramArray, (short) 0, ramArray2, (short) 0);
+ }
+
+ Util.setShort(buffer, offset, keyTester.getSW());
+ length += 2;
+
+ if ((export & EXPORT_ECDH) != 0) {
+ Util.setShort(buffer, (short) (offset + length), secretLength);
+ length += 2;
+ Util.arrayCopyNonAtomic(ramArray2, (short) 0, buffer, (short) (offset + length), secretLength);
+ length += secretLength;
+ }
+
+ return length;
+ }
+
+ /**
+ * Does and ECDSA signature and verification on data provided, using the keypair in P1(local/remote).
+ * returns ecdsa SW, if export != 0 => short signature_length, byte[] signature
+ *
+ * @param apdu P1 = byte keypair (KEYPAIR_*)
+ * P2 = byte export (EXPORT_SIG || 0)
+ * DATA = short data_length (00 = random data generated, !00 = data length)
+ * byte[] data
+ */
+ private void insECDSA(APDU apdu) {
+ apdu.setIncomingAndReceive();
+ byte[] apdubuf = apdu.getBuffer();
+
+ byte keypair = apdubuf[ISO7816.OFFSET_P1];
+ byte export = apdubuf[ISO7816.OFFSET_P2];
+
+ short len = ecdsa(keypair, export, apdubuf, ISO7816.OFFSET_CDATA, (short) 0);
+
+ apdu.setOutgoingAndSend((short) 0, len);
+ }
+
+ /**
+ * @param keypair keypair to use for signing and verification (KEYPAIR_LOCAL || KEYPAIR_REMOTE)
+ * @param export whether to export ECDSA signature
+ * @param buffer buffer to write sw to, and export ECDSA signature if (export & EXPORT_SIG) != 0
+ * @param inOffset input offset in buffer
+ * @param outOffset output offset in buffer
+ * @return length of data written to the buffer
+ */
+ private short ecdsa(byte keypair, byte export, byte[] buffer, short inOffset, short outOffset) {
+ short length = 0;
+
+ short dataLength = Util.getShort(buffer, inOffset);
+ if (dataLength == 0) { //no data to sign
+ //generate random
+ dataLength = 32;
+ randomData.generateData(ramArray, (short) 0, dataLength);
+ } else {
+ Util.arrayCopyNonAtomic(buffer, (short) (inOffset + 2), ramArray, (short) 0, dataLength);
+ }
+
+ KeyPair sign = ((keypair & KEYPAIR_LOCAL) != 0) ? localKeypair : remoteKeypair;
+
+ short signatureLength = keyTester.testECDSA((ECPrivateKey) sign.getPrivate(), (ECPublicKey) sign.getPublic(), ramArray, (short) 0, dataLength, ramArray2, (short) 0);
+ Util.setShort(buffer, outOffset, keyTester.getSW());
+ length += 2;
+
+ if ((export & EXPORT_SIG) != 0) {
+ Util.setShort(buffer, (short) (outOffset + length), signatureLength);
+ length += 2;
+
+ Util.arrayCopyNonAtomic(ramArray2, (short) 0, buffer, (short) (outOffset + length), signatureLength);
+ length += signatureLength;
+ }
+
+ return length;
+ }
+}