diff --git a/platformio.ini b/platformio.ini index 92e56e7c..190e5759 100644 --- a/platformio.ini +++ b/platformio.ini @@ -54,6 +54,7 @@ lib_deps = https://github.com/bmorcelli/ESPping/ https://github.com/rennancockles/PN532 https://github.com/rennancockles/MFRC522-I2C + https://github.com/rennancockles/ESP-ChameleonUltra NTPClient Timezone ESP32Time @@ -75,7 +76,7 @@ lib_deps = tobozo/ESP32-PSRamFS ;chegewara/EspTinyUSB bitbank2/AnimatedGIF - + [env:m5stack-cplus2] platform = https://github.com/bmorcelli/platform-espressif32/releases/download/0.0.4/platform-espressif32.zip board = m5stick-c @@ -634,7 +635,7 @@ build_flags = ;Can run USB as HID ;-DUSB_as_HID=1 ;uncomment to enable - -DBAD_TX=32 + -DBAD_TX=32 -DBAD_RX=33 ;Battery ADC read pin @@ -763,7 +764,7 @@ build_flags = ;Can run USB as HID ;-DUSB_as_HID=1 ;uncomment to enable - -DBAD_TX=21 + -DBAD_TX=21 -DBAD_RX=22 ;Battery ADC read pin @@ -889,7 +890,7 @@ build_flags = ;Can run USB as HID ;-DUSB_as_HID=1 ;uncomment to enable - -DBAD_TX=21 + -DBAD_TX=21 -DBAD_RX=22 ;Battery ADC read pin @@ -995,7 +996,7 @@ build_flags = ; needed for serial -DARDUINO_USB_CDC_ON_BOOT=1 -DBOARD_HAS_PSRAM - + ; grove pins ; defaults from https://github.com/espressif/arduino-esp32/blob/master/variants/esp32s3/pins_arduino.h -DGROVE_SDA=8 ; default RF TX pin @@ -1078,7 +1079,7 @@ build_flags = -DSPI_MOSI_PIN=11 -DSPI_MISO_PIN=13 -DSPI_SS_PIN=10 - + lib_deps = ${common.lib_deps} @@ -1344,7 +1345,7 @@ build_flags = ;tft Brighness Control -DTFT_BRIGHT_CHANNEL=0 -DTFT_BRIGHT_Bits=8 - -DTFT_BRIGHT_FREQ=5000 + -DTFT_BRIGHT_FREQ=5000 ;Default I2C port -DGROVE_SDA=27 @@ -1498,7 +1499,7 @@ build_flags = ;tft Brighness Control -DTFT_BRIGHT_CHANNEL=0 -DTFT_BRIGHT_Bits=8 - -DTFT_BRIGHT_FREQ=10000 + -DTFT_BRIGHT_FREQ=10000 ;Default I2C port -DGROVE_SDA=27 diff --git a/src/core/sd_functions.cpp b/src/core/sd_functions.cpp index 9dce6a07..ed1573f1 100644 --- a/src/core/sd_functions.cpp +++ b/src/core/sd_functions.cpp @@ -385,6 +385,8 @@ bool checkExt(String ext, String pattern) { pattern.toUpperCase(); if (ext == pattern) return true; + pattern = "^(" + pattern + ")$"; + char charArray[pattern.length() + 1]; pattern.toCharArray(charArray, pattern.length() + 1); std::regex ext_regex(charArray); diff --git a/src/modules/rfid/PN532.cpp b/src/modules/rfid/PN532.cpp index 8c4781b8..f6a3ce49 100644 --- a/src/modules/rfid/PN532.cpp +++ b/src/modules/rfid/PN532.cpp @@ -9,6 +9,7 @@ #include "PN532.h" #include "core/sd_functions.h" #include "core/i2c_finder.h" +#include "core/display.h" PN532::PN532(bool use_i2c) { @@ -31,6 +32,7 @@ int PN532::read() { if (!nfc.startPassiveTargetIDDetection()) return TAG_NOT_PRESENT; if (!nfc.readDetectedPassiveTargetID()) return FAILURE; + displayInfo("Reading data blocks..."); pageReadSuccess = read_data_blocks(); format_data(); set_uid(); @@ -432,6 +434,7 @@ int PN532::write_data_blocks() { int lineBreakIndex; int pageIndex; bool blockWriteSuccess; + int totalSize = strAllPages.length(); while (strAllPages.length() > 0) { lineBreakIndex = strAllPages.indexOf("\n"); @@ -463,6 +466,8 @@ int PN532::write_data_blocks() { } if (!blockWriteSuccess) return FAILURE; + + progressHandler(totalSize-strAllPages.length(), totalSize, "Writing data blocks..."); } return SUCCESS; diff --git a/src/modules/rfid/RFID2.cpp b/src/modules/rfid/RFID2.cpp index 5b8b7488..2ef2aeca 100644 --- a/src/modules/rfid/RFID2.cpp +++ b/src/modules/rfid/RFID2.cpp @@ -9,6 +9,7 @@ #include "RFID2.h" #include "core/sd_functions.h" #include "core/i2c_finder.h" +#include "core/display.h" #define RFID2_I2C_ADDRESS 0x28 @@ -44,6 +45,8 @@ int RFID2::read() { if (!PICC_IsNewCardPresent() || !mfrc522.PICC_ReadCardSerial()) { return TAG_NOT_PRESENT; } + + displayInfo("Reading data blocks..."); pageReadSuccess = read_data_blocks(); format_data(); set_uid(); @@ -450,6 +453,7 @@ int RFID2::write_data_blocks() { int lineBreakIndex; int pageIndex; bool blockWriteSuccess; + int totalSize = strAllPages.length(); while (strAllPages.length() > 0) { lineBreakIndex = strAllPages.indexOf("\n"); @@ -481,6 +485,8 @@ int RFID2::write_data_blocks() { } if (!blockWriteSuccess) return FAILURE; + + progressHandler(totalSize-strAllPages.length(), totalSize, "Writing data blocks..."); } return SUCCESS; diff --git a/src/modules/rfid/chameleon.cpp b/src/modules/rfid/chameleon.cpp index de783bad..05a7b265 100644 --- a/src/modules/rfid/chameleon.cpp +++ b/src/modules/rfid/chameleon.cpp @@ -10,510 +10,1120 @@ #include "core/mykeyboard.h" #include "core/display.h" -#define CMD_DELAY 500 -#define MAX_DUMP_SIZE 160 +Chameleon::Chameleon() { + setup(); +} + + +Chameleon::~Chameleon() {} + + +void Chameleon::setup() { + displayBanner(); + + if (!connect()) return; + + displayBanner(); -uint8_t calculateLRC(const uint8_t *data, size_t length) { - uint8_t lrc = 0; + setMode(BATTERY_INFO_MODE); + delay(500); + return loop(); +} + + +bool Chameleon::connect() { + displayInfo("Turn on Chameleon device", true); + + displayBanner(); + padprintln(""); + padprintln("Searching Chameleon Device..."); + + if (!chmUltra.searchChameleonDevice()) { + displayError("Chameleon not found"); + delay(1000); + return false; + } - for (size_t i = 0; i < length; i++) { - lrc += data[i]; + if (!chmUltra.connectToChamelon()) { + displayError("Chameleon connect error"); + delay(1000); + return false; } - lrc = 0x100 - (lrc & 0xff); - return lrc; + displaySuccess("Chameleon Connected"); + delay(1000); + + return true; } -class scanCallbacks : public NimBLEAdvertisedDeviceCallbacks { - void onResult(NimBLEAdvertisedDevice* advertisedDevice) { - if (advertisedDevice->getName() == "ChameleonUltra") { - Serial.print("Chameleon Device found: "); - Serial.println(advertisedDevice->toString().c_str()); - NimBLEDevice::getScan()->stop(); +void Chameleon::loop() { + while(1) { + if (checkEscPress() || returnToMenu) { + break; + } + + if (checkSelPress()) { + selectMode(); + } + + switch (currentMode) { + case BATTERY_INFO_MODE: + getBatteryInfo(); + break; + + case FACTORY_RESET_MODE: + factoryReset(); + break; + + case LF_READ_MODE: + readLFTag(); + break; + case LF_CLONE_MODE: + cloneLFTag(); + break; + case LF_CUSTOM_UID_MODE: + customLFUid(); + break; + case LF_EMULATION_MODE: + emulateLF(); + break; + case LF_SAVE_MODE: + saveFileLF(); + break; + case LF_LOAD_MODE: + loadFileLF(); + break; + + case HF_READ_MODE: + readHFTag(); + break; + case HF_SCAN_MODE: + scanHFTags(); + break; + case HF_WRITE_MODE: + writeHFData(); + break; + case HF_CLONE_MODE: + cloneHFTag(); + break; + case HF_CUSTOM_UID_MODE: + customHFUid(); + break; + case HF_EMULATION_MODE: + emulateHF(); + break; + case HF_SAVE_MODE: + saveFileHF(); + break; + case HF_LOAD_MODE: + loadFileHF(); + break; } } -}; +} -void chameleonNotifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){ - String str = (isNotify == true) ? "Notification" : "Indication"; - str += " value:"; - for (int i=0; i0) { + saveHFScanResult(); + _scanned_set.clear(); + _scanned_tags.clear(); + } + + chmUltra.cmdChangeMode(chmUltra.HW_MODE_READER); + + switch (mode) { + case LF_READ_MODE: + case HF_READ_MODE: + _lf_read_uid = false; + _hf_read_uid = false; + break; + case HF_SCAN_MODE: + _scanned_set.clear(); + _scanned_tags.clear(); + break; + case LF_LOAD_MODE: + case HF_LOAD_MODE: + case LF_CUSTOM_UID_MODE: + case HF_CUSTOM_UID_MODE: + _lf_read_uid = false; + _hf_read_uid = false; + break; + case LF_CLONE_MODE: + padprintln("New UID: " + printableLFUID); + padprintln(""); + break; + case HF_CLONE_MODE: + padprintln("Device type: " + printableHFUID.piccType); + padprintln("New UID: " + printableHFUID.uid); + padprintln(""); + break; + case LF_EMULATION_MODE: + padprintln("UID: " + printableLFUID); + padprintln(""); + break; + case HF_EMULATION_MODE: + padprintln("Device type: " + printableHFUID.piccType); + padprintln("UID: " + printableHFUID.uid); + padprintln(""); + break; + + case LF_SAVE_MODE: + case HF_SAVE_MODE: + case BATTERY_INFO_MODE: + case FACTORY_RESET_MODE: + break; + } + delay(300); } -Chameleon::~Chameleon() { - Serial.println("Killing Chameleon..."); - if (NimBLEDevice::getInitialized()) { - Serial.println("Deiniting ble..."); - NimBLEDevice::deinit(true); +void Chameleon::displayBanner() { + drawMainBorderWithTitle("CHAMELEON"); + + switch (currentMode) { + case BATTERY_INFO_MODE: + padprintln(" BATTERY INFO"); + padprintln(" ------------"); + break; + case FACTORY_RESET_MODE: + padprintln(" FACTORY RESET"); + padprintln(" -------------"); + break; + + case LF_READ_MODE: + padprintln(" LF READ MODE"); + padprintln(" ------------"); + break; + case LF_CLONE_MODE: + padprintln(" LF CLONE MODE"); + padprintln(" -------------"); + break; + case LF_CUSTOM_UID_MODE: + padprintln("LF CUSTOM UID MODE"); + padprintln("------------------"); + break; + case LF_EMULATION_MODE: + padprintln(" LF EMULATION MODE"); + padprintln(" -----------------"); + break; + case LF_SAVE_MODE: + padprintln(" LF SAVE MODE"); + padprintln(" ------------"); + break; + case LF_LOAD_MODE: + padprintln(" LF LOAD MODE"); + padprintln(" ------------"); + break; + + case HF_READ_MODE: + padprintln(" HF READ MODE"); + padprintln(" ------------"); + break; + case HF_SCAN_MODE: + padprintln(" HF SCAN MODE"); + padprintln(" ------------"); + break; + case HF_CLONE_MODE: + padprintln(" HF CLONE MODE"); + padprintln(" -------------"); + break; + case HF_WRITE_MODE: + padprintln(" HF WRITE MODE"); + padprintln(" -------------"); + break; + case HF_CUSTOM_UID_MODE: + padprintln("HF CUSTOM UID MODE"); + padprintln("------------------"); + break; + case HF_EMULATION_MODE: + padprintln(" HF EMULATION MODE"); + padprintln(" -----------------"); + break; + case HF_SAVE_MODE: + padprintln(" HF SAVE MODE"); + padprintln(" ------------"); + break; + case HF_LOAD_MODE: + padprintln(" HF LOAD MODE"); + padprintln(" ------------"); + break; } + + tft.setTextSize(FP); + padprintln(""); + padprintln("Press [OK] to change mode."); + padprintln(""); } -void Chameleon::setup() { - if (!openDumpFile()) return; +void Chameleon::dumpHFCardDetails() { + padprintln("Device type: " + printableHFUID.piccType); + padprintln("UID: " + printableHFUID.uid); + padprintln("ATQA: " + printableHFUID.atqa); + padprintln("SAK: " + printableHFUID.sak); + if (!pageReadSuccess) padprintln("[!] Failed to read data blocks"); +} - if (!getEmulationTagType()) { - displayError("Invalid tag type"); - delay(1000); - return; + +void Chameleon::dumpHFScanResults() { + for (int i = _scanned_tags.size(); i > 0; i--) { + if (_scanned_tags.size() > 5 && i <= _scanned_tags.size()-5) return; + padprintln(String(i) + ": " + _scanned_tags[i-1]); } +} - selectEmulationSlot(); - displayDumpInfo(); - padprintln("Searching Chameleon Device..."); +uint8_t Chameleon::selectSlot() { + uint8_t slot = 8; - displayInfo("Turn on Chameleon device", true); + options = { + {"1", [&]() { slot=1; }}, + {"2", [&]() { slot=2; }}, + {"3", [&]() { slot=3; }}, + {"4", [&]() { slot=4; }}, + {"5", [&]() { slot=5; }}, + {"6", [&]() { slot=6; }}, + {"7", [&]() { slot=7; }}, + {"8", [&]() { slot=8; }}, + }; + delay(200); + loopOptions(options,false,true,"Set Emulation Slot"); - if (!searchChameleonDevice()) { - displayError("Chameleon not found"); - delay(1000); - return; + return slot; +} + + +bool Chameleon::isMifareClassic(byte sak) { + return ( + sak == 0x08 + || sak == 0x09 + || sak == 0x10 + || sak == 0x11 + || sak == 0x18 + || sak == 0x19 + ); +} + + +// HW Methods + +void Chameleon::getBatteryInfo() { + if (_battery_set) return; + + chmUltra.cmdBatteryInfo(); + + displayBanner(); + padprintln(""); + padprintln("Battery " + String(chmUltra.cmdResponse.data[2]) + "%"); + + _battery_set = true; + + delay(500); +} + + +void Chameleon::factoryReset() { + bool proceed = false; + + options = { + {"No", [&]() { proceed=false; }}, + {"Yes", [&]() { proceed=true; }}, + }; + delay(200); + loopOptions(options,false,true,"Proceed with Factory Reset?"); + + displayBanner(); + + if (!proceed) { + displayInfo("Aborting factory reset."); + } + else if (chmUltra.cmdFactoryReset()) { + displaySuccess("Factory reset success"); + } + else { + displayError("Factory reset error"); } - if (!connectToChamelon()) { - displayError("Chameleon connect error"); - delay(1000); - return; + delay(1000); + returnToMenu = true; +} + + +// LF Methods + +void Chameleon::readLFTag() { + if (!chmUltra.cmdLFRead()) return; + + formatLFUID(); + lfTagData = chmUltra.lfTagData; + + displayBanner(); + padprintln("UID: " + printableLFUID); + + _lf_read_uid = true; + delay(500); +} + + +void Chameleon::cloneLFTag() { + if (!chmUltra.cmdLFRead()) return; + + if (chmUltra.cmdLFWrite(lfTagData.uidByte, lfTagData.size)) { + displaySuccess("UID written successfully."); + } else { + displayError("Error writing UID to tag."); } - displayDumpInfo(); - padprintln("Chameleon Connected"); - padprintln("Sending commands..."); + delay(1000); + setMode(BATTERY_INFO_MODE); +} + + +void Chameleon::customLFUid() { + String custom_uid = keyboard("", 10, "UID (hex):"); + + custom_uid.trim(); + custom_uid.replace(" ", ""); + custom_uid.toUpperCase(); + + displayBanner(); - if (!sendCommands()) { - displayError("Chameleon communication error"); + if (custom_uid.length() != 10) { + displayError("Invalid UID"); delay(1000); - return; + return setMode(BATTERY_INFO_MODE); } - displaySuccess("Success"); - delay(1000); + printableLFUID = ""; + for (size_t i = 0; i < custom_uid.length(); i += 2) { + printableLFUID += custom_uid.substring(i, i + 2) + " "; + } + printableLFUID.trim(); + parseLFUID(); + + options = { + {"Clone UID", [=]() { setMode(LF_CLONE_MODE); }}, + {"Emulate", [=]() { setMode(LF_EMULATION_MODE); }}, + }; + delay(200); + loopOptions(options); } -void Chameleon::displayBanner() { - drawMainBorderWithTitle("CHAMELEON"); +void Chameleon::emulateLF() { + uint8_t slot = selectSlot(); - padprintln(" EMULATOR"); - padprintln(" --------"); + displayBanner(); - tft.setTextSize(FP); - padprintln(""); + if ( + chmUltra.cmdEnableSlot(slot, chmUltra.RFID_LF) + && chmUltra.cmdChangeActiveSlot(slot) + && chmUltra.cmdLFEconfig(lfTagData.uidByte, lfTagData.size) + && chmUltra.cmdChangeMode(chmUltra.HW_MODE_EMULATOR) + ) { + displaySuccess("Emulation successful."); + } else { + displayError("Error emulating LF tag."); + } + + delay(1000); + setMode(BATTERY_INFO_MODE); } -void Chameleon::displayDumpInfo() { +void Chameleon::loadFileLF() { displayBanner(); - padprintln(printableUID.picc_type); - padprintln("UID: " + printableUID.uid); - padprintln("SAK: " + printableUID.sak); - padprintln(""); + if (readFileLF()) { + displaySuccess("File loaded"); + delay(1000); + _lf_read_uid = true; + + options = { + {"Clone UID", [=]() { setMode(LF_CLONE_MODE); }}, + {"Emulate", [=]() { setMode(LF_EMULATION_MODE); }}, + }; + delay(200); + loopOptions(options); + } + else { + displayError("Error loading file"); + delay(1000); + setMode(BATTERY_INFO_MODE); + } +} + + +void Chameleon::saveFileLF() { + String data = printableLFUID; + data.replace(" ", ""); + String filename = keyboard(data, 30, "File name:"); + + displayBanner(); + + if (writeFileLF(filename)) { + displaySuccess("File saved."); + } + else { + displayError("Error writing file."); + } + delay(1000); + setMode(BATTERY_INFO_MODE); } -bool Chameleon::openDumpFile() { +bool Chameleon::readFileLF() { String filepath; File file; FS *fs; - if(!getFsStorage(fs)) { - displayError("Storage error"); - delay(1000); - return false; - } - - filepath = loopSD(*fs, true, "RFID|NFC"); + if(!getFsStorage(fs)) return false; + filepath = loopSD(*fs, true, "RFIDLF"); file = fs->open(filepath, FILE_READ); if (!file) { - displayError("Dump file error"); - delay(1000); return false; } String line; String strData; - bool pageReadSuccess = true; - strDump = ""; while (file.available()) { line = file.readStringUntil('\n'); strData = line.substring(line.indexOf(":") + 1); strData.trim(); - if(line.startsWith("Device type:")) printableUID.picc_type = strData; - if(line.startsWith("UID:")) printableUID.uid = strData; - if(line.startsWith("SAK:")) printableUID.sak = strData; - if(line.startsWith("ATQA:")) printableUID.atqa = strData; - if(line.startsWith("Pages read:")) pageReadSuccess = false; - if(line.startsWith("Page ")) strDump += strData; + if(line.startsWith("UID:")) printableLFUID = strData; } file.close(); delay(100); + parseLFUID(); - if (!pageReadSuccess) { - displayError("Incomplete dump file"); - delay(1000); - return false; + return true; +} + + +bool Chameleon::writeFileLF(String filename) { + FS *fs; + if(!getFsStorage(fs)) return false; + + if (!(*fs).exists("/BruceRFID")) (*fs).mkdir("/BruceRFID"); + if ((*fs).exists("/BruceRFID/" + filename + ".rfidlf")) { + int i = 1; + filename += "_"; + while((*fs).exists("/BruceRFID/" + filename + String(i) + ".rfidlf")) i++; + filename += String(i); } + File file = (*fs).open("/BruceRFID/"+ filename + ".rfidlf", FILE_WRITE); - printableUID.uid.trim(); - printableUID.uid.replace(" ", ""); - printableUID.sak.trim(); - printableUID.sak.replace(" ", ""); - printableUID.atqa.trim(); - printableUID.atqa.replace(" ", ""); - strDump.trim(); - strDump.replace(" ", ""); + if(!file) { + return false; + } - Serial.print("Uid: "); Serial.println(printableUID.uid); - Serial.print("Sak: "); Serial.println(printableUID.sak); - Serial.print("Data: "); Serial.println(strDump); - Serial.print("Data len: "); Serial.println(strDump.length()/2); + file.println("Filetype: Bruce RFID 125kHz File"); + file.println("Version 1"); + file.println("UID: " + printableLFUID); + file.close(); + delay(100); return true; } -bool Chameleon::getEmulationTagType() { - byte sak = strtoul(printableUID.sak.c_str(), NULL, 16); - int dataLen = strDump.length() / 2; +void Chameleon::formatLFUID() { + printableLFUID = ""; + for (byte i = 0; i < chmUltra.lfTagData.size; i++) { + printableLFUID += chmUltra.lfTagData.uidByte[i] < 0x10 ? " 0" : " "; + printableLFUID += String(chmUltra.lfTagData.uidByte[i], HEX); + } + printableLFUID.trim(); + printableLFUID.toUpperCase(); +} - switch (sak) { - case 0x09: // MIFARE_Mini - tagType = 1000; - break; - case 0x08: // MIFARE_1K - tagType = 1001; - break; +void Chameleon::parseLFUID() { + String strUID = printableLFUID; + strUID.trim(); + strUID.replace(" ", ""); - case 0x24: // MIFARE_4K - tagType = 1003; - break; + lfTagData.size = strUID.length() / 2; + for (size_t i = 0; i < strUID.length(); i += 2) { + lfTagData.uidByte[i / 2] = strtoul(strUID.substring(i, i + 2).c_str(), NULL, 16); + } +} - case 0x0: // MIFARE_UL - if (strDump.substring(0,8) == strDump.substring(strDump.length()-8)) { - strDump = strDump.substring(0,strDump.length()-8); - } - dataLen = strDump.length() / 2; - switch (dataLen) { - case 180: // NTAG_213 - tagType = 1100; - break; +// HF Methods - case 540: // NTAG_215 - tagType = 1101; - break; +void Chameleon::readHFTag() { + if (!chmUltra.cmd14aScan()) return; - case 924: // NTAG_216 - tagType = 1102; - break; + displayInfo("Reading data blocks..."); + if (chmUltra.hfTagData.sak == 0x00) chmUltra.cmdMfuVersion(); - default: - break; - } - break; + pageReadSuccess = readHFDataBlocks(); - default: - break; - } + formatHFData(); + hfTagData = chmUltra.hfTagData; + + displayBanner(); + dumpHFCardDetails(); - Serial.print("Tag type: "); Serial.println(tagType); - return tagType != 0; + _hf_read_uid = true; + delay(500); } -void Chameleon::selectEmulationSlot() { - options = { - {"1", [&]() { emulationSlot=1; }}, - {"2", [&]() { emulationSlot=2; }}, - {"3", [&]() { emulationSlot=3; }}, - {"4", [&]() { emulationSlot=4; }}, - {"5", [&]() { emulationSlot=5; }}, - {"6", [&]() { emulationSlot=6; }}, - {"7", [&]() { emulationSlot=7; }}, - {"8", [&]() { emulationSlot=8; }}, - }; +void Chameleon::scanHFTags() { + if (!chmUltra.cmd14aScan()) return; + + formatHFData(); + + if (_scanned_set.find(printableHFUID.uid) == _scanned_set.end()) { + Serial.println("New tag found: " + printableHFUID.uid); + _scanned_set.insert(printableHFUID.uid); + _scanned_tags.push_back(printableHFUID.uid); + } + + displayBanner(); + dumpHFScanResults(); + delay(200); - loopOptions(options,false,true,"Set Emulation Slot"); } -bool Chameleon::searchChameleonDevice() { - NimBLEDevice::init(""); +void Chameleon::cloneHFTag() { + if (!chmUltra.cmd14aScan()) return; - NimBLEScan* pScan = NimBLEDevice::getScan(); - pScan->setAdvertisedDeviceCallbacks(new scanCallbacks()); - pScan->setActiveScan(true); + if (chmUltra.hfTagData.sak != hfTagData.sak) { + displayError("Tag types do not match."); + delay(1000); + return; + } - BLEScanResults foundDevices = pScan->start(5); - bool chameleonFound = false; + if (chmUltra.cmdMfSetUid(hfTagData.uidByte, hfTagData.size)) { + displaySuccess("UID written successfully."); + } else { + displayError("Error writing UID to tag."); + } - for (int i=0; iclearResults(); + if (writeHFDataBlocks()) { + displaySuccess("Tag written successfully."); + } else { + displayError("Error writing data to tag."); + } - return chameleonFound; + delay(1000); + setMode(BATTERY_INFO_MODE); } -bool Chameleon::connectToChamelon() { - NimBLEClient *pClient = NimBLEDevice::createClient(); - bool chrFound = false; +void Chameleon::customHFUid() { + String custom_uid = keyboard("", 14, "UID (hex):"); - if (!pClient->connect(&chameleonDevice)) return false; + custom_uid.trim(); + custom_uid.replace(" ", ""); + custom_uid.toUpperCase(); - Serial.print("Connected to: "); - Serial.println(pClient->getPeerAddress().toString().c_str()); + displayBanner(); - std::vector * pSvcs = pClient->getServices(true); - Serial.print(pSvcs->size()); Serial.println(" services found"); + if (custom_uid.length() != 8 && custom_uid.length() != 14) { + displayError("Invalid UID"); + delay(1000); + return setMode(BATTERY_INFO_MODE); + } - for (NimBLERemoteService* pSvc : *pSvcs) { - Serial.print("Service ");Serial.println(pSvc->toString().c_str()); + printableHFUID.uid = ""; + for (size_t i = 0; i < custom_uid.length(); i += 2) { + printableHFUID.uid += custom_uid.substring(i, i + 2) + " "; + } + printableHFUID.uid.trim(); - std::vector * pChrs = pSvc->getCharacteristics(true); - Serial.print(pChrs->size()); Serial.println(" characteristics found"); + printableHFUID.sak = custom_uid.length() == 8 ? "08" : "00"; + printableHFUID.atqa = custom_uid.length() == 8 ? "0004" : "0044"; + pageReadSuccess = true; + parseHFData(); + printableHFUID.piccType = chmUltra.getTagTypeStr(hfTagData.sak); - if (pChrs->empty()) continue; + options = { + {"Clone UID", [=]() { setMode(HF_CLONE_MODE); }}, + {"Emulate", [=]() { setMode(HF_EMULATION_MODE); }}, + }; + delay(200); + loopOptions(options); +} - for (NimBLERemoteCharacteristic* pChr : *pChrs) { - if (pChr->canWrite() && pChr->getUUID().toString().length() > 30) { - Serial.print("Found Characteristic ");Serial.println(pChr->toString().c_str()); - writeChr = pChr; - chrFound = true; - } - if(pChr->canNotify() && pChr->getUUID().toString().length() > 30) { - pChr->subscribe(true, chameleonNotifyCB); - } - } +void Chameleon::emulateHF() { + if (!isMifareClassic(hfTagData.sak)) { + displayError("Not implemented for this tag type"); + delay(1000); + return setMode(BATTERY_INFO_MODE); + } + + String strDump = ""; + String strData = ""; + String line = ""; + int startIndex = 0; + int finalIndex; + + while(true) { + finalIndex = strAllPages.indexOf("\n", startIndex); + if (finalIndex == -1) finalIndex = strAllPages.length(); - if (chrFound) return true; + line = strAllPages.substring(startIndex, finalIndex); + if (line.length() < 5) break; + + strData = line.substring(line.indexOf(":") + 1); + strData.trim(); + strDump += strData; + + startIndex = finalIndex + 1; } + strDump.trim(); + strDump.replace(" ", ""); + + uint8_t slot = selectSlot(); + + ChameleonUltra::TagType tagType = chmUltra.getTagType(hfTagData.sak); - return false; + displayBanner(); + + if ( + chmUltra.cmdEnableSlot(slot, chmUltra.RFID_HF) + && chmUltra.cmdChangeActiveSlot(slot) + && chmUltra.cmdChangeSlotType(slot, tagType) + && chmUltra.cmdMfEload(strDump) + && chmUltra.cmdMfEconfig(hfTagData.uidByte, hfTagData.size, hfTagData.atqaByte, hfTagData.sak) + && chmUltra.cmdChangeMode(chmUltra.HW_MODE_EMULATOR) + ) { + displaySuccess("Emulation successful."); + } else { + displayError("Error emulating HF tag."); + } + + delay(1000); + setMode(BATTERY_INFO_MODE); } -bool Chameleon::chamelonServiceDiscovery() { - NimBLEClient *pClient = NimBLEDevice::createClient(); +void Chameleon::loadFileHF() { + displayBanner(); - if (!pClient->connect(&chameleonDevice)) return false; + if (readFileHF()) { + displaySuccess("File loaded"); + delay(1000); + _hf_read_uid = true; + + options = { + {"Clone UID", [=]() { setMode(HF_CLONE_MODE); }}, + {"Write Data", [=]() { setMode(HF_WRITE_MODE); }}, + // {"Write Data", [=]() { setMode(HF_WRITE_MODE); }}, + {"Emulate", [=]() { setMode(HF_EMULATION_MODE); }}, + }; + delay(200); + loopOptions(options); + } + else { + displayError("Error loading file"); + delay(1000); + setMode(BATTERY_INFO_MODE); + } +} - Serial.print("Connected to: "); - Serial.println(pClient->getPeerAddress().toString().c_str()); - std::vector * pSvcs = pClient->getServices(true); - Serial.print(pSvcs->size()); Serial.println(" services found"); +void Chameleon::saveFileHF() { + String uid_str = printableHFUID.uid; + uid_str.replace(" ", ""); + String filename = keyboard(uid_str, 30, "File name:"); - for (NimBLERemoteService* pSvc : *pSvcs) { - Serial.println(pSvc->toString().c_str()); + displayBanner(); - std::vector * pChrs = pSvc->getCharacteristics(true); - Serial.print(pChrs->size()); Serial.println(" characteristics found"); + if (writeFileHF(filename)) { + displaySuccess("File saved."); + } + else { + displayError("Error writing file."); + } + delay(1000); + setMode(BATTERY_INFO_MODE); +} - if (pChrs->empty()) continue; - for (NimBLERemoteCharacteristic* pChr : *pChrs) { - Serial.println(pChr->toString().c_str()); - Serial.print("UID size: ");Serial.println(pChr->getUUID().toString().length()); - Serial.print("Value? ");Serial.println(pChr->getValue()); - Serial.print("Can read? ");Serial.println(pChr->canRead()); - Serial.print("Can write? ");Serial.println(pChr->canWrite()); - Serial.print("Can write no response? ");Serial.println(pChr->canWriteNoResponse()); - Serial.print("Can notify? ");Serial.println(pChr->canNotify()); - Serial.print("Can indicate? ");Serial.println(pChr->canIndicate()); - Serial.print("Can broadcast? ");Serial.println(pChr->canBroadcast()); +bool Chameleon::readFileHF() { + String filepath; + File file; + FS *fs; + if(!getFsStorage(fs)) return false; + filepath = loopSD(*fs, true, "RFID|NFC"); + file = fs->open(filepath, FILE_READ); - std::vector * pDscs = pChr->getDescriptors(true); - Serial.print(pDscs->size()); Serial.println(" descriptors found"); - for (NimBLERemoteDescriptor* pDsc : *pDscs) { - Serial.println(pDsc->toString().c_str()); - } - } + if (!file) { + return false; + } + String line; + String strData; + strAllPages = ""; + pageReadSuccess = true; + + while (file.available()) { + line = file.readStringUntil('\n'); + strData = line.substring(line.indexOf(":") + 1); + strData.trim(); + if(line.startsWith("Device type:")) printableHFUID.piccType = strData; + if(line.startsWith("UID:")) printableHFUID.uid = strData; + if(line.startsWith("SAK:")) printableHFUID.sak = strData; + if(line.startsWith("ATQA:")) printableHFUID.atqa = strData; + if(line.startsWith("Pages total:")) dataPages = strData.toInt(); + if(line.startsWith("Pages read:")) pageReadSuccess = false; + if(line.startsWith("Page ")) strAllPages += line + "\n"; } + file.close(); + delay(100); + parseHFData(); + return true; } -bool Chameleon::sendCommands() { - return ( - cmdEnableSlotHF() - && cmdChangeActiveSlot() - && cmdChangeSlotType() +bool Chameleon::writeFileHF(String filename) { + FS *fs; + if(!getFsStorage(fs)) return false; + + if (!(*fs).exists("/BruceRFID")) (*fs).mkdir("/BruceRFID"); + if ((*fs).exists("/BruceRFID/" + filename + ".rfid")) { + int i = 1; + filename += "_"; + while((*fs).exists("/BruceRFID/" + filename + String(i) + ".rfid")) i++; + filename += String(i); + } + File file = (*fs).open("/BruceRFID/"+ filename + ".rfid", FILE_WRITE); - && cmdUploadDumpData() - && cmdSetEmulationConfig() + if(!file) { + return false; + } - && cmdSetEmulationMode() - && cmdChangeHFSlotNickName() - ); + file.println("Filetype: Bruce RFID File"); + file.println("Version 1"); + file.println("Device type: " + printableHFUID.piccType); + file.println("# UID, ATQA and SAK are common for all formats"); + file.println("UID: " + printableHFUID.uid); + file.println("SAK: " + printableHFUID.sak); + file.println("ATQA: " + printableHFUID.atqa); + file.println("# Memory dump"); + file.println("Pages total: " + String(dataPages)); + if (!pageReadSuccess) file.println("Pages read: " + String(dataPages)); + file.print(strAllPages); + + file.close(); + delay(100); + return true; } -bool Chameleon::cmdEnableSlotHF() { - Serial.printf("Enable HF on slot %d\n", emulationSlot); - uint8_t cmd[13] = { - 0x11, 0xef, 0x03, 0xee, 0x00, 0x00, 0x00, 0x03, 0x0c, 0x00, 0x02, 0x01, 0x00 - }; - cmd[9] = emulationSlot-1; - cmd[12] = calculateLRC(cmd+9, cmd[7]); +bool Chameleon::readHFDataBlocks() { + dataPages = 0; + totalPages = 0; + bool readSuccess = false; + strAllPages = ""; + + switch (chmUltra.hfTagData.sak) { + case 0x08: + case 0x09: + case 0x10: + case 0x11: + case 0x18: + case 0x19: + readSuccess = readMifareClassicDataBlocks({}); + break; + + case 0x00: + readSuccess = readMifareUltralightDataBlocks(); + break; + + default: + break; + } - return submitCommand(cmd, sizeof(cmd)); + return readSuccess; } -bool Chameleon::cmdChangeActiveSlot() { - Serial.printf("Change active slot to %d\n", emulationSlot); - uint8_t cmd[11] = { - 0x11, 0xef, 0x03, 0xeb, 0x00, 0x00, 0x00, 0x01, 0x11, 0x00, 0x00 - }; - cmd[9] = emulationSlot-1; - cmd[10] = calculateLRC(cmd+9, cmd[7]); +bool Chameleon::readMifareClassicDataBlocks(uint8_t *key) { + bool sectorReadSuccess; - return submitCommand(cmd, sizeof(cmd)); -} + switch (chmUltra.hfTagData.sak) { + case 0x09: + totalPages = 20; // 320 bytes / 16 bytes per page + break; + case 0x08: + totalPages = 64; // 1024 bytes / 16 bytes per page + break; -bool Chameleon::cmdChangeSlotType() { - Serial.printf("Change slot %d type\n", emulationSlot); - uint8_t cmd[13] = { - 0x11, 0xef, 0x03, 0xec, 0x00, 0x00, 0x00, 0x03, 0x0e, 0x00, 0x00, 0x00, 0x00 - }; - cmd[9] = emulationSlot-1; - cmd[10] = (tagType >> 8) & 0xFF; - cmd[11] = tagType & 0xFF; - cmd[12] = calculateLRC(cmd+9, cmd[7]); + case 0x18: + totalPages = 256; // 4096 bytes / 16 bytes per page + break; - return submitCommand(cmd, sizeof(cmd)); -} + case 0x19: + totalPages = 128; // 2048 bytes / 16 bytes per page + break; + + default: // Should not happen. Ignore. + break; + } + + String strPage; + for (byte i = 0; i < totalPages; i++) { + if (!chmUltra.cmdMfReadBlock(i, key)) return false; -bool Chameleon::cmdUploadDumpData() { - Serial.println("Upload dump data"); - uint8_t cmd[507] = {0x0}; + strPage = ""; + for (byte index = 0; index < chmUltra.cmdResponse.dataSize; index++) { + strPage += chmUltra.cmdResponse.data[index] < 0x10 ? F(" 0") : F(" "); + strPage += String(chmUltra.cmdResponse.data[index], HEX); + } + strPage.trim(); + strPage.toUpperCase(); - cmd[0] = 0x11; - cmd[1] = 0xef; - cmd[2] = 0x0f; - cmd[3] = 0x0a; - cmd[4] = 0x00; - cmd[5] = 0x00; + strAllPages += "Page " + String(dataPages) + ": " + strPage + "\n"; + dataPages++; + } - int index = 0; - int block = 0; - for (size_t i = 0; i < strDump.length(); i += 2) { - cmd[10 + index++] = strtoul(strDump.substring(i, i + 2).c_str(), NULL, 16); + return true; +} - if (index == MAX_DUMP_SIZE || i+2 == strDump.length()) { - cmd[6] = ((index+1) >> 8) & 0xFF; - cmd[7] = (index+1) & 0xFF; - cmd[8] = calculateLRC(cmd+2, 6); - cmd[9] = block; - cmd[10 + index] = calculateLRC(cmd+9, index+2); +bool Chameleon::readMifareUltralightDataBlocks() { + String strPage; - if (!submitCommand(cmd, 10+index+1)) return false; + ChameleonUltra::TagType tagType = chmUltra.getTagType(chmUltra.hfTagData.sak); - block += index / 16; - index = 0; + switch (tagType) { + case ChameleonUltra::NTAG_210: + case ChameleonUltra::MF0UL11: + totalPages = 20; + break; + case ChameleonUltra::NTAG_212: + case ChameleonUltra::MF0UL21: + totalPages = 41; + break; + case ChameleonUltra::NTAG_213: + totalPages = 45; + break; + case ChameleonUltra::NTAG_215: + totalPages = 135; + break; + case ChameleonUltra::NTAG_216: + totalPages = 231; + break; + default: + totalPages = 256; + break; + } + + for (byte i = 0; i < totalPages; i++) { + if (!chmUltra.cmdMfuReadPage(i)) return false; + if (chmUltra.cmdResponse.dataSize == 0) break; + + strPage = ""; + for (byte index = 0; index < chmUltra.cmdResponse.dataSize; index++) { + strPage += chmUltra.cmdResponse.data[index] < 0x10 ? F(" 0") : F(" "); + strPage += String(chmUltra.cmdResponse.data[index], HEX); } + strPage.trim(); + strPage.toUpperCase(); + + strAllPages += "Page " + String(dataPages) + ": " + strPage + "\n"; + dataPages++; } return true; } -bool Chameleon::cmdSetEmulationConfig() { - Serial.println("Set emulation config"); +bool Chameleon::writeHFDataBlocks() { + String pageLine = ""; + String strBytes = ""; + int lineBreakIndex; + int pageIndex; + bool blockWriteSuccess; + int totalSize = strAllPages.length(); - uint8_t cmd[22] = { - 0x11, 0xef, 0x0f, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - cmd[7] = 5 + (printableUID.uid.length() / 2); - cmd[8] = calculateLRC(cmd, 8); - cmd[9] = printableUID.uid.length() / 2; + while (strAllPages.length() > 0) { + lineBreakIndex = strAllPages.indexOf("\n"); + pageLine = strAllPages.substring(0, lineBreakIndex); + strAllPages = strAllPages.substring(lineBreakIndex + 1); - for (size_t i = 0; i < printableUID.uid.length(); i += 2) { - cmd[10 + (i / 2)] = strtoul(printableUID.uid.substring(i, i + 2).c_str(), NULL, 16); - } + pageIndex = pageLine.substring(5, pageLine.indexOf(":")).toInt(); + strBytes = pageLine.substring(pageLine.indexOf(":") + 1); + strBytes.trim(); + strBytes.replace(" ", ""); + + if (pageIndex == 0) continue; - int index = 10 + (printableUID.uid.length() / 2); - cmd[index++] = strtoul(printableUID.atqa.substring(2, 4).c_str(), NULL, 16); - cmd[index++] = strtoul(printableUID.atqa.substring(0, 2).c_str(), NULL, 16); - cmd[index++] = strtoul(printableUID.sak.c_str(), NULL, 16); - cmd[index++] = 0x00; // ats - cmd[index++] = calculateLRC(cmd+9, index-9); + byte size = strBytes.length() / 2; + byte buffer[size]; + for (size_t i = 0; i < strBytes.length(); i += 2) { + buffer[i / 2] = strtoul(strBytes.substring(i, i + 2).c_str(), NULL, 16); + } - return submitCommand(cmd, index); + blockWriteSuccess = false; + if (isMifareClassic(chmUltra.hfTagData.sak)) { + if (pageIndex == 0 || (pageIndex + 1) % 4 == 0) continue; // Data blocks for MIFARE Classic + blockWriteSuccess = chmUltra.cmdMfWriteBlock(pageIndex, {}, buffer, size); + } + else if (chmUltra.hfTagData.sak == 0x00) { + if (pageIndex < 4 || pageIndex >= dataPages-5) continue; // Data blocks for NTAG21X + blockWriteSuccess = chmUltra.cmdMfuWritePage(pageIndex, buffer, size); + } + + if (!blockWriteSuccess) return false; + + progressHandler(totalSize-strAllPages.length(), totalSize, "Writing data blocks..."); + } + + return true; } -bool Chameleon::cmdSetEmulationMode() { - Serial.println("Set emulation mode"); - uint8_t cmd[11] = { - 0x11, 0xef, 0x03, 0xe9, 0x00, 0x00, 0x00, 0x01, 0x13, 0x00, 0x00 - }; +void Chameleon::formatHFData() { + byte bcc = 0; - return submitCommand(cmd, sizeof(cmd)); + printableHFUID.piccType = chmUltra.getTagTypeStr(chmUltra.hfTagData.sak); + + printableHFUID.sak = chmUltra.hfTagData.sak < 0x10 ? "0" : ""; + printableHFUID.sak += String(chmUltra.hfTagData.sak, HEX); + printableHFUID.sak.toUpperCase(); + + // UID + printableHFUID.uid = ""; + for (byte i = 0; i < chmUltra.hfTagData.size; i++) { + printableHFUID.uid += chmUltra.hfTagData.uidByte[i] < 0x10 ? " 0" : " "; + printableHFUID.uid += String(chmUltra.hfTagData.uidByte[i], HEX); + bcc = bcc ^ chmUltra.hfTagData.uidByte[i]; + } + printableHFUID.uid.trim(); + printableHFUID.uid.toUpperCase(); + + // BCC + printableHFUID.bcc = bcc < 0x10 ? "0" : ""; + printableHFUID.bcc += String(bcc, HEX); + printableHFUID.bcc.toUpperCase(); + + // ATQA + printableHFUID.atqa = ""; + for (byte i = 0; i < 2; i++) { + printableHFUID.atqa += chmUltra.hfTagData.atqaByte[i] < 0x10 ? " 0" : " "; + printableHFUID.atqa += String(chmUltra.hfTagData.atqaByte[i], HEX); + } + printableHFUID.atqa.trim(); + printableHFUID.atqa.toUpperCase(); } -bool Chameleon::cmdChangeHFSlotNickName() { - Serial.println("Change slot nick name to Bruce"); - uint8_t cmd[17] = { - 0x11, 0xef, 0x03, 0xef, 0x00, 0x00, 0x00, 0x07, 0x07, - 0x00, 0x02, 0x42, 0x72, 0x75, 0x63, 0x65, 0x00 - }; - cmd[9] = emulationSlot-1; - cmd[16] = calculateLRC(cmd+9, cmd[7]); +void Chameleon::parseHFData() { + String strUID = printableHFUID.uid; + strUID.trim(); + strUID.replace(" ", ""); + hfTagData.size = strUID.length() / 2; + for (size_t i = 0; i < strUID.length(); i += 2) { + hfTagData.uidByte[i / 2] = strtoul(strUID.substring(i, i + 2).c_str(), NULL, 16); + } + + printableHFUID.sak.trim(); + hfTagData.sak = strtoul(printableHFUID.sak.c_str(), NULL, 16); - return submitCommand(cmd, sizeof(cmd)); + String strATQA = printableHFUID.atqa; + strATQA.trim(); + strATQA.replace(" ", ""); + for (size_t i = 0; i < strATQA.length(); i += 2) { + hfTagData.atqaByte[i / 2] = strtoul(strATQA.substring(i, i + 2).c_str(), NULL, 16); + } } -bool Chameleon::submitCommand(uint8_t *data, size_t length) { - Serial.print("Cmd:"); - for (int i=0; iwriteValue(data, length, true); + if(!file) { + return; + } - delay(CMD_DELAY); + file.println("Filetype: Bruce RFID Scan Result"); + for (String uid : _scanned_tags) { + file.println(uid); + } - return res; + file.close(); + delay(100); + return; } diff --git a/src/modules/rfid/chameleon.h b/src/modules/rfid/chameleon.h index bc43140a..ec6fcd16 100644 --- a/src/modules/rfid/chameleon.h +++ b/src/modules/rfid/chameleon.h @@ -11,6 +11,8 @@ #define __CHAMELEON_H__ #include +#include +#include class Chameleon { @@ -20,9 +22,34 @@ class Chameleon { String bcc; String sak; String atqa; - String picc_type; + String piccType; } PrintableUID; + enum AppMode { + BATTERY_INFO_MODE, + FACTORY_RESET_MODE, + + LF_READ_MODE, + LF_CLONE_MODE, + LF_EMULATION_MODE, + LF_SAVE_MODE, + LF_LOAD_MODE, + LF_CUSTOM_UID_MODE, + + HF_READ_MODE, + HF_SCAN_MODE, + HF_EMULATION_MODE, + HF_SAVE_MODE, + HF_LOAD_MODE, + HF_CLONE_MODE, + HF_WRITE_MODE, + HF_CUSTOM_UID_MODE, + + // WRITE_NDEF_MODE, + // ERASE_MODE, + }; + + ///////////////////////////////////////////////////////////////////////////////////// // Constructor ///////////////////////////////////////////////////////////////////////////////////// @@ -33,36 +60,86 @@ class Chameleon { // Life Cycle ///////////////////////////////////////////////////////////////////////////////////// void setup(); + void loop(); + bool connect(); private: - NimBLERemoteCharacteristic* writeChr; - NimBLEAdvertisedDevice chameleonDevice; - PrintableUID printableUID; + ChameleonUltra chmUltra = ChameleonUltra(true); + ChameleonUltra::LfTag lfTagData; + ChameleonUltra::HfTag hfTagData; + AppMode currentMode; + PrintableUID printableHFUID; + String printableLFUID; + String dumpFilename = ""; String strDump = ""; - int emulationSlot; - int tagType = 0; + bool _lf_read_uid = false; + bool _hf_read_uid = false; + bool _battery_set = false; + bool pageReadSuccess = false; + String strAllPages = ""; + int totalPages = 0; + int dataPages = 0; + std::set _scanned_set; + std::vector _scanned_tags; + + ///////////////////////////////////////////////////////////////////////////////////// + // State management + ///////////////////////////////////////////////////////////////////////////////////// + void selectMode(); + void setMode(AppMode mode); + + ///////////////////////////////////////////////////////////////////////////////////// + // Display functions + ///////////////////////////////////////////////////////////////////////////////////// void displayBanner(); - void displayDumpInfo(); - - bool openDumpFile(); - bool getEmulationTagType(); - void selectEmulationSlot(); - - bool searchChameleonDevice(); - bool connectToChamelon(); - bool chamelonServiceDiscovery(); - - bool sendCommands(); - bool submitCommand(uint8_t *data, size_t length); - - bool cmdEnableSlotHF(); - bool cmdChangeActiveSlot(); - bool cmdChangeSlotType(); - bool cmdUploadDumpData(); - bool cmdSetEmulationConfig(); - bool cmdSetEmulationMode(); - bool cmdChangeHFSlotNickName(); + void dumpHFCardDetails(); + void dumpHFScanResults(); + + ///////////////////////////////////////////////////////////////////////////////////// + // Operations + ///////////////////////////////////////////////////////////////////////////////////// + void readLFTag(); + void cloneLFTag(); + void customLFUid(); + void emulateLF(); + void saveFileLF(); + void loadFileLF(); + + void readHFTag(); + void scanHFTags(); + void cloneHFTag(); + void writeHFData(); + void customHFUid(); + void emulateHF(); + void saveFileHF(); + void loadFileHF(); + + void getBatteryInfo(); + void factoryReset(); + // void erase_card(); + // void write_ndef_data(); + + ///////////////////////////////////////////////////////////////////////////////////// + // Helpers + ///////////////////////////////////////////////////////////////////////////////////// + void formatLFUID(); + void parseLFUID(); + bool writeFileLF(String filename); + bool readFileLF(); + + void formatHFData(); + void parseHFData(); + bool writeFileHF(String filename); + bool readFileHF(); + bool readHFDataBlocks(); + bool readMifareClassicDataBlocks(uint8_t *key); + bool readMifareUltralightDataBlocks(); + bool writeHFDataBlocks(); + void saveHFScanResult(); + + uint8_t selectSlot(); + bool isMifareClassic(byte sak); }; diff --git a/src/modules/rfid/rfid125.cpp b/src/modules/rfid/rfid125.cpp index a536aad0..d5503503 100644 --- a/src/modules/rfid/rfid125.cpp +++ b/src/modules/rfid/rfid125.cpp @@ -250,13 +250,13 @@ bool RFID125::write_file(String filename) { if(!getFsStorage(fs)) return false; if (!(*fs).exists("/BruceRFID")) (*fs).mkdir("/BruceRFID"); - if ((*fs).exists("/BruceRFID/" + filename + ".lfrfid")) { + if ((*fs).exists("/BruceRFID/" + filename + ".rfidlf")) { int i = 1; filename += "_"; - while((*fs).exists("/BruceRFID/" + filename + String(i) + ".lfrfid")) i++; + while((*fs).exists("/BruceRFID/" + filename + String(i) + ".rfidlf")) i++; filename += String(i); } - File file = (*fs).open("/BruceRFID/"+ filename + ".lfrfid", FILE_WRITE); + File file = (*fs).open("/BruceRFID/"+ filename + ".rfidlf", FILE_WRITE); if(!file) { return false;