Smartcard library for Python providing PC/SC interface for smart card communication
—
Comprehensive utility functions for data conversion between different formats (hex strings, byte lists, ASCII), padding operations, and specialized encoding support. These utilities are essential for smart card data processing and formatting.
Core functions for converting between different data representations commonly used in smart card applications.
def toHexString(data, output_format=0):
"""
Convert a list of integers to formatted hexadecimal string.
Args:
data (list[int] | None): List of bytes to convert (0-255)
output_format (int): Formatting options (bitwise OR of format constants)
Returns:
str: Formatted hexadecimal string
Format constants:
PACK (1): Remove spaces between bytes
HEX (2): Add 0x prefix to each byte
UPPERCASE (4): Use uppercase 0X prefix (requires HEX)
COMMA (8): Add commas between bytes
"""
def toBytes(bytestring):
"""
Convert hexadecimal string to list of integers.
Args:
bytestring (str): Hex string (spaces optional), e.g. "3B 65 00" or "3B6500"
Returns:
list[int]: List of byte values (0-255)
Raises:
TypeError: If string is not valid hexadecimal
"""
def toASCIIBytes(string):
"""
Convert string to list of UTF-8 encoded bytes.
Args:
string (str): String to convert
Returns:
list[int]: List of UTF-8 byte values
"""
def toASCIIString(bytelist):
"""
Convert list of integers to ASCII string.
Args:
bytelist (list[int]): List of bytes (0-255)
Returns:
str: ASCII string with non-printable chars (outside 32-127) as periods
"""Support for GSM 3.38 encoding commonly used in telecommunications smart cards.
def toGSM3_38Bytes(stringtoconvert):
"""
Convert string to GSM 3.38 encoded bytes.
Args:
stringtoconvert (str | bytes): String to encode using GSM 3.38 character set
Returns:
list[int]: List of GSM 3.38 encoded bytes
Note:
Supports GSM 3.38 character set including Greek letters and special symbols
"""Utility for padding byte lists to specified lengths.
def padd(bytelist, length, padding="FF"):
"""
Pad byte list with constant value to specified length.
Args:
bytelist (list[int]): List of bytes to pad
length (int): Target length (no padding if length <= current length)
padding (str): Hex string for padding byte (default "FF" = 0xFF)
Returns:
list[int]: Padded byte list
"""Constants for controlling hex string formatting output.
# Format constants for toHexString()
PACK = 1 # Remove spaces between bytes: "3B6500"
HEX = 2 # Add 0x prefix: "0x3B 0x65 0x00"
UPPERCASE = 4 # Use 0X prefix: "0X3B 0X65 0X00" (requires HEX)
COMMA = 8 # Add commas: "3B, 65, 00"Legacy functions maintained for backward compatibility.
def HexListToBinString(hexlist):
"""
Deprecated: Convert hex list to binary string.
Use bytes(hexlist).decode("utf-8") instead.
Args:
hexlist (list[int]): List of bytes
Returns:
str: Decoded string
"""
def BinStringToHexList(binstring):
"""
Deprecated: Convert binary string to hex list.
Use list(binstring.encode("utf-8")) instead.
Args:
binstring (str): String to encode
Returns:
list[int]: List of encoded bytes
"""
# Deprecated aliases
hl2bs = HexListToBinString
bs2hl = BinStringToHexListfrom smartcard.util import toHexString, toBytes, toASCIIBytes, toASCIIString
# Convert bytes to hex string (default format)
data = [0x3B, 0x65, 0x00, 0x00, 0x9C, 0x11, 0x01, 0x01, 0x03]
hex_str = toHexString(data)
print(f"Default: {hex_str}") # "3B 65 00 00 9C 11 01 01 03"
# Convert hex string back to bytes
bytes_back = toBytes(hex_str)
print(f"Bytes: {bytes_back}") # [59, 101, 0, 0, 156, 17, 1, 1, 3]
# ASCII conversion
text = "Hello, Card!"
ascii_bytes = toASCIIBytes(text)
print(f"ASCII bytes: {ascii_bytes}")
text_back = toASCIIString(ascii_bytes)
print(f"Text back: {text_back}") # "Hello, Card!"from smartcard.util import toHexString, PACK, HEX, UPPERCASE, COMMA
data = [0x3B, 0x65, 0x00, 0x00, 0x9C]
# Different formatting options
print(f"Default: {toHexString(data)}") # "3B 65 00 00 9C"
print(f"Packed: {toHexString(data, PACK)}") # "3B6500009C"
print(f"With 0x: {toHexString(data, HEX)}") # "0x3B 0x65 0x00 0x00 0x9C"
print(f"Uppercase: {toHexString(data, HEX | UPPERCASE)}") # "0X3B 0X65 0X00 0X00 0X9C"
print(f"Commas: {toHexString(data, COMMA)}") # "3B, 65, 00, 00, 9C"
print(f"All opts: {toHexString(data, HEX | UPPERCASE | COMMA)}") # "0X3B, 0X65, 0X00, 0X00, 0X9C"from smartcard.util import padd, toHexString
# Pad to specific length
data = [0x3B, 0x65, 0x00]
padded = padd(data, 8) # Pad to 8 bytes with 0xFF
print(f"Padded: {toHexString(padded)}") # "3B 65 00 FF FF FF FF FF"
# Custom padding value
padded_custom = padd(data, 8, "00") # Pad with 0x00
print(f"Zero pad: {toHexString(padded_custom)}") # "3B 65 00 00 00 00 00 00"
# Pad with specific value
padded_80 = padd(data, 6, "80") # Pad with 0x80
print(f"0x80 pad: {toHexString(padded_80)}") # "3B 65 00 80 80 80"from smartcard.util import toGSM3_38Bytes, toHexString
# GSM 3.38 supports special characters
text = "@£$¥èéùì" # GSM 3.38 special characters
gsm_bytes = toGSM3_38Bytes(text)
print(f"GSM 3.38: {toHexString(gsm_bytes)}")
# Regular ASCII characters
text2 = "Hello123"
gsm_bytes2 = toGSM3_38Bytes(text2)
print(f"ASCII in GSM: {toHexString(gsm_bytes2)}")from smartcard.util import toHexString, toBytes, toASCIIString, padd
from smartcard import Session
def format_apdu_trace(command, response, sw1, sw2):
"""Format APDU command and response for logging."""
cmd_hex = toHexString(command, toHexString.HEX | toHexString.UPPERCASE)
if response:
resp_hex = toHexString(response)
print(f">> {cmd_hex}")
print(f"<< {resp_hex} {sw1:02X} {sw2:02X}")
else:
print(f">> {cmd_hex}")
print(f"<< {sw1:02X} {sw2:02X}")
def process_text_data(response_bytes):
"""Process response data that might contain text."""
# Try to extract readable text
text = toASCIIString(response_bytes)
# Show both hex and text representations
print(f"Hex: {toHexString(response_bytes)}")
print(f"Text: '{text}'")
# Check for meaningful text (more than 50% printable)
printable_count = sum(1 for c in text if c.isprintable() and c != '.')
if printable_count / len(text) > 0.5:
print("Data appears to contain readable text")
else:
print("Data appears to be binary")
# Example usage with session
try:
session = Session()
# Send GET DATA command
GET_DATA = [0x00, 0xCA, 0x9F, 0x7F, 0x00]
response, sw1, sw2 = session.sendCommandAPDU(GET_DATA)
# Format the trace
format_apdu_trace(GET_DATA, response, sw1, sw2)
# Process response if successful
if sw1 == 0x90 and sw2 == 0x00 and response:
process_text_data(response)
session.close()
except Exception as e:
print(f"Error: {e}")# Type aliases for utility functions
HexString = str
ByteList = list[int]
ASCIIString = str
FormatFlags = int
# Format constant values
PACK: int = 1
HEX: int = 2
UPPERCASE: int = 4
COMMA: int = 8Install with Tessl CLI
npx tessl i tessl/pypi-pyscard