Merge pull request #2697 from piotrva/move-keys-library-to-spiffs

Move keys library to spiffs
This commit is contained in:
Iceman 2024-12-28 19:21:35 +01:00 committed by GitHub
commit 2c72db9b54
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 173 additions and 243 deletions

View file

@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file.
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
## [unreleased][unreleased]
- Changed flash-stored key dictionaries (Mifare, iClass, T55XX) and T55XX configurations to SPIFFS files (@piotrva)
- Changed `lf em 410x sim` to use default gap value of 0 and extended help (@piotrva)
- Changed `hf 14a info` - now identifies MIAFRE Duox (@iceman1001)
- Added `hf iclass trbl` to perform tear-off attacks on iClass (@antiklesys)

View file

@ -440,7 +440,41 @@ static void SendStatus(uint32_t wait) {
ModInfo();
#ifdef WITH_FLASH
Flashmem_print_info();
DbpString(_CYAN_("Flash memory dictionary loaded"));
uint32_t num = 0;
if (exists_in_spiffs(MF_KEYS_FILE)) {
num = size_in_spiffs(MF_KEYS_FILE) / MF_KEY_LENGTH;
} else {
num = 0;
}
if (num > 0) {
Dbprintf(" Mifare.................. "_YELLOW_("%u")" keys (spiffs: "_GREEN_("%s")")", num, MF_KEYS_FILE);
} else {
Dbprintf(" Mifare.................. "_RED_("%u")" keys (spiffs: "_RED_("%s")")", num, MF_KEYS_FILE);
}
if (exists_in_spiffs(T55XX_KEYS_FILE)) {
num = size_in_spiffs(T55XX_KEYS_FILE) / T55XX_KEY_LENGTH;
} else {
num = 0;
}
if (num > 0) {
Dbprintf(" T55xx................... "_YELLOW_("%u")" keys (spiffs: "_GREEN_("%s")")", num, T55XX_KEYS_FILE);
} else {
Dbprintf(" T55xx................... "_RED_("%u")" keys (spiffs: "_RED_("%s")")", num, T55XX_KEYS_FILE);
}
if (exists_in_spiffs(ICLASS_KEYS_FILE)) {
num = size_in_spiffs(ICLASS_KEYS_FILE) / ICLASS_KEY_LENGTH;
} else {
num = 0;
}
if (num > 0) {
Dbprintf(" iClass.................. "_YELLOW_("%u")" keys (spiffs: "_GREEN_("%s")")", num, ICLASS_KEYS_FILE);
} else {
Dbprintf(" iClass.................. "_RED_("%u")" keys (spiffs: "_RED_("%s")")", num, ICLASS_KEYS_FILE);
}
#endif
DbpString("");
reply_ng(CMD_STATUS, PM3_SUCCESS, NULL, 0);
@ -2748,34 +2782,7 @@ static void PacketReceived(PacketCommandNG *packet) {
break;
}
if (payload->startidx == DEFAULT_T55XX_KEYS_OFFSET_P(spi_flash_pages64k)) {
Flash_CheckBusy(BUSY_TIMEOUT);
Flash_WriteEnable();
Flash_Erase4k(spi_flash_pages64k - 1, 0xC);
} else if (payload->startidx == DEFAULT_MF_KEYS_OFFSET_P(spi_flash_pages64k)) {
Flash_CheckBusy(BUSY_TIMEOUT);
Flash_WriteEnable();
Flash_Erase4k(spi_flash_pages64k - 1, 0x5);
Flash_CheckBusy(BUSY_TIMEOUT);
Flash_WriteEnable();
Flash_Erase4k(spi_flash_pages64k - 1, 0x6);
Flash_CheckBusy(BUSY_TIMEOUT);
Flash_WriteEnable();
Flash_Erase4k(spi_flash_pages64k - 1, 0x7);
Flash_CheckBusy(BUSY_TIMEOUT);
Flash_WriteEnable();
Flash_Erase4k(spi_flash_pages64k - 1, 0x8);
Flash_CheckBusy(BUSY_TIMEOUT);
Flash_WriteEnable();
Flash_Erase4k(spi_flash_pages64k - 1, 0x9);
Flash_CheckBusy(BUSY_TIMEOUT);
Flash_WriteEnable();
Flash_Erase4k(spi_flash_pages64k - 1, 0xA);
} else if (payload->startidx == DEFAULT_ICLASS_KEYS_OFFSET_P(spi_flash_pages64k)) {
Flash_CheckBusy(BUSY_TIMEOUT);
Flash_WriteEnable();
Flash_Erase4k(spi_flash_pages64k - 1, 0xB);
} else if (payload->startidx == FLASH_MEM_SIGNATURE_OFFSET_P(spi_flash_pages64k)) {
if (payload->startidx == FLASH_MEM_SIGNATURE_OFFSET_P(spi_flash_pages64k)) {
Flash_CheckBusy(BUSY_TIMEOUT);
Flash_WriteEnable();
Flash_Erase4k(spi_flash_pages64k - 1, 0xF);

View file

@ -37,6 +37,7 @@
#include "protocols.h"
#include "pmflash.h"
#include "flashmem.h" // persistence on flash
#include "spiffs.h" // spiffs
#include "appmain.h" // print stack
/*
@ -324,31 +325,7 @@ void setT55xxConfig(uint8_t arg0, const t55xx_configurations_t *c) {
return;
}
if (!FlashInit()) {
BigBuf_free();
return;
}
uint8_t *buf = BigBuf_malloc(T55XX_CONFIG_LEN);
Flash_CheckBusy(BUSY_TIMEOUT);
uint16_t res = Flash_ReadDataCont(T55XX_CONFIG_OFFSET, buf, T55XX_CONFIG_LEN);
if (res == 0) {
FlashStop();
BigBuf_free();
return;
}
memcpy(buf, &T55xx_Timing, T55XX_CONFIG_LEN);
// delete old configuration
Flash_CheckBusy(BUSY_TIMEOUT);
Flash_WriteEnable();
Flash_Erase4k(3, 0xD);
// write new
res = Flash_Write(T55XX_CONFIG_OFFSET, buf, T55XX_CONFIG_LEN);
if (res == T55XX_CONFIG_LEN && g_dbglevel > 1) {
if (SPIFFS_OK == rdv40_spiffs_write(T55XX_CONFIG_FILE, (uint8_t*)&T55xx_Timing, T55XX_CONFIG_LEN, RDV40_SPIFFS_SAFETY_SAFE)) {
DbpString("T55XX Config save " _GREEN_("success"));
}
@ -363,15 +340,23 @@ t55xx_configurations_t *getT55xxConfig(void) {
void loadT55xxConfig(void) {
#ifdef WITH_FLASH
if (!FlashInit()) {
uint8_t *buf = BigBuf_malloc(T55XX_CONFIG_LEN);
uint32_t size = 0;
if (exists_in_spiffs(T55XX_CONFIG_FILE)) {
size = size_in_spiffs(T55XX_CONFIG_FILE);
}
if (size == 0) {
Dbprintf("Spiffs file: %s does not exists or empty.", T55XX_CONFIG_FILE);
BigBuf_free();
return;
}
uint8_t *buf = BigBuf_malloc(T55XX_CONFIG_LEN);
Flash_CheckBusy(BUSY_TIMEOUT);
uint16_t isok = Flash_ReadDataCont(T55XX_CONFIG_OFFSET, buf, T55XX_CONFIG_LEN);
FlashStop();
if (SPIFFS_OK != rdv40_spiffs_read(T55XX_CONFIG_FILE, buf, T55XX_CONFIG_LEN, RDV40_SPIFFS_SAFETY_SAFE)) {
Dbprintf("Spiffs file: %s cannot be read.", T55XX_CONFIG_FILE);
BigBuf_free();
return;
}
// verify read mem is actual data.
uint8_t cntA = T55XX_CONFIG_LEN, cntB = T55XX_CONFIG_LEN;
@ -380,6 +365,7 @@ void loadT55xxConfig(void) {
if (buf[i] == 0x00) cntB--;
}
if (!cntA || !cntB) {
Dbprintf("Spiffs file: %s does not malformed or empty.", T55XX_CONFIG_FILE);
BigBuf_free();
return;
}
@ -387,7 +373,7 @@ void loadT55xxConfig(void) {
if (buf[0] != 0xFF) // if not set for clear
memcpy((uint8_t *)&T55xx_Timing, buf, T55XX_CONFIG_LEN);
if (isok == T55XX_CONFIG_LEN) {
if (size == T55XX_CONFIG_LEN) {
if (g_dbglevel > 1) DbpString("T55XX Config load success");
}
@ -2146,29 +2132,34 @@ void T55xx_ChkPwds(uint8_t flags, bool ledcontrol) {
#ifdef WITH_FLASH
BigBuf_Clear_EM();
uint16_t isok = 0;
uint8_t counter[2] = {0x00, 0x00};
isok = Flash_ReadData(DEFAULT_T55XX_KEYS_OFFSET_P(spi_flash_pages64k), counter, sizeof(counter));
if (isok != sizeof(counter))
goto OUT;
uint32_t size = 0;
pwd_count = (uint16_t)(counter[1] << 8 | counter[0]);
if (exists_in_spiffs(T55XX_KEYS_FILE)) {
size = size_in_spiffs(T55XX_KEYS_FILE);
}
if (size == 0) {
Dbprintf("Spiffs file: %s does not exists or empty.", T55XX_KEYS_FILE);
goto OUT;
}
pwd_count = size / T55XX_KEY_LENGTH;
if (pwd_count == 0)
goto OUT;
// since flash can report way too many pwds, we need to limit it.
// bigbuff EM size is determined by CARD_MEMORY_SIZE
// a password is 4bytes.
uint16_t pwd_size_available = MIN(CARD_MEMORY_SIZE, pwd_count * 4);
uint16_t pwd_size_available = MIN(CARD_MEMORY_SIZE, pwd_count * T55XX_KEY_LENGTH);
// adjust available pwd_count
pwd_count = pwd_size_available / 4;
pwd_count = pwd_size_available / T55XX_KEY_LENGTH;
isok = Flash_ReadData(DEFAULT_T55XX_KEYS_OFFSET_P(spi_flash_pages64k) + 2, pwds, pwd_size_available);
if (isok != pwd_size_available)
if (SPIFFS_OK == rdv40_spiffs_read_as_filetype(T55XX_KEYS_FILE, pwds, pwd_size_available, RDV40_SPIFFS_SAFETY_SAFE)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Loaded %u passwords from spiffs file: %s", pwd_count, T55XX_KEYS_FILE);
} else {
Dbprintf("Spiffs file: %s cannot be read.", T55XX_KEYS_FILE);
goto OUT;
Dbprintf("Password dictionary count " _YELLOW_("%d"), pwd_count);
}
#endif

View file

@ -1900,31 +1900,36 @@ void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *da
#ifdef WITH_FLASH
if (use_flashmem) {
BigBuf_free();
uint16_t isok = 0;
uint8_t size[2] = {0x00, 0x00};
isok = Flash_ReadData(DEFAULT_MF_KEYS_OFFSET_P(spi_flash_pages64k), size, 2);
if (isok != 2)
uint32_t size = 0;
if (exists_in_spiffs(MF_KEYS_FILE)) {
size = size_in_spiffs(MF_KEYS_FILE);
}
if (size == 0) {
Dbprintf("Spiffs file: %s does not exists or empty.", MF_KEYS_FILE);
goto OUT;
}
keyCount = size[1] << 8 | size[0];
keyCount = size / MF_KEY_LENGTH;
if (keyCount == 0)
goto OUT;
// limit size of available for keys in bigbuff
// a key is 6bytes
uint16_t key_mem_available = MIN(BigBuf_get_size(), keyCount * 6);
uint16_t key_mem_available = MIN(BigBuf_get_size(), keyCount * MF_KEY_LENGTH);
keyCount = key_mem_available / 6;
keyCount = key_mem_available / MF_KEY_LENGTH;
datain = BigBuf_malloc(key_mem_available);
if (datain == NULL)
goto OUT;
isok = Flash_ReadData(DEFAULT_MF_KEYS_OFFSET_P(spi_flash_pages64k) + 2, datain, key_mem_available);
if (isok != key_mem_available)
if (SPIFFS_OK == rdv40_spiffs_read_as_filetype(MF_KEYS_FILE, datain, keyCount * MF_KEY_LENGTH, RDV40_SPIFFS_SAFETY_SAFE)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Loaded %u keys from spiffs file: %s", keyCount, MF_KEYS_FILE);
} else {
Dbprintf("Spiffs file: %s cannot be read.", MF_KEYS_FILE);
goto OUT;
}
}
#endif

View file

@ -17,6 +17,7 @@
//-----------------------------------------------------------------------------
#include "cmdflashmem.h"
#include <ctype.h>
#include <string.h>
#include "cmdparser.h" // command_t
#include "cliparser.h"
#include "pmflash.h" // rdv40validation_t
@ -192,7 +193,7 @@ static int CmdFlashMemLoad(const char *Cmd) {
CLIParserInit(&ctx, "mem load",
"Loads binary file into flash memory on device\n"
"Warning: mem area to be written must have been wiped first\n"
"( this is already taken care when loading dictionaries )",
"( dictionaries are serviced as files in spiffs so no wipe is needed )",
"mem load -f myfile -> upload file myfile values at default offset 0\n"
"mem load -f myfile -o 1024 -> upload file myfile values at offset 1024\n"
"mem load -f mfc_default_keys -m -> upload MFC keys\n"
@ -217,6 +218,7 @@ static int CmdFlashMemLoad(const char *Cmd) {
bool is_t55xx = arg_get_lit(ctx, 4);
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
char spiffsDest[32] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
CLIParserFree(ctx);
@ -246,57 +248,46 @@ static int CmdFlashMemLoad(const char *Cmd) {
switch (d) {
case DICTIONARY_MIFARE:
offset = DEFAULT_MF_KEYS_OFFSET_P(spi_flash_pages);
keylen = 6;
res = loadFileDICTIONARY(filename, data + 2, &datalen, keylen, &keycount);
keylen = MF_KEY_LENGTH;
res = loadFileDICTIONARY(filename, data, &datalen, keylen, &keycount);
if (res || !keycount) {
free(data);
return PM3_EFILE;
}
// limited space on flash mem
if (keycount > DEFAULT_MF_KEYS_MAX) {
keycount = DEFAULT_MF_KEYS_MAX;
datalen = keycount * keylen;
if (datalen > FLASH_MEM_MAX_SIZE_P(spi_flash_pages)) {
PrintAndLogEx(ERR, "error, filesize is larger than available memory");
free(data);
return PM3_EOVFLOW;
}
data[0] = (keycount >> 0) & 0xFF;
data[1] = (keycount >> 8) & 0xFF;
datalen += 2;
strcpy(spiffsDest, MF_KEYS_FILE);
break;
case DICTIONARY_T55XX:
offset = DEFAULT_T55XX_KEYS_OFFSET_P(spi_flash_pages);
keylen = 4;
res = loadFileDICTIONARY(filename, data + 2, &datalen, keylen, &keycount);
keylen = T55XX_KEY_LENGTH;
res = loadFileDICTIONARY(filename, data, &datalen, keylen, &keycount);
if (res || !keycount) {
free(data);
return PM3_EFILE;
}
// limited space on flash mem
if (keycount > DEFAULT_T55XX_KEYS_MAX) {
keycount = DEFAULT_T55XX_KEYS_MAX;
datalen = keycount * keylen;
if (datalen > FLASH_MEM_MAX_SIZE_P(spi_flash_pages)) {
PrintAndLogEx(ERR, "error, filesize is larger than available memory");
free(data);
return PM3_EOVFLOW;
}
data[0] = (keycount >> 0) & 0xFF;
data[1] = (keycount >> 8) & 0xFF;
datalen += 2;
strcpy(spiffsDest, T55XX_KEYS_FILE);
break;
case DICTIONARY_ICLASS:
offset = DEFAULT_ICLASS_KEYS_OFFSET_P(spi_flash_pages);
res = loadFileDICTIONARY(filename, data + 2, &datalen, keylen, &keycount);
keylen = ICLASS_KEY_LENGTH;
res = loadFileDICTIONARY(filename, data, &datalen, keylen, &keycount);
if (res || !keycount) {
free(data);
return PM3_EFILE;
}
// limited space on flash mem
if (keycount > DEFAULT_ICLASS_KEYS_MAX) {
keycount = DEFAULT_ICLASS_KEYS_MAX;
datalen = keycount * keylen;
if (datalen > FLASH_MEM_MAX_SIZE_P(spi_flash_pages)) {
PrintAndLogEx(ERR, "error, filesize is larger than available memory");
free(data);
return PM3_EOVFLOW;
}
data[0] = (keycount >> 0) & 0xFF;
data[1] = (keycount >> 8) & 0xFF;
datalen += 2;
strcpy(spiffsDest, ICLASS_KEYS_FILE);
break;
case DICTIONARY_NONE:
res = loadFile_safe(filename, ".bin", (void **)&data, &datalen);
@ -326,7 +317,18 @@ static int CmdFlashMemLoad(const char *Cmd) {
uint32_t bytes_sent = 0;
uint32_t bytes_remaining = datalen;
// we will treat dictionary files as spiffs files, so we need to handle this here
if (d != DICTIONARY_NONE) {
res = flashmem_spiffs_load(spiffsDest, data, datalen);
if (res != PM3_SUCCESS) {
PrintAndLogEx(FAILED, "Failed writing passwrods to file %s", spiffsDest);
free(data);
return res;
}
PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%u")" passwords to file "_GREEN_("%s"), keycount, spiffsDest);
SendCommandNG(CMD_SPIFFS_UNMOUNT, NULL, 0);
SendCommandNG(CMD_SPIFFS_MOUNT, NULL, 0);
} else {
// fast push mode
g_conn.block_after_ACK = true;
@ -362,8 +364,10 @@ static int CmdFlashMemLoad(const char *Cmd) {
}
g_conn.block_after_ACK = false;
free(data);
PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%zu")" bytes to offset "_GREEN_("%u"), datalen, offset);
}
free(data);
return PM3_SUCCESS;
}

View file

@ -273,7 +273,9 @@ int mf_check_keys_fast_ex(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastCh
// max timeout for one chunk of 85keys, 60*3sec = 180seconds
// s70 with 40*2 keys to check, 80*85 = 6800 auth.
// takes about 97s, still some margin before abort
if (timeout > 180) {
// timeout = 180 => ~360s @ Mifare Classic 1k @ ~2300 keys in dict
// ~2300 keys @ Mifare Classic 1k => ~620s
if (timeout > 60*12) {
PrintAndLogEx(WARNING, "\nNo response from Proxmark3. Aborting...");
return PM3_ETIMEOUT;
}

View file

@ -383,43 +383,6 @@ void Flashmem_print_status(void) {
FlashStop();
}
void Flashmem_print_info(void) {
if (!FlashInit()) return;
DbpString(_CYAN_("Flash memory dictionary loaded"));
// load dictionary offsets.
uint8_t keysum[2];
uint16_t num;
Flash_CheckBusy(BUSY_TIMEOUT);
uint16_t isok = Flash_ReadDataCont(DEFAULT_MF_KEYS_OFFSET_P(spi_flash_pages64k), keysum, 2);
if (isok == 2) {
num = ((keysum[1] << 8) | keysum[0]);
if (num != 0xFFFF && num != 0x0)
Dbprintf(" Mifare.................. "_YELLOW_("%u")" / "_GREEN_("%u")" keys", num, DEFAULT_MF_KEYS_MAX);
}
Flash_CheckBusy(BUSY_TIMEOUT);
isok = Flash_ReadDataCont(DEFAULT_T55XX_KEYS_OFFSET_P(spi_flash_pages64k), keysum, 2);
if (isok == 2) {
num = ((keysum[1] << 8) | keysum[0]);
if (num != 0xFFFF && num != 0x0)
Dbprintf(" T55x7................... "_YELLOW_("%u")" / "_GREEN_("%u")" keys", num, DEFAULT_T55XX_KEYS_MAX);
}
Flash_CheckBusy(BUSY_TIMEOUT);
isok = Flash_ReadDataCont(DEFAULT_ICLASS_KEYS_OFFSET_P(spi_flash_pages64k), keysum, 2);
if (isok == 2) {
num = ((keysum[1] << 8) | keysum[0]);
if (num != 0xFFFF && num != 0x0)
Dbprintf(" iClass.................. "_YELLOW_("%u")" / "_GREEN_("%u")" keys", num, DEFAULT_ICLASS_KEYS_MAX);
}
FlashStop();
}
bool FlashDetect(void) {
flash_device_type_t flash_data = {0};
bool ret = false;

View file

@ -134,7 +134,6 @@ uint16_t Flash_Write(uint32_t address, uint8_t *in, uint16_t len);
uint16_t Flash_WriteData(uint32_t address, uint8_t *in, uint16_t len);
uint16_t Flash_WriteDataCont(uint32_t address, uint8_t *in, uint16_t len);
void Flashmem_print_status(void);
void Flashmem_print_info(void);
typedef struct {
uint8_t manufacturer_id;

View file

@ -37,20 +37,20 @@ Therefore a flash address can be interpreted as such:
Page 0:
* available for user data
* to dump it: `mem dump -f page0_dump -o 0 -l 65536`
* to erase it: `mem wipe p 0`
* to erase it: `mem wipe -p 0`
Page 1:
* available for user data
* to dump it: `mem dump -f page1_dump -o 65536 -l 65536`
* to erase it: `mem wipe p 1`
* to erase it: `mem wipe -p 1`
Page 2:
* available for user data
* to dump it: `mem dump -f page2_dump -o 131072 -l 65536`
* to erase it: `mem wipe p 2`
* to erase it: `mem wipe -p 2`
Page 3:
* used by Proxmark3 RDV4 specific functions: flash signature and keys dictionaries, see below for details
* used by Proxmark3 RDV4 specific functions: flash signature, see below for details
* to dump it: `mem dump -f page3_dump -o 196608 -l 65536`
* to erase it:
* **Beware** it will erase your flash signature so better to back it up first as you won't be able to regenerate it by yourself!
@ -62,22 +62,6 @@ Page 3:
Page3 is used as follows by the Proxmark3 RDV4 firmware:
* **MF_KEYS**
* offset: page 3 sector 5 (0x5) @ 3*0x10000+5*0x1000=0x35000
* length: 6 sectors
* **ICLASS_KEYS**
* offset: page 3 sector 11 (0xB) @ 3*0x10000+11*0x1000=0x3B000
* length: 1 sector
* **T55XX_KEYS**
* offset: page 3 sector 12 (0xC) @ 3*0x10000+12*0x1000=0x3C000
* length: 1 sector
* **T55XX_CONFIG**
* offset: page 3 sector 13 (0xD) @ 3*0x10000+13*0x1000=0x3D000
* length: 1 sector (actually only a few bytes are used to store `t55xx_config` structure)
* **RSA SIGNATURE**, see below for details
* offset: page 3 sector 15 (0xF) offset 0xF7F @ 3*0x10000+15*0x1000+0xF7F=0x3FF7F (decimal 262015)
* length: 128 bytes

View file

@ -26,9 +26,6 @@
//
// 0x3F000 - 1 4kb sector = signature
// 0x3E000 - 1 4kb sector = settings
// 0x3D000 - 1 4kb sector = default T55XX keys dictionary
// 0x3B000 - 1 4kb sector = default ICLASS keys dictionary
// 0x35000 - 6 4kb sectors = default MFC keys dictionary
//
#ifndef FLASH_MEM_BLOCK_SIZE
# define FLASH_MEM_BLOCK_SIZE 256
@ -68,42 +65,19 @@
# define T55XX_CONFIG_LEN sizeof( t55xx_configurations_t )
#endif
#ifndef T55XX_CONFIG_OFFSET
# define T55XX_CONFIG_OFFSET (FLASH_MEM_MAX_4K_SECTOR - 0x2000)
#endif
#ifndef T55XX_CONFIG_OFFSET_P
# define T55XX_CONFIG_OFFSET_P(p64k) (FLASH_MEM_MAX_4K_SECTOR_P(p64k) - 0x2000)
#endif
#define T55XX_CONFIG_FILE "cfg_t55xx.bin"
// Reserved space for T55XX PWD = 4 kb
#ifndef DEFAULT_T55XX_KEYS_OFFSET
# define DEFAULT_T55XX_KEYS_LEN (0x1000)
# define DEFAULT_T55XX_KEYS_OFFSET (T55XX_CONFIG_OFFSET - DEFAULT_T55XX_KEYS_LEN)
# define DEFAULT_T55XX_KEYS_MAX ((DEFAULT_T55XX_KEYS_LEN - 2) / 4)
#endif
#ifndef DEFAULT_T55XX_KEYS_OFFSET_P
# define DEFAULT_T55XX_KEYS_OFFSET_P(p64k) (T55XX_CONFIG_OFFSET_P(p64k) - DEFAULT_T55XX_KEYS_LEN)
#endif
// T55XX PWD stored in spiffs
#define T55XX_KEYS_FILE "dict_t55xx.bin"
#define T55XX_KEY_LENGTH 4
// Reserved space for iClass keys = 4 kb
#ifndef DEFAULT_ICLASS_KEYS_OFFSET
# define DEFAULT_ICLASS_KEYS_LEN (0x1000)
# define DEFAULT_ICLASS_KEYS_OFFSET (DEFAULT_T55XX_KEYS_OFFSET - DEFAULT_ICLASS_KEYS_LEN)
# define DEFAULT_ICLASS_KEYS_MAX ((DEFAULT_ICLASS_KEYS_LEN - 2) / 8)
#endif
#ifndef DEFAULT_ICLASS_KEYS_OFFSET_P
# define DEFAULT_ICLASS_KEYS_OFFSET_P(p64k) (DEFAULT_T55XX_KEYS_OFFSET_P(p64k) - DEFAULT_ICLASS_KEYS_LEN)
#endif
// iClass keys stored in spiffs
#define ICLASS_KEYS_FILE "dict_iclass.bin"
#define ICLASS_KEY_LENGTH 8
// Reserved space for MIFARE Keys = 24 kb
#ifndef DEFAULT_MF_KEYS_OFFSET
# define DEFAULT_MF_KEYS_LEN (0x6000)
# define DEFAULT_MF_KEYS_OFFSET (DEFAULT_ICLASS_KEYS_OFFSET - DEFAULT_MF_KEYS_LEN)
# define DEFAULT_MF_KEYS_MAX ((DEFAULT_MF_KEYS_LEN - 2) / 6)
#endif
#ifndef DEFAULT_MF_KEYS_OFFSET_P
# define DEFAULT_MF_KEYS_OFFSET_P(p64k) (DEFAULT_ICLASS_KEYS_OFFSET_P(p64k) - DEFAULT_MF_KEYS_LEN)
#endif
// Mifare keys stored in spiffs
#define MF_KEYS_FILE "dict_mf.bin"
#define MF_KEY_LENGTH 6
// RDV40, validation structure to help identifying that client/firmware is talking with RDV40
typedef struct {