Logical Channels in ISO/IEC 7816-4
ISO/IEC 7816-4 defines logical channels as a mechanism to run multiple independent sessions on a single physical smart card connection. Each channel maintains its own state:
- Currently selected application (DF – Dedicated File)
- Current file (EF – Elementary File)
- Security status (e.g., PIN verified, keys in use)
This is essential for multi-application cards (common in eID, health cards, or credential systems) where you might need to access identity data and a signing certificate at the same time without deselecting one to use the other.
Key facts
- Channel 0 → basic channel, always open at card reset (ATR), cannot be closed.
- Additional channels → typically 1–3 (total 4 channels); some modern/JavaCard implementations support up to 19.
- Channels are independent → no interleaving of command-response pairs across channels (you finish one command before the next, but you can switch channels between commands).
- Support level → indicated by the card (e.g., in ATR historical bytes or card capabilities data objects).
How the channel number is encoded (most common case – channels 0–3)
The channel number is in the CLA byte of every APDU, in bits b2 and b1:
| Channel | b2 b1 | CLA example (standard interindustry, no SM/chaining) |
|---|---|---|
| 0 | 00 | 0x00 |
| 1 | 01 | 0x01 |
| 2 | 10 | 0x02 |
| 3 | 11 | 0x03 |
To switch channel in code: cla = (base_cla & 0xFC) | channel_number (clears low bits then ORs the number).
Opening and closing channels
Two ways:
- Implicit (most common): Send any command (usually SELECT) with CLA indicating a closed channel → the card opens it automatically if supported.
- Explicit: Use the MANAGE CHANNEL command (INS = 0x70).
MANAGE CHANNEL details
- Open (P1 = 0x00):
- P2 = 0x00 → card assigns an available channel and returns it.
- P2 = 0x01–0x03 → request a specific channel.
- Le = 0x01 if P2=0x00 (to get the assigned number), absent otherwise.
- Close (P1 = 0x80):
- P2 = channel number to close (bits b2 b1).
Practical examples (APDUs in hex)
- Explicit open, card assigns (sent on channel 0):
Command: 00 70 00 00 01
Response: 02 90 00 ← card assigned channel 2 (data byte = 02)
- Explicit open, request channel 1:
Command: 00 70 00 01
Response: 90 00 ← success, no data needed
- Use the channel – SELECT an application on channel 1:
Command: 01 A4 04 00 0A A0 00 00 00 63 50 4B 43 53 2D 31 35 ← example PKSC#15 AID
- Close channel 1 (sent on channel 0):
Command: 00 70 80 01
Response: 90 00
- Implicit open example – directly open channel 2 and select:
Command: 02 A4 04 00 ... (AID)
Quick code snippet (Python-like with pcsc/pyscard style)
def send_on_channel(conn, base_cla, ins, p1, p2, data=b'', le=None, channel=0):
cla = (base_cla & 0xFC) | channel # set channel in low bits
apdu = bytes([cla, ins, p1, p2]) + len_field + data + le_field
response, sw1, sw2 = conn.transmit(apdu)
return response, sw1, sw2
# Example: open channel 1 implicitly by selecting on it
send_on_channel(conn, 0x00, 0xA4, 0x04, 0x00, data=aid, channel=1)
This gives you concurrent access – perfect for credential systems where one channel can stay authenticated to an identity app while another performs operations in a different app. Tomorrow you can build on this (e.g., how security status can be shared or isolated across channels).