From eb73388e21c9ff0ca460839dbdd76b330c41a482 Mon Sep 17 00:00:00 2001 From: Michele Balistreri Date: Tue, 12 Feb 2019 12:21:40 +0300 Subject: [PATCH] bring applet to protocol version 2.1 --- .../java/im/status/keycard/KeycardApplet.java | 71 +++++++++++++------ .../java/im/status/keycard/KeycardTest.java | 22 ++---- 2 files changed, 56 insertions(+), 37 deletions(-) diff --git a/src/main/java/im/status/keycard/KeycardApplet.java b/src/main/java/im/status/keycard/KeycardApplet.java index c00e0fc..8e234a9 100644 --- a/src/main/java/im/status/keycard/KeycardApplet.java +++ b/src/main/java/im/status/keycard/KeycardApplet.java @@ -8,7 +8,7 @@ import javacardx.crypto.Cipher; * The applet's main class. All incoming commands a processed by this class. */ public class KeycardApplet extends Applet { - static final short APPLICATION_VERSION = (short) 0x0200; + static final short APPLICATION_VERSION = (short) 0x0201; static final byte INS_GET_STATUS = (byte) 0xF2; static final byte INS_SET_NDEF = (byte) 0xF3; @@ -84,6 +84,14 @@ public class KeycardApplet extends Applet { static final byte TLV_APPLICATION_INFO_TEMPLATE = (byte) 0xA4; static final byte TLV_UID = (byte) 0x8F; static final byte TLV_KEY_UID = (byte) 0x8E; + static final byte TLV_CAPABILITIES = (byte) 0x8D; + + static final byte CAPABILITY_SECURE_CHANNEL = (byte) 0x01; + static final byte CAPABILITY_KEY_MANAGEMENT = (byte) 0x02; + static final byte CAPABILITY_CREDENTIALS_MANAGEMENT = (byte) 0x04; + static final byte CAPABILITY_NDEF = (byte) 0x08; + + static final byte APPLICATION_CAPABILITIES = (byte)(CAPABILITY_SECURE_CHANNEL | CAPABILITY_KEY_MANAGEMENT | CAPABILITY_CREDENTIALS_MANAGEMENT | CAPABILITY_NDEF); static final byte[] EIP_1581_PREFIX = { (byte) 0x80, 0x00, 0x00, 0x2B, (byte) 0x80, 0x00, 0x00, 0x3C, (byte) 0x80, 0x00, 0x06, 0x2D}; @@ -357,31 +365,50 @@ public class KeycardApplet extends Applet { byte[] apduBuffer = apdu.getBuffer(); - apduBuffer[0] = TLV_APPLICATION_INFO_TEMPLATE; - apduBuffer[2] = TLV_UID; - apduBuffer[3] = UID_LENGTH; - Util.arrayCopyNonAtomic(uid, (short) 0, apduBuffer, (short) 4, UID_LENGTH); - apduBuffer[(short)(UID_LENGTH + 4)] = TLV_PUB_KEY; - short keyLength = secureChannel.copyPublicKey(apduBuffer, (short) (UID_LENGTH + 6)); - apduBuffer[(short)(UID_LENGTH + 5)] = (byte) keyLength; - apduBuffer[(short)(UID_LENGTH + keyLength + 6)] = TLV_INT; - apduBuffer[(short)(UID_LENGTH + keyLength + 7)] = 2; - Util.setShort(apduBuffer, (short)(UID_LENGTH + keyLength + 8), APPLICATION_VERSION); - apduBuffer[(short)(UID_LENGTH + keyLength + 10)] = TLV_INT; - apduBuffer[(short)(UID_LENGTH + keyLength + 11)] = 1; - apduBuffer[(short)(UID_LENGTH + keyLength + 12)] = secureChannel.getRemainingPairingSlots(); - apduBuffer[(short)(UID_LENGTH + keyLength + 13)] = TLV_KEY_UID; + short off = 0; + + apduBuffer[off++] = TLV_APPLICATION_INFO_TEMPLATE; if (privateKey.isInitialized()) { - apduBuffer[(short)(UID_LENGTH + keyLength + 14)] = KEY_UID_LENGTH; - Util.arrayCopyNonAtomic(keyUID, (short) 0, apduBuffer, (short)(UID_LENGTH + keyLength + 15), KEY_UID_LENGTH); - keyLength += KEY_UID_LENGTH; - } else { - apduBuffer[(short)(UID_LENGTH + keyLength + 14)] = 0; + apduBuffer[off++] = (byte) 0x81; } - apduBuffer[1] = (byte)(keyLength + UID_LENGTH + 13); - apdu.setOutgoingAndSend((short) 0, (short)(apduBuffer[1] + 2)); + short lenoff = off++; + + apduBuffer[off++] = TLV_UID; + apduBuffer[off++] = UID_LENGTH; + Util.arrayCopyNonAtomic(uid, (short) 0, apduBuffer, off, UID_LENGTH); + off += UID_LENGTH; + + apduBuffer[off++] = TLV_PUB_KEY; + short keyLength = secureChannel.copyPublicKey(apduBuffer, (short) (off + 1)); + apduBuffer[off++] = (byte) keyLength; + off += keyLength; + + apduBuffer[off++] = TLV_INT; + apduBuffer[off++] = 2; + Util.setShort(apduBuffer, off, APPLICATION_VERSION); + off += 2; + + apduBuffer[off++] = TLV_INT; + apduBuffer[off++] = 1; + apduBuffer[off++] = secureChannel.getRemainingPairingSlots(); + apduBuffer[off++] = TLV_KEY_UID; + + if (privateKey.isInitialized()) { + apduBuffer[off++] = KEY_UID_LENGTH; + Util.arrayCopyNonAtomic(keyUID, (short) 0, apduBuffer, off, KEY_UID_LENGTH); + off += KEY_UID_LENGTH; + } else { + apduBuffer[off++] = 0; + } + + apduBuffer[off++] = TLV_CAPABILITIES; + apduBuffer[off++] = 1; + apduBuffer[off++] = APPLICATION_CAPABILITIES; + + apduBuffer[lenoff] = (byte)(off - lenoff - 1); + apdu.setOutgoingAndSend((short) 0, off); } /** diff --git a/src/test/java/im/status/keycard/KeycardTest.java b/src/test/java/im/status/keycard/KeycardTest.java index 5b0b1b0..56c77a3 100644 --- a/src/test/java/im/status/keycard/KeycardTest.java +++ b/src/test/java/im/status/keycard/KeycardTest.java @@ -3,6 +3,7 @@ package im.status.keycard; import com.licel.jcardsim.smartcardio.CardSimulator; import com.licel.jcardsim.smartcardio.CardTerminalSimulator; import com.licel.jcardsim.utils.AIDUtil; +import im.status.keycard.applet.ApplicationInfo; import im.status.keycard.applet.Identifiers; import im.status.keycard.applet.KeycardCommandSet; import im.status.keycard.desktop.PCSCCardChannel; @@ -168,10 +169,10 @@ public class KeycardTest { // Verify that the keys are changed correctly. Since we do not know the internal counter we just iterate until that // happens for a maximum of SC_COUNTER_MAX times - byte[] initialKey = extractPublicKeyFromSelect(cmdSet.select().getData()); + byte[] initialKey = new ApplicationInfo(cmdSet.select().getData()).getSecureChannelPubKey(); for (int i = 0; i < SecureChannel.SC_COUNTER_MAX; i++) { - byte[] otherKey = extractPublicKeyFromSelect(cmdSet.select().getData()); + byte[] otherKey = new ApplicationInfo(cmdSet.select().getData()).getSecureChannelPubKey(); if (!Arrays.equals(initialKey, otherKey)) { secureChannel.generateSecret(otherKey); @@ -697,9 +698,8 @@ public class KeycardTest { response = cmdSet.select(); assertEquals(0x9000, response.getSw()); - byte[] data = response.getData(); - assertEquals(32, data[30 + data[21]]); - verifyKeyUID(Arrays.copyOfRange(data, (31 + data[21]), (63 + data[21])), (ECPublicKey) keyPair.getPublic()); + ApplicationInfo info = new ApplicationInfo(response.getData()); + verifyKeyUID(info.getKeyUID(), (ECPublicKey) keyPair.getPublic()); cmdSet.autoOpenSecureChannel(); response = cmdSet.verifyPIN("000000"); @@ -715,8 +715,8 @@ public class KeycardTest { response = cmdSet.select(); assertEquals(0x9000, response.getSw()); - data = response.getData(); - assertEquals(0, data[30 + data[21]]); + info = new ApplicationInfo(response.getData()); + assertEquals(0, info.getKeyUID().length); } @Test @@ -1229,14 +1229,6 @@ public class KeycardTest { return Arrays.copyOfRange(sig, 5, 5 + sig[4]); } - private byte[] extractPublicKeyFromSelect(byte[] select) { - assertEquals(KeycardApplet.TLV_APPLICATION_INFO_TEMPLATE, select[0]); - assertEquals(KeycardApplet.TLV_UID, select[2]); - assertEquals(KeycardApplet.TLV_PUB_KEY, select[20]); - - return Arrays.copyOfRange(select, 22, 22 + select[21]); - } - private void reset() { if (USE_SIMULATOR) { simulator.reset();