more extensive key derivation/sign/get status testing

This commit is contained in:
Michele Balistreri 2019-02-27 17:38:52 +03:00
parent 4549f9794f
commit 5d94371015
1 changed files with 115 additions and 66 deletions

View File

@ -4,6 +4,7 @@ import com.licel.jcardsim.smartcardio.CardSimulator;
import com.licel.jcardsim.smartcardio.CardTerminalSimulator; import com.licel.jcardsim.smartcardio.CardTerminalSimulator;
import com.licel.jcardsim.utils.AIDUtil; import com.licel.jcardsim.utils.AIDUtil;
import im.status.keycard.applet.ApplicationInfo; import im.status.keycard.applet.ApplicationInfo;
import im.status.keycard.applet.ApplicationStatus;
import im.status.keycard.applet.Identifiers; import im.status.keycard.applet.Identifiers;
import im.status.keycard.applet.KeycardCommandSet; import im.status.keycard.applet.KeycardCommandSet;
import im.status.keycard.desktop.LedgerUSBManager; import im.status.keycard.desktop.LedgerUSBManager;
@ -397,37 +398,49 @@ public class KeycardTest {
@Test @Test
@DisplayName("GET STATUS command") @DisplayName("GET STATUS command")
void getStatusTest() throws Exception { void getStatusTest() throws Exception {
// Security condition violation: SecureChannel not open APDUResponse response;
APDUResponse response = cmdSet.getStatus(KeycardApplet.GET_STATUS_P1_APPLICATION);
assertEquals(0x6985, response.getSw()); if (cmdSet.getApplicationInfo().hasSecureChannelCapability()) {
cmdSet.autoOpenSecureChannel(); // Security condition violation: SecureChannel not open
response = cmdSet.getStatus(KeycardApplet.GET_STATUS_P1_APPLICATION);
assertEquals(0x6985, response.getSw());
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.
response = cmdSet.getStatus(KeycardApplet.GET_STATUS_P1_APPLICATION); response = cmdSet.getStatus(KeycardApplet.GET_STATUS_P1_APPLICATION);
assertEquals(0x9000, response.getSw()); assertEquals(0x9000, response.getSw());
byte[] data = response.getData(); ApplicationStatus status = new ApplicationStatus(response.getData());
assertTrue(Hex.toHexString(data).matches("a30c0201030201050101[0f][0f]"));
response = cmdSet.verifyPIN("123456"); if (cmdSet.getApplicationInfo().hasCredentialsManagementCapability()) {
assertEquals(0x63C2, response.getSw()); assertEquals(3, status.getPINRetryCount());
response = cmdSet.getStatus(KeycardApplet.GET_STATUS_P1_APPLICATION); assertEquals(5, status.getPUKRetryCount());
assertEquals(0x9000, response.getSw());
data = response.getData();
assertTrue(Hex.toHexString(data).matches("a30c0201020201050101[0f][0f]"));
response = cmdSet.verifyPIN("000000"); response = cmdSet.verifyPIN("123456");
assertEquals(0x9000, response.getSw()); assertEquals(0x63C2, response.getSw());
response = cmdSet.getStatus(KeycardApplet.GET_STATUS_P1_APPLICATION); response = cmdSet.getStatus(KeycardApplet.GET_STATUS_P1_APPLICATION);
assertEquals(0x9000, response.getSw()); assertEquals(0x9000, response.getSw());
data = response.getData(); status = new ApplicationStatus(response.getData());
assertTrue(Hex.toHexString(data).matches("a30c0201030201050101[0f][0f]")); assertEquals(2, status.getPINRetryCount());
assertEquals(5, status.getPUKRetryCount());
response = cmdSet.verifyPIN("000000");
assertEquals(0x9000, response.getSw());
response = cmdSet.getStatus(KeycardApplet.GET_STATUS_P1_APPLICATION);
assertEquals(0x9000, response.getSw());
status = new ApplicationStatus(response.getData());
assertEquals(3, status.getPINRetryCount());
assertEquals(5, status.getPUKRetryCount());
} else {
assertEquals((byte) 0xff, status.getPINRetryCount());
assertEquals((byte) 0xff, status.getPUKRetryCount());
}
// Check that key path is empty // Check that key path is empty
response = cmdSet.getStatus(KeycardApplet.GET_STATUS_P1_KEY_PATH); response = cmdSet.getStatus(KeycardApplet.GET_STATUS_P1_KEY_PATH);
assertEquals(0x9000, response.getSw()); assertEquals(0x9000, response.getSw());
data = response.getData(); assertEquals(0, response.getData().length);
assertEquals(0, data.length);
} }
@Test @Test
@ -661,19 +674,24 @@ public class KeycardTest {
void loadKeyTest() throws Exception { void loadKeyTest() throws Exception {
KeyPairGenerator g = keypairGenerator(); KeyPairGenerator g = keypairGenerator();
KeyPair keyPair = g.generateKeyPair(); KeyPair keyPair = g.generateKeyPair();
APDUResponse response;
// Security condition violation: SecureChannel not open if (cmdSet.getApplicationInfo().hasSecureChannelCapability()) {
APDUResponse response = cmdSet.loadKey(keyPair); // Security condition violation: SecureChannel not open
assertEquals(0x6985, response.getSw()); response = cmdSet.loadKey(keyPair);
assertEquals(0x6985, response.getSw());
cmdSet.autoOpenSecureChannel(); cmdSet.autoOpenSecureChannel();
}
// Security condition violation: PIN not verified if (cmdSet.getApplicationInfo().hasCredentialsManagementCapability()) {
response = cmdSet.loadKey(keyPair); // Security condition violation: PIN not verified
assertEquals(0x6985, response.getSw()); response = cmdSet.loadKey(keyPair);
assertEquals(0x6985, response.getSw());
response = cmdSet.verifyPIN("000000"); response = cmdSet.verifyPIN("000000");
assertEquals(0x9000, response.getSw()); assertEquals(0x9000, response.getSw());
}
// Wrong key type // Wrong key type
response = cmdSet.loadKey(new byte[] { (byte) 0xAA, 0x02, (byte) 0x80, 0x00}, (byte) 0x00); response = cmdSet.loadKey(new byte[] { (byte) 0xAA, 0x02, (byte) 0x80, 0x00}, (byte) 0x00);
@ -724,9 +742,13 @@ public class KeycardTest {
@Tag("keyManagement") @Tag("keyManagement")
void generateMnemonicTest() throws Exception { void generateMnemonicTest() throws Exception {
// Security condition violation: SecureChannel not open // Security condition violation: SecureChannel not open
APDUResponse response = cmdSet.generateMnemonic(4); APDUResponse response;
assertEquals(0x6985, response.getSw());
cmdSet.autoOpenSecureChannel(); if (cmdSet.getApplicationInfo().hasSecureChannelCapability()) {
response = cmdSet.generateMnemonic(4);
assertEquals(0x6985, response.getSw());
cmdSet.autoOpenSecureChannel();
}
// Wrong P1 (too short, too long) // Wrong P1 (too short, too long)
response = cmdSet.generateMnemonic(3); response = cmdSet.generateMnemonic(3);
@ -763,18 +785,23 @@ public class KeycardTest {
void removeKeyTest() throws Exception { void removeKeyTest() throws Exception {
KeyPairGenerator g = keypairGenerator(); KeyPairGenerator g = keypairGenerator();
KeyPair keyPair = g.generateKeyPair(); KeyPair keyPair = g.generateKeyPair();
APDUResponse response;
// Security condition violation: SecureChannel not open if (cmdSet.getApplicationInfo().hasSecureChannelCapability()) {
APDUResponse response = cmdSet.removeKey(); // Security condition violation: SecureChannel not open
assertEquals(0x6985, response.getSw()); response = cmdSet.removeKey();
cmdSet.autoOpenSecureChannel(); assertEquals(0x6985, response.getSw());
cmdSet.autoOpenSecureChannel();
}
// Security condition violation: PIN not verified if (cmdSet.getApplicationInfo().hasCredentialsManagementCapability()) {
response = cmdSet.removeKey(); // Security condition violation: PIN not verified
assertEquals(0x6985, response.getSw()); response = cmdSet.removeKey();
assertEquals(0x6985, response.getSw());
response = cmdSet.verifyPIN("000000"); response = cmdSet.verifyPIN("000000");
assertEquals(0x9000, response.getSw()); assertEquals(0x9000, response.getSw());
}
response = cmdSet.loadKey(keyPair); response = cmdSet.loadKey(keyPair);
assertEquals(0x9000, response.getSw()); assertEquals(0x9000, response.getSw());
@ -784,9 +811,14 @@ public class KeycardTest {
ApplicationInfo info = new ApplicationInfo(response.getData()); ApplicationInfo info = new ApplicationInfo(response.getData());
verifyKeyUID(info.getKeyUID(), (ECPublicKey) keyPair.getPublic()); verifyKeyUID(info.getKeyUID(), (ECPublicKey) keyPair.getPublic());
cmdSet.autoOpenSecureChannel(); if (cmdSet.getApplicationInfo().hasSecureChannelCapability()) {
response = cmdSet.verifyPIN("000000"); cmdSet.autoOpenSecureChannel();
assertEquals(0x9000, response.getSw()); }
if (cmdSet.getApplicationInfo().hasCredentialsManagementCapability()) {
response = cmdSet.verifyPIN("000000");
assertEquals(0x9000, response.getSw());
}
assertTrue(cmdSet.getKeyInitializationStatus()); assertTrue(cmdSet.getKeyInitializationStatus());
@ -806,18 +838,23 @@ public class KeycardTest {
@DisplayName("GENERATE KEY command") @DisplayName("GENERATE KEY command")
@Tag("keyManagement") @Tag("keyManagement")
void generateKeyTest() throws Exception { void generateKeyTest() throws Exception {
// Security condition violation: SecureChannel not open APDUResponse response;
APDUResponse response = cmdSet.generateKey();
assertEquals(0x6985, response.getSw());
cmdSet.autoOpenSecureChannel(); if (cmdSet.getApplicationInfo().hasSecureChannelCapability()) {
// Security condition violation: SecureChannel not open
response = cmdSet.generateKey();
assertEquals(0x6985, response.getSw());
cmdSet.autoOpenSecureChannel();
}
// Security condition violation: PIN not verified if (cmdSet.getApplicationInfo().hasCredentialsManagementCapability()) {
response = cmdSet.generateKey(); // Security condition violation: PIN not verified
assertEquals(0x6985, response.getSw()); response = cmdSet.generateKey();
assertEquals(0x6985, response.getSw());
response = cmdSet.verifyPIN("000000"); response = cmdSet.verifyPIN("000000");
assertEquals(0x9000, response.getSw()); assertEquals(0x9000, response.getSw());
}
// Good case // Good case
response = cmdSet.generateKey(); response = cmdSet.generateKey();
@ -980,11 +1017,15 @@ public class KeycardTest {
byte[] chainCode = new byte[32]; byte[] chainCode = new byte[32];
new Random().nextBytes(chainCode); new Random().nextBytes(chainCode);
// Security condition violation: SecureChannel not open APDUResponse response;
APDUResponse response = cmdSet.setPinlessPath(new byte[] {0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02});
assertEquals(0x6985, response.getSw());
cmdSet.autoOpenSecureChannel(); if (cmdSet.getApplicationInfo().hasSecureChannelCapability()) {
// Security condition violation: SecureChannel not open
response = cmdSet.setPinlessPath(new byte[]{0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02});
assertEquals(0x6985, response.getSw());
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});
@ -1398,21 +1439,29 @@ public class KeycardTest {
} }
private void verifyKeyDerivation(KeyPair keyPair, byte[] chainCode, int[] path) throws Exception { private void verifyKeyDerivation(KeyPair keyPair, byte[] chainCode, int[] path) throws Exception {
if (!cmdSet.getApplicationInfo().hasKeyManagementCapability()) { byte[] hash = sha256(new byte[8]);
return;
}
DeterministicKey key = deriveKey(keyPair, chainCode, path);
byte[] hash = Hash.sha3(new byte[8]);
APDUResponse resp = cmdSet.sign(hash); APDUResponse resp = cmdSet.sign(hash);
assertEquals(0x9000, resp.getSw()); assertEquals(0x9000, resp.getSw());
byte[] sig = resp.getData(); byte[] sig = resp.getData();
byte[] publicKey = extractPublicKeyFromSignature(sig); byte[] publicKey = extractPublicKeyFromSignature(sig);
sig = extractSignature(sig); sig = extractSignature(sig);
assertTrue(key.verify(hash, sig)); if (cmdSet.getApplicationInfo().hasKeyManagementCapability()) {
assertArrayEquals(key.getPubKeyPoint().getEncoded(false), publicKey); DeterministicKey key = deriveKey(keyPair, chainCode, path);
assertTrue(key.verify(hash, sig));
assertArrayEquals(key.getPubKeyPoint().getEncoded(false), publicKey);
} else {
Signature signature = Signature.getInstance("SHA256withECDSA", "BC");
ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp256k1");
ECPublicKeySpec cardKeySpec = new ECPublicKeySpec(ecSpec.getCurve().decodePoint(publicKey), ecSpec);
ECPublicKey cardKey = (ECPublicKey) KeyFactory.getInstance("ECDSA", "BC").generatePublic(cardKeySpec);
signature.initVerify(cardKey);
signature.update(new byte[8]);
assertTrue(signature.verify(sig));
}
resp = cmdSet.getStatus(KeycardApplet.GET_STATUS_P1_KEY_PATH); resp = cmdSet.getStatus(KeycardApplet.GET_STATUS_P1_KEY_PATH);
assertEquals(0x9000, resp.getSw()); assertEquals(0x9000, resp.getSw());