0
# PC/SC Interface
1
2
Direct access to PC/SC (Personal Computer/Smart Card) functions and constants, providing complete control over smart card operations at the system level. This is the lowest-level API in pyscard.
3
4
## Capabilities
5
6
### Low-Level PC/SC Access
7
8
The scard module provides direct access to all PC/SC functions through a C extension module that wraps the native PC/SC libraries (WinSCard on Windows, PCSC-Lite on Unix-like systems).
9
10
```python { .api }
11
# Import all PC/SC functions and constants
12
from smartcard.scard import *
13
14
# Key PC/SC functions available:
15
def SCardEstablishContext(dwScope):
16
"""
17
Establish PC/SC context.
18
19
Args:
20
dwScope: Context scope (SCARD_SCOPE_USER, SCARD_SCOPE_TERMINAL, SCARD_SCOPE_SYSTEM)
21
22
Returns:
23
tuple: (result_code, context_handle)
24
"""
25
26
def SCardReleaseContext(hContext):
27
"""
28
Release PC/SC context.
29
30
Args:
31
hContext: Context handle from SCardEstablishContext
32
33
Returns:
34
int: Result code
35
"""
36
37
def SCardListReaders(hContext, mszGroups):
38
"""
39
List available smart card readers.
40
41
Args:
42
hContext: PC/SC context handle
43
mszGroups: Reader groups to query (None for all)
44
45
Returns:
46
tuple: (result_code, reader_list)
47
"""
48
49
def SCardConnect(hContext, szReader, dwShareMode, dwPreferredProtocols):
50
"""
51
Connect to smart card.
52
53
Args:
54
hContext: PC/SC context handle
55
szReader: Reader name
56
dwShareMode: Share mode (SCARD_SHARE_SHARED, SCARD_SHARE_EXCLUSIVE, etc.)
57
dwPreferredProtocols: Preferred protocols (SCARD_PROTOCOL_T0, SCARD_PROTOCOL_T1, etc.)
58
59
Returns:
60
tuple: (result_code, card_handle, active_protocol)
61
"""
62
63
def SCardDisconnect(hCard, dwDisposition):
64
"""
65
Disconnect from smart card.
66
67
Args:
68
hCard: Card handle from SCardConnect
69
dwDisposition: Disposition (SCARD_LEAVE_CARD, SCARD_RESET_CARD, etc.)
70
71
Returns:
72
int: Result code
73
"""
74
75
def SCardTransmit(hCard, pioSendPci, pbSendBuffer, pioRecvPci):
76
"""
77
Transmit APDU to smart card.
78
79
Args:
80
hCard: Card handle
81
pioSendPci: Send protocol info
82
pbSendBuffer: Command APDU bytes
83
pioRecvPci: Receive protocol info
84
85
Returns:
86
tuple: (result_code, response_bytes)
87
"""
88
89
def SCardGetStatusChange(hContext, dwTimeout, rgReaderStates):
90
"""
91
Wait for reader state changes.
92
93
Args:
94
hContext: PC/SC context handle
95
dwTimeout: Timeout in milliseconds (INFINITE for no timeout)
96
rgReaderStates: List of reader states to monitor
97
98
Returns:
99
tuple: (result_code, updated_reader_states)
100
"""
101
102
def SCardStatus(hCard):
103
"""
104
Get smart card status information.
105
106
Args:
107
hCard: Card handle
108
109
Returns:
110
tuple: (result_code, reader_name, state, protocol, atr)
111
"""
112
113
def SCardGetAttrib(hCard, dwAttrId):
114
"""
115
Get card/reader attribute.
116
117
Args:
118
hCard: Card handle
119
dwAttrId: Attribute identifier
120
121
Returns:
122
tuple: (result_code, attribute_data)
123
"""
124
125
def SCardControl(hCard, dwControlCode, pbSendBuffer):
126
"""
127
Send control command to reader.
128
129
Args:
130
hCard: Card handle
131
dwControlCode: Control code
132
pbSendBuffer: Control data
133
134
Returns:
135
tuple: (result_code, response_data)
136
"""
137
```
138
139
### PC/SC Constants
140
141
All PC/SC constants are available through the scard module import.
142
143
```python { .api }
144
# Context scope constants
145
SCARD_SCOPE_USER = 0
146
SCARD_SCOPE_TERMINAL = 1
147
SCARD_SCOPE_SYSTEM = 2
148
149
# Share mode constants
150
SCARD_SHARE_EXCLUSIVE = 1
151
SCARD_SHARE_SHARED = 2
152
SCARD_SHARE_DIRECT = 3
153
154
# Protocol constants
155
SCARD_PROTOCOL_UNDEFINED = 0
156
SCARD_PROTOCOL_T0 = 1
157
SCARD_PROTOCOL_T1 = 2
158
SCARD_PROTOCOL_RAW = 4
159
SCARD_PROTOCOL_T15 = 8
160
161
# Card state constants
162
SCARD_STATE_UNAWARE = 0x00
163
SCARD_STATE_IGNORE = 0x01
164
SCARD_STATE_CHANGED = 0x02
165
SCARD_STATE_UNKNOWN = 0x04
166
SCARD_STATE_UNAVAILABLE = 0x08
167
SCARD_STATE_EMPTY = 0x10
168
SCARD_STATE_PRESENT = 0x20
169
SCARD_STATE_ATRMATCH = 0x40
170
SCARD_STATE_EXCLUSIVE = 0x80
171
SCARD_STATE_INUSE = 0x100
172
SCARD_STATE_MUTE = 0x200
173
174
# Disposition constants
175
SCARD_LEAVE_CARD = 0
176
SCARD_RESET_CARD = 1
177
SCARD_UNPOWER_CARD = 2
178
SCARD_EJECT_CARD = 3
179
180
# Result codes
181
SCARD_S_SUCCESS = 0
182
SCARD_E_CANCELLED = 0x80100002
183
SCARD_E_INVALID_HANDLE = 0x80100003
184
SCARD_E_INVALID_PARAMETER = 0x80100004
185
SCARD_E_NO_MEMORY = 0x80100006
186
SCARD_E_TIMEOUT = 0x8010000A
187
SCARD_E_SHARING_VIOLATION = 0x8010000B
188
SCARD_E_NO_SMARTCARD = 0x8010000C
189
SCARD_E_UNKNOWN_CARD = 0x8010000D
190
SCARD_E_PROTO_MISMATCH = 0x8010000F
191
SCARD_E_NOT_READY = 0x80100010
192
SCARD_E_SYSTEM_CANCELLED = 0x80100012
193
SCARD_E_NOT_TRANSACTED = 0x80100016
194
SCARD_E_READER_UNAVAILABLE = 0x80100017
195
SCARD_W_UNSUPPORTED_CARD = 0x80100065
196
SCARD_W_UNRESPONSIVE_CARD = 0x80100066
197
SCARD_W_UNPOWERED_CARD = 0x80100067
198
SCARD_W_RESET_CARD = 0x80100068
199
SCARD_W_REMOVED_CARD = 0x80100069
200
201
# Timeout constants
202
INFINITE = 0xFFFFFFFF
203
204
# Attribute constants
205
SCARD_ATTR_VENDOR_NAME = 0x00010100
206
SCARD_ATTR_VENDOR_IFD_TYPE = 0x00010101
207
SCARD_ATTR_VENDOR_IFD_VERSION = 0x00010102
208
SCARD_ATTR_VENDOR_IFD_SERIAL_NO = 0x00010103
209
SCARD_ATTR_ATR_STRING = 0x00090303
210
SCARD_ATTR_ICC_PRESENCE = 0x00090300
211
```
212
213
## Usage Examples
214
215
### Basic PC/SC Operations
216
217
```python
218
from smartcard.scard import *
219
220
def basic_pcsc_example():
221
"""Demonstrate basic PC/SC operations."""
222
223
# Establish context
224
result, context = SCardEstablishContext(SCARD_SCOPE_USER)
225
if result != SCARD_S_SUCCESS:
226
print(f"Failed to establish context: {result:08X}")
227
return
228
229
print("✓ PC/SC context established")
230
231
try:
232
# List readers
233
result, readers = SCardListReaders(context, None)
234
if result != SCARD_S_SUCCESS:
235
print(f"Failed to list readers: {result:08X}")
236
return
237
238
print(f"Found {len(readers)} readers:")
239
for reader in readers:
240
print(f" {reader}")
241
242
if not readers:
243
print("No readers available")
244
return
245
246
# Connect to first reader
247
reader_name = readers[0]
248
result, card_handle, active_protocol = SCardConnect(
249
context, reader_name, SCARD_SHARE_SHARED,
250
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1
251
)
252
253
if result != SCARD_S_SUCCESS:
254
print(f"Failed to connect to {reader_name}: {result:08X}")
255
return
256
257
print(f"✓ Connected to {reader_name}")
258
print(f"Active protocol: {active_protocol}")
259
260
try:
261
# Get card status
262
result, reader, state, protocol, atr = SCardStatus(card_handle)
263
if result == SCARD_S_SUCCESS:
264
print(f"Card status:")
265
print(f" Reader: {reader}")
266
print(f" State: {state:08X}")
267
print(f" Protocol: {protocol}")
268
print(f" ATR: {' '.join(f'{b:02X}' for b in atr)}")
269
270
# Transmit APDU (GET CHALLENGE)
271
if active_protocol == SCARD_PROTOCOL_T0:
272
send_pci = (SCARD_PROTOCOL_T0, 8)
273
else:
274
send_pci = (SCARD_PROTOCOL_T1, 8)
275
276
get_challenge = [0x00, 0x84, 0x00, 0x00, 0x08]
277
result, response = SCardTransmit(card_handle, send_pci, get_challenge, None)
278
279
if result == SCARD_S_SUCCESS:
280
print(f"GET CHALLENGE response: {' '.join(f'{b:02X}' for b in response)}")
281
else:
282
print(f"APDU transmission failed: {result:08X}")
283
284
finally:
285
# Disconnect
286
SCardDisconnect(card_handle, SCARD_LEAVE_CARD)
287
print("✓ Disconnected from card")
288
289
finally:
290
# Release context
291
SCardReleaseContext(context)
292
print("✓ PC/SC context released")
293
294
basic_pcsc_example()
295
```
296
297
### Reader State Monitoring
298
299
```python
300
from smartcard.scard import *
301
import time
302
303
def monitor_reader_states(duration=30):
304
"""Monitor reader state changes using PC/SC."""
305
306
result, context = SCardEstablishContext(SCARD_SCOPE_USER)
307
if result != SCARD_S_SUCCESS:
308
print(f"Context establishment failed: {result:08X}")
309
return
310
311
try:
312
# Get initial reader list
313
result, readers = SCardListReaders(context, None)
314
if result != SCARD_S_SUCCESS or not readers:
315
print("No readers available")
316
return
317
318
print(f"Monitoring {len(readers)} readers for {duration} seconds")
319
320
# Initialize reader states
321
reader_states = []
322
for reader in readers:
323
reader_states.append({
324
'reader': reader,
325
'current_state': SCARD_STATE_UNAWARE,
326
'event_state': SCARD_STATE_UNAWARE,
327
'atr': []
328
})
329
330
start_time = time.time()
331
while time.time() - start_time < duration:
332
# Wait for state changes (1 second timeout)
333
result, updated_states = SCardGetStatusChange(context, 1000, reader_states)
334
335
if result == SCARD_S_SUCCESS:
336
for i, state in enumerate(updated_states):
337
old_state = reader_states[i]['current_state']
338
new_state = state['event_state']
339
340
if new_state != old_state:
341
reader_name = state['reader']
342
print(f"\n{reader_name}:")
343
print(f" State change: {old_state:08X} -> {new_state:08X}")
344
345
# Analyze state flags
346
if new_state & SCARD_STATE_PRESENT:
347
print(" ✓ Card present")
348
if state['atr']:
349
atr_str = ' '.join(f'{b:02X}' for b in state['atr'])
350
print(f" ATR: {atr_str}")
351
elif new_state & SCARD_STATE_EMPTY:
352
print(" ✗ Card removed")
353
354
if new_state & SCARD_STATE_EXCLUSIVE:
355
print(" ⚠ Exclusive access")
356
if new_state & SCARD_STATE_INUSE:
357
print(" ⚠ In use")
358
if new_state & SCARD_STATE_MUTE:
359
print(" ⚠ Mute card")
360
361
# Update current state for next iteration
362
reader_states[i]['current_state'] = new_state
363
364
elif result != SCARD_E_TIMEOUT:
365
print(f"State monitoring error: {result:08X}")
366
break
367
368
finally:
369
SCardReleaseContext(context)
370
371
monitor_reader_states(15)
372
```
373
374
### Advanced PC/SC Operations
375
376
```python
377
from smartcard.scard import *
378
379
def advanced_pcsc_operations():
380
"""Demonstrate advanced PC/SC features."""
381
382
result, context = SCardEstablishContext(SCARD_SCOPE_SYSTEM)
383
if result != SCARD_S_SUCCESS:
384
print(f"Context failed: {result:08X}")
385
return
386
387
try:
388
result, readers = SCardListReaders(context, None)
389
if result != SCARD_S_SUCCESS or not readers:
390
return
391
392
reader_name = readers[0]
393
394
# Connect in direct mode for reader control
395
result, card_handle, protocol = SCardConnect(
396
context, reader_name, SCARD_SHARE_DIRECT, 0
397
)
398
399
if result != SCARD_S_SUCCESS:
400
print(f"Direct connection failed: {result:08X}")
401
return
402
403
try:
404
# Get reader attributes
405
attributes = [
406
(SCARD_ATTR_VENDOR_NAME, "Vendor Name"),
407
(SCARD_ATTR_VENDOR_IFD_TYPE, "Reader Type"),
408
(SCARD_ATTR_VENDOR_IFD_VERSION, "Reader Version"),
409
(SCARD_ATTR_VENDOR_IFD_SERIAL_NO, "Serial Number")
410
]
411
412
print(f"Reader: {reader_name}")
413
print("Attributes:")
414
415
for attr_id, attr_name in attributes:
416
result, attr_data = SCardGetAttrib(card_handle, attr_id)
417
if result == SCARD_S_SUCCESS:
418
if attr_name in ["Vendor Name", "Reader Type"]:
419
# Convert to string (remove null terminators)
420
try:
421
attr_str = bytes(attr_data).decode('ascii').rstrip('\x00')
422
print(f" {attr_name}: {attr_str}")
423
except:
424
print(f" {attr_name}: {attr_data}")
425
else:
426
print(f" {attr_name}: {' '.join(f'{b:02X}' for b in attr_data)}")
427
else:
428
print(f" {attr_name}: Not available ({result:08X})")
429
430
# Try reader-specific control commands (example for CCID readers)
431
# Note: Control codes are reader-specific
432
try:
433
# Example: Get reader features (CCID)
434
control_code = 0x42000D48 # IOCTL_SMARTCARD_GET_FEATURE_REQUEST
435
result, features = SCardControl(card_handle, control_code, [])
436
if result == SCARD_S_SUCCESS:
437
print(f"Reader features: {len(features)} bytes")
438
print(f" Data: {' '.join(f'{b:02X}' for b in features)}")
439
except:
440
print(" Control commands not supported or failed")
441
442
finally:
443
SCardDisconnect(card_handle, SCARD_LEAVE_CARD)
444
445
finally:
446
SCardReleaseContext(context)
447
448
advanced_pcsc_operations()
449
```
450
451
### Error Handling and Result Codes
452
453
```python
454
from smartcard.scard import *
455
456
def error_code_analysis():
457
"""Demonstrate PC/SC error handling."""
458
459
# Common result codes and their meanings
460
error_codes = {
461
SCARD_S_SUCCESS: "Success",
462
SCARD_E_CANCELLED: "Cancelled",
463
SCARD_E_INVALID_HANDLE: "Invalid handle",
464
SCARD_E_INVALID_PARAMETER: "Invalid parameter",
465
SCARD_E_NO_MEMORY: "No memory",
466
SCARD_E_TIMEOUT: "Timeout",
467
SCARD_E_SHARING_VIOLATION: "Sharing violation",
468
SCARD_E_NO_SMARTCARD: "No smart card",
469
SCARD_E_UNKNOWN_CARD: "Unknown card",
470
SCARD_E_PROTO_MISMATCH: "Protocol mismatch",
471
SCARD_E_NOT_READY: "Not ready",
472
SCARD_E_SYSTEM_CANCELLED: "System cancelled",
473
SCARD_E_NOT_TRANSACTED: "Not transacted",
474
SCARD_E_READER_UNAVAILABLE: "Reader unavailable",
475
SCARD_W_UNSUPPORTED_CARD: "Warning: Unsupported card",
476
SCARD_W_UNRESPONSIVE_CARD: "Warning: Unresponsive card",
477
SCARD_W_UNPOWERED_CARD: "Warning: Unpowered card",
478
SCARD_W_RESET_CARD: "Warning: Reset card",
479
SCARD_W_REMOVED_CARD: "Warning: Removed card"
480
}
481
482
def interpret_result(result_code):
483
"""Interpret PC/SC result code."""
484
if result_code in error_codes:
485
return error_codes[result_code]
486
elif result_code & 0x80000000:
487
return f"Error: {result_code:08X}"
488
else:
489
return f"Success/Warning: {result_code:08X}"
490
491
# Test various scenarios
492
print("PC/SC Error Code Analysis:")
493
494
# Try to establish context
495
result, context = SCardEstablishContext(SCARD_SCOPE_USER)
496
print(f"Context: {interpret_result(result)}")
497
498
if result == SCARD_S_SUCCESS:
499
# Try invalid reader
500
result, card, protocol = SCardConnect(
501
context, "NonexistentReader", SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0
502
)
503
print(f"Invalid reader: {interpret_result(result)}")
504
505
# Try with no timeout
506
result, states = SCardGetStatusChange(context, 0, [])
507
print(f"Zero timeout: {interpret_result(result)}")
508
509
SCardReleaseContext(context)
510
511
error_code_analysis()
512
```
513
514
## Related Types
515
516
```python { .api }
517
# PC/SC handle types
518
ContextHandle = int
519
CardHandle = int
520
521
# PC/SC data structures
522
ReaderState = dict # {'reader': str, 'current_state': int, 'event_state': int, 'atr': list[int]}
523
ProtocolInfo = tuple[int, int] # (protocol, pci_length)
524
525
# Result code type
526
ResultCode = int
527
528
# Common PC/SC constants (subset - full list available via import)
529
ScopeType = int # SCARD_SCOPE_* constants
530
ShareMode = int # SCARD_SHARE_* constants
531
Protocol = int # SCARD_PROTOCOL_* constants
532
Disposition = int # SCARD_*_CARD constants
533
StateFlag = int # SCARD_STATE_* constants
534
AttributeId = int # SCARD_ATTR_* constants
535
```