Skip to content

Commit

Permalink
Merge pull request #60 from status-im/reduce-nvm-writes
Browse files Browse the repository at this point in the history
JCOP4 compatibility + reduce NVM writes
  • Loading branch information
bitgamma authored Feb 10, 2020
2 parents 5908a8b + 1d16a82 commit e110c64
Show file tree
Hide file tree
Showing 6 changed files with 15 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,11 @@ public void blockLoaded(int loadedBlock, int blockCount) {
}
});
logger.info("Installing the Keycard Applet");
cmdSet.installKeycardApplet();
cmdSet.installKeycardApplet().checkOK();
logger.info("Installing the NDEF Applet");
cmdSet.installNDEFApplet(new byte[0]);
cmdSet.installNDEFApplet(new byte[0]).checkOK();
logger.info("Installing the Cash Applet");
cmdSet.installCashApplet();
cmdSet.installCashApplet().checkOK();
} catch (IOException e) {
throw new GradleException("I/O error", e);
} catch (APDUException e) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/im/status/keycard/CashApplet.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public static void install(byte[] bArray, short bOffset, byte bLength) {
*/
public CashApplet(byte[] bArray, short bOffset, byte bLength) {
crypto = new Crypto();
secp256k1 = new SECP256k1(crypto);
secp256k1 = new SECP256k1();

keypair = new KeyPair(KeyPair.ALG_EC_FP, SECP256k1.SECP256K1_KEY_SIZE);
publicKey = (ECPublicKey) keypair.getPublic();
Expand Down
9 changes: 5 additions & 4 deletions src/main/java/im/status/keycard/KeycardApplet.java
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,7 @@ public static void install(byte[] bArray, short bOffset, byte bLength) {
*/
public KeycardApplet(byte[] bArray, short bOffset, byte bLength) {
crypto = new Crypto();
secp256k1 = new SECP256k1(crypto);
secureChannel = new SecureChannel(PAIRING_MAX_CLIENT_COUNT, crypto, secp256k1);
secp256k1 = new SECP256k1();

uid = new byte[UID_LENGTH];
crypto.random.generateData(uid, (short) 0, UID_LENGTH);
Expand Down Expand Up @@ -199,7 +198,7 @@ public KeycardApplet(byte[] bArray, short bOffset, byte bLength) {

derivationOutput = JCSystem.makeTransientByteArray((short) (Crypto.KEY_SECRET_SIZE + CHAIN_CODE_SIZE), JCSystem.CLEAR_ON_RESET);

data = new byte[MAX_DATA_LENGTH + 1];
data = new byte[(short)(MAX_DATA_LENGTH + 1)];

register(bArray, (short) (bOffset + 1), bArray[bOffset]);
}
Expand All @@ -214,6 +213,9 @@ public KeycardApplet(byte[] bArray, short bOffset, byte bLength) {
public void process(APDU apdu) throws ISOException {
// If we have no PIN it means we still have to initialize the applet.
if (pin == null) {
if (secureChannel == null) {
secureChannel = new SecureChannel(PAIRING_MAX_CLIENT_COUNT, crypto, secp256k1);
}
processInit(apdu);
return;
}
Expand Down Expand Up @@ -374,7 +376,6 @@ private void selectApplet(APDU apdu) {
pin.reset();
puk.reset();
secureChannel.reset();
secureChannel.updateSecureChannelCounter();

byte[] apduBuffer = apdu.getBuffer();

Expand Down
4 changes: 1 addition & 3 deletions src/main/java/im/status/keycard/SECP256k1.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,12 @@ public class SECP256k1 {


private KeyAgreement ecPointMultiplier;
private Crypto crypto;
ECPrivateKey tmpECPrivateKey;

/**
* Allocates objects needed by this class. Must be invoked during the applet installation exactly 1 time.
*/
SECP256k1(Crypto crypto) {
this.crypto = crypto;
SECP256k1() {
this.ecPointMultiplier = KeyAgreement.getInstance(ALG_EC_SVDP_DH_PLAIN_XY, false);
this.tmpECPrivateKey = (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, SECP256K1_KEY_SIZE, false);
setCurveParameters(tmpECPrivateKey);
Expand Down
20 changes: 3 additions & 17 deletions src/main/java/im/status/keycard/SecureChannel.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ public class SecureChannel {
public static final short PAIRING_KEY_LENGTH = SC_SECRET_LENGTH + 1;
public static final short SC_BLOCK_SIZE = Crypto.AES_BLOCK_SIZE;
public static final short SC_OUT_OFFSET = ISO7816.OFFSET_CDATA + (SC_BLOCK_SIZE * 2);
public static final short SC_COUNTER_MAX = 100;

public static final byte INS_OPEN_SECURE_CHANNEL = 0x10;
public static final byte INS_MUTUALLY_AUTHENTICATE = 0x11;
Expand All @@ -33,8 +32,6 @@ public class SecureChannel {
private byte[] secret;
private byte[] pairingSecret;

private short scCounter;

/*
* To avoid overhead, the pairing keys are stored in a plain byte array as sequences of 33-bytes elements. The first
* byte is 0 if the slot is free and 1 if used. The following 32 bytes are the actual key data.
Expand All @@ -59,13 +56,14 @@ public SecureChannel(byte pairingLimit, Crypto crypto, SECP256k1 secp256k1) {
scEncKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES_TRANSIENT_DESELECT, KeyBuilder.LENGTH_AES_256, false);
scMacKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES_TRANSIENT_DESELECT, KeyBuilder.LENGTH_AES_256, false);

secret = JCSystem.makeTransientByteArray((short)(SC_SECRET_LENGTH * 2), JCSystem.CLEAR_ON_DESELECT);
pairingKeys = new byte[(short)(PAIRING_KEY_LENGTH * pairingLimit)];

scKeypair = new KeyPair(KeyPair.ALG_EC_FP, SC_KEY_LENGTH);
secp256k1.setCurveParameters((ECKey) scKeypair.getPrivate());
secp256k1.setCurveParameters((ECKey) scKeypair.getPublic());
scKeypair.genKeyPair();

secret = JCSystem.makeTransientByteArray((short)(SC_SECRET_LENGTH * 2), JCSystem.CLEAR_ON_DESELECT);
pairingKeys = new byte[(short)(PAIRING_KEY_LENGTH * pairingLimit)];
remainingSlots = pairingLimit;

}
Expand Down Expand Up @@ -391,18 +389,6 @@ public byte getRemainingPairingSlots() {
return remainingSlots;
}

/**
* Called before sending the public key to the client, gives a chance to change keys if needed.
*/
public void updateSecureChannelCounter() {
if (scCounter < SC_COUNTER_MAX) {
scCounter++;
} else {
scKeypair.genKeyPair();
scCounter = 0;
}
}

/**
* Resets the Secure Channel, invalidating the current session. If no session is opened, this does nothing.
*/
Expand Down
16 changes: 2 additions & 14 deletions src/test/java/im/status/keycard/KeycardTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -289,19 +289,7 @@ void openSecureChannelTest() throws Exception {
response = cmdSet.getStatus(KeycardApplet.GET_STATUS_P1_APPLICATION);
assertEquals(0x9000, response.getSw());

// Verify that the keys are changed correctly. Since we do not know the internal counter we just iterate until that
// happens for a maximum of SC_COUNTER_MAX times
byte[] initialKey = new ApplicationInfo(cmdSet.select().getData()).getSecureChannelPubKey();

for (int i = 0; i < SecureChannel.SC_COUNTER_MAX; i++) {
byte[] otherKey = new ApplicationInfo(cmdSet.select().getData()).getSecureChannelPubKey();

if (!Arrays.equals(initialKey, otherKey)) {
secureChannel.generateSecret(otherKey);
cmdSet.autoOpenSecureChannel();
break;
}
}
}

@Test
Expand Down Expand Up @@ -384,7 +372,7 @@ void pairTest() throws Exception {
cmdSet.openSecureChannel(secureChannel.getPairingIndex(), secureChannel.getPublicKey());

// Pair multiple indexes
for (int i = 1; i < 5; i++) {
for (int i = 1; i < KeycardApplet.PAIRING_MAX_CLIENT_COUNT; i++) {
cmdSet.autoPair(sharedSecret);
assertEquals(i, secureChannel.getPairingIndex());
cmdSet.autoOpenSecureChannel();
Expand All @@ -403,7 +391,7 @@ void pairTest() throws Exception {
assertEquals(0x9000, response.getSw());
}

for (byte i = 0; i < 4; i++) {
for (byte i = 0; i < (KeycardApplet.PAIRING_MAX_CLIENT_COUNT - 1); i++) {
response = cmdSet.unpair(i);
assertEquals(0x9000, response.getSw());
}
Expand Down

0 comments on commit e110c64

Please sign in to comment.