Files
nfc-pn532-warlord/firmware/components/nfc_engine/nfc_deep.c
drjones 8968560565 Add PN532 toolkit firmware, web UI, and embedded SPIFFS assets
Includes ESP-IDF NFC stack (deep capture, 4K Classic geometry, UL write API,
open SoftAP), React dashboard with live tag diagnostics, and docs. README
updated for APIs and lab Wi-Fi defaults.

Made-with: Cursor
2026-03-29 09:32:55 -07:00

189 lines
6.1 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
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 = (tag->sak == 0x19);
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)
{
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 < sizeof(k_default_keys) / 6; 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_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];
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_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];
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)
{
int sectors = (tag->sak == 0x19) ? 40 : 16;
cJSON *arr = cJSON_CreateArray();
for (int s = 0; s < sectors; s++) {
cJSON *sec = cJSON_CreateObject();
(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)
{
(void)tag;
cJSON *pages = cJSON_CreateArray();
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)
{
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);
}
if (tag->type_hint == 1) {
add_mifare_classic(tag, o);
} else if (tag->type_hint == 2) {
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;
}