first init
This commit is contained in:
+172
@@ -0,0 +1,172 @@
|
||||
"""
|
||||
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()
|
||||
Reference in New Issue
Block a user