How to Build APDU Commands — C-APDU Construction Walkthrough
Every interaction with a smart card is an APDU (Application Protocol Data Unit) exchange. The command APDU (C-APDU) is a 4-byte header + optional body that tells the card what to do. This guide covers CLA/INS/P1/P2 byte selection, Lc/Le encoding, Case 1-4 structures, extended APDU, and practical command recipes for common smart card operations.
1. C-APDU Structure — The 4 Cases
ISO 7816-4 defines four C-APDU cases based on whether data is sent (Lc) and/or expected back (Le):
| Case | Structure | Lc | Le | Example |
|---|---|---|---|---|
| 1 | CLA INS P1 P2 | No | No | SELECT MF (implicit Le=0) |
| 2 | CLA INS P1 P2 Le | No | Yes | READ BINARY with expected length |
| 3 | CLA INS P1 P2 Lc Data | Yes | No | VERIFY PIN with no response |
| 4 | CLA INS P1 P2 Lc Data Le | Yes | Yes | SELECT by AID with response |
2. CLA Byte — Class Byte
CLA byte structure (ISO 7816-4): Bit 7-6: Interindustry flags Bit 5: Command chaining (0=last/no chain, 1=more commands follow) Bit 4-3: Secure messaging flags Bit 2-0: Logical channel number (0-3) Common CLA values: 0x00 — ISO standard, no SM, no chaining, channel 0 0x04 — ISO standard, chaining, no SM, channel 0 0x0C — ISO standard, SM, channel 0 0x80 — GP (GlobalPlatform) command 0x84 — GP with chaining 0xB0 — EMV payment system command (always P1=0x00, P2=0x00)
3. INS Byte — Instruction Byte
| INS | Name | Description | Typical Use |
|---|---|---|---|
| 0xA4 | SELECT | Select file/applet by DF name or FID | Every session starts here |
| 0xB0 | READ BINARY | Read transparent/linear EF data | Read ICCID, IMSI, PAN |
| 0xD6 | UPDATE BINARY | Write to transparent EF | Update card data records |
| 0xB2 | READ RECORD | Read linear/circular EF record | Read EMV AFL records |
| 0x20 | VERIFY | Submit PIN/password | PIN verification (P2=CHV reference) |
| 0x84 | GET CHALLENGE | Request random number for auth | Mutual authentication start |
| 0x88 | INTERNAL AUTHENTICATE | Card signs/encrypts challenge | Card authentication proof |
| 0x82 | EXTERNAL AUTHENTICATE | Terminal authenticates to card | GP secure channel init |
| 0xC0 | GET RESPONSE | Retrieve data from T=0 card | After 61XX status (T=0 protocol) |
4. Building Common APDU Commands
SELECT — Select an Application
# SELECT by AID (Application Identifier) # CLA=0x00, INS=0xA4, P1=0x04 (by DF name), P2=0x00 aid = [0xA0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10] # Visa AID SELECT_AID = [0x00, 0xA4, 0x04, 0x00, len(aid)] + aid + [0x00] # → [00 A4 04 00 07 A0 00 00 00 03 10 10 00] # SELECT MF (Master File) SELECT_MF = [0x00, 0xA4, 0x00, 0x00, 0x02, 0x3F, 0x00] # SELECT by FID (2-byte file ID) # P1=0x00 selects MF/DF/EF by file identifier SELECT_FILE = [0x00, 0xA4, 0x00, 0x00, 0x02, 0x2F, 0xE2] # EF_ICCID
READ BINARY — Read File Data
# READ BINARY on currently selected EF # P1P2 = offset (15 bits available, top bit reserved) # Le = bytes to read (0x00 = read 256 bytes in short APDU) READ_10_BYTES = [0x00, 0xB0, 0x00, 0x00, 0x0A] # Offset 0, 10 bytes READ_OFFSET_50 = [0x00, 0xB0, 0x00, 0x32, 0x10] # Offset 50, 16 bytes # Extended APDU (>256 bytes): # Offset = 0x000200 (512), Le = 0x0100 (256 bytes) READ_EXTENDED = [0x00, 0xB0, 0x02, 0x00, 0x00, 0x01, 0x00] # CLA INS P1 P2 00(Lc field) Le_hi Le_lo
VERIFY — Submit PIN
# VERIFY CHV1 (P2 = 0x01 for basic PIN, 0x80 for GP PIN) pin = "1234" VERIFY_PIN = [0x00, 0x20, 0x00, 0x01, len(pin)] + [ord(c) for c in pin] # → [00 20 00 01 04 31 32 33 34]
5. Extended APDU — Handling >256 Bytes
Standard APDU uses 1-byte Lc and Le (max 255). Extended APDU uses 3 bytes for each, supporting up to 65535 bytes:
# Standard (short) APDU: CLA INS P1 P2 [Lc] [Data] [Le] # Extended APDU: CLA INS P1 P2 00 Lc_hi Lc_lo [Data] Le_hi Le_lo # SELECT with extended Le: SELECT_EXT = [0x00, 0xA4, 0x04, 0x00, 0x07] + aid + [0x00, 0x00, 0x00] # CLA INS P1 P2 Lc Data 00(Lc empty) Le=0x000000 # Card indicates extended APDU support in ATR (interface bytes TA2/TB2)
6. CLA/INS Quick Reference for Common Smart Cards
| Card Type | CLA Range | Notable INS Values |
|---|---|---|
| ISO 7816-4 standard | 0x00, 0x04, 0x0C | Same as table above |
| GlobalPlatform (GP) | 0x80, 0x84 | DELETE 0xE4, INSTALL 0xE6, PUT KEY 0xD8, STORE DATA 0xE2 |
| EMV Payment | 0x80, 0xB0 | GET PROCESSING OPTIONS 0xA8, READ RECORD 0xB2, GENERATE AC 0xAE, GET DATA 0xCA |
| MIFARE DESFire | 0x90 | GET VERSION 0x60, CREATE APPLICATION 0xCA, GET FILE IDS 0x6F |
| PIV / CAC | 0x00 | GET DATA 0xCB, GENERAL AUTHENTICATE 0x87 (P1=alg, P2=key ref) |
7. Command Chaining
When command data exceeds 255 bytes (and extended APDU isn't supported), split across multiple commands:
# Chain 500-byte UPDATE BINARY across 3 commands: CHUNK1 = [0x10, 0xD6, 0x00, 0x00, 0xFF] + data[0:255] # CLA bit5=1: more follows CHUNK2 = [0x10, 0xD6, 0x00, 0x00, 0xFF] + data[255:510] # Last chunk: CLA bit5=0 CHUNK3 = [0x00, 0xD6, 0x00, 0x00, len(data[510:])] + data[510:] # First 2 have CLA=0x10 (chaining), last has CLA=0x00 (end of chain)
Related Tools
APDU Command Builder — Visual APDU construction | APDU Response Debugger — Decode SW1 SW2 | APDU Quick Reference — All ISO 7816-4 instructions | ISO 7816 Protocol Reference