aboutsummaryrefslogtreecommitdiff
path: root/src/applets/SimpleECCApplet.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/applets/SimpleECCApplet.java')
-rw-r--r--src/applets/SimpleECCApplet.java351
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);
+ }
+}
+