implement GET STATUS
This commit is contained in:
parent
f4ea1f6e69
commit
5ba012fad1
|
@ -55,6 +55,22 @@ be used by the client to establish the Secure Channel.
|
||||||
|
|
||||||
The OPEN SECURE CHANNEL command is as specified in the [SECURE_CHANNEL.MD](SECURE_CHANNEL.MD).
|
The OPEN SECURE CHANNEL command is as specified in the [SECURE_CHANNEL.MD](SECURE_CHANNEL.MD).
|
||||||
|
|
||||||
|
### GET STATUS
|
||||||
|
* CLA = 0x80
|
||||||
|
* INS = 0xF2
|
||||||
|
* P1 = 0x00
|
||||||
|
* P2 = 0x00
|
||||||
|
* Response SW = 0x9000 on success
|
||||||
|
* Response Data = Application Status Template
|
||||||
|
* Preconditions: Secure Channel must be opened
|
||||||
|
|
||||||
|
Response Data format:
|
||||||
|
- Tag 0xA3 = Application Status Template
|
||||||
|
- Tag 0xC0 = PIN retry count (1 byte)
|
||||||
|
- Tag 0xC1 = PUK retry count (1 byte)
|
||||||
|
- Tag 0xC2 = 0 if key is not initialized, 1 otherwise
|
||||||
|
- Tag 0xC3 = 0 if public key derivation is not supported, 1 otherwise
|
||||||
|
|
||||||
### VERIFY PIN
|
### VERIFY PIN
|
||||||
|
|
||||||
* CLA = 0x80
|
* CLA = 0x80
|
||||||
|
@ -143,14 +159,14 @@ signing sessions, if any. Unless a DERIVE KEY is sent, a subsequent SIGN command
|
||||||
* Data = key derivation template
|
* Data = key derivation template
|
||||||
* Response SW = 0x9000 on success, 0x6A80 if the format is invalid, 0x6A81 if public keys are omitted and their derivation
|
* Response SW = 0x9000 on success, 0x6A80 if the format is invalid, 0x6A81 if public keys are omitted and their derivation
|
||||||
is not supported.
|
is not supported.
|
||||||
* Preconditions: Secure Channel must be opened, user PIN must be verified
|
* Preconditions: Secure Channel must be opened, user PIN must be verified, an extended keyset must be loaded
|
||||||
|
|
||||||
Data format:
|
Data format:
|
||||||
|
|
||||||
- Tag 0xA2 = key derivation template
|
- Tag 0xA2 = key derivation template
|
||||||
- Tag 0x82 = a sequence of 32-bit integers (most significant byte first). Empty if the master key must be used.
|
- Tag 0xC0 = a sequence of 32-bit integers (most significant byte first). Empty if the master key must be used.
|
||||||
- Tag 0x81 = parent public key (omitted if master or public key derivation is supported)
|
- Tag 0xC1 = derived public key (omitted if master or public key derivation is supported)
|
||||||
- Tag 0x80 = derived public key (omitted if master or public key derivation is supported)
|
- Tag 0xC2 = parent public key (omitted if master or public key derivation is supported)
|
||||||
|
|
||||||
This command is used before a signing session to generated a private key according to the [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)
|
This command is used before a signing session to generated a private key according to the [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)
|
||||||
specifications. The generated key is used for all subsequent SIGN sessions. An empty 0x82 is used in order for SIGN to
|
specifications. The generated key is used for all subsequent SIGN sessions. An empty 0x82 is used in order for SIGN to
|
||||||
|
|
|
@ -4,10 +4,13 @@ import javacard.framework.*;
|
||||||
import javacard.security.*;
|
import javacard.security.*;
|
||||||
|
|
||||||
public class WalletApplet extends Applet {
|
public class WalletApplet extends Applet {
|
||||||
|
static final byte INS_GET_STATUS = (byte) 0xF2;
|
||||||
static final byte INS_VERIFY_PIN = (byte) 0x20;
|
static final byte INS_VERIFY_PIN = (byte) 0x20;
|
||||||
static final byte INS_CHANGE_PIN = (byte) 0x21;
|
static final byte INS_CHANGE_PIN = (byte) 0x21;
|
||||||
static final byte INS_UNBLOCK_PIN = (byte) 0x22;
|
static final byte INS_UNBLOCK_PIN = (byte) 0x22;
|
||||||
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_GENERATE_MNEMONIC = (byte) 0xD2;
|
||||||
static final byte INS_SIGN = (byte) 0xC0;
|
static final byte INS_SIGN = (byte) 0xC0;
|
||||||
|
|
||||||
static final byte PUK_LENGTH = 12;
|
static final byte PUK_LENGTH = 12;
|
||||||
|
@ -18,6 +21,8 @@ public class WalletApplet extends Applet {
|
||||||
static final short EC_KEY_SIZE = 256;
|
static final short EC_KEY_SIZE = 256;
|
||||||
|
|
||||||
static final byte LOAD_KEY_P1_EC = 0x01;
|
static final byte LOAD_KEY_P1_EC = 0x01;
|
||||||
|
static final byte LOAD_KEY_P1_EXT_EC = 0x02;
|
||||||
|
static final byte LOAD_KEY_P1_SEED = 0x03;
|
||||||
|
|
||||||
static final byte SIGN_P1_DATA = 0x00;
|
static final byte SIGN_P1_DATA = 0x00;
|
||||||
static final byte SIGN_P1_PRECOMPUTED_HASH = 0x01;
|
static final byte SIGN_P1_PRECOMPUTED_HASH = 0x01;
|
||||||
|
@ -26,9 +31,22 @@ public class WalletApplet extends Applet {
|
||||||
static final byte SIGN_P2_LAST_BLOCK_MASK = (byte) 0x80;
|
static final byte SIGN_P2_LAST_BLOCK_MASK = (byte) 0x80;
|
||||||
|
|
||||||
static final byte TLV_SIGNATURE_TEMPLATE = (byte) 0xA0;
|
static final byte TLV_SIGNATURE_TEMPLATE = (byte) 0xA0;
|
||||||
|
|
||||||
static final byte TLV_KEY_TEMPLATE = (byte) 0xA1;
|
static final byte TLV_KEY_TEMPLATE = (byte) 0xA1;
|
||||||
static final byte TLV_PUB_KEY = (byte) 0x80;
|
static final byte TLV_PUB_KEY = (byte) 0x80;
|
||||||
static final byte TLV_PRIV_KEY = (byte) 0x81;
|
static final byte TLV_PRIV_KEY = (byte) 0x81;
|
||||||
|
static final byte TLV_CHAIN_CODE = (byte) 0x82;
|
||||||
|
|
||||||
|
static final byte TLV_KEY_DERIVATION_TEMPLATE = (byte) 0xA2;
|
||||||
|
static final byte TLV_DERIVATION_SEQUENCE = (byte) 0xC0;
|
||||||
|
static final byte TLV_DERIVED_PUB_KEY = (byte) 0xC1;
|
||||||
|
static final byte TLV_PARENT_PUB_KEY = (byte) 0xC2;
|
||||||
|
|
||||||
|
static final byte TLV_APPLICATION_STATUS_TEMPLATE = (byte) 0xA3;
|
||||||
|
static final byte TLV_PIN_RETRY_COUNT = (byte) 0xC0;
|
||||||
|
static final byte TLV_PUK_RETRY_COUNT = (byte) 0xC1;
|
||||||
|
static final byte TLV_KEY_INITIALIZATION_STATUS = (byte) 0xC2;
|
||||||
|
static final byte TLV_PUBLIC_KEY_DERIVATION_SUPPORTED = (byte) 0xC3;
|
||||||
|
|
||||||
private OwnerPIN pin;
|
private OwnerPIN pin;
|
||||||
private OwnerPIN puk;
|
private OwnerPIN puk;
|
||||||
|
@ -77,6 +95,9 @@ public class WalletApplet extends Applet {
|
||||||
case SecureChannel.INS_OPEN_SECURE_CHANNEL:
|
case SecureChannel.INS_OPEN_SECURE_CHANNEL:
|
||||||
secureChannel.openSecureChannel(apdu);
|
secureChannel.openSecureChannel(apdu);
|
||||||
break;
|
break;
|
||||||
|
case INS_GET_STATUS:
|
||||||
|
getStatus(apdu);
|
||||||
|
break;
|
||||||
case INS_VERIFY_PIN:
|
case INS_VERIFY_PIN:
|
||||||
verifyPIN(apdu);
|
verifyPIN(apdu);
|
||||||
break;
|
break;
|
||||||
|
@ -89,6 +110,12 @@ public class WalletApplet extends Applet {
|
||||||
case INS_LOAD_KEY:
|
case INS_LOAD_KEY:
|
||||||
loadKey(apdu);
|
loadKey(apdu);
|
||||||
break;
|
break;
|
||||||
|
case INS_DERIVE_KEY:
|
||||||
|
deriveKey(apdu);
|
||||||
|
break;
|
||||||
|
case INS_GENERATE_MNEMONIC:
|
||||||
|
generateMnemonic(apdu);
|
||||||
|
break;
|
||||||
case INS_SIGN:
|
case INS_SIGN:
|
||||||
sign(apdu);
|
sign(apdu);
|
||||||
break;
|
break;
|
||||||
|
@ -108,6 +135,33 @@ public class WalletApplet extends Applet {
|
||||||
apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, keyLength);
|
apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, keyLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void getStatus(APDU apdu) {
|
||||||
|
if (!secureChannel.isOpen()) {
|
||||||
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||||
|
}
|
||||||
|
|
||||||
|
short off = SecureChannel.SC_OUT_OFFSET;
|
||||||
|
byte[] apduBuffer = apdu.getBuffer();
|
||||||
|
|
||||||
|
apduBuffer[off++] = TLV_APPLICATION_STATUS_TEMPLATE;
|
||||||
|
apduBuffer[off++] = 12;
|
||||||
|
apduBuffer[off++] = TLV_PIN_RETRY_COUNT;
|
||||||
|
apduBuffer[off++] = 1;
|
||||||
|
apduBuffer[off++] = pin.getTriesRemaining();
|
||||||
|
apduBuffer[off++] = TLV_PUK_RETRY_COUNT;
|
||||||
|
apduBuffer[off++] = 1;
|
||||||
|
apduBuffer[off++] = puk.getTriesRemaining();
|
||||||
|
apduBuffer[off++] = TLV_KEY_INITIALIZATION_STATUS;
|
||||||
|
apduBuffer[off++] = 1;
|
||||||
|
apduBuffer[off++] = privateKey.isInitialized() ? (byte) 0x01 : (byte) 0x00;
|
||||||
|
apduBuffer[off++] = TLV_PUBLIC_KEY_DERIVATION_SUPPORTED;
|
||||||
|
apduBuffer[off++] = 1;
|
||||||
|
apduBuffer[off++] = 1; //TODO: actually check if it is supported or totally remove if a fallback software implementation is a requirement
|
||||||
|
|
||||||
|
short len = secureChannel.encryptAPDU(apduBuffer, (short) (off - SecureChannel.SC_OUT_OFFSET));
|
||||||
|
apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, len);
|
||||||
|
}
|
||||||
|
|
||||||
private void verifyPIN(APDU apdu) {
|
private void verifyPIN(APDU apdu) {
|
||||||
apdu.setIncomingAndReceive();
|
apdu.setIncomingAndReceive();
|
||||||
|
|
||||||
|
@ -199,6 +253,14 @@ public class WalletApplet extends Applet {
|
||||||
signInProgress = false;
|
signInProgress = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void deriveKey(APDU apdu) {
|
||||||
|
ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateMnemonic(APDU apdu) {
|
||||||
|
ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED);
|
||||||
|
}
|
||||||
|
|
||||||
private void sign(APDU apdu) {
|
private void sign(APDU apdu) {
|
||||||
apdu.setIncomingAndReceive();
|
apdu.setIncomingAndReceive();
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,11 @@ public class WalletAppletCommandSet {
|
||||||
return secureChannel.openSecureChannel(apduChannel);
|
return secureChannel.openSecureChannel(apduChannel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ResponseAPDU getStatus() throws CardException {
|
||||||
|
CommandAPDU getStatus = new CommandAPDU(0x80, WalletApplet.INS_GET_STATUS, 0, 0);
|
||||||
|
return apduChannel.transmit(getStatus);
|
||||||
|
}
|
||||||
|
|
||||||
public ResponseAPDU verifyPIN(String pin) throws CardException {
|
public ResponseAPDU verifyPIN(String pin) throws CardException {
|
||||||
CommandAPDU verifyPIN = new CommandAPDU(0x80, WalletApplet.INS_VERIFY_PIN, 0, 0, secureChannel.encryptAPDU(pin.getBytes()));
|
CommandAPDU verifyPIN = new CommandAPDU(0x80, WalletApplet.INS_VERIFY_PIN, 0, 0, secureChannel.encryptAPDU(pin.getBytes()));
|
||||||
return apduChannel.transmit(verifyPIN);
|
return apduChannel.transmit(verifyPIN);
|
||||||
|
|
|
@ -12,7 +12,6 @@ import org.web3j.crypto.*;
|
||||||
import org.web3j.protocol.Web3j;
|
import org.web3j.protocol.Web3j;
|
||||||
import org.web3j.protocol.core.DefaultBlockParameterName;
|
import org.web3j.protocol.core.DefaultBlockParameterName;
|
||||||
import org.web3j.protocol.core.methods.request.RawTransaction;
|
import org.web3j.protocol.core.methods.request.RawTransaction;
|
||||||
import org.web3j.protocol.core.methods.response.EthGetTransactionReceipt;
|
|
||||||
import org.web3j.protocol.core.methods.response.EthSendTransaction;
|
import org.web3j.protocol.core.methods.response.EthSendTransaction;
|
||||||
import org.web3j.protocol.http.HttpService;
|
import org.web3j.protocol.http.HttpService;
|
||||||
import org.web3j.tx.Transfer;
|
import org.web3j.tx.Transfer;
|
||||||
|
@ -106,6 +105,36 @@ public class WalletAppletTest {
|
||||||
assertEquals(SecureChannel.SC_SECRET_LENGTH, response.getData().length);
|
assertEquals(SecureChannel.SC_SECRET_LENGTH, response.getData().length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("GET STATUS command")
|
||||||
|
void getStatusTest() throws CardException {
|
||||||
|
// Security condition violation: SecureChannel not open
|
||||||
|
ResponseAPDU response = cmdSet.getStatus();
|
||||||
|
assertEquals(0x6985, response.getSW());
|
||||||
|
cmdSet.openSecureChannel();
|
||||||
|
|
||||||
|
// Good case. Since the order of test execution is undefined, the test cannot know if the keys are initialized or not.
|
||||||
|
// Additionally, the public key derivation cannot also be known here.
|
||||||
|
response = cmdSet.getStatus();
|
||||||
|
assertEquals(0x9000, response.getSW());
|
||||||
|
byte[] data = secureChannel.decryptAPDU(response.getData());
|
||||||
|
assertTrue(Hex.toHexString(data).matches("a30cc00103c10105c2010[0-1]c3010[0-1]"));
|
||||||
|
|
||||||
|
response = cmdSet.verifyPIN("123456");
|
||||||
|
assertEquals(0x63C2, response.getSW());
|
||||||
|
response = cmdSet.getStatus();
|
||||||
|
assertEquals(0x9000, response.getSW());
|
||||||
|
data = secureChannel.decryptAPDU(response.getData());
|
||||||
|
assertTrue(Hex.toHexString(data).matches("a30cc00102c10105c2010[0-1]c3010[0-1]"));
|
||||||
|
|
||||||
|
response = cmdSet.verifyPIN("000000");
|
||||||
|
assertEquals(0x9000, response.getSW());
|
||||||
|
response = cmdSet.getStatus();
|
||||||
|
assertEquals(0x9000, response.getSW());
|
||||||
|
data = secureChannel.decryptAPDU(response.getData());
|
||||||
|
assertTrue(Hex.toHexString(data).matches("a30cc00103c10105c2010[0-1]c3010[0-1]"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("VERIFY PIN command")
|
@DisplayName("VERIFY PIN command")
|
||||||
void verifyPinTest() throws CardException {
|
void verifyPinTest() throws CardException {
|
||||||
|
@ -442,8 +471,6 @@ public class WalletAppletTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
assertFalse(ethSendTransaction.hasError());
|
assertFalse(ethSendTransaction.hasError());
|
||||||
|
|
||||||
EthGetTransactionReceipt receipt = web3j.ethGetTransactionReceipt(ethSendTransaction.getTransactionHash()).send();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private KeyPairGenerator keypairGenerator() throws Exception {
|
private KeyPairGenerator keypairGenerator() throws Exception {
|
||||||
|
@ -481,7 +508,7 @@ public class WalletAppletTest {
|
||||||
cmdSet.openSecureChannel();
|
cmdSet.openSecureChannel();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Sign.SignatureData signMessage(byte[] message) throws Exception {
|
private Sign.SignatureData signMessage(byte[] message) throws Exception {
|
||||||
byte[] messageHash = Hash.sha3(message);
|
byte[] messageHash = Hash.sha3(message);
|
||||||
|
|
||||||
ResponseAPDU response = cmdSet.sign(messageHash, WalletApplet.SIGN_P1_PRECOMPUTED_HASH,true, true);
|
ResponseAPDU response = cmdSet.sign(messageHash, WalletApplet.SIGN_P1_PRECOMPUTED_HASH,true, true);
|
||||||
|
|
Loading…
Reference in New Issue