CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pyscard

Smartcard library for Python providing PC/SC interface for smart card communication

Pending
Overview
Eval results
Files

pcsc-interface.mddocs/

PC/SC Interface

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.

Capabilities

Low-Level PC/SC Access

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)
    """

PC/SC Constants

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 = 0x00090300

Usage Examples

Basic PC/SC Operations

from 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()

Reader State Monitoring

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)

Advanced PC/SC Operations

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()

Error Handling and Result Codes

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()

Related Types

# 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_* constants

Install with Tessl CLI

npx tessl i tessl/pypi-pyscard

docs

atr-card-types.md

card-connections.md

gui-components.md

index.md

monitoring.md

pcsc-interface.md

reader-management.md

session-api.md

status-word-handling.md

utilities.md

tile.json