diff --git a/APPLICATION.MD b/APPLICATION.MD index 9b123b9..990d4bb 100644 --- a/APPLICATION.MD +++ b/APPLICATION.MD @@ -48,11 +48,17 @@ SW 0x6985 is returned. All tagged data structures are encoded in the [BER-TLV fo * P1 = 0x04 * P2 = 0x00 * Data = 53746174757357616C6C6574417070 (hex) -* Response = The public key used to establish the SecureChannel +* Response = Application Info Template + +Response Data format: +- Tag 0xA4 = Application Info Template + - Tag 0xC0 = Instance UID (16 bytes) + - Tag 0x80 = ECC public Key The SELECT command is documented in the ISO 7816-4 specifications and is used to select the application on the card, -making it the active one. The data field is the AID of the application. The response is the public key which must -be used by the client to establish the Secure Channel. +making it the active one. The data field is the AID of the application. The response is the Application Info template +which contains the instance UID (which can be used by the client to keep track of multiple cards) and the public key +which must be used by the client to establish the Secure Channel. ### OPEN SECURE CHANNEL diff --git a/src/main/java/im/status/wallet/SecureChannel.java b/src/main/java/im/status/wallet/SecureChannel.java index 5bdf21a..ec09902 100644 --- a/src/main/java/im/status/wallet/SecureChannel.java +++ b/src/main/java/im/status/wallet/SecureChannel.java @@ -233,7 +233,7 @@ public class SecureChannel { * @param off the offset in the buffer * @return the length of the public key */ - public short copyPublicKey(byte[] buf, byte off) { + public short copyPublicKey(byte[] buf, short off) { ECPublicKey pk = (ECPublicKey) scKeypair.getPublic(); return pk.getW(buf, off); } diff --git a/src/main/java/im/status/wallet/WalletApplet.java b/src/main/java/im/status/wallet/WalletApplet.java index 9d303b8..f0b0e0d 100644 --- a/src/main/java/im/status/wallet/WalletApplet.java +++ b/src/main/java/im/status/wallet/WalletApplet.java @@ -24,6 +24,7 @@ public class WalletApplet extends Applet { static final byte PIN_MAX_RETRIES = 3; static final byte KEY_PATH_MAX_DEPTH = 10; static final byte PAIRING_MAX_CLIENT_COUNT = 5; + static final byte UID_LENGTH = 16; static final short EC_KEY_SIZE = 256; static final short CHAIN_CODE_SIZE = 32; @@ -70,11 +71,15 @@ public class WalletApplet extends Applet { static final byte TLV_KEY_INITIALIZATION_STATUS = (byte) 0xC2; static final byte TLV_PUBLIC_KEY_DERIVATION = (byte) 0xC3; + static final byte TLV_APPLICATION_INFO_TEMPLATE = (byte) 0xA4; + static final byte TLV_UID = (byte) 0xC0; + private static final byte[] ASSISTED_DERIVATION_HASH = {(byte) 0xAA, (byte) 0x2D, (byte) 0xA9, (byte) 0x9D, (byte) 0x91, (byte) 0x8C, (byte) 0x7D, (byte) 0x95, (byte) 0xB8, (byte) 0x96, (byte) 0x89, (byte) 0x87, (byte) 0x3E, (byte) 0xAA, (byte) 0x37, (byte) 0x67, (byte) 0x25, (byte) 0x0C, (byte) 0xFF, (byte) 0x50, (byte) 0x13, (byte) 0x9A, (byte) 0x2F, (byte) 0x87, (byte) 0xBB, (byte) 0x4F, (byte) 0xCA, (byte) 0xB4, (byte) 0xAE, (byte) 0xC3, (byte) 0xE8, (byte) 0x90}; private static final byte[] WHISPER_KEY_PATH = {0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01}; private OwnerPIN pin; private OwnerPIN puk; + private byte[] uid; private SecureChannel secureChannel; private ECPublicKey masterPublic; @@ -122,6 +127,9 @@ public class WalletApplet extends Applet { Crypto.init(); SECP256k1.init(); + uid = new byte[UID_LENGTH]; + Crypto.random.generateData(uid, (short) 0, UID_LENGTH); + masterPublic = (ECPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC, EC_KEY_SIZE, false); masterPrivate = (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, EC_KEY_SIZE, false); masterChainCode = new byte[CHAIN_CODE_SIZE]; @@ -246,8 +254,17 @@ public class WalletApplet extends Applet { puk.reset(); apdu.setIncomingAndReceive(); - short keyLength = secureChannel.copyPublicKey(apdu.getBuffer(), ISO7816.OFFSET_CDATA); - apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, keyLength); + 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[1] = (byte)(keyLength + UID_LENGTH + 4); + apdu.setOutgoingAndSend((short) 0, (short)(apduBuffer[1] + 2)); } /** diff --git a/src/test/java/im/status/wallet/WalletAppletTest.java b/src/test/java/im/status/wallet/WalletAppletTest.java index 226c8f0..d1db6d1 100644 --- a/src/test/java/im/status/wallet/WalletAppletTest.java +++ b/src/test/java/im/status/wallet/WalletAppletTest.java @@ -84,7 +84,7 @@ public class WalletAppletTest { void init() throws CardException { reset(); cmdSet = new WalletAppletCommandSet(apduChannel); - byte[] keyData = cmdSet.select().getData(); + byte[] keyData = extractPublicKeyFromSelect(cmdSet.select().getData()); secureChannel = new SecureChannelSession(keyData); cmdSet.setSecureChannel(secureChannel); cmdSet.autoPair(sha256("123456789012".getBytes())); @@ -855,6 +855,14 @@ public class WalletAppletTest { return Arrays.copyOfRange(sig, 5, 5 + sig[4]); } + private byte[] extractPublicKeyFromSelect(byte[] select) { + assertEquals(WalletApplet.TLV_APPLICATION_INFO_TEMPLATE, select[0]); + assertEquals(WalletApplet.TLV_UID, select[2]); + assertEquals(WalletApplet.TLV_PUB_KEY, select[20]); + + return Arrays.copyOfRange(select, 22, select.length); + } + private void reset() { if (USE_SIMULATOR) { simulator.reset();