diff options
Diffstat (limited to 'src/applets/SimpleECCApplet.java')
| -rw-r--r-- | src/applets/SimpleECCApplet.java | 351 |
1 files changed, 351 insertions, 0 deletions
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); + } +} + |
