Skip to content

Commit

Permalink
implement plausble deniability
Browse files Browse the repository at this point in the history
  • Loading branch information
bitgamma committed Nov 9, 2022
1 parent 5bc653c commit 8ac2e88
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 22 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ if (!testTarget) {

def pairingPass = project.properties['im.status.keycard.test.pairing']
if (!pairingPass) {
pairingPass = 'KeycardTest'
pairingPass = 'KeycardDefaultPairing'
}


Expand Down
64 changes: 50 additions & 14 deletions src/main/java/im/status/keycard/KeycardApplet.java
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,17 @@ public class KeycardApplet extends Applet {
static final byte[] EIP_1581_PREFIX = { (byte) 0x80, 0x00, 0x00, 0x2B, (byte) 0x80, 0x00, 0x00, 0x3C, (byte) 0x80, 0x00, 0x06, 0x2D};

private OwnerPIN pin;
private OwnerPIN realPin;
private OwnerPIN fakePin;
private OwnerPIN puk;
private byte[] uid;
private SecureChannel secureChannel;

private ECPublicKey masterPublic;
private ECPrivateKey masterPrivate;
private byte[] masterChainCode;
private byte[] fakeChainCode;
private byte[] chainCode;
private boolean isExtended;

private byte[] tmpPath;
Expand Down Expand Up @@ -173,6 +177,8 @@ public KeycardApplet(byte[] bArray, short bOffset, byte bLength) {
masterPublic = (ECPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC, SECP256k1.SECP256K1_KEY_SIZE, false);
masterPrivate = (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, SECP256k1.SECP256K1_KEY_SIZE, false);
masterChainCode = new byte[CHAIN_CODE_SIZE];
fakeChainCode = new byte[CHAIN_CODE_SIZE];
chainCode = masterChainCode;

keyPath = new byte[KEY_PATH_MAX_DEPTH * 4];
pinlessPath = new byte[KEY_PATH_MAX_DEPTH * 4];
Expand Down Expand Up @@ -341,15 +347,16 @@ private void processInit(APDU apdu) {

secureChannel.initSecureChannel(apduBuffer, (short)(ISO7816.OFFSET_CDATA + PIN_LENGTH + PUK_LENGTH));

JCSystem.beginTransaction();
realPin = new OwnerPIN(pinLimit, PIN_LENGTH);
realPin.update(apduBuffer, ISO7816.OFFSET_CDATA, PIN_LENGTH);

pin = new OwnerPIN(pinLimit, PIN_LENGTH);
pin.update(apduBuffer, ISO7816.OFFSET_CDATA, PIN_LENGTH);
fakePin = new OwnerPIN(pinLimit, PIN_LENGTH);
fakePin.update(apduBuffer, (short)(ISO7816.OFFSET_CDATA + PIN_LENGTH), PIN_LENGTH);

puk = new OwnerPIN(pukLimit, PUK_LENGTH);
puk.update(apduBuffer, (short)(ISO7816.OFFSET_CDATA + PIN_LENGTH), PUK_LENGTH);

JCSystem.commitTransaction();
pin = realPin;
} else if (apduBuffer[ISO7816.OFFSET_INS] == IdentApplet.INS_IDENTIFY_CARD) {
IdentApplet.identifyCard(apdu, null, signature);
} else {
Expand Down Expand Up @@ -385,7 +392,8 @@ private void unpair(APDU apdu) {
* @param apdu the JCRE-owned APDU object.
*/
private void selectApplet(APDU apdu) {
pin.reset();
fakePin.reset();
realPin.reset();
puk.reset();
secureChannel.reset();

Expand Down Expand Up @@ -516,8 +524,24 @@ private void verifyPIN(APDU apdu) {
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
}

if (!pin.check(apduBuffer, ISO7816.OFFSET_CDATA, len)) {
ISOException.throwIt((short)((short) 0x63c0 | (short) pin.getTriesRemaining()));
short resp = realPin.check(apduBuffer, ISO7816.OFFSET_CDATA, len) ? (short) 1 : (short) 0;
resp += fakePin.check(apduBuffer, ISO7816.OFFSET_CDATA, len) ? (short) 2 : (short) 0;

switch(resp) {
case 0:
ISOException.throwIt((short)((short) 0x63c0 | (short) pin.getTriesRemaining()));
break;
case 1:
chainCode = masterChainCode;
fakePin.resetAndUnblock();
pin = realPin;
break;
case 2:
case 3: // if pins are equal fake pin takes precedence
chainCode = fakeChainCode;
realPin.resetAndUnblock();
pin = fakePin;
break;
}
}

Expand Down Expand Up @@ -615,7 +639,8 @@ private void unblockPIN(APDU apdu) {
ISOException.throwIt((short)((short) 0x63c0 | (short) puk.getTriesRemaining()));
}

pin.resetAndUnblock();
fakePin.resetAndUnblock();
realPin.resetAndUnblock();
pin.update(apduBuffer, (short)(ISO7816.OFFSET_CDATA + PUK_LENGTH), PIN_LENGTH);
pin.check(apduBuffer, (short)(ISO7816.OFFSET_CDATA + PUK_LENGTH), PIN_LENGTH);
puk.reset();
Expand Down Expand Up @@ -662,6 +687,10 @@ private void loadKey(APDU apdu) {
* @param apduBuffer the APDU buffer
*/
private void generateKeyUIDAndRespond(APDU apdu, byte[] apduBuffer) {
if (isExtended) {
crypto.sha256.doFinal(masterChainCode, (short) 0, CHAIN_CODE_SIZE, fakeChainCode, (short) 0);
}

short pubLen = masterPublic.getW(apduBuffer, (short) 0);
crypto.sha256.doFinal(apduBuffer, (short) 0, pubLen, keyUID, (short) 0);
Util.arrayCopyNonAtomic(keyUID, (short) 0, apduBuffer, SecureChannel.SC_OUT_OFFSET, KEY_UID_LENGTH);
Expand Down Expand Up @@ -782,14 +811,20 @@ private void deriveKey(APDU apdu) {
/**
* Updates the derivation path for a subsequent EXPORT KEY/SIGN APDU. Optionally stores the result in the current path.
*
* @param apduBuffer the APDU buffer
* @param off the offset in the APDU buffer relative to the data field
* @param path the path
* @param off the offset in the path
* @param len the len of the path
* @param source derivation source
*/
private void updateDerivationPath(byte[] apduBuffer, short off, short len, byte source) {
private void updateDerivationPath(byte[] path, short off, short len, byte source) {
if (!isExtended) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
if (len == 0) {
tmpPathLen = 0;
} else {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}

return;
}

short newPathLen;
Expand Down Expand Up @@ -833,7 +868,7 @@ private void updateDerivationPath(byte[] apduBuffer, short off, short len, byte
short pathOff = (short) (ISO7816.OFFSET_CDATA + off);

Util.arrayCopyNonAtomic(srcKeyPath, (short) 0, tmpPath, (short) 0, pathLenOff);
Util.arrayCopyNonAtomic(apduBuffer, pathOff, tmpPath, pathLenOff, len);
Util.arrayCopyNonAtomic(path, pathOff, tmpPath, pathLenOff, len);
tmpPathLen = newPathLen;
}

Expand Down Expand Up @@ -862,7 +897,7 @@ private void doDerive(byte[] apduBuffer, short off) {
short dataOff = (short) (scratchOff + Crypto.KEY_DERIVATION_SCRATCH_SIZE);

short pubKeyOff = (short) (dataOff + masterPrivate.getS(apduBuffer, dataOff));
pubKeyOff = Util.arrayCopyNonAtomic(masterChainCode, (short) 0, apduBuffer, pubKeyOff, CHAIN_CODE_SIZE);
pubKeyOff = Util.arrayCopyNonAtomic(chainCode, (short) 0, apduBuffer, pubKeyOff, CHAIN_CODE_SIZE);

if (!crypto.bip32IsHardened(tmpPath, (short) 0)) {
masterPublic.getW(apduBuffer, pubKeyOff);
Expand Down Expand Up @@ -987,6 +1022,7 @@ private void removeKey(APDU apdu) {
masterPublic.clearKey();
resetCurveParameters();
Util.arrayFillNonAtomic(masterChainCode, (short) 0, (short) masterChainCode.length, (byte) 0);
Util.arrayFillNonAtomic(fakeChainCode, (short) 0, (short) fakeChainCode.length, (byte) 0);
Util.arrayFillNonAtomic(keyPath, (short) 0, (short) keyPath.length, (byte) 0);
Util.arrayFillNonAtomic(pinlessPath, (short) 0, (short) pinlessPath.length, (byte) 0);
}
Expand Down
14 changes: 7 additions & 7 deletions src/test/java/im/status/keycard/KeycardTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -222,10 +222,10 @@ private static void initIfNeeded() throws Exception {

initCapabilities(cmdSet.getApplicationInfo());

sharedSecret = cmdSet.pairingPasswordToSecret(System.getProperty("im.status.keycard.test.pairing", "KeycardTest"));
sharedSecret = cmdSet.pairingPasswordToSecret(System.getProperty("im.status.keycard.test.pairing", "KeycardDefaultPairing"));

if (!cmdSet.getApplicationInfo().isInitializedCard()) {
assertEquals(0x9000, cmdSet.init("000000", "123456789012", sharedSecret).getSw());
assertEquals(0x9000, cmdSet.init("000000", "012345678901", sharedSecret).getSw());
cmdSet.select().checkOK();
initCapabilities(cmdSet.getApplicationInfo());
}
Expand Down Expand Up @@ -581,7 +581,7 @@ void verifyPinTest() throws Exception {
assertEquals(0x63C0, response.getSw());

// Unblock PIN to make further tests possible
response = cmdSet.unblockPIN("123456789012", "000000");
response = cmdSet.unblockPIN("012345678901", "000000");
assertEquals(0x9000, response.getSw());
}

Expand Down Expand Up @@ -667,7 +667,7 @@ void changePinTest() throws Exception {
assertEquals(0x9000, response.getSw());

// Reset PUK
response = cmdSet.changePIN(KeycardApplet.CHANGE_PIN_P1_PUK, "123456789012");
response = cmdSet.changePIN(KeycardApplet.CHANGE_PIN_P1_PUK, "012345678901");
assertEquals(0x9000, response.getSw());

// Change the pairing secret
Expand All @@ -694,13 +694,13 @@ void changePinTest() throws Exception {
@Capabilities("credentialsManagement")
void unblockPinTest() throws Exception {
// Security condition violation: SecureChannel not open
APDUResponse response = cmdSet.unblockPIN("123456789012", "000000");
APDUResponse response = cmdSet.unblockPIN("012345678901", "000000");
assertEquals(0x6985, response.getSw());

cmdSet.autoOpenSecureChannel();

// Condition violation: PIN is not blocked
response = cmdSet.unblockPIN("123456789012", "000000");
response = cmdSet.unblockPIN("012345678901", "000000");
assertEquals(0x6985, response.getSw());

// Block the PIN
Expand All @@ -725,7 +725,7 @@ void unblockPinTest() throws Exception {
assertEquals(0x63C4, response.getSw());

// Correct PUK
response = cmdSet.unblockPIN("123456789012", "654321");
response = cmdSet.unblockPIN("012345678901", "654321");
assertEquals(0x9000, response.getSw());

// Check that PIN has been changed and unblocked
Expand Down

0 comments on commit 8ac2e88

Please sign in to comment.