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

status-word-handling.mddocs/

Status Word Handling

Structured error checking and exception handling for smart card status words (SW1, SW2) with support for various ISO standards and custom error checking chains. Status words indicate the result of APDU command execution.

Capabilities

Status Word Exceptions

Exception hierarchy for handling different categories of smart card status word errors.

class SWException(SmartcardException):
    """
    Base exception for smart card status word errors.
    
    Args:
        data (list[int]): Response data from APDU
        sw1 (int): First status word byte (0x00-0xFF)
        sw2 (int): Second status word byte (0x00-0xFF)
    """

class WarningProcessingException(SWException):
    """
    Non-volatile memory unchanged (SW1=62) or corrupted (SW1=63).
    Indicates warnings during command processing.
    """

class ExecutionErrorException(SWException):
    """
    Non-volatile memory changed (SW1=64) or unchanged (SW1=65).
    Indicates execution errors during command processing.
    """

class SecurityRelatedException(SWException):
    """
    Security-related errors (SW1=66).
    Indicates authentication or access control failures.
    """

class CheckingErrorException(SWException):
    """
    Wrong length or instruction errors (SW1=67-6F).
    Indicates parameter or command format errors.
    """

Error Checker Classes

Base classes and implementations for checking status words and raising appropriate exceptions.

class ErrorChecker:
    """Abstract base class for status word error checking. Concrete implementations include ISO7816_4ErrorChecker, ISO7816_8ErrorChecker, and ISO7816_9ErrorChecker."""
    
    def __call__(self, data, sw1, sw2):
        """
        Check status words and raise exception if error detected.
        
        Args:
            data (list[int]): Response data from APDU
            sw1 (int): First status word byte
            sw2 (int): Second status word byte
        
        Raises:
            SWException: If error condition is detected
        """

class ISO7816_4ErrorChecker(ErrorChecker):
    """Error checker for ISO 7816-4 status words."""

class ISO7816_4_SW1ErrorChecker(ErrorChecker):
    """Error checker focusing on SW1 values per ISO 7816-4."""

class ISO7816_8ErrorChecker(ErrorChecker):
    """Error checker for ISO 7816-8 (cryptographic) status words."""

class ISO7816_9ErrorChecker(ErrorChecker):
    """Error checker for ISO 7816-9 (enhanced cryptographic) status words."""

class op21_ErrorChecker(ErrorChecker):
    """Error checker for Open Platform 2.1 status words."""

Error Checking Chain

Mechanism for combining multiple error checkers in a processing chain.

class ErrorCheckingChain:
    """Chain multiple error checkers for comprehensive status word analysis."""
    
    def __init__(self, chain, strategy):
        """
        Initialize error checking chain.
        
        Args:
            chain (list): List to append this error checker to
            strategy (ErrorChecker): Error checking strategy to add to chain
        """
    
    def addErrorChecker(self, errorchecker):
        """
        Add an error checker to the chain.
        
        Args:
            errorchecker (ErrorChecker): Error checker to add
        """
    
    def removeErrorChecker(self, errorchecker):
        """
        Remove an error checker from the chain.
        
        Args:
            errorchecker (ErrorChecker): Error checker to remove
        """
    
    def __call__(self, data, sw1, sw2):
        """
        Execute all error checkers in the chain.
        
        Args:
            data (list[int]): Response data
            sw1 (int): First status word
            sw2 (int): Second status word
        
        Raises:
            SWException: If any checker detects an error
        """

Usage Examples

Basic Status Word Checking

from smartcard.sw.ISO7816_4ErrorChecker import ISO7816_4ErrorChecker
from smartcard.sw.SWExceptions import SWException
from smartcard import Session

def check_apdu_response(command, session):
    """Send APDU and check status words."""
    
    # Create error checker
    error_checker = ISO7816_4ErrorChecker()
    
    try:
        # Send command
        response, sw1, sw2 = session.sendCommandAPDU(command)
        
        # Check for errors
        error_checker(response, sw1, sw2)
        
        print(f"✓ Command successful: {sw1:02X} {sw2:02X}")
        return response
        
    except SWException as e:
        print(f"✗ Status word error: {e}")
        print(f"  SW1 SW2: {e.sw1:02X} {e.sw2:02X}")
        if hasattr(e, 'data') and e.data:
            print(f"  Data: {' '.join(f'{b:02X}' for b in e.data)}")
        raise

# Example usage
try:
    session = Session()
    
    # Valid command (should succeed)
    SELECT_MF = [0x00, 0xA4, 0x00, 0x00]
    response = check_apdu_response(SELECT_MF, session)
    
    # Invalid command (will likely fail)
    INVALID_CMD = [0xFF, 0xFF, 0xFF, 0xFF]
    response = check_apdu_response(INVALID_CMD, session)
    
    session.close()
    
except Exception as e:
    print(f"Session error: {e}")

Error Checking Chain

from smartcard.sw.ErrorCheckingChain import ErrorCheckingChain
from smartcard.sw.ISO7816_4ErrorChecker import ISO7816_4ErrorChecker
from smartcard.sw.ISO7816_8ErrorChecker import ISO7816_8ErrorChecker
from smartcard.sw.SWExceptions import *
from smartcard import Session

def comprehensive_error_checking():
    """Demonstrate error checking chain usage."""
    
    # Create error checking chain
    chain = ErrorCheckingChain()
    chain.addErrorChecker(ISO7816_4ErrorChecker())
    chain.addErrorChecker(ISO7816_8ErrorChecker())
    
    session = Session()
    
    commands = [
        ([0x00, 0xA4, 0x00, 0x00], "SELECT Master File"),
        ([0x00, 0xCA, 0x9F, 0x7F, 0x00], "GET DATA"),
        ([0x00, 0x84, 0x00, 0x00, 0x08], "GET CHALLENGE"),
        ([0xFF, 0xFF, 0xFF, 0xFF], "Invalid Command")
    ]
    
    for command, description in commands:
        try:
            print(f"\nTesting: {description}")
            print(f"Command: {' '.join(f'{b:02X}' for b in command)}")
            
            response, sw1, sw2 = session.sendCommandAPDU(command)
            
            # Apply error checking chain
            chain(response, sw1, sw2)
            
            print(f"✓ Success: {sw1:02X} {sw2:02X}")
            if response:
                print(f"  Response: {' '.join(f'{b:02X}' for b in response)}")
                
        except WarningProcessingException as e:
            print(f"⚠ Warning: {e}")
        except ExecutionErrorException as e:
            print(f"✗ Execution Error: {e}")
        except SecurityRelatedException as e:
            print(f"🔒 Security Error: {e}")
        except CheckingErrorException as e:
            print(f"📋 Parameter Error: {e}")
        except SWException as e:
            print(f"❌ Status Word Error: {e}")
    
    session.close()

comprehensive_error_checking()

Custom Error Checker

from smartcard.sw.ErrorChecker import ErrorChecker
from smartcard.sw.SWExceptions import SWException

class CustomApplicationException(SWException):
    """Custom exception for application-specific errors."""
    pass

class ApplicationErrorChecker(ErrorChecker):
    """Custom error checker for application-specific status words."""
    
    def __call__(self, data, sw1, sw2):
        # Check for application-specific error patterns
        
        # Example: Custom application returns 0x90 0x01 for partial success
        if sw1 == 0x90 and sw2 == 0x01:
            raise CustomApplicationException(
                data, sw1, sw2, 
                "Partial success - some data may be incomplete"
            )
        
        # Example: Application-specific authentication error
        if sw1 == 0x63 and 0xC0 <= sw2 <= 0xCF:
            remaining_tries = sw2 & 0x0F
            raise CustomApplicationException(
                data, sw1, sw2,
                f"Authentication failed - {remaining_tries} tries remaining"
            )
        
        # Example: Application-specific file errors
        if sw1 == 0x94:
            error_messages = {
                0x00: "No current EF",
                0x02: "Address range exceeded", 
                0x04: "File ID not found",
                0x08: "File incompatible with command"
            }
            message = error_messages.get(sw2, f"File error: {sw2:02X}")
            raise CustomApplicationException(data, sw1, sw2, message)

def test_custom_error_checker():
    """Test custom error checker implementation."""
    
    checker = ApplicationErrorChecker()
    
    test_cases = [
        ([], 0x90, 0x00, "Should pass"),
        ([], 0x90, 0x01, "Partial success"),
        ([], 0x63, 0xC3, "Auth failed, 3 tries left"),
        ([], 0x94, 0x04, "File not found"),
        ([], 0x94, 0xFF, "Unknown file error")
    ]
    
    for data, sw1, sw2, description in test_cases:
        try:
            print(f"\nTesting: {description} ({sw1:02X} {sw2:02X})")
            checker(data, sw1, sw2)
            print("✓ No error detected")
            
        except CustomApplicationException as e:
            print(f"🔍 Custom error: {e}")
        except Exception as e:
            print(f"❌ Unexpected error: {e}")

test_custom_error_checker()

Connection-Level Error Checking

from smartcard.CardRequest import CardRequest
from smartcard.CardType import AnyCardType
from smartcard.sw.ErrorCheckingChain import ErrorCheckingChain
from smartcard.sw.ISO7816_4ErrorChecker import ISO7816_4ErrorChecker

def connection_with_error_checking():
    """Demonstrate setting error checking at connection level."""
    
    # Wait for card
    cardrequest = CardRequest(timeout=10, cardType=AnyCardType())
    
    try:
        cardservice = cardrequest.waitforcard()
        
        with cardservice:
            connection = cardservice.connection
            
            # Set up error checking chain for this connection
            error_chain = ErrorCheckingChain()
            error_chain.addErrorChecker(ISO7816_4ErrorChecker())
            connection.setErrorCheckingChain(error_chain)
            
            print("Error checking enabled for connection")
            
            # Now all transmit operations will automatically check status words
            try:
                # This will automatically check status words
                response, sw1, sw2 = connection.transmit([0x00, 0xA4, 0x00, 0x00])
                print(f"✓ SELECT successful: {sw1:02X} {sw2:02X}")
                
                # This might fail and raise an exception automatically
                response, sw1, sw2 = connection.transmit([0xFF, 0xFF, 0xFF, 0xFF])
                print(f"✓ Invalid command succeeded: {sw1:02X} {sw2:02X}")
                
            except Exception as e:
                print(f"🚫 Connection error checking caught: {e}")
                
    except Exception as e:
        print(f"Card request failed: {e}")

connection_with_error_checking()

Status Word Analysis

def analyze_status_words(sw1, sw2):
    """Analyze and explain status word meanings."""
    
    print(f"Status Words: {sw1:02X} {sw2:02X}")
    
    # Success cases
    if sw1 == 0x90 and sw2 == 0x00:
        print("✓ Success - Command completed successfully")
        return
    
    if sw1 == 0x61:
        print(f"✓ Success - {sw2} bytes available with GET RESPONSE")
        return
    
    # Warning cases (SW1 = 62, 63)
    if sw1 == 0x62:
        warnings = {
            0x00: "No information given (NV-RAM not changed)",
            0x81: "Part of returned data may be corrupted",
            0x82: "End of file/record reached before reading Le bytes",
            0x83: "Selected file invalidated",
            0x84: "Selected file is not valid (FCI not formated)"
        }
        msg = warnings.get(sw2, f"Warning: Non-volatile memory unchanged ({sw2:02X})")
        print(f"⚠ {msg}")
    
    elif sw1 == 0x63:
        if 0xC0 <= sw2 <= 0xCF:
            tries = sw2 & 0x0F
            print(f"⚠ Authentication failed - {tries} tries remaining")
        else:
            warnings = {
                0x00: "No information given (NV-RAM changed)",
                0x81: "File filled up by the last write"
            }
            msg = warnings.get(sw2, f"Warning: Non-volatile memory changed ({sw2:02X})")
            print(f"⚠ {msg}")
    
    # Error cases (SW1 = 64, 65, 66, 67-6F)
    elif sw1 == 0x64:
        print(f"❌ Execution error: Non-volatile memory changed ({sw2:02X})")
    
    elif sw1 == 0x65:
        print(f"❌ Execution error: Non-volatile memory unchanged ({sw2:02X})")
    
    elif sw1 == 0x66:
        print(f"🔒 Security error: {sw2:02X}")
    
    elif 0x67 <= sw1 <= 0x6F:
        error_types = {
            0x67: "Wrong length",
            0x68: "Function in CLA not supported", 
            0x69: "Command not allowed",
            0x6A: "Wrong parameters P1-P2",
            0x6B: "Wrong parameters P1-P2", 
            0x6C: f"Wrong Le field (correct length: {sw2})",
            0x6D: "Instruction code not supported",
            0x6E: "Class not supported",
            0x6F: "No precise diagnosis"
        }
        error_msg = error_types.get(sw1, f"Parameter error ({sw1:02X})")
        print(f"📋 {error_msg}: {sw2:02X}")
    
    else:
        print(f"❓ Unknown status words: {sw1:02X} {sw2:02X}")

# Test status word analysis
test_status_words = [
    (0x90, 0x00), (0x61, 0x10), (0x62, 0x83), (0x63, 0xC2),
    (0x64, 0x00), (0x66, 0x00), (0x67, 0x00), (0x6C, 0x08),
    (0x6D, 0x00), (0x6E, 0x00), (0x9F, 0x10)
]

print("Status Word Analysis:")
for sw1, sw2 in test_status_words:
    print()
    analyze_status_words(sw1, sw2)

Related Types

# Exception hierarchy
class SmartcardException(Exception):
    """Base exception for smartcard operations."""

class SWException(SmartcardException):
    """Base class for status word exceptions."""
    def __init__(self, data, sw1, sw2, message=""):
        self.data = data
        self.sw1 = sw1  
        self.sw2 = sw2

# Type aliases
StatusWord = int        # SW1 or SW2 (0x00-0xFF)
ResponseData = list[int]  # APDU response data
ErrorChecker = callable   # Function/class that checks status words

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