mirror of
https://github.com/status-im/status-keycard.git
synced 2025-01-20 10:38:58 +00:00
implement duplication (partial)
This commit is contained in:
parent
b00120c637
commit
2a72585cbb
@ -362,8 +362,8 @@ Returns the encrypted duplicate of the master key and terminates the duplication
|
||||
|
||||
#### IMPORT DUPLICATE
|
||||
This command must be sent to all the cards which are a target for duplication. The Data field must contain the output
|
||||
from the EXPORT DUPLICATE command performed on the source card. Returns no data. It follows exactly the same rules as
|
||||
the EXPORT DUPLICATE subcommand
|
||||
from the EXPORT DUPLICATE command performed on the source card. Returns the key UID. It follows exactly the same rules
|
||||
as the EXPORT DUPLICATE subcommand.
|
||||
|
||||
### SIGN
|
||||
|
||||
|
@ -88,12 +88,10 @@ public class SecureChannel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts the content of the APDU by generating an AES key using EC-DH. Only usable in pre-initialization state.
|
||||
* Decrypts the content of the APDU by generating an AES key using EC-DH. Usable only with specific commands.
|
||||
* @param apduBuffer the APDU buffer
|
||||
*/
|
||||
public void oneShotDecrypt(byte[] apduBuffer) {
|
||||
if (pairingSecret != null) return;
|
||||
|
||||
crypto.ecdh.init(scKeypair.getPrivate());
|
||||
|
||||
short off = (short)(ISO7816.OFFSET_CDATA + 1);
|
||||
|
@ -20,6 +20,7 @@ public class WalletApplet extends Applet {
|
||||
static final byte INS_GENERATE_MNEMONIC = (byte) 0xD2;
|
||||
static final byte INS_REMOVE_KEY = (byte) 0xD3;
|
||||
static final byte INS_GENERATE_KEY = (byte) 0xD4;
|
||||
static final byte INS_DUPLICATE_KEY = (byte) 0xD5;
|
||||
static final byte INS_SIGN = (byte) 0xC0;
|
||||
static final byte INS_SET_PINLESS_PATH = (byte) 0xC1;
|
||||
static final byte INS_EXPORT_KEY = (byte) 0xC2;
|
||||
@ -56,6 +57,11 @@ public class WalletApplet extends Applet {
|
||||
static final byte GENERATE_MNEMONIC_P1_CS_MAX = 8;
|
||||
static final byte GENERATE_MNEMONIC_TMP_OFF = SecureChannel.SC_OUT_OFFSET + ((((GENERATE_MNEMONIC_P1_CS_MAX * 32) + GENERATE_MNEMONIC_P1_CS_MAX) / 11) * 2);
|
||||
|
||||
static final byte DUPLICATE_KEY_P1_START = 0x00;
|
||||
static final byte DUPLICATE_KEY_P1_ADD_ENTROPY = 0x01;
|
||||
static final byte DUPLICATE_KEY_P1_EXPORT = 0x02;
|
||||
static final byte DUPLICATE_KEY_P1_IMPORT = 0x03;
|
||||
|
||||
static final byte EXPORT_KEY_P1_ANY = 0x00;
|
||||
static final byte EXPORT_KEY_P1_HIGH = 0x01;
|
||||
|
||||
@ -111,6 +117,9 @@ public class WalletApplet extends Applet {
|
||||
private Crypto crypto;
|
||||
private SECP256k1 secp256k1;
|
||||
|
||||
private byte[] duplicationEncKey;
|
||||
private short expectedEntropy;
|
||||
|
||||
/**
|
||||
* Invoked during applet installation. Creates an instance of this class. The installation parameters are passed in
|
||||
* the given buffer.
|
||||
@ -163,6 +172,9 @@ public class WalletApplet extends Applet {
|
||||
signature = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false);
|
||||
secureChannel = new SecureChannel(PAIRING_MAX_CLIENT_COUNT, crypto, secp256k1);
|
||||
|
||||
duplicationEncKey = new byte[(short)(KeyBuilder.LENGTH_AES_128/8)];
|
||||
expectedEntropy = -1;
|
||||
|
||||
register(bArray, (short) (bOffset + 1), bArray[bOffset]);
|
||||
}
|
||||
|
||||
@ -233,6 +245,8 @@ public class WalletApplet extends Applet {
|
||||
case INS_GENERATE_KEY:
|
||||
generateKey(apdu);
|
||||
break;
|
||||
case INS_DUPLICATE_KEY:
|
||||
duplicateKey(apdu);
|
||||
case INS_SIGN:
|
||||
sign(apdu);
|
||||
break;
|
||||
@ -620,9 +634,9 @@ public class WalletApplet extends Applet {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called internally by the loadKey method to load a key in the TLV format. The presence of the public key is optional
|
||||
* if public key derivation is supported on card, otherwise it is mandatory. The presence of a chain code is indicated
|
||||
* explicitly through the newExtended argument (which is set depending on the P1 parameter of the command).
|
||||
* Called internally by the loadKey method to load a key in the TLV format. The presence of the public key is optional.
|
||||
* The presence of a chain code is indicated explicitly through the newExtended argument (which is set depending on
|
||||
* the P1 parameter of the command).
|
||||
*
|
||||
* @param apduBuffer the APDU buffer
|
||||
* @param newExtended whether the key to load contains a chain code or not
|
||||
@ -952,6 +966,90 @@ public class WalletApplet extends Applet {
|
||||
generateKeyUIDAndRespond(apdu, apduBuffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the DUPLICATE KEY command. The actual processing depends on the subcommand.
|
||||
*
|
||||
* @param apdu the JCRE-owned APDU object.
|
||||
*/
|
||||
private void duplicateKey(APDU apdu) {
|
||||
byte[] apduBuffer = apdu.getBuffer();
|
||||
|
||||
if (apduBuffer[ISO7816.OFFSET_P1] == DUPLICATE_KEY_P1_ADD_ENTROPY) {
|
||||
if (expectedEntropy <= 0) {
|
||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||
}
|
||||
|
||||
secureChannel.oneShotDecrypt(apduBuffer);
|
||||
addEntropy(apduBuffer);
|
||||
return;
|
||||
} else {
|
||||
secureChannel.preprocessAPDU(apduBuffer);
|
||||
|
||||
if (!pin.isValidated()) {
|
||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||
}
|
||||
}
|
||||
|
||||
switch(apduBuffer[ISO7816.OFFSET_P1]) {
|
||||
case DUPLICATE_KEY_P1_START:
|
||||
startDuplication(apduBuffer);
|
||||
break;
|
||||
case DUPLICATE_KEY_P1_EXPORT:
|
||||
short len = exportDuplicate(apduBuffer);
|
||||
secureChannel.respond(apdu, len, ISO7816.SW_NO_ERROR);
|
||||
break;
|
||||
case DUPLICATE_KEY_P1_IMPORT:
|
||||
importDuplicate(apduBuffer);
|
||||
generateKeyUIDAndRespond(apdu, apduBuffer);
|
||||
break;
|
||||
default:
|
||||
ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void startDuplication(byte[] apduBuffer) {
|
||||
if (apduBuffer[ISO7816.OFFSET_LC] != (short) duplicationEncKey.length) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
}
|
||||
|
||||
JCSystem.beginTransaction();
|
||||
Util.arrayCopy(apduBuffer, ISO7816.OFFSET_CDATA, duplicationEncKey, (short) 0, (short) duplicationEncKey.length);
|
||||
expectedEntropy = (short) (apduBuffer[ISO7816.OFFSET_P2] - 1);
|
||||
JCSystem.commitTransaction();
|
||||
}
|
||||
|
||||
private void addEntropy(byte[] apduBuffer) {
|
||||
if (apduBuffer[ISO7816.OFFSET_LC] != (short) duplicationEncKey.length) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
}
|
||||
|
||||
JCSystem.beginTransaction();
|
||||
for (short i = 0; i < (short) duplicationEncKey.length; i++) {
|
||||
duplicationEncKey[i] ^= apduBuffer[(short) (ISO7816.OFFSET_CDATA + i)];
|
||||
}
|
||||
|
||||
expectedEntropy--;
|
||||
JCSystem.commitTransaction();
|
||||
}
|
||||
|
||||
private void finalizeDuplicationKey() {
|
||||
if (expectedEntropy != 0) {
|
||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||
}
|
||||
|
||||
expectedEntropy = -1;
|
||||
}
|
||||
|
||||
private short exportDuplicate(byte[] apduBuffer) {
|
||||
finalizeDuplicationKey();
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void importDuplicate(byte[] apduBuffer) {
|
||||
finalizeDuplicationKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the SIGN command. Requires a secure channel to open and either the PIN to be verified or the PIN-less key
|
||||
* path to be the current key path. This command supports signing data using SHA-256 with possible segmentation over
|
||||
|
Loading…
x
Reference in New Issue
Block a user