Merge pull request #89 from status-im/internal-factory-reset
Internal factory reset
This commit is contained in:
commit
146c049b45
|
@ -59,7 +59,7 @@ dependencies {
|
||||||
testCompile(files("../jcardsim/jcardsim-3.0.5-SNAPSHOT.jar"))
|
testCompile(files("../jcardsim/jcardsim-3.0.5-SNAPSHOT.jar"))
|
||||||
testCompile('org.web3j:core:2.3.1')
|
testCompile('org.web3j:core:2.3.1')
|
||||||
testCompile('org.bitcoinj:bitcoinj-core:0.14.5')
|
testCompile('org.bitcoinj:bitcoinj-core:0.14.5')
|
||||||
testCompile('com.github.status-im.status-keycard-java:desktop:15a61e1')
|
testCompile('com.github.status-im.status-keycard-java:desktop:3.1.2')
|
||||||
testCompile('org.bouncycastle:bcprov-jdk15on:1.65')
|
testCompile('org.bouncycastle:bcprov-jdk15on:1.65')
|
||||||
testCompile("org.junit.jupiter:junit-jupiter-api:5.1.1")
|
testCompile("org.junit.jupiter:junit-jupiter-api:5.1.1")
|
||||||
testRuntime("org.junit.jupiter:junit-jupiter-engine:5.1.1")
|
testRuntime("org.junit.jupiter:junit-jupiter-engine:5.1.1")
|
||||||
|
|
|
@ -4,5 +4,5 @@ repositories {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile 'com.github.status-im.status-keycard-java:desktop:15a61e1'
|
compile 'com.github.status-im.status-keycard-java:desktop:3.1.2'
|
||||||
}
|
}
|
|
@ -13,6 +13,7 @@ public class KeycardApplet extends Applet {
|
||||||
|
|
||||||
static final byte INS_GET_STATUS = (byte) 0xF2;
|
static final byte INS_GET_STATUS = (byte) 0xF2;
|
||||||
static final byte INS_INIT = (byte) 0xFE;
|
static final byte INS_INIT = (byte) 0xFE;
|
||||||
|
static final byte INS_FACTORY_RESET = (byte) 0xFD;
|
||||||
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;
|
||||||
|
@ -85,6 +86,9 @@ public class KeycardApplet extends Applet {
|
||||||
static final byte STORE_DATA_P1_NDEF = 0x01;
|
static final byte STORE_DATA_P1_NDEF = 0x01;
|
||||||
static final byte STORE_DATA_P1_CASH = 0x02;
|
static final byte STORE_DATA_P1_CASH = 0x02;
|
||||||
|
|
||||||
|
static final byte FACTORY_RESET_P1_MAGIC = (byte) 0xAA;
|
||||||
|
static final byte FACTORY_RESET_P2_MAGIC = 0x55;
|
||||||
|
|
||||||
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;
|
||||||
|
@ -105,8 +109,9 @@ public class KeycardApplet extends Applet {
|
||||||
static final byte CAPABILITY_KEY_MANAGEMENT = (byte) 0x02;
|
static final byte CAPABILITY_KEY_MANAGEMENT = (byte) 0x02;
|
||||||
static final byte CAPABILITY_CREDENTIALS_MANAGEMENT = (byte) 0x04;
|
static final byte CAPABILITY_CREDENTIALS_MANAGEMENT = (byte) 0x04;
|
||||||
static final byte CAPABILITY_NDEF = (byte) 0x08;
|
static final byte CAPABILITY_NDEF = (byte) 0x08;
|
||||||
|
static final byte CAPABILITY_FACTORY_RESET = (byte) 0x10;
|
||||||
|
|
||||||
static final byte APPLICATION_CAPABILITIES = (byte)(CAPABILITY_SECURE_CHANNEL | CAPABILITY_KEY_MANAGEMENT | CAPABILITY_CREDENTIALS_MANAGEMENT | CAPABILITY_NDEF);
|
static final byte APPLICATION_CAPABILITIES = (byte)(CAPABILITY_SECURE_CHANNEL | CAPABILITY_KEY_MANAGEMENT | CAPABILITY_CREDENTIALS_MANAGEMENT | CAPABILITY_NDEF | CAPABILITY_FACTORY_RESET);
|
||||||
|
|
||||||
static final byte[] EIP_1581_PREFIX = { (byte) 0x80, 0x00, 0x00, 0x2B, (byte) 0x80, 0x00, 0x00, 0x3C, (byte) 0x80, 0x00, 0x06, 0x2D};
|
static final byte[] EIP_1581_PREFIX = { (byte) 0x80, 0x00, 0x00, 0x2B, (byte) 0x80, 0x00, 0x00, 0x3C, (byte) 0x80, 0x00, 0x06, 0x2D};
|
||||||
|
|
||||||
|
@ -282,6 +287,9 @@ public class KeycardApplet extends Applet {
|
||||||
case INS_STORE_DATA:
|
case INS_STORE_DATA:
|
||||||
storeData(apdu);
|
storeData(apdu);
|
||||||
break;
|
break;
|
||||||
|
case INS_FACTORY_RESET:
|
||||||
|
factoryReset(apdu);
|
||||||
|
return;
|
||||||
default:
|
default:
|
||||||
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
|
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
|
||||||
break;
|
break;
|
||||||
|
@ -1008,6 +1016,26 @@ public class KeycardApplet extends Applet {
|
||||||
return (short) ((short)((short) 0x4000 >>> (short) (amount - 1)) | tmp);
|
return (short) ((short)((short) 0x4000 >>> (short) (amount - 1)) | tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all keys and erases the key UID.
|
||||||
|
*/
|
||||||
|
private void clearKeys() {
|
||||||
|
keyPathLen = 0;
|
||||||
|
pinlessPathLen = 0;
|
||||||
|
tmpPathLen = 0;
|
||||||
|
isExtended = false;
|
||||||
|
masterPrivate.clearKey();
|
||||||
|
masterPublic.clearKey();
|
||||||
|
resetCurveParameters();
|
||||||
|
Util.arrayFillNonAtomic(masterChainCode, (short) 0, (short) masterChainCode.length, (byte) 0);
|
||||||
|
Util.arrayFillNonAtomic(altChainCode, (short) 0, (short) altChainCode.length, (byte) 0);
|
||||||
|
Util.arrayFillNonAtomic(keyPath, (short) 0, (short) keyPath.length, (byte) 0);
|
||||||
|
Util.arrayFillNonAtomic(pinlessPath, (short) 0, (short) pinlessPath.length, (byte) 0);
|
||||||
|
Util.arrayFillNonAtomic(tmpPath, (short) 0, (short) tmpPath.length, (byte) 0);
|
||||||
|
Util.arrayFillNonAtomic(derivationOutput, (short) 0, (short) derivationOutput.length, (byte) 0);
|
||||||
|
Util.arrayFillNonAtomic(keyUID, (short) 0, (short) keyUID.length, (byte) 0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes the REMOVE KEY command. Removes the master key and all derived keys. Secure Channel and PIN
|
* Processes the REMOVE KEY command. Removes the master key and all derived keys. Secure Channel and PIN
|
||||||
* authentication are required.
|
* authentication are required.
|
||||||
|
@ -1022,16 +1050,28 @@ public class KeycardApplet extends Applet {
|
||||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||||
}
|
}
|
||||||
|
|
||||||
keyPathLen = 0;
|
clearKeys();
|
||||||
pinlessPathLen = 0;
|
}
|
||||||
isExtended = false;
|
|
||||||
masterPrivate.clearKey();
|
private void factoryReset(APDU apdu) {
|
||||||
masterPublic.clearKey();
|
byte[] apduBuffer = apdu.getBuffer();
|
||||||
resetCurveParameters();
|
|
||||||
Util.arrayFillNonAtomic(masterChainCode, (short) 0, (short) masterChainCode.length, (byte) 0);
|
if ((apduBuffer[OFFSET_P1] != FACTORY_RESET_P1_MAGIC) || (apduBuffer[ISO7816.OFFSET_P2] != FACTORY_RESET_P2_MAGIC)) {
|
||||||
Util.arrayFillNonAtomic(altChainCode, (short) 0, (short) altChainCode.length, (byte) 0);
|
ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
|
||||||
Util.arrayFillNonAtomic(keyPath, (short) 0, (short) keyPath.length, (byte) 0);
|
}
|
||||||
Util.arrayFillNonAtomic(pinlessPath, (short) 0, (short) pinlessPath.length, (byte) 0);
|
|
||||||
|
clearKeys();
|
||||||
|
pin = null;
|
||||||
|
mainPIN = null;
|
||||||
|
altPIN = null;
|
||||||
|
puk = null;
|
||||||
|
secureChannel = null;
|
||||||
|
crypto.random.generateData(uid, (short) 0, UID_LENGTH);
|
||||||
|
Util.arrayFillNonAtomic(data, (short) 0, (short) data.length, (byte) 0);
|
||||||
|
|
||||||
|
if (JCSystem.isObjectDeletionSupported()) {
|
||||||
|
JCSystem.requestObjectDeletion();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -128,6 +128,10 @@ public class KeycardTest {
|
||||||
capabilities.add("ndef");
|
capabilities.add("ndef");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (info.hasFactoryResetCapability()) {
|
||||||
|
capabilities.add("factoryReset");
|
||||||
|
}
|
||||||
|
|
||||||
CapabilityCondition.availableCapabilities = capabilities;
|
CapabilityCondition.availableCapabilities = capabilities;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,6 +214,11 @@ public class KeycardTest {
|
||||||
usbManager.start();
|
usbManager.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void initCard(KeycardCommandSet cmdSet) throws Exception {
|
||||||
|
assertEquals(0x9000, cmdSet.init("000000", "024680", "012345678901", sharedSecret, (byte) 3, (byte) 5).getSw());
|
||||||
|
cmdSet.select().checkOK();
|
||||||
|
}
|
||||||
|
|
||||||
private static void initIfNeeded() throws Exception {
|
private static void initIfNeeded() throws Exception {
|
||||||
KeyPair identKeyPair = Certificate.generateIdentKeyPair();
|
KeyPair identKeyPair = Certificate.generateIdentKeyPair();
|
||||||
Certificate cert = Certificate.createCertificate(caKeyPair, identKeyPair);
|
Certificate cert = Certificate.createCertificate(caKeyPair, identKeyPair);
|
||||||
|
@ -225,8 +234,7 @@ public class KeycardTest {
|
||||||
sharedSecret = cmdSet.pairingPasswordToSecret(System.getProperty("im.status.keycard.test.pairing", "KeycardDefaultPairing"));
|
sharedSecret = cmdSet.pairingPasswordToSecret(System.getProperty("im.status.keycard.test.pairing", "KeycardDefaultPairing"));
|
||||||
|
|
||||||
if (!cmdSet.getApplicationInfo().isInitializedCard()) {
|
if (!cmdSet.getApplicationInfo().isInitializedCard()) {
|
||||||
assertEquals(0x9000, cmdSet.init("000000", "024680", "012345678901", sharedSecret, (byte) 3, (byte) 5).getSw());
|
initCard(cmdSet);
|
||||||
cmdSet.select().checkOK();
|
|
||||||
initCapabilities(cmdSet.getApplicationInfo());
|
initCapabilities(cmdSet.getApplicationInfo());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -932,6 +940,46 @@ public class KeycardTest {
|
||||||
assertEquals(0, info.getKeyUID().length);
|
assertEquals(0, info.getKeyUID().length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("FACTORY RESET command")
|
||||||
|
@Capabilities("factoryReset")
|
||||||
|
void factoryResetTest() throws Exception {
|
||||||
|
KeyPairGenerator g = keypairGenerator();
|
||||||
|
KeyPair keyPair = g.generateKeyPair();
|
||||||
|
|
||||||
|
// Invalid P1 P2
|
||||||
|
APDUResponse response = sdkChannel.send(new APDUCommand(0x80, KeycardApplet.INS_FACTORY_RESET, 0, 0, new byte[0]));
|
||||||
|
assertEquals(0x6a86, response.getSw());
|
||||||
|
|
||||||
|
// Good case
|
||||||
|
response = cmdSet.factoryReset();
|
||||||
|
assertEquals(0x9000, response.getSw());
|
||||||
|
|
||||||
|
response = cmdSet.getStatus(KeycardCommandSet.GET_STATUS_P1_KEY_PATH);
|
||||||
|
assertEquals(0x6d00, response.getSw());
|
||||||
|
|
||||||
|
response = cmdSet.select();
|
||||||
|
assertEquals(0x9000, response.getSw());
|
||||||
|
assertFalse(cmdSet.getApplicationInfo().isInitializedCard());
|
||||||
|
|
||||||
|
initCard(cmdSet);
|
||||||
|
|
||||||
|
response = cmdSet.select();
|
||||||
|
assertEquals(0x9000, response.getSw());
|
||||||
|
|
||||||
|
if (cmdSet.getApplicationInfo().hasSecureChannelCapability()) {
|
||||||
|
cmdSet.autoPair(sharedSecret);
|
||||||
|
cmdSet.autoOpenSecureChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmdSet.getApplicationInfo().hasCredentialsManagementCapability()) {
|
||||||
|
response = cmdSet.verifyPIN("000000");
|
||||||
|
assertEquals(0x9000, response.getSw());
|
||||||
|
}
|
||||||
|
|
||||||
|
assertFalse(cmdSet.getKeyInitializationStatus());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("GENERATE KEY command")
|
@DisplayName("GENERATE KEY command")
|
||||||
@Capabilities("keyManagement")
|
@Capabilities("keyManagement")
|
||||||
|
|
Loading…
Reference in New Issue