add NDEF applet

This commit is contained in:
Michele Balistreri 2018-10-08 13:18:55 +02:00
parent 7e11327f69
commit f71286e7d1
2 changed files with 152 additions and 0 deletions

View File

@ -23,6 +23,10 @@ javacard {
aid = '0x53:0x74:0x61:0x74:0x75:0x73:0x57:0x61:0x6c:0x6c:0x65:0x74:0x41:0x70:0x70'
className = 'WalletApplet'
}
applet {
aid = '0x53:0x74:0x61:0x74:0x75:0x73:0x57:0x61:0x6c:0x6c:0x65:0x74:0x4e:0x46:0x43'
className = 'NDEFApplet'
}
version = '1.2'
}
}
@ -64,6 +68,7 @@ task install(type: Exec) {
install_for_load -pkgAID 53746174757357616C6C6574
load -file build/javacard/im/status/wallet/javacard/wallet.cap
send_apdu -sc 1 -APDU 80E60C005F0C53746174757357616C6C65740F53746174757357616C6C65744170700F53746174757357616C6C657441707001002EC92C313233343536373839303132e929d425d7f73c2a0a24ffefad87b65e9b2ee96603eab34d64088b5aae2a026f00
install_for_install -AID 53746174757357616C6C65744e4643 -pkgAID 53746174757357616C6C6574 -instAID D2760000850101
card_disconnect
release_context
"""

View File

@ -0,0 +1,147 @@
package im.status.wallet;
import javacard.framework.*;
/**
* The applet's main class. All incoming commands a processed by this class.
*/
public class NDEFApplet extends Applet {
private static final byte INS_READ_BINARY = (byte) 0xb0;
private static final short FILEID_NONE = (short) 0xffff;
private static final short FILEID_NDEF_CAPS = (short) 0xe103;
private static final short FILEID_NDEF_DATA = (short) 0xe104;
private static final byte SELECT_P1_BY_FILEID = (byte) 0x00;
private static final byte SELECT_P2_FIRST_OR_ONLY = (byte) 0x0c;
private static final short NDEF_READ_SIZE = (short) 0xff;
private static final byte[] NDEF_DATA_FILE = {
(byte) 0x00, (byte) 0x24, (byte) 0xd4, (byte) 0x0f, (byte) 0x12, (byte) 0x61, (byte) 0x6e, (byte) 0x64,
(byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x2e, (byte) 0x63, (byte) 0x6f, (byte) 0x6d,
(byte) 0x3a, (byte) 0x70, (byte) 0x6b, (byte) 0x67, (byte) 0x69, (byte) 0x6d, (byte) 0x2e, (byte) 0x73,
(byte) 0x74, (byte) 0x61, (byte) 0x74, (byte) 0x75, (byte) 0x73, (byte) 0x2e, (byte) 0x65, (byte) 0x74,
(byte) 0x68, (byte) 0x65, (byte) 0x72, (byte) 0x65, (byte) 0x75, (byte) 0x6d
};
private static final byte[] NDEF_CAPS_FILE = {
(byte) 0x00, (byte) 0x0f, (byte) 0x20, (byte) 0x00, (byte) 0xff, (byte) 0x00, (byte) 0x01, (byte) 0x04,
(byte) 0x06, (byte) 0xe1, (byte) 0x04, (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0xff
};
private short selectedFile;
/**
* Invoked during applet installation. Creates an instance of this class. The installation parameters are passed in
* the given buffer.
*
* @param bArray installation parameters buffer
* @param bOffset offset where the installation parameters begin
* @param bLength length of the installation parameters
*/
public static void install(byte[] bArray, short bOffset, byte bLength) {
new NDEFApplet(bArray, bOffset, bLength);
}
/**
* Application constructor. All memory allocation is done here. The reason for this is two-fold: first the card might
* not have Garbage Collection so dynamic allocation will eventually eat all memory. The second reason is to be sure
* that if the application installs successfully, there is no risk of running out of memory because of other applets
* allocating memory. The constructor also registers the applet with the JCRE so that it becomes selectable.
*
* @param bArray installation parameters buffer
* @param bOffset offset where the installation parameters begin
* @param bLength length of the installation parameters
*/
public NDEFApplet(byte[] bArray, short bOffset, byte bLength) {
register(bArray, (short) (bOffset + 1), bArray[bOffset]);
}
/**
* This method is called on every incoming APDU. This method is just a dispatcher which invokes the correct method
* depending on the INS of the APDU.
*
* @param apdu the JCRE-owned APDU object.
* @throws ISOException any processing error
*/
public void process(APDU apdu) throws ISOException {
if (selectingApplet()) {
selectedFile = FILEID_NONE;
return;
}
byte[] apduBuffer = apdu.getBuffer();
switch (apduBuffer[ISO7816.OFFSET_INS]) {
case ISO7816.INS_SELECT:
processSelect(apdu);
break;
case INS_READ_BINARY:
processReadBinary(apdu);
break;
default:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
break;
}
}
private void processSelect(APDU apdu) {
byte[] apduBuffer = apdu.getBuffer();
apdu.setIncomingAndReceive();
if(apduBuffer[ISO7816.OFFSET_P1] != SELECT_P1_BY_FILEID || apduBuffer[ISO7816.OFFSET_P2] != SELECT_P2_FIRST_OR_ONLY) {
ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
} else if (apduBuffer[ISO7816.OFFSET_LC] != 2) {
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
}
short fid = Util.getShort(apduBuffer, ISO7816.OFFSET_CDATA);
switch(fid) {
case FILEID_NDEF_CAPS:
case FILEID_NDEF_DATA:
selectedFile = fid;
break;
default:
ISOException.throwIt(ISO7816.SW_FILE_NOT_FOUND);
break;
}
}
private void processReadBinary(APDU apdu) {
byte[] apduBuffer = apdu.getBuffer();
byte[] output;
switch(selectedFile) {
case FILEID_NDEF_CAPS:
output = NDEF_CAPS_FILE;
break;
case FILEID_NDEF_DATA:
output = NDEF_DATA_FILE;
break;
default:
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
return;
}
short offset = Util.getShort(apduBuffer, ISO7816.OFFSET_P1);
if (offset < 0 || offset >= (short) output.length) {
ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
}
short le = apdu.setOutgoingNoChaining();
if (le > NDEF_READ_SIZE) {
le = NDEF_READ_SIZE;
}
if((short)(offset + le) >= (short) output.length) {
le = (short)(((short) output.length) - offset);
}
apdu.setOutgoingLength(le);
apdu.sendBytesLong(output, offset, le);
}
}