diff --git a/src/main/java/im/status/keycard/KeycardApplet.java b/src/main/java/im/status/keycard/KeycardApplet.java index 86f426a..de94256 100644 --- a/src/main/java/im/status/keycard/KeycardApplet.java +++ b/src/main/java/im/status/keycard/KeycardApplet.java @@ -8,7 +8,7 @@ * The applet's main class. All incoming commands a processed by this class. */ public class KeycardApplet extends Applet { - static final short APPLICATION_VERSION = (short) 0x0202; + static final short APPLICATION_VERSION = (short) 0x0203; static final byte INS_GET_STATUS = (byte) 0xF2; static final byte INS_SET_NDEF = (byte) 0xF3; @@ -38,8 +38,12 @@ public class KeycardApplet extends Applet { static final byte KEY_PATH_MAX_DEPTH = 10; static final byte PAIRING_MAX_CLIENT_COUNT = 1; static final byte UID_LENGTH = 16; - // Maximum payload size of an encrypted APDU: https://status.im/keycard_api/apdu/opensecurechannel.html - static final short SAVED_DATA_SIZE = 223; + + // We choose 124 because after exhaustive testing it is the largest + // encrypted APDU buffer size we can send. + // We want to allow storage of roughly 500 bytes (496 here) + static final short SAVED_DATA_PAGE_SZ = 124; + static final short SAVED_DATA_NUM_PAGES = 4; static final short CHAIN_CODE_SIZE = 32; static final short KEY_UID_LENGTH = 32; @@ -196,7 +200,7 @@ public KeycardApplet(byte[] bArray, short bOffset, byte bLength) { uid = new byte[UID_LENGTH]; crypto.random.generateData(uid, (short) 0, UID_LENGTH); - savedData = new byte[SAVED_DATA_SIZE]; + savedData = new byte[SAVED_DATA_PAGE_SZ * SAVED_DATA_NUM_PAGES]; masterSeed = new byte[BIP39_SEED_SIZE]; masterSeedStatus = MASTERSEED_EMPTY; @@ -882,15 +886,25 @@ private void loadData(APDU apdu) { if (!pin.isValidated()) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } - - if ((short) apduBuffer[ISO7816.OFFSET_LC] != SAVED_DATA_SIZE) { + + // Included data must be a full page + if ((short) apduBuffer[ISO7816.OFFSET_LC] != SAVED_DATA_PAGE_SZ) { + ISOException.throwIt(ISO7816.SW_WRONG_DATA); + } + + // Must reference a page in the saved data boundary (0 - (SAVED_DATA_NUM_PAGES-1)) + if ((short) apduBuffer[ISO7816.OFFSET_P1] >= SAVED_DATA_NUM_PAGES) { ISOException.throwIt(ISO7816.SW_WRONG_DATA); } + // Copy this page of data JCSystem.beginTransaction(); - Util.arrayCopy(apduBuffer, (short) ISO7816.OFFSET_CDATA, savedData, (short) 0, apduBuffer[ISO7816.OFFSET_LC]); + Util.arrayCopy( apduBuffer, + (short) ISO7816.OFFSET_CDATA, + savedData, + (short) (apduBuffer[ISO7816.OFFSET_P1] * SAVED_DATA_PAGE_SZ), + SAVED_DATA_PAGE_SZ); JCSystem.commitTransaction(); - secureChannel.respond(apdu, (short) 0, ISO7816.SW_NO_ERROR); } @@ -903,12 +917,19 @@ private void exportData(APDU apdu) { if (!pin.isValidated()) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } - - JCSystem.beginTransaction(); - Util.arrayCopy(savedData, (short) 0, apduBuffer, (short) 0, SAVED_DATA_SIZE); - JCSystem.commitTransaction(); - secureChannel.respond(apdu, SAVED_DATA_SIZE, ISO7816.SW_NO_ERROR); + // Must reference a page in the saved data boundary (0 - (SAVED_DATA_NUM_PAGES-1)) + if ((short) apduBuffer[ISO7816.OFFSET_P1] >= SAVED_DATA_NUM_PAGES) { + ISOException.throwIt(ISO7816.SW_WRONG_DATA); + } + + // Copy the page + Util.arrayCopyNonAtomic(savedData, + (short) (apduBuffer[ISO7816.OFFSET_P1] * SAVED_DATA_PAGE_SZ), + apduBuffer, + (short) SecureChannel.SC_OUT_OFFSET, + SAVED_DATA_PAGE_SZ); + secureChannel.respond(apdu, SAVED_DATA_PAGE_SZ, ISO7816.SW_NO_ERROR); } /**