diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/applets/EC_Consts.java | 184 | ||||
| -rw-r--r-- | src/applets/SimpleECCApplet.java | 351 | ||||
| -rw-r--r-- | src/simpleapdu/CardMngr.java | 209 | ||||
| -rw-r--r-- | src/simpleapdu/ISO7816_status_words.txt | 71 | ||||
| -rw-r--r-- | src/simpleapdu/SimpleAPDU.java | 78 |
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); + } + } +} |
