Add the MUTUALLY AUTHENTICATE command
This commit is contained in:
parent
09fe778d85
commit
60f18b7afd
|
@ -64,6 +64,10 @@ which must 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).
|
||||||
|
|
||||||
|
### MUTUALLY AUTHENTICATE
|
||||||
|
|
||||||
|
The MUTUALLY AUTHENTICATE command is as specified in the [SECURE_CHANNEL.MD](SECURE_CHANNEL.MD).
|
||||||
|
|
||||||
### PAIR
|
### PAIR
|
||||||
|
|
||||||
The PAIR command is as specified in the [SECURE_CHANNEL.MD](SECURE_CHANNEL.MD). The shared secret is the SHA-256 of the
|
The PAIR command is as specified in the [SECURE_CHANNEL.MD](SECURE_CHANNEL.MD). The shared secret is the SHA-256 of the
|
||||||
|
|
|
@ -15,6 +15,8 @@ A short description of establishing a session is as follows
|
||||||
generate a shared 256-bit secret (more details below).
|
generate a shared 256-bit secret (more details below).
|
||||||
3. The generated secret is used as an AES key to encrypt all further communication. CBC mode is used with a random IV
|
3. The generated secret is used as an AES key to encrypt all further communication. CBC mode is used with a random IV
|
||||||
generated for each APDU and prepended to the APDU payload. Both command and responses are encrypted.
|
generated for each APDU and prepended to the APDU payload. Both command and responses are encrypted.
|
||||||
|
4. The client sends a MUTUALLY AUTHENTICATE command to verify that the keys are matching and thus the secure channel is
|
||||||
|
successfully established.
|
||||||
|
|
||||||
The EC keyset used by the card for the EC-DH algorithm is generated on-card on applet installation and is not used
|
The EC keyset used by the card for the EC-DH algorithm is generated on-card on applet installation and is not used
|
||||||
for anything else. The EC keyset used by the client is generated every time a new secure channel session must be
|
for anything else. The EC keyset used by the client is generated every time a new secure channel session must be
|
||||||
|
@ -32,7 +34,7 @@ opened.
|
||||||
* Response Data = A 256-bit salt
|
* Response Data = A 256-bit salt
|
||||||
* Response SW = 0x9000 on success, 0x6A86 if P1 is invalid
|
* Response SW = 0x9000 on success, 0x6A86 if P1 is invalid
|
||||||
|
|
||||||
This APDU is sent to establish a Secure Channel session. A session is aborted when the application is deselected,
|
This APDU is the first step to establish a Secure Channel session. A session is aborted when the application is deselected,
|
||||||
either directly or because of a card reset/tear. This APDU and its response are not encrypted.
|
either directly or because of a card reset/tear. This APDU and its response are not encrypted.
|
||||||
|
|
||||||
The card generates a random 256-bit salt which is sent to the client. Both the client and the card do the following
|
The card generates a random 256-bit salt which is sent to the client. Both the client and the card do the following
|
||||||
|
@ -43,12 +45,28 @@ for key derivation
|
||||||
calculated.
|
calculated.
|
||||||
3. The output of the SHA-256 algorithm is used as the AES key for further communication.
|
3. The output of the SHA-256 algorithm is used as the AES key for further communication.
|
||||||
|
|
||||||
TODO: define a second step where client and card mutually verify that they have the same keys and thus are authenticated
|
### MUTUALLY AUTHENTICATE
|
||||||
|
|
||||||
|
* CLA = 0x80
|
||||||
|
* INS = 0x11
|
||||||
|
* P1 = 0x00
|
||||||
|
* P2 = 0x00
|
||||||
|
* Data = 256-bit random number and its SHA-256 hash
|
||||||
|
* Response Data = 256-bit random number and its SHA-256 hash
|
||||||
|
* Response SW = 0x9000 on success, 0x6985 if the previous successfully executed APDU was not OPEN SECURE CHANNEL, 0x6982
|
||||||
|
if authentication failed, 0x6A80 if the data is not exactly 64 bytes long
|
||||||
|
|
||||||
|
This APDU allows both parties to verify that the keys generated in the OPEN SECURE CHANNEL step are matching and thus
|
||||||
|
guarantee authentication of the counterpart. The data sent by both parties is a 32-bit random number followed by its own
|
||||||
|
SHA-256 hash. The APDU data is sent encrypted with the keys generated in the OPEN SECURE CHANNEL step. Each party must
|
||||||
|
verify that the hash indeed matches the value sent. If this is true, then the decryption was correct, meaning that the
|
||||||
|
keys are matching. Only after this step has been executed the secure channel can be considered to be open and other
|
||||||
|
commands can be sent.
|
||||||
|
|
||||||
### PAIR
|
### PAIR
|
||||||
|
|
||||||
* CLA = 0x80
|
* CLA = 0x80
|
||||||
* INS = 0x11
|
* INS = 0x12
|
||||||
* P1 = pairing phase
|
* P1 = pairing phase
|
||||||
* P2 = 0x00
|
* P2 = 0x00
|
||||||
* Data = see below
|
* Data = see below
|
||||||
|
@ -89,7 +107,7 @@ happens depend on the specific applet.
|
||||||
### UNPAIR
|
### UNPAIR
|
||||||
|
|
||||||
* CLA = 0x80
|
* CLA = 0x80
|
||||||
* INS = 0x12
|
* INS = 0x13
|
||||||
* P1 = the index to unpair
|
* P1 = the index to unpair
|
||||||
* P2 = 0x00
|
* P2 = 0x00
|
||||||
* Data = the same index as in P1
|
* Data = the same index as in P1
|
||||||
|
|
|
@ -15,8 +15,9 @@ public class SecureChannel {
|
||||||
public static final short SC_OUT_OFFSET = ISO7816.OFFSET_CDATA + (SC_BLOCK_SIZE * 2);
|
public static final short SC_OUT_OFFSET = ISO7816.OFFSET_CDATA + (SC_BLOCK_SIZE * 2);
|
||||||
|
|
||||||
public static final byte INS_OPEN_SECURE_CHANNEL = 0x10;
|
public static final byte INS_OPEN_SECURE_CHANNEL = 0x10;
|
||||||
public static final byte INS_PAIR = 0x11;
|
public static final byte INS_MUTUALLY_AUTHENTICATE = 0x11;
|
||||||
public static final byte INS_UNPAIR = 0x12;
|
public static final byte INS_PAIR = 0x12;
|
||||||
|
public static final byte INS_UNPAIR = 0x13;
|
||||||
|
|
||||||
public static final byte PAIR_P1_FIRST_STEP = 0x00;
|
public static final byte PAIR_P1_FIRST_STEP = 0x00;
|
||||||
public static final byte PAIR_P1_LAST_STEP = 0x01;
|
public static final byte PAIR_P1_LAST_STEP = 0x01;
|
||||||
|
@ -34,6 +35,7 @@ public class SecureChannel {
|
||||||
private byte[] pairingKeys;
|
private byte[] pairingKeys;
|
||||||
|
|
||||||
private short preassignedPairingOffset = -1;
|
private short preassignedPairingOffset = -1;
|
||||||
|
private boolean mutuallyAuthenticated = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates a Secure Channel. All memory allocations needed for the secure channel are performed here. The keypair
|
* Instantiates a Secure Channel. All memory allocations needed for the secure channel are performed here. The keypair
|
||||||
|
@ -64,6 +66,7 @@ public class SecureChannel {
|
||||||
*/
|
*/
|
||||||
public void openSecureChannel(APDU apdu) {
|
public void openSecureChannel(APDU apdu) {
|
||||||
preassignedPairingOffset = -1;
|
preassignedPairingOffset = -1;
|
||||||
|
mutuallyAuthenticated = false;
|
||||||
|
|
||||||
apdu.setIncomingAndReceive();
|
apdu.setIncomingAndReceive();
|
||||||
byte[] apduBuffer = apdu.getBuffer();
|
byte[] apduBuffer = apdu.getBuffer();
|
||||||
|
@ -86,6 +89,39 @@ public class SecureChannel {
|
||||||
apdu.setOutgoingAndSend((short) 0, SC_SECRET_LENGTH);
|
apdu.setOutgoingAndSend((short) 0, SC_SECRET_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes the MUTUALLY AUTHENTICATE command.
|
||||||
|
*
|
||||||
|
* @param apdu the JCRE-owned APDU object.
|
||||||
|
*/
|
||||||
|
public void mutuallyAuthenticate(APDU apdu) {
|
||||||
|
if (!scKey.isInitialized() || mutuallyAuthenticated) {
|
||||||
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||||
|
}
|
||||||
|
|
||||||
|
apdu.setIncomingAndReceive();
|
||||||
|
byte[] apduBuffer = apdu.getBuffer();
|
||||||
|
|
||||||
|
short len = decryptAPDU(apduBuffer);
|
||||||
|
|
||||||
|
if (len != (short) (SC_SECRET_LENGTH * 2)) {
|
||||||
|
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
Crypto.sha256.doFinal(apduBuffer, ISO7816.OFFSET_CDATA, SC_SECRET_LENGTH, apduBuffer, ISO7816.OFFSET_CDATA);
|
||||||
|
|
||||||
|
if (Util.arrayCompare(apduBuffer, ISO7816.OFFSET_CDATA, apduBuffer, (short) (ISO7816.OFFSET_CDATA + SC_SECRET_LENGTH), SC_SECRET_LENGTH) != 0) {
|
||||||
|
ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutuallyAuthenticated = true;
|
||||||
|
|
||||||
|
Crypto.random.generateData(apduBuffer, SC_OUT_OFFSET, SC_SECRET_LENGTH);
|
||||||
|
Crypto.sha256.doFinal(apduBuffer, SC_OUT_OFFSET, SC_SECRET_LENGTH, apduBuffer, (short) (SC_OUT_OFFSET + SC_SECRET_LENGTH));
|
||||||
|
len = encryptAPDU(apduBuffer, len);
|
||||||
|
apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, len);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes the PAIR command.
|
* Processes the PAIR command.
|
||||||
*
|
*
|
||||||
|
@ -243,7 +279,7 @@ public class SecureChannel {
|
||||||
* @return whether a secure channel is currently established or not.
|
* @return whether a secure channel is currently established or not.
|
||||||
*/
|
*/
|
||||||
public boolean isOpen() {
|
public boolean isOpen() {
|
||||||
return scKey.isInitialized();
|
return scKey.isInitialized() && mutuallyAuthenticated;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -185,6 +185,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 SecureChannel.INS_MUTUALLY_AUTHENTICATE:
|
||||||
|
secureChannel.mutuallyAuthenticate(apdu);
|
||||||
|
break;
|
||||||
case SecureChannel.INS_PAIR:
|
case SecureChannel.INS_PAIR:
|
||||||
secureChannel.pair(apdu);
|
secureChannel.pair(apdu);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -71,13 +71,18 @@ public class SecureChannelSession {
|
||||||
* @return the card response
|
* @return the card response
|
||||||
* @throws CardException communication error
|
* @throws CardException communication error
|
||||||
*/
|
*/
|
||||||
public ResponseAPDU openSecureChannel(CardChannel apduChannel) throws CardException {
|
public void autoOpenSecureChannel(CardChannel apduChannel) throws CardException {
|
||||||
CommandAPDU openSecureChannel = new CommandAPDU(0x80, SecureChannel.INS_OPEN_SECURE_CHANNEL, pairingIndex, 0, publicKey);
|
ResponseAPDU response = openSecureChannel(apduChannel, pairingIndex, publicKey);
|
||||||
ResponseAPDU response = apduChannel.transmit(openSecureChannel);
|
|
||||||
byte[] salt = response.getData();
|
byte[] salt = response.getData();
|
||||||
|
|
||||||
|
if (response.getSW() != 0x9000) {
|
||||||
|
throw new CardException("OPEN SECURE CHANNEL failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageDigest md;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
MessageDigest md = MessageDigest.getInstance("SHA256", "BC");
|
md = MessageDigest.getInstance("SHA256", "BC");
|
||||||
md.update(secret);
|
md.update(secret);
|
||||||
md.update(pairingKey);
|
md.update(pairingKey);
|
||||||
sessionKey = new SecretKeySpec(md.digest(salt), "AES");
|
sessionKey = new SecretKeySpec(md.digest(salt), "AES");
|
||||||
|
@ -86,7 +91,24 @@ public class SecureChannelSession {
|
||||||
throw new RuntimeException("Is BouncyCastle in the classpath?", e);
|
throw new RuntimeException("Is BouncyCastle in the classpath?", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
random.nextBytes(salt);
|
||||||
|
byte[] digest = md.digest(salt);
|
||||||
|
salt = Arrays.copyOf(salt, SecureChannel.SC_SECRET_LENGTH * 2);
|
||||||
|
System.arraycopy(digest, 0, salt, SecureChannel.SC_SECRET_LENGTH, SecureChannel.SC_SECRET_LENGTH);
|
||||||
|
response = mutuallyAuthenticate(apduChannel, salt);
|
||||||
|
salt = decryptAPDU(response.getData());
|
||||||
|
|
||||||
|
if (response.getSW() != 0x9000) {
|
||||||
|
throw new CardException("MUTUALLY AUTHENTICATE failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
md.update(salt, 0, SecureChannel.SC_SECRET_LENGTH);
|
||||||
|
digest = md.digest();
|
||||||
|
salt = Arrays.copyOfRange(salt, SecureChannel.SC_SECRET_LENGTH, salt.length);
|
||||||
|
|
||||||
|
if (!Arrays.equals(salt, digest)) {
|
||||||
|
throw new CardException("Invalid authentication data from the card");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -153,6 +175,33 @@ public class SecureChannelSession {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a OPEN SECURE CHANNEL APDU.
|
||||||
|
*
|
||||||
|
* @param apduChannel the apdu channel
|
||||||
|
* @param index the P1 parameter
|
||||||
|
* @param data the data
|
||||||
|
* @return the raw card response
|
||||||
|
* @throws CardException communication error
|
||||||
|
*/
|
||||||
|
public ResponseAPDU openSecureChannel(CardChannel apduChannel, byte index, byte[] data) throws CardException {
|
||||||
|
CommandAPDU openSecureChannel = new CommandAPDU(0x80, SecureChannel.INS_OPEN_SECURE_CHANNEL, index, 0, data);
|
||||||
|
return apduChannel.transmit(openSecureChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a MUTUALLY AUTHENTICATE APDU.
|
||||||
|
*
|
||||||
|
* @param apduChannel the apdu channel
|
||||||
|
* @param data the data
|
||||||
|
* @return the raw card response
|
||||||
|
* @throws CardException communication error
|
||||||
|
*/
|
||||||
|
public ResponseAPDU mutuallyAuthenticate(CardChannel apduChannel, byte[] data) throws CardException {
|
||||||
|
CommandAPDU mutuallyAuthenticate = new CommandAPDU(0x80, SecureChannel.INS_MUTUALLY_AUTHENTICATE, 0, 0, encryptAPDU(data));
|
||||||
|
return apduChannel.transmit(mutuallyAuthenticate);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a PAIR APDU.
|
* Sends a PAIR APDU.
|
||||||
*
|
*
|
||||||
|
|
|
@ -51,8 +51,8 @@ public class WalletAppletCommandSet {
|
||||||
* @return the raw card response
|
* @return the raw card response
|
||||||
* @throws CardException communication error
|
* @throws CardException communication error
|
||||||
*/
|
*/
|
||||||
public ResponseAPDU openSecureChannel() throws CardException {
|
public void autoOpenSecureChannel() throws CardException {
|
||||||
return secureChannel.openSecureChannel(apduChannel);
|
secureChannel.autoOpenSecureChannel(apduChannel);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -73,6 +73,20 @@ public class WalletAppletCommandSet {
|
||||||
secureChannel.autoUnpair(apduChannel);
|
secureChannel.autoUnpair(apduChannel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a OPEN SECURE CHANNEL APDU. Calls the corresponding method of the SecureChannel class.
|
||||||
|
*/
|
||||||
|
public ResponseAPDU openSecureChannel(byte index, byte[] data) throws CardException {
|
||||||
|
return secureChannel.openSecureChannel(apduChannel, index, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a MUTUALLY AUTHENTICATE APDU. Calls the corresponding method of the SecureChannel class.
|
||||||
|
*/
|
||||||
|
public ResponseAPDU mutuallyAuthenticate(byte[] data) throws CardException {
|
||||||
|
return secureChannel.mutuallyAuthenticate(apduChannel, data);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a PAIR APDU. Calls the corresponding method of the SecureChannel class.
|
* Sends a PAIR APDU. Calls the corresponding method of the SecureChannel class.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -115,9 +115,7 @@ public class WalletAppletTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("OPEN SECURE CHANNEL command")
|
@DisplayName("OPEN SECURE CHANNEL command")
|
||||||
void openSecureChannelTest() throws CardException {
|
void openSecureChannelTest() throws CardException {
|
||||||
ResponseAPDU response = cmdSet.openSecureChannel();
|
cmdSet.autoOpenSecureChannel();
|
||||||
assertEquals(0x9000, response.getSW());
|
|
||||||
assertEquals(SecureChannel.SC_SECRET_LENGTH, response.getData().length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -126,7 +124,7 @@ public class WalletAppletTest {
|
||||||
// Security condition violation: SecureChannel not open
|
// Security condition violation: SecureChannel not open
|
||||||
ResponseAPDU response = cmdSet.getStatus(WalletApplet.GET_STATUS_P1_APPLICATION);
|
ResponseAPDU response = cmdSet.getStatus(WalletApplet.GET_STATUS_P1_APPLICATION);
|
||||||
assertEquals(0x6985, response.getSW());
|
assertEquals(0x6985, response.getSW());
|
||||||
cmdSet.openSecureChannel();
|
cmdSet.autoOpenSecureChannel();
|
||||||
|
|
||||||
// Good case. Since the order of test execution is undefined, the test cannot know if the keys are initialized or not.
|
// Good case. Since the order of test execution is undefined, the test cannot know if the keys are initialized or not.
|
||||||
// Additionally, support for public key derivation is hw dependent.
|
// Additionally, support for public key derivation is hw dependent.
|
||||||
|
@ -163,7 +161,7 @@ public class WalletAppletTest {
|
||||||
ResponseAPDU response = cmdSet.verifyPIN("000000");
|
ResponseAPDU response = cmdSet.verifyPIN("000000");
|
||||||
assertEquals(0x6985, response.getSW());
|
assertEquals(0x6985, response.getSW());
|
||||||
|
|
||||||
cmdSet.openSecureChannel();
|
cmdSet.autoOpenSecureChannel();
|
||||||
|
|
||||||
// Wrong PIN
|
// Wrong PIN
|
||||||
response = cmdSet.verifyPIN("123456");
|
response = cmdSet.verifyPIN("123456");
|
||||||
|
@ -198,7 +196,7 @@ public class WalletAppletTest {
|
||||||
ResponseAPDU response = cmdSet.changePIN("123456");
|
ResponseAPDU response = cmdSet.changePIN("123456");
|
||||||
assertEquals(0x6985, response.getSW());
|
assertEquals(0x6985, response.getSW());
|
||||||
|
|
||||||
cmdSet.openSecureChannel();
|
cmdSet.autoOpenSecureChannel();
|
||||||
|
|
||||||
// Security condition violation: PIN n ot verified
|
// Security condition violation: PIN n ot verified
|
||||||
response = cmdSet.changePIN("123456");
|
response = cmdSet.changePIN("123456");
|
||||||
|
@ -242,7 +240,7 @@ public class WalletAppletTest {
|
||||||
ResponseAPDU response = cmdSet.unblockPIN("123456789012", "000000");
|
ResponseAPDU response = cmdSet.unblockPIN("123456789012", "000000");
|
||||||
assertEquals(0x6985, response.getSW());
|
assertEquals(0x6985, response.getSW());
|
||||||
|
|
||||||
cmdSet.openSecureChannel();
|
cmdSet.autoOpenSecureChannel();
|
||||||
|
|
||||||
// Condition violation: PIN is not blocked
|
// Condition violation: PIN is not blocked
|
||||||
response = cmdSet.unblockPIN("123456789012", "000000");
|
response = cmdSet.unblockPIN("123456789012", "000000");
|
||||||
|
@ -294,7 +292,7 @@ public class WalletAppletTest {
|
||||||
ResponseAPDU response = cmdSet.loadKey(keyPair);
|
ResponseAPDU response = cmdSet.loadKey(keyPair);
|
||||||
assertEquals(0x6985, response.getSW());
|
assertEquals(0x6985, response.getSW());
|
||||||
|
|
||||||
cmdSet.openSecureChannel();
|
cmdSet.autoOpenSecureChannel();
|
||||||
|
|
||||||
int publicKeyDerivationSW = cmdSet.getPublicKeyDerivationSupport() ? 0x9000 : 0x6a81;
|
int publicKeyDerivationSW = cmdSet.getPublicKeyDerivationSupport() ? 0x9000 : 0x6a81;
|
||||||
|
|
||||||
|
@ -351,7 +349,7 @@ public class WalletAppletTest {
|
||||||
// Security condition violation: SecureChannel not open
|
// Security condition violation: SecureChannel not open
|
||||||
ResponseAPDU response = cmdSet.generateMnemonic(4);
|
ResponseAPDU response = cmdSet.generateMnemonic(4);
|
||||||
assertEquals(0x6985, response.getSW());
|
assertEquals(0x6985, response.getSW());
|
||||||
cmdSet.openSecureChannel();
|
cmdSet.autoOpenSecureChannel();
|
||||||
|
|
||||||
// Wrong P1 (too short, too long)
|
// Wrong P1 (too short, too long)
|
||||||
response = cmdSet.generateMnemonic(3);
|
response = cmdSet.generateMnemonic(3);
|
||||||
|
@ -389,7 +387,7 @@ public class WalletAppletTest {
|
||||||
ResponseAPDU response = cmdSet.deriveKey(new byte[] {0x00, 0x00, 0x00, 0x00});
|
ResponseAPDU response = cmdSet.deriveKey(new byte[] {0x00, 0x00, 0x00, 0x00});
|
||||||
assertEquals(0x6985, response.getSW());
|
assertEquals(0x6985, response.getSW());
|
||||||
|
|
||||||
cmdSet.openSecureChannel();
|
cmdSet.autoOpenSecureChannel();
|
||||||
boolean autonomousDerivation = cmdSet.getPublicKeyDerivationSupport();
|
boolean autonomousDerivation = cmdSet.getPublicKeyDerivationSupport();
|
||||||
|
|
||||||
// Security condition violation: PIN is not verified
|
// Security condition violation: PIN is not verified
|
||||||
|
@ -509,7 +507,7 @@ public class WalletAppletTest {
|
||||||
ResponseAPDU response = cmdSet.sign(hash, WalletApplet.SIGN_P1_PRECOMPUTED_HASH,true, true);
|
ResponseAPDU response = cmdSet.sign(hash, WalletApplet.SIGN_P1_PRECOMPUTED_HASH,true, true);
|
||||||
assertEquals(0x6985, response.getSW());
|
assertEquals(0x6985, response.getSW());
|
||||||
|
|
||||||
cmdSet.openSecureChannel();
|
cmdSet.autoOpenSecureChannel();
|
||||||
|
|
||||||
// Security condition violation: PIN not verified
|
// Security condition violation: PIN not verified
|
||||||
response = cmdSet.sign(hash, WalletApplet.SIGN_P1_PRECOMPUTED_HASH,true,true);
|
response = cmdSet.sign(hash, WalletApplet.SIGN_P1_PRECOMPUTED_HASH,true,true);
|
||||||
|
@ -558,7 +556,7 @@ public class WalletAppletTest {
|
||||||
ResponseAPDU response = cmdSet.setPinlessPath(new byte[] {0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02});
|
ResponseAPDU response = cmdSet.setPinlessPath(new byte[] {0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02});
|
||||||
assertEquals(0x6985, response.getSW());
|
assertEquals(0x6985, response.getSW());
|
||||||
|
|
||||||
cmdSet.openSecureChannel();
|
cmdSet.autoOpenSecureChannel();
|
||||||
|
|
||||||
// Security condition violation: PIN not verified
|
// Security condition violation: PIN not verified
|
||||||
response = cmdSet.setPinlessPath(new byte[] {0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02});
|
response = cmdSet.setPinlessPath(new byte[] {0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02});
|
||||||
|
@ -644,7 +642,7 @@ public class WalletAppletTest {
|
||||||
ResponseAPDU response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_WHISPER);
|
ResponseAPDU response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_WHISPER);
|
||||||
assertEquals(0x6985, response.getSW());
|
assertEquals(0x6985, response.getSW());
|
||||||
|
|
||||||
cmdSet.openSecureChannel();
|
cmdSet.autoOpenSecureChannel();
|
||||||
|
|
||||||
// Security condition violation: PIN not verified
|
// Security condition violation: PIN not verified
|
||||||
response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_WHISPER);
|
response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_WHISPER);
|
||||||
|
@ -698,7 +696,7 @@ public class WalletAppletTest {
|
||||||
byte[] smallData = Arrays.copyOf(data, 20);
|
byte[] smallData = Arrays.copyOf(data, 20);
|
||||||
r.nextBytes(data);
|
r.nextBytes(data);
|
||||||
|
|
||||||
cmdSet.openSecureChannel();
|
cmdSet.autoOpenSecureChannel();
|
||||||
|
|
||||||
ResponseAPDU response = cmdSet.verifyPIN("000000");
|
ResponseAPDU response = cmdSet.verifyPIN("000000");
|
||||||
assertEquals(0x9000, response.getSW());
|
assertEquals(0x9000, response.getSW());
|
||||||
|
@ -798,7 +796,7 @@ public class WalletAppletTest {
|
||||||
Credentials wallet2 = WalletUtils.loadCredentials("testwallet", "testwallets/wallet2.json");
|
Credentials wallet2 = WalletUtils.loadCredentials("testwallet", "testwallets/wallet2.json");
|
||||||
|
|
||||||
// Load keys on card
|
// Load keys on card
|
||||||
cmdSet.openSecureChannel();
|
cmdSet.autoOpenSecureChannel();
|
||||||
ResponseAPDU response = cmdSet.verifyPIN("000000");
|
ResponseAPDU response = cmdSet.verifyPIN("000000");
|
||||||
assertEquals(0x9000, response.getSW());
|
assertEquals(0x9000, response.getSW());
|
||||||
response = cmdSet.loadKey(wallet1.getEcKeyPair());
|
response = cmdSet.loadKey(wallet1.getEcKeyPair());
|
||||||
|
@ -874,7 +872,7 @@ public class WalletAppletTest {
|
||||||
private void resetAndSelectAndOpenSC() throws CardException {
|
private void resetAndSelectAndOpenSC() throws CardException {
|
||||||
reset();
|
reset();
|
||||||
cmdSet.select();
|
cmdSet.select();
|
||||||
cmdSet.openSecureChannel();
|
cmdSet.autoOpenSecureChannel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertMnemonic(int expectedLength, byte[] data) {
|
private void assertMnemonic(int expectedLength, byte[] data) {
|
||||||
|
|
Loading…
Reference in New Issue