From 61cce57401674cf9ad4d825dde454de1b49a1571 Mon Sep 17 00:00:00 2001 From: ahtlon Date: Thu, 21 May 2026 21:11:46 +0200 Subject: [PATCH] Reading done, writing almost --- app/controllers/cardManager.py | 10 +- app/main.py | 4 +- app/services/scanner.py | 184 +++++++++++++++++++++++++++++---- 3 files changed, 173 insertions(+), 25 deletions(-) diff --git a/app/controllers/cardManager.py b/app/controllers/cardManager.py index ef8ea02..2ac1cb5 100644 --- a/app/controllers/cardManager.py +++ b/app/controllers/cardManager.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Depends, HTTPException +from fastapi import APIRouter, Depends, HTTPException, status from sqlmodel import Session, select from typing import List @@ -6,12 +6,16 @@ from ..model.models import Card from ..services.database import engine, get_session, add_and_refresh from ..services.auth import auth_is_admin import uuid as gen_uuid +from app.services.scanner import WriteNewCard card_router = APIRouter(prefix="/cards", tags=["Card"]) def register_card(group_id: int): - uuid = str(gen_uuid.uuid4()) #hier code für mifare registrierung - card = Card(group_id=group_id, uuid=uuid) + key = WriteNewCard() + if key == None: + print("No card registered. Check logs!") + raise HTTPException(status.HTTP_417_EXPECTATION_FAILED, detail="No card registered. Check logs!") + card = Card(group_id=group_id, uuid=key) return card @card_router.post("/{group_id}", response_model=Card) diff --git a/app/main.py b/app/main.py index 9f6fde7..fcb3bd3 100644 --- a/app/main.py +++ b/app/main.py @@ -9,6 +9,7 @@ from .services.auth import token_router, create_first_user from app.services.scanner import BackgroundScanner oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") +scanner = BackgroundScanner(db=get_session()) @asynccontextmanager async def lifespan(app: FastAPI): @@ -16,10 +17,9 @@ async def lifespan(app: FastAPI): create_db_and_tables() create_first_user() print("Database created and tables initialized.") - scanner = BackgroundScanner(db=get_session()) scanner.start() yield - scanner.stop() + #scanner.stop() app = FastAPI(lifespan=lifespan) diff --git a/app/services/scanner.py b/app/services/scanner.py index 3e20084..feef85f 100644 --- a/app/services/scanner.py +++ b/app/services/scanner.py @@ -2,6 +2,7 @@ import logging import threading import time import os +import secrets from typing import Optional from sqlmodel import Session @@ -15,6 +16,7 @@ from desfire import DESFire, DESFireKey, PCSCDevice, diversify_key, get_list, to from desfire.enums import DESFireCommunicationMode, DESFireFileType, DESFireKeySettings, DESFireKeyType from desfire.schemas import FilePermissions, FileSettings, KeySettings + #ENV vars load_dotenv() MIFARE_APP_MASTER_KEY = os.getenv('MIFARE_APP_MASTER_KEY') @@ -34,7 +36,129 @@ logger = logging.getLogger(__name__) if MIFARE_APP_MASTER_KEY == None: logger.critical("NO MASTER KEY LOADED") - exit(1) + #exit(1) +##ADD DELETING APP +## + +def WriteNewCard(): + try: + from app.main import scanner as scannerThread + scannerThread.stop() + + cardtype = AnyCardType() + cardrequest = CardRequest(timeout=10, cardType=cardtype) + print("Please present DESfire tag...") + try: + cardservice = cardrequest.waitforcard() + except CardRequestTimeoutException: + logger.error("No tag detected within the timeout.") + return None + + cardservice.connection.connect() + + # Create Desfire object + desfire = DESFire(PCSCDevice(cardservice.connection.component)) + + # Create Key objects + AES_NULL_KEY_DATA = "00" * 16 + aes_keysettings = KeySettings( + key_type=DESFireKeyType.DF_KEY_AES, + ) + aes_null_key = DESFireKey(aes_keysettings, AES_NULL_KEY_DATA) + aes_master_key = DESFireKey(aes_keysettings, MIFARE_APP_MASTER_KEY) + + # Authenticate with default DES key + print("Authenticating with default DES key...") + key_settings = desfire.get_key_setting() + mk = DESFireKey(key_settings, "00" * 8) + desfire.authenticate(0x0, mk) + + #get uid + uid = desfire.get_real_uid() + + # Set default key + print("Setting default key...") + desfire.change_default_key(aes_master_key, 0x0) + + # Create application + print("Creating application...") + app_settings = KeySettings( + settings=[ + DESFireKeySettings.KS_ALLOW_CHANGE_MK, + DESFireKeySettings.KS_LISTING_WITHOUT_MK, + DESFireKeySettings.KS_CREATE_DELETE_WITHOUT_MK, + DESFireKeySettings.KS_CONFIGURATION_CHANGEABLE, + ], + key_type=DESFireKeyType.DF_KEY_AES, + ) + desfire.create_application(MIFARE_APP_ID, app_settings, 4) + + # Verify application creation + applications = desfire.get_application_ids() + assert len(applications) == 1 + assert applications[0] == get_list(MIFARE_APP_ID) + print(" - Application created successfully.") + + # Select application + print("Selecting application...") + desfire.select_application(MIFARE_APP_ID) + #Auth again as 0key + desfire.authenticate(0x0, mk) + #change to master + desfire.change_key(0x0, mk, aes_master_key, 0x1) + + # Authenticate with AES key, as this has been set as the default key + print("Authenticating with master key...") + desfire.authenticate(0x0, aes_master_key) + + #generate div data + diversification_data = [0x01] + uid + get_list(MIFARE_APP_ID) + get_list(MIFARE_SYS_ID) + read_div_key_bytes = diversify_key(get_list(MIFARE_ACL_READ_BASE_KEY), diversification_data, pad_to_32=False) + write_div_key_bytes = diversify_key(get_list(MIFARE_ACL_WRITE_BASE_KEY), diversification_data, pad_to_32=False) + + print("Changing file read key...") + aes_file_read_key = DESFireKey(aes_keysettings, read_div_key_bytes) + desfire.change_key(MIFARE_ACL_READ_BASE_KEY_ID, aes_master_key, aes_file_read_key, 0x1) + + print("Changing file write key...") + aes_file_write_key = DESFireKey(aes_keysettings, write_div_key_bytes) + desfire.change_key(MIFARE_ACL_WRITE_BASE_KEY_ID, aes_master_key, aes_file_write_key, 0x1) + + print("Create encrypted file containing UUID...") + file_settings = FileSettings( + file_size=32, + encryption=DESFireCommunicationMode.ENCRYPTED, + permissions=FilePermissions( + read_key=MIFARE_ACL_READ_BASE_KEY_ID, + write_key=MIFARE_ACL_WRITE_BASE_KEY_ID, + ), + file_type=DESFireFileType.MDFT_STANDARD_DATA_FILE, + ) + desfire.create_standard_file(MIFARE_ENCRYPTED_FILE_ID, file_settings) + + print("Read and verify file settings again...") + file_data = desfire.get_file_settings(MIFARE_ENCRYPTED_FILE_ID) + assert file_data.file_size == 16 + assert file_data.encryption == DESFireCommunicationMode.ENCRYPTED + assert file_data.permissions is not None + assert file_data.permissions.read_access == MIFARE_ACL_READ_BASE_KEY_ID + assert file_data.permissions.write_access == MIFARE_ACL_WRITE_BASE_KEY_ID + assert file_data.file_type == DESFireFileType.MDFT_STANDARD_DATA_FILE + print(" - File created successfully.") + + print("Writing UID to encrypted file...") + key = secrets.token_hex(16) + desfire.write_file_data(MIFARE_ENCRYPTED_FILE_ID, 0x0, file_data.encryption, get_list(key)) + + print("Reading from encrypted file...") + rdata = desfire.read_file_data(MIFARE_ENCRYPTED_FILE_ID, file_data) + assert rdata == key + print(" - Data written successfully.") + return key + + except Exception as e: + logger.error(f"Error in write function: {e}", exc_info=True) + class BackgroundScanner: def __init__(self, db): @@ -83,25 +207,45 @@ class BackgroundScanner: logger.debug("No tag detected within the timeout.") return - cardservice.connection.connect() - # Create Desfire object - desfire = DESFire(PCSCDevice(cardservice.connection.component)) - aes_keysettings = KeySettings( - key_type=DESFireKeyType.DF_KEY_AES, - ) + try: + cardservice.connection.connect() - #Auth - logger.info("Start auth") - aes_app_mk = DESFireKey(aes_keysettings, MIFARE_APP_MASTER_KEY) + # Create Desfire object + desfire = DESFire(PCSCDevice(cardservice.connection.component)) + aes_keysettings = KeySettings( + key_type=DESFireKeyType.DF_KEY_AES, + ) - apps = desfire.get_application_ids() - logger.info(f"app id's are: {apps}") - desfire.select_application(MIFARE_APP_ID) + # Get real UID + mk = DESFireKey(desfire.get_key_setting(), "00" * 8) + desfire.authenticate(0x0, mk) + #To get the uid you have to auth with an empty (default) key + uid = desfire.get_real_uid() + applications = desfire.get_application_ids() + try: + assert len(applications) == 1 + assert applications[0] == get_list(MIFARE_APP_ID) + except AssertionError: + logger.error("No application found!") + time.sleep(4) + return None + #Then use the key derivation with that uid, the appid, the sysid + diversification_data = [0x01] + uid + get_list(MIFARE_APP_ID) + get_list(MIFARE_SYS_ID) + read_div_key_bytes = diversify_key(get_list(MIFARE_ACL_READ_BASE_KEY), diversification_data, pad_to_32=True) + + #Log in with derived read key + logger.info("Start auth") + aes_app_read_key = DESFireKey(aes_keysettings, read_div_key_bytes) + desfire.select_application(MIFARE_APP_ID) - desfire.authenticate(0x0, aes_app_mk) - - logger.info("Read data") - file_data = desfire.get_file_settings(MIFARE_ENCRYPTED_FILE_ID) - logger.info(f"File settings: {file_data}") - rdata = desfire.read_file_data(MIFARE_ENCRYPTED_FILE_ID, file_data) - return rdata \ No newline at end of file + desfire.authenticate(MIFARE_ACL_READ_BASE_KEY_ID, aes_app_read_key) + + logger.info("Read data") + file_data = desfire.get_file_settings(MIFARE_ENCRYPTED_FILE_ID) + logger.info(f"File settings: {file_data}") + rdata = desfire.read_file_data(MIFARE_ENCRYPTED_FILE_ID, file_data) + return rdata + + except Exception as e: + logger.error(f"something went wrong: {e}") + time.sleep(5) \ No newline at end of file