decouple pairing secret from PUK
This commit is contained in:
parent
eb233757fc
commit
527760d7df
|
@ -55,14 +55,14 @@ Response Data format:
|
|||
- Tag 0x8F = Instance UID (16 bytes)
|
||||
- Tag 0x80 = ECC public Key
|
||||
- Tag 0x02 = Application Version (2 bytes)
|
||||
- Tag 0x02 = PUK retry count (1 byte)
|
||||
- Tag 0x02 = Number of remaining pairing slots (1 byte)
|
||||
|
||||
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
|
||||
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. Additionally it contains the version number of the
|
||||
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 PUK retry count is also included in the response.
|
||||
(e.g: version 1.1 is formatted as 0x0101). The number of remaining pairing slots is also included in the response.
|
||||
|
||||
### OPEN SECURE CHANNEL
|
||||
|
||||
|
@ -74,8 +74,14 @@ The MUTUALLY AUTHENTICATE command is as specified in the [SECURE_CHANNEL.MD](SEC
|
|||
|
||||
### PAIR
|
||||
|
||||
The PAIR command is as specified in the [SECURE_CHANNEL.MD](SECURE_CHANNEL.MD). The shared secret is the SHA-256 of the
|
||||
PUK.
|
||||
The PAIR command is as specified in the [SECURE_CHANNEL.MD](SECURE_CHANNEL.MD). The shared secret is 32 bytes long. The
|
||||
way the secret is generated is not within the scope of this document because the applet accepts the already generated
|
||||
secret as an installation parameter. However the card issuer and clients must agree on the way it is generated for
|
||||
interoperability reasons.
|
||||
|
||||
If the code is meant to be input manually, a random, variable length alphanumeric password to be used in conjuction with
|
||||
an algorithm such as scrypt or PBKDF2 is suggested. If another input mechanism is foreseen (such as QR-code scanning),
|
||||
then the sequence should be composed of random bytes from a secure source of randomness.
|
||||
|
||||
### UNPAIR
|
||||
|
||||
|
|
|
@ -61,7 +61,9 @@ task install(type: Exec) {
|
|||
select -AID ${project.properties['im.status.gradle.gpshell.isd']}
|
||||
open_sc -security 1 -keyind 0 -keyver ${project.properties['im.status.gradle.gpshell.kvn']} -mac_key ${project.properties['im.status.gradle.gpshell.mac_key']} -enc_key ${project.properties['im.status.gradle.gpshell.enc_key']} -kek_key ${project.properties['im.status.gradle.gpshell.kek_key']}
|
||||
send_apdu_nostop -sc 1 -APDU 80E400800E4F0C53746174757357616C6C6574
|
||||
install -file build/javacard/im/status/wallet/javacard/wallet.cap -AID 53746174757357616C6C6574417070 -instAID 53746174757357616C6C6574417070 -instParam 313233343536373839303132
|
||||
install_for_load -pkgAID 53746174757357616C6C6574
|
||||
load -file build/javacard/im/status/wallet/javacard/wallet.cap
|
||||
send_apdu -sc 1 -APDU 80E60C005F0C53746174757357616C6C65740F53746174757357616C6C65744170700F53746174757357616C6C657441707001002EC92C313233343536373839303132178381C5E8D324BED4033D14E1E1FDCAAADB74803869bee9f7a10b1b7108ed5300
|
||||
card_disconnect
|
||||
release_context
|
||||
"""
|
||||
|
|
|
@ -21,6 +21,8 @@ public class Crypto {
|
|||
final static private short HMAC_BLOCK_SIZE = (short) 128;
|
||||
final static private short HMAC_BLOCK_OFFSET = (short) KEY_DERIVATION_INPUT_SIZE + HMAC_OUT_SIZE;
|
||||
|
||||
final static private byte[] KEY_BITCOIN_SEED = {'B', 'i', 't', 'c', 'o', 'i', 'n', ' ', 's', 'e', 'e', 'd'};
|
||||
|
||||
// The below 4 objects can be accessed anywhere from the entire applet
|
||||
static RandomData random;
|
||||
static KeyAgreement ecdh;
|
||||
|
@ -102,6 +104,19 @@ public class Crypto {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the algorithm for master key derivation defined by BIP32 to the binary seed provided as input.
|
||||
*
|
||||
* @param seed the binary seed
|
||||
* @param seedOff the offset of the binary seed
|
||||
* @param seedSize the size of the binary seed
|
||||
* @param masterKey the output buffer
|
||||
* @param keyOff the offset in the output buffer
|
||||
*/
|
||||
static void bip32MasterFromSeed(byte[] seed, short seedOff, short seedSize, byte[] masterKey, short keyOff) {
|
||||
Crypto.hmacSHA512(KEY_BITCOIN_SEED, (short) 0, (short) KEY_BITCOIN_SEED.length, seed, seedOff, seedSize, masterKey, keyOff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes the S value of the signature as described in BIP-62 to avoid malleable signatures. It also fixes the all
|
||||
* internal TLV length fields. Returns the number of bytes by which the overall signature length changed (0 or -1).
|
||||
|
|
|
@ -41,6 +41,7 @@ public class SecureChannel {
|
|||
private byte[] pairingKeys;
|
||||
|
||||
private short preassignedPairingOffset = -1;
|
||||
private byte remainingSlots;
|
||||
private boolean mutuallyAuthenticated = false;
|
||||
|
||||
/**
|
||||
|
@ -68,6 +69,7 @@ public class SecureChannel {
|
|||
secret = JCSystem.makeTransientByteArray((short)(SC_SECRET_LENGTH * 2), JCSystem.CLEAR_ON_DESELECT);
|
||||
pairingSecret = new byte[SC_SECRET_LENGTH];
|
||||
pairingKeys = new byte[(short)(PAIRING_KEY_LENGTH * pairingLimit)];
|
||||
remainingSlots = pairingLimit;
|
||||
|
||||
Util.arrayCopyNonAtomic(aPairingSecret, off, pairingSecret, (short) 0, SC_SECRET_LENGTH);
|
||||
}
|
||||
|
@ -222,6 +224,7 @@ public class SecureChannel {
|
|||
Crypto.sha256.update(pairingSecret, (short) 0, SC_SECRET_LENGTH);
|
||||
Crypto.sha256.doFinal(apduBuffer, (short) 1, SC_SECRET_LENGTH, pairingKeys, (short) (preassignedPairingOffset + 1));
|
||||
pairingKeys[preassignedPairingOffset] = 1;
|
||||
remainingSlots--;
|
||||
apduBuffer[0] = (byte) (preassignedPairingOffset / PAIRING_KEY_LENGTH);
|
||||
|
||||
preassignedPairingOffset = -1;
|
||||
|
@ -237,7 +240,10 @@ public class SecureChannel {
|
|||
*/
|
||||
public void unpair(byte[] apduBuffer) {
|
||||
short off = checkPairingIndexAndGetOffset(apduBuffer[ISO7816.OFFSET_P1]);
|
||||
Util.arrayFillNonAtomic(pairingKeys, off, PAIRING_KEY_LENGTH, (byte) 0);
|
||||
if (pairingKeys[off] == 1) {
|
||||
Util.arrayFillNonAtomic(pairingKeys, off, PAIRING_KEY_LENGTH, (byte) 0);
|
||||
remainingSlots++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -361,6 +367,13 @@ public class SecureChannel {
|
|||
return scEncKey.isInitialized() && scMacKey.isInitialized() && mutuallyAuthenticated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of still available pairing slots.
|
||||
*/
|
||||
public byte getRemainingPairingSlots() {
|
||||
return remainingSlots;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the Secure Channel, invalidating the current session. If no session is opened, this does nothing.
|
||||
*/
|
||||
|
|
|
@ -30,7 +30,7 @@ public class WalletApplet extends Applet {
|
|||
|
||||
static final short EC_KEY_SIZE = 256;
|
||||
static final short CHAIN_CODE_SIZE = 32;
|
||||
static final short SEED_SIZE = CHAIN_CODE_SIZE * 2;
|
||||
static final short BIP39_SEED_SIZE = CHAIN_CODE_SIZE * 2;
|
||||
|
||||
static final byte GET_STATUS_P1_APPLICATION = 0x00;
|
||||
static final byte GET_STATUS_P1_KEY_PATH = 0x01;
|
||||
|
@ -157,7 +157,6 @@ public class WalletApplet extends Applet {
|
|||
keyPath = new byte[KEY_PATH_MAX_DEPTH * 4];
|
||||
pinlessPath = new byte[KEY_PATH_MAX_DEPTH * 4];
|
||||
|
||||
|
||||
SECP256k1.setCurveParameters(masterPublic);
|
||||
SECP256k1.setCurveParameters(masterPrivate);
|
||||
|
||||
|
@ -173,9 +172,7 @@ public class WalletApplet extends Applet {
|
|||
short c9Off = (short)(bOffset + bArray[bOffset] + 1); // Skip AID
|
||||
c9Off += (short)(bArray[c9Off] + 2); // Skip Privileges and parameter length
|
||||
|
||||
Crypto.sha256.doFinal(bArray, c9Off, PUK_LENGTH, chainCode, (short) 0);
|
||||
secureChannel = new SecureChannel(PAIRING_MAX_CLIENT_COUNT, chainCode, (short) 0);
|
||||
Util.arrayFillNonAtomic(chainCode, (short) 0, CHAIN_CODE_SIZE, (byte) 0);
|
||||
secureChannel = new SecureChannel(PAIRING_MAX_CLIENT_COUNT, bArray, (short) (c9Off + PUK_LENGTH));
|
||||
|
||||
puk = new OwnerPIN(PUK_MAX_RETRIES, PUK_LENGTH);
|
||||
puk.update(bArray, c9Off, PUK_LENGTH);
|
||||
|
@ -312,7 +309,7 @@ public class WalletApplet extends Applet {
|
|||
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)] = puk.getTriesRemaining();
|
||||
apduBuffer[(short)(UID_LENGTH + keyLength + 12)] = secureChannel.getRemainingPairingSlots();
|
||||
apduBuffer[1] = (byte)(keyLength + UID_LENGTH + 11);
|
||||
apdu.setOutgoingAndSend((short) 0, (short)(apduBuffer[1] + 2));
|
||||
}
|
||||
|
@ -574,10 +571,12 @@ public class WalletApplet extends Applet {
|
|||
private void loadSeed(byte[] apduBuffer) {
|
||||
SECP256k1.assertECPointMultiplicationSupport();
|
||||
|
||||
if (apduBuffer[ISO7816.OFFSET_LC] != SEED_SIZE) {
|
||||
if (apduBuffer[ISO7816.OFFSET_LC] != BIP39_SEED_SIZE) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
}
|
||||
|
||||
Crypto.bip32MasterFromSeed(apduBuffer, (short) ISO7816.OFFSET_CDATA, BIP39_SEED_SIZE, apduBuffer, (short) ISO7816.OFFSET_CDATA);
|
||||
|
||||
JCSystem.beginTransaction();
|
||||
isExtended = true;
|
||||
|
||||
|
|
|
@ -42,7 +42,8 @@ import static org.junit.jupiter.api.Assertions.*;
|
|||
|
||||
@DisplayName("Test the Wallet Applet")
|
||||
public class WalletAppletTest {
|
||||
public static final byte[] SHARED_SECRET = sha256("123456789012".getBytes());
|
||||
// Generated on https://www.browserling.com/tools/scrypt using Password abc123, Salt WVpJqW4u, Output Size 32, N 16384, r 8, p 1
|
||||
public static final byte[] SHARED_SECRET = new byte[] { (byte) 0x17, (byte) 0x83, (byte) 0x81, (byte) 0xc5, (byte) 0xe8, (byte) 0xd3, (byte) 0x24, (byte) 0xbe, (byte) 0xd4, (byte) 0x03, (byte) 0x3d, (byte) 0x14, (byte) 0xe1, (byte) 0xe1, (byte) 0xfd, (byte) 0xca, (byte) 0xaa, (byte) 0xdb, (byte) 0x74, (byte) 0x80, (byte) 0x38, (byte) 0x69, (byte) 0xbe, (byte) 0xe9, (byte) 0xf7, (byte) 0xa1, (byte) 0x0b, (byte) 0x1b, (byte) 0x71, (byte) 0x08, (byte) 0xed, (byte) 0x53 };
|
||||
private static CardTerminal cardTerminal;
|
||||
private static CardChannel apduChannel;
|
||||
private static CardSimulator simulator;
|
||||
|
|
Loading…
Reference in New Issue