import hashlib import hmac import struct from typing import Optional from smartcard.CardRequest import CardRequest from smartcard.CardConnection import CardConnection from secret import SECRET def read_page(connection, page): apdu = [0xFF, 0xB0, 0x00, page, 0x04] data, sw1, sw2 = connection.transmit(apdu) if sw1 == 0x90 and sw2 == 0x00: return data[:4] return None def extract_user_id(raw: bytes) -> int | None: try: # Must be exactly 10 bytes (2 ID + 8 MAC) if len(raw) != 10: return None user_bytes = raw[:2] stored_mac = raw[2:] expected_mac = hmac.new( SECRET, user_bytes, hashlib.sha256 ).digest()[:8] # Constant-time comparison if not hmac.compare_digest(stored_mac, expected_mac): return None # Decode big-endian unsigned short user_id = struct.unpack(">H", user_bytes)[0] return user_id except Exception as e: print("Error extracting user id:", e) return None def read_card(connection) -> Optional[int]: # Read pages 4–15 try: raw = [] for page in range(4, 16): data = read_page(connection, page) if data: raw.extend(data) # Parse NDEF if raw[0] == 0x03: # NDEF TLV length = raw[1] ndef = raw[2:2 + length] if ndef[0] == 0xD1 and ndef[3] == 0x54: lang_len = ndef[4] payload_hex = bytes(ndef[5 + lang_len:]).decode("utf-8") payload_bytes = bytes.fromhex(payload_hex) return extract_user_id(payload_bytes) except Exception as e: print("Error reading card:", e) return None return None def perform_single_console_read() -> None: """ Prints the user ID of the card to console """ print("Auf Ticket warten...") card_request = CardRequest(timeout=None) card_service = card_request.waitforcard() connection = card_service.connection connection.connect(CardConnection.T1_protocol) user_id = read_card(connection) if user_id is not None: print("User ID:", user_id) else: print("User ID nicht auf Ticket gefunden") def main(): print("Auf Ticket warten...") card_request = CardRequest(timeout=None) card_service = card_request.waitforcard() connection = card_service.connection connection.connect(CardConnection.T1_protocol) user_id = read_card(connection) if user_id is not None: print("User ID:", user_id) else: print("User ID nicht auf Ticket gefunden") if __name__ == "__main__": perform_single_console_read()