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:
@@ -26,14 +26,14 @@ export function parseTrailerAccess(hex16: string): AccessBits | null {
|
||||
|
||||
export function sakLabel(sak: number): string {
|
||||
switch (sak) {
|
||||
case 0x00:
|
||||
return "Ultralight / NTAG / Type 2";
|
||||
case 0x08:
|
||||
return "Ultralight family";
|
||||
return "MIFARE Classic 1K";
|
||||
case 0x09:
|
||||
return "Mini / Classic";
|
||||
return "MIFARE Mini";
|
||||
case 0x18:
|
||||
return "Classic 1K";
|
||||
case 0x19:
|
||||
return "Classic 4K";
|
||||
return "MIFARE Classic 4K";
|
||||
default:
|
||||
return `SAK 0x${sak.toString(16).toUpperCase()}`;
|
||||
}
|
||||
@@ -68,14 +68,15 @@ export function scanCheatLines(tag: { uidLen: number; atqa: number; sak: number
|
||||
} else if (tag.uidLen === 10) {
|
||||
lines.push("10-byte UID — triple cascade path.");
|
||||
}
|
||||
if (tag.sak === 0x08) {
|
||||
lines.push("SAK 0x08 — often Ultralight / Type 2; use page read/write for user memory.");
|
||||
if (tag.sak === 0x00) {
|
||||
lines.push("SAK 0x00 — typical Type 2 / Ultralight-style path; use page read/write.");
|
||||
}
|
||||
if (tag.sak === 0x18 || tag.sak === 0x19) {
|
||||
lines.push("MIFARE Classic — authenticate per sector trailer, then block read/write.");
|
||||
if (tag.sak === 0x08 || tag.sak === 0x09 || tag.sak === 0x18) {
|
||||
lines.push("MIFARE Classic family — authenticate per sector trailer, then block read/write.");
|
||||
}
|
||||
if (tag.atqa === 0x0044 || tag.atqa === 0x4400) {
|
||||
lines.push("ATQA 0x4400 pattern — very common Type A inventory response.");
|
||||
/* Firmware stores ATQA as (resp[3]<<8)|resp[4]; e.g. 04 00 → 0x0400. */
|
||||
if (tag.atqa === 0x0400 || tag.atqa === 0x4400) {
|
||||
lines.push("ATQA 0x0400/0x4400 class — common Type A / MIFARE inventory shapes.");
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
@@ -19,11 +19,25 @@ export default function WriteClone() {
|
||||
const [ulData, setUlData] = useState("00000000");
|
||||
|
||||
const write = async () => {
|
||||
const cleanKey = key.replace(/\s/g, "");
|
||||
const cleanData = data.replace(/\s/g, "");
|
||||
if (cleanKey.length !== 12 || !/^[0-9A-Fa-f]+$/.test(cleanKey)) {
|
||||
toast("Key must be exactly 12 hex digits", "err");
|
||||
return;
|
||||
}
|
||||
if (cleanData.length !== 32 || !/^[0-9A-Fa-f]+$/.test(cleanData)) {
|
||||
toast("Block data must be exactly 32 hex digits (16 bytes)", "err");
|
||||
return;
|
||||
}
|
||||
if (!Number.isInteger(block) || block < 0 || block > 255) {
|
||||
toast("Block must be an integer 0–255", "err");
|
||||
return;
|
||||
}
|
||||
if (!confirm("Write will modify tag memory. Continue?")) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await apiPost("/api/mifare/write-block", { block, key, keyB, data });
|
||||
await apiPost("/api/mifare/write-block", { block, key: cleanKey, keyB, data: cleanData });
|
||||
toast("Write OK");
|
||||
} catch (e) {
|
||||
toast(String(e), "err");
|
||||
@@ -31,11 +45,16 @@ export default function WriteClone() {
|
||||
};
|
||||
|
||||
const writeUl = async () => {
|
||||
const h = ulData.replace(/\s/g, "");
|
||||
if (h.length !== 8 || !/^[0-9A-Fa-f]+$/.test(h)) {
|
||||
toast("UL data must be exactly 8 hex digits (4 bytes)", "err");
|
||||
return;
|
||||
}
|
||||
if (!confirm("Ultralight page write — can brick OTP/lock bytes if misused. Continue?")) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await apiPost("/api/ul/write-page", { page: ulPage, data: ulData.replace(/\s/g, "") });
|
||||
await apiPost("/api/ul/write-page", { page: ulPage, data: h });
|
||||
toast("UL page write OK");
|
||||
} catch (e) {
|
||||
toast(String(e), "err");
|
||||
|
||||
Reference in New Issue
Block a user