0
# ATR and Card Types
1
2
Tools for parsing Answer To Reset (ATR) sequences, extracting card parameters, and implementing card type detection logic. The ATR contains essential information about the card's capabilities and communication parameters.
3
4
## Capabilities
5
6
### ATR Processing
7
8
The ATR class provides comprehensive parsing and analysis of Answer To Reset sequences according to ISO/IEC 7816-3 standards.
9
10
```python { .api }
11
class ATR:
12
def __init__(self, atr):
13
"""
14
Parse an Answer To Reset sequence.
15
16
Args:
17
atr (list[int]): ATR bytes as list of integers
18
19
Raises:
20
InvalidATRException: If ATR format is invalid
21
"""
22
23
# ATR Structure Properties
24
@property
25
def TS(self):
26
"""int: Initial character indicating bit order and voltage class."""
27
28
@property
29
def T0(self):
30
"""int: Format character encoding interface characters presence and historical bytes count."""
31
32
@property
33
def K(self):
34
"""int: Number of historical bytes."""
35
36
@property
37
def TA(self):
38
"""list[int]: Interface characters TA1, TA2, ..."""
39
40
@property
41
def TB(self):
42
"""list[int]: Interface characters TB1, TB2, ..."""
43
44
@property
45
def TC(self):
46
"""list[int]: Interface characters TC1, TC2, ..."""
47
48
@property
49
def TD(self):
50
"""list[int]: Interface characters TD1, TD2, ..."""
51
52
@property
53
def Y(self):
54
"""list[int]: Interface character presence indicators."""
55
56
@property
57
def historicalBytes(self):
58
"""list[int]: Historical bytes containing card-specific information."""
59
60
@property
61
def TCK(self):
62
"""int: Check character for ATR integrity verification."""
63
64
# Protocol Detection
65
def getSupportedProtocols(self):
66
"""
67
Get dictionary of communication protocols supported by the card.
68
69
Returns:
70
dict[str, bool]: Dictionary with protocol names as keys (e.g., "T=0", "T=1")
71
and True as values for supported protocols
72
"""
73
74
def isT0Supported(self):
75
"""
76
Check if T=0 protocol is supported.
77
78
Returns:
79
bool: True if T=0 is supported
80
"""
81
82
def isT1Supported(self):
83
"""
84
Check if T=1 protocol is supported.
85
86
Returns:
87
bool: True if T=1 is supported
88
"""
89
90
def isT15Supported(self):
91
"""
92
Check if T=15 protocol is supported.
93
94
Returns:
95
bool: True if T=15 is supported
96
"""
97
98
# ATR Data Access
99
def getChecksum(self):
100
"""
101
Get the ATR checksum (TCK).
102
103
Returns:
104
int: Checksum byte, or None if not present
105
"""
106
107
def getHistoricalBytes(self):
108
"""
109
Get the historical bytes.
110
111
Returns:
112
list[int]: Historical bytes
113
"""
114
115
def getHistoricalBytesCount(self):
116
"""
117
Get the number of historical bytes.
118
119
Returns:
120
int: Count of historical bytes
121
"""
122
123
# Communication Parameters
124
def getBitRateFactor(self):
125
"""
126
Get the bit rate adjustment factor from TA1.
127
128
Returns:
129
int: Bit rate factor (1-16)
130
"""
131
132
def getClockRateConversion(self):
133
"""
134
Get the clock rate conversion factor from TA1.
135
136
Returns:
137
int: Clock rate conversion factor
138
"""
139
140
def getProgrammingCurrent(self):
141
"""
142
Get the maximum programming current from TB1.
143
144
Returns:
145
int: Programming current in mA, or None if not specified
146
"""
147
148
def getProgrammingVoltage(self):
149
"""
150
Get the programming voltage from TB1.
151
152
Returns:
153
int: Programming voltage class, or None if not specified
154
"""
155
156
def getGuardTime(self):
157
"""
158
Get the guard time from TC1.
159
160
Returns:
161
int: Guard time in ETUs (Elementary Time Units)
162
"""
163
164
# Display and Formatting
165
def render(self):
166
"""
167
Render ATR in human-readable format with detailed breakdown.
168
169
Returns:
170
str: Formatted ATR analysis
171
"""
172
173
def __str__(self):
174
"""
175
String representation of ATR.
176
177
Returns:
178
str: ATR as space-separated hex bytes
179
"""
180
181
def dump(self):
182
"""
183
Deprecated: Use render() instead.
184
185
Returns:
186
str: ATR dump (deprecated)
187
"""
188
189
# Class Constants for Parameter Lookup
190
clockrateconversion = {
191
# Clock rate conversion factors indexed by TA1 high nibble
192
0x0: 372, 0x1: 372, 0x2: 558, 0x3: 744, 0x4: 1116, 0x5: 1488,
193
0x6: 1860, 0x7: 2232, 0x8: 2604, 0x9: 372, 0xA: 512, 0xB: 768,
194
0xC: 1024, 0xD: 1536, 0xE: 2048, 0xF: 2048
195
}
196
197
bitratefactor = {
198
# Bit rate adjustment factors indexed by TA1 low nibble
199
0x1: 1, 0x2: 2, 0x3: 4, 0x4: 8, 0x5: 16, 0x6: 32,
200
0x8: 12, 0x9: 20
201
}
202
203
currenttable = {
204
# Programming current values in mA indexed by TB1 low nibble
205
0x0: 25, 0x1: 50, 0x2: 100, 0x3: 200, 0x4: 400, 0x5: 800
206
}
207
```
208
209
### Card Type Detection
210
211
Card type classes provide flexible mechanisms for matching cards based on ATR patterns, enabling applications to identify specific card types.
212
213
```python { .api }
214
class CardType:
215
"""Abstract base class for card type matching."""
216
217
def matches(self, atr, reader=None):
218
"""
219
Check if the given ATR matches this card type.
220
221
Args:
222
atr (list[int]): ATR bytes to check
223
reader (Reader, optional): Reader containing the card
224
225
Returns:
226
bool: True if ATR matches this card type
227
"""
228
229
class AnyCardType(CardType):
230
"""Card type that matches any card."""
231
232
def matches(self, atr, reader=None):
233
"""
234
Always returns True - matches any card type.
235
236
Args:
237
atr (list[int]): ATR bytes (ignored)
238
reader (Reader, optional): Reader (ignored)
239
240
Returns:
241
bool: Always True
242
"""
243
244
class ATRCardType(CardType):
245
"""Card type that matches specific ATR patterns with optional masking."""
246
247
def __init__(self, atr, mask=None):
248
"""
249
Create ATR-based card type matcher.
250
251
Args:
252
atr (list[int]): ATR pattern to match
253
mask (list[int], optional): Mask for ATR comparison.
254
If provided, must be same length as atr.
255
0x00 bytes in mask are ignored during comparison.
256
0xFF bytes in mask must match exactly.
257
258
Raises:
259
InvalidATRMaskLengthException: If mask length doesn't match ATR length
260
"""
261
262
def matches(self, atr, reader=None):
263
"""
264
Check if ATR matches the pattern with optional masking.
265
266
Args:
267
atr (list[int]): ATR bytes to check
268
reader (Reader, optional): Reader containing the card
269
270
Returns:
271
bool: True if ATR matches pattern (considering mask if provided)
272
"""
273
```
274
275
## Usage Examples
276
277
### Basic ATR Analysis
278
279
```python
280
from smartcard import ATR
281
from smartcard.util import toHexString
282
283
# Example ATR from a contact smart card
284
atr_bytes = [0x3B, 0x7F, 0x96, 0x00, 0x00, 0x80, 0x31, 0x80, 0x65, 0xB0, 0x84, 0x41, 0x82, 0x54, 0x43, 0x4F, 0x53, 0x70, 0x02, 0x47, 0x00, 0x44, 0x32, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
285
286
# Parse the ATR
287
atr = ATR(atr_bytes)
288
289
print(f"ATR: {atr}")
290
print(f"ATR hex: {toHexString(atr_bytes)}")
291
print()
292
293
# Analyze structure
294
print("ATR Structure:")
295
print(f" TS (Initial Character): 0x{atr.TS:02X}")
296
print(f" T0 (Format Character): 0x{atr.T0:02X}")
297
print(f" Historical Bytes Count: {atr.K}")
298
print(f" Historical Bytes: {toHexString(atr.historicalBytes)}")
299
print()
300
301
# Check protocol support
302
print("Protocol Support:")
303
print(f" T=0 supported: {atr.isT0Supported()}")
304
print(f" T=1 supported: {atr.isT1Supported()}")
305
print(f" T=15 supported: {atr.isT15Supported()}")
306
supported_protocols = atr.getSupportedProtocols()
307
print(f" All supported protocols: {list(supported_protocols.keys())}")
308
print()
309
310
# Get communication parameters
311
print("Communication Parameters:")
312
if atr.TA:
313
print(f" Clock rate conversion: {atr.getClockRateConversion()}")
314
print(f" Bit rate factor: {atr.getBitRateFactor()}")
315
if atr.TB:
316
current = atr.getProgrammingCurrent()
317
voltage = atr.getProgrammingVoltage()
318
if current: print(f" Programming current: {current} mA")
319
if voltage: print(f" Programming voltage class: {voltage}")
320
if atr.TC:
321
print(f" Guard time: {atr.getGuardTime()} ETU")
322
```
323
324
### ATR Detailed Analysis
325
326
```python
327
from smartcard import ATR
328
329
def analyze_atr(atr_bytes):
330
"""Perform comprehensive ATR analysis."""
331
atr = ATR(atr_bytes)
332
333
print("=== ATR Analysis ===")
334
print(atr.render()) # Detailed human-readable breakdown
335
print()
336
337
# Interface character analysis
338
print("Interface Characters:")
339
for i, ta in enumerate(atr.TA, 1):
340
print(f" TA{i}: 0x{ta:02X}")
341
for i, tb in enumerate(atr.TB, 1):
342
print(f" TB{i}: 0x{tb:02X}")
343
for i, tc in enumerate(atr.TC, 1):
344
print(f" TC{i}: 0x{tc:02X}")
345
for i, td in enumerate(atr.TD, 1):
346
print(f" TD{i}: 0x{td:02X}")
347
348
return atr
349
350
# Example usage
351
sample_atr = [0x3B, 0x65, 0x00, 0x00, 0x9C, 0x11, 0x01, 0x01, 0x03]
352
analyze_atr(sample_atr)
353
```
354
355
### Card Type Matching
356
357
```python
358
from smartcard.CardType import ATRCardType, AnyCardType
359
from smartcard.CardRequest import CardRequest
360
361
# Match any card type
362
any_card = AnyCardType()
363
print(f"Any card matches sample ATR: {any_card.matches([0x3B, 0x65])}")
364
365
# Match specific ATR exactly
366
exact_atr = [0x3B, 0x65, 0x00, 0x00, 0x9C, 0x11, 0x01, 0x01, 0x03]
367
exact_card_type = ATRCardType(exact_atr)
368
369
test_atr1 = [0x3B, 0x65, 0x00, 0x00, 0x9C, 0x11, 0x01, 0x01, 0x03] # Exact match
370
test_atr2 = [0x3B, 0x65, 0x00, 0x00, 0x9C, 0x11, 0x01, 0x01, 0x04] # Different
371
372
print(f"Exact match: {exact_card_type.matches(test_atr1)}") # True
373
print(f"Different ATR: {exact_card_type.matches(test_atr2)}") # False
374
375
# Match with mask (ignore certain bytes)
376
atr_pattern = [0x3B, 0x65, 0x00, 0x00, 0x9C, 0x11, 0x01, 0x01, 0x03]
377
atr_mask = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00] # Ignore last byte
378
379
masked_card_type = ATRCardType(atr_pattern, atr_mask)
380
381
test_atr3 = [0x3B, 0x65, 0x00, 0x00, 0x9C, 0x11, 0x01, 0x01, 0xFF] # Last byte different but masked
382
383
print(f"Masked match: {masked_card_type.matches(test_atr3)}") # True
384
```
385
386
### Card Type in Card Request
387
388
```python
389
from smartcard.CardType import ATRCardType
390
from smartcard.CardRequest import CardRequest
391
from smartcard import ATR
392
393
# Define card type for specific smart card series
394
mifare_atr_pattern = [0x3B, 0x8F, 0x80, 0x01, 0x80, 0x4F, 0x0C, 0xA0, 0x00, 0x00, 0x03, 0x06, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x6A]
395
mifare_card_type = ATRCardType(mifare_atr_pattern)
396
397
# Wait specifically for this card type
398
card_request = CardRequest(timeout=30, cardType=mifare_card_type)
399
400
try:
401
print("Waiting for MIFARE card...")
402
card_service = card_request.waitforcard()
403
404
print("MIFARE card detected!")
405
connection = card_service.connection
406
407
# Analyze the ATR
408
atr_bytes = connection.getATR()
409
atr = ATR(atr_bytes)
410
411
print(f"Card ATR: {atr}")
412
print(f"Protocols: {list(atr.getSupportedProtocols().keys())}")
413
print(f"Historical bytes: {atr.getHistoricalBytes()}")
414
415
except Exception as e:
416
print(f"Card request failed: {e}")
417
```
418
419
### Custom Card Type Implementation
420
421
```python
422
from smartcard.CardType import CardType
423
from smartcard import ATR
424
425
class JavaCardType(CardType):
426
"""Custom card type for detecting Java Cards based on historical bytes."""
427
428
def matches(self, atr_bytes, reader=None):
429
try:
430
atr = ATR(atr_bytes)
431
historical = atr.getHistoricalBytes()
432
433
# Check for Java Card indicators in historical bytes
434
# This is a simplified example - real detection would be more complex
435
if len(historical) >= 4:
436
# Look for Java Card identifier
437
if historical[0:3] == [0x4A, 0x43, 0x4F]: # "JCO" in ASCII
438
return True
439
440
# Look for other Java Card patterns
441
if 0x72 in historical: # Common in Java Card ATRs
442
return True
443
444
return False
445
except:
446
return False
447
448
# Use custom card type
449
java_card_type = JavaCardType()
450
card_request = CardRequest(timeout=20, cardType=java_card_type)
451
452
try:
453
print("Waiting for Java Card...")
454
card_service = card_request.waitforcard()
455
print("Java Card detected!")
456
457
except Exception as e:
458
print(f"No Java Card found: {e}")
459
```
460
461
## Related Types
462
463
```python { .api }
464
# Exception types for ATR processing
465
class InvalidATRException(SmartcardException):
466
"""Raised when ATR format is invalid."""
467
468
class InvalidATRMaskLengthException(SmartcardException):
469
"""Raised when ATR mask length doesn't match ATR length."""
470
471
# Type aliases for clarity
472
ATRBytes = list[int]
473
ATRMask = list[int]
474
ProtocolDict = dict[str, bool]
475
HistoricalBytes = list[int]
476
477
# ATR parameter constants (from ATR class)
478
ClockRateConversion = dict[int, int]
479
BitRateFactor = dict[int, int]
480
CurrentTable = dict[int, int]
481
```