Smartcard library for Python providing PC/SC interface for smart card communication
—
Framework for waiting for card insertion, establishing connections, and managing card communication with fine-grained control over protocols, connection modes, and card services.
The CardRequest class provides functionality for waiting for card insertion and obtaining card services when cards become available.
class CardRequest:
def __init__(self, newcardonly=False, readers=None, cardType=None, cardServiceClass=None, timeout=1):
"""
Create a card request for waiting for card insertion.
Args:
newcardonly (bool): If True, only newly inserted cards trigger the request
readers (list[Reader], optional): List of readers to monitor. If None, monitors all readers
cardType (CardType, optional): Specific card type to wait for. If None, accepts any card
cardServiceClass (class, optional): Card service class to instantiate. Defaults to PassThruCardService
timeout (int): Timeout in seconds for waitforcard() calls
"""
def waitforcard(self):
"""
Wait for a card matching the request criteria.
Returns:
CardService: Card service instance for the inserted card
Raises:
CardRequestTimeoutException: If timeout expires without card insertion
CardRequestException: If card request fails
"""
def waitforcardevent(self):
"""
Wait for any card insertion or removal event.
Returns:
tuple: (inserted_cards, removed_cards) where each is a list of Card objects
"""
def getReaders(self):
"""
Get the list of readers being monitored by this request.
Returns:
list[Reader]: List of Reader objects
"""
def __enter__(self):
"""Context manager entry."""
def __exit__(self, exc_type, exc_val, exc_tb):
"""Context manager exit."""CardConnection provides the core interface for communicating with smart cards, with protocol management and connection control.
Note: CardConnection is an abstract base class. Actual implementations are provided by concrete classes like PCSCCardConnection in the smartcard.pcsc module. The methods below define the interface that all implementations must provide.
class CardConnection:
# Protocol constants
T0_protocol = 0x00000001
T1_protocol = 0x00000002
RAW_protocol = 0x00010000
T15_protocol = 0x00000008
def connect(self, protocol=None, mode=None, disposition=None):
"""
Connect to the smart card.
Args:
protocol (int, optional): Communication protocol (T0_protocol, T1_protocol, etc.)
mode (int, optional): Connection mode
disposition (int, optional): What to do with card when disconnecting
Raises:
CardConnectionException: If connection fails
"""
def reconnect(self, protocol=None, mode=None, disposition=None):
"""
Reconnect to the smart card.
Args:
protocol (int, optional): Communication protocol
mode (int, optional): Connection mode
disposition (int, optional): Disposition for reconnection
"""
def disconnect(self):
"""
Disconnect from the smart card.
"""
def release(self):
"""
Release the connection and associated resources.
"""
def transmit(self, command, protocol=None):
"""
Transmit an APDU command to the card.
Args:
command (list[int]): APDU command bytes
protocol (int, optional): Protocol to use for transmission
Returns:
tuple[list[int], int, int]: (response, sw1, sw2)
"""
def control(self, controlCode, command=None):
"""
Send a control command to the reader.
Args:
controlCode (int): Reader-specific control code
command (list[int], optional): Command data
Returns:
list[int]: Control command response
"""
def getATR(self):
"""
Get the Answer To Reset of the connected card.
Returns:
list[int]: ATR bytes
"""
def getProtocol(self):
"""
Get the active communication protocol.
Returns:
int: Protocol constant (T0_protocol, T1_protocol, etc.)
"""
def getReader(self):
"""
Get the reader associated with this connection.
Returns:
Reader: Reader object
"""
def setProtocol(self, protocol):
"""
Set the communication protocol.
Args:
protocol (int): Protocol constant
"""
def setErrorCheckingChain(self, errorcheckingchain):
"""
Set the error checking chain for status word processing.
Args:
errorcheckingchain (ErrorCheckingChain): Chain of error checkers
"""
def addSWExceptionToFilter(self, exClass):
"""
Add a status word exception class to the filter.
Args:
exClass (class): Exception class to filter
"""
def addObserver(self, observer):
"""
Add an observer for connection events.
Args:
observer (CardConnectionObserver): Observer to add
"""
def deleteObserver(self, observer):
"""
Remove an observer.
Args:
observer (CardConnectionObserver): Observer to remove
"""
def __enter__(self):
"""Context manager entry."""
def __exit__(self, exc_type, exc_val, exc_tb):
"""Context manager exit - automatically disconnects."""CardService provides an abstraction layer over card connections for implementing card-specific functionality.
class CardService:
def __init__(self, connection, cardname=None):
"""
Initialize card service with a connection.
Args:
connection (CardConnection): Connection to the card
cardname (str, optional): Name identifier for the card
"""
@staticmethod
def supports(cardname):
"""
Check if this service supports a given card.
Args:
cardname (str): Card name to check
Returns:
bool: True if card is supported
"""
def __enter__(self):
"""Context manager entry."""
def __exit__(self, exc_type, exc_val, exc_tb):
"""Context manager exit."""
class PassThruCardService(CardService):
"""Pass-through card service that supports all cards."""
@staticmethod
def supports(cardname):
"""Always returns True - supports all cards."""
return Trueclass Card:
def __init__(self, reader, atr):
"""
Create a card object.
Args:
reader (Reader): Reader containing the card
atr (list[int]): Answer To Reset bytes
"""
def createConnection(self):
"""
Create a connection to this card.
Returns:
CardConnection: Connection object for this card
"""
def __eq__(self, other):
"""Check card equality based on ATR and reader."""
def __hash__(self):
"""Hash based on ATR and reader."""
def __repr__(self):
"""String representation of the card."""
class Reader:
def __init__(self, readername):
"""
Create a reader object.
Args:
readername (str): Name of the reader
"""
def createConnection(self):
"""
Create a connection through this reader.
Returns:
CardConnection: New connection object
"""
def addtoreadergroup(self, groupname):
"""Add this reader to a reader group."""
def removefromreadergroup(self, groupname):
"""Remove this reader from a reader group."""from smartcard.CardRequest import CardRequest
from smartcard.CardType import AnyCardType
# Wait for any card insertion
cardtype = AnyCardType()
cardrequest = CardRequest(timeout=10, cardType=cardtype)
try:
cardservice = cardrequest.waitforcard()
print("Card inserted!")
# Use the card service
with cardservice:
connection = cardservice.connection
atr = connection.getATR()
print(f"ATR: {' '.join(f'{b:02X}' for b in atr)}")
# Send APDU
response, sw1, sw2 = connection.transmit([0x00, 0xA4, 0x04, 0x00])
print(f"Response: {response}, SW: {sw1:02X} {sw2:02X}")
except Exception as e:
print(f"Card request failed: {e}")from smartcard.CardRequest import CardRequest
from smartcard.CardType import ATRCardType
# Wait for card with specific ATR pattern
atr_pattern = [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]
atr_mask = [0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
cardtype = ATRCardType(atr_pattern, atr_mask)
cardrequest = CardRequest(timeout=30, cardType=cardtype)
cardservice = cardrequest.waitforcard()
print("Matching card inserted!")from smartcard.System import readers
from smartcard.CardRequest import CardRequest
# Get first reader and create connection
reader_list = readers()
if reader_list:
reader = reader_list[0]
connection = reader.createConnection()
try:
# Connect with specific protocol
connection.connect(connection.T1_protocol)
print(f"Connected using T=1 protocol")
# Check active protocol
protocol = connection.getProtocol()
print(f"Active protocol: {protocol}")
# Transmit command
GET_CHALLENGE = [0x00, 0x84, 0x00, 0x00, 0x08]
response, sw1, sw2 = connection.transmit(GET_CHALLENGE)
if sw1 == 0x90 and sw2 == 0x00:
print(f"Challenge: {' '.join(f'{b:02X}' for b in response)}")
except Exception as e:
print(f"Connection error: {e}")
finally:
connection.disconnect()from smartcard.CardRequest import CardRequest
from smartcard.CardType import AnyCardType
cardrequest = CardRequest(timeout=10, cardType=AnyCardType())
try:
with cardrequest.waitforcard() as cardservice:
with cardservice.connection as connection:
# Connection automatically managed
atr = connection.getATR()
response, sw1, sw2 = connection.transmit([0x00, 0xCA, 0x9F, 0x7F, 0x00])
print(f"ATR: {atr}")
print(f"Response: {response}, Status: {sw1:02X} {sw2:02X}")
# Connection automatically disconnected
# Service automatically cleaned up
except Exception as e:
print(f"Error: {e}")# Exception types
class CardConnectionException(SmartcardException):
"""Raised when card connection operations fail."""
class CardRequestException(SmartcardException):
"""Raised when card request operations fail."""
class CardRequestTimeoutException(CardRequestException):
"""Raised when card request times out."""
class CardServiceException(SmartcardException):
"""Raised when card service operations fail."""
class NoCardException(SmartcardException):
"""Raised when no card is present in reader."""
class InvalidReaderException(SmartcardException):
"""Raised when trying to access an invalid smartcard reader."""
class NoReadersException(SmartcardException):
"""Raised when the system has no smartcard reader."""
class InvalidATRMaskLengthException(SmartcardException):
"""Raised when an ATR mask does not match an ATR length."""
# Connection event types
class CardConnectionEvent:
def __init__(self, event_type, args=None):
"""
Card connection event.
Args:
event_type (str): 'connect', 'reconnect', 'disconnect', 'command', 'response'
args: Event-specific arguments
"""
class CardConnectionObserver:
def update(self, cardconnection, ccevent):
"""
Called when card connection events occur.
Args:
cardconnection (CardConnection): The connection that generated the event
ccevent (CardConnectionEvent): Event details
"""Install with Tessl CLI
npx tessl i tessl/pypi-pyscard