Files
nfc-pn532-warlord/firmware/components/nfc_engine/nfc_deep.c
drjones 3be2d3f936 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
2026-04-02 18:32:06 -07:00

212 lines
6.4 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "nfc_engine/nfc_deep.h"
#include "nfc_engine/nfc_engine.h"
#include "pn532_host/pn532_core.h"
#include "esp_log.h"
#include "esp_timer.h"
#include <stdio.h>
#include <string.h>
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},
{0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5},
{0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
};
static void block_to_hex(const uint8_t blk[NFC_BLOCK_LEN], char *out33)
{
for (int i = 0; i < NFC_BLOCK_LEN; i++) {
snprintf(out33 + i * 2, 3, "%02X", blk[i]);
}
out33[32] = 0;
}
static void key_to_hex(const uint8_t k[6], char *out13)
{
for (int i = 0; i < 6; i++) {
snprintf(out13 + i * 2, 3, "%02X", k[i]);
}
out13[12] = 0;
}
/** Classic 1K: 16 sectors x 4 blocks. Classic 4K: sectors 031 x 4 blocks, sectors 3239 x 16 blocks. */
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 = nfc_tag_is_mifare_classic_4k(tag);
if (!is4k) {
if (sector < 0 || sector > 15) {
return false;
}
*first_block = sector * 4;
*num_blocks = 4;
*trailer_block = (uint8_t)(sector * 4 + 3);
return true;
}
if (sector < 0 || sector > 39) {
return false;
}
if (sector <= 31) {
*first_block = sector * 4;
*num_blocks = 4;
*trailer_block = (uint8_t)(sector * 4 + 3);
} else {
int r = sector - 32;
*first_block = 128 + r * 16;
*num_blocks = 16;
*trailer_block = (uint8_t)(128 + r * 16 + 15);
}
return true;
}
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;
if (!mfc_sector_layout(tag, sector, &fb, &nb, &trailer)) {
return false;
}
cJSON_AddNumberToObject(sec_out, "sector", sector);
cJSON_AddNumberToObject(sec_out, "firstBlock", fb);
cJSON_AddNumberToObject(sec_out, "trailerBlock", trailer);
cJSON_AddNumberToObject(sec_out, "blockCount", nb);
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);
for (int b = 0; b < nb; b++) {
uint8_t bn = (uint8_t)(fb + b);
uint8_t blk[NFC_BLOCK_LEN];
if (nfc_mifare_read_block(bn, blk) == ESP_OK) {
char hx[36];
block_to_hex(blk, hx);
cJSON_AddItemToArray(blocks, cJSON_CreateString(hx));
} else {
cJSON_AddItemToArray(blocks, cJSON_CreateNull());
}
}
cJSON_AddItemToObject(sec_out, "blocksHex", blocks);
return true;
}
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);
for (int b = 0; b < nb; b++) {
uint8_t bn = (uint8_t)(fb + b);
uint8_t blk[NFC_BLOCK_LEN];
if (nfc_mifare_read_block(bn, blk) == ESP_OK) {
char hx[36];
block_to_hex(blk, hx);
cJSON_AddItemToArray(blocks, cJSON_CreateString(hx));
} else {
cJSON_AddItemToArray(blocks, cJSON_CreateNull());
}
}
cJSON_AddItemToObject(sec_out, "blocksHex", blocks);
return true;
}
}
cJSON_AddBoolToObject(sec_out, "authFailed", true);
return false;
}
static void add_mifare_classic(nfc_tag_info_t *tag, cJSON *root)
{
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);
}
cJSON_AddItemToObject(root, "mifareClassic", arr);
}
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) {
break;
}
char line[12];
snprintf(line, sizeof line, "%02X%02X%02X%02X", buf[0], buf[1], buf[2], buf[3]);
cJSON_AddItemToArray(pages, cJSON_CreateString(line));
}
cJSON_AddItemToObject(root, "ultralightPagesHex", pages);
}
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 *tag_json = nfc_tag_to_json(tag);
if (!tag_json) {
cJSON_Delete(o);
return NULL;
}
cJSON_AddItemToObject(o, "tag", tag_json);
if (nfc_tag_is_mifare_classic(tag)) {
add_mifare_classic(tag, o);
} else if (nfc_tag_is_type2(tag)) {
add_ultralight(tag, o);
} else {
cJSON_AddStringToObject(
o, "note",
"Unknown type from SAK/ATQA — stored inventory + PN532 status only. Use Raw console for ISO14443-4 / other stacks.");
}
ESP_LOGI(TAG, "deep profile done type_hint=%u", tag->type_hint);
return o;
}