HostApduService
declares processCommandApdu()
method that the developer needs to implement. The method processCommandApdu()
is called when the service receives an Application Protocol Data Unit (APDU) sent by contactless/NFC reader. The developer needs to implement how to process APDUs specific to the card application(s) the service supports. The implementation can become complex if the service supports multiple card applications.SimplyTapp SDK provides a robust architecture where each card application is implemented in a "card agent". Using SimplyTapp SDK, there is no need for the developer to implement a complex
processCommandApdu()
method for a service that supports multiple card applications. Instead the developer implements process()
method in the card agent for each card application. A mobile application can support multiple card agents and card agents can be remotely deployed to the mobile application, similar to remotely deploying Java Card applet (aka cardlet) to the Secure Element. Essentially SimplyTapp SDK provides an architecture that is familiar to Java Card developers.I will describe how the card agent processes different APDU cases as defined in ISO/IEC 7816-4 specification.
ISO 7816-4 Case 1 | No Lc No Le Example: 80100000 |
ISO 7816-4 Case 2 | No Lc 1-byte Le (1 to 256, Le=0x00 represents 256) Example: 8012000000 |
ISO 7816-4 Case 3 | 1-byte Lc (1 to 255) No Le Example: 80140000081112131415161718 |
ISO 7816-4 Case 4 | 1-byte Lc (1 to 255) 1-byte Le (1 to 256, Le=0x00 represents 256) Example: 8016000008212223242526272800 |
Here is sample code for card agent implementation that demonstrates how to process the example APDUs. The class extends
com.simplytapp.virtualcard.Agent
. import javacard.framework.APDU;
import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import com.simplytapp.virtualcard.Agent;
public class CardAgent extends Agent {
public void process(APDU apdu) throws ISOException {
byte[] apduBuffer = apdu.getBuffer();
byte claByte = apduBuffer[ISO7816.OFFSET_CLA];
byte insByte = apduBuffer[ISO7816.OFFSET_INS];
if (claByte == (byte) 0x80) {
if (insByte == (byte) 0x10) {
// Process case 1 command APDU.
processCase1Apdu();
// Return SW=0x9000.
}
else if (insByte == (byte) 0x12) {
// Process case 2 command APDU.
// Copy response data to APDU buffer.
System.arraycopy(responseData, 0,
apduBuffer, 0, responseData.length);
short le = apdu.setOutgoing();
if (responseData.length > le) {
le = (short) responseData.length;
}
apdu.setOutgoingLength(le);
apdu.sendBytes((short) 0, le);
// Return response data with SW=0x9000.
}
else if (insByte == (byte) 0x14) {
// Process case 3 command APDU.
short lc = apdu.setIncomingAndReceive();
// Save command data from APDU buffer.
System.arraycopy(apduBuffer, ISO7816.OFFSET_CDATA,
commandData, 0, lc);
// Return SW=0x9000.
}
else if (insByte == (byte) 0x16) {
// Process case 4 command APDU.
short lc = apdu.setIncomingAndReceive();
// Save command data from APDU buffer.
System.arraycopy(apduBuffer, ISO7816.OFFSET_CDATA,
commandData, 0, lc);
// Copy response data to APDU buffer.
System.arraycopy(responseData, 0,
apduBuffer, 0, responseData.length);
short le = apdu.setOutgoing();
if (responseData.length > le) {
le = (short) responseData.length;
}
apdu.setOutgoingLength(le);
apdu.sendBytes((short) 0, le);
// Return response data with SW=0x9000.
}
}
}
}
The card agent can also process extended length APDUs as defined in ISO/IEC 7816-4 specification. The maximum supported length using extended length is 32767 to be consistent with Java Card API.
ISO 7816-4 Case 1 | Not Applicable |
ISO 7816-4 Case 2 | No Lc 3-byte Le (1 to 32767, Le=0x000000 represents 32767) Example: 80120000000000 |
ISO 7816-4 Case 3 | 3-byte Lc (1 to 32767) No Le Example: 801400000000081112131415161718 |
ISO 7816-4 Case 4 | 3-byte Lc (1 to 32767) 2-byte Le (1 to 32767, Le=0x0000 represents 32767) Example: 8016000000000821222324252627280000 |
Here is sample code for card agent implementation that demonstrates how to process the example extended length APDUs. The class extends
com.simplytapp.virtualcard.Agent
and implements javacardx.apdu.ExtendedLength
. import javacard.framework.APDU;
import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import javacardx.apdu.ExtendedLength;
import com.simplytapp.virtualcard.Agent;
public class CardAgent extends Agent implements ExtendedLength {
public void process(APDU apdu) throws ISOException {
byte[] apduBuffer = apdu.getBuffer();
byte claByte = apduBuffer[ISO7816.OFFSET_CLA];
byte insByte = apduBuffer[ISO7816.OFFSET_INS];
if (claByte == (byte) 0x80) {
if (insByte == (byte) 0x12) {
// Process case 2 command APDU.
// Copy response data to APDU buffer.
System.arraycopy(responseData, 0,
apduBuffer, 0, responseData.length);
short le = apdu.setOutgoing();
if (responseData.length > le) {
le = (short) responseData.length;
}
apdu.setOutgoingLength(le);
apdu.sendBytes((short) 0, le);
// Return response data with SW=0x9000.
}
else if (insByte == (byte) 0x14) {
// Process case 3 command APDU.
short receivedLen = apdu.setIncomingAndReceive();
// Save command data from APDU buffer.
System.arraycopy(apduBuffer, apdu.getOffsetCdata(),
commandData, 0, receivedLen);
short lc = apdu.getIncomingLength();
// Check if additional command data not yet received.
if (receivedLen != lc) {
short commandDataOffset = receivedLen;
// Receive more command data until no more available.
receivedLen = apdu.receiveBytes((short) 0);
while (receivedLen != 0) {
// Save additional command data from APDU buffer.
System.arraycopy(apduBuffer, 0,
commandData, commandDataOffset, receivedLen);
commandDataOffset += receivedLen;
receivedLen = apdu.receiveBytes((short) 0);
}
}
// Return SW=0x9000.
}
else if (insByte == (byte) 0x16) {
// Process case 4 command APDU.
short receivedLen = apdu.setIncomingAndReceive();
// Save command data from APDU buffer.
System.arraycopy(apduBuffer, apdu.getOffsetCdata(),
commandData, 0, receivedLen);
short lc = apdu.getIncomingLength();
// Check if additional command data not yet received.
if (receivedLen != lc) {
short commandDataOffset = receivedLen;
// Receive more command data until no more available.
receivedLen = apdu.receiveBytes((short) 0);
while (receivedLen != 0) {
// Save additional command data from APDU buffer.
System.arraycopy(apduBuffer, 0,
commandData, commandDataOffset, receivedLen);
commandDataOffset += receivedLen;
receivedLen = apdu.receiveBytes((short) 0);
}
}
// Copy response data to APDU buffer.
System.arraycopy(responseData, 0,
apduBuffer, 0, responseData.length);
short le = apdu.setOutgoing();
if (responseData.length > le) {
le = (short) responseData.length;
}
apdu.setOutgoingLength(le);
apdu.sendBytes((short) 0, le);
// Return response data with SW=0x9000.
}
}
}
}