Skip to content

Commit

Permalink
Updates LOAD_DATA and EXPORT_DATA APDUs
Browse files Browse the repository at this point in the history
There were two bugs that needed fixing:
1. Return data offset was incorrect (`EXPORT_DATA`)
2. Data size was too large to use in a single APDU
Here we introduce paged data with correct offsets.
Closes #56
  • Loading branch information
alex-miller-0 committed Jun 22, 2022
1 parent d41f240 commit 45e4f5a
Showing 1 changed file with 34 additions and 13 deletions.
47 changes: 34 additions & 13 deletions src/main/java/im/status/keycard/KeycardApplet.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}

Expand All @@ -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);
}

/**
Expand Down

0 comments on commit 45e4f5a

Please sign in to comment.