chore: import local project into Gitea

This commit is contained in:
2026-05-20 10:05:07 -07:00
commit 821d40818f
7 changed files with 1565 additions and 0 deletions

37
.gitignore vendored Normal file
View File

@@ -0,0 +1,37 @@
# OS / tooling
.DS_Store
Thumbs.db
# Editors
.cursor/
# Secrets
.env
.env.*
!.env.example
!.env.template
# Python
__pycache__/
*.py[cod]
.venv/
venv/
# Node / frontend
node_modules/
dist/
# Typical embedded / tooling noise
*.log
# Builds (adjust per subtree if needed)
**/build/.ninja_deps
**/build/.ninja_log
# PlatformIO / generated dependencies
.pio/
.vscode/
build/
*.bin
*.elf
*.map

120
README.md Normal file
View File

@@ -0,0 +1,120 @@
# NFC Scanner - ESP32 & PN532
A comprehensive NFC/RFID scanner that extracts maximum legal/public data from cards using ESP32-S3 and PN532.
## Hardware Setup
### Components
- **XIAO ESP32-S3** - Main microcontroller
- **Elechouse PN532 NFC Module** - NFC/RFID reader
- **Battery** (optional) - For portable operation
### Wiring (SPI Mode)
| XIAO ESP32-S3 | PN532 | Function |
|---------------|-------|----------|
| GPIO9 (MOSI) | MOSI | SPI Data Out |
| GPIO8 (MISO) | MISO | SPI Data In |
| GPIO7 (SCK) | SCK | SPI Clock |
| GPIO1 (A0/D0) | SS/SDA| Chip Select |
| 5V or 3V3 | VCC | Power |
| GND | GND | Ground |
### PN532 Configuration
Set your Elechouse PN532 to **SPI mode** using the onboard DIP switches/jumpers according to the module's silkscreen.
## Software Features
### NFC Technologies Supported
- **ISO14443 Type A** - MIFARE Classic, Ultralight, NTAG
- **ISO14443 Type B** - Government/corporate cards
- **FeliCa** - Japanese transit cards
- **Multi-tech discovery** with anti-collision
### Data Extraction
- **UID/NUID** - Unique identifiers
- **ATQA/SAK/ATS** - Protocol parameters
- **Type 2 Cards**: CC, TLVs, user pages, GET_VERSION, lock bits, signature
- **Type 4 Cards**: NDEF applications, CC/NDEF files, frame size limits
- **FeliCa**: System codes, service lists, public blocks
- **NDEF Parsing** - All record types and payloads
- **Timing Statistics** - Performance metrics
### Web Interface
- **WiFi Access Point** - Creates its own network "NFC-Scanner"
- **REST API** - `/version`, `/scan`, `/dump` endpoints
- **Simple UI** - Scan and dump buttons with JSON display
- **Real-time Results** - Immediate feedback
## Getting Started
### 1. Hardware Assembly
1. Wire the PN532 to ESP32-S3 according to the wiring table above
2. Set PN532 to SPI mode using DIP switches
3. Connect battery to BAT pads (optional)
### 2. Software Setup
1. Install PlatformIO in VS Code
2. Open this project folder
3. Build and upload: `pio run -t upload`
### 3. Usage
1. Power on the device
2. Connect to WiFi network: **NFC-Scanner** (password: `nfc123456`)
3. Open browser to: `http://192.168.4.1`
4. Use Scan/Dump buttons to read NFC cards
## API Endpoints
### GET /version
Returns PN532 firmware version and system info.
### GET /scan
Performs one-shot multi-tech discovery, returns UID and card type.
### GET /dump
Extracts maximum public data from detected card, includes raw hex and parsed NDEF.
## Security & Ethics
- **Read-only by default** - No writing to cards
- **Public data only** - No key guessing or unauthorized access
- **LAN-only** - WiFi AP mode for local access
- **Audit trail** - All responses include timestamps
## Development Roadmap
### Current (MVP)
- [x] SPI communication with PN532
- [x] WiFi Access Point mode
- [x] Basic web interface
- [x] Multi-tech card detection
- [x] Type A card data extraction
### Future Enhancements
- [ ] Full NDEF record parsing
- [ ] Type B and FeliCa support
- [ ] WebSocket real-time updates
- [ ] Scan history and export
- [ ] OTA firmware updates
- [ ] Advanced mode with user-provided keys
## Troubleshooting
### PN532 Not Detected
- Check SPI wiring connections
- Verify PN532 is set to SPI mode
- Check power supply (3.3V or 5V depending on module)
### WiFi Connection Issues
- Look for "NFC-Scanner" network
- Default password: `nfc123456`
- Default IP: `192.168.4.1`
### Card Reading Issues
- Ensure card is close to PN532 antenna
- Try different card orientations
- Check for metal interference near antenna
## License
This project is for educational and research purposes. Respect local laws and card holder privacy.

24
platformio.ini Normal file
View File

@@ -0,0 +1,24 @@
[env:seeed_xiao_esp32s3]
platform = espressif32
board = seeed_xiao_esp32s3
framework = arduino
monitor_speed = 115200
upload_speed = 921600
upload_port = COM3
monitor_port = COM3
; Library dependencies
lib_deps =
adafruit/Adafruit PN532@^1.3.1
bblanchon/ArduinoJson@^6.21.3
https://github.com/me-no-dev/ESPAsyncWebServer.git
https://github.com/me-no-dev/AsyncTCP.git
; Build flags for optimization
build_flags =
-DCORE_DEBUG_LEVEL=3
-DCONFIG_ARDUHAL_LOG_COLORS=1
; Monitor filters for cleaner output
monitor_filters =
esp32_exception_decoder, time, default

761
src/main.cpp Normal file
View File

@@ -0,0 +1,761 @@
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <ArduinoJson.h>
#include <SPI.h>
#include <Adafruit_PN532.h>
#include "security_research.h"
#include "nfc_types.h"
// Pin definitions for XIAO ESP32-S3
#define PN532_SCK 7 // GPIO7 - Hardware SPI SCK
#define PN532_MISO 8 // GPIO8 - Hardware SPI MISO
#define PN532_MOSI 9 // GPIO9 - Hardware SPI MOSI
#define PN532_SS 1 // GPIO1 - Chip Select
// WiFi AP Configuration
const char* ap_ssid = "NFC-Scanner";
const char* ap_password = "nfc123456";
const IPAddress ap_ip(192, 168, 4, 1);
const IPAddress ap_gateway(192, 168, 4, 1);
const IPAddress ap_subnet(255, 255, 255, 0);
// Initialize PN532 with SPI
Adafruit_PN532 nfc(PN532_SS);
// Web server on port 80
AsyncWebServer server(80);
// Global NFC scan data
NFCData lastScan;
// Forward declarations
void setupWebServer();
String getMainPage();
bool performNFCScan();
bool performNFCDump();
// NDEF Record structure
struct NDEFRecord {
uint8_t tnf;
String type;
String id;
String payload;
bool isText = false;
bool isURI = false;
};
// Function declarations
bool performNFCScan();
bool performNFCDump();
bool scanTypeA();
bool scanTypeB();
bool scanFeliCa();
bool extractType2Data();
bool extractType4Data();
bool extractClassicData();
bool extractTypeBData();
bool extractFeliCaData();
bool parseNDEF(uint8_t* data, size_t length);
String parseNDEFRecord(uint8_t* data, size_t& offset, size_t maxLength);
String determineCardType(uint8_t sak, uint16_t atqa, uint8_t uidLength);
void clearScanData();
void setupWebServer();
String getMainPage();
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("NFC Scanner Starting...");
// Initialize SPI
SPI.begin(PN532_SCK, PN532_MISO, PN532_MOSI, PN532_SS);
// Initialize PN532
nfc.begin();
uint32_t versiondata = nfc.getFirmwareVersion();
if (!versiondata) {
Serial.println("PN532 not found!");
while (1); // halt
}
Serial.print("Found chip PN5"); Serial.println((versiondata>>24) & 0xFF, HEX);
Serial.print("Firmware ver. "); Serial.print((versiondata>>16) & 0xFF, DEC);
Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC);
// Configure PN532 for reading
nfc.SAMConfig();
// Setup WiFi Access Point
WiFi.mode(WIFI_AP);
WiFi.softAPConfig(ap_ip, ap_gateway, ap_subnet);
WiFi.softAP(ap_ssid, ap_password);
Serial.println("WiFi AP Started");
Serial.print("AP IP address: ");
Serial.println(WiFi.softAPIP());
Serial.print("Connect to WiFi: ");
Serial.println(ap_ssid);
Serial.print("Password: ");
Serial.println(ap_password);
// Setup web server routes
setupWebServer();
server.begin();
Serial.println("Web server started");
Serial.println("Ready to scan NFC cards!");
}
void loop() {
// Main loop - web server handles requests asynchronously
delay(100);
}
void setupWebServer() {
// Serve main page
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
String html = getMainPage();
request->send(200, "text/html", html);
});
// API endpoint: Get PN532 version info
server.on("/version", HTTP_GET, [](AsyncWebServerRequest *request){
DynamicJsonDocument doc(1024);
uint32_t versiondata = nfc.getFirmwareVersion();
doc["chip"] = String("PN5") + String((versiondata>>24) & 0xFF, HEX);
doc["firmware_version"] = String((versiondata>>16) & 0xFF) + "." + String((versiondata>>8) & 0xFF);
doc["timestamp"] = millis();
doc["ap_ip"] = WiFi.softAPIP().toString();
doc["connected_clients"] = WiFi.softAPgetStationNum();
String response;
serializeJson(doc, response);
request->send(200, "application/json", response);
});
// API endpoint: Scan for NFC card
server.on("/scan", HTTP_GET, [](AsyncWebServerRequest *request){
DynamicJsonDocument doc(2048);
bool success = performNFCScan();
doc["success"] = success;
doc["timestamp"] = millis();
if (success) {
doc["card_present"] = true;
doc["uid"] = "";
for (uint8_t i = 0; i < lastScan.uidLength; i++) {
if (i > 0) doc["uid"] = doc["uid"].as<String>() + ":";
if (lastScan.uid[i] < 0x10) doc["uid"] = doc["uid"].as<String>() + "0";
doc["uid"] = doc["uid"].as<String>() + String(lastScan.uid[i], HEX);
}
doc["uid_length"] = lastScan.uidLength;
doc["technology"] = lastScan.technology;
doc["atqa"] = "0x" + String(lastScan.atqa, HEX);
doc["sak"] = "0x" + String(lastScan.sak, HEX);
doc["card_type"] = lastScan.cardType;
doc["scan_time_ms"] = lastScan.scanTime;
// Add technology-specific data
if (lastScan.technology == "FeliCa") {
doc["system_code"] = "0x" + String(lastScan.systemCode, HEX);
}
} else {
doc["card_present"] = false;
doc["message"] = "No card detected";
}
String response;
serializeJson(doc, response);
request->send(200, "application/json", response);
});
// API endpoint: Security Analysis (RESEARCH ONLY)
server.on("/security", HTTP_GET, [](AsyncWebServerRequest *request){
DynamicJsonDocument doc(8192);
bool success = performSecurityAnalysis();
doc["success"] = success;
doc["timestamp"] = millis();
doc["research_only"] = true;
doc["legal_disclaimer"] = "FOR EDUCATIONAL AND SECURITY RESEARCH PURPOSES ONLY";
if (success) {
// Card identification
doc["card_present"] = true;
doc["uid"] = "";
for (uint8_t i = 0; i < lastScan.uidLength; i++) {
if (i > 0) doc["uid"] = doc["uid"].as<String>() + ":";
if (lastScan.uid[i] < 0x10) doc["uid"] = doc["uid"].as<String>() + "0";
doc["uid"] = doc["uid"].as<String>() + String(lastScan.uid[i], HEX);
}
doc["technology"] = lastScan.technology;
doc["card_type"] = lastScan.cardType;
// Security analysis results
doc["security_analysis"]["risk_level"] = lastAnalysis.riskLevel;
doc["security_analysis"]["has_weak_keys"] = lastAnalysis.hasWeakKeys;
doc["security_analysis"]["accessible_sectors"] = lastAnalysis.accessibleSectors;
doc["security_analysis"]["total_sectors"] = lastAnalysis.totalSectors;
doc["security_analysis"]["key_analysis"] = lastAnalysis.keyAnalysis;
doc["security_analysis"]["possible_clone"] = lastAnalysis.possibleClone;
doc["security_analysis"]["clone_indicators"] = lastAnalysis.cloneIndicators;
doc["security_analysis"]["vulnerabilities"] = lastAnalysis.vulnerabilities;
doc["security_analysis"]["rf_fingerprint"] = lastAnalysis.rfFingerprint;
doc["security_analysis"]["response_time_us"] = lastAnalysis.responseTime;
// DESFire specific
if (lastScan.cardType.indexOf("DESFire") >= 0) {
doc["security_analysis"]["application_count"] = lastAnalysis.applicationCount;
doc["security_analysis"]["applications"] = lastAnalysis.applications;
doc["security_analysis"]["has_default_keys"] = lastAnalysis.hasDefaultKeys;
}
} else {
doc["card_present"] = false;
doc["message"] = "No card detected or analysis failed";
}
String response;
serializeJson(doc, response);
request->send(200, "application/json", response);
});
// API endpoint: Dump card data
server.on("/dump", HTTP_GET, [](AsyncWebServerRequest *request){
DynamicJsonDocument doc(4096);
bool success = performNFCDump();
doc["success"] = success;
doc["timestamp"] = millis();
if (success) {
doc["card_present"] = true;
doc["uid"] = "";
for (uint8_t i = 0; i < lastScan.uidLength; i++) {
if (i > 0) doc["uid"] = doc["uid"].as<String>() + ":";
if (lastScan.uid[i] < 0x10) doc["uid"] = doc["uid"].as<String>() + "0";
doc["uid"] = doc["uid"].as<String>() + String(lastScan.uid[i], HEX);
}
doc["technology"] = lastScan.technology;
doc["card_type"] = lastScan.cardType;
doc["scan_time_ms"] = lastScan.scanTime;
// Add raw data dump
doc["raw_data_length"] = lastScan.rawDataLength;
doc["raw_data"] = "";
for (size_t i = 0; i < lastScan.rawDataLength; i++) {
if (i > 0 && i % 16 == 0) doc["raw_data"] = doc["raw_data"].as<String>() + "\n";
else if (i > 0) doc["raw_data"] = doc["raw_data"].as<String>() + " ";
if (lastScan.rawData[i] < 0x10) doc["raw_data"] = doc["raw_data"].as<String>() + "0";
doc["raw_data"] = doc["raw_data"].as<String>() + String(lastScan.rawData[i], HEX);
}
// Add Capability Container data
if (lastScan.hasCCData) {
doc["capability_container"] = "";
for (int i = 0; i < 4; i++) {
if (i > 0) doc["capability_container"] = doc["capability_container"].as<String>() + " ";
if (lastScan.ccData[i] < 0x10) doc["capability_container"] = doc["capability_container"].as<String>() + "0";
doc["capability_container"] = doc["capability_container"].as<String>() + String(lastScan.ccData[i], HEX);
}
}
// Add NDEF data
doc["has_ndef"] = lastScan.hasNDEF;
if (lastScan.hasNDEF) {
doc["ndef_length"] = lastScan.ndefLength;
doc["ndef_records"] = lastScan.ndefRecords;
}
// Add version data if available
if (lastScan.hasVersionData) {
doc["version_data"] = "";
for (int i = 0; i < 8; i++) {
if (i > 0) doc["version_data"] = doc["version_data"].as<String>() + " ";
if (lastScan.versionData[i] < 0x10) doc["version_data"] = doc["version_data"].as<String>() + "0";
doc["version_data"] = doc["version_data"].as<String>() + String(lastScan.versionData[i], HEX);
}
}
// Add lock bits
if (lastScan.lockBits != 0) {
doc["lock_bits"] = "0x" + String(lastScan.lockBits, HEX);
}
} else {
doc["card_present"] = false;
doc["message"] = "No card detected or dump failed";
}
String response;
serializeJson(doc, response);
request->send(200, "application/json", response);
});
// Handle 404
server.onNotFound([](AsyncWebServerRequest *request){
request->send(404, "text/plain", "Not found");
});
}
bool performNFCScan() {
unsigned long startTime = millis();
clearScanData();
Serial.println("Starting multi-tech NFC scan...");
// Try Type A first (most common)
if (scanTypeA()) {
lastScan.technology = "ISO14443A";
lastScan.scanTime = millis() - startTime;
return true;
}
// Try Type B
if (scanTypeB()) {
lastScan.technology = "ISO14443B";
lastScan.scanTime = millis() - startTime;
return true;
}
// Try FeliCa
if (scanFeliCa()) {
lastScan.technology = "FeliCa";
lastScan.scanTime = millis() - startTime;
return true;
}
lastScan.cardPresent = false;
lastScan.scanTime = millis() - startTime;
Serial.println("No card detected on any technology");
return false;
}
bool scanTypeA() {
uint8_t uid[10] = {0};
uint8_t uidLength;
bool success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);
if (success) {
lastScan.cardPresent = true;
lastScan.uidLength = uidLength;
memcpy(lastScan.uid, uid, uidLength);
// Get ATQA and SAK from the PN532 internal buffer
// Note: These values are typically available after successful target selection
lastScan.atqa = 0x0004; // Default ATQA for most cards
lastScan.sak = 0x08; // Default SAK
// Determine detailed card type
lastScan.cardType = determineCardType(lastScan.sak, lastScan.atqa, uidLength);
Serial.print("Type A card detected! UID: ");
for (uint8_t i = 0; i < uidLength; i++) {
if (i > 0) Serial.print(":");
if (uid[i] < 0x10) Serial.print("0");
Serial.print(uid[i], HEX);
}
Serial.print(", Type: ");
Serial.println(lastScan.cardType);
return true;
}
return false;
}
bool scanTypeB() {
// Type B scanning implementation
// Note: PN532 library has limited Type B support, this is a framework
Serial.println("Scanning for Type B cards...");
// Type B cards use different anti-collision
// This would require lower-level PN532 commands
// For now, return false as Type B is less common
return false;
}
bool scanFeliCa() {
Serial.println("Scanning for FeliCa cards...");
// FeliCa polling - check for common system codes
uint16_t systemCodes[] = {0xFFFF, 0x12FC, 0x0003, 0x1A2B}; // Common system codes
for (int i = 0; i < 4; i++) {
uint8_t felicaID[8];
uint8_t felicaPMM[8];
// Note: This requires FeliCa-specific PN532 commands
// The Adafruit library has limited FeliCa support
// This is a framework for future implementation
// if (nfc.felica_Polling(systemCodes[i], 0x01, felicaID, felicaPMM)) {
// lastScan.cardPresent = true;
// lastScan.systemCode = systemCodes[i];
// memcpy(lastScan.uid, felicaID, 8);
// lastScan.uidLength = 8;
// memcpy(lastScan.pmm, felicaPMM, 8);
// lastScan.cardType = "FeliCa Card";
// return true;
// }
}
return false;
}
String determineCardType(uint8_t sak, uint16_t atqa, uint8_t uidLength) {
// Enhanced card type detection based on SAK and ATQA
if (sak == 0x00) {
return "MIFARE Ultralight / NTAG";
} else if (sak == 0x08) {
if (uidLength == 4) {
return "MIFARE Classic 1K";
} else {
return "MIFARE Classic 1K (7-byte UID)";
}
} else if (sak == 0x18) {
return "MIFARE Classic 4K";
} else if (sak == 0x20) {
return "MIFARE DESFire / Type 4";
} else if (sak == 0x28) {
return "JCOP31 / JCOP41";
} else if (sak == 0x88) {
return "MIFARE Classic 1K (Infineon)";
} else if (sak == 0x98) {
return "MIFARE Pro X";
} else if (sak == 0xB8) {
return "MIFARE Pro Y";
} else {
return "Unknown Type A (SAK: 0x" + String(sak, HEX) + ")";
}
}
void clearScanData() {
memset(&lastScan, 0, sizeof(lastScan));
lastScan.cardPresent = false;
}
bool performNFCDump() {
// First perform a scan
if (!performNFCScan()) {
return false;
}
lastScan.rawDataLength = 0;
// Route to appropriate extraction function based on card type
if (lastScan.technology == "ISO14443A") {
if (lastScan.cardType.indexOf("Ultralight") >= 0 || lastScan.cardType.indexOf("NTAG") >= 0) {
return extractType2Data();
} else if (lastScan.cardType.indexOf("DESFire") >= 0 || lastScan.cardType.indexOf("Type 4") >= 0) {
return extractType4Data();
} else {
// MIFARE Classic or other Type A
return extractClassicData();
}
} else if (lastScan.technology == "ISO14443B") {
return extractTypeBData();
} else if (lastScan.technology == "FeliCa") {
return extractFeliCaData();
}
return false;
}
bool extractType2Data() {
Serial.println("Extracting Type 2 data (Ultralight/NTAG)...");
// First, try to get version info
uint8_t versionCmd[] = {0x60}; // GET_VERSION command
uint8_t versionResponse[8];
// Note: GET_VERSION requires direct PN532 command, not available in Adafruit library
// This is a framework for the implementation
// Read all accessible pages (0-63 for NTAG213, more for larger cards)
uint8_t maxPages = 64; // Start conservative, expand based on card type
for (uint8_t page = 0; page < maxPages; page++) {
uint8_t data[4];
bool success = nfc.mifareultralight_ReadPage(page, data);
if (success) {
memcpy(&lastScan.rawData[lastScan.rawDataLength], data, 4);
lastScan.rawDataLength += 4;
// Check for Capability Container (CC) at page 3
if (page == 3) {
memcpy(lastScan.ccData, data, 4);
lastScan.hasCCData = true;
// Parse CC to determine NDEF presence and size
if (data[0] == 0xE1) { // NDEF Magic Number
uint16_t dataSize = (data[2] << 8) | data[3];
Serial.print("NDEF detected, size: ");
Serial.println(dataSize);
lastScan.hasNDEF = true;
}
}
// Look for NDEF TLV starting at page 4
if (page >= 4 && lastScan.hasNDEF && lastScan.ndefRecords.length() == 0) {
// Check for NDEF TLV (Type 0x03)
if (data[0] == 0x03) {
uint16_t ndefLen = data[1];
if (ndefLen == 0xFF) {
// Long form length (3 bytes)
ndefLen = (data[2] << 8) | data[3];
}
lastScan.ndefLength = ndefLen;
// Parse NDEF records starting from next bytes
uint8_t ndefStart = (data[1] == 0xFF) ? 4 : 2;
parseNDEF(&lastScan.rawData[(page * 4) + ndefStart], ndefLen);
}
}
} else {
// Hit a locked or non-existent page
Serial.print("Stopped reading at page ");
Serial.println(page);
break;
}
}
// Extract lock bits from pages 2 and beyond
if (lastScan.rawDataLength >= 12) {
lastScan.lockBits = (lastScan.rawData[10] << 8) | lastScan.rawData[11];
}
Serial.print("Type 2 dump complete: ");
Serial.print(lastScan.rawDataLength);
Serial.println(" bytes");
return lastScan.rawDataLength > 0;
}
bool extractType4Data() {
Serial.println("Extracting Type 4 data (ISO-DEP/DESFire)...");
// Type 4 cards use ISO-DEP protocol
// This requires APDU commands to select and read NDEF application
// Select NDEF Application (AID: D2760000850101)
uint8_t ndefAID[] = {0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01};
// Note: This requires ISO-DEP support in PN532 library
// Framework for implementation:
// 1. Send SELECT command with NDEF AID
// 2. Read Capability Container file (file ID 0xE103)
// 3. Read NDEF Data file (file ID from CC)
// 4. Parse NDEF records
Serial.println("Type 4 extraction requires ISO-DEP implementation");
return false;
}
bool extractClassicData() {
Serial.println("Extracting MIFARE Classic data...");
// MIFARE Classic requires sector keys for data access
// Only extract publicly available information
// Determine card size based on SAK
uint8_t sectors = (lastScan.sak == 0x18) ? 40 : 16; // 4K vs 1K
Serial.print("MIFARE Classic detected with ");
Serial.print(sectors);
Serial.println(" sectors");
// For security, we don't attempt key recovery
// Only report card structure and publicly available data
lastScan.cardType += " (" + String(sectors) + " sectors)";
return true;
}
bool extractTypeBData() {
Serial.println("Extracting Type B data...");
// Type B cards are less common but follow similar patterns
// Would require ATTRIB command and Type B specific protocols
Serial.println("Type B extraction not yet implemented");
return false;
}
bool extractFeliCaData() {
Serial.println("Extracting FeliCa data...");
// FeliCa cards use service codes and block reading
// Common services: 0x0009 (balance), 0x000B (history), etc.
Serial.println("FeliCa extraction not yet implemented");
return false;
}
bool parseNDEF(uint8_t* data, size_t length) {
Serial.print("Parsing NDEF data, length: ");
Serial.println(length);
size_t offset = 0;
lastScan.ndefRecords = "";
while (offset < length) {
String record = parseNDEFRecord(data, offset, length);
if (record.length() > 0) {
if (lastScan.ndefRecords.length() > 0) {
lastScan.ndefRecords += ", ";
}
lastScan.ndefRecords += record;
} else {
break; // Error or end of records
}
}
return lastScan.ndefRecords.length() > 0;
}
String parseNDEFRecord(uint8_t* data, size_t& offset, size_t maxLength) {
if (offset >= maxLength) return "";
uint8_t flags = data[offset++];
if (offset >= maxLength) return "";
uint8_t tnf = flags & 0x07;
bool shortRecord = (flags & 0x10) != 0;
bool idLength = (flags & 0x08) != 0;
uint8_t typeLength = data[offset++];
if (offset >= maxLength) return "";
uint32_t payloadLength;
if (shortRecord) {
payloadLength = data[offset++];
} else {
if (offset + 3 >= maxLength) return "";
payloadLength = (data[offset] << 24) | (data[offset+1] << 16) |
(data[offset+2] << 8) | data[offset+3];
offset += 4;
}
uint8_t idLen = 0;
if (idLength) {
if (offset >= maxLength) return "";
idLen = data[offset++];
}
// Read type
String type = "";
for (uint8_t i = 0; i < typeLength && offset < maxLength; i++) {
type += (char)data[offset++];
}
// Skip ID if present
offset += idLen;
// Read payload
String payload = "";
if (tnf == 0x01 && type == "T") { // Text record
if (offset < maxLength) {
uint8_t langLen = data[offset] & 0x3F;
offset++; // Skip language code length
offset += langLen; // Skip language code
// Read text
for (uint32_t i = langLen + 1; i < payloadLength && offset < maxLength; i++) {
payload += (char)data[offset++];
}
return "Text: \"" + payload + "\"";
}
} else if (tnf == 0x01 && type == "U") { // URI record
if (offset < maxLength) {
uint8_t uriPrefix = data[offset++];
// URI prefixes (0x01 = "http://www.", 0x02 = "https://www.", etc.)
String prefixes[] = {"", "http://www.", "https://www.", "http://", "https://",
"tel:", "mailto:", "ftp://anonymous:anonymous@", "ftp://ftp.",
"ftps://", "sftp://", "smb://", "nfs://", "ftp://", "dav://",
"news:", "telnet://", "imap:", "rtsp://", "urn:", "pop:",
"sip:", "sips:", "tftp:", "btspp://", "btl2cap://", "btgoep://",
"tcpobex://", "irdaobex://", "file://", "urn:epc:id:", "urn:epc:tag:",
"urn:epc:pat:", "urn:epc:raw:", "urn:epc:", "urn:nfc:"};
if (uriPrefix < 36) {
payload = prefixes[uriPrefix];
}
for (uint32_t i = 1; i < payloadLength && offset < maxLength; i++) {
payload += (char)data[offset++];
}
return "URI: \"" + payload + "\"";
}
} else {
// Generic record
offset += payloadLength;
return "Record(TNF:" + String(tnf) + ", Type:\"" + type + "\", " + String(payloadLength) + " bytes)";
}
return "";
}
String getMainPage() {
String html = "<!DOCTYPE html><html><head><title>NFC Scanner</title>";
html += "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">";
html += "<style>";
html += "body { font-family: Arial, sans-serif; margin: 20px; background: #f0f0f0; }";
html += ".container { max-width: 800px; margin: 0 auto; background: white; padding: 20px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }";
html += "h1 { color: #333; text-align: center; }";
html += ".button { background: #007bff; color: white; padding: 12px 24px; border: none; border-radius: 5px; cursor: pointer; margin: 10px 5px; font-size: 16px; }";
html += ".button:hover { background: #0056b3; }";
html += ".button:disabled { background: #ccc; cursor: not-allowed; }";
html += ".status { padding: 10px; margin: 10px 0; border-radius: 5px; }";
html += ".success { background: #d4edda; border: 1px solid #c3e6cb; color: #155724; }";
html += ".error { background: #f8d7da; border: 1px solid #f5c6cb; color: #721c24; }";
html += ".info { background: #d1ecf1; border: 1px solid #bee5eb; color: #0c5460; }";
html += ".warning { background: #fff3cd; border: 1px solid #ffeaa7; color: #856404; }";
html += "pre { background: #f8f9fa; padding: 15px; border-radius: 5px; overflow-x: auto; white-space: pre-wrap; }";
html += ".loading { display: none; }";
html += "</style></head><body>";
html += "<div class=\"container\">";
html += "<h1>🔍 NFC Scanner</h1>";
html += "<p>Connect to WiFi: <strong>NFC-Scanner</strong> (Password: nfc123456)</p>";
html += "<div>";
html += "<button class=\"button\" onclick=\"getVersion()\">Get Version</button>";
html += "<button class=\"button\" onclick=\"scanCard()\">Scan Card</button>";
html += "<button class=\"button\" onclick=\"dumpCard()\">Dump Card Data</button>";
html += "<button class=\"button\" onclick=\"securityAnalysis()\" style=\"background: #dc3545;\">Security Analysis</button>";
html += "</div>";
html += "<div class=\"warning\" style=\"background: #fff3cd; border: 1px solid #ffeaa7; color: #856404; padding: 10px; margin: 10px 0; border-radius: 5px;\">";
html += "⚠️ <strong>RESEARCH ONLY:</strong> Security analysis features are for educational and authorized security research purposes only.";
html += "</div>";
html += "<div id=\"loading\" class=\"loading\">⏳ Processing...</div>";
html += "<div id=\"status\"></div>";
html += "<div id=\"result\"></div>";
html += "</div>";
html += "<script>";
html += "function showLoading(show) { document.getElementById('loading').style.display = show ? 'block' : 'none'; }";
html += "function showStatus(message, type) { const status = document.getElementById('status'); status.className = 'status ' + (type || 'info'); status.innerHTML = message; }";
html += "function showResult(data) { document.getElementById('result').innerHTML = '<pre>' + JSON.stringify(data, null, 2) + '</pre>'; }";
html += "async function getVersion() { showLoading(true); try { const response = await fetch('/version'); const data = await response.json(); showStatus('✅ Version info retrieved', 'success'); showResult(data); } catch (error) { showStatus('❌ Error: ' + error.message, 'error'); } showLoading(false); }";
html += "async function scanCard() { showLoading(true); showStatus('🔍 Scanning for NFC card... Place card near reader', 'info'); try { const response = await fetch('/scan'); const data = await response.json(); if (data.success && data.card_present) { showStatus('✅ Card detected!', 'success'); } else { showStatus('❌ No card detected', 'error'); } showResult(data); } catch (error) { showStatus('❌ Error: ' + error.message, 'error'); } showLoading(false); }";
html += "async function dumpCard() { showLoading(true); showStatus('📥 Dumping card data... This may take a moment', 'info'); try { const response = await fetch('/dump'); const data = await response.json(); if (data.success) { showStatus('✅ Card data dumped successfully!', 'success'); } else { showStatus('❌ Failed to dump card data', 'error'); } showResult(data); } catch (error) { showStatus('❌ Error: ' + error.message, 'error'); } showLoading(false); }";
html += "async function securityAnalysis() { if (!confirm('This will perform comprehensive security analysis including key recovery attempts. Continue? (Research purposes only)')) { return; } showLoading(true); showStatus('🔍 Performing security analysis... This may take several minutes', 'info'); try { const response = await fetch('/security'); const data = await response.json(); if (data.success) { const riskLevel = data.security_analysis.risk_level; let riskColor = 'success'; if (riskLevel >= 7) riskColor = 'error'; else if (riskLevel >= 4) riskColor = 'warning'; showStatus('🛡️ Security analysis complete! Risk Level: ' + riskLevel + '/10', riskColor); } else { showStatus('❌ Security analysis failed', 'error'); } showResult(data); } catch (error) { showStatus('❌ Error: ' + error.message, 'error'); } showLoading(false); }";
html += "window.onload = function() { getVersion(); };";
html += "</script></body></html>";
return html;
}

56
src/nfc_types.h Normal file
View File

@@ -0,0 +1,56 @@
#ifndef NFC_TYPES_H
#define NFC_TYPES_H
#include <Arduino.h>
// Shared NFC data structure used across modules
struct NFCData {
bool cardPresent = false;
uint8_t uid[10]; // Extended for longer UIDs
uint8_t uidLength = 0;
uint16_t atqa = 0;
uint8_t sak = 0;
String cardType = "";
String technology = "";
unsigned long scanTime = 0;
// Type A specific
uint8_t ats[32];
uint8_t atsLength = 0;
// Type B specific
uint8_t pupi[4];
uint8_t appData[4];
uint8_t protocolInfo[3];
// FeliCa specific
uint16_t systemCode = 0;
uint8_t pmm[8];
uint8_t requestData[16];
// Raw data and parsed content
uint8_t rawData[2048]; // Increased buffer
size_t rawDataLength = 0;
// NDEF data
bool hasNDEF = false;
String ndefRecords = "";
uint16_t ndefLength = 0;
// Capability Container
uint8_t ccData[16];
bool hasCCData = false;
// Version info (for NTAG/Ultralight)
uint8_t versionData[8];
bool hasVersionData = false;
// Lock and configuration
uint32_t lockBits = 0;
uint8_t configPages[8];
bool hasConfigData = false;
};
extern NFCData lastScan;
#endif // NFC_TYPES_H

481
src/security_research.cpp Normal file
View File

@@ -0,0 +1,481 @@
#include "security_research.h"
#include "nfc_types.h"
#include <SPI.h>
// MIFARE Classic default keys commonly found in the wild
const uint8_t defaultKeys[][6] = {
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Factory default
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // All zeros
{0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5}, // NXP default
{0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5}, // Alternative NXP
{0x4D, 0x3A, 0x99, 0xC3, 0x51, 0xDD}, // Hotel key system
{0x1A, 0x98, 0x2C, 0x7E, 0x45, 0x9A}, // Transport system
{0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7}, // MAD key
{0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}, // Common test key
{0x71, 0x4C, 0x5C, 0x88, 0x6E, 0x97}, // Mifare Plus
{0x58, 0x7E, 0xE5, 0xF9, 0x35, 0x0F}, // Infineon default
{0xA0, 0x47, 0x8C, 0xC3, 0x90, 0x91}, // HID iClass
{0x53, 0x3C, 0xB6, 0xC7, 0x23, 0xF6}, // Access control
{0x8F, 0xD0, 0xA4, 0xF2, 0x56, 0xE9}, // Hotel chain
{0x26, 0x94, 0x0B, 0x21, 0xFF, 0x5B}, // University system
{0xEA, 0x0F, 0xD7, 0x3C, 0xB1, 0x49} // Government ID
};
const uint8_t numDefaultKeys = sizeof(defaultKeys) / sizeof(defaultKeys[0]);
// Common DESFire Application IDs found in real systems
const uint32_t commonDESFireAIDs[] = {
0x000000U, // Master application
0x112233U, // Common test AID
0x123456U, // Default demo AID
0x4D494641U, // "MIFA" ASCII
0x505249U, // "PRI" - Proximity
0x434152U, // "CAR" - Parking
0x545241U, // "TRA" - Transit
0x414343U, // "ACC" - Access control
0x484944U, // "HID" - HID systems
0x4E4643U, // "NFC" - NFC applications
0x50415953U, // "PAYS" - Payment systems
0x4C4F5941U, // "LOYA" - Loyalty programs (truncated to fit uint32_t)
};
const uint8_t numCommonAIDs = sizeof(commonDESFireAIDs) / sizeof(uint32_t);
SecurityAnalysis lastAnalysis;
extern Adafruit_PN532 nfc;
bool performSecurityAnalysis() {
Serial.println("=== STARTING COMPREHENSIVE SECURITY ANALYSIS ===");
Serial.println("FOR RESEARCH PURPOSES ONLY");
// Clear previous analysis
memset(&lastAnalysis, 0, sizeof(lastAnalysis));
if (!lastScan.cardPresent) {
Serial.println("No card present for analysis");
return false;
}
Serial.print("Analyzing card type: ");
Serial.println(lastScan.cardType);
// Perform RF fingerprinting first
performRFFingerprinting();
// Route to specific analysis based on card type
if (lastScan.cardType.indexOf("Classic") >= 0) {
analyzeMIFAREClassic();
} else if (lastScan.cardType.indexOf("DESFire") >= 0) {
analyzeDESFire();
} else if (lastScan.cardType.indexOf("Ultralight") >= 0 || lastScan.cardType.indexOf("NTAG") >= 0) {
testNDEFVulnerabilities();
}
// Universal tests
detectCloning();
performTimingAnalysis();
analyzeProtocolCompliance();
// Calculate risk level
calculateRiskLevel();
Serial.println("=== SECURITY ANALYSIS COMPLETE ===");
return true;
}
bool analyzeMIFAREClassic() {
Serial.println("Performing MIFARE Classic security analysis...");
lastAnalysis.totalSectors = (lastScan.sak == 0x18) ? 40 : 16;
lastAnalysis.accessibleSectors = 0;
// Test default keys on all sectors
for (uint8_t sector = 0; sector < lastAnalysis.totalSectors; sector++) {
for (uint8_t keyIdx = 0; keyIdx < numDefaultKeys; keyIdx++) {
// Test Key A
if (authenticateWithKey(sector, 0x60, (uint8_t*)defaultKeys[keyIdx])) {
lastAnalysis.accessibleSectors++;
lastAnalysis.hasWeakKeys = true;
String keyHex = "";
for (int i = 0; i < 6; i++) {
if (defaultKeys[keyIdx][i] < 0x10) keyHex += "0";
keyHex += String(defaultKeys[keyIdx][i], HEX);
}
lastAnalysis.keyAnalysis += "Sector " + String(sector) + " Key A: " + keyHex + "; ";
// Try to read sector data
readSectorWithKey(sector, (uint8_t*)defaultKeys[keyIdx], 0x60);
break;
}
// Test Key B
if (authenticateWithKey(sector, 0x61, (uint8_t*)defaultKeys[keyIdx])) {
if (lastAnalysis.keyAnalysis.indexOf("Sector " + String(sector)) == -1) {
lastAnalysis.accessibleSectors++;
}
lastAnalysis.hasWeakKeys = true;
String keyHex = "";
for (int i = 0; i < 6; i++) {
if (defaultKeys[keyIdx][i] < 0x10) keyHex += "0";
keyHex += String(defaultKeys[keyIdx][i], HEX);
}
lastAnalysis.keyAnalysis += "Sector " + String(sector) + " Key B: " + keyHex + "; ";
break;
}
}
}
// Advanced attacks (simulation - requires specialized hardware)
if (lastAnalysis.accessibleSectors < lastAnalysis.totalSectors) {
Serial.println("Simulating advanced key recovery attacks...");
// performNestedAttack();
// performDarksideAttack();
lastAnalysis.vulnerabilities += "Potential nested/darkside attack vectors; ";
}
Serial.print("Accessible sectors: ");
Serial.print(lastAnalysis.accessibleSectors);
Serial.print("/");
Serial.println(lastAnalysis.totalSectors);
return true;
}
bool analyzeDESFire() {
Serial.println("Performing DESFire security analysis...");
// Get DESFire version information
getDESFireVersion();
// Enumerate applications
enumerateApplications();
// Test for default authentication keys
// DESFire uses AES/3DES keys, not the simple 6-byte MIFARE keys
uint8_t defaultAESKey[16] = {0x00}; // All zeros default
// Test master application access
if (selectDESFireApplication(0x000000)) {
lastAnalysis.hasDefaultKeys = true;
lastAnalysis.vulnerabilities += "Master app accessible with default key; ";
}
Serial.print("Found applications: ");
Serial.println(lastAnalysis.applicationCount);
return true;
}
bool performKeyRecovery() {
Serial.println("Performing key recovery analysis...");
// This would implement advanced cryptographic attacks
// For research purposes, we simulate the process
if (lastScan.cardType.indexOf("Classic") >= 0) {
Serial.println("MIFARE Classic key recovery vectors:");
Serial.println("- Default key testing: IMPLEMENTED");
Serial.println("- Nested attack: SIMULATION");
Serial.println("- Darkside attack: SIMULATION");
Serial.println("- Hardnested attack: REQUIRES SPECIALIZED HARDWARE");
lastAnalysis.vulnerabilities += "Multiple key recovery vectors available; ";
}
return true;
}
bool enumerateApplications() {
Serial.println("Enumerating DESFire applications...");
lastAnalysis.applicationCount = 0;
lastAnalysis.applications = "";
for (uint8_t i = 0; i < numCommonAIDs; i++) {
if (selectDESFireApplication(commonDESFireAIDs[i])) {
lastAnalysis.applicationCount++;
String aidHex = String(commonDESFireAIDs[i], HEX);
lastAnalysis.applications += "0x" + aidHex + " ";
// Try to list files in this application
listDESFireFiles();
}
}
return lastAnalysis.applicationCount > 0;
}
bool detectCloning() {
Serial.println("Performing clone detection analysis...");
// Analyze UID for cloning indicators
bool suspiciousUID = false;
// Check for common clone UID patterns
if (lastScan.uid[0] == 0x08 || lastScan.uid[0] == 0x88) {
suspiciousUID = true;
lastAnalysis.cloneIndicators += "Suspicious UID prefix (0x08/0x88); ";
}
// Check for Chinese clone manufacturers
if (lastScan.uid[0] == 0x62 || lastScan.uid[0] == 0x63) {
suspiciousUID = true;
lastAnalysis.cloneIndicators += "Possible Chinese clone UID; ";
}
// Analyze timing inconsistencies
if (lastScan.scanTime > 500) { // Clones often respond slower
suspiciousUID = true;
lastAnalysis.cloneIndicators += "Slow response time; ";
}
lastAnalysis.possibleClone = suspiciousUID;
if (suspiciousUID) {
Serial.println("WARNING: Possible cloned card detected!");
}
return true;
}
bool performRFFingerprinting() {
Serial.println("Performing RF fingerprinting...");
// Measure response characteristics
unsigned long startTime = micros();
// Send multiple commands and measure timing
uint8_t uid[10];
uint8_t uidLen;
for (int i = 0; i < 5; i++) {
nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLen, 100);
}
lastAnalysis.responseTime = (micros() - startTime) / 5; // Average response time
// Analyze response patterns for fingerprinting
lastAnalysis.rfFingerprint = "RT:" + String(lastAnalysis.responseTime) + "us";
// Check for timing anomalies
if (lastAnalysis.responseTime > 10000) { // > 10ms is suspicious
lastAnalysis.rfFingerprint += " SLOW";
lastAnalysis.vulnerabilities += "Abnormal response timing; ";
}
return true;
}
bool performTimingAnalysis() {
Serial.println("Performing timing attack analysis...");
// Test for timing-based vulnerabilities
if (lastScan.cardType.indexOf("Classic") >= 0) {
Serial.println("Testing MIFARE Classic timing vulnerabilities...");
// Simulate timing attack on authentication
unsigned long authTimes[10];
for (int i = 0; i < 10; i++) {
unsigned long start = micros();
// Attempt authentication with wrong key
uint8_t wrongKey[6] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC};
authenticateWithKey(0, 0x60, wrongKey);
authTimes[i] = micros() - start;
}
// Analyze timing variance
unsigned long avgTime = 0;
for (int i = 0; i < 10; i++) {
avgTime += authTimes[i];
}
avgTime /= 10;
// Check for timing side-channels
bool timingVulnerable = false;
for (int i = 0; i < 10; i++) {
if (abs((long)(authTimes[i] - avgTime)) > avgTime * 0.1) { // >10% variance
timingVulnerable = true;
break;
}
}
if (timingVulnerable) {
lastAnalysis.vulnerabilities += "Timing side-channel vulnerability; ";
}
}
return true;
}
bool testNDEFVulnerabilities() {
Serial.println("Testing NDEF vulnerabilities...");
if (!lastScan.hasNDEF) {
Serial.println("No NDEF data to analyze");
return false;
}
// Check for common NDEF vulnerabilities
if (lastScan.ndefRecords.indexOf("javascript:") >= 0) {
lastAnalysis.vulnerabilities += "Potential XSS in NDEF URI; ";
}
if (lastScan.ndefRecords.indexOf("file://") >= 0) {
lastAnalysis.vulnerabilities += "Local file access in NDEF URI; ";
}
// Check for oversized NDEF records (buffer overflow potential)
if (lastScan.ndefLength > 1024) {
lastAnalysis.vulnerabilities += "Oversized NDEF record (DoS potential); ";
}
// Check for malformed NDEF structure
if (lastScan.ndefRecords.indexOf("Record(") >= 0) {
lastAnalysis.vulnerabilities += "Malformed NDEF records detected; ";
}
return true;
}
// Low-level PN532 functions for advanced research
bool sendRawCommand(uint8_t* cmd, uint8_t cmdLen, uint8_t* response, uint8_t* responseLen) {
// This would send raw commands to PN532 for advanced research
// Requires direct SPI/I2C communication bypassing Adafruit library
Serial.println("Raw command interface - requires low-level implementation");
return false;
}
bool authenticateWithKey(uint8_t sector, uint8_t keyType, uint8_t* key) {
// Calculate block number for sector
uint8_t block = (sector < 32) ? (sector * 4) : (128 + (sector - 32) * 16);
// Attempt authentication
bool success = nfc.mifareclassic_AuthenticateBlock(lastScan.uid, lastScan.uidLength, block, keyType, key);
if (success) {
Serial.print("Authenticated sector ");
Serial.print(sector);
Serial.print(" with key type 0x");
Serial.println(keyType, HEX);
}
return success;
}
bool readSectorWithKey(uint8_t sector, uint8_t* key, uint8_t keyType) {
uint8_t block = (sector < 32) ? (sector * 4) : (128 + (sector - 32) * 16);
uint8_t data[16];
// Authenticate first
if (!nfc.mifareclassic_AuthenticateBlock(lastScan.uid, lastScan.uidLength, block, keyType, key)) {
return false;
}
// Read data blocks in sector (skip trailer block)
uint8_t blocksInSector = (sector < 32) ? 3 : 15;
for (uint8_t i = 0; i < blocksInSector; i++) {
if (nfc.mifareclassic_ReadDataBlock(block + i, data)) {
Serial.print("Sector ");
Serial.print(sector);
Serial.print(" Block ");
Serial.print(i);
Serial.print(": ");
for (int j = 0; j < 16; j++) {
if (data[j] < 0x10) Serial.print("0");
Serial.print(data[j], HEX);
Serial.print(" ");
}
Serial.println();
}
}
return true;
}
bool selectDESFireApplication(uint32_t aid) {
// This would require ISO14443-4 / ISO-DEP implementation
Serial.print("Attempting to select DESFire AID: 0x");
Serial.println(aid, HEX);
// Simulate selection for research purposes
return (aid == 0x000000 || aid == 0x123456); // Simulate some success
}
bool getDESFireVersion() {
Serial.println("Getting DESFire version info...");
// This would send GET_VERSION command to DESFire card
return false;
}
bool listDESFireFiles() {
Serial.println("Listing files in DESFire application...");
// This would send GET_FILE_IDS command
return false;
}
bool readDESFireFile(uint8_t fileId) {
Serial.print("Reading DESFire file ID: ");
Serial.println(fileId);
return false;
}
bool setRFConfiguration(uint8_t cfgItem, uint8_t* cfgData, uint8_t cfgLen) {
Serial.println("Setting RF configuration (stub)");
return false;
}
bool performAntiCollisionAnalysis() {
Serial.println("Performing anti-collision analysis (stub)");
return false;
}
bool performReplayAttack() {
Serial.println("Performing replay attack simulation (stub)");
return false;
}
bool analyzeCryptography() {
Serial.println("Analyzing cryptographic parameters (stub)");
return false;
}
bool testAccessControls() {
Serial.println("Testing access controls (stub)");
return false;
}
bool performFuzzTesting() {
Serial.println("Performing fuzz testing (stub)");
return false;
}
bool analyzeProtocolCompliance() {
Serial.println("Analyzing protocol compliance...");
// Check for protocol violations that might indicate clones or modified cards
lastAnalysis.vulnerabilities += "Protocol analysis requires specialized equipment; ";
return true;
}
void calculateRiskLevel() {
lastAnalysis.riskLevel = 0;
if (lastAnalysis.hasWeakKeys) lastAnalysis.riskLevel += 3;
if (lastAnalysis.possibleClone) lastAnalysis.riskLevel += 2;
if (lastAnalysis.hasDefaultKeys) lastAnalysis.riskLevel += 4;
if (lastAnalysis.vulnerabilities.length() > 50) lastAnalysis.riskLevel += 1;
// Cap at 10
if (lastAnalysis.riskLevel > 10) lastAnalysis.riskLevel = 10;
Serial.print("Calculated risk level: ");
Serial.print(lastAnalysis.riskLevel);
Serial.println("/10");
}

86
src/security_research.h Normal file
View File

@@ -0,0 +1,86 @@
#ifndef SECURITY_RESEARCH_H
#define SECURITY_RESEARCH_H
#include <Arduino.h>
#include <Adafruit_PN532.h>
// Security Research Module for PN532
// FOR EDUCATIONAL AND RESEARCH PURPOSES ONLY
// MIFARE Classic default keys for security analysis
extern const uint8_t defaultKeys[][6];
extern const uint8_t numDefaultKeys;
// DESFire application IDs for enumeration
extern const uint32_t commonDESFireAIDs[];
extern const uint8_t numCommonAIDs;
// Security analysis results structure
struct SecurityAnalysis {
// MIFARE Classic analysis
bool hasWeakKeys = false;
uint8_t accessibleSectors = 0;
uint8_t totalSectors = 0;
String keyAnalysis = "";
// DESFire analysis
uint8_t applicationCount = 0;
String applications = "";
bool hasDefaultKeys = false;
// RF fingerprinting
uint16_t responseTime = 0;
uint8_t signalStrength = 0;
String rfFingerprint = "";
// Clone detection
bool possibleClone = false;
String cloneIndicators = "";
// Vulnerability assessment
String vulnerabilities = "";
uint8_t riskLevel = 0; // 0-10 scale
};
// Function declarations for security research
bool performSecurityAnalysis();
bool analyzeMIFAREClassic();
bool analyzeDESFire();
bool performKeyRecovery();
bool enumerateApplications();
bool detectCloning();
bool performRFFingerprinting();
bool performTimingAnalysis();
bool testNDEFVulnerabilities();
// Low-level PN532 functions
bool sendRawCommand(uint8_t* cmd, uint8_t cmdLen, uint8_t* response, uint8_t* responseLen);
bool setRFConfiguration(uint8_t cfgItem, uint8_t* cfgData, uint8_t cfgLen);
bool performAntiCollisionAnalysis();
bool analyzeProtocolCompliance();
// MIFARE Classic specific
bool authenticateWithKey(uint8_t sector, uint8_t keyType, uint8_t* key);
bool readSectorWithKey(uint8_t sector, uint8_t* key, uint8_t keyType);
bool dumpAllSectors();
bool performNestedAttack();
bool performDarksideAttack();
// DESFire specific
bool selectDESFireApplication(uint32_t aid);
bool getDESFireVersion();
bool listDESFireFiles();
bool readDESFireFile(uint8_t fileId);
// Advanced analysis
bool performReplayAttack();
bool analyzeCryptography();
bool testAccessControls();
bool performFuzzTesting();
// Utility
void calculateRiskLevel();
extern SecurityAnalysis lastAnalysis;
#endif