""" Simple CLI Example - Smart Card Command Tool A command-line interface demonstrating how to use the API.py library for sending APDU and escape commands to smart card readers. Usage: python example_cli.py """ from API import list_readers, connect_to_reader, send_command, disconnect_reader, parse_command_string from smartcard.util import toHexString from smartcard.Exceptions import NoCardException def list_available_readers(): """List and display all available smart card readers.""" readers = list_readers() if not readers: print("No smart card readers found.") return [] print("Available readers:") for index, reader in enumerate(readers): print(f"{index + 1}: {reader}") return readers def connect_to_reader_with_fallback(reader): """ Connect to reader with automatic fallback logic. Tries standard mode (APDU) first, falls back to direct mode (escape) if no card. Returns: tuple: (connection, is_direct_mode, error_msg, context) """ # Try standard connection first (APDU mode - requires card) connection, error_msg, _ = connect_to_reader(reader, mode='share') if connection: print("Connected in standard mode (APDU)") return connection, False, None, None # If connection failed due to no card, try direct mode (escape commands) if error_msg and "No card" in error_msg: print("No card detected, attempting direct mode...") hcard, error_msg, hcontext = connect_to_reader(reader, mode='direct') if hcard: print("Connected using Direct Mode (Escape commands)") return hcard, True, None, hcontext else: print(f"Direct Mode Failed: {error_msg}") return None, False, error_msg, None else: print(f"Standard Connection Failed: {error_msg}") return None, False, error_msg, None def transmit_apdu(connection, command_hex): """Send an APDU command and display the response.""" try: success, response, sw1, sw2, error_msg = send_command( connection, command_hex, mode='apdu' ) if success: print(f"Response: {toHexString(response)}") print(f"Status words: {sw1:02X} {sw2:02X}") else: print(f"Transmit Failed: {error_msg}") except Exception as e: print(f"Transmit Failed: {str(e)}") def transmit_escape(connection, command_hex): """Send an escape command and display the response.""" try: command_bytes = parse_command_string(command_hex) success, response, error_msg = send_command( connection, command_bytes, mode='escape' ) if success: if response and len(response) > 0: # Show first 50 bytes if response is long if len(response) > 50: response_str = toHexString(response[:50]) print(f"Response ({len(response)} bytes): {response_str}...") else: print(f"Response ({len(response)} bytes): {toHexString(response)}") else: print("Response: (empty)") else: print(f"Control Failed: {error_msg}") except Exception as e: print(f"Control Failed: {str(e)}") def main(): """Main interactive loop following apdu.py logic.""" print("=" * 80) print("Smart Card Command Tool - CLI Example") print("=" * 80) print() while True: # List available readers pcsc_readers = list_available_readers() if not pcsc_readers: print("No readers available, exiting...") break # Prompt user to select reader or quit try: user_input = input("\nSelect the reader to connect or press 'q' to exit: ").strip() except KeyboardInterrupt: print("\n\nExiting...") break if user_input.lower() == 'q': break if not user_input: print("Please enter a number or 'q'") continue try: reader_index = int(user_input) - 1 if 0 <= reader_index < len(pcsc_readers): selected_reader = pcsc_readers[reader_index] # Connect with automatic fallback connection, is_direct, error_msg, hcontext = connect_to_reader_with_fallback(selected_reader) if connection: try: if is_direct: # Direct mode - escape commands data = input("Enter the control data to transmit (in hex, e.g., 'E0 00 00 18 00'): ").strip() if data: transmit_escape(connection, data) else: # Standard mode - APDU commands data = input("Enter the APDU to transmit (in hex, e.g., 'FF CA 00 00 00'): ").strip() if data: transmit_apdu(connection, data) except KeyboardInterrupt: print("\nCommand cancelled.") except Exception as e: print(f"Error: {str(e)}") finally: # Disconnect based on mode if is_direct: disconnect_reader(connection, hcontext) else: disconnect_reader(connection) else: print("Connection failed") else: print("Invalid reader index") except ValueError: print("Please enter a valid number or 'q'") except KeyboardInterrupt: print("\n\nExiting...") break print("\nProgram completed.") if __name__ == "__main__": main()