fix situation where the card would get stuck in waiting for public key status

This commit is contained in:
Michele Balistreri 2017-10-25 12:25:08 +03:00
parent 24352fb0bc
commit c062e53a6e
3 changed files with 55 additions and 26 deletions

View File

@ -279,6 +279,7 @@ public class WalletApplet extends Applet {
}
signInProgress = false;
expectPublicKey = false;
}
private void loadKeyPair(byte[] apduBuffer, boolean newExtended) {
@ -500,7 +501,7 @@ public class WalletApplet extends Applet {
private void sign(APDU apdu) {
apdu.setIncomingAndReceive();
if (!(secureChannel.isOpen() && pin.isValidated() && privateKey.isInitialized())) {
if (!(secureChannel.isOpen() && pin.isValidated() && privateKey.isInitialized() && !expectPublicKey)) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}

View File

@ -42,6 +42,12 @@ public class WalletAppletCommandSet {
return apduChannel.transmit(getStatus);
}
public boolean getPublicKeyDerivationSupport() throws CardException {
ResponseAPDU resp = getStatus();
byte[] data = secureChannel.decryptAPDU(resp.getData());
return data[data.length - 1] == 1;
}
public ResponseAPDU verifyPIN(String pin) throws CardException {
CommandAPDU verifyPIN = new CommandAPDU(0x80, WalletApplet.INS_VERIFY_PIN, 0, 0, secureChannel.encryptAPDU(pin.getBytes()));
return apduChannel.transmit(verifyPIN);

View File

@ -282,6 +282,8 @@ public class WalletAppletTest {
cmdSet.openSecureChannel();
int publicKeyDerivationSW = cmdSet.getPublicKeyDerivationSupport() ? 0x9000 : 0x6a81;
// Security condition violation: PIN not verified
response = cmdSet.loadKey(keyPair);
assertEquals(0x6985, response.getSW());
@ -320,13 +322,13 @@ public class WalletAppletTest {
// Check omitted public key
response = cmdSet.loadKey(keyPair, true, null);
assertEquals(0x9000, response.getSW());
assertEquals(publicKeyDerivationSW, response.getSW());
response = cmdSet.loadKey(keyPair, true, chainCode);
assertEquals(0x9000, response.getSW());
assertEquals(publicKeyDerivationSW, response.getSW());
// Check seed load
response = cmdSet.loadKey(keyPair.getPrivate(), chainCode);
assertEquals(0x9000, response.getSW());
assertEquals(publicKeyDerivationSW, response.getSW());
}
@Test
@ -374,6 +376,7 @@ public class WalletAppletTest {
assertEquals(0x6985, response.getSW());
cmdSet.openSecureChannel();
boolean autonomousDerivation = cmdSet.getPublicKeyDerivationSupport();
// Security condition violation: PIN is not verified
response = cmdSet.deriveKey(new byte[] {0x00, 0x00, 0x00, 0x00});
@ -410,15 +413,17 @@ public class WalletAppletTest {
response = cmdSet.deriveKey(new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, true, true, false);
assertEquals(0x6A80, response.getSW());
if (autonomousDerivation) {
// Correct
response = cmdSet.deriveKey(new byte[] {0x00, 0x00, 0x00, 0x01});
response = cmdSet.deriveKey(new byte[]{0x00, 0x00, 0x00, 0x01});
assertEquals(0x9000, response.getSW());
verifyKeyDerivation(keyPair, chainCode, new int[] { 1 });
verifyKeyDerivation(keyPair, chainCode, new int[]{1});
// 3 levels with hardened key
response = cmdSet.deriveKey(new byte[] {0x00, 0x00, 0x00, 0x01, (byte) 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02});
response = cmdSet.deriveKey(new byte[]{0x00, 0x00, 0x00, 0x01, (byte) 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02});
assertEquals(0x9000, response.getSW());
verifyKeyDerivation(keyPair, chainCode, new int[] { 1, 0x80000000, 2});
verifyKeyDerivation(keyPair, chainCode, new int[]{1, 0x80000000, 2});
// Reset master key
response = cmdSet.deriveKey(new byte[0]);
@ -426,13 +431,17 @@ public class WalletAppletTest {
verifyKeyDerivation(keyPair, chainCode, new int[0]);
// 3 levels with hardened key using separate commands
response = cmdSet.deriveKey(new byte[] {0x00, 0x00, 0x00, 0x01}, true, false, false);
response = cmdSet.deriveKey(new byte[]{0x00, 0x00, 0x00, 0x01}, true, false, false);
assertEquals(0x9000, response.getSW());
response = cmdSet.deriveKey(new byte[] {(byte) 0x80, 0x00, 0x00, 0x00}, false, false, false);
response = cmdSet.deriveKey(new byte[]{(byte) 0x80, 0x00, 0x00, 0x00}, false, false, false);
assertEquals(0x9000, response.getSW());
response = cmdSet.deriveKey(new byte[] {0x00, 0x00, 0x00, 0x02}, false, false, false);
response = cmdSet.deriveKey(new byte[]{0x00, 0x00, 0x00, 0x02}, false, false, false);
assertEquals(0x9000, response.getSW());
verifyKeyDerivation(keyPair, chainCode, new int[] { 1, 0x80000000, 2});
verifyKeyDerivation(keyPair, chainCode, new int[]{1, 0x80000000, 2});
} else {
response = cmdSet.deriveKey(new byte[]{0x00, 0x00, 0x00, 0x01});
assertEquals(0x6a81, response.getSW());
}
// Assisted derivation
response = cmdSet.deriveKey(new byte[] {0x00, 0x00, 0x00, 0x01}, true, true, false);
@ -452,9 +461,21 @@ public class WalletAppletTest {
assertEquals(0x9000, response.getSW());
response = cmdSet.deriveKey(new byte[] {0x00, 0x00, 0x00, 0x02}, false, true, false);
assertEquals(0x6a86, response.getSW());
// Reset master key
response = cmdSet.deriveKey(new byte[0]);
assertEquals(0x9000, response.getSW());
verifyKeyDerivation(keyPair, chainCode, new int[0]);
// Try to sign before load public key, then resume loading public key
response = cmdSet.deriveKey(new byte[] {0x00, 0x00, 0x00, 0x02}, false, true, false);
assertEquals(0x9000, response.getSW());
byte[] key = derivePublicKey(secureChannel.decryptAPDU(response.getData()));
response = cmdSet.sign(sha256("test".getBytes()), WalletApplet.SIGN_P1_PRECOMPUTED_HASH, true, true);
assertEquals(0x6985, response.getSW());
response = cmdSet.deriveKey(key, false, true, true);
assertEquals(0x9000, response.getSW());
verifyKeyDerivation(keyPair, chainCode, new int[] { 2 });
}
@Test
@ -732,13 +753,14 @@ public class WalletAppletTest {
private byte[] derivePublicKey(byte[] data) {
byte[] pubKey = Arrays.copyOfRange(data, 3, 4 + data[3]);
byte[] signature = Arrays.copyOfRange(data, 4 + data[3], data.length);
byte[] hash = sha256("STATUS KEY DERIVATION".getBytes());
pubKey[0] = 0x02;
ECKey candidate = ECKey.fromPublicOnly(pubKey);
if (!candidate.verify(WalletApplet.ASSISTED_DERIVATION_HASH, signature)) {
if (!candidate.verify(hash, signature)) {
pubKey[0] = 0x03;
candidate = ECKey.fromPublicOnly(pubKey);
assertTrue(candidate.verify(WalletApplet.ASSISTED_DERIVATION_HASH, signature));
assertTrue(candidate.verify(hash, signature));
}
return candidate.decompress().getPubKey();