Smartcard library for Python providing PC/SC interface for smart card communication
—
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.
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).
# Import all PC/SC functions and constants
from smartcard.scard import *
# Key PC/SC functions available:
def SCardEstablishContext(dwScope):
"""
Establish PC/SC context.
Args:
dwScope: Context scope (SCARD_SCOPE_USER, SCARD_SCOPE_TERMINAL, SCARD_SCOPE_SYSTEM)
Returns:
tuple: (result_code, context_handle)
"""
def SCardReleaseContext(hContext):
"""
Release PC/SC context.
Args:
hContext: Context handle from SCardEstablishContext
Returns:
int: Result code
"""
def SCardListReaders(hContext, mszGroups):
"""
List available smart card readers.
Args:
hContext: PC/SC context handle
mszGroups: Reader groups to query (None for all)
Returns:
tuple: (result_code, reader_list)
"""
def SCardConnect(hContext, szReader, dwShareMode, dwPreferredProtocols):
"""
Connect to smart card.
Args:
hContext: PC/SC context handle
szReader: Reader name
dwShareMode: Share mode (SCARD_SHARE_SHARED, SCARD_SHARE_EXCLUSIVE, etc.)
dwPreferredProtocols: Preferred protocols (SCARD_PROTOCOL_T0, SCARD_PROTOCOL_T1, etc.)
Returns:
tuple: (result_code, card_handle, active_protocol)
"""
def SCardDisconnect(hCard, dwDisposition):
"""
Disconnect from smart card.
Args:
hCard: Card handle from SCardConnect
dwDisposition: Disposition (SCARD_LEAVE_CARD, SCARD_RESET_CARD, etc.)
Returns:
int: Result code
"""
def SCardTransmit(hCard, pioSendPci, pbSendBuffer, pioRecvPci):
"""
Transmit APDU to smart card.
Args:
hCard: Card handle
pioSendPci: Send protocol info
pbSendBuffer: Command APDU bytes
pioRecvPci: Receive protocol info
Returns:
tuple: (result_code, response_bytes)
"""
def SCardGetStatusChange(hContext, dwTimeout, rgReaderStates):
"""
Wait for reader state changes.
Args:
hContext: PC/SC context handle
dwTimeout: Timeout in milliseconds (INFINITE for no timeout)
rgReaderStates: List of reader states to monitor
Returns:
tuple: (result_code, updated_reader_states)
"""
def SCardStatus(hCard):
"""
Get smart card status information.
Args:
hCard: Card handle
Returns:
tuple: (result_code, reader_name, state, protocol, atr)
"""
def SCardGetAttrib(hCard, dwAttrId):
"""
Get card/reader attribute.
Args:
hCard: Card handle
dwAttrId: Attribute identifier
Returns:
tuple: (result_code, attribute_data)
"""
def SCardControl(hCard, dwControlCode, pbSendBuffer):
"""
Send control command to reader.
Args:
hCard: Card handle
dwControlCode: Control code
pbSendBuffer: Control data
Returns:
tuple: (result_code, response_data)
"""All PC/SC constants are available through the scard module import.
# Context scope constants
SCARD_SCOPE_USER = 0
SCARD_SCOPE_TERMINAL = 1
SCARD_SCOPE_SYSTEM = 2
# Share mode constants
SCARD_SHARE_EXCLUSIVE = 1
SCARD_SHARE_SHARED = 2
SCARD_SHARE_DIRECT = 3
# Protocol constants
SCARD_PROTOCOL_UNDEFINED = 0
SCARD_PROTOCOL_T0 = 1
SCARD_PROTOCOL_T1 = 2
SCARD_PROTOCOL_RAW = 4
SCARD_PROTOCOL_T15 = 8
# Card state constants
SCARD_STATE_UNAWARE = 0x00
SCARD_STATE_IGNORE = 0x01
SCARD_STATE_CHANGED = 0x02
SCARD_STATE_UNKNOWN = 0x04
SCARD_STATE_UNAVAILABLE = 0x08
SCARD_STATE_EMPTY = 0x10
SCARD_STATE_PRESENT = 0x20
SCARD_STATE_ATRMATCH = 0x40
SCARD_STATE_EXCLUSIVE = 0x80
SCARD_STATE_INUSE = 0x100
SCARD_STATE_MUTE = 0x200
# Disposition constants
SCARD_LEAVE_CARD = 0
SCARD_RESET_CARD = 1
SCARD_UNPOWER_CARD = 2
SCARD_EJECT_CARD = 3
# Result codes
SCARD_S_SUCCESS = 0
SCARD_E_CANCELLED = 0x80100002
SCARD_E_INVALID_HANDLE = 0x80100003
SCARD_E_INVALID_PARAMETER = 0x80100004
SCARD_E_NO_MEMORY = 0x80100006
SCARD_E_TIMEOUT = 0x8010000A
SCARD_E_SHARING_VIOLATION = 0x8010000B
SCARD_E_NO_SMARTCARD = 0x8010000C
SCARD_E_UNKNOWN_CARD = 0x8010000D
SCARD_E_PROTO_MISMATCH = 0x8010000F
SCARD_E_NOT_READY = 0x80100010
SCARD_E_SYSTEM_CANCELLED = 0x80100012
SCARD_E_NOT_TRANSACTED = 0x80100016
SCARD_E_READER_UNAVAILABLE = 0x80100017
SCARD_W_UNSUPPORTED_CARD = 0x80100065
SCARD_W_UNRESPONSIVE_CARD = 0x80100066
SCARD_W_UNPOWERED_CARD = 0x80100067
SCARD_W_RESET_CARD = 0x80100068
SCARD_W_REMOVED_CARD = 0x80100069
# Timeout constants
INFINITE = 0xFFFFFFFF
# Attribute constants
SCARD_ATTR_VENDOR_NAME = 0x00010100
SCARD_ATTR_VENDOR_IFD_TYPE = 0x00010101
SCARD_ATTR_VENDOR_IFD_VERSION = 0x00010102
SCARD_ATTR_VENDOR_IFD_SERIAL_NO = 0x00010103
SCARD_ATTR_ATR_STRING = 0x00090303
SCARD_ATTR_ICC_PRESENCE = 0x00090300from smartcard.scard import *
def basic_pcsc_example():
"""Demonstrate basic PC/SC operations."""
# Establish context
result, context = SCardEstablishContext(SCARD_SCOPE_USER)
if result != SCARD_S_SUCCESS:
print(f"Failed to establish context: {result:08X}")
return
print("✓ PC/SC context established")
try:
# List readers
result, readers = SCardListReaders(context, None)
if result != SCARD_S_SUCCESS:
print(f"Failed to list readers: {result:08X}")
return
print(f"Found {len(readers)} readers:")
for reader in readers:
print(f" {reader}")
if not readers:
print("No readers available")
return
# Connect to first reader
reader_name = readers[0]
result, card_handle, active_protocol = SCardConnect(
context, reader_name, SCARD_SHARE_SHARED,
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1
)
if result != SCARD_S_SUCCESS:
print(f"Failed to connect to {reader_name}: {result:08X}")
return
print(f"✓ Connected to {reader_name}")
print(f"Active protocol: {active_protocol}")
try:
# Get card status
result, reader, state, protocol, atr = SCardStatus(card_handle)
if result == SCARD_S_SUCCESS:
print(f"Card status:")
print(f" Reader: {reader}")
print(f" State: {state:08X}")
print(f" Protocol: {protocol}")
print(f" ATR: {' '.join(f'{b:02X}' for b in atr)}")
# Transmit APDU (GET CHALLENGE)
if active_protocol == SCARD_PROTOCOL_T0:
send_pci = (SCARD_PROTOCOL_T0, 8)
else:
send_pci = (SCARD_PROTOCOL_T1, 8)
get_challenge = [0x00, 0x84, 0x00, 0x00, 0x08]
result, response = SCardTransmit(card_handle, send_pci, get_challenge, None)
if result == SCARD_S_SUCCESS:
print(f"GET CHALLENGE response: {' '.join(f'{b:02X}' for b in response)}")
else:
print(f"APDU transmission failed: {result:08X}")
finally:
# Disconnect
SCardDisconnect(card_handle, SCARD_LEAVE_CARD)
print("✓ Disconnected from card")
finally:
# Release context
SCardReleaseContext(context)
print("✓ PC/SC context released")
basic_pcsc_example()from smartcard.scard import *
import time
def monitor_reader_states(duration=30):
"""Monitor reader state changes using PC/SC."""
result, context = SCardEstablishContext(SCARD_SCOPE_USER)
if result != SCARD_S_SUCCESS:
print(f"Context establishment failed: {result:08X}")
return
try:
# Get initial reader list
result, readers = SCardListReaders(context, None)
if result != SCARD_S_SUCCESS or not readers:
print("No readers available")
return
print(f"Monitoring {len(readers)} readers for {duration} seconds")
# Initialize reader states
reader_states = []
for reader in readers:
reader_states.append({
'reader': reader,
'current_state': SCARD_STATE_UNAWARE,
'event_state': SCARD_STATE_UNAWARE,
'atr': []
})
start_time = time.time()
while time.time() - start_time < duration:
# Wait for state changes (1 second timeout)
result, updated_states = SCardGetStatusChange(context, 1000, reader_states)
if result == SCARD_S_SUCCESS:
for i, state in enumerate(updated_states):
old_state = reader_states[i]['current_state']
new_state = state['event_state']
if new_state != old_state:
reader_name = state['reader']
print(f"\n{reader_name}:")
print(f" State change: {old_state:08X} -> {new_state:08X}")
# Analyze state flags
if new_state & SCARD_STATE_PRESENT:
print(" ✓ Card present")
if state['atr']:
atr_str = ' '.join(f'{b:02X}' for b in state['atr'])
print(f" ATR: {atr_str}")
elif new_state & SCARD_STATE_EMPTY:
print(" ✗ Card removed")
if new_state & SCARD_STATE_EXCLUSIVE:
print(" ⚠ Exclusive access")
if new_state & SCARD_STATE_INUSE:
print(" ⚠ In use")
if new_state & SCARD_STATE_MUTE:
print(" ⚠ Mute card")
# Update current state for next iteration
reader_states[i]['current_state'] = new_state
elif result != SCARD_E_TIMEOUT:
print(f"State monitoring error: {result:08X}")
break
finally:
SCardReleaseContext(context)
monitor_reader_states(15)from smartcard.scard import *
def advanced_pcsc_operations():
"""Demonstrate advanced PC/SC features."""
result, context = SCardEstablishContext(SCARD_SCOPE_SYSTEM)
if result != SCARD_S_SUCCESS:
print(f"Context failed: {result:08X}")
return
try:
result, readers = SCardListReaders(context, None)
if result != SCARD_S_SUCCESS or not readers:
return
reader_name = readers[0]
# Connect in direct mode for reader control
result, card_handle, protocol = SCardConnect(
context, reader_name, SCARD_SHARE_DIRECT, 0
)
if result != SCARD_S_SUCCESS:
print(f"Direct connection failed: {result:08X}")
return
try:
# Get reader attributes
attributes = [
(SCARD_ATTR_VENDOR_NAME, "Vendor Name"),
(SCARD_ATTR_VENDOR_IFD_TYPE, "Reader Type"),
(SCARD_ATTR_VENDOR_IFD_VERSION, "Reader Version"),
(SCARD_ATTR_VENDOR_IFD_SERIAL_NO, "Serial Number")
]
print(f"Reader: {reader_name}")
print("Attributes:")
for attr_id, attr_name in attributes:
result, attr_data = SCardGetAttrib(card_handle, attr_id)
if result == SCARD_S_SUCCESS:
if attr_name in ["Vendor Name", "Reader Type"]:
# Convert to string (remove null terminators)
try:
attr_str = bytes(attr_data).decode('ascii').rstrip('\x00')
print(f" {attr_name}: {attr_str}")
except:
print(f" {attr_name}: {attr_data}")
else:
print(f" {attr_name}: {' '.join(f'{b:02X}' for b in attr_data)}")
else:
print(f" {attr_name}: Not available ({result:08X})")
# Try reader-specific control commands (example for CCID readers)
# Note: Control codes are reader-specific
try:
# Example: Get reader features (CCID)
control_code = 0x42000D48 # IOCTL_SMARTCARD_GET_FEATURE_REQUEST
result, features = SCardControl(card_handle, control_code, [])
if result == SCARD_S_SUCCESS:
print(f"Reader features: {len(features)} bytes")
print(f" Data: {' '.join(f'{b:02X}' for b in features)}")
except:
print(" Control commands not supported or failed")
finally:
SCardDisconnect(card_handle, SCARD_LEAVE_CARD)
finally:
SCardReleaseContext(context)
advanced_pcsc_operations()from smartcard.scard import *
def error_code_analysis():
"""Demonstrate PC/SC error handling."""
# Common result codes and their meanings
error_codes = {
SCARD_S_SUCCESS: "Success",
SCARD_E_CANCELLED: "Cancelled",
SCARD_E_INVALID_HANDLE: "Invalid handle",
SCARD_E_INVALID_PARAMETER: "Invalid parameter",
SCARD_E_NO_MEMORY: "No memory",
SCARD_E_TIMEOUT: "Timeout",
SCARD_E_SHARING_VIOLATION: "Sharing violation",
SCARD_E_NO_SMARTCARD: "No smart card",
SCARD_E_UNKNOWN_CARD: "Unknown card",
SCARD_E_PROTO_MISMATCH: "Protocol mismatch",
SCARD_E_NOT_READY: "Not ready",
SCARD_E_SYSTEM_CANCELLED: "System cancelled",
SCARD_E_NOT_TRANSACTED: "Not transacted",
SCARD_E_READER_UNAVAILABLE: "Reader unavailable",
SCARD_W_UNSUPPORTED_CARD: "Warning: Unsupported card",
SCARD_W_UNRESPONSIVE_CARD: "Warning: Unresponsive card",
SCARD_W_UNPOWERED_CARD: "Warning: Unpowered card",
SCARD_W_RESET_CARD: "Warning: Reset card",
SCARD_W_REMOVED_CARD: "Warning: Removed card"
}
def interpret_result(result_code):
"""Interpret PC/SC result code."""
if result_code in error_codes:
return error_codes[result_code]
elif result_code & 0x80000000:
return f"Error: {result_code:08X}"
else:
return f"Success/Warning: {result_code:08X}"
# Test various scenarios
print("PC/SC Error Code Analysis:")
# Try to establish context
result, context = SCardEstablishContext(SCARD_SCOPE_USER)
print(f"Context: {interpret_result(result)}")
if result == SCARD_S_SUCCESS:
# Try invalid reader
result, card, protocol = SCardConnect(
context, "NonexistentReader", SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0
)
print(f"Invalid reader: {interpret_result(result)}")
# Try with no timeout
result, states = SCardGetStatusChange(context, 0, [])
print(f"Zero timeout: {interpret_result(result)}")
SCardReleaseContext(context)
error_code_analysis()# PC/SC handle types
ContextHandle = int
CardHandle = int
# PC/SC data structures
ReaderState = dict # {'reader': str, 'current_state': int, 'event_state': int, 'atr': list[int]}
ProtocolInfo = tuple[int, int] # (protocol, pci_length)
# Result code type
ResultCode = int
# Common PC/SC constants (subset - full list available via import)
ScopeType = int # SCARD_SCOPE_* constants
ShareMode = int # SCARD_SHARE_* constants
Protocol = int # SCARD_PROTOCOL_* constants
Disposition = int # SCARD_*_CARD constants
StateFlag = int # SCARD_STATE_* constants
AttributeId = int # SCARD_ATTR_* constantsInstall with Tessl CLI
npx tessl i tessl/pypi-pyscard