mirror of
https://github.com/status-im/status-keycard.git
synced 2025-01-31 07:57:41 +00:00
add remove key command
This commit is contained in:
parent
3c5fd3bfca
commit
6a0ca5e260
@ -104,8 +104,8 @@ if P1 = 0x00:
|
|||||||
- Tag 0xA3 = Application Status Template
|
- Tag 0xA3 = Application Status Template
|
||||||
- Tag 0x02 = PIN retry count (1 byte)
|
- Tag 0x02 = PIN retry count (1 byte)
|
||||||
- Tag 0x02 = PUK retry count (1 byte)
|
- Tag 0x02 = PUK retry count (1 byte)
|
||||||
- Tag 0x01 = 0 if key is not initialized, 1 otherwise
|
- Tag 0x01 = 0xff if key is initialized, 0 otherwise
|
||||||
- Tag 0x01 = 1 if public key derivation is supported, 0 otherwise
|
- Tag 0x01 = 0xff if public key derivation is supported, 0 otherwise
|
||||||
|
|
||||||
if P1 = 0x01
|
if P1 = 0x01
|
||||||
- a sequence of 32-bit numbers indicating the current key path. Empty if master key is selected.
|
- a sequence of 32-bit numbers indicating the current key path. Empty if master key is selected.
|
||||||
@ -255,6 +255,18 @@ Used to generate a mnemonic according to the algorithm specified in [BIP39](http
|
|||||||
The returned data is a list of 16-byte integers which should be used as indexes in a wordlist to generate the
|
The returned data is a list of 16-byte integers which should be used as indexes in a wordlist to generate the
|
||||||
human-readable mnemonic. Each integer can have a value from 0 to 2047.
|
human-readable mnemonic. Each integer can have a value from 0 to 2047.
|
||||||
|
|
||||||
|
### REMOVE KEY
|
||||||
|
|
||||||
|
* CLA = 0x80
|
||||||
|
* INS = 0xD3
|
||||||
|
* P1 = 0x00
|
||||||
|
* P2 = 0x00
|
||||||
|
* Response SW = 0x9000 on success.
|
||||||
|
* Preconditions: Secure Channel must be opened, user PIN must be verified
|
||||||
|
|
||||||
|
Removes the key from the card, bringing it back to an uninitialized state. No signing operation is possible after this
|
||||||
|
command until a new LOAD KEY command is performed.
|
||||||
|
|
||||||
### SIGN
|
### SIGN
|
||||||
|
|
||||||
* CLA = 0x80
|
* CLA = 0x80
|
||||||
|
@ -16,6 +16,7 @@ public class WalletApplet extends Applet {
|
|||||||
static final byte INS_LOAD_KEY = (byte) 0xD0;
|
static final byte INS_LOAD_KEY = (byte) 0xD0;
|
||||||
static final byte INS_DERIVE_KEY = (byte) 0xD1;
|
static final byte INS_DERIVE_KEY = (byte) 0xD1;
|
||||||
static final byte INS_GENERATE_MNEMONIC = (byte) 0xD2;
|
static final byte INS_GENERATE_MNEMONIC = (byte) 0xD2;
|
||||||
|
static final byte INS_REMOVE_KEY = (byte) 0xD3;
|
||||||
static final byte INS_SIGN = (byte) 0xC0;
|
static final byte INS_SIGN = (byte) 0xC0;
|
||||||
static final byte INS_SET_PINLESS_PATH = (byte) 0xC1;
|
static final byte INS_SET_PINLESS_PATH = (byte) 0xC1;
|
||||||
static final byte INS_EXPORT_KEY = (byte) 0xC2;
|
static final byte INS_EXPORT_KEY = (byte) 0xC2;
|
||||||
@ -160,15 +161,7 @@ public class WalletApplet extends Applet {
|
|||||||
keyPath = new byte[KEY_PATH_MAX_DEPTH * 4];
|
keyPath = new byte[KEY_PATH_MAX_DEPTH * 4];
|
||||||
pinlessPath = new byte[KEY_PATH_MAX_DEPTH * 4];
|
pinlessPath = new byte[KEY_PATH_MAX_DEPTH * 4];
|
||||||
|
|
||||||
secp256k1.setCurveParameters(masterPublic);
|
resetCurveParameters();
|
||||||
secp256k1.setCurveParameters(masterPrivate);
|
|
||||||
|
|
||||||
secp256k1.setCurveParameters(parentPublicKey);
|
|
||||||
secp256k1.setCurveParameters(parentPrivateKey);
|
|
||||||
parentValid = false;
|
|
||||||
|
|
||||||
secp256k1.setCurveParameters(publicKey);
|
|
||||||
secp256k1.setCurveParameters(privateKey);
|
|
||||||
|
|
||||||
signature = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false);
|
signature = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false);
|
||||||
|
|
||||||
@ -239,6 +232,9 @@ public class WalletApplet extends Applet {
|
|||||||
case INS_GENERATE_MNEMONIC:
|
case INS_GENERATE_MNEMONIC:
|
||||||
generateMnemonic(apdu);
|
generateMnemonic(apdu);
|
||||||
break;
|
break;
|
||||||
|
case INS_REMOVE_KEY:
|
||||||
|
removeKey(apdu);
|
||||||
|
break;
|
||||||
case INS_SIGN:
|
case INS_SIGN:
|
||||||
sign(apdu);
|
sign(apdu);
|
||||||
break;
|
break;
|
||||||
@ -832,6 +828,34 @@ public class WalletApplet extends Applet {
|
|||||||
return (short) ((short)((short) 0x4000 >>> (short) (amount - 1)) | tmp);
|
return (short) ((short)((short) 0x4000 >>> (short) (amount - 1)) | tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void removeKey(APDU apdu) {
|
||||||
|
byte[] apduBuffer = apdu.getBuffer();
|
||||||
|
secureChannel.preprocessAPDU(apduBuffer);
|
||||||
|
|
||||||
|
if (!pin.isValidated()) {
|
||||||
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||||
|
}
|
||||||
|
|
||||||
|
keyPathLen = 0;
|
||||||
|
pinlessPathLen = 0;
|
||||||
|
parentValid = false;
|
||||||
|
isExtended = false;
|
||||||
|
signInProgress = false;
|
||||||
|
expectPublicKey = false;
|
||||||
|
privateKey.clearKey();
|
||||||
|
publicKey.clearKey();
|
||||||
|
masterPrivate.clearKey();
|
||||||
|
masterPublic.clearKey();
|
||||||
|
parentPrivateKey.clearKey();
|
||||||
|
parentPublicKey.clearKey();
|
||||||
|
resetCurveParameters();
|
||||||
|
Util.arrayFillNonAtomic(chainCode, (short) 0, (short) chainCode.length, (byte) 0);
|
||||||
|
Util.arrayFillNonAtomic(parentChainCode, (short) 0, (short) parentChainCode.length, (byte) 0);
|
||||||
|
Util.arrayFillNonAtomic(masterChainCode, (short) 0, (short) masterChainCode.length, (byte) 0);
|
||||||
|
Util.arrayFillNonAtomic(keyPath, (short) 0, (short) keyPath.length, (byte) 0);
|
||||||
|
Util.arrayFillNonAtomic(pinlessPath, (short) 0, (short) pinlessPath.length, (byte) 0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes the SIGN command. Requires a secure channel to open and either the PIN to be verified or the PIN-less key
|
* 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
|
* path to be the current key path. This command supports signing data using SHA-256 with possible segmentation over
|
||||||
@ -927,7 +951,7 @@ public class WalletApplet extends Applet {
|
|||||||
byte[] apduBuffer = apdu.getBuffer();
|
byte[] apduBuffer = apdu.getBuffer();
|
||||||
secureChannel.preprocessAPDU(apduBuffer);
|
secureChannel.preprocessAPDU(apduBuffer);
|
||||||
|
|
||||||
if (!pin.isValidated()) {
|
if (!pin.isValidated() || !privateKey.isInitialized()) {
|
||||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1014,4 +1038,18 @@ public class WalletApplet extends Applet {
|
|||||||
private boolean isPinless() {
|
private boolean isPinless() {
|
||||||
return (pinlessPathLen > 0) && (pinlessPathLen == keyPathLen) && (Util.arrayCompare(keyPath, (short) 0, pinlessPath, (short) 0, keyPathLen) == 0);
|
return (pinlessPathLen > 0) && (pinlessPathLen == keyPathLen) && (Util.arrayCompare(keyPath, (short) 0, pinlessPath, (short) 0, keyPathLen) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set curve parameters to cleared keys
|
||||||
|
*/
|
||||||
|
private void resetCurveParameters() {
|
||||||
|
secp256k1.setCurveParameters(masterPublic);
|
||||||
|
secp256k1.setCurveParameters(masterPrivate);
|
||||||
|
|
||||||
|
secp256k1.setCurveParameters(parentPublicKey);
|
||||||
|
secp256k1.setCurveParameters(parentPrivateKey);
|
||||||
|
|
||||||
|
secp256k1.setCurveParameters(publicKey);
|
||||||
|
secp256k1.setCurveParameters(privateKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,6 +138,19 @@ public class WalletAppletCommandSet {
|
|||||||
return data[data.length - 1] != 0x00;
|
return data[data.length - 1] != 0x00;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a GET STATUS APDU to retrieve the APPLICATION STATUS template and reads the byte indicating key initialization
|
||||||
|
* status
|
||||||
|
*
|
||||||
|
* @return whether public key derivation is supported or not
|
||||||
|
* @throws CardException communication error
|
||||||
|
*/
|
||||||
|
public boolean getKeyInitializationStatus() throws CardException {
|
||||||
|
ResponseAPDU resp = getStatus(WalletApplet.GET_STATUS_P1_APPLICATION);
|
||||||
|
byte[] data = resp.getData();
|
||||||
|
return data[data.length - 4] != 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a VERIFY PIN APDU. The raw bytes of the given string are encrypted using the secure channel and used as APDU
|
* Sends a VERIFY PIN APDU. The raw bytes of the given string are encrypted using the secure channel and used as APDU
|
||||||
* data.
|
* data.
|
||||||
@ -355,6 +368,17 @@ public class WalletAppletCommandSet {
|
|||||||
return secureChannel.transmit(apduChannel, generateMnemonic);
|
return secureChannel.transmit(apduChannel, generateMnemonic);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a REMOVE KEY APDU.
|
||||||
|
*
|
||||||
|
* @return the raw card response
|
||||||
|
* @throws CardException communication error
|
||||||
|
*/
|
||||||
|
public ResponseAPDU removeKey() throws CardException {
|
||||||
|
CommandAPDU removeKey = secureChannel.protectedCommand(0x80, WalletApplet.INS_REMOVE_KEY, 0, 0, new byte[0]);
|
||||||
|
return secureChannel.transmit(apduChannel, removeKey);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a SIGN APDU. The dataType is P1 as defined in the applet. The isFirst and isLast arguments are used to form
|
* Sends a SIGN APDU. The dataType is P1 as defined in the applet. The isFirst and isLast arguments are used to form
|
||||||
* the P2 parameter. The data is the data to sign, or part of it. Only when sending the last block a signature is
|
* the P2 parameter. The data is the data to sign, or part of it. Only when sending the last block a signature is
|
||||||
|
@ -550,6 +550,36 @@ public class WalletAppletTest {
|
|||||||
assertMnemonic(24, response.getData());
|
assertMnemonic(24, response.getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("REMOVE KEY command")
|
||||||
|
void removeKeyTest() throws Exception {
|
||||||
|
KeyPairGenerator g = keypairGenerator();
|
||||||
|
KeyPair keyPair = g.generateKeyPair();
|
||||||
|
|
||||||
|
// Security condition violation: SecureChannel not open
|
||||||
|
ResponseAPDU response = cmdSet.removeKey();
|
||||||
|
assertEquals(0x6985, response.getSW());
|
||||||
|
cmdSet.autoOpenSecureChannel();
|
||||||
|
|
||||||
|
// Security condition violation: PIN not verified
|
||||||
|
response = cmdSet.removeKey();
|
||||||
|
assertEquals(0x6985, response.getSW());
|
||||||
|
|
||||||
|
response = cmdSet.verifyPIN("000000");
|
||||||
|
assertEquals(0x9000, response.getSW());
|
||||||
|
|
||||||
|
response = cmdSet.loadKey(keyPair);
|
||||||
|
assertEquals(0x9000, response.getSW());
|
||||||
|
|
||||||
|
assertTrue(cmdSet.getKeyInitializationStatus());
|
||||||
|
|
||||||
|
// Good case
|
||||||
|
response = cmdSet.removeKey();
|
||||||
|
assertEquals(0x9000, response.getSW());
|
||||||
|
|
||||||
|
assertFalse(cmdSet.getKeyInitializationStatus());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("DERIVE KEY command")
|
@DisplayName("DERIVE KEY command")
|
||||||
void deriveKeyTest() throws Exception {
|
void deriveKeyTest() throws Exception {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user