Author SHA1 Message Date
Michele Balistreri 793c8eedc9 bump pairing slot count to 100 2025-02-12 09:07:40 +01:00
Michele Balistreri 146c049b45 Merge pull request #89 from status-im/internal-factory-reset
Internal factory reset
2023-06-06 17:03:32 +02:00
Michele Balistreri 86faab3f26 update sdk 2023-06-06 17:02:26 +02:00
Michele Balistreri 44adec536e clear also some ram buffers 2023-06-06 15:26:18 +02:00
Michele Balistreri e79d84b9b4 fix and test factory reset 2023-06-06 14:40:24 +02:00
Michele Balistreri 039964e8d0 implement factory reset 2023-06-06 10:46:46 +02:00
guylouis 4a04bdacec Update README.md with enno wallet mention 2023-05-17 07:40:52 +02:00
Michele Balistreri b21fef0fec Merge pull request #87 from status-im/plausible-deniability
implement plausible deniability
2022-12-02 12:47:18 +01:00
5 changed files with 109 additions and 20 deletions
+4 -3
View File
@@ -17,10 +17,11 @@ A good place to start is our documentation site https://keycard.tech/docs/
You can also join the dicussion about this project on Status channel: https://get.status.im/chat/public/status-keycard
If you just want to use the Keycard as your hardware wallet there are currently two apps supporting it
If you just want to use the Keycard as your hardware wallet there are currently three apps supporting it
1. WallETH [[Android](https://play.google.com/store/apps/details?id=org.walleth)]
2. Status [[Android](https://play.google.com/store/apps/details?id=im.status.ethereum)][[iOS](https://apps.apple.com/us/app/status-private-communication/id1178893006)]
1. Status [[Android](https://play.google.com/store/apps/details?id=im.status.ethereum)][[iOS](https://apps.apple.com/us/app/status-private-communication/id1178893006)]
2. WallETH [[Android](https://play.google.com/store/apps/details?id=org.walleth)]
3. Enno Walet https://ennowallet.com/
# How to contribute?
+1 -1
View File
@@ -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")
+1 -1
View File
@@ -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'
}
@@ -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;
@@ -39,7 +40,7 @@ public class KeycardApplet extends Applet {
static final byte PIN_LENGTH = 6;
static final byte DEFAULT_PIN_MAX_RETRIES = 3;
static final byte KEY_PATH_MAX_DEPTH = 10;
static final byte PAIRING_MAX_CLIENT_COUNT = 5;
static final byte PAIRING_MAX_CLIENT_COUNT = 100;
static final byte UID_LENGTH = 16;
static final byte MAX_DATA_LENGTH = 127;
@@ -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;
@@ -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};
@@ -282,6 +287,9 @@ public class KeycardApplet extends Applet {
case INS_STORE_DATA:
storeData(apdu);
break;
case INS_FACTORY_RESET:
factoryReset(apdu);
return;
default:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
break;
@@ -1008,6 +1016,26 @@ 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;
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.
@@ -1022,16 +1050,28 @@ 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[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();
}
}
/**
@@ -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());
}
}
@@ -481,7 +489,7 @@ public class KeycardTest {
}
// Wrong P1
response = cmdSet.unpair((byte) 5);
response = cmdSet.unpair(KeycardApplet.PAIRING_MAX_CLIENT_COUNT);
assertEquals(0x6A86, response.getSw());
// Unpair spare keyset
@@ -932,6 +940,46 @@ 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());
// 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")