RFIDemon is a Raspberry Pi based RFID analysis and cloning workstation for MIFARE Classic cards. The Raspberry Pi owns the RC522 reader, the REST API, the reader loop, and the AutoPwn cracking pipeline. A laptop runs the operator dashboard proxy and an HTTPS receiver for captured card snapshots.
The current supported network model is intentionally simple:
- The Raspberry Pi and laptop must be on the same infrastructure Wi-Fi network.
- The Pi serves the API on port
5000. - The laptop dashboard proxy serves the browser UI on
http://localhost:5001. - The laptop receiver accepts pushed captures on port
5002. - BLE and Pi-hosted hotspot fallback are not part of the active runtime.
RFIDemon is intended for authorised RFID security research, lab work, and defensive assessment. Only use it on cards and systems you own or have explicit permission to test.
- Raspberry Pi RC522/MFRC522 reader support over SPI.
- Windows/non-Pi development support through
MockMFRC522. - MIFARE Classic 1K, Mini, 4K, and Fudan-style card classification.
- AutoPwn pipeline with dictionary, Darkside, and nested attack phases.
- Thread-safe reader access through one shared
reader_lock. - Encrypted Pi-side capture storage using
core/Encryption.py. - Plaintext laptop inbox by design for easy review and export.
- Authenticated dashboard login with JWT access and refresh tokens.
- Save-only Pi database persistence: live cards stay in memory until the Database tab Save action writes encrypted profiles.
- TLS certificate generation for the Pi API and laptop receiver.
- Secure exfil using HTTPS, deployment-secret auth, HMAC request signatures, and Fernet-encrypted payloads.
- CSV, TXT, PDF, and HTML report generation.
- Active automated tests for transport, receiver, reports, and auth.
graph LR
subgraph Pi["Raspberry Pi"]
RC522["RC522 reader"]
API["server/app.py\nFlask API :5000"]
AutoPwn["AutoPwn\n1K + 4K crack threads"]
Session["Live session memory"]
Store["Encrypted SQLite\ncore/CardCapture.py"]
Discovery["UDP discovery responder\n:52117"]
end
subgraph Laptop["Laptop"]
Browser["Browser"]
Proxy["dashboard_proxy.py\nHTTP :5001"]
Receiver["laptop_receiver.py\nHTTPS :5002"]
Inbox["Plaintext inbox\ndata/exfil_inbox"]
end
RC522 --> API
API --> AutoPwn
API --> Session
AutoPwn --> Session
API -->|"Database Save"| Store
API <--> Discovery
Browser --> Proxy
Proxy <--> API
API -->|"capture push"| Receiver
Receiver --> Inbox
The Pi starts in config mode. The operator opens the laptop dashboard and
switches to attack mode. Attack mode starts a scan loop and AutoPwn worker
threads. When a card is detected, the scan loop routes it to the correct
MIFARE Classic AutoPwn instance by SAK/card type. Cracked profiles are exposed
to the dashboard as live session data and pushed to the configured laptop
receiver when exfil is enabled. The Pi database is written only when the
operator presses Save in the Database tab.
| Component | Host | Purpose |
|---|---|---|
server/app.py |
Pi | Flask API, reader loop, mode switching, AutoPwn orchestration, capture storage, exfil push |
MFRC522.py |
Pi/dev | RC522 SPI driver plus MockMFRC522 for non-Pi development |
Config.py |
Pi/laptop | Environment-backed runtime configuration |
autopwn/ |
Pi | MIFARE Classic card detection, key recovery, CRYPTO1 helpers, KeyStore |
core/ |
Pi/laptop | Card read/write/dump/fingerprint logic, encrypted storage, formatting, TLS cert helpers |
network/transport_manager.py |
Pi | UDP discovery responder and in-memory card snapshot buffer |
network/discovery.py |
Laptop | UDP discovery client and TLS fingerprint helper |
windows/dashboard_proxy.py |
Laptop | Browser-facing dashboard proxy and Pi API forwarder |
windows/laptop_receiver.py |
Laptop | HTTPS inbox for card pushes from the Pi |
reports.py |
Pi/laptop | CSV, TXT, PDF, and HTML exports |
security/ |
Pi | JWT token creation/verification and Flask auth decorators |
web/ |
Laptop browser | Single-page operator dashboard assets |
setup/ |
Pi/laptop | Setup, launch, systemd, and validation scripts |
tests/ |
Dev | Supported automated tests |
RFIDemon/
|-- server/ Pi Flask API, mode switching, scan loop, AutoPwn orchestration
|-- autopwn/ Dictionary, Darkside, nested auth, CRYPTO1 helpers
|-- core/ RFID operations, encrypted storage, cloning, formatting, TLS helpers
|-- network/ UDP discovery and transport support
|-- security/ JWT auth and Flask route protection
|-- shared/ HMAC, deployment secret, Fernet payload security
|-- windows/ Laptop dashboard proxy, receiver, launcher
|-- web/ Browser dashboard assets
|-- setup/ Pi/laptop setup scripts and service files
|-- tests/ Pytest regression and integration tests
|-- reports.py CSV, TXT, PDF, and HTML report generation
|-- Config.py Runtime configuration
|-- MFRC522.py RC522 hardware driver and mock reader
|-- README.md Main documentation
data/ is repo-local runtime state. It is intentionally ignored except for
data/.gitkeep; generated certs, keys, databases, logs, laptop inbox files,
and caches should not be committed.
Use Raspberry Pi OS Lite with SSH, SPI-capable hardware, and Wi-Fi configured in Raspberry Pi Imager. RFIDemon no longer manages access-point mode itself.
Clone or copy the project onto the Pi, then run:
cd /opt/RFIDemon
bash setup/ServerSetup.shThe setup script:
- Checks Python 3.9+ and Raspberry Pi suitability.
- Installs Python, venv, development headers,
iproute2, andiw. - Creates the
rfidemonservice user. - Creates
.venvand installsrequirements.txt. - Creates
data/rfidemon.envfromsetup/rfidemon.env.example. - Enables SPI.
- Installs and starts
rfidemon.service.
Edit the Pi environment file:
nano data/rfidemon.envSet at least:
RFIDEMON_API_PASSWORD=<choose-a-strong-password>
RFIDEMON_DEPLOYMENT_SECRET=<same-secret-as-laptop>TLS is enabled by default. On first start, the Pi generates:
data/rfidemon.crtdata/rfidemon.keydata/encryption.keydata/jwt_secret.key
Restart and watch logs:
sudo systemctl restart rfidemon
sudo journalctl -u rfidemon -fIf SPI was first enabled during setup, reboot once:
sudo rebootValidate the Pi:
.venv/bin/python setup/validate_deployment.pyRun:
setup\ClientSetup.batThe client setup creates .venv, installs requirements.txt, creates
data/, and copies setup/rfidemon.env.example to data/rfidemon.env if the
env file is missing. It preserves an existing env file.
Start both laptop services:
.\.venv\Scripts\activate
python .\windows\launch_laptop.pyOpen:
http://localhost:5001
The launcher starts:
windows/laptop_receiver.pyon HTTPS port5002windows/dashboard_proxy.pyon HTTP port5001
Before starting, the launcher validates critical Python imports, checks that
data/rfidemon.env exists, warns when RFIDEMON_DEPLOYMENT_SECRET is unset,
checks the receiver/dashboard ports are free, and prints log paths on failure.
Allow Python through Windows Firewall on private networks. The Pi must be able
to reach the laptop receiver on port 5002.
Use the same RFIDEMON_DEPLOYMENT_SECRET in data/rfidemon.env on the Pi and
Windows laptop. That static deployment secret replaces the old interactive
pairing ceremony.
On the Pi, first start creates:
data/rfidemon.crtanddata/rfidemon.keyfor the Pi HTTPS APIdata/encryption.keyfor encrypted Pi storagedata/jwt_secret.keyfor persistent JWT signing
On the laptop, first receiver start creates:
data/laptop.crtanddata/laptop.keyfor the laptop HTTPS receiver
Card pushes to the laptop receiver use:
- HTTPS to the learned receiver IP/port.
Authorization: Bearer <RFIDEMON_DEPLOYMENT_SECRET>.X-RFIDemon-Signature: sha256=<hmac>.- Fernet payload encryption with a key derived from
RFIDEMON_DEPLOYMENT_SECRET.
TLS trust uses SHA-256 certificate fingerprints, so normal DHCP IP changes on
the shared Wi-Fi can reconnect without hostname errors. The laptop keeps the
last Pi target and learned fingerprint in data/pi_config.json; after a new
launcher session, log in again and the proxy can rediscover or reuse that
saved target. Saved database profiles remain on the Pi. Unsaved live session
cards do not survive a Pi process restart.
To force a clean run-through on the laptop:
Remove-Item .\data\laptop.crt, .\data\laptop.key, .\data\pi_config.json `
-ErrorAction SilentlyContinueThen restart python .\windows\launch_laptop.py and log in again.
Pi-side card data is encrypted before disk writes. The capture database is SQLite, but the card payload column is a Fernet-encrypted JSON blob. The Pi database is intended to survive process restarts without leaving raw card dumps on disk.
Live read/dump/exfil snapshots stay in server memory during the current Pi
process. They appear in the Cards/session views immediately, but they are not
inserted into data/captured_cards.db until the operator presses Save in the
Database tab. /api/db/cards returns persisted database rows only and marks a
saved UID with in_session when that UID is also live.
After a Pi restart, the live session is empty. Previously saved encrypted profiles still appear in the Database tab because they are loaded from the Pi database.
Laptop receiver output is intentionally plaintext and written under:
data/exfil_inbox/
That design makes the laptop inbox easy to inspect, submit, export, or archive after a test. Do not add laptop-side encryption unless the project requirements change.
data/recovered_keys.json stores recovered sector keys only. It should never
store raw card dumps.
Runtime secrets, captures, logs, virtual environments, editor
folders, and caches are ignored by .gitignore and .sftpignore.
| Area | Current design |
|---|---|
| Dashboard login | Password login through /api/auth/login; JWT access and refresh tokens |
| Browser to proxy | Localhost HTTP at http://localhost:5001 |
| Proxy to Pi | HTTPS when Pi TLS is enabled; SHA-256 fingerprint pinning |
| Pi to laptop receiver | HTTPS receiver, deployment-secret auth, HMAC signatures, encrypted payload |
| Pi capture storage | Encrypted at rest through core/Encryption.py |
| Laptop inbox | Plaintext by design |
| Rate limiting | Flask-Limiter global default plus stricter write/clone/security/test limits |
| Hardware access | All RC522 access must go through reader_lock |
Authentication is enforced by @require_auth only when
RFIDEMON_API_PASSWORD is set. Do not deploy without setting that password.
- Start the Pi service.
- Start the laptop receiver and dashboard proxy with
windows/launch_laptop.py. - Open
http://localhost:5001. - Log in with the Pi
RFIDEMON_API_PASSWORD. - Let the dashboard discover the Pi, or set the Pi IP manually.
- Switch to attack mode.
- Present a MIFARE Classic card to the RC522 reader.
- Review live card status, recovered keys, cloneability, exports, and reports.
- Press Save in the Database tab for any session cards that must persist into a later Pi session.
The supported non-Pi development path uses MockMFRC522, so most code can be
tested on Windows, macOS, or Linux without RFID hardware.
Install dependencies:
python -m venv .venv
.venv/bin/python -m pip install -r requirements.txtOn Windows PowerShell:
python -m venv .venv
.\.venv\Scripts\python -m pip install -r requirements.txtRun the active automated baseline:
python -m pytest tests -qRun the full suite:
python -m pytest -qUse this order when explaining the codebase:
Config.pyshows the active Wi-Fi, TLS, capture, auth, and rate-limit settings.server/app.pyshows startup, reader initialisation, mode switching, the scan loop, and route boundaries.autopwn/MifareAutoPwnBase.pyshows the dictionary -> Darkside -> nested -> final profile pipeline.core/CardCapture.pyandcore/Encryption.pyshow save-only encrypted Pi storage.shared/security.py,security/token_manager.py, andsecurity/auth_middleware.pyshow HMAC/Fernet deployment security and JWT route protection.windows/dashboard_proxy.pyandwindows/laptop_receiver.pyshow laptop discovery, proxying, secure ingest, and the plaintext inbox.web/app.jsshows dashboard auth token handling, polling, mode switching, database save, diagnostics, and report export flow.tests/shows regression coverage for auth, AutoPwn, receiver security, robustness, Wi-Fi transport, and mock reader behaviour.
- MIFARE Classic support is the focus; DESFire, Plus SL3, and Java Card devices are detected but not attacked.
- Pi and laptop must share the same infrastructure Wi-Fi network.
- Laptop inbox files are plaintext by design; protect the laptop workspace after an engagement.
- Self-signed TLS uses certificate fingerprint pinning rather than public CA trust.
- AutoPwn success depends on card family, keys, PRNG behaviour, reader positioning, and legal authorisation.
- Future work could add stronger operator roles, richer report templates, hardware status telemetry, and broader non-Classic card analysis.
README.md is the primary documentation entry point. The files under docs/
are intentionally slim pointers back here to avoid duplicate deployment and
code-map guidance drifting out of date.