initial commit
This commit is contained in:
parent
1b9a89ce73
commit
ddc60f80fb
70
ClipboardReaderService.py
Normal file
70
ClipboardReaderService.py
Normal file
@ -0,0 +1,70 @@
|
||||
import time
|
||||
import pyperclip
|
||||
|
||||
from smartcard.CardRequest import CardRequest
|
||||
from smartcard.Exceptions import CardConnectionException
|
||||
from smartcard.CardConnection import CardConnection
|
||||
from smartcard.util import toHexString
|
||||
|
||||
from card_reader import read_card
|
||||
|
||||
|
||||
class ClipboardReaderService:
|
||||
def __init__(self) -> None:
|
||||
self._is_running = True
|
||||
self._last_uid = None
|
||||
|
||||
def run(self) -> None:
|
||||
print("Reader ready.")
|
||||
|
||||
while self._is_running:
|
||||
try:
|
||||
card_request = CardRequest(timeout=None)
|
||||
card_service = card_request.waitforcard()
|
||||
connection = card_service.connection
|
||||
|
||||
try:
|
||||
connection.connect(CardConnection.T1_protocol)
|
||||
|
||||
GET_UID = [0xFF, 0xCA, 0x00, 0x00, 0x00]
|
||||
uid, sw1, sw2 = connection.transmit(GET_UID)
|
||||
|
||||
if sw1 != 0x90:
|
||||
raise CardConnectionException("UID read failed")
|
||||
|
||||
uid_hex = toHexString(uid)
|
||||
|
||||
if uid_hex == self._last_uid:
|
||||
connection.disconnect()
|
||||
time.sleep(0.5)
|
||||
continue
|
||||
|
||||
self._last_uid = uid_hex
|
||||
|
||||
user_id = read_card(connection)
|
||||
|
||||
if user_id is not None:
|
||||
pyperclip.copy(str(user_id))
|
||||
else:
|
||||
pyperclip.copy("Fehler: Erneut scannen")
|
||||
|
||||
except CardConnectionException:
|
||||
pass
|
||||
|
||||
finally:
|
||||
try:
|
||||
connection.disconnect()
|
||||
except:
|
||||
pass
|
||||
|
||||
time.sleep(0.5)
|
||||
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
self.stop()
|
||||
|
||||
def stop(self) -> None:
|
||||
self._is_running = False
|
||||
|
||||
if __name__ == "__main__":
|
||||
service = ClipboardReaderService()
|
||||
service.run()
|
||||
101
card_reader.py
Normal file
101
card_reader.py
Normal file
@ -0,0 +1,101 @@
|
||||
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()
|
||||
86
card_writer.py
Normal file
86
card_writer.py
Normal file
@ -0,0 +1,86 @@
|
||||
import hmac
|
||||
import hashlib
|
||||
import struct
|
||||
|
||||
from smartcard.CardConnection import CardConnection
|
||||
from smartcard.CardRequest import CardRequest
|
||||
|
||||
from secret import SECRET
|
||||
|
||||
|
||||
def generate_payload(user_id: int) -> bytes:
|
||||
user_bytes = struct.pack(">H", user_id)
|
||||
|
||||
mac = hmac.new(
|
||||
SECRET,
|
||||
user_bytes,
|
||||
hashlib.sha256
|
||||
).digest()
|
||||
|
||||
mac_truncated = mac[:8]
|
||||
|
||||
return user_bytes + mac_truncated
|
||||
|
||||
def write_page(connection, page, data4):
|
||||
apdu = [0xFF, 0xD6, 0x00, page, 0x04] + data4
|
||||
response, sw1, sw2 = connection.transmit(apdu)
|
||||
return sw1 == 0x90 and sw2 == 0x00
|
||||
|
||||
def write_ndef_text(connection, text, start_page=4) -> bool:
|
||||
text_bytes = text.encode("utf-8")
|
||||
lang = b"en"
|
||||
|
||||
ndef_record = bytearray()
|
||||
|
||||
ndef_record.append(0xD1)
|
||||
ndef_record.append(0x01)
|
||||
ndef_record.append(len(text_bytes) + 1 + len(lang))
|
||||
ndef_record.append(0x54)
|
||||
ndef_record.append(len(lang))
|
||||
ndef_record.extend(lang)
|
||||
ndef_record.extend(text_bytes)
|
||||
|
||||
tlv = bytearray()
|
||||
tlv.append(0x03)
|
||||
tlv.append(len(ndef_record))
|
||||
tlv.extend(ndef_record)
|
||||
tlv.append(0xFE)
|
||||
|
||||
# Pad to multiple of 4 bytes
|
||||
while len(tlv) % 4 != 0:
|
||||
tlv.append(0x00)
|
||||
|
||||
page = start_page
|
||||
for i in range(0, len(tlv), 4):
|
||||
chunk = list(tlv[i:i + 4])
|
||||
if not write_page(connection, page, chunk):
|
||||
print(f"Konnte Seite {page} nicht schreiben. Ticket kaputt.")
|
||||
return False
|
||||
page += 1
|
||||
|
||||
print("Erfolgreich geschrieben")
|
||||
return True
|
||||
|
||||
def write_secure_user_id(connection, user_id: int):
|
||||
payload = generate_payload(user_id)
|
||||
hex_string = payload.hex().upper()
|
||||
write_ndef_text(connection, hex_string)
|
||||
|
||||
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)
|
||||
|
||||
try:
|
||||
user_id = int(input("User ID eingeben: "))
|
||||
except ValueError:
|
||||
print("User ID muss numerisch sein!")
|
||||
exit(1)
|
||||
|
||||
write_secure_user_id(connection, user_id)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
1
hmac_key.example.txt
Normal file
1
hmac_key.example.txt
Normal file
@ -0,0 +1 @@
|
||||
Alkohol1
|
||||
58
main.py
Normal file
58
main.py
Normal file
@ -0,0 +1,58 @@
|
||||
from smartcard.CardRequest import CardRequest
|
||||
from smartcard.util import toHexString
|
||||
from smartcard.CardConnection import CardConnection
|
||||
|
||||
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 main():
|
||||
c = input("1 = read, 2 = write")
|
||||
print("Waiting for NTAG...")
|
||||
card_request = CardRequest(timeout=None)
|
||||
card_service = card_request.waitforcard()
|
||||
connection = card_service.connection
|
||||
connection.connect(CardConnection.T1_protocol)
|
||||
|
||||
if c == "1":
|
||||
# UID
|
||||
GET_UID = [0xFF, 0xCA, 0x00, 0x00, 0x00]
|
||||
uid, _, _ = connection.transmit(GET_UID)
|
||||
print("UID:", toHexString(uid))
|
||||
|
||||
# Read pages 4–15
|
||||
raw = []
|
||||
for page in range(4, 16):
|
||||
data = read_page(connection, page)
|
||||
if data:
|
||||
raw.extend(data)
|
||||
|
||||
print("\nRaw HEX:")
|
||||
print(toHexString(raw))
|
||||
|
||||
# Parse NDEF
|
||||
if raw[0] == 0x03: # NDEF TLV
|
||||
length = raw[1]
|
||||
ndef = raw[2:2+length]
|
||||
|
||||
if ndef[0] == 0xD1 and ndef[3] == 0x54: # Text record
|
||||
lang_len = ndef[4]
|
||||
text = bytes(ndef[5+lang_len:]).decode("utf-8")
|
||||
print("\nDecoded NDEF Text:")
|
||||
print(text)
|
||||
else:
|
||||
print("\nUnsupported NDEF record type")
|
||||
else:
|
||||
print("\nNo NDEF message found")
|
||||
|
||||
if c == "2":
|
||||
|
||||
write_ndef_text(connection, "EZLAN00000123")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in New Issue
Block a user