From 039964e8d0e5bc62c709a70f41cbe013a3114515 Mon Sep 17 00:00:00 2001 From: Michele Balistreri Date: Tue, 6 Jun 2023 10:46:46 +0200 Subject: [PATCH 1/4] implement factory reset --- .../java/im/status/keycard/KeycardApplet.java | 52 +++++++++++++++---- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/src/main/java/im/status/keycard/KeycardApplet.java b/src/main/java/im/status/keycard/KeycardApplet.java index aee521e..f3db6a5 100644 --- a/src/main/java/im/status/keycard/KeycardApplet.java +++ b/src/main/java/im/status/keycard/KeycardApplet.java @@ -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; @@ -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; @@ -282,6 +286,9 @@ public class KeycardApplet extends Applet { case INS_STORE_DATA: storeData(apdu); break; + case INS_FACTORY_RESET: + factoryReset(apdu); + break; default: ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); break; @@ -1008,6 +1015,23 @@ public class KeycardApplet extends Applet { return (short) ((short)((short) 0x4000 >>> (short) (amount - 1)) | tmp); } + /** + * Clear all keys and erases the key UID. + */ + private void clearKeys() { + 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); + 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. @@ -1022,16 +1046,24 @@ public class KeycardApplet extends Applet { 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[OFFSET_P2] != FACTORY_RESET_P2_MAGIC)) { + ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2); + } + + clearKeys(); + pin = 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); + JCSystem.requestObjectDeletion(); } /** From e79d84b9b4023fe8db78d540d8d572bc797ac3ff Mon Sep 17 00:00:00 2001 From: Michele Balistreri Date: Tue, 6 Jun 2023 14:40:24 +0200 Subject: [PATCH 2/4] fix and test factory reset --- .../java/im/status/keycard/KeycardApplet.java | 12 +++-- .../java/im/status/keycard/KeycardTest.java | 51 ++++++++++++++++++- 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/src/main/java/im/status/keycard/KeycardApplet.java b/src/main/java/im/status/keycard/KeycardApplet.java index f3db6a5..cf8ab1d 100644 --- a/src/main/java/im/status/keycard/KeycardApplet.java +++ b/src/main/java/im/status/keycard/KeycardApplet.java @@ -109,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}; @@ -288,7 +289,7 @@ public class KeycardApplet extends Applet { break; case INS_FACTORY_RESET: factoryReset(apdu); - break; + return; default: ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); break; @@ -1052,7 +1053,7 @@ public class KeycardApplet extends Applet { private void factoryReset(APDU apdu) { byte[] apduBuffer = apdu.getBuffer(); - if ((apduBuffer[OFFSET_P1] != FACTORY_RESET_P1_MAGIC) || (apduBuffer[OFFSET_P2] != FACTORY_RESET_P2_MAGIC)) { + if ((apduBuffer[OFFSET_P1] != FACTORY_RESET_P1_MAGIC) || (apduBuffer[ISO7816.OFFSET_P2] != FACTORY_RESET_P2_MAGIC)) { ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2); } @@ -1063,7 +1064,10 @@ public class KeycardApplet extends Applet { secureChannel = null; crypto.random.generateData(uid, (short) 0, UID_LENGTH); Util.arrayFillNonAtomic(data, (short) 0, (short) data.length, (byte) 0); - JCSystem.requestObjectDeletion(); + + if (JCSystem.isObjectDeletionSupported()) { + JCSystem.requestObjectDeletion(); + } } /** diff --git a/src/test/java/im/status/keycard/KeycardTest.java b/src/test/java/im/status/keycard/KeycardTest.java index 9759318..38b3be1 100644 --- a/src/test/java/im/status/keycard/KeycardTest.java +++ b/src/test/java/im/status/keycard/KeycardTest.java @@ -128,6 +128,10 @@ public class KeycardTest { capabilities.add("ndef"); } + //if (info.hasFactoryResetCapability()) { + capabilities.add("factoryReset"); + //} + CapabilityCondition.availableCapabilities = capabilities; } @@ -210,6 +214,11 @@ public class KeycardTest { 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); @@ -225,8 +234,7 @@ public class KeycardTest { 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()); } } @@ -932,6 +940,45 @@ public class KeycardTest { 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()); + + response = sdkChannel.send(new APDUCommand(0x80, KeycardApplet.INS_FACTORY_RESET, 0xAA, 0x55, new byte[0])); + 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") From 44adec536e6aba31afed9ad84793cd8ac8e90c54 Mon Sep 17 00:00:00 2001 From: Michele Balistreri Date: Tue, 6 Jun 2023 15:26:18 +0200 Subject: [PATCH 3/4] clear also some ram buffers --- src/main/java/im/status/keycard/KeycardApplet.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/im/status/keycard/KeycardApplet.java b/src/main/java/im/status/keycard/KeycardApplet.java index cf8ab1d..798e403 100644 --- a/src/main/java/im/status/keycard/KeycardApplet.java +++ b/src/main/java/im/status/keycard/KeycardApplet.java @@ -1022,6 +1022,7 @@ public class KeycardApplet extends Applet { private void clearKeys() { keyPathLen = 0; pinlessPathLen = 0; + tmpPathLen = 0; isExtended = false; masterPrivate.clearKey(); masterPublic.clearKey(); @@ -1030,6 +1031,8 @@ public class KeycardApplet extends Applet { 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); } @@ -1059,6 +1062,7 @@ public class KeycardApplet extends Applet { clearKeys(); pin = null; + mainPIN = null; altPIN = null; puk = null; secureChannel = null; From 86faab3f26541ccd91d7dbc838fe880f75bc7f7e Mon Sep 17 00:00:00 2001 From: Michele Balistreri Date: Tue, 6 Jun 2023 17:02:26 +0200 Subject: [PATCH 4/4] update sdk --- build.gradle | 2 +- buildSrc/build.gradle | 2 +- src/test/java/im/status/keycard/KeycardTest.java | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 413d147..b7446c7 100644 --- a/build.gradle +++ b/build.gradle @@ -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") diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 44786a5..3e21886 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -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' } \ No newline at end of file diff --git a/src/test/java/im/status/keycard/KeycardTest.java b/src/test/java/im/status/keycard/KeycardTest.java index 38b3be1..2e2a84e 100644 --- a/src/test/java/im/status/keycard/KeycardTest.java +++ b/src/test/java/im/status/keycard/KeycardTest.java @@ -128,9 +128,9 @@ public class KeycardTest { capabilities.add("ndef"); } - //if (info.hasFactoryResetCapability()) { + if (info.hasFactoryResetCapability()) { capabilities.add("factoryReset"); - //} + } CapabilityCondition.availableCapabilities = capabilities; } @@ -951,7 +951,8 @@ public class KeycardTest { APDUResponse response = sdkChannel.send(new APDUCommand(0x80, KeycardApplet.INS_FACTORY_RESET, 0, 0, new byte[0])); assertEquals(0x6a86, response.getSw()); - response = sdkChannel.send(new APDUCommand(0x80, KeycardApplet.INS_FACTORY_RESET, 0xAA, 0x55, new byte[0])); + // Good case + response = cmdSet.factoryReset(); assertEquals(0x9000, response.getSw()); response = cmdSet.getStatus(KeycardCommandSet.GET_STATUS_P1_KEY_PATH);