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