mirror of
https://github.com/status-im/status-keycard.git
synced 2025-01-16 16:54:12 +00:00
add key uid
This commit is contained in:
parent
6a0ca5e260
commit
582002b29e
@ -56,6 +56,7 @@ Response Data format:
|
||||
- Tag 0x80 = ECC public Key
|
||||
- Tag 0x02 = Application Version (2 bytes)
|
||||
- Tag 0x02 = Number of remaining pairing slots (1 byte)
|
||||
- Tag 0x8E = Key UID (0 or 32 bytes)
|
||||
|
||||
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 Application Info template
|
||||
@ -64,6 +65,8 @@ which must be used by the client to establish the Secure Channel. Additionally i
|
||||
application, formatted on two bytes. The first byte is the major version and the second is the minor version
|
||||
(e.g: version 1.1 is formatted as 0x0101). The number of remaining pairing slots is also included in the response.
|
||||
|
||||
The Key UID can be either empty (when no key is loaded on card) or the SHA-256 hash of the master public key.
|
||||
|
||||
### OPEN SECURE CHANNEL
|
||||
|
||||
The OPEN SECURE CHANNEL command is as specified in the [SECURE_CHANNEL.MD](SECURE_CHANNEL.MD).
|
||||
@ -166,6 +169,7 @@ always returns 0x63C0, even if the PUK is inserted correctly. In this case the w
|
||||
* Data = the key data
|
||||
* Response SW = 0x9000 on success, 0x6A80 if the format is invalid, 0x6A86 if P1 is invalid, 0x6A81 if public key
|
||||
derivation is not supported and the public key is omitted
|
||||
* Response Data = the key UID, defined as the SHA-256 of the public key
|
||||
* Preconditions: Secure Channel must be opened, user PIN must be verified
|
||||
|
||||
P1:
|
||||
|
@ -23,7 +23,7 @@ javacard {
|
||||
aid = '0x53:0x74:0x61:0x74:0x75:0x73:0x57:0x61:0x6c:0x6c:0x65:0x74:0x41:0x70:0x70'
|
||||
className = 'WalletApplet'
|
||||
}
|
||||
version = '1.1'
|
||||
version = '1.2'
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@ import javacard.security.*;
|
||||
* The applet's main class. All incoming commands a processed by this class.
|
||||
*/
|
||||
public class WalletApplet extends Applet {
|
||||
static final short APPLICATION_VERSION = (short) 0x0101;
|
||||
static final short APPLICATION_VERSION = (short) 0x0102;
|
||||
|
||||
static final byte INS_GET_STATUS = (byte) 0xF2;
|
||||
static final byte INS_VERIFY_PIN = (byte) 0x20;
|
||||
@ -31,6 +31,7 @@ public class WalletApplet extends Applet {
|
||||
|
||||
static final short EC_KEY_SIZE = 256;
|
||||
static final short CHAIN_CODE_SIZE = 32;
|
||||
static final short KEY_UID_LENGTH = 32;
|
||||
static final short BIP39_SEED_SIZE = CHAIN_CODE_SIZE * 2;
|
||||
|
||||
static final byte GET_STATUS_P1_APPLICATION = 0x00;
|
||||
@ -81,6 +82,7 @@ public class WalletApplet 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;
|
||||
|
||||
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 EXPORT_KEY_HIGH_MASK = (byte) 0xc0;
|
||||
@ -114,6 +116,8 @@ public class WalletApplet extends Applet {
|
||||
private boolean signInProgress;
|
||||
private boolean expectPublicKey;
|
||||
|
||||
private byte[] keyUID;
|
||||
|
||||
private Crypto crypto;
|
||||
private SECP256k1 secp256k1;
|
||||
|
||||
@ -161,6 +165,8 @@ public class WalletApplet extends Applet {
|
||||
keyPath = new byte[KEY_PATH_MAX_DEPTH * 4];
|
||||
pinlessPath = new byte[KEY_PATH_MAX_DEPTH * 4];
|
||||
|
||||
keyUID = new byte[KEY_UID_LENGTH];
|
||||
|
||||
resetCurveParameters();
|
||||
|
||||
signature = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false);
|
||||
@ -309,7 +315,17 @@ public class WalletApplet extends Applet {
|
||||
apduBuffer[(short)(UID_LENGTH + keyLength + 10)] = TLV_INT;
|
||||
apduBuffer[(short)(UID_LENGTH + keyLength + 11)] = 1;
|
||||
apduBuffer[(short)(UID_LENGTH + keyLength + 12)] = secureChannel.getRemainingPairingSlots();
|
||||
apduBuffer[1] = (byte)(keyLength + UID_LENGTH + 11);
|
||||
apduBuffer[(short)(UID_LENGTH + keyLength + 13)] = TLV_KEY_UID;
|
||||
|
||||
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[1] = (byte)(keyLength + UID_LENGTH + 13);
|
||||
apdu.setOutgoingAndSend((short) 0, (short)(apduBuffer[1] + 2));
|
||||
}
|
||||
|
||||
@ -485,6 +501,11 @@ public class WalletApplet extends Applet {
|
||||
ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
|
||||
break;
|
||||
}
|
||||
|
||||
short pubLen = masterPublic.getW(apduBuffer, (short) 0);
|
||||
crypto.sha256.doFinal(apduBuffer, (short) 0, pubLen, keyUID, (short) 0);
|
||||
Util.arrayCopyNonAtomic(keyUID, (short) 0, apduBuffer, SecureChannel.SC_OUT_OFFSET, KEY_UID_LENGTH);
|
||||
secureChannel.respond(apdu, KEY_UID_LENGTH, ISO7816.SW_NO_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -34,6 +34,7 @@ import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.Security;
|
||||
import java.security.Signature;
|
||||
import org.bouncycastle.jce.interfaces.ECPublicKey;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
||||
@ -117,6 +118,7 @@ public class WalletAppletTest {
|
||||
assertEquals(WalletApplet.APPLICATION_VERSION >> 8, data[24 + data[21]]);
|
||||
assertEquals(WalletApplet.APPLICATION_VERSION & 0xFF, data[25 + data[21]]);
|
||||
assertEquals(WalletApplet.TLV_INT, data[26 + data[21]]);
|
||||
assertEquals(WalletApplet.TLV_KEY_UID, data[29 + data[21]]);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -495,18 +497,22 @@ public class WalletAppletTest {
|
||||
// Correct LOAD KEY
|
||||
response = cmdSet.loadKey(keyPair);
|
||||
assertEquals(0x9000, response.getSW());
|
||||
verifyKeyUID(response.getData(), ((ECPublicKey) keyPair.getPublic()));
|
||||
|
||||
keyPair = g.generateKeyPair();
|
||||
|
||||
// Check extended key
|
||||
response = cmdSet.loadKey(keyPair, false, chainCode);
|
||||
assertEquals(0x9000, response.getSW());
|
||||
verifyKeyUID(response.getData(), ((ECPublicKey) keyPair.getPublic()));
|
||||
|
||||
// Check omitted public key
|
||||
response = cmdSet.loadKey(keyPair, true, null);
|
||||
assertEquals(publicKeyDerivationSW, response.getSW());
|
||||
verifyKeyUID(response.getData(), ((ECPublicKey) keyPair.getPublic()));
|
||||
response = cmdSet.loadKey(keyPair, true, chainCode);
|
||||
assertEquals(publicKeyDerivationSW, response.getSW());
|
||||
verifyKeyUID(response.getData(), ((ECPublicKey) keyPair.getPublic()));
|
||||
|
||||
// Check seed load
|
||||
response = cmdSet.loadKey(keyPair.getPrivate(), chainCode);
|
||||
@ -571,6 +577,16 @@ public class WalletAppletTest {
|
||||
response = cmdSet.loadKey(keyPair);
|
||||
assertEquals(0x9000, response.getSW());
|
||||
|
||||
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());
|
||||
|
||||
cmdSet.autoOpenSecureChannel();
|
||||
response = cmdSet.verifyPIN("000000");
|
||||
assertEquals(0x9000, response.getSW());
|
||||
|
||||
assertTrue(cmdSet.getKeyInitializationStatus());
|
||||
|
||||
// Good case
|
||||
@ -578,6 +594,11 @@ public class WalletAppletTest {
|
||||
assertEquals(0x9000, response.getSW());
|
||||
|
||||
assertFalse(cmdSet.getKeyInitializationStatus());
|
||||
|
||||
response = cmdSet.select();
|
||||
assertEquals(0x9000, response.getSW());
|
||||
data = response.getData();
|
||||
assertEquals(0, data[30 + data[21]]);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -1524,4 +1545,12 @@ public class WalletAppletTest {
|
||||
|
||||
return new Sign.SignatureData(v, rB, sB);
|
||||
}
|
||||
|
||||
private void verifyKeyUID(byte[] keyUID, ECPublicKey pubKey) {
|
||||
verifyKeyUID(keyUID, pubKey.getQ().getEncoded(false));
|
||||
}
|
||||
|
||||
private void verifyKeyUID(byte[] keyUID, byte[] pubKey) {
|
||||
assertArrayEquals(sha256(pubKey), keyUID);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user