more extensive key derivation/sign/get status testing
This commit is contained in:
parent
4549f9794f
commit
5d94371015
|
@ -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());
|
||||||
|
|
Loading…
Reference in New Issue