What are APDUs - Application Protocol Data Units explained
Updated November 26, 2023 06:16
If you're working with NFC, you likely have heard the term APDU. APDUs are used by NFC tags of all types, including the wallet passes with NFC on your Android device or Apple device.
Application Protocol Data Unit (APDU) is a standardized data format and communication protocol used in the interaction between smart cards - in our case, NFC tags - and card readers or host systems. APDUs define how data and commands are exchanged between these devices, enabling transactions and data retrieval in a secure and structured manner. You can think of them kind of like the HTTP protocol, where you make a request, and get a response, but with a diff set of specifications for how requests are made and responses are parsed.
In this guide we're going to go over the different kinds of APDUs, explain the different kinds Command APDUs and show you an example command that is very common.
A Command APDU is sent by the host system (e.g., card reader or application) to the smart card to request a specific action or operation. Command APDUs are always sent in bytes that are hexadecimal characters. It consists of the following components:
- Class Byte (CLA): Specifies the command class and operation category.
- Instruction Byte (INS): Identifies the specific command or operation to be performed.
- Parameter 1 Byte (P1): Optional data or parameters needed for the command.
- Parameter 2 Byte (P2): Optional data or parameters needed for the command.
- Length Expected (LE): Specifies the expected length of the response data.
There are two kinds of Command APDUs, the first is commonly known as an "Escape command" and usually sent to control the hardware (beep buzzers, flash leds, get device info, send configuration data, etc). They typically start with
0xE byte. Here is an example that should return the firmware version on an ACS WalletMate:
The other kind of Command APDU is for collecting data from the smart card, aka PICC, aka NFC tag. It's typically sent to the reader, and forwarded on to the tag or target that is in field. Here is an example that should get a tags UID if it's in the field:
A Response APDU is sent by the smart card in response to a Command APDU. It contains the results of the requested operation or command. A Response APDU is also sent in bytes that are presented in hexidecimal digits and consist of the following components:
- Data Field: Contains the response data, such as the requested information or the results of the command.
- Status Word (SW1, SW2): Consists of two bytes that provide information about the status of the operation (e.g., success, error, warnings).
The status word codes are typically provided by the hardware manufacturer. For example, here is a table of status word bytes from ACS for their ACR1252U device (aka ACS WalletMate):
Issuing a command
You can issue APDUs over many different mediums, including bluetooth, NFC and USB. For our example, we'll be issuing APDUs over USB using pyscard. To do this, you'll need to install pcsc-lite or in some cases, a driver from the hardware manufacturer:
brew install pcsc-lite
Next you'll need the pyscard python package. You only need to run one of these:
python3 -m pip install pyscard pip3 install pyscard
We'll grab the exact APDU from the manufacturer's manual. Here it is:
Now plug in your reader and put an NFC tag against it until you hear a beep. Now you can run this script which grabs the firmware version and prints it out in ASCII:
from smartcard.scard import ( SCardGetErrorMessage, SCARD_SCOPE_USER, SCARD_S_SUCCESS, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0, SCARD_PROTOCOL_T1, SCARD_CTL_CODE, SCardEstablishContext, SCardListReaders, SCardConnect, SCardControl ) hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) assert hresult == SCARD_S_SUCCESS hresult, readers = SCardListReaders(hcontext, ) assert len(readers) > 0 reader = readers hresult, hcard, dwActiveProtocol = SCardConnect(hcontext, reader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1) apdu_cmd = [0xE0, 0x00, 0x00, 0x18, 0x00] hresult, response = SCardControl(hcard, SCARD_CTL_CODE(3500), apdu_cmd) if hresult != SCARD_S_SUCCESS: print(SCardGetErrorMessage(hresult)) print(''.join(map(lambda x: chr(x), response[1:])))
You should now understand the basics of what APDUs are, how to send Command APDUs and receive Response APDUs, a couple common Command APDUs as well as have a working example of managing APDUs with pyscard.
If you have any feedback on this article, let us know! Email email@example.com