Skip to content

Commit

Permalink
Merge pull request #89 from status-im/internal-factory-reset
Browse files Browse the repository at this point in the history
Internal factory reset
  • Loading branch information
bitgamma authored Jun 6, 2023
2 parents 4a04bda + 86faab3 commit 146c049
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 15 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ dependencies {
testCompile(files("../jcardsim/jcardsim-3.0.5-SNAPSHOT.jar"))
testCompile('org.web3j:core:2.3.1')
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.junit.jupiter:junit-jupiter-api:5.1.1")
testRuntime("org.junit.jupiter:junit-jupiter-engine:5.1.1")
Expand Down
2 changes: 1 addition & 1 deletion buildSrc/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ repositories {
}

dependencies {
compile 'com.github.status-im.status-keycard-java:desktop:15a61e1'
compile 'com.github.status-im.status-keycard-java:desktop:3.1.2'
}
62 changes: 51 additions & 11 deletions src/main/java/im/status/keycard/KeycardApplet.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public class KeycardApplet extends Applet {

static final byte INS_GET_STATUS = (byte) 0xF2;
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_CHANGE_PIN = (byte) 0x21;
static final byte INS_UNBLOCK_PIN = (byte) 0x22;
Expand Down Expand Up @@ -85,6 +86,9 @@ public class KeycardApplet extends Applet {
static final byte STORE_DATA_P1_NDEF = 0x01;
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_KEY_TEMPLATE = (byte) 0xA1;
Expand All @@ -105,8 +109,9 @@ public class KeycardApplet extends Applet {
static final byte CAPABILITY_KEY_MANAGEMENT = (byte) 0x02;
static final byte CAPABILITY_CREDENTIALS_MANAGEMENT = (byte) 0x04;
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};

Expand Down Expand Up @@ -282,6 +287,9 @@ public void process(APDU apdu) throws ISOException {
case INS_STORE_DATA:
storeData(apdu);
break;
case INS_FACTORY_RESET:
factoryReset(apdu);
return;
default:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
break;
Expand Down Expand Up @@ -1008,6 +1016,26 @@ private short logicrShift(short v, short amount) {
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
* authentication are required.
Expand All @@ -1022,16 +1050,28 @@ private void removeKey(APDU apdu) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}

keyPathLen = 0;
pinlessPathLen = 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);
clearKeys();
}

private void factoryReset(APDU apdu) {
byte[] apduBuffer = apdu.getBuffer();

if ((apduBuffer[OFFSET_P1] != FACTORY_RESET_P1_MAGIC) || (apduBuffer[ISO7816.OFFSET_P2] != FACTORY_RESET_P2_MAGIC)) {
ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
}

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();
}
}

/**
Expand Down
52 changes: 50 additions & 2 deletions src/test/java/im/status/keycard/KeycardTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ private static void initCapabilities(ApplicationInfo info) {
capabilities.add("ndef");
}

if (info.hasFactoryResetCapability()) {
capabilities.add("factoryReset");
}

CapabilityCondition.availableCapabilities = capabilities;
}

Expand Down Expand Up @@ -210,6 +214,11 @@ public void onDisconnected() {
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 {
KeyPair identKeyPair = Certificate.generateIdentKeyPair();
Certificate cert = Certificate.createCertificate(caKeyPair, identKeyPair);
Expand All @@ -225,8 +234,7 @@ private static void initIfNeeded() throws Exception {
sharedSecret = cmdSet.pairingPasswordToSecret(System.getProperty("im.status.keycard.test.pairing", "KeycardDefaultPairing"));

if (!cmdSet.getApplicationInfo().isInitializedCard()) {
assertEquals(0x9000, cmdSet.init("000000", "024680", "012345678901", sharedSecret, (byte) 3, (byte) 5).getSw());
cmdSet.select().checkOK();
initCard(cmdSet);
initCapabilities(cmdSet.getApplicationInfo());
}
}
Expand Down Expand Up @@ -932,6 +940,46 @@ void removeKeyTest() throws Exception {
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
@DisplayName("GENERATE KEY command")
@Capabilities("keyManagement")
Expand Down

0 comments on commit 146c049

Please sign in to comment.