Smart Card ATR (Answer-to-Reset) — Developer Guide
When a smart card is powered on, it sends an Answer-to-Reset (ATR) — a sequence of bytes that tells the reader everything about the card's communication capabilities: which protocol it speaks (T=0 or T=1), what voltage it supports, what clock frequency it expects, and who manufactured it. If you're developing smart card readers, writing card drivers, or debugging card communication failures, understanding the ATR is the first step.
ATR Structure at a Glance
An ATR is a variable-length byte string (2–33 bytes) with this structure:
| Field | Bytes | Description |
|---|---|---|
| TS | 1 | Initial character — direct (3B) or inverse (3F) convention |
| T0 | 1 | Format byte — indicates presence of TA1/TB1/TC1/TD1 + historical byte count |
| TAi TBi TCi TDi | 0–15 | Interface bytes (protocol parameters) |
| Historical | 0–15 | Card-specific data (manufacturer, OS version, serial, etc.) |
| TCK | 0–1 | Check byte (XOR of all bytes from T0 onward; present only if T=1 or multi-protocol) |
1. TS — Initial Character
The very first byte of the ATR tells the reader which bit convention the card uses:
| Value | Convention | Description |
|---|---|---|
3B | Direct | Most significant bit first (standard). Used by >95% of cards. |
3F | Inverse | Least significant bit first (rare, some legacy cards). |
3B, you're reading it correctly. If it starts with 3F, the card uses inverse convention and all subsequent bytes must be interpreted with reversed bit order. Most modern readers handle this transparently.2. T0 — Format Byte
T0 is the second byte and encodes two things:
T0 = Y1 || K
\____/ \_/
bits 7-4 bits 3-0
Y1 (upper nibble): Bit flags indicating which interface bytes follow
bit 7 = 1 → TA1 present
bit 6 = 1 → TB1 present
bit 5 = 1 → TC1 present
bit 4 = 1 → TD1 present
K (lower nibble): Number of historical bytes (0–15)
Example
If T0 = 95 (binary 10010101):
- Upper nibble =
1001→ TA1 present, TD1 present (bits 7 and 4 are set) - Lower nibble =
0101→ K = 5 historical bytes
3. Interface Bytes (TAi, TBi, TCi, TDi)
Interface bytes carry protocol parameters. The first set (TA1/TB1/TC1/TD1) is indicated by T0. Each subsequent TDi byte indicates whether more interface bytes follow for protocol i+1.
TA1 — Fi, Di (Clock and Bit Rate)
TA1 = Fi || Di
\_/ \_/
bits 7-4 bits 3-0
Fi: Clock rate integer (lookup table → f(max) in MHz)
Di: Bit rate adjustment (lookup table → D value)
| Fi | f(max) MHz | Di | D |
|---|---|---|---|
| 0 | Reserved | 0 | Reserved |
| 1 | 5 | 1 | 1 |
| 2 | 6 | 2 | 2 |
| 3 | 8 | 3 | 4 |
| 4 | 12 | 4 | 8 |
| 5 | 16 | 5 | 16 |
| 6 | 20 | 6 | 32 |
| 7 | Reserved | 8 | 12 |
| 9 | 25 | 9 | 20 |
The effective bit rate is: bit_rate = D × f(clock) / (F × 8), where F is from the Fi table (e.g., F1=372, F2=558, F3=744, F4=1116, F5=1488, F6=1860, F9=512).
TB1 — Vpp Programming Voltage (Deprecated)
TB1 carries programming voltage parameters (Vpp). In modern cards (ISO 7816-3:2006+), TB1 is typically 00 or absent — Vpp is no longer used. If present, it encodes PI1 (programming voltage) and II (maximum programming current).
TC1 — Extra Guard Time (N)
TC1 specifies the extra guard time N added between consecutive characters. A value of FF means the guard time is reduced to the minimum (11 etu for T=0, 12 etu for T=1). Most cards send TC1=00 (no extra guard time) or FF (reduced guard time).
TD1 — Protocol Type + Next Interface Bytes
TDi = Yi+1 || T
\_____/ \_/
bits 7-4 bits 3-0
Yi+1: Bit flags for next interface bytes (same format as T0 upper nibble)
T: Protocol type
0 = T=0 (byte-oriented, half-duplex)
1 = T=1 (block-oriented, full-duplex)
2-13 = Reserved / RFU
14 = Vendor-specific
15 = Not applicable
Subsequent Interface Bytes (TAi/TBi/TCi/TDi for i ≥ 2)
| Byte | Protocol | Meaning |
|---|---|---|
| TA2 | T=0 | Specific mode indication (if present, card is in specific mode) |
| TA2 | T=1 | IFSC (Information Field Size for the Card) — max 254 |
| TB2 | T=1 | BWI (Block Waiting Time Integer) || CWI (Character Waiting Time Integer) |
| TC2 | T=0 | WWT (Waiting Time Integer) for T=0 |
| TD2 | Any | Protocol for next group + Yi+3 flags |
4. Historical Bytes
The K historical bytes (K from T0) contain card-specific information. There is no standard format, but common conventions exist:
ISO 7816-4 Category Indicator
If the first historical byte is:
| Value | Category | Remaining Bytes |
|---|---|---|
80 | Compact TLV | One status byte + compact TLV data |
00 | Not compact TLV | Remaining bytes are card issuer proprietary |
| Other | Not compact TLV | Card issuer proprietary |
Common Compact TLV Tags
| Tag | Meaning | Length |
|---|---|---|
| 0x31 | Country code (ISO 3166-1 numeric) | 1–2 |
| 0x32 | Issuer identification number | Variable |
| 0x33 | Card service data | 1 |
| 0x34 | Initial access data | Variable |
| 0x35 | Card issuer data | Variable |
| 0x36 | Pre-issuing data | Variable |
| 0x50 | Application identifier (AID) | Variable |
| 0x7F | Status indicator (with sub-tags) | Variable |
Java Card Historical Bytes
Java Card applets typically include the Java Card AID (e.g., A000000062) or a GlobalPlatform AID (e.g., A0000001510000) in the historical bytes.
5. TCK — Check Byte
TCK is the XOR of all ATR bytes from T0 to the last byte before TCK:
TCK = T0 XOR TA1 XOR TB1 XOR ... XOR TDi XOR Historical[0] XOR ... XOR Historical[K-1]
TCK is present only when the ATR indicates T=1 or multiple protocols. If the card uses only T=0 and no TD1 byte is present, TCK is absent.
00. If it's not, the ATR is corrupted.6. Real-World ATR Examples
Example 1: Basic T=0 SIM Card
ATR: 3B 9A 00 92 01 92 44 53 49 4D 32 34 82
Byte Value Description
TS 3B Direct convention
T0 9A Y1=1001 (TA1+TD1), K=10 historical bytes
TA1 00 Fi=0/Di=0 → default (372/1 = 9600 bps at 3.5795 MHz)
TD1 92 Y2=1001 (TA2+TD2), T=2... wait
Let me use a cleaner example:
ATR: 3B 16 94 10 10 92 00 00
TS 3B Direct convention
T0 16 Y1=0001 (TA1 present), K=6 historical bytes
TA1 94 Fi=9/Di=4 → F=512, D=8 → 25 MHz / high speed
Hist 10 10 92 00 00 → 5 historical bytes (manufacturer data)
Example 2: Java Card with T=0 + T=1
ATR: 3B 7F 96 00 00 80 31 80 65 B0 83 41 3D F6
TS 3B Direct convention
T0 7F Y1=0111 (TA1+TB1+TC1), K=15 historical bytes
TA1 96 Fi=9/Di=6 → F=512, D=32 → high-speed
TB1 00 No Vpp
TC1 00 No extra guard time
Hist 00 80 31 80 65 B0 83 41 3D F6
→ 80 = compact TLV category
→ 31 80 → Country code tag, no data
→ 65 B0 → Card service data
→ 83 41 3D → Status info
TCK F6 Check byte (XOR of T0..last historical = F6)
Example 3: NXP JCOP Card (T=1)
ATR: 3B F5 96 00 00 80 31 FE 45 4A 43 4F 50 33 02
TS 3B Direct convention
T0 F5 Y1=1111 (TA1+TB1+TC1+TD1), K=5 historical bytes
TA1 96 Fi=9/Di=6
TB1 00 No Vpp
TC1 00 No extra guard time
TD1 80 Y2=1000 (TD2 present), T=0 (first protocol is T=0)
TD2 31 Y3=0011 (TA3+TB3), T=1 (second protocol is T=1)
TA3 FE IFSC = 254 (max T=1 information field size)
TB3 45 BWI=4, CWI=5 → BWT=4.6s, CWT=8.1ms
Hist 4A 43 4F 50 33 → "JCOP3" (Java Card Open Platform 3)
TCK 02 Check byte
7. Protocol Selection
When a card supports multiple protocols (e.g., T=0 and T=1 via TD1/TD2), the reader must decide which to use:
- Default protocol: The first protocol indicated by TD1. If TD1 is absent, the default is T=0.
- Negotiable mode: If TA2 is absent, the card is in negotiable mode. The reader can request a protocol change using PPS (Protocol and Parameter Selection).
- Specific mode: If TA2 is present, the card is in specific mode and will only use the protocol indicated by TA2.
PPS (Protocol and Parameter Selection)
Reader → Card: PPSS || PPS0 || [PPS1] || [PPS2] || [PPS3] || PPS_END
Card → Reader: PPSS || PPS0 || [PPS1] || [PPS2] || [PPS3] || PPS_END
PPSS = 0xFF (PPS start byte)
PPS0 = format byte (like T0, indicates which PPS1/2/3 follow + protocol T)
PPS1 = TA1 value (Fi/Di to negotiate)
PPS2 = specific parameters
PPS3 = specific parameters
PPS_END = 0xFF (not always required)
If the card returns the same PPS → accepted. If error → rejected.
8. Common ATR Patterns
| ATR Prefix | Card Type | Notes |
|---|---|---|
3B 00 | Basic T=0 | Minimal ATR, no interface bytes, no historical bytes |
3B 9x | Standard T=0 | T=0 with interface bytes, common for SIM/USIM |
3B 7F | Multi-protocol | T=0 + T=1, Java Card / GlobalPlatform |
3B Fx | T=0 + T=1 | Full interface byte set, often JCOP cards |
3B 90 | T=0 only | TA1 present, no TB1/TC1/TD1 |
3B A5 | Extended | TA1+TC1 present, K=5 |
Famous Historical Byte Patterns
| Historical Bytes | ASCII | Card |
|---|---|---|
4A 43 4F 50 33 | JCOP3 | NXP JCOP 3 Java Card |
44 53 49 4D | DSIM | GSM SIM card |
55 53 49 4D | USIM | 3G/4G USIM card |
49 53 44 | ISD | GlobalPlatform Issuer Security Domain |
56 49 53 41 | VISA | Visa EMV card |
4D 43 | MC | Mastercard EMV card |
9. Python ATR Parser
def parse_atr(hex_str: str) -> dict:
"""Parse an ATR hex string into its components."""
atr = bytes.fromhex(hex_str.replace(" ", ""))
idx = 0
result = {}
# TS
result["TS"] = atr[idx]
result["convention"] = "direct" if atr[idx] == 0x3B else "inverse"
idx += 1
# T0
t0 = atr[idx]
result["T0"] = t0
y1 = (t0 >> 4) & 0xF
k = t0 & 0xF
result["K"] = k
idx += 1
# Interface bytes group 1
iface = {}
if y1 & 0x8: iface["TA1"] = atr[idx]; idx += 1
if y1 & 0x4: iface["TB1"] = atr[idx]; idx += 1
if y1 & 0x2: iface["TC1"] = atr[idx]; idx += 1
if y1 & 0x1:
iface["TD1"] = atr[idx]
result["protocol_TD1"] = atr[idx] & 0xF
idx += 1
result["interface_bytes"] = iface
# Subsequent interface byte groups (TD2, TA2, etc.)
group = 1
while True:
td_key = f"TD{group}"
if td_key not in iface:
break
yi = (iface[td_key] >> 4) & 0xF
result[f"protocol_T{group+1}"] = iface[td_key] & 0xF
group += 1
new_iface = {}
if yi & 0x8: new_iface[f"TA{group}"] = atr[idx]; idx += 1
if yi & 0x4: new_iface[f"TB{group}"] = atr[idx]; idx += 1
if yi & 0x2: new_iface[f"TC{group}"] = atr[idx]; idx += 1
if yi & 0x1:
new_iface[f"TD{group}"] = atr[idx]; idx += 1
iface.update(new_iface)
if not (yi & 0x1):
break
# Historical bytes
hist_start = idx
result["historical"] = list(atr[idx:idx+k])
idx += k
# TCK (check byte)
if idx < len(atr):
result["TCK"] = atr[idx]
# Verify
xor = 0
for i in range(1, len(atr)):
xor ^= atr[i]
result["TCK_valid"] = (xor == 0)
return result
# Example usage
atr_hex = "3BF59600008031FE454A434F503302"
result = parse_atr(atr_hex)
for k, v in result.items():
print(f"{k}: {v}")
10. JavaScript ATR Parser (Browser)
function parseATR(hexStr) {
const atr = hexStr.replace(/\s/g, '');
const bytes = [];
for (let i = 0; i < atr.length; i += 2) {
bytes.push(parseInt(atr.substr(i, 2), 16));
}
let idx = 0;
const result = {};
// TS
result.TS = bytes[idx];
result.convention = bytes[idx] === 0x3B ? 'direct' : 'inverse';
idx++;
// T0
const t0 = bytes[idx];
result.T0 = t0;
let y = (t0 >> 4) & 0xF;
const k = t0 & 0xF;
result.K = k;
idx++;
// Interface bytes
result.interfaceBytes = {};
let group = 1;
while (y > 0) {
if (y & 0x8) result.interfaceBytes[`TA${group}`] = bytes[idx++];
if (y & 0x4) result.interfaceBytes[`TB${group}`] = bytes[idx++];
if (y & 0x2) result.interfaceBytes[`TC${group}`] = bytes[idx++];
if (y & 0x1) {
result.interfaceBytes[`TD${group}`] = bytes[idx];
y = (bytes[idx] >> 4) & 0xF;
result[`protocol_T${group}`] = bytes[idx] & 0xF;
idx++;
group++;
} else {
break;
}
}
// Historical bytes
result.historicalBytes = bytes.slice(idx, idx + k);
idx += k;
// TCK
if (idx < bytes.length) {
result.TCK = bytes[idx];
let xor = 0;
for (let i = 1; i < bytes.length; i++) xor ^= bytes[i];
result.TCK_valid = xor === 0;
}
return result;
}
11. Common Debugging Issues
3B 00), the card may not be fully powered or the reader is timing out. Check VCC and clock signal.12. Summary
TS → determine convention (3B = direct, 3F = inverse)
2. Read T0 → get interface byte flags (Y1) and historical byte count (K)
3. Read interface bytes indicated by Y1 (TA1, TB1, TC1, TD1)
4. If TD1 present, continue reading interface bytes for next protocol group
5. Read K historical bytes
6. If T=1 or multi-protocol, verify TCK
7. Identify protocol: default is T=0 unless TD1 specifies otherwise
Use our ATR Decoder for instant parsing, or the APDU Builder to construct your first command after parsing the ATR. For a closer look at card communication, see our ISO 7816 Protocol Reference and PC/SC Programming Guide.