Fix PN532 transport and NFC tag handling.
Tighten PN532 framing and NFC access locking so scan, read, write, and raw operations behave reliably on hardware, and correct Classic versus Type 2 tag classification so common cards hit the right code paths. Refresh the embedded web assets and launcher files to match the corrected firmware behavior. Made-with: Cursor
This commit is contained in:
@@ -29,6 +29,9 @@ typedef struct {
|
||||
esp_err_t nfc_engine_init(void);
|
||||
|
||||
esp_err_t nfc_poll_passive_target(nfc_tag_info_t *out);
|
||||
bool nfc_tag_is_mifare_classic(const nfc_tag_info_t *tag);
|
||||
bool nfc_tag_is_mifare_classic_4k(const nfc_tag_info_t *tag);
|
||||
bool nfc_tag_is_type2(const nfc_tag_info_t *tag);
|
||||
|
||||
esp_err_t nfc_mifare_authenticate_block(const nfc_tag_info_t *tag, uint8_t block_no,
|
||||
const nfc_mifare_key_t *key);
|
||||
|
||||
@@ -25,7 +25,6 @@ bool session_capture_append_line(const char *line);
|
||||
|
||||
/** Export raw bytes (entire buffer) for HTTP download. */
|
||||
size_t session_capture_export_size(void);
|
||||
const char *session_capture_export_ptr(void);
|
||||
|
||||
/** Copy up to `cap` bytes (typically cap >= session_capture_export_size()). */
|
||||
void session_capture_copy_to(char *dst, size_t cap, size_t *out_len);
|
||||
|
||||
@@ -36,6 +36,27 @@ static const uint8_t k_builtin[][6] = {
|
||||
#define MAX_VARIANTS_PER_KEY 14
|
||||
#define MAX_TRIES_BEFORE_WDT 48
|
||||
|
||||
static bool add_sector_hit(cJSON *hits, uint8_t sector, const uint8_t key[6], const char *key_type)
|
||||
{
|
||||
if (!hits || !key || !key_type) {
|
||||
return false;
|
||||
}
|
||||
char hx[16];
|
||||
for (int i = 0; i < 6; i++) {
|
||||
snprintf(hx + i * 2, 3, "%02X", key[i]);
|
||||
}
|
||||
hx[12] = 0;
|
||||
cJSON *h = cJSON_CreateObject();
|
||||
if (!h) {
|
||||
return false;
|
||||
}
|
||||
cJSON_AddNumberToObject(h, "sector", sector);
|
||||
cJSON_AddStringToObject(h, "keyHex", hx);
|
||||
cJSON_AddStringToObject(h, "keyType", key_type);
|
||||
cJSON_AddItemToArray(hits, h);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int push_variant(const uint8_t base[6], int idx, uint8_t out[6])
|
||||
{
|
||||
memcpy(out, base, 6);
|
||||
@@ -76,7 +97,7 @@ static bool try_key_on_trailer(nfc_tag_info_t *tag, uint8_t trailer, const uint8
|
||||
/** Trailer block for MIFARE Classic sector index (0..15 for 1K, 0..39 for 4K). */
|
||||
static uint8_t classic_trailer_for_sector(const nfc_tag_info_t *tag, uint8_t sec)
|
||||
{
|
||||
if (tag->sak == 0x19) {
|
||||
if (nfc_tag_is_mifare_classic_4k(tag)) {
|
||||
if (sec <= 31) {
|
||||
return (uint8_t)(sec * 4 + 3);
|
||||
}
|
||||
@@ -102,7 +123,13 @@ cJSON *nfc_mifare_dictionary_attack(nfc_tag_info_t *tag, uint8_t sector_first, u
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (tag->type_hint != 1) {
|
||||
if (!tag) {
|
||||
cJSON_Delete(root);
|
||||
cJSON_Delete(hits);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!nfc_tag_is_mifare_classic(tag)) {
|
||||
cJSON_AddStringToObject(root, "error", "not_classic_sak_hint");
|
||||
cJSON_AddItemToObject(root, "sectorHits", hits);
|
||||
if (attempts_out) {
|
||||
@@ -111,13 +138,22 @@ cJSON *nfc_mifare_dictionary_attack(nfc_tag_info_t *tag, uint8_t sector_first, u
|
||||
return root;
|
||||
}
|
||||
|
||||
uint8_t sector_max = nfc_tag_is_mifare_classic_4k(tag) ? 39 : 15;
|
||||
|
||||
if (sector_first > sector_last) {
|
||||
uint8_t t = sector_first;
|
||||
sector_first = sector_last;
|
||||
sector_last = t;
|
||||
}
|
||||
if (sector_first > sector_max) {
|
||||
sector_first = sector_max;
|
||||
}
|
||||
if (sector_last > sector_max) {
|
||||
sector_last = sector_max;
|
||||
}
|
||||
|
||||
for (uint8_t sec = sector_first; sec <= sector_last; sec++) {
|
||||
for (int sec_i = sector_first; sec_i <= sector_last; sec_i++) {
|
||||
uint8_t sec = (uint8_t)sec_i;
|
||||
uint8_t trailer = classic_trailer_for_sector(tag, sec);
|
||||
if (trailer == 0xFF) {
|
||||
continue;
|
||||
@@ -133,30 +169,18 @@ cJSON *nfc_mifare_dictionary_attack(nfc_tag_info_t *tag, uint8_t sector_first, u
|
||||
}
|
||||
attempts++;
|
||||
if (try_key_on_trailer(tag, trailer, trial, false)) {
|
||||
char hx[16];
|
||||
for (int i = 0; i < 6; i++) {
|
||||
snprintf(hx + i * 2, 3, "%02X", trial[i]);
|
||||
if (!add_sector_hit(hits, sec, trial, "A")) {
|
||||
cJSON_Delete(root);
|
||||
return NULL;
|
||||
}
|
||||
hx[12] = 0;
|
||||
cJSON *h = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(h, "sector", sec);
|
||||
cJSON_AddStringToObject(h, "keyHex", hx);
|
||||
cJSON_AddStringToObject(h, "keyType", "A");
|
||||
cJSON_AddItemToArray(hits, h);
|
||||
got = true;
|
||||
break;
|
||||
}
|
||||
if (try_key_on_trailer(tag, trailer, trial, true)) {
|
||||
char hx[16];
|
||||
for (int i = 0; i < 6; i++) {
|
||||
snprintf(hx + i * 2, 3, "%02X", trial[i]);
|
||||
if (!add_sector_hit(hits, sec, trial, "B")) {
|
||||
cJSON_Delete(root);
|
||||
return NULL;
|
||||
}
|
||||
hx[12] = 0;
|
||||
cJSON *h = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(h, "sector", sec);
|
||||
cJSON_AddStringToObject(h, "keyHex", hx);
|
||||
cJSON_AddStringToObject(h, "keyType", "B");
|
||||
cJSON_AddItemToArray(hits, h);
|
||||
got = true;
|
||||
break;
|
||||
}
|
||||
@@ -177,30 +201,18 @@ cJSON *nfc_mifare_dictionary_attack(nfc_tag_info_t *tag, uint8_t sector_first, u
|
||||
}
|
||||
attempts++;
|
||||
if (try_key_on_trailer(tag, trailer, trial, false)) {
|
||||
char hx[16];
|
||||
for (int i = 0; i < 6; i++) {
|
||||
snprintf(hx + i * 2, 3, "%02X", trial[i]);
|
||||
if (!add_sector_hit(hits, sec, trial, "A")) {
|
||||
cJSON_Delete(root);
|
||||
return NULL;
|
||||
}
|
||||
hx[12] = 0;
|
||||
cJSON *h = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(h, "sector", sec);
|
||||
cJSON_AddStringToObject(h, "keyHex", hx);
|
||||
cJSON_AddStringToObject(h, "keyType", "A");
|
||||
cJSON_AddItemToArray(hits, h);
|
||||
got = true;
|
||||
break;
|
||||
}
|
||||
if (try_key_on_trailer(tag, trailer, trial, true)) {
|
||||
char hx[16];
|
||||
for (int i = 0; i < 6; i++) {
|
||||
snprintf(hx + i * 2, 3, "%02X", trial[i]);
|
||||
if (!add_sector_hit(hits, sec, trial, "B")) {
|
||||
cJSON_Delete(root);
|
||||
return NULL;
|
||||
}
|
||||
hx[12] = 0;
|
||||
cJSON *h = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(h, "sector", sec);
|
||||
cJSON_AddStringToObject(h, "keyHex", hx);
|
||||
cJSON_AddStringToObject(h, "keyType", "B");
|
||||
cJSON_AddItemToArray(hits, h);
|
||||
got = true;
|
||||
break;
|
||||
}
|
||||
@@ -213,6 +225,10 @@ cJSON *nfc_mifare_dictionary_attack(nfc_tag_info_t *tag, uint8_t sector_first, u
|
||||
|
||||
if (!got) {
|
||||
cJSON *h = cJSON_CreateObject();
|
||||
if (!h) {
|
||||
cJSON_Delete(root);
|
||||
return NULL;
|
||||
}
|
||||
cJSON_AddNumberToObject(h, "sector", sec);
|
||||
cJSON_AddBoolToObject(h, "miss", true);
|
||||
cJSON_AddItemToArray(hits, h);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
static const char *TAG = "nfc_deep";
|
||||
|
||||
#define MAX_UL_PAGES 240
|
||||
#define DEFAULT_KEY_COUNT (sizeof(k_default_keys) / sizeof(k_default_keys[0]))
|
||||
|
||||
static const uint8_t k_default_keys[][6] = {
|
||||
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
|
||||
@@ -37,7 +38,7 @@ static void key_to_hex(const uint8_t k[6], char *out13)
|
||||
static bool mfc_sector_layout(const nfc_tag_info_t *tag, int sector, int *first_block, int *num_blocks,
|
||||
uint8_t *trailer_block)
|
||||
{
|
||||
bool is4k = (tag->sak == 0x19);
|
||||
bool is4k = nfc_tag_is_mifare_classic_4k(tag);
|
||||
if (!is4k) {
|
||||
if (sector < 0 || sector > 15) {
|
||||
return false;
|
||||
@@ -65,6 +66,9 @@ static bool mfc_sector_layout(const nfc_tag_info_t *tag, int sector, int *first_
|
||||
|
||||
static bool try_sector(nfc_tag_info_t *tag, int sector, cJSON *sec_out)
|
||||
{
|
||||
if (!tag || !sec_out) {
|
||||
return false;
|
||||
}
|
||||
int fb = 0;
|
||||
int nb = 0;
|
||||
uint8_t trailer = 0;
|
||||
@@ -76,16 +80,19 @@ static bool try_sector(nfc_tag_info_t *tag, int sector, cJSON *sec_out)
|
||||
cJSON_AddNumberToObject(sec_out, "trailerBlock", trailer);
|
||||
cJSON_AddNumberToObject(sec_out, "blockCount", nb);
|
||||
|
||||
for (size_t ki = 0; ki < sizeof(k_default_keys) / 6; ki++) {
|
||||
for (size_t ki = 0; ki < DEFAULT_KEY_COUNT; ki++) {
|
||||
nfc_mifare_key_t key;
|
||||
memcpy(key.key, k_default_keys[ki], 6);
|
||||
key.key_b = false;
|
||||
if (nfc_mifare_authenticate_block(tag, trailer, &key) == ESP_OK) {
|
||||
cJSON *blocks = cJSON_CreateArray();
|
||||
if (!blocks) {
|
||||
return false;
|
||||
}
|
||||
cJSON_AddStringToObject(sec_out, "keyType", "A");
|
||||
char kh[16];
|
||||
key_to_hex(key.key, kh);
|
||||
cJSON_AddStringToObject(sec_out, "keyHex", kh);
|
||||
cJSON *blocks = cJSON_CreateArray();
|
||||
for (int b = 0; b < nb; b++) {
|
||||
uint8_t bn = (uint8_t)(fb + b);
|
||||
uint8_t blk[NFC_BLOCK_LEN];
|
||||
@@ -102,11 +109,14 @@ static bool try_sector(nfc_tag_info_t *tag, int sector, cJSON *sec_out)
|
||||
}
|
||||
key.key_b = true;
|
||||
if (nfc_mifare_authenticate_block(tag, trailer, &key) == ESP_OK) {
|
||||
cJSON *blocks = cJSON_CreateArray();
|
||||
if (!blocks) {
|
||||
return false;
|
||||
}
|
||||
cJSON_AddStringToObject(sec_out, "keyType", "B");
|
||||
char kh[16];
|
||||
key_to_hex(key.key, kh);
|
||||
cJSON_AddStringToObject(sec_out, "keyHex", kh);
|
||||
cJSON *blocks = cJSON_CreateArray();
|
||||
for (int b = 0; b < nb; b++) {
|
||||
uint8_t bn = (uint8_t)(fb + b);
|
||||
uint8_t blk[NFC_BLOCK_LEN];
|
||||
@@ -128,10 +138,19 @@ static bool try_sector(nfc_tag_info_t *tag, int sector, cJSON *sec_out)
|
||||
|
||||
static void add_mifare_classic(nfc_tag_info_t *tag, cJSON *root)
|
||||
{
|
||||
int sectors = (tag->sak == 0x19) ? 40 : 16;
|
||||
if (!tag || !root) {
|
||||
return;
|
||||
}
|
||||
int sectors = nfc_tag_is_mifare_classic_4k(tag) ? 40 : 16;
|
||||
cJSON *arr = cJSON_CreateArray();
|
||||
if (!arr) {
|
||||
return;
|
||||
}
|
||||
for (int s = 0; s < sectors; s++) {
|
||||
cJSON *sec = cJSON_CreateObject();
|
||||
if (!sec) {
|
||||
break;
|
||||
}
|
||||
(void)try_sector(tag, s, sec);
|
||||
cJSON_AddItemToArray(arr, sec);
|
||||
}
|
||||
@@ -140,8 +159,14 @@ static void add_mifare_classic(nfc_tag_info_t *tag, cJSON *root)
|
||||
|
||||
static void add_ultralight(nfc_tag_info_t *tag, cJSON *root)
|
||||
{
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
(void)tag;
|
||||
cJSON *pages = cJSON_CreateArray();
|
||||
if (!pages) {
|
||||
return;
|
||||
}
|
||||
uint8_t buf[4];
|
||||
for (int p = 0; p < MAX_UL_PAGES; p++) {
|
||||
if (nfc_ultralight_read_page((uint8_t)p, buf) != ESP_OK) {
|
||||
@@ -156,26 +181,24 @@ static void add_ultralight(nfc_tag_info_t *tag, cJSON *root)
|
||||
|
||||
cJSON *nfc_tag_deep_profile(nfc_tag_info_t *tag)
|
||||
{
|
||||
if (!tag) {
|
||||
return NULL;
|
||||
}
|
||||
cJSON *o = cJSON_CreateObject();
|
||||
if (!o) {
|
||||
return NULL;
|
||||
}
|
||||
cJSON_AddNumberToObject(o, "capturedMs", (double)(esp_timer_get_time() / 1000));
|
||||
cJSON_AddItemToObject(o, "tag", nfc_tag_to_json(tag));
|
||||
|
||||
uint8_t gs[32];
|
||||
size_t gl = 0;
|
||||
if (pn532_get_general_status(gs, sizeof(gs), &gl) == ESP_OK && gl > 0) {
|
||||
cJSON *g = cJSON_CreateArray();
|
||||
for (size_t i = 0; i < gl; i++) {
|
||||
cJSON_AddItemToArray(g, cJSON_CreateNumber(gs[i]));
|
||||
}
|
||||
cJSON_AddItemToObject(o, "pn532GeneralStatus", g);
|
||||
cJSON *tag_json = nfc_tag_to_json(tag);
|
||||
if (!tag_json) {
|
||||
cJSON_Delete(o);
|
||||
return NULL;
|
||||
}
|
||||
cJSON_AddItemToObject(o, "tag", tag_json);
|
||||
|
||||
if (tag->type_hint == 1) {
|
||||
if (nfc_tag_is_mifare_classic(tag)) {
|
||||
add_mifare_classic(tag, o);
|
||||
} else if (tag->type_hint == 2) {
|
||||
} else if (nfc_tag_is_type2(tag)) {
|
||||
add_ultralight(tag, o);
|
||||
} else {
|
||||
cJSON_AddStringToObject(
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "esp_log.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
static const char *TAG = "nfc_engine";
|
||||
|
||||
@@ -12,20 +13,34 @@ static void hint_type(nfc_tag_info_t *t)
|
||||
{
|
||||
switch (t->sak) {
|
||||
case 0x08:
|
||||
case 0x00:
|
||||
t->type_hint = 2;
|
||||
break;
|
||||
case 0x09:
|
||||
case 0x18:
|
||||
case 0x19:
|
||||
t->type_hint = 1;
|
||||
break;
|
||||
case 0x00:
|
||||
t->type_hint = 2;
|
||||
break;
|
||||
default:
|
||||
t->type_hint = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool nfc_tag_is_mifare_classic(const nfc_tag_info_t *tag)
|
||||
{
|
||||
return tag && tag->type_hint == 1;
|
||||
}
|
||||
|
||||
bool nfc_tag_is_mifare_classic_4k(const nfc_tag_info_t *tag)
|
||||
{
|
||||
return nfc_tag_is_mifare_classic(tag) && tag->sak == 0x18;
|
||||
}
|
||||
|
||||
bool nfc_tag_is_type2(const nfc_tag_info_t *tag)
|
||||
{
|
||||
return tag && tag->type_hint == 2;
|
||||
}
|
||||
|
||||
esp_err_t nfc_engine_init(void)
|
||||
{
|
||||
esp_err_t e = pn532_core_init();
|
||||
@@ -48,6 +63,9 @@ esp_err_t nfc_engine_init(void)
|
||||
|
||||
esp_err_t nfc_poll_passive_target(nfc_tag_info_t *out)
|
||||
{
|
||||
if (!out) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
memset(out, 0, sizeof(*out));
|
||||
uint8_t resp[64];
|
||||
size_t rlen = 0;
|
||||
@@ -55,23 +73,23 @@ esp_err_t nfc_poll_passive_target(nfc_tag_info_t *out)
|
||||
if (e != ESP_OK) {
|
||||
return e;
|
||||
}
|
||||
if (rlen < 2 || resp[0] != 0x00) {
|
||||
if (rlen < 2) {
|
||||
return ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
if (resp[1] < 1) {
|
||||
if (resp[0] < 1) {
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
if (rlen < 8) {
|
||||
if (rlen < 7) {
|
||||
return ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
s_tg = resp[2];
|
||||
out->atqa = (uint16_t)(((uint16_t)resp[3] << 8) | resp[4]);
|
||||
out->sak = resp[5];
|
||||
out->uid_len = resp[6];
|
||||
if (out->uid_len > NFC_MAX_UID_LEN || (size_t)(7 + out->uid_len) > rlen) {
|
||||
s_tg = resp[1];
|
||||
out->atqa = (uint16_t)(((uint16_t)resp[2] << 8) | resp[3]);
|
||||
out->sak = resp[4];
|
||||
out->uid_len = resp[5];
|
||||
if (out->uid_len > NFC_MAX_UID_LEN || (size_t)(6 + out->uid_len) > rlen) {
|
||||
return ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
memcpy(out->uid, resp + 7, out->uid_len);
|
||||
memcpy(out->uid, resp + 6, out->uid_len);
|
||||
hint_type(out);
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -80,7 +98,7 @@ static esp_err_t in_data_tg(const uint8_t *data, size_t data_len, uint8_t *respo
|
||||
size_t *response_len)
|
||||
{
|
||||
uint8_t buf[64];
|
||||
if (data_len > sizeof(buf) - 3) {
|
||||
if (!data || !response || !response_len || data_len == 0 || data_len > sizeof(buf) - 2) {
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
buf[0] = PN532_CMD_INDATAEXCHANGE;
|
||||
@@ -92,6 +110,9 @@ static esp_err_t in_data_tg(const uint8_t *data, size_t data_len, uint8_t *respo
|
||||
esp_err_t nfc_mifare_authenticate_block(const nfc_tag_info_t *tag, uint8_t block_no,
|
||||
const nfc_mifare_key_t *key)
|
||||
{
|
||||
if (!tag || !key) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
uint8_t data[12];
|
||||
data[0] = key->key_b ? PN532_MIFARE_CMD_AUTH_B : PN532_MIFARE_CMD_AUTH_A;
|
||||
data[1] = block_no;
|
||||
@@ -113,7 +134,7 @@ esp_err_t nfc_mifare_authenticate_block(const nfc_tag_info_t *tag, uint8_t block
|
||||
if (e != ESP_OK) {
|
||||
return e;
|
||||
}
|
||||
if (rlen < 1 || resp[0] != 0x00) {
|
||||
if (rlen < 2 || resp[0] != (uint8_t)(PN532_CMD_INDATAEXCHANGE + 1) || resp[1] != 0x00) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
@@ -121,6 +142,9 @@ esp_err_t nfc_mifare_authenticate_block(const nfc_tag_info_t *tag, uint8_t block
|
||||
|
||||
esp_err_t nfc_mifare_read_block(uint8_t block_no, uint8_t block[NFC_BLOCK_LEN])
|
||||
{
|
||||
if (!block) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
uint8_t d[] = {PN532_MIFARE_CMD_READ, block_no};
|
||||
uint8_t resp[32];
|
||||
size_t rlen = 0;
|
||||
@@ -128,15 +152,18 @@ esp_err_t nfc_mifare_read_block(uint8_t block_no, uint8_t block[NFC_BLOCK_LEN])
|
||||
if (e != ESP_OK) {
|
||||
return e;
|
||||
}
|
||||
if (rlen < 1 + NFC_BLOCK_LEN || resp[0] != 0x00) {
|
||||
if (rlen < 2 + NFC_BLOCK_LEN || resp[0] != (uint8_t)(PN532_CMD_INDATAEXCHANGE + 1) || resp[1] != 0x00) {
|
||||
return ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
memcpy(block, resp + 1, NFC_BLOCK_LEN);
|
||||
memcpy(block, resp + 2, NFC_BLOCK_LEN);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t nfc_mifare_write_block(uint8_t block_no, const uint8_t block[NFC_BLOCK_LEN])
|
||||
{
|
||||
if (!block) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
uint8_t d[2 + NFC_BLOCK_LEN];
|
||||
d[0] = PN532_MIFARE_CMD_WRITE;
|
||||
d[1] = block_no;
|
||||
@@ -147,7 +174,7 @@ esp_err_t nfc_mifare_write_block(uint8_t block_no, const uint8_t block[NFC_BLOCK
|
||||
if (e != ESP_OK) {
|
||||
return e;
|
||||
}
|
||||
if (rlen < 1 || resp[0] != 0x00) {
|
||||
if (rlen < 2 || resp[0] != (uint8_t)(PN532_CMD_INDATAEXCHANGE + 1) || resp[1] != 0x00) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
@@ -155,6 +182,9 @@ esp_err_t nfc_mifare_write_block(uint8_t block_no, const uint8_t block[NFC_BLOCK
|
||||
|
||||
esp_err_t nfc_ultralight_read_page(uint8_t page, uint8_t data[4])
|
||||
{
|
||||
if (!data) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
uint8_t d[] = {PN532_MIFARE_CMD_READ, page};
|
||||
uint8_t resp[32];
|
||||
size_t rlen = 0;
|
||||
@@ -162,15 +192,18 @@ esp_err_t nfc_ultralight_read_page(uint8_t page, uint8_t data[4])
|
||||
if (e != ESP_OK) {
|
||||
return e;
|
||||
}
|
||||
if (rlen < 1 + 16 || resp[0] != 0x00) {
|
||||
if (rlen < 2 + 16 || resp[0] != (uint8_t)(PN532_CMD_INDATAEXCHANGE + 1) || resp[1] != 0x00) {
|
||||
return ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
memcpy(data, resp + 1, 4);
|
||||
memcpy(data, resp + 2, 4);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t nfc_ultralight_write_page(uint8_t page, const uint8_t data[4])
|
||||
{
|
||||
if (!data) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
uint8_t d[6] = {0xA2, page, data[0], data[1], data[2], data[3]};
|
||||
uint8_t resp[16];
|
||||
size_t rlen = 0;
|
||||
@@ -178,7 +211,7 @@ esp_err_t nfc_ultralight_write_page(uint8_t page, const uint8_t data[4])
|
||||
if (e != ESP_OK) {
|
||||
return e;
|
||||
}
|
||||
if (rlen < 1 || resp[0] != 0x00) {
|
||||
if (rlen < 2 || resp[0] != (uint8_t)(PN532_CMD_INDATAEXCHANGE + 1) || resp[1] != 0x00) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
@@ -186,30 +219,40 @@ esp_err_t nfc_ultralight_write_page(uint8_t page, const uint8_t data[4])
|
||||
|
||||
esp_err_t nfc_ul_fast_read(uint8_t start_page, uint8_t *out, size_t out_max, size_t *got)
|
||||
{
|
||||
if (!out || !got || out_max == 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
*got = 0;
|
||||
size_t off = 0;
|
||||
uint8_t d[2] = {0x3A, start_page};
|
||||
size_t max_pages = MAX((size_t)1, MIN((size_t)16, out_max / 4));
|
||||
size_t remaining_pages = 256U - (size_t)start_page;
|
||||
if (max_pages > remaining_pages) {
|
||||
max_pages = remaining_pages;
|
||||
}
|
||||
uint8_t end_page = (uint8_t)(start_page + max_pages - 1);
|
||||
uint8_t d[3] = {0x3A, start_page, end_page};
|
||||
uint8_t resp[256];
|
||||
size_t rlen = 0;
|
||||
esp_err_t e = in_data_tg(d, sizeof(d), resp, sizeof(resp), &rlen);
|
||||
if (e != ESP_OK) {
|
||||
return e;
|
||||
}
|
||||
if (rlen < 2 || resp[0] != 0x00) {
|
||||
if (rlen < 2 || resp[0] != (uint8_t)(PN532_CMD_INDATAEXCHANGE + 1) || resp[1] != 0x00) {
|
||||
return ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
size_t payload = rlen - 1;
|
||||
size_t payload = rlen - 2;
|
||||
if (payload > out_max) {
|
||||
payload = out_max;
|
||||
}
|
||||
memcpy(out, resp + 1, payload);
|
||||
off = payload;
|
||||
*got = off;
|
||||
memcpy(out, resp + 2, payload);
|
||||
*got = payload;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
cJSON *nfc_tag_to_json(const nfc_tag_info_t *tag)
|
||||
{
|
||||
if (!tag || tag->uid_len > NFC_MAX_UID_LEN) {
|
||||
return NULL;
|
||||
}
|
||||
cJSON *o = cJSON_CreateObject();
|
||||
if (!o) {
|
||||
return NULL;
|
||||
@@ -233,9 +276,9 @@ cJSON *nfc_tag_to_json(const nfc_tag_info_t *tag)
|
||||
cJSON_AddStringToObject(o, "sakHex", sk);
|
||||
|
||||
const char *guess = "Unknown / use Raw or RATS";
|
||||
if (tag->type_hint == 1) {
|
||||
guess = (tag->sak == 0x19) ? "MIFARE Classic 4K" : "MIFARE Classic 1K or compatible";
|
||||
} else if (tag->type_hint == 2) {
|
||||
if (nfc_tag_is_mifare_classic(tag)) {
|
||||
guess = nfc_tag_is_mifare_classic_4k(tag) ? "MIFARE Classic 4K" : "MIFARE Classic 1K or compatible";
|
||||
} else if (nfc_tag_is_type2(tag)) {
|
||||
guess = "Ultralight / NTAG / Type 2 family";
|
||||
}
|
||||
cJSON_AddStringToObject(o, "typeGuess", guess);
|
||||
|
||||
@@ -35,12 +35,40 @@ void session_capture_clear(void)
|
||||
|
||||
bool session_capture_is_full(void)
|
||||
{
|
||||
return s_full;
|
||||
bool full = false;
|
||||
if (s_mu) {
|
||||
xSemaphoreTake(s_mu, portMAX_DELAY);
|
||||
}
|
||||
full = s_full;
|
||||
if (s_mu) {
|
||||
xSemaphoreGive(s_mu);
|
||||
}
|
||||
return full;
|
||||
}
|
||||
|
||||
bool session_capture_deep_enabled(void) { return s_deep; }
|
||||
bool session_capture_deep_enabled(void)
|
||||
{
|
||||
bool deep = false;
|
||||
if (s_mu) {
|
||||
xSemaphoreTake(s_mu, portMAX_DELAY);
|
||||
}
|
||||
deep = s_deep;
|
||||
if (s_mu) {
|
||||
xSemaphoreGive(s_mu);
|
||||
}
|
||||
return deep;
|
||||
}
|
||||
|
||||
void session_capture_set_deep(bool on) { s_deep = on; }
|
||||
void session_capture_set_deep(bool on)
|
||||
{
|
||||
if (s_mu) {
|
||||
xSemaphoreTake(s_mu, portMAX_DELAY);
|
||||
}
|
||||
s_deep = on;
|
||||
if (s_mu) {
|
||||
xSemaphoreGive(s_mu);
|
||||
}
|
||||
}
|
||||
|
||||
size_t session_capture_max(void) { return sizeof(s_buf) - 2; }
|
||||
|
||||
@@ -65,7 +93,7 @@ void session_capture_get_status(size_t *used_bytes, uint32_t *line_count, bool *
|
||||
|
||||
bool session_capture_append_line(const char *line)
|
||||
{
|
||||
if (!line || s_full) {
|
||||
if (!line) {
|
||||
return false;
|
||||
}
|
||||
size_t l = strlen(line);
|
||||
@@ -106,8 +134,6 @@ size_t session_capture_export_size(void)
|
||||
return n;
|
||||
}
|
||||
|
||||
const char *session_capture_export_ptr(void) { return s_buf; }
|
||||
|
||||
void session_capture_copy_to(char *dst, size_t cap, size_t *out_len)
|
||||
{
|
||||
if (s_mu) {
|
||||
|
||||
Reference in New Issue
Block a user