aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/applets/EC_Consts.java184
-rw-r--r--src/applets/SimpleECCApplet.java351
-rw-r--r--src/simpleapdu/CardMngr.java209
-rw-r--r--src/simpleapdu/ISO7816_status_words.txt71
-rw-r--r--src/simpleapdu/SimpleAPDU.java78
5 files changed, 893 insertions, 0 deletions
diff --git a/src/applets/EC_Consts.java b/src/applets/EC_Consts.java
new file mode 100644
index 0000000..7923c91
--- /dev/null
+++ b/src/applets/EC_Consts.java
@@ -0,0 +1,184 @@
+/**
+ *
+ */
+package applets;
+
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+import javacard.security.ECPrivateKey;
+import javacard.security.ECPublicKey;
+import javacard.security.KeyBuilder;
+
+public class EC_Consts {
+ public static byte[] EC_FP_P = null;
+ public static byte[] EC_FP_A = null;
+ public static byte[] EC_FP_B = null;
+ public static byte[] EC_FP_G_X = null;
+ public static byte[] EC_FP_G_Y = null;
+ public static byte[] EC_FP_R = null;
+ public static short EC_FP_K = 1;
+
+ // secp192r1 from http://www.secg.org/sec2-v2.pdf
+ public static final byte[] EC192_FP_P = new byte[]{
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFE,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF};
+ public static final byte[] EC192_FP_A = new byte[]{
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFE,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFC};
+ public static final byte[] EC192_FP_B = new byte[]{
+ (byte) 0x64, (byte) 0x21, (byte) 0x05, (byte) 0x19,
+ (byte) 0xE5, (byte) 0x9C, (byte) 0x80, (byte) 0xE7,
+ (byte) 0x0F, (byte) 0xA7, (byte) 0xE9, (byte) 0xAB,
+ (byte) 0x72, (byte) 0x24, (byte) 0x30, (byte) 0x49,
+ (byte) 0xFE, (byte) 0xB8, (byte) 0xDE, (byte) 0xEC,
+ (byte) 0xC1, (byte) 0x46, (byte) 0xB9, (byte) 0xB1};
+ // G in compressed form / first part of ucompressed
+ public static final byte[] EC192_FP_G_X = new byte[]{
+ (byte) 0x18, (byte) 0x8D, (byte) 0xA8, (byte) 0x0E,
+ (byte) 0xB0, (byte) 0x30, (byte) 0x90, (byte) 0xF6,
+ (byte) 0x7C, (byte) 0xBF, (byte) 0x20, (byte) 0xEB,
+ (byte) 0x43, (byte) 0xA1, (byte) 0x88, (byte) 0x00,
+ (byte) 0xF4, (byte) 0xFF, (byte) 0x0A, (byte) 0xFD,
+ (byte) 0x82, (byte) 0xFF, (byte) 0x10, (byte) 0x12};
+ // second part of G uncompressed
+ public static final byte[] EC192_FP_G_Y = new byte[]{
+ (byte) 0x07, (byte) 0x19, (byte) 0x2B, (byte) 0x95,
+ (byte) 0xFF, (byte) 0xC8, (byte) 0xDA, (byte) 0x78,
+ (byte) 0x63, (byte) 0x10, (byte) 0x11, (byte) 0xED,
+ (byte) 0x6B, (byte) 0x24, (byte) 0xCD, (byte) 0xD5,
+ (byte) 0x73, (byte) 0xF9, (byte) 0x77, (byte) 0xA1,
+ (byte) 0x1E, (byte) 0x79, (byte) 0x48, (byte) 0x11};
+ // Order of G
+ public static final byte[] EC192_FP_R = new byte[]{
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0x99, (byte) 0xDE, (byte) 0xF8, (byte) 0x36,
+ (byte) 0x14, (byte) 0x6B, (byte) 0xC9, (byte) 0xB1,
+ (byte) 0xB4, (byte) 0xD2, (byte) 0x28, (byte) 0x31};
+ // cofactor of G
+ public static final short EC192_FP_K = 1;
+
+ // secp256r1 from http://www.secg.org/sec2-v2.pdf
+ public static final byte[] EC256_FP_P = new byte[]{
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF};
+ public static final byte[] EC256_FP_A = new byte[]{
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFC};
+ public static final byte[] EC256_FP_B = new byte[]{
+ (byte) 0x5A, (byte) 0xC6, (byte) 0x35, (byte) 0xD8,
+ (byte) 0xAA, (byte) 0x3A, (byte) 0x93, (byte) 0xE7,
+ (byte) 0xB3, (byte) 0xEB, (byte) 0xBD, (byte) 0x55,
+ (byte) 0x76, (byte) 0x98, (byte) 0x86, (byte) 0xBC,
+ (byte) 0x65, (byte) 0x1D, (byte) 0x06, (byte) 0xB0,
+ (byte) 0xCC, (byte) 0x53, (byte) 0xB0, (byte) 0xF6,
+ (byte) 0x3B, (byte) 0xCE, (byte) 0x3C, (byte) 0x3E,
+ (byte) 0x27, (byte) 0xD2, (byte) 0x60, (byte) 0x4B};
+ // G in compressed form / first part of ucompressed
+ public static final byte[] EC256_FP_G_X = new byte[]{
+ (byte) 0x6B, (byte) 0x17, (byte) 0xD1, (byte) 0xF2,
+ (byte) 0xE1, (byte) 0x2C, (byte) 0x42, (byte) 0x47,
+ (byte) 0xF8, (byte) 0xBC, (byte) 0xE6, (byte) 0xE5,
+ (byte) 0x63, (byte) 0xA4, (byte) 0x40, (byte) 0xF2,
+ (byte) 0x77, (byte) 0x03, (byte) 0x7D, (byte) 0x81,
+ (byte) 0x2D, (byte) 0xEB, (byte) 0x33, (byte) 0xA0,
+ (byte) 0xF4, (byte) 0xA1, (byte) 0x39, (byte) 0x45,
+ (byte) 0xD8, (byte) 0x98, (byte) 0xC2, (byte) 0x96};
+ // second part of G uncompressed
+ public static final byte[] EC256_FP_G_Y = new byte[]{
+ (byte) 0x4F, (byte) 0xE3, (byte) 0x42, (byte) 0xE2,
+ (byte) 0xFE, (byte) 0x1A, (byte) 0x7F, (byte) 0x9B,
+ (byte) 0x8E, (byte) 0xE7, (byte) 0xEB, (byte) 0x4A,
+ (byte) 0x7C, (byte) 0x0F, (byte) 0x9E, (byte) 0x16,
+ (byte) 0x2B, (byte) 0xCE, (byte) 0x33, (byte) 0x57,
+ (byte) 0x6B, (byte) 0x31, (byte) 0x5E, (byte) 0xCE,
+ (byte) 0xCB, (byte) 0xB6, (byte) 0x40, (byte) 0x68,
+ (byte) 0x37, (byte) 0xBF, (byte) 0x51, (byte) 0xF5};
+ // Order of G
+ public static final byte[] EC256_FP_R = new byte[]{
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xBC, (byte) 0xE6, (byte) 0xFA, (byte) 0xAD,
+ (byte) 0xA7, (byte) 0x17, (byte) 0x9E, (byte) 0x84,
+ (byte) 0xF3, (byte) 0xB9, (byte) 0xCA, (byte) 0xC2,
+ (byte) 0xFC, (byte) 0x63, (byte) 0x25, (byte) 0x51};
+ // cofactor of G
+ public static final short EC256_FP_K = 1;
+
+ // TODO: add parameters for longer lengths
+
+ public static void setECKeyParams(ECPublicKey ecPubKey, ECPrivateKey ecPrivKey, short ecLength, byte[] auxBuffer) {
+ // Select proper courve parameters
+ switch (ecLength) {
+ case (short) 192: {
+ EC_FP_P = EC192_FP_P;
+ EC_FP_A = EC192_FP_A;
+ EC_FP_B = EC192_FP_B;
+ EC_FP_G_X = EC192_FP_G_X;
+ EC_FP_G_Y = EC192_FP_G_Y;
+ EC_FP_R = EC192_FP_R;
+ EC_FP_K = EC192_FP_K;
+ break;
+ }
+ case (short) 256: {
+ EC_FP_P = EC256_FP_P;
+ EC_FP_A = EC256_FP_A;
+ EC_FP_B = EC256_FP_B;
+ EC_FP_G_X = EC256_FP_G_X;
+ EC_FP_G_Y = EC256_FP_G_Y;
+ EC_FP_R = EC256_FP_R;
+ EC_FP_K = EC256_FP_K;
+ break;
+ }
+ default: {
+ ISOException.throwIt((short) -1);
+ }
+ }
+ // prepare an ANSI X9.62 uncompressed EC point representation for G
+ short gSize = (short) 1;
+ gSize += (short) EC_FP_G_X.length;
+ gSize += (short) EC_FP_G_Y.length;
+ auxBuffer[0] = 0x04;
+ short off = 1;
+ off = Util.arrayCopy(EC_FP_G_X, (short) 0, auxBuffer, off, (short) EC_FP_G_X.length);
+ Util.arrayCopy(EC_FP_G_Y, (short) 0, auxBuffer, off, (short) EC_FP_G_Y.length);
+
+ // pre-set basic EC parameters:
+ ecPubKey.setFieldFP(EC_FP_P, (short) 0, (short) EC_FP_P.length);
+ ecPubKey.setA(EC_FP_A, (short) 0, (short) EC_FP_A.length);
+ ecPubKey.setB(EC_FP_B, (short) 0, (short) EC_FP_B.length);
+ ecPubKey.setG(auxBuffer, (short) 0, gSize);
+ ecPubKey.setR(EC_FP_R, (short) 0, (short) EC_FP_R.length);
+ ecPubKey.setK(EC_FP_K);
+
+ ecPrivKey.setFieldFP(EC_FP_P, (short) 0, (short) EC_FP_P.length);
+ ecPrivKey.setA(EC_FP_A, (short) 0, (short) EC_FP_A.length);
+ ecPrivKey.setB(EC_FP_B, (short) 0, (short) EC_FP_B.length);
+ ecPrivKey.setG(auxBuffer, (short) 0, gSize);
+ ecPrivKey.setR(EC_FP_R, (short) 0, (short) EC_FP_R.length);
+ ecPrivKey.setK(EC_FP_K);
+ }
+}
diff --git a/src/applets/SimpleECCApplet.java b/src/applets/SimpleECCApplet.java
new file mode 100644
index 0000000..325d834
--- /dev/null
+++ b/src/applets/SimpleECCApplet.java
@@ -0,0 +1,351 @@
+/*
+ * PACKAGEID: 4C6162616B417070
+ * APPLETID: 4C6162616B4170706C6574
+ */
+package applets;
+
+import javacard.framework.*;
+import javacard.security.*;
+import javacardx.crypto.*;
+
+public class SimpleECCApplet extends javacard.framework.Applet
+{
+ // MAIN INSTRUCTION CLASS
+ final static byte CLA_SIMPLEECCAPPLET = (byte) 0xB0;
+
+ // INSTRUCTIONS
+ final static byte INS_GENERATEKEY = (byte) 0x5a;
+ final static byte INS_ALLOCATEKEYPAIRS = (byte) 0x5b;
+ final static byte INS_ALLOCATEKEYPAIR = (byte) 0x5c;
+ final static byte INS_DERIVEECDHSECRET = (byte) 0x5d;
+
+
+ final static short ARRAY_LENGTH = (short) 0xff;
+ final static byte AES_BLOCK_LENGTH = (short) 0x16;
+
+ final static short EC_LENGTH_BITS = KeyBuilder.LENGTH_EC_FP_192;
+ //final static short EC_LENGTH_BITS = KeyBuilder.LENGTH_EC_FP_160;
+ //final static short EC_LENGTH_BITS = (short) 256;
+/*
+ public static final byte[] EC192_FP_PUBLICW = new byte[]{
+ (byte) 0x04, (byte) 0xC9, (byte) 0xC0, (byte) 0xED, (byte) 0xFB, (byte) 0x27,
+ (byte) 0xB7, (byte) 0x1E, (byte) 0xBE, (byte) 0x30, (byte) 0x93, (byte) 0xFC,
+ (byte) 0x4F, (byte) 0x33, (byte) 0x76, (byte) 0x38, (byte) 0xCE, (byte) 0xE0,
+ (byte) 0x2F, (byte) 0x78, (byte) 0xF6, (byte) 0x3C, (byte) 0xEA, (byte) 0x90,
+ (byte) 0x22, (byte) 0x61, (byte) 0x32, (byte) 0x8E, (byte) 0x9F, (byte) 0x03,
+ (byte) 0x8A, (byte) 0xFD, (byte) 0x60, (byte) 0xA0, (byte) 0xCE, (byte) 0x01,
+ (byte) 0x9B, (byte) 0x76, (byte) 0x34, (byte) 0x59, (byte) 0x79, (byte) 0x64,
+ (byte) 0xD7, (byte) 0x79, (byte) 0x8E, (byte) 0x3B, (byte) 0x16, (byte) 0xD5,
+ (byte) 0x15};
+ */
+ public static final byte[] EC192_FP_PUBLICW = new byte[]{
+ (byte) 0x04,
+ (byte) 0x9d, (byte) 0x42, (byte) 0x76, (byte) 0x9d, (byte) 0xfd, (byte) 0xbe,
+ (byte) 0x11, (byte) 0x3a, (byte) 0x85, (byte) 0x1b, (byte) 0xb6, (byte) 0xb0,
+ (byte) 0x1b, (byte) 0x1a, (byte) 0x51, (byte) 0x5d, (byte) 0x89, (byte) 0x3b,
+ (byte) 0x5a, (byte) 0xdb, (byte) 0xc1, (byte) 0xf6, (byte) 0x13, (byte) 0x29,
+ (byte) 0x74, (byte) 0x74, (byte) 0x9a, (byte) 0xc0, (byte) 0x96, (byte) 0x7a,
+ (byte) 0x8f, (byte) 0xf4, (byte) 0xcc, (byte) 0x54, (byte) 0xd9, (byte) 0x31,
+ (byte) 0x87, (byte) 0x60, (byte) 0x2d, (byte) 0xd6, (byte) 0x7e, (byte) 0xb3,
+ (byte) 0xd2, (byte) 0x29, (byte) 0x70a, (byte) 0xca, (byte) 0x2ca};
+
+
+ private KeyPair ecKeyPair = null;
+ private KeyPair ecKeyPair128 = null;
+ private KeyPair ecKeyPair160 = null;
+ private KeyPair ecKeyPair192 = null;
+ private KeyPair ecKeyPair256 = null;
+ private ECPublicKey ecPubKey = null;
+ private ECPublicKey ecPubKey128 = null;
+ private ECPublicKey ecPubKey160 = null;
+ private ECPublicKey ecPubKey192 = null;
+ private ECPublicKey ecPubKey256 = null;
+ private ECPrivateKey ecPrivKey = null;
+ private ECPrivateKey ecPrivKey128 = null;
+ private ECPrivateKey ecPrivKey160 = null;
+ private ECPrivateKey ecPrivKey192 = null;
+ private ECPrivateKey ecPrivKey256 = null;
+
+ private KeyAgreement dhKeyAgreement = null;
+
+ // TEMPORARRY ARRAY IN RAM
+ private byte m_ramArray[] = null;
+ // PERSISTENT ARRAY IN EEPROM
+ private byte m_dataArray[] = null;
+
+ protected SimpleECCApplet(byte[] buffer, short offset, byte length) {
+ short dataOffset = offset;
+
+ if(length > 9) {
+ // 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++;
+
+ m_ramArray = JCSystem.makeTransientByteArray(ARRAY_LENGTH, JCSystem.CLEAR_ON_RESET);
+ m_dataArray = new byte[ARRAY_LENGTH];
+ Util.arrayFillNonAtomic(m_dataArray, (short) 0, ARRAY_LENGTH, (byte) 0);
+
+ dhKeyAgreement = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DH, false);
+ }
+
+ register();
+ }
+
+ public static void install(byte[] bArray, short bOffset, byte bLength) throws ISOException {
+ // applet instance creation
+ new SimpleECCApplet (bArray, bOffset, bLength);
+ }
+
+ public boolean select() {
+ return true;
+ }
+
+ public void deselect() {
+ return;
+ }
+
+ 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_SIMPLEECCAPPLET) {
+ switch ( apduBuffer[ISO7816.OFFSET_INS] ) {
+ case INS_ALLOCATEKEYPAIR:
+ AllocateKeyPairReturnDefCourve(apdu);
+ break;
+ case INS_ALLOCATEKEYPAIRS:
+ AllocateKeyPairs(apdu);
+ break;
+ case INS_GENERATEKEY:
+ GenerateKey(apdu);
+ break;
+ case INS_DERIVEECDHSECRET:
+ DeriveECDHSecret(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);
+ }
+
+ void AllocateKeyPair(byte algorithm, short bitLen) {
+ // Select proper attributes
+ switch (bitLen) {
+ case (short) 128: {
+ ecKeyPair = ecKeyPair128;
+ ecKeyPair = ecKeyPair128;
+ ecPrivKey = ecPrivKey128;
+ break;
+ }
+ case (short) 160: {
+ ecKeyPair = ecKeyPair160;
+ ecKeyPair = ecKeyPair160;
+ ecPrivKey = ecPrivKey160;
+ break;
+ }
+ case (short) 192: {
+ ecKeyPair = ecKeyPair192;
+ ecKeyPair = ecKeyPair192;
+ ecPrivKey = ecPrivKey192;
+ break;
+ }
+ case (short) 256: {
+ ecKeyPair = ecKeyPair256;
+ ecKeyPair = ecKeyPair256;
+ ecPrivKey = ecPrivKey256;
+ break;
+ }
+ default: {
+ ISOException.throwIt((short) -1);
+ }
+ }
+
+ // Allocate instance
+ ecKeyPair = new KeyPair(algorithm, bitLen);
+ ecKeyPair.genKeyPair();
+ ecPubKey = (ECPublicKey) ecKeyPair.getPublic();
+ // sometimes null is returned and previous one call to genKeyPair()
+ // is required before we can get public key
+ if (ecPubKey == null) {
+ ecKeyPair.genKeyPair();
+ }
+ ecPubKey = (ECPublicKey) ecKeyPair.getPublic();
+ ecPrivKey = (ECPrivateKey) ecKeyPair.getPrivate();
+ // Set required EC parameters
+ EC_Consts.setECKeyParams(ecPubKey, ecPrivKey, bitLen, m_ramArray);
+ }
+
+ short TryAllocateKeyPair(byte algorithm, short bitLen, byte[] buffer, short offset) {
+ // Try allocation, log result
+ try {
+ offset = Util.setShort(buffer, offset, bitLen);
+ AllocateKeyPair(KeyPair.ALG_EC_FP, bitLen);
+ buffer[offset] = 1;
+ offset++;
+ } catch (Exception e) {
+ buffer[offset] = 0;
+ offset++;
+ }
+ return offset;
+ }
+ void AllocateKeyPairs(APDU apdu) {
+ byte[] apdubuf = apdu.getBuffer();
+ apdu.setIncomingAndReceive();
+
+ short offset = 0;
+
+ //offset = TryAllocateKeyPair(KeyPair.ALG_EC_FP, (short) 128, apdubuf, offset);
+ //offset = TryAllocateKeyPair(KeyPair.ALG_EC_FP, (short) 160, apdubuf, offset);
+ //offset = TryAllocateKeyPair(KeyPair.ALG_EC_FP, (short) 192, apdubuf, offset);
+ //offset = TryAllocateKeyPair(KeyPair.ALG_EC_FP, (short) 256, apdubuf, offset);
+
+ apdu.setOutgoingAndSend((short) 0, offset);
+ }
+
+ void AllocateKeyPairReturnDefCourve(APDU apdu) {
+ byte[] apdubuf = apdu.getBuffer();
+ apdu.setIncomingAndReceive();
+
+ short bitLen = Util.getShort(apdubuf, ISO7816.OFFSET_CDATA);
+
+ // Note: all locations shoudl happen in constructor. But here it is intentional
+ // as we like to test for result of allocation
+ ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, bitLen);
+
+ // If required, generate also new key pair
+ if (apdubuf[ISO7816.OFFSET_P1] == (byte) 1) {
+ ecPubKey = (ECPublicKey) ecKeyPair.getPublic();
+ ecPrivKey = (ECPrivateKey) ecKeyPair.getPrivate();
+ // Some implementation wil not return valid pub key until ecKeyPair.genKeyPair() is called
+ // Other implementation will fail with exception if same is called => try catch
+ try {
+ if (ecPubKey == null) {ecKeyPair.genKeyPair();}
+ }
+ catch (Exception e) {} // do nothing
+
+
+ // If required, initialize curve parameters first
+ if (apdubuf[ISO7816.OFFSET_P2] == (byte) 2) {
+ EC_Consts.setECKeyParams(ecPubKey, ecPrivKey, bitLen, m_ramArray);
+ }
+
+ // Now generate new keypair with either default or custom curve
+ ecKeyPair.genKeyPair();
+ ecPubKey = (ECPublicKey) ecKeyPair.getPublic();
+ ecPrivKey = (ECPrivateKey) ecKeyPair.getPrivate();
+
+ short len = 0;
+ short offset = 0;
+
+ // Export curve public parameters
+ offset += 2; // reserve space for length
+ len = ecPubKey.getField(apdubuf, offset);
+ Util.setShort(apdubuf, (short) (offset - 2), len);
+ offset += len;
+ offset += 2; // reserve space for length
+ len = ecPubKey.getA(apdubuf, offset);
+ Util.setShort(apdubuf, (short) (offset - 2), len);
+ offset += len;
+
+ offset += 2; // reserve space for length
+ len = ecPubKey.getB(apdubuf, offset);
+ Util.setShort(apdubuf, (short) (offset - 2), len);
+ offset += len;
+ offset += 2; // reserve space for length
+ len = ecPubKey.getR(apdubuf, offset);
+ Util.setShort(apdubuf, (short) (offset - 2), len);
+ offset += len;
+/*
+ offset += 2; // reserve space for length
+ len = ecPubKey.getW(apdubuf, offset);
+ Util.setShort(apdubuf, (short) (offset - 2), len);
+ offset += len;
+*/
+ apdu.setOutgoingAndSend((short) 0, offset);
+ }
+ }
+
+/**
+ For a first quick test, this would be the workflow:
+ *
+ * 1. Import a given ECC public key (i.e. a point that is not on the curve)
+ *
+ * 2. Generate a fresh ECC keypair
+ *
+ * 3. Perform a ECDH key-derivation with the keys from steps 1 and 2
+ *
+ * 4. Repeat steps 2 & 3 a couple of times and record the generated secrets
+ *
+ *
+ *
+ * If the card is vulnerable, then the generated secrets will repeat. For
+ * example, we have points of order 5. With such a point and a vulnerable
+ * card, there are only 5 (or even less) different possible values for the
+ * generated secrets. This is pretty obvious, if you do a couple of (e.g.
+ * +-10) key-derivations ;)
+ * @param apdu
+ */
+ void DeriveECDHSecret(APDU apdu) {
+ byte[] apdubuf = apdu.getBuffer();
+ short len = apdu.setIncomingAndReceive();
+
+ // Assumption: proper EC keyPair is already allocated
+
+ // If public key point is provided, then use it
+ if (len == 0) {
+ // if not provided, use build-in one
+ Util.arrayCopyNonAtomic(EC192_FP_PUBLICW, (short) 0, apdubuf, ISO7816.OFFSET_CDATA, (short) EC192_FP_PUBLICW.length);
+ len = (short) EC192_FP_PUBLICW.length;
+ }
+
+ // Generate fresh EC keypair
+ ecKeyPair.genKeyPair();
+ ecPrivKey = (ECPrivateKey) ecKeyPair.getPrivate();
+
+ dhKeyAgreement.init(ecPrivKey);
+ short secretLen = 0;
+ // Generate and export secret
+ secretLen = dhKeyAgreement.generateSecret(apdubuf, ISO7816.OFFSET_CDATA, len, m_ramArray, (short) 0);
+ Util.arrayCopyNonAtomic(m_ramArray, (short) 0, apdubuf, (short) 0, secretLen);
+
+ apdu.setOutgoingAndSend((short) 0, secretLen);
+ }
+
+
+ void GenerateKey(APDU apdu) {
+ byte[] apdubuf = apdu.getBuffer();
+ apdu.setIncomingAndReceive();
+
+ // Assumption: proper EC keyPair is already allocated and initialized
+
+ ecKeyPair.genKeyPair();
+ ecPubKey = (ECPublicKey) ecKeyPair.getPrivate();
+ ecPrivKey = (ECPrivateKey) ecKeyPair.getPrivate();
+
+ short offset = 0;
+ offset += 2; // reserve space for length
+ short len = ecPubKey.getW(apdubuf, offset);
+ Util.setShort(apdubuf, (short) (offset - 2), len);
+ offset += len;
+ offset += 2; // reserve space for length
+ len = ecPrivKey.getS(apdubuf, offset);
+ Util.setShort(apdubuf, (short) (offset - 2), len);
+ offset += len;
+
+ apdu.setOutgoingAndSend((short) 0, offset);
+ }
+}
+
diff --git a/src/simpleapdu/CardMngr.java b/src/simpleapdu/CardMngr.java
new file mode 100644
index 0000000..d3ff86b
--- /dev/null
+++ b/src/simpleapdu/CardMngr.java
@@ -0,0 +1,209 @@
+package simpleapdu;
+
+import com.licel.jcardsim.io.CAD;
+import com.licel.jcardsim.io.JavaxSmartCardInterface;
+import java.util.List;
+import javacard.framework.AID;
+import javax.smartcardio.*;
+
+/**
+ *
+ * @author xsvenda
+ */
+public class CardMngr {
+ CardTerminal m_terminal = null;
+ CardChannel m_channel = null;
+ Card m_card = null;
+
+ // Simulator related attributes
+ private static CAD m_cad = null;
+ private static JavaxSmartCardInterface m_simulator = null;
+
+
+ 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};
+
+ public static final byte OFFSET_CLA = 0x00;
+ public static final byte OFFSET_INS = 0x01;
+ public static final byte OFFSET_P1 = 0x02;
+ public static final byte OFFSET_P2 = 0x03;
+ public static final byte OFFSET_LC = 0x04;
+ public static final byte OFFSET_DATA = 0x05;
+ public static final byte HEADER_LENGTH = 0x05;
+ public final static short DATA_RECORD_LENGTH = (short) 0x80; // 128B per record
+ public final static short NUMBER_OF_RECORDS = (short) 0x0a; // 10 records
+
+ public boolean ConnectToCard() throws Exception {
+ // TRY ALL READERS, FIND FIRST SELECTABLE
+ List terminalList = GetReaderList();
+
+ if (terminalList.isEmpty()) {
+ System.out.println("No terminals found");
+ }
+
+ //List numbers of Card readers
+ boolean cardFound = false;
+ for (int i = 0; i < terminalList.size(); i++) {
+ System.out.println(i + " : " + terminalList.get(i));
+ m_terminal = (CardTerminal) terminalList.get(i);
+ if (m_terminal.isCardPresent()) {
+ m_card = m_terminal.connect("*");
+ System.out.println("card: " + m_card);
+ m_channel = m_card.getBasicChannel();
+
+ //reset the card
+ ATR atr = m_card.getATR();
+ System.out.println(bytesToHex(m_card.getATR().getBytes()));
+
+ cardFound = true;
+ }
+ }
+
+ return cardFound;
+ }
+
+ public void DisconnectFromCard() throws Exception {
+ if (m_card != null) {
+ m_card.disconnect(false);
+ m_card = null;
+ }
+ }
+
+ public byte[] GetCPLCData() throws Exception {
+ byte[] data;
+
+ // TODO: Modify to obtain CPLC data
+ byte apdu[] = new byte[HEADER_LENGTH];
+ apdu[OFFSET_CLA] = (byte) 0x00;
+ apdu[OFFSET_INS] = (byte) 0x00;
+ apdu[OFFSET_P1] = (byte) 0x00;
+ apdu[OFFSET_P2] = (byte) 0x00;
+ apdu[OFFSET_LC] = (byte) 0x00;
+
+ ResponseAPDU resp = sendAPDU(apdu);
+ if (resp.getSW() != 0x9000) { // 0x9000 is "OK"
+ System.out.println("Fail to obtain card's response data");
+ data = null;
+ } else {
+ byte temp[] = resp.getBytes();
+ data = new byte[temp.length - 2];
+ System.arraycopy(temp, 0, data, 0, temp.length - 2);
+ // Last two bytes are status word (also obtainable by resp.getSW())
+ // Take a look at ISO7816_status_words.txt for common codes
+ }
+
+ return data;
+ }
+
+ public void ProbeCardCommands() throws Exception {
+ // TODO: modify to probe for instruction
+ for (int i = 0; i <= 0; i++) {
+ byte apdu[] = new byte[HEADER_LENGTH];
+ apdu[OFFSET_CLA] = (byte) 0x00;
+ apdu[OFFSET_INS] = (byte) 0x00;
+ apdu[OFFSET_P1] = (byte) 0x00;
+ apdu[OFFSET_P2] = (byte) 0x00;
+ apdu[OFFSET_LC] = (byte) 0x00;
+
+ ResponseAPDU resp = sendAPDU(apdu);
+
+ System.out.println("Response: " + Integer.toHexString(resp.getSW()));
+
+ if (resp.getSW() != 0x6D00) { // Note: 0x6D00 is SW_INS_NOT_SUPPORTED
+ // something?
+ }
+ }
+ }
+
+ public List GetReaderList() {
+ try {
+ TerminalFactory factory = TerminalFactory.getDefault();
+ List readersList = factory.terminals().list();
+ return readersList;
+ } catch (Exception ex) {
+ System.out.println("Exception : " + ex);
+ return null;
+ }
+ }
+
+ public ResponseAPDU sendAPDU(byte apdu[]) throws Exception {
+ CommandAPDU commandAPDU = new CommandAPDU(apdu);
+
+ System.out.println(">>>>");
+ System.out.println(commandAPDU);
+
+ System.out.println(bytesToHex(commandAPDU.getBytes()));
+
+ long elapsed = -System.nanoTime();
+
+ ResponseAPDU responseAPDU = m_channel.transmit(commandAPDU);
+
+ elapsed += System.nanoTime();
+
+ System.out.println(responseAPDU);
+ System.out.println(bytesToHex(responseAPDU.getBytes()));
+
+ if (responseAPDU.getSW1() == (byte) 0x61) {
+ CommandAPDU apduToSend = new CommandAPDU((byte) 0x00,
+ (byte) 0xC0, (byte) 0x00, (byte) 0x00,
+ (int) responseAPDU.getSW1());
+
+ responseAPDU = m_channel.transmit(apduToSend);
+ System.out.println(bytesToHex(responseAPDU.getBytes()));
+ }
+
+ System.out.println("<<<<");
+ System.out.println("Elapsed time (ms): " + elapsed / 1000000);
+ return (responseAPDU);
+ }
+
+ public String byteToHex(byte data) {
+ StringBuilder buf = new StringBuilder();
+ buf.append(toHexChar((data >>> 4) & 0x0F));
+ buf.append(toHexChar(data & 0x0F));
+ return buf.toString();
+ }
+
+ public char toHexChar(int i) {
+ if ((0 <= i) && (i <= 9)) {
+ return (char) ('0' + i);
+ } else {
+ return (char) ('a' + (i - 10));
+ }
+ }
+
+ public String bytesToHex(byte[] data) {
+ StringBuilder buf = new StringBuilder();
+ for (int i = 0; i < data.length; i++) {
+ buf.append(byteToHex(data[i]));
+ buf.append(" ");
+ }
+ return (buf.toString());
+ }
+
+
+ public boolean prepareLocalSimulatorApplet(byte[] appletAIDArray, byte[] installData, Class appletClass) {
+ System.setProperty("com.licel.jcardsim.terminal.type", "2");
+ m_cad = new CAD(System.getProperties());
+ m_simulator = (JavaxSmartCardInterface) m_cad.getCardInterface();
+ AID appletAID = new AID(appletAIDArray, (short) 0, (byte) appletAIDArray.length);
+
+ AID appletAIDRes = m_simulator.installApplet(appletAID, appletClass, installData, (short) 0, (byte) installData.length);
+ return m_simulator.selectApplet(appletAID);
+ }
+
+ public byte[] sendAPDUSimulator(byte apdu[]) throws Exception {
+ System.out.println(">>>>");
+ System.out.println(bytesToHex(apdu));
+
+ byte[] responseBytes = m_simulator.transmitCommand(apdu);
+
+ System.out.println(bytesToHex(responseBytes));
+ System.out.println("<<<<");
+
+ return responseBytes;
+ }
+
+
+}
diff --git a/src/simpleapdu/ISO7816_status_words.txt b/src/simpleapdu/ISO7816_status_words.txt
new file mode 100644
index 0000000..bf5af2b
--- /dev/null
+++ b/src/simpleapdu/ISO7816_status_words.txt
@@ -0,0 +1,71 @@
+public interface ISO7816 {
+
+ // Fields
+ public static final byte INS_EXTERNAL_AUTHENTICATE = -126;
+ public static final byte INS_SELECT = -92;
+ public static final byte CLA_ISO7816 = 0;
+ public static final byte OFFSET_CDATA = 5;
+ public static final byte OFFSET_LC = 4;
+ public static final byte OFFSET_P2 = 3;
+ public static final byte OFFSET_P1 = 2;
+ public static final byte OFFSET_INS = 1;
+ public static final byte OFFSET_CLA = 0;
+ public static final short SW_FILE_FULL = 27268; 0x6A84
+ public static final short SW_UNKNOWN = 28416; 0x6F00
+ public static final short SW_CLA_NOT_SUPPORTED = 28160; 0x6E00
+ public static final short SW_INS_NOT_SUPPORTED = 27904; 0x6D00
+ public static final short SW_CORRECT_LENGTH_00 = 27648; 0x6C00
+ public static final short SW_WRONG_P1P2 = 27392; 0x6B00
+ public static final short SW_INCORRECT_P1P2 = 27270; 0x6A86
+ public static final short SW_RECORD_NOT_FOUND = 27267; 0x6A83
+ public static final short SW_FILE_NOT_FOUND = 27266; 0x6A82
+ public static final short SW_FUNC_NOT_SUPPORTED = 27265; 0x6A81
+ public static final short SW_WRONG_DATA = 27264; 0x6A80
+ public static final short SW_APPLET_SELECT_FAILED = 27033; 0x6999
+ public static final short SW_COMMAND_NOT_ALLOWED = 27014; 0x6986
+ public static final short SW_CONDITIONS_NOT_SATISFIED = 27013; 0x6985
+ public static final short SW_DATA_INVALID = 27012; 0x6984
+ public static final short SW_FILE_INVALID = 27011; 0x6983
+ public static final short SW_SECURITY_STATUS_NOT_SATISFIED = 27010; 0x6982
+ public static final short SW_WRONG_LENGTH = 26368; 0x6700
+ public static final short SW_BYTES_REMAINING_00 = 24832; 0x6100
+ public static final short SW_NO_ERROR = -28672; 0x9000
+}
+
+public interface JCStatus {
+static int ALGORITHM_NOT_SUPPORTED = 0x9484;
+static int APPLET_INVALIDATED = 0x6283;
+static int APPLET_SELECT_FAILED = 0x6999
+static int AUTHENTICATION_FAILED = 0x6300
+static int AUTHORIZATION_FAILED = 0x9482
+static int CHECKSUM_FAILED = 0x9584
+static int CLA_NOT_SUPPORTED = 0x6E00
+static int COMMAND_NOT_ALLOWED = 0x6986
+static int CONDITIONS_NOT_SATISFIED = 0x6985
+static int CORRECT_LENGTH_00 = 0x6C00
+static int DATA_INVALID = 0x6984
+static int DECRYPTION_FAILED = 0x9583
+static int FILE_FULL = 0x6A84
+static int FILE_INVALID = 0x6983
+static int FILE_NOT_FOUND = 0x6A82
+static int FUNC_NOT_SUPPORTED = 0x6A81
+static int INCORRECT_P1P2 = 0x6A86
+static int INS_NOT_SUPPORTED = 0x6D00
+static int INSTALLATION_FAILED = 0x9585
+static int INVALID_STATE = 0x9481
+static int NO_ERROR = 0x9000
+static int NO_SPECIFIC_DIAGNOSIS = 0x6400
+static int PIN_REQUIRED = 0x6982
+static int RECORD_NOT_FOUND = 0x6A83
+static int REFERENCE_DATA_NOT_FOUND = 0x6A88
+static int REGISTRATION_FAILED = 0x9586
+static int SECURITY_STATUS_NOT_SATISFIED = 0x6982
+static int SIGNATURE_CHECK_FAILED = 0x9582
+static int SM_INCORRECT = 0x6988
+static int SM_MISSING = 0x6987
+static int TRUNCATED_DATA = 0x6100
+static int UNKNOWN = 0x6F00
+static int WRONG_DATA = 0x6A80
+static int WRONG_LENGTH = 0x6700
+static int WRONG_P1P2 = 0x6B00
+} \ No newline at end of file
diff --git a/src/simpleapdu/SimpleAPDU.java b/src/simpleapdu/SimpleAPDU.java
new file mode 100644
index 0000000..240a1cf
--- /dev/null
+++ b/src/simpleapdu/SimpleAPDU.java
@@ -0,0 +1,78 @@
+package simpleapdu;
+
+import applets.SimpleECCApplet;
+import javax.smartcardio.ResponseAPDU;
+
+/**
+ *
+ * @author xsvenda
+ */
+public class SimpleAPDU {
+ static CardMngr cardManager = new CardMngr();
+
+ private static byte DEFAULT_USER_PIN[] = {(byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0x30};
+ private static byte NEW_USER_PIN[] = {(byte) 0x31, (byte) 0x31, (byte) 0x31, (byte) 0x31};
+ private static byte APPLET_AID[] = {(byte) 0x4C, (byte) 0x61, (byte) 0x62, (byte) 0x61, (byte) 0x6B,
+ (byte) 0x41, (byte) 0x70, (byte) 0x70, (byte) 0x6C, (byte) 0x65, (byte) 0x74};
+ private static byte SELECT_SIMPLEAPPLET[] = {(byte) 0x00, (byte) 0xa4, (byte) 0x04, (byte) 0x00, (byte) 0x0b,
+ (byte) 0x4C, (byte) 0x61, (byte) 0x62, (byte) 0x61, (byte) 0x6B,
+ (byte) 0x41, (byte) 0x70, (byte) 0x70, (byte) 0x6C, (byte) 0x65, (byte) 0x74};
+
+ 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};
+
+ private static byte GENERATEKEY[] = {(byte) 0xB0, (byte) 0x5A, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00};
+ private static byte RESPONDDATA[] = {(byte) 0xB0, (byte) 0x5B, (byte) 0x00, (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x30};
+ public static void main(String[] args) {
+ try {
+ //
+ // SIMULATED CARDS
+ //
+/*
+ // Prepare simulated card
+ byte[] installData = new byte[10]; // no special install data passed now - can be used to pass initial keys etc.
+ cardManager.prepareLocalSimulatorApplet(APPLET_AID, installData, SimpleECCApplet.class);
+
+ // TODO: prepare proper APDU command
+ short additionalDataLen = 0;
+ byte apdu[] = new byte[CardMngr.HEADER_LENGTH + additionalDataLen];
+ apdu[CardMngr.OFFSET_CLA] = (byte) 0xB0;
+ apdu[CardMngr.OFFSET_INS] = (byte) 0x5a;
+ apdu[CardMngr.OFFSET_P1] = (byte) 0x00;
+ apdu[CardMngr.OFFSET_P2] = (byte) 0x00;
+ apdu[CardMngr.OFFSET_LC] = (byte) additionalDataLen;
+
+ // TODO: if additional data are supplied (additionalDataLen != 0), then set proper inpupt here
+
+ // NOTE: we are using sendAPDUSimulator() instead of sendAPDU()
+ byte[] response = cardManager.sendAPDUSimulator(apdu);
+ // TODO: parse response data - status, data
+ response = cardManager.sendAPDUSimulator(apdu);
+
+*/
+
+
+ //
+ // REAL CARDS
+ //
+
+ // TODO: Try same with real card
+ if (cardManager.ConnectToCard()) {
+ // Select our application on card
+ cardManager.sendAPDU(SELECT_SIMPLEAPPLET);
+
+ for (int i = 0; i < 10; i++) {
+ cardManager.sendAPDU(GENERATEKEY);
+ }
+ cardManager.sendAPDU(RESPONDDATA);
+
+ cardManager.DisconnectFromCard();
+ } else {
+ System.out.println("Failed to connect to card");
+ }
+ } catch (Exception ex) {
+ System.out.println("Exception : " + ex);
+ }
+ }
+}