diff --git a/docs/plans/2026-03-03-ntag5-config-check.md b/docs/plans/2026-03-03-ntag5-config-check.md index 147ac69..5b79931 100644 --- a/docs/plans/2026-03-03-ntag5-config-check.md +++ b/docs/plans/2026-03-03-ntag5-config-check.md @@ -1,22 +1,19 @@ -# NTAG5Link Config Verification via uFR Zero +# NTAG5Link Config Verification via NFC Reader **Date**: 2026-03-03 -**Status**: Tool written, reader connection blocked +**Status**: Partial — tag detected, config read requires ACR1552 ## Goal -Read-only verification that the NTAG5Link Click board's configuration matches xblink requirements, using the Digital Logic uFR Zero Multi-ISO reader. +Read-only verification that the NTAG5Link Click board's configuration matches xblink requirements. ## Tool -`tools/ntag5_config_check.py` — Python script using uFCoder library (ctypes) in ISO15693 transparent mode. +`tools/ntag5_config_check.py` — Python script using pyscard (PCSC) with auto-detection of reader capabilities. -Features: -- ISO15693 INVENTORY to discover all tags by UID -- Addressed mode for all config reads (supports multiple tags in field) -- Reads CONFIG block (0x37) and EH/ED CONFIG block (0x3D) -- NXP system info for interface type verification -- Checks against xblink expected config values +**Reader support:** +- **ACR1552**: Full NXP config verification via transparent ISO15693 mode (NXP READ_CONFIG 0xC0) +- **uFR Zero (CCID)**: Basic tag info only — UID, EEPROM blocks. Cannot read NXP config registers. ## Expected Config @@ -29,45 +26,56 @@ Features: | EH enable | true | EH_CONFIG byte 0, bit 0 = 1 | | EH voltage | 3.0V | EH_CONFIG byte 0, bits 2:1 = 10 | -## Reader Connection Issue +## uFR Zero Investigation Results -**Problem**: uFCoder library returns `UFR_TIMEOUT_ERR` (0x1002) for all `ReaderOpenEx` parameter combinations. +The D-Logic uFR Zero Multi-ISO reader presents as a **CCID** (USB SmartCard class 0x0B) device, not serial. -**USB device**: `10c4:ea60` — Silicon Labs CP2102 USB to UART Bridge Controller, iSerial "0001". Generic descriptor, no D-Logic branding in USB strings. +**USB**: VID:PID `3629:301b`, serial "UD101156" -**Raw serial investigation**: -- At 1 Mbps (expected uFR baud): responds with `00 80` (2 bytes) — not a valid uFR protocol frame (expected 6+ bytes with `0x55...0xAA` framing) -- At 115200 bps: returns ASCII shell commands mentioning "systemctl", "proxmark3" paths -- At 250 Kbps: returns structured but unrecognized data +### What works (PCSC/CCID) -**Possible causes**: -1. Device may not be a uFR Zero — the CP210x bridge has generic descriptors -2. Reader firmware may need updating -3. Reader may be in a non-standard mode -4. Different hardware variant requiring different protocol +| Command | APDU | Result | +|---------|------|--------| +| GET_DATA (UID) | `FF CA 00 00 00` | UID: `E0:04:01:58:7F:8F:11:00` (NXP ISO15693) | +| READ_BINARY | `FF B0 00 xx 00` | Reads ISO15693 user EEPROM blocks | +| NDEF CC (block 0) | `FF B0 00 00 00` | `E1 40 80 09` — NDEF v4.0, 1024 bytes | -**Next steps**: -1. Physically verify the device is indeed the uFR Zero (check labels, LEDs) -2. Try the D-Logic `ufr_online` Windows utility to confirm reader identity -3. If confirmed as uFR Zero, check firmware version and update if needed -4. Alternative: use the ACR1552 PCSC reader with ntag5sensor directly (requires porting the reader to share the field with NTAG5 Click) +### What doesn't work -## Fallback Plan: ntag5sensor + ACR1552 +| Approach | Result | Why | +|----------|--------|-----| +| uFCoder `ReaderOpen` | 0x1002 (timeout) | Library scans serial/tty, doesn't support CCID USB | +| uFCoder `ReaderOpenHnd_ZeroUSB` | 0x0054 (opening error) | Even with pcscd stopped | +| PCSC transparent (FF C2) | SW=6A81 | Not supported by uFR Zero CCID firmware | +| CCID escape (IOCTL 0x42000001) | 6A81 for all commands | Advertised but non-functional | +| Raw ISO15693 via SCardControl | 6A81 | No passthrough mechanism | -If the uFR Zero connection cannot be resolved, use the existing ntag5sensor Python tooling with the ACR1552 PCSC reader: +### Key finding: CP210x is NOT the uFR Zero + +`/dev/ttyUSB0` (`10c4:ea60`, Silicon Labs CP210x) is a **separate device** (Proxmark3). The uFR Zero is a pure CCID device with no serial interface. + +### CCID setup required + +The uFR Zero must be added to the CCID driver's device list: + +1. Add VID `0x3629` / PID `0x301B` to `/usr/lib/pcsc/drivers/ifd-ccid.bundle/Contents/Info.plist` +2. Set `ifdDriverOptions` to `0x0001` (enables CCID exchange — though escape commands still return 6A81) +3. Restart pcscd: `systemctl restart pcscd` + +## Path Forward: ACR1552 + +The ACR1552 reader supports transparent ISO15693 mode (FF C2 pseudo-APDUs with TLV encoding), enabling NXP custom commands. The `ntag5_config_check.py` tool auto-detects the ACR1552 and switches to full config verification mode. ```python -# From ntag5sensor — already has full config read/write support +# ntag5sensor has proven ACR1552 + NXP custom command support: chip = NTAG5Link(reader) config = chip.get_config_info() eh_config = chip.get_eh_ed_config_info() ``` -The ACR1552 is already proven with ntag5sensor. Only change needed: physically position the ACR1552 over the NTAG5 Click board instead of the uFR Zero. - ## Config Change Plan (if mismatches found) -Use ntag5sensor's write functions (via ACR1552 or adapted for uFR Zero): +Use ntag5sensor's write functions via ACR1552: ```python # Set I2C slave mode + SRAM passthrough + SRAM enable diff --git a/tools/Info.plist.ccid-backup b/tools/Info.plist.ccid-backup new file mode 100644 index 0000000..7083eb5 --- /dev/null +++ b/tools/Info.plist.ccid-backup @@ -0,0 +1,1898 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + libccid.so + CFBundleIdentifier + fr.apdu.ccid.smartcardccid + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + CCIDCLASSDRIVER + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.5.5 + CFBundleSignature + ???? + CFBundleVersion + 0.0.1d1 + ifdCapabilities + 0x00000000 + + + + ifdProtocolSupport + 0x00000001 + ifdVersionNumber + 0x00000001 + + ifdLogLevel + 0x0003 + + + + ifdDriverOptions + 0x0000 + + + + ifdManufacturerString + Ludovic Rousseau (ludovic.rousseau@free.fr) + + ifdProductString + Generic CCID driver + + ifdVendorID + + 0x072F + 0x09C3 + 0x09C3 + 0x058F + 0x0DC3 + 0x0DC3 + 0x1B0E + 0x0783 + 0x046A + 0x046A + 0x046A + 0x046A + 0x046A + 0x0982 + 0x413C + 0x413C + 0x073D + 0x073D + 0x073D + 0x073D + 0x073D + 0x08E6 + 0x08E6 + 0x08E6 + 0x08E6 + 0x08E6 + 0x08E6 + 0x08E6 + 0x08E6 + 0x08E6 + 0x08E6 + 0x08E6 + 0x08E6 + 0x1059 + 0x1059 + 0x0B81 + 0x0F14 + 0x0F14 + 0x0D46 + 0x0D46 + 0x0D46 + 0x0D46 + 0x17EF + 0x09BE + 0x076B + 0x076B + 0x03F0 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0xA625 + 0x08E6 + 0x08E6 + 0x0F14 + 0x0F14 + 0x0F14 + 0x0DB5 + 0x0DB5 + 0x0DB5 + 0x0DB5 + 0x0DB5 + 0x0DB5 + 0x072F + 0x072F + 0x072F + 0x072F + 0x072F + 0x072F + 0x072F + 0x072F + 0x072F + 0x072F + 0x072F + 0x072F + 0x072F + 0x072F + 0x072F + 0x072F + 0x072F + 0x072F + 0x072F + 0x072F + 0x072F + 0x1C34 + 0x1C34 + 0x1C34 + 0x1C34 + 0x2021 + 0x2021 + 0x2021 + 0x0A89 + 0x0A89 + 0x0A89 + 0x0A89 + 0x0A89 + 0x0A89 + 0x0A89 + 0x0A89 + 0x24DC + 0x24DC + 0x24DC + 0x24DC + 0x24DC + 0x24DC + 0x24DC + 0x24DC + 0x2CE3 + 0x2CE3 + 0x2CE3 + 0x058F + 0x2A0C + 0x0483 + 0x2406 + 0x2406 + 0x2406 + 0x33B6 + 0x1FD3 + 0x0DC3 + 0x0DC3 + 0x0DC3 + 0x03EB + 0x03EB + 0x03EB + 0x03EB + 0x03EB + 0x03EB + 0x03EB + 0xC1A6 + 0x15CF + 0x15CF + 0x15CF + 0x04E6 + 0x23A0 + 0x23A0 + 0x23A0 + 0x23A0 + 0x25DD + 0x25DD + 0x25DD + 0x25DD + 0x25DD + 0x25DD + 0x25DD + 0x25DD + 0x25DD + 0x25DD + 0x25DD + 0x25DD + 0x25DD + 0x25DD + 0x25DD + 0x2ABE + 0x1B0E + 0x1B0E + 0x0A5C + 0x0A5C + 0x0A5C + 0x0A5C + 0x0A5C + 0x0A5C + 0x0A5C + 0x0A5C + 0x0A5C + 0x0783 + 0x0783 + 0x0783 + 0x0783 + 0x20A0 + 0x0CA6 + 0x8829 + 0x2DFF + 0x2DFF + 0x19E7 + 0x046A + 0x046A + 0x046A + 0x046A + 0x046A + 0x046A + 0x046A + 0x046A + 0x046A + 0x046A + 0x046A + 0x04F2 + 0x03F0 + 0x03F0 + 0x04F2 + 0x31AA + 0x31AA + 0x31AA + 0x31AA + 0x31AA + 0x31AA + 0x31AA + 0x31AA + 0x31AA + 0x31AA + 0x31AA + 0x31AA + 0x31AA + 0x31AA + 0x31AA + 0x31AA + 0x31AA + 0x31AA + 0x31AA + 0x31AA + 0x31AA + 0x31AA + 0x1FC9 + 0x0982 + 0x0982 + 0x23D8 + 0x257B + 0x1AC2 + 0x1AC2 + 0x0483 + 0x0483 + 0x1C34 + 0x1C34 + 0x1C34 + 0x1C34 + 0x1DB2 + 0x1DB2 + 0x1DB2 + 0x257B + 0x09D8 + 0x09D8 + 0x1FFA + 0x2CE4 + 0x2CE4 + 0x2CE4 + 0x1EA8 + 0x1209 + 0x096E + 0x096E + 0x096E + 0x096E + 0x096E + 0x096E + 0x096E + 0x096E + 0x096E + 0x096E + 0x096E + 0x096E + 0x096E + 0x096E + 0x2925 + 0x234B + 0x096E + 0x096E + 0x096E + 0x096E + 0x096E + 0x096E + 0x096E + 0x096E + 0x096E + 0x0BF8 + 0x0BF8 + 0x0BF8 + 0x0BF8 + 0x0BF8 + 0x0BF8 + 0x0BF8 + 0x0898 + 0x08E6 + 0x08E6 + 0x08E6 + 0x08E6 + 0x08E6 + 0x08E6 + 0x08E6 + 0x08E6 + 0x08E6 + 0x08E6 + 0x0BDA + 0x048D + 0x058C + 0x076B + 0x05E3 + 0x05E3 + 0x05E3 + 0x20A0 + 0xAE68 + 0x1059 + 0x1059 + 0x0F1A + 0x19C8 + 0x32A3 + 0x1677 + 0x03F0 + 0x03F0 + 0x03F0 + 0x09C3 + 0x09C3 + 0x09C3 + 0x09C3 + 0x076B + 0x076B + 0x076B + 0x076B + 0x076B + 0x076B + 0x076B + 0x076B + 0x076B + 0x076B + 0x076B + 0x076B + 0x076B + 0x04A4 + 0x04A4 + 0x0B81 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x1FFA + 0x2406 + 0x03EB + 0x03EB + 0x0C27 + 0x2FD2 + 0x2FB0 + 0x2022 + 0x1FC9 + 0x1403 + 0x1403 + 0x1403 + 0x2406 + 0x2406 + 0x2406 + 0x2406 + 0x2406 + 0x2406 + 0x2406 + 0x2406 + 0x076B + 0x04D8 + 0x2A18 + 0x2A18 + 0x2A18 + 0x2947 + 0x2947 + 0x2947 + 0x2947 + 0x2947 + 0x2947 + 0x2947 + 0x2947 + 0x2947 + 0x28B9 + 0x2F76 + 0x0483 + 0x0D46 + 0x0D46 + 0x0D46 + 0x0D46 + 0x0D46 + 0x0D46 + 0x0D46 + 0x2D25 + 0x2D25 + 0x0925 + 0x2C97 + 0x2C97 + 0x2C97 + 0x2C97 + 0x2C97 + 0x2C97 + 0x17EF + 0x17EF + 0x17EF + 0x03F0 + 0x03F0 + 0x03F0 + 0x08AE + 0x1403 + 0x0424 + 0x0424 + 0x0416 + 0x0416 + 0x079B + 0x079B + 0x0BDA + 0x4D55 + 0x1E0D + 0x1E0D + 0x1E0D + 0x1E0D + 0x20A0 + 0x20A0 + 0x20A0 + 0x20A0 + 0x20A0 + 0x04E6 + 0x1FC9 + 0x1FC9 + 0x1FC9 + 0x1A74 + 0x1A74 + 0x076B + 0x076B + 0x076B + 0x076B + 0x076B + 0x076B + 0x076B + 0x076B + 0x076B + 0x076B + 0x076B + 0x076B + 0x076B + 0x04DA + 0x0471 + 0x04B9 + 0x096E + 0x21AB + 0x316D + 0x14DD + 0x1C6A + 0x0C4B + 0x0C4B + 0x0C4B + 0x0C4B + 0x0C4B + 0x0C27 + 0x14CD + 0x13FE + 0x0529 + 0x0529 + 0x08E6 + 0x08E6 + 0x08E6 + 0x08E6 + 0x24A2 + 0x2EE1 + 0x0973 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x04E6 + 0x0B81 + 0x0403 + 0x356F + 0x356F + 0x1677 + 0x0DF6 + 0x04E8 + 0x1209 + 0x054C + 0x054C + 0x054C + 0x316E + 0x316E + 0x316E + 0x316E + 0x1C34 + 0x1C34 + 0x1C34 + 0x1C34 + 0x1C34 + 0x1C34 + 0x1C34 + 0x1C34 + 0x1C34 + 0x1C34 + 0x1C34 + 0x1C34 + 0x1C34 + 0x1C34 + 0x1C34 + 0x1C34 + 0x1C34 + 0x1C34 + 0x1C34 + 0x1C34 + 0x08DF + 0x08DF + 0x08DF + 0x08DF + 0x08DF + 0x05AF + 0x1370 + 0x1206 + 0x1206 + 0x0E6A + 0x1D50 + 0x1862 + 0x08E6 + 0x17B9 + 0x1976 + 0xA625 + 0x0B0C + 0x0B0C + 0x349E + 0x349E + 0x23EB + 0x19A6 + 0x2A17 + 0x2DFF + 0x2DFF + 0x1CF0 + 0x1A44 + 0x1A44 + 0x1A44 + 0x1A44 + 0x1A44 + 0x1A44 + 0x1A44 + 0x1A44 + 0x1A44 + 0x1A44 + 0x1A44 + 0x1A44 + 0x1A44 + 0x0E0F + 0x163C + 0x163C + 0x163C + 0x163C + 0x163C + 0x34EB + 0x0416 + 0x1050 + 0x1050 + 0x1050 + 0x1050 + 0x1050 + 0x1050 + 0x1050 + 0x1050 + 0x072F + 0x09C3 + 0x058F + 0x0DC3 + 0x03EB + 0x0A5C + 0x0A5C + 0x0A5C + 0x0A5C + 0x0A5C + 0x0A5C + 0x0A5C + 0x0783 + 0x096E + 0x0BDA + 0x03F0 + 0x03F0 + 0x04CC + 0x0D46 + 0x0D46 + 0x0B97 + 0x0B97 + 0x08C3 + 0x08C3 + 0x15E1 + 0x062D + + + ifdProductID + + 0x90CC + 0x0013 + 0x0014 + 0x9520 + 0x1004 + 0x1102 + 0x1078 + 0x0006 + 0x0005 + 0x0010 + 0x002D + 0x003E + 0x01A2 + 0x0007 + 0x2100 + 0x2101 + 0x0007 + 0x0008 + 0x0B00 + 0x0C00 + 0x0C01 + 0x1227 + 0x3437 + 0x3438 + 0x3478 + 0x3480 + 0x34C0 + 0x34C3 + 0x4433 + 0x5503 + 0x5504 + 0x8000 + 0x8141 + 0x0017 + 0x0019 + 0x0200 + 0x003B + 0x003D + 0x3001 + 0x3002 + 0x3003 + 0x3010 + 0x1003 + 0x0002 + 0x3021 + 0xA022 + 0x0824 + 0x5111 + 0x5116 + 0x511D + 0x5410 + 0xE001 + 0xE003 + 0x0801 + 0x1359 + 0xACE0 + 0x0011 + 0x0037 + 0x0038 + 0x0138 + 0x0160 + 0x0164 + 0x0170 + 0x0180 + 0x0184 + 0x1204 + 0x221A + 0x2224 + 0x223B + 0x223E + 0x223F + 0x2259 + 0x226B + 0x2301 + 0x2303 + 0x2308 + 0x8201 + 0x8202 + 0x8300 + 0x90DB + 0xB000 + 0xB100 + 0xB106 + 0xB112 + 0xB501 + 0xB506 + 0xAFC0 + 0xAFC1 + 0xAFC2 + 0xAFC3 + 0x0001 + 0x0011 + 0x0101 + 0x0025 + 0x0030 + 0x0069 + 0x006A + 0x0080 + 0x0081 + 0x0082 + 0x0060 + 0x0101 + 0x0102 + 0x0201 + 0x0401 + 0x0402 + 0x0428 + 0x0501 + 0x100F + 0x9563 + 0x9567 + 0x9573 + 0x9522 + 0x0021 + 0xACD1 + 0x5003 + 0x5004 + 0x5006 + 0x0001 + 0xCC1D + 0x0900 + 0x1007 + 0x1008 + 0x6004 + 0x6009 + 0x600B + 0x6010 + 0x6011 + 0x6012 + 0x6014 + 0x0131 + 0x0019 + 0x001D + 0x0020 + 0x511C + 0x0001 + 0x0002 + 0x0003 + 0x0008 + 0x1101 + 0x1201 + 0x2221 + 0x2321 + 0x2341 + 0x2351 + 0x2354 + 0x2361 + 0x2362 + 0x2371 + 0x3211 + 0xB001 + 0x3111 + 0x3403 + 0x3503 + 0x1003 + 0x1079 + 0x5A02 + 0x5802 + 0x5804 + 0x5832 + 0x5833 + 0x5834 + 0x5842 + 0x5843 + 0x5864 + 0x5865 + 0x0007 + 0x0009 + 0x0010 + 0x0036 + 0x42D4 + 0x00A0 + 0xCCB2 + 0xB601 + 0xB602 + 0x0002 + 0x0090 + 0x0092 + 0x00A1 + 0x00A2 + 0x00A3 + 0x00A4 + 0x00A5 + 0x00A7 + 0x005B + 0x0070 + 0x0072 + 0x0967 + 0x114A + 0x124A + 0x1469 + 0x0224 + 0x0324 + 0x1000 + 0x1100 + 0x2000 + 0x2001 + 0x2100 + 0x3000 + 0x3001 + 0x3002 + 0x3003 + 0x3100 + 0x4000 + 0x4001 + 0x5000 + 0x6000 + 0x6001 + 0x7001 + 0x7003 + 0x7004 + 0x7005 + 0x7006 + 0x0905 + 0x0008 + 0x0040 + 0x0603 + 0xC305 + 0x0300 + 0x0302 + 0xA389 + 0xA40B + 0xAFD0 + 0xAFD1 + 0xAFD2 + 0xAFD3 + 0x0801 + 0x080C + 0x088B + 0xD205 + 0x0427 + 0x0428 + 0x0002 + 0x5001 + 0x5020 + 0x7479 + 0x8013 + 0x2702 + 0x0608 + 0x060D + 0x0621 + 0x0622 + 0x0623 + 0x0624 + 0x062E + 0x0685 + 0x0807 + 0x080F + 0x0619 + 0x061A + 0x061C + 0x0505 + 0x700A + 0x0000 + 0x080A + 0x0853 + 0x0855 + 0x0856 + 0x0859 + 0x0867 + 0x0868 + 0x0869 + 0x086C + 0x1022 + 0x1023 + 0x1024 + 0x1005 + 0x1006 + 0x1017 + 0x1021 + 0x0101 + 0x2202 + 0x34C1 + 0x34C2 + 0x34C5 + 0x34EC + 0x4042 + 0x5743 + 0x8108 + 0x3440 + 0x3479 + 0x0169 + 0x1366 + 0x9590 + 0x3A21 + 0x0757 + 0x0758 + 0x0771 + 0x4107 + 0x8001 + 0x000C + 0x000D + 0x0002 + 0x0012 + 0x3201 + 0x0025 + 0x104A + 0x2924 + 0x581D + 0x0028 + 0x0029 + 0x002B + 0x002D + 0x3031 + 0x5022 + 0x5023 + 0x5400 + 0x5412 + 0x5422 + 0x5432 + 0x5A27 + 0x6632 + 0x502A + 0x5127 + 0x5326 + 0x5427 + 0x00C7 + 0x00D4 + 0x0220 + 0x5612 + 0x5613 + 0x5713 + 0x5724 + 0x5725 + 0x5790 + 0x5791 + 0x5811 + 0x5812 + 0x5814 + 0x5815 + 0x5816 + 0x5818 + 0x5819 + 0x581A + 0x581B + 0x581C + 0x581D + 0x5826 + 0x5710 + 0x5720 + 0x5721 + 0x5723 + 0x5810 + 0x5817 + 0x000C + 0x6200 + 0x9308 + 0x9324 + 0xCCDB + 0x0100 + 0x003A + 0x016C + 0x0102 + 0x7506 + 0x750C + 0x750D + 0x6300 + 0x6301 + 0x6302 + 0x6403 + 0x6404 + 0x6407 + 0x6303 + 0x6305 + 0x3B01 + 0xEB61 + 0x5000 + 0x5001 + 0x5002 + 0x0101 + 0x0102 + 0x0103 + 0x0104 + 0x0105 + 0x0112 + 0x0111 + 0x0113 + 0x0114 + 0x0002 + 0x0906 + 0x0007 + 0x3014 + 0x301D + 0x4189 + 0x41A9 + 0x4289 + 0x4389 + 0x43A9 + 0x0000 + 0x0001 + 0x900A + 0x0001 + 0x0004 + 0x0005 + 0x1009 + 0x4009 + 0x5009 + 0x6007 + 0x6055 + 0x6111 + 0x164A + 0x174A + 0x184A + 0x0BDF + 0x7502 + 0x1104 + 0x1202 + 0xC136 + 0xC137 + 0x0026 + 0x0052 + 0x0161 + 0x0010 + 0x0013 + 0x0033 + 0x1023 + 0x8033 + 0x4108 + 0x4109 + 0x4211 + 0x4230 + 0x42B2 + 0x511A + 0x0107 + 0x010B + 0x0117 + 0xB111 + 0x6354 + 0x1021 + 0x4321 + 0x5321 + 0x5421 + 0x6321 + 0x3022 + 0x3621 + 0x3821 + 0x5121 + 0x5125 + 0x6622 + 0x6623 + 0xA021 + 0x117A + 0x040F + 0x1400 + 0x0603 + 0x0010 + 0x4C4B + 0x1006 + 0x7050 + 0x0504 + 0x0520 + 0x0551 + 0x0580 + 0x9102 + 0xCCDA + 0x8166 + 0xC831 + 0x0602 + 0x0620 + 0x34CC + 0x34CE + 0x34CF + 0x34D2 + 0x0102 + 0x0001 + 0x0003 + 0x5113 + 0x5115 + 0x5117 + 0x5119 + 0x511F + 0x5120 + 0x5121 + 0x512B + 0x512C + 0x5291 + 0x5293 + 0x8007 + 0xC587 + 0x22FC + 0x23FE + 0x0341 + 0x800A + 0x0007 + 0xBEEE + 0x0D8F + 0x0DC8 + 0x0DC9 + 0x0001 + 0x0002 + 0x0003 + 0x0010 + 0x6012 + 0x601A + 0x6112 + 0x611A + 0x6122 + 0x612A + 0x6132 + 0x613A + 0x6212 + 0x621A + 0x7113 + 0x7121 + 0x7123 + 0x7124 + 0x7136 + 0x7138 + 0x7141 + 0x8141 + 0x91B1 + 0xA1A1 + 0x0004 + 0x3115 + 0x3117 + 0x3201 + 0x3203 + 0x605A + 0x0901 + 0x2105 + 0x2107 + 0x5083 + 0x6141 + 0x0000 + 0x34C7 + 0x400B + 0x0001 + 0x0810 + 0x0050 + 0x0052 + 0x0300 + 0x0430 + 0x0003 + 0x0009 + 0x0001 + 0x1540 + 0x1543 + 0x0001 + 0x0001 + 0x0101 + 0x0112 + 0x0115 + 0x0117 + 0x0119 + 0x0120 + 0x0122 + 0x0855 + 0x0865 + 0x0870 + 0x0875 + 0x0920 + 0x0004 + 0x0406 + 0x0407 + 0x0417 + 0x0418 + 0x0A03 + 0x1506 + 0x3815 + 0x0111 + 0x0112 + 0x0115 + 0x0116 + 0x0404 + 0x0405 + 0x0406 + 0x0407 + 0x2200 + 0x0008 + 0x9540 + 0x100F + 0x6016 + 0x5800 + 0x5801 + 0x5805 + 0x5844 + 0x5845 + 0x5866 + 0x5867 + 0x0003 + 0x0503 + 0x0165 + 0x0036 + 0x1024 + 0x5072 + 0x4000 + 0x4001 + 0x7762 + 0x7772 + 0x0401 + 0x0402 + 0x2007 + 0x0001 + + + ifdFriendlyName + + ACS ACR 38U-CCID + ActivIdentity USB Reader V3 + ActivIdentity Activkey_Sim + Alcor Micro AU9520 + Athena ASE IIIe + Athena ASEDrive IIIe KB + BLUTRONICS BLUDRIVE II CCID + C3PO LTC31 v2 + Cherry GmbH SmartBoard XX33 + Cherry GmbH SmartBoard XX44 + Cherry GmbH SmartTerminal XX44 + Cherry GmbH SmartTerminal ST-2xxx + Cherry GmbH CHERRY SECURE BOARD 1.0 + COVADIS ALYA + Dell keyboard SK-3106 + Dell Dell Smart Card Reader Keyboard + Eutron CryptoIdentity CCID + Eutron CryptoIdentity CCID + Eutron Digipass 860 + Eutron Card Reader + Eutron Smart Pocket + Gemalto PDT + Gemalto PC Twin Reader + Gemalto USB Shell Token V2 + Gemalto USB GemPCPinpad SmartCard Reader + Gemalto GemCore SIM Pro Smart Card Reader + Gemalto Ezio Shield + Gemalto EZIO CB+ + Gemalto Gemplus USB SmartCard Reader 433-Swap + Gemalto Prox Dual USB PC Link Reader + Gemalto Prox SU USB PC LinkReader + Gemalto Smart Enterprise Guardian Secure USB Device + Gemalto IDBridge K3000 + Giesecke & Devrient GmbH StarSign Crypto USB Token + Giesecke & Devrient GmbH StarSign CUT S + id3 Semiconductors CL1356T + INGENICO Leo + Ingenico WITEO USB Smart Card Reader + KOBIL KAAN Base + KOBIL KAAN Advanced + KOBIL KAAN SIM III + KOBIL EMV CAP - SecOVID Reader III + Lenovo Integrated Smart Card Reader + MYSMART MySMART PAD V2.0 + OMNIKEY AG CardMan 3121 + Precise Biometrics Sense MC + SCM Microsystems Inc. HP USB Smartcard Reader + SCM Microsystems Inc. SCR 331-DI + SCM Microsystems Inc. SCR 3310 + SCM Microsystems Inc. SCR 3311 + SCM Microsystems Inc. SCR 355 + SCM Microsystems Inc. SCR 331 + SCM Microsystems Inc. SPR 532 + TianYu CCID Key TianYu CCID SmartKey + Verisign Secure Storage Token + Verisign Secure Token + XIRING XI-SIGN USB V2 + XIRING MyLeo + XIRING Leo v2 + Access IS ePassport Reader + Access IS NFC Smart Module + Access IS NFC Smart Module + Access IS ATR210 + Access IS ATR220 + Access IS ATR220 + ACS ACR101 ICC Reader + ACS ACR1251 Dual Reader + ACS ACR1281U + ACS ACR1252 Dual Reader + ACS ACR1252 Reader + ACS ACR1255U-J1 + ACS ACR1252IMP Reader + ACS WalletMate 1S CL Reader + ACS ACR1581 1S Dual Reader + ACS ACR1552 1S CL Reader + ACS ACR1552 CL Reader + ACS APG8201 PINhandy 1 + ACS APG8201 USB Reader + ACS ACR33 ICC Reader + ACS CryptoMate64 + ACS ACR3901U ICC Reader + ACS ACR39U ICC Reader + ACS CryptoMate (T2) + ACS CryptoMate EVO + ACS ACR40T ICC Reader + ACS ACR40U ICC Reader + AF Care One + AF Care One + AF Care Two + AF Care Two + AK910 CKey + AK910 CKey + AK910 IDONE + Aktiv Rutoken lite + Aktiv Rutoken ECP + Aktiv Rutoken SCR 3001 Reader + Aktiv Rutoken SCR 3101 NFC Reader + Aktiv PINPad Ex + Aktiv PINPad In + Aktiv Rutoken PINPad 2 + Aktiv Co., ProgramPark Rutoken Magistra + Aladdin R.D. JaCarta + Aladdin R.D. JaCarta LT + Aladdin R.D. JCR-770 + Aladdin R.D. JC-WebPass (JC600) + Aladdin R.D. JaCarta + Aladdin R.D. JaCartaReader + Aladdin R.D. JaCarta U2F (JC602) + Aladdin R.D. JaCarta Flash + Alcor Link AK9563 + Alcor Link AK9567 + Alcor Link AK9572 + Alcor Micro AU9522 + Alpha-Project ANGARA Token + ANCUD CCID USB Reader & RNG + appidkey GmbH ID50 -USB + appidkey GmbH ID100L-USB-SC-Reader + appidkey GmbH ID60-USB + ArkSigner Connect2Sign + ASK-RFID CPL108 + Athena IDProtect Key v2 + Athena ASEDrive IIIe KB Bio PIV + Athena ASEDrive IIIe Combo Bio PIV + ATMEL AT91SO CCID Smart Card Reader + ATMEL AT98SC032CT-USB + ATMEL AT91SC192192CT-USB ICCD reader + ATMEL AT90SCR100 + ATMEL AT90SCR050 + ATMEL VaultIC420 Smart Object + ATMEL VaultIC440 + AvestUA AvestKey + Avtor SecureToken + Avtor SC Reader 371 + Avtor SecureToken + Axalto Reflex USB v3 + BIFIT USB-Token iBank2key + BIFIT iBank2Key + BIFIT iToken + BIFIT ANGARA + Bit4id miniLector-s + Bit4id cryptokey + Bit4id iAM + Bit4id CKey4 + Bit4id tokenME FIPS v3 + Bit4id Digital DNA Key + Bit4id Digital-DNA Key + Bit4id Digital-DNA Key BT + Bit4id Digital-DNA Key + Bit4id TokenME EVO v2 + Bit4id miniLector AIR EVO + Bit4id miniLector Blue + bit4id miniLector-EVO + BIT4ID miniLector AIR NFC v3 + BIT4ID mLector AIR DI V3 + Bluink Ltd. Bluink CCID + BLUTRONICS BLUDRIVE II CCID + BLUTRONICS TAURUS NFC + Broadcom Corp 5880 + Broadcom Corp 5880 + Broadcom Corp 5880 + Broadcom Corp 5880 + Broadcom Corp 5880 + Broadcom Corp 58200 + Broadcom Corp 58200 + Broadcom Corp 58200 + Broadcom Corp 58200 + C3PO TLTC2USB + C3PO KBR36 + C3PO LTC32 + C3PO LTC36 + Canokeys Canokey + CASTLES EZCCID Smart Card Reader + CCB eSafeLD + Certgate GmbH AirID 2 USB + Certgate GmbH ONEKEY ID 2 USB + charismathics plug'n'crypt CCID token + Cherry Smart Card Reader USB + Cherry TC 1300 + Cherry KC 1000 SC + Cherry KC 1000 SC/DI + Cherry Smartcard Keyboard G87-1xx44 + Cherry KC 1000 SC Z + Cherry KC 1000 SC/DI Z + Cherry SmartTerminal XX44 + Cherry GmbH SmartBoard XX1X + Cherry GmbH SmartTerminal XX1X + Cherry GmbH SmartTerminal ST-1275 + Chicony USB Smart Card Keyboard + Chicony HP USB Smartcard CCID Keyboard KR + Chicony HP USB Smartcard CCID Keyboard JP + Chicony HP Skylab USB Smartcard Keyboard + Circle Idaxis SecurePIV + Circle CIR315 + Circle CIR115 ICC + Circle CIR125 ICC + Circle CIR215 PICC + Circle CIR215 CL + Circle CIR215 CL + Circle CIR315 Dual & 1S + Circle CIR315 + Circle CIR315 CL + Circle CIR315 DI + Circle CIR315 + Circle CIR415 CL & 1S + Circle CIR415 CL + Circle CIR515 ICC + Circle CIR615 CL & 1S + Circle CIR615 CL + Circle CCR7115 ICC + Circle CCR7315 + Circle CIR315 + Circle CCR7125 ICC + Circle CIR125-DOT ICC + CIRIGHT ONE PASS U2F + COVADIS VEGA-ALPHA + COVADIS Auriga + CREATOR CRT-603(CZ1) CCR + DC.Ltd DC4 5CCID READER + DESKO GmbH IDenty chrom + DESKO GmbH PENTA Scanner + Dexon Tecnologias Digitais LTDA DXToken + Dexon Tecnologias Digitais LTDA eSmartDX + Doctolib SR + Doctolib SR + Doctolib SR + Doctolib SR + DUALi DE-620 Combi + DUALi DE-ABCM6 RFRW + DUALi DRAGON NFC READER + eID_R6 001 X8 + Elatec TWN4 SmartCard NFC + Elatec TWN4/B1.06/CPF3.05/S1SC1.32/P (Beta 3) + ELYCTIS CL reader + ESMART Reader ER433x ICC + ESMART Reader ER773x Dual & 1S + ESMART Token GOST + Excelsecu Card reader + F-Secure Foundry USB Armory Mk II + Feitian 502-CL + Feitian R502 + Feitian BLE CCID Dongle + Feitian VR504 VHBR Contactless & Contact Card Reader + Feitian bR500 + Feitian bR301 + Feitian R701 + Feitian R805 + Feitian ePass2003 + Feitian eJAVA Token + FEITIAN iR301 + FEITIAN bR301 + FEITIAN iR301 + Feitian Technologies FT SCR310 + Flight system consulting Incredist + Free Software Initiative of Japan Gnuk + FT ePass2003Auto + FT U2F CCID KB + FT CCID KB + FT U2F CCID + FT CCID + FT Biopass FIDO2 + FT Biopass KB FIDO CCID + FT Biopass KB CCID + FT Biopass CCID + Fujitsu Keyboard KB100 SCR + Fujitsu Keyboard KB100 SCR eSIG + Fujitsu Smartcard Reader D323 + Fujitsu Siemens Computers SmartCard Keyboard USB 2A + Fujitsu Siemens Computers SmartCard USB 2A + FujitsuTechnologySolutions GmbH SmartCase KB SCR eSIG + FujitsuTechnologySolutions GmbH Keyboard KB SCR2 + Gemalto RF CR5400 + Gemalto Gem e-Seal Pro USB Token + Gemalto Ezio Shield Secure Channel + Gemalto Ezio Shield + Gemalto Ezio Shield Branch Reader + Gemalto GemPC Express + Gemalto SA .NET Dual + Gemalto Hybrid Smartcard Reader + Gemalto Smart Enterprise Guardian Secure USB Device + GEMALTO CT1100 + Gemplus GemCore POS Pro Smart Card Reader + Generic USB2.0-CRW + Generic MultiCard Device + Generic EMV Smartcard Reader + Generic USB Smart Card Reader + Genesys Logic CCID Card Reader + Genesys Logic Combo Card Reader + Genesys Logic CCID Card Reader + German Privacy Foundation Crypto Stick v1.2 + GHI NC001 + Giesecke & Devrient GmbH Star Sign Card Token 350 (ICCD) + Giesecke & Devrient GmbH Star Sign Card Token 550 (ICCD) + GIS Ltd SmartMouse USB + GoldKey Security PIV Token + GoTrust Idem Key + HDZB uKeyCI800-K18 + Hewlett Packard HP USB Smartcard CCID Keyboard + Hewlett Packard MFP Smart Card Reader + Hewlett-Packard HP lt4112 Gobi 4G Module + HID Global Crescendo Key + HID Global Crescendo Key + HID Global Crescendo Key + HID Global Crescendo Key + HID Global OMNIKEY 3x21 Smart Card Reader + HID Global OMNIKEY 5022 Smart Card Reader + HID Global OMNIKEY 5023 Smart Card Reader + HID Global veriCLASS Reader + HID Global OMNIKEY 5122 Smartcard Reader + HID Global OMNIKEY 5422 Smartcard Reader + HID Global OMNIKEY 5122 Dual + HID Global OMNIKEY 5027CK CCID CONFIG IF + HID Global OMNIKEY 6121 Smart Card Reader + HID OMNIKEY 5025-CL + HID OMNIKEY 5127 CK + HID OMNIKEY 5326 DFR + HID OMNIKEY 5427 CK + Hitachi, Ltd. Hitachi Biometric Reader + Hitachi, Ltd. Hitachi Portable Biometric Reader + id3 Semiconductors CL1356A_HID + Identiv uTrust 3720 Contactless Reader + Identiv uTrust 3721 Contactless Reader + Identiv CLOUD 2980 F Smart Card Reader + Identiv Identiv uTrust 4701 F Dual Interface Reader + Identiv Identiv uTrust 4711 F CL + SAM Reader + Identiv uTrust 3700 F CL Reader + Identiv uTrust 3701 F CL Reader + Identiv uTrust 2900 R Smart Card Reader + Identiv uTrust 2910 R Smart Card Reader + Identiv SCR3500 A Contact Reader + Identiv SCR3500 B Contact Reader + Identiv uTrust 3512 SAM slot Token + Identiv @MAXX Light2 token + Identiv @MAXX ID-1 Smart Card Reader + Identiv uTrust 3522 embd SE RFID Token + Identiv uTrust 2910 R Taglio SC Reader + Identiv SCR35xx USB Smart Card Reader + Identiv SCR3500 C Contact Reader + Identiv uTrust Token Flex + Identive CLOUD 2700 F Smart Card Reader + Identive Identive CLOUD 4500 F Dual Interface Reader + Identive Identive CLOUD 4510 F Contactless + SAM Reader + Identive Identive CLOUD 4000 F DTC + Identive CLOUD 2700 R Smart Card Reader + Identive SCT3522CC token + Identive Technologies Multi-ISO HF Reader - USB + IID AT90S064 CCID READER + IIT E.Key Crystal-1 + IIT E.Key Almaz-1C + Imprivata USB CCID + InfoCert WirelessKey + Infocrypt Token++ lite + Infocrypt HWDSSL DEVICE + InfoThink IT-102MU Reader + InfoThink IT-500U Reader + INMAX DWR18 HC + INMAX DWR18 HPC + Inside Secure VaultIC 420 Smart Object + Inside Secure VaultIC 440 Smart Object + Inside Secure VaultIC 460 Smart Object + Inside Secure AT90SCR100 + Inside Secure AT90SCR050 + Inside Secure AT90SCR200 + INSIDE Secure VaultIC 405 Smart Object + INSIDE Secure VaultIC 441 Smart Object + IonIDe Smartcard Reader + jSolutions s.r.o. Multi SIM card reader 4/8 + KACST HSID Reader + KACST HSID Reader Single Storage + KACST HSID Reader Dual Storage + KAPELSE KAP-LINK + KAPELSE KAP-Care + KAPELSE KAP-GO + KAPELSE eS-KAP-Ad + KAPELSE KAP-LINK2 + KAPELSE KAP-eCV + Kapelse Ti-Kap + Kapelse inSide + Kapelse KAP-Move + Kapsch TrafficCom USB SAM reader + KeyXentic Inc. KX906 Smart Card Reader + Kingtrust Multi-Reader + KOBIL Systems Smart Token + KOBIL Systems IDToken + KOBIL Systems mIDentity 4smart + KOBIL Systems mIDentity 4smart AES + KOBIL Systems mIDentity visual + KOBIL Systems mIDentity fullsize + KOBIL Systems mIDentity fullsize AES + KRONEGGER NFC blue Reader Platform + KRONEGGER Micro Core Platform + LDU LANDI + Ledger Nano S + Ledger Nano X + Ledger Nano SP + Ledger Nano S + Ledger Nano X + Ledger Nano S Plus + Lenovo Lenovo USB Smartcard Keyboard + Lenovo Lenovo USB Smartcard Keyboard + Lenovo Lenovo Smartcard Wired Keyboard II + Liteon HP SC Keyboard - Apollo (Liteon) + Liteon HP SC Keyboard - Apollo KR (Liteon) + Liteon HP SC Keyboard - Apollo JP (Liteon) + Macally NFC CCID eNetPad + mCore SCard-Reader + Microchip SEC1110 + Microchip SEC1210 + MK Technology KeyPass S1 + MK Technology KeyPass D1 + Morpho MSO350/MSO351 Fingerprint Sensor & SmartCard Reader + Morpho MSO1350 Fingerprint Sensor & SmartCard Reader + MSI StarReader SMART + Mulann PVT + Neowave Weneo + Neowave Weneo + Neowave Weneo + Neowave Weneo + Nitrokey Nitrokey Pro + Nitrokey Nitrokey Storage + Nitrokey Nitrokey Start + Nitrokey Nitrokey HSM + Nitrokey Nitrokey 3 + NTT Communications Corp. SCR3310-NTTCom USB SmartCard Reader + NXP Pegoda 2 N + NXP PR533 + NXP PN7462AU CCID + OBERTHUR TECHNOLOGIES ID-ONE TOKEN SLIM v2 + OCS ID-One Cosmo Card USB Smart Chip Device + OMNIKEY CardMan 1021 + OMNIKEY CardMan 4321 + OMNIKEY CardMan 5321 + OMNIKEY 5421 + OMNIKEY 6321 CLi USB + OMNIKEY AG 3121 USB + OMNIKEY AG CardMan 3621 + OMNIKEY AG CardMan 3821 + OMNIKEY AG CardMan 5121 + OMNIKEY AG CardMan 5125 + OMNIKEY AG CardMan 6121 + OMNIKEY AG 6121 USB mobile + OMNIKEY AG Smart Card Reader + Panasonic Panasonic USB Smart Card Reader 7A-Smart + Philips Semiconductors JCOP41V221 + Philips Semiconductors SmartMX Sample + PIVKey T800 + Planeta RC700-NFC CCID + Purism, SPC Librem Key + Raritan D2CIM-DVUSB VM/CCID + Regula RFID Reader + REINER SCT cyberJack go + REINER SCT tanJack Bluetooth + REINER SCT tanJack USB + REINER SCT cyberJack one + REINER SCT cyberJack RFID basis + rf IDEAS USB CCID + Rocketek RT-SCR1 + Route1 MobiKEY Fusion3 + SafeNet eToken 7300 + SafeNet eToken 5100 + SafeNet eToken 5300 + SafeNet eToken 5300 C + SafeNet eToken 5110+ FIPS + SafeNet eToken Fusion + SafeTech SafeTouch + SAFETRUST SABRE SCR + SchlumbergerSema SchlumbergerSema Cyberflex Access + SCM Microsystems Inc. SCR33x USB Smart Card Reader + SCM Microsystems Inc. SCR 335 + SCM Microsystems Inc. SCR3320 - Smart Card Reader + SCM Microsystems Inc. SCR3340 - ExpressCard54 Smart Card Reader + SCM Microsystems Inc. SCR3310 USB Smart Card Reader + SCM Microsystems Inc. SCR331-DI USB Smart Card Reader + SCM Microsystems Inc. SDI010 Smart Card Reader + SCM Microsystems Inc. SDI011 Contactless Reader + SCM Microsystems Inc. SDI011 Contactless Reader + SCM Microsystems Inc. SCL010 Contactless Reader + SCM Microsystems Inc. SCL01x Contactless Reader + Secure Device Solutions DOMINO-Key TWIN + SecuTech SecuTech Token + Sensyl SSC-NFC Reader + Sensyl SSC-HV Reader + SIMHUB pcsc reader + Sitecom Sitecom USB simcard reader MD-010 + Softforum Co., Ltd XecureHSM + SoloKeys Solo 2 + SONY FeliCa RC-S660/U + SONY FeliCa RC-S300/S + SONY FeliCa RC-S300/P + SPECINFOSYSTEMS DIAMOND token + SPECINFOSYSTEMS DIAMOND PRO token + SPECINFOSYSTEMS DIAMOND PLUS token + SPECINFOSYSTEMS DIAMOND HSM + SpringCard SpringCore + SpringCard SpringCore + SpringCard E518 + SpringCard E518 + SpringCard H518 + SpringCard H518 + SpringCard Puck + SpringCard Puck + SpringCard M519 + SpringCard M519 + SpringCard CrazyWriter + SpringCard CSB6 Basic + SpringCard CSB6 Secure + SpringCard CSB6 Ultimate + SpringCard EasyFinger Standard + SpringCard EasyFinger Ultimate + SpringCard Prox'N'Roll + SpringCard NFC'Roll + SpringCard H663 Series + SpringCard H512 Series + Spyrus Inc Rosetta USB + Spyrus Inc WorkSafe Pro + Spyrus Inc WorkSafe Pro + Spyrus Inc PocketVault P-3X + Spyrus Inc PocketVault P-3X + Sunrex HP USB Business Slim Smartcard CCID Keyboard + Swissbit Secure USB PU-50n SE/PE + SYNNIX STD200 + SYNNIX CL-2100R + Sysking MII136C + sysmocom - s.f.m.c. GmbH sysmoOCTSIM + Teridian Semiconductors TSC12xxFV.09 + Thales Shield M4 Reader + Thales RF Reader + THURSBY SOFTWARE TSS-PK1 + Tianyu Smart Card Reader + Todos Argos Mini II + Todos CX00 + TOKEN2 Molto2 + TOKEN2 MFA NFC Reader + TOPPAN FORMS CO.,LTD TC63CUT021 + ubisys 13.56MHz RFID (CCID) + udea MILKO V1. + Unicept GmbH AirID USB + Unicept GmbH AirID USB Dongle + Validy TokenA sl vt + VASCO DP905v1.1 + VASCO DIGIPASS KEY 101 + VASCO DIGIPASS KEY 860 + VASCO DIGIPASS KEY 200 + VASCO DIGIPASS KEY 860 + VASCO DIGIPASS KEY 200 + VASCO DIGIPASS KEY 202 + VASCO DIGIPASS KEY 202 + VASCO DP855 + VASCO DP865 + VASCO DIGIPASS 870 + VASCO DIGIPASS 875 + VASCO DIGIPASS 920 + VMware Virtual USB CCID + WatchCNPC USB CCID Key + Watchdata USB Key + Watchdata USB Key + Watchdata USB Key + Watchdata W5181 + WCMi SD5931 + Winbond CCID SmartCard Controller + Yubico Yubikey NEO OTP+CCID + Yubico Yubikey NEO CCID + Yubico Yubikey NEO U2F+CCID + Yubico Yubikey NEO OTP+U2F+CCID + Yubico YubiKey CCID + Yubico YubiKey OTP+CCID + Yubico YubiKey FIDO+CCID + Yubico YubiKey OTP+FIDO+CCID + ACS ACR122U PICC Interface + ActivCard ActivCard USB Reader V2 + Alcor Micro AU9540 + Athena IDProtect Flash + ATMEL VaultIC460 + Broadcom Corp 5880 + Broadcom Corp 5880 + Broadcom Corp 5880 + Broadcom Corp 58200 + Broadcom Corp 58200 + Broadcom Corp 58200 + Broadcom Corp 58200 + C3PO LTC3x USB + Feitian SCR301 + Generic Smart Card Reader Interface + Hewlett-Packard Company HP USB CCID Smartcard Keyboard + Hewlett-Packard Company HP USB Smart Card Keyboard + KEBTechnology KONA USB SmartCard + KOBIL Systems mIDentity M + KOBIL Systems mIDentity XL + O2 Micro Oz776 + O2 Micro Oz776 + Precise Biometrics Precise 250 MC + Precise Biometrics Precise 200 MC + RSA RSA SecurID (R) Authenticator + THRC Smart Card Reader + + + Copyright + This driver is protected by terms of the GNU Lesser General Public License version 2.1, or (at your option) any later version. + + + diff --git a/tools/libuFCoder-x86_64.so b/tools/libuFCoder-x86_64.so deleted file mode 100755 index f1e47a5..0000000 Binary files a/tools/libuFCoder-x86_64.so and /dev/null differ diff --git a/tools/ntag5_config_check.py b/tools/ntag5_config_check.py index fd86020..00cb324 100644 --- a/tools/ntag5_config_check.py +++ b/tools/ntag5_config_check.py @@ -1,148 +1,60 @@ #!/usr/bin/env python3 -"""Read NTAG5Link configuration via uFR Zero reader (read-only). +"""NTAG5Link configuration verification via PCSC reader. -Uses the Digital Logic uFCoder library in ISO15693 transparent mode -to send NXP custom commands for reading config registers. +Supports two reader types: +- ACR1552: Full NXP config verification via transparent ISO15693 mode +- uFR Zero (CCID): Basic tag info only (UID, EEPROM). NXP config registers + require custom commands not available through CCID. Use ACR1552 or + ntag5sensor directly for full config verification. -Supports multiple tags in the field: runs INVENTORY first, then -reads each tag using addressed mode. - -Usage: python3 ntag5_config_check.py [/dev/ttyUSBx] +Usage: python3 ntag5_config_check.py """ -import ctypes import sys import os -# --------------------------------------------------------------------------- -# Load uFCoder library -# --------------------------------------------------------------------------- - -SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) -LIB_PATH = os.path.join(SCRIPT_DIR, "libuFCoder-x86_64.so") - -if not os.path.exists(LIB_PATH): - print(f"ERROR: uFCoder library not found at {LIB_PATH}") - sys.exit(1) - -ufr = ctypes.cdll.LoadLibrary(LIB_PATH) - -# --------------------------------------------------------------------------- -# uFCoder function signatures -# --------------------------------------------------------------------------- - -ufr.ReaderOpenEx.argtypes = [ctypes.c_uint32, ctypes.c_char_p, ctypes.c_uint32, ctypes.c_char_p] -ufr.ReaderOpenEx.restype = ctypes.c_uint32 - -ufr.ReaderClose.argtypes = [] -ufr.ReaderClose.restype = ctypes.c_uint32 - -ufr.card_transceive_mode_start.argtypes = [ctypes.c_uint8, ctypes.c_uint8, ctypes.c_uint32, ctypes.c_uint32] -ufr.card_transceive_mode_start.restype = ctypes.c_uint32 - -ufr.card_transceive_mode_stop.argtypes = [] -ufr.card_transceive_mode_stop.restype = ctypes.c_uint32 - -ufr.uart_transceive.argtypes = [ - ctypes.POINTER(ctypes.c_uint8), # send_data - ctypes.c_uint8, # send_len - ctypes.POINTER(ctypes.c_uint8), # rcv_data - ctypes.c_uint32, # bytes_to_receive - ctypes.POINTER(ctypes.c_uint32), # rcv_len -] -ufr.uart_transceive.restype = ctypes.c_uint32 - -# UFR status codes -UFR_OK = 0 - -# --------------------------------------------------------------------------- -# ISO15693 flags and commands -# --------------------------------------------------------------------------- - -# Request flags -ISO_FLAG_SUB_CARRIER = (1 << 0) -ISO_FLAG_DATA_RATE = (1 << 1) -ISO_FLAG_INVENTORY = (1 << 2) -ISO_FLAG_PROTOCOL_EXT = (1 << 3) -# When INVENTORY flag is NOT set: -ISO_FLAG_SELECT = (1 << 4) -ISO_FLAG_ADDRESS = (1 << 5) -ISO_FLAG_OPTION = (1 << 6) -# When INVENTORY flag IS set: -ISO_FLAG_AFI = (1 << 4) -ISO_FLAG_NB_SLOTS_1 = (1 << 5) # single slot - -# Standard ISO15693 commands -ISO_CMD_INVENTORY = 0x01 -ISO_CMD_STAY_QUIET = 0x02 -ISO_CMD_SYSTEM_INFO = 0x2B - -# NXP custom commands -NXP_CMD_SYSTEM_INFO = 0xAB -NXP_CMD_READ_CONFIG = 0xC0 -NXP_CMD_MANUF_CODE_NXP = 0x04 +from smartcard.System import readers +from smartcard.CardRequest import CardRequest +from smartcard.CardType import AnyCardType +from smartcard.CardConnection import CardConnection # --------------------------------------------------------------------------- # NXP NTAG5Link config constants (from ntag5sensor) # --------------------------------------------------------------------------- # Config addresses -NXP_CONFIG_ADDR_CONFIG = 0x37 +NXP_CONFIG_ADDR_CONFIG = 0x37 NXP_CONFIG_ADDR_EH_CONFIG = 0x3D # Config byte 0 flags -NXP_CONFIG_0_AUTO_STANDBY_MODE_EN = (1 << 0) -NXP_CONFIG_0_LOCK_SESSION_REG = (1 << 1) -NXP_CONFIG_0_EH_MODE_MASK = (3 << 2) -NXP_CONFIG_0_EH_MODE_LOW_FIELD_STRENGTH = (2 << 2) -NXP_CONFIG_0_EH_MODE_HIGH_FIELD_STRENGTH = (3 << 2) -NXP_CONFIG_0_SRAM_COPY_EN = (1 << 7) +NXP_CONFIG_0_EH_MODE_MASK = (3 << 2) # Config byte 1 flags -NXP_CONFIG_1_PT_TRANSFER_DIR = (1 << 0) -NXP_CONFIG_1_SRAM_ENABLE = (1 << 1) -NXP_CONFIG_1_ARBITER_MASK = (3 << 2) -NXP_CONFIG_1_ARBITER_MODE_SRAM_PASSTHROUGH = (2 << 2) -NXP_CONFIG_1_USE_CASE_MASK = (3 << 4) -NXP_CONFIG_1_USE_CASE_CONF_I2C_SLAVE = (0 << 4) -NXP_CONFIG_1_EH_ARBITER_MODE_EN = (1 << 7) - -# Config byte 2 flags -NXP_CONFIG_2_GPIO0_SLEW_RATE = (1 << 0) -NXP_CONFIG_2_GPIO1_SLEW_RATE = (1 << 1) -NXP_CONFIG_2_LOCK_BLOCK_COMMAND_SUPPORTED = (1 << 2) -NXP_CONFIG_2_EXTENDED_COMMANDS_SUPPORTED = (1 << 3) -NXP_CONFIG_2_GPIO0_PAD_MASK = (3 << 4) -NXP_CONFIG_2_GPIO1_PAD_MASK = (3 << 6) +NXP_CONFIG_1_SRAM_ENABLE = (1 << 1) +NXP_CONFIG_1_ARBITER_MASK = (3 << 2) +NXP_CONFIG_1_USE_CASE_MASK = (3 << 4) # EH config flags -NXP_EH_CONFIG_EH_ENABLE = (1 << 0) -NXP_EH_CONFIG_VOUT_V_MASK = (3 << 1) -NXP_EH_CONFIG_DISABLE_POWER_CHECK = (1 << 3) -NXP_EH_CONFIG_VOUT_I_MASK = (7 << 4) +NXP_EH_CONFIG_EH_ENABLE = (1 << 0) +NXP_EH_CONFIG_VOUT_V_MASK = (3 << 1) # Lookup tables +EH_MODE_MAP = {0: "rfu0", 1: "rfu1", 2: "low_field_strength", 3: "high_field_strength"} +ARBITER_MAP = {0: "normal", 1: "sram_mirror", 2: "sram_passthrough", 3: "sram_phdc"} +USE_CASE_MAP = {0: "i2c_slave", 1: "i2c_master", 2: "gpio_pwm", 3: "tristate"} EH_V_SEL = {0: "1.8V", 1: "2.4V", 2: "3.0V", 3: "RFU"} EH_I_SEL = {0: "0.4mA", 1: "0.6mA", 2: "1.4mA", 3: "2.7mA", 4: "4.0mA", 5: "6.5mA", 6: "9.0mA", 7: "12.5mA"} - -ARBITER_MAP = {0: "normal", 1: "sram_mirror", 2: "sram_passthrough", 3: "sram_phdc"} -USE_CASE_MAP = {0: "i2c_slave", 1: "i2c_master", 2: "gpio_pwm", 3: "tristate"} -GPIO_PAD_MAP = {0: "disabled", 1: "plain_pullup", 2: "plain", 3: "plain_pulldown"} -EH_MODE_MAP = {0: "rfu0", 1: "rfu1", 2: "low_field_strength", 3: "high_field_strength"} ED_CONFIG_MAP = {0: "disable", 1: "nfc_field_detect", 2: "pwm", 3: "i2c_to_nfc_passthrough", 4: "nfc_to_i2c_passthrough", 5: "arbiter_lock", 6: "ndef_msg_tlv_length", 7: "standby_mode", 8: "write_cmd_indication", 9: "read_cmd_indication", 10: "start_of_command_indication", 11: "read_from_synch_block", 12: "write_to_synch_block", 13: "software_interrupt"} - +GPIO_PAD_MAP = {0: "disabled", 1: "plain_pullup", 2: "plain", 3: "plain_pulldown"} INTERFACE_MAP = {0: "nfc_only", 1: "gpio", 2: "rfu", 3: "gpio_i2c"} -# --------------------------------------------------------------------------- # xblink expected configuration -# --------------------------------------------------------------------------- - EXPECTED = { "eh_mode": "low_field_strength", "use_case": "i2c_slave", @@ -153,206 +65,223 @@ EXPECTED = { } # --------------------------------------------------------------------------- -# Low-level transceive +# PCSC helpers # --------------------------------------------------------------------------- -def iso15693_transceive(cmd_bytes, expected_response_len=32): - """Send raw ISO15693 command and return response bytes (flags stripped).""" - cmd = (ctypes.c_uint8 * len(cmd_bytes))(*cmd_bytes) - rcv = (ctypes.c_uint8 * 256)() - rcv_len = ctypes.c_uint32(0) +def hex_str(data): + return ' '.join(f'{b:02X}' for b in data) - status = ufr.uart_transceive(cmd, len(cmd_bytes), rcv, expected_response_len, ctypes.byref(rcv_len)) - if status != UFR_OK: - if rcv_len.value > 0: - data = bytes(rcv[:rcv_len.value]) - if data[0] == 0x00: # Response flags OK - return data[1:] - else: - print(f" WARNING: Response flags = 0x{data[0]:02X} (error)") - return data[1:] - print(f" ERROR: uart_transceive failed, status=0x{status:04X}, rcv_len={rcv_len.value}") - return None +def transmit(conn, apdu): + """Send APDU, return (data, sw1, sw2). Raises on transmit error.""" + res, sw1, sw2 = conn.transmit(list(apdu)) + return bytes(res), sw1, sw2 - data = bytes(rcv[:rcv_len.value]) - if len(data) > 0 and data[0] != 0x00: - print(f" WARNING: Response flags = 0x{data[0]:02X}") - return data[1:] if len(data) > 1 else data + +def pcsc_read_binary(conn, block, le=0x00): + """Read a single ISO15693 block via PCSC READ_BINARY.""" + try: + data, sw1, sw2 = transmit(conn, [0xFF, 0xB0, 0x00, block, le]) + if sw1 == 0x90 and sw2 == 0x00: + return data + except Exception: + pass + return None # --------------------------------------------------------------------------- -# ISO15693 inventory +# ACR1552 transparent ISO15693 support # --------------------------------------------------------------------------- -def inventory(): - """Run ISO15693 INVENTORY to discover all tags in the field. +def acr1552_available(): + """Check if ber_tlv is installed (required for ACR1552 transparent mode).""" + try: + from ber_tlv.tlv import Tlv + return True + except ImportError: + return False - Returns list of 8-byte UIDs (as bytes, LSB first as received on air). + +class ACR1552ISO15693: + """ACR1552 reader with transparent ISO15693 mode. + + Adapted from ntag5sensor/reader/acr1552.py. """ - # Single-slot inventory: flags = data_rate | inventory | single_slot - flags = ISO_FLAG_DATA_RATE | ISO_FLAG_INVENTORY | ISO_FLAG_NB_SLOTS_1 - cmd = [flags, ISO_CMD_INVENTORY, 0x00] # mask_length=0 (no mask) - # Response: flags(1) + DSFID(1) + UID(8) = 10 bytes - data = iso15693_transceive(cmd, 10) - if data is None: - return [] + CLA_PSEUDO = 0xFF + INS_TRANS = 0xC2 + TLV_TAG_ERROR = 0xC0 + TLV_TAG_CMD_DATA = 0x95 + TLV_TAG_CMD_TIMEOUT = 0x5F46 + TLV_TAG_CMD_FWTI = 0xFF6E + TLV_TAG_RESP_STATUS = 0x96 + TLV_TAG_RESP_FRAMING = 0x92 + TLV_TAG_RESP_DATA = 0x97 - uids = [] - if len(data) >= 9: - dsfid = data[0] - uid = data[1:9] # 8 bytes, LSB first - uids.append(uid) + def __init__(self, conn): + from ber_tlv.tlv import Tlv + self.Tlv = Tlv + self.conn = conn + # Begin transparent session + self._transmit_pseudo(0x00, bytes([0x81, 0x00])) + # Switch to ISO15693 L3 + self._transmit_pseudo(0x02, bytes([0x8F, 0x02, 0x02, 0x03])) - # TODO: for multi-slot (16 slots), would iterate and collect multiple UIDs. - # Single-slot works when only one tag is present; with multiple tags, - # collisions occur and we'd need anti-collision. For now this covers - # the primary use case (one NTAG5 Click on the reader). - return uids + def disconnect(self): + self._transmit_pseudo(0x00, bytes([0x82, 0x00])) -# --------------------------------------------------------------------------- -# Addressed config reads -# --------------------------------------------------------------------------- + def _transmit_pseudo(self, function, data): + apdu = bytes([self.CLA_PSEUDO, self.INS_TRANS, 0x00, function, + len(data)]) + data + bytes([0x00]) + res, sw1, sw2 = self.conn.transmit(list(apdu)) + if sw1 != 0x90 or sw2 != 0x00: + raise Exception(f"PCSC error: {sw1:02X}{sw2:02X}") + tlv = dict(self.Tlv.parse(bytes(res))) + error = tlv[self.TLV_TAG_ERROR] + if error[0] != 0x00 or error[1] != 0x90 or error[2] != 0x00: + raise Exception(f"Transparent error: {hex_str(error)}") + return tlv -def read_config_block_addressed(uid, address, num_blocks=1): - """Read NTAG5Link config block via NXP READ_CONFIG in addressed mode. - - Args: - uid: 8-byte UID (LSB first, as returned by inventory) - address: config block address (e.g. 0x37) - num_blocks: number of 4-byte blocks to read - """ - flags = ISO_FLAG_DATA_RATE | ISO_FLAG_ADDRESS - cmd = [flags, NXP_CMD_READ_CONFIG, NXP_CMD_MANUF_CODE_NXP] - cmd.extend(uid) # 8-byte UID - cmd.extend([address, num_blocks - 1]) - # Response: 1 byte flags + num_blocks * 4 bytes data - return iso15693_transceive(cmd, 1 + num_blocks * 4) + def transmit_iso15693(self, cmd_data): + """Send raw ISO15693 command, return response data (flags stripped).""" + tlv_data = self.Tlv.build([ + (self.TLV_TAG_CMD_TIMEOUT, (1000000).to_bytes(4, "big")), + (self.TLV_TAG_CMD_FWTI, bytes([0x03, 0x01, 15])), + (self.TLV_TAG_CMD_DATA, bytes(cmd_data)), + ]) + tlv = self._transmit_pseudo(0x01, tlv_data) + data = tlv[self.TLV_TAG_RESP_DATA] + if data[0] & 0x01: # Error flag + raise Exception(f"ISO15693 error: {data[1]:02X}") + return data[1:] # Strip flags byte -def read_config_block_nonaddressed(address, num_blocks=1): - """Read NTAG5Link config block in non-addressed mode (single tag only).""" - cmd = [ISO_FLAG_DATA_RATE, NXP_CMD_READ_CONFIG, NXP_CMD_MANUF_CODE_NXP, - address, num_blocks - 1] - return iso15693_transceive(cmd, 1 + num_blocks * 4) +def acr1552_read_config(reader, address, num_blocks=1): + """Read NXP config block via ACR1552 transparent mode.""" + cmd = bytes([0x02, 0xC0, 0x04, address, num_blocks - 1]) + return reader.transmit_iso15693(cmd) -def get_system_info_addressed(uid): - """Get ISO15693 system info in addressed mode.""" - flags = ISO_FLAG_DATA_RATE | ISO_FLAG_ADDRESS - cmd = [flags, ISO_CMD_SYSTEM_INFO] - cmd.extend(uid) - # Response varies; request generous buffer - return iso15693_transceive(cmd, 32) +def acr1552_get_system_info(reader): + """Get ISO15693 system info via ACR1552.""" + cmd = bytes([0x02, 0x2B]) + return reader.transmit_iso15693(cmd) -def get_nxp_system_info_addressed(uid): - """Get NXP-specific system info in addressed mode.""" - flags = ISO_FLAG_DATA_RATE | ISO_FLAG_ADDRESS - cmd = [flags, NXP_CMD_SYSTEM_INFO, NXP_CMD_MANUF_CODE_NXP] - cmd.extend(uid) - return iso15693_transceive(cmd, 32) +def acr1552_get_nxp_info(reader): + """Get NXP system info via ACR1552.""" + cmd = bytes([0x02, 0xAB, 0x04]) + return reader.transmit_iso15693(cmd) # --------------------------------------------------------------------------- # Decoders # --------------------------------------------------------------------------- -def decode_uid(uid_bytes): - """Format UID for display (MSB first, colon-separated).""" - return ":".join(f"{b:02X}" for b in reversed(uid_bytes)) +def decode_uid_from_pcsc(uid_bytes): + """Format UID from PCSC GET_DATA response (already in NFC byte order).""" + uid_reversed = bytes(reversed(uid_bytes)) + return ":".join(f"{b:02X}" for b in uid_reversed) def decode_system_info(data): """Decode ISO15693 GET_SYSTEM_INFO response.""" if data is None or len(data) < 9: return None - info_flags = data[0] - uid = data[1:9][::-1] # reverse to MSB first - res = {"uid": uid, "uid_str": ":".join(f"{b:02X}" for b in uid)} - + uid = data[1:9][::-1] + res = {"uid_str": ":".join(f"{b:02X}" for b in uid)} idx = 9 - if info_flags & 0x01 and idx < len(data): # DSFID - res["dsfid"] = data[idx] - idx += 1 - if info_flags & 0x02 and idx < len(data): # AFI - res["afi"] = data[idx] - idx += 1 - if info_flags & 0x04 and idx + 1 < len(data): # Memory size + if info_flags & 0x01 and idx < len(data): + res["dsfid"] = data[idx]; idx += 1 + if info_flags & 0x02 and idx < len(data): + res["afi"] = data[idx]; idx += 1 + if info_flags & 0x04 and idx + 1 < len(data): res["num_blocks"] = data[idx] + 1 res["block_size"] = (data[idx + 1] & 0x1F) + 1 res["memory_bytes"] = res["num_blocks"] * res["block_size"] idx += 2 - if info_flags & 0x08 and idx < len(data): # IC reference - res["ic_ref"] = data[idx] - idx += 1 - + if info_flags & 0x08 and idx < len(data): + res["ic_ref"] = data[idx]; idx += 1 return res -def decode_nxp_system_info(data): +def decode_nxp_info(data): """Decode NXP GET_NXP_SYSTEM_INFO response.""" if data is None or len(data) < 7: return None - - res = { + b3 = data[6] + return { "pp_pointer": data[0], - "lock_bits_raw": data[2], + "interface": INTERFACE_MAP.get((b3 >> 5) & 0x03, "unknown"), + "num_keys": b3 & 0x0F, } - if len(data) >= 7: - b3 = data[6] - res["interface"] = INTERFACE_MAP.get((b3 >> 5) & 0x03, "unknown") - res["num_keys"] = b3 & 0x0F - - return res - def decode_config(data): - """Decode CONFIG block (address 0x37) — 4 bytes.""" + """Decode CONFIG block (NXP config address 0x37) — 4 bytes.""" if data is None or len(data) < 3: return None - b0, b1, b2 = data[0], data[1], data[2] return { "raw": [f"0x{b:02X}" for b in data[:4]] if len(data) >= 4 else [f"0x{b:02X}" for b in data], - "auto_standby": bool(b0 & NXP_CONFIG_0_AUTO_STANDBY_MODE_EN), - "lock_session_reg": bool(b0 & NXP_CONFIG_0_LOCK_SESSION_REG), + "auto_standby": bool(b0 & 0x01), + "lock_session_reg": bool(b0 & 0x02), "eh_mode": EH_MODE_MAP.get((b0 >> 2) & 0x03, "unknown"), - "sram_copy_en": bool(b0 & NXP_CONFIG_0_SRAM_COPY_EN), - "pt_transfer_dir": "reader_to_tag" if (b1 & NXP_CONFIG_1_PT_TRANSFER_DIR) else "tag_to_reader", - "sram_enabled": bool(b1 & NXP_CONFIG_1_SRAM_ENABLE), + "sram_copy_en": bool(b0 & 0x80), + "pt_transfer_dir": "reader_to_tag" if (b1 & 0x01) else "tag_to_reader", + "sram_enabled": bool(b1 & 0x02), "arbiter_mode": ARBITER_MAP.get((b1 >> 2) & 0x03, "unknown"), "use_case": USE_CASE_MAP.get((b1 >> 4) & 0x03, "unknown"), - "eh_arbiter_mode_en": bool(b1 & NXP_CONFIG_1_EH_ARBITER_MODE_EN), - "gpio0_slew_fast": bool(b2 & NXP_CONFIG_2_GPIO0_SLEW_RATE), - "gpio1_slew_fast": bool(b2 & NXP_CONFIG_2_GPIO1_SLEW_RATE), - "lock_block_supported": bool(b2 & NXP_CONFIG_2_LOCK_BLOCK_COMMAND_SUPPORTED), - "ext_commands_supported": bool(b2 & NXP_CONFIG_2_EXTENDED_COMMANDS_SUPPORTED), + "eh_arbiter_mode_en": bool(b1 & 0x80), + "gpio0_slew_fast": bool(b2 & 0x01), + "gpio1_slew_fast": bool(b2 & 0x02), + "lock_block_supported": bool(b2 & 0x04), + "ext_commands_supported": bool(b2 & 0x08), "gpio0_pad_in": GPIO_PAD_MAP.get((b2 >> 4) & 0x03, "unknown"), "gpio1_pad_in": GPIO_PAD_MAP.get((b2 >> 6) & 0x03, "unknown"), } def decode_eh_config(data): - """Decode EH/ED CONFIG block (address 0x3D) — 4 bytes.""" + """Decode EH/ED CONFIG block (NXP config address 0x3D) — 4 bytes.""" if data is None or len(data) < 1: return None - eh = data[0] ed = data[2] if len(data) > 2 else 0 - return { "raw": [f"0x{b:02X}" for b in data[:4]] if len(data) >= 4 else [f"0x{b:02X}" for b in data], - "eh_enable": bool(eh & NXP_EH_CONFIG_EH_ENABLE), + "eh_enable": bool(eh & 0x01), "eh_voltage": EH_V_SEL.get((eh >> 1) & 0x03, "unknown"), - "disable_power_check": bool(eh & NXP_EH_CONFIG_DISABLE_POWER_CHECK), + "disable_power_check": bool(eh & 0x08), "eh_current": EH_I_SEL.get((eh >> 4) & 0x07, "unknown"), "ed_config": ED_CONFIG_MAP.get(ed & 0x0F, "unknown"), } -# --------------------------------------------------------------------------- -# Verification -# --------------------------------------------------------------------------- + +def print_config(config): + """Display CONFIG block contents.""" + print(f" Raw bytes: {config['raw']}") + print(f" EH mode: {config['eh_mode']}") + print(f" Use case: {config['use_case']}") + print(f" SRAM enabled: {config['sram_enabled']}") + print(f" Arbiter mode: {config['arbiter_mode']}") + print(f" Auto standby: {config['auto_standby']}") + print(f" SRAM copy: {config['sram_copy_en']}") + print(f" PT transfer dir: {config['pt_transfer_dir']}") + print(f" EH arbiter mode: {config['eh_arbiter_mode_en']}") + print(f" GPIO0 pad: {config['gpio0_pad_in']}") + print(f" GPIO1 pad: {config['gpio1_pad_in']}") + print(f" Lock block: {config['lock_block_supported']}") + print(f" Ext commands: {config['ext_commands_supported']}") + + +def print_eh_config(eh_config): + """Display EH/ED CONFIG block contents.""" + print(f" Raw bytes: {eh_config['raw']}") + print(f" EH enable: {eh_config['eh_enable']}") + print(f" EH voltage: {eh_config['eh_voltage']}") + print(f" EH current limit: {eh_config['eh_current']}") + print(f" Power check: {'disabled' if eh_config['disable_power_check'] else 'enabled'}") + print(f" ED config: {eh_config['ed_config']}") + def check_against_expected(config, eh_config): """Compare config against xblink expected values.""" @@ -365,7 +294,6 @@ def check_against_expected(config, eh_config): ("EH enable", eh_config.get("eh_enable"), EXPECTED["eh_enable"]), ("EH voltage", eh_config.get("eh_voltage"), EXPECTED["eh_voltage"]), ] - all_ok = True for name, actual, expected in checks: match = actual == expected @@ -377,153 +305,185 @@ def check_against_expected(config, eh_config): if all_ok: print("\n All config values match xblink requirements!") else: - print("\n Some values need updating. Use ntag5sensor tooling to reconfigure.") - + print("\n Some values need updating. Use ntag5sensor + ACR1552 to reconfigure.") return all_ok +# --------------------------------------------------------------------------- +# Reader-specific flows +# --------------------------------------------------------------------------- -def read_and_check_tag(uid): - """Read all config from one tag and verify against xblink requirements. +def run_with_acr1552(conn): + """Full config verification via ACR1552 transparent ISO15693 mode.""" + print("Initializing ACR1552 transparent ISO15693 mode...") + reader = ACR1552ISO15693(conn) - Args: - uid: 8-byte UID (LSB first) or None for non-addressed mode - """ - use_addressed = uid is not None - uid_str = decode_uid(uid) if uid else "(non-addressed)" - - # --- ISO15693 system info --- - if use_addressed: - print(f"\nISO15693 System Info...") - sys_data = get_system_info_addressed(uid) + try: + # System info + print("\nISO15693 System Info...") + sys_data = acr1552_get_system_info(reader) sys_info = decode_system_info(sys_data) if sys_info: + print(f" UID: {sys_info['uid_str']}") print(f" Memory: {sys_info.get('memory_bytes', '?')} bytes " - f"({sys_info.get('num_blocks', '?')} blocks x {sys_info.get('block_size', '?')} bytes)") + f"({sys_info.get('num_blocks', '?')} x {sys_info.get('block_size', '?')})") if "ic_ref" in sys_info: print(f" IC ref: 0x{sys_info['ic_ref']:02X}") - else: - print(" Failed to read system info.") - # --- NXP system info --- - if use_addressed: - print(f"\nNXP System Info...") - nxp_data = get_nxp_system_info_addressed(uid) - nxp_info = decode_nxp_system_info(nxp_data) + # NXP system info + print("\nNXP System Info...") + nxp_data = acr1552_get_nxp_info(reader) + nxp_info = decode_nxp_info(nxp_data) if nxp_info: - print(f" Interface: {nxp_info.get('interface', '?')}") - print(f" Num keys: {nxp_info.get('num_keys', '?')}") + print(f" Interface: {nxp_info['interface']}") + print(f" Num keys: {nxp_info['num_keys']}") + + # CONFIG block + print("\nCONFIG block (0x37)...") + config_data = acr1552_read_config(reader, NXP_CONFIG_ADDR_CONFIG) + config = decode_config(config_data) + if config: + print_config(config) + + # EH/ED CONFIG block + print("\nEH/ED CONFIG block (0x3D)...") + eh_data = acr1552_read_config(reader, NXP_CONFIG_ADDR_EH_CONFIG) + eh_config = decode_eh_config(eh_data) + if eh_config: + print_eh_config(eh_config) + + # Verify + if config and eh_config: + check_against_expected(config, eh_config) + finally: + reader.disconnect() + + +def run_with_generic_pcsc(conn, reader_name): + """Basic tag info via standard PCSC commands (uFR Zero, etc). + + READ_BINARY maps to ISO15693 READ_SINGLE_BLOCK — can only read user + EEPROM, not NXP config registers (which require custom command 0xC0). + """ + # Get UID + print("\nTag UID...") + try: + data, sw1, sw2 = transmit(conn, [0xFF, 0xCA, 0x00, 0x00, 0x00]) + except Exception as e: + print(f" Failed: {e}") + return + if sw1 == 0x90: + print(f" UID: {decode_uid_from_pcsc(data)}") + print(f" Raw: {hex_str(data)}") + # Check if NXP ISO15693 tag (E0 04 prefix in reversed UID) + uid_rev = bytes(reversed(data)) + if len(uid_rev) >= 2 and uid_rev[0] == 0xE0 and uid_rev[1] == 0x04: + print(f" Type: NXP ISO15693 (manufacturer code 0x04)") else: - print(" Failed to read NXP system info (may not be an NXP tag).") - - # --- CONFIG block (0x37) --- - print(f"\nCONFIG block (0x37)...") - if use_addressed: - config_data = read_config_block_addressed(uid, NXP_CONFIG_ADDR_CONFIG) + print(f" Type: ISO15693 (manufacturer code 0x{uid_rev[1]:02X})") else: - config_data = read_config_block_nonaddressed(NXP_CONFIG_ADDR_CONFIG) - config = decode_config(config_data) + print(f" Failed to read UID: SW={sw1:02X}{sw2:02X}") - if config: - print(f" Raw bytes: {config['raw']}") - print(f" EH mode: {config['eh_mode']}") - print(f" Use case: {config['use_case']}") - print(f" SRAM enabled: {config['sram_enabled']}") - print(f" Arbiter mode: {config['arbiter_mode']}") - print(f" Auto standby: {config['auto_standby']}") - print(f" SRAM copy: {config['sram_copy_en']}") - print(f" PT transfer dir: {config['pt_transfer_dir']}") - print(f" EH arbiter mode: {config['eh_arbiter_mode_en']}") - print(f" GPIO0 pad: {config['gpio0_pad_in']}") - print(f" GPIO1 pad: {config['gpio1_pad_in']}") - print(f" Lock block: {config['lock_block_supported']}") - print(f" Ext commands: {config['ext_commands_supported']}") + # Read EEPROM block 0 (Capability Container) + print("\nEEPROM Block 0 (Capability Container)...") + data = pcsc_read_binary(conn, 0x00) + if data: + print(f" Data: {hex_str(data)}") + if len(data) >= 4 and data[0] == 0xE1: + print(f" NDEF formatted: version {data[1] >> 4}.{data[1] & 0x0F}, " + f"max size {data[2] * 8} bytes") else: - print(" Failed to read config block.") + print(" Failed to read block 0") - # --- EH/ED CONFIG block (0x3D) --- - print(f"\nEH/ED CONFIG block (0x3D)...") - if use_addressed: - eh_data = read_config_block_addressed(uid, NXP_CONFIG_ADDR_EH_CONFIG) - else: - eh_data = read_config_block_nonaddressed(NXP_CONFIG_ADDR_EH_CONFIG) - eh_config = decode_eh_config(eh_data) + # Read a few EEPROM blocks to verify access + print("\nEEPROM Blocks 1-4 (user data)...") + for block in range(1, 5): + data = pcsc_read_binary(conn, block) + if data: + print(f" Block 0x{block:02X}: {hex_str(data)}") + else: + print(f" Block 0x{block:02X}: read failed") - if eh_config: - print(f" Raw bytes: {eh_config['raw']}") - print(f" EH enable: {eh_config['eh_enable']}") - print(f" EH voltage: {eh_config['eh_voltage']}") - print(f" EH current limit: {eh_config['eh_current']}") - print(f" Power check: {'disabled' if eh_config['disable_power_check'] else 'enabled'}") - print(f" ED config: {eh_config['ed_config']}") - else: - print(" Failed to read EH config block.") - - # --- Check against xblink expected values --- - if config and eh_config: - return check_against_expected(config, eh_config) - return False + # Report limitation + print("\n" + "=" * 60) + print("LIMITATION: NXP config registers cannot be read") + print("=" * 60) + print(f"\nReader '{reader_name}' only supports standard PCSC commands,") + print("which map to ISO15693 READ_SINGLE_BLOCK for user EEPROM.") + print("NXP config registers (CONFIG 0x37, EH_CONFIG 0x3D) require") + print("the NXP custom READ_CONFIG command (0xC0), which needs a") + print("reader with transparent ISO15693 passthrough support.") + print("\nTo verify NTAG5Link config for xblink:") + print(" 1. Use ACR1552 reader with this tool (auto-detected)") + print(" 2. Use ntag5sensor directly: python3 -m ntag5sensor info") + print(" 3. Use NXP TagInfo app on Android") # --------------------------------------------------------------------------- # Main # --------------------------------------------------------------------------- def main(): - port = sys.argv[1] if len(sys.argv) > 1 else "/dev/ttyUSB0" - print(f"Connecting to uFR Zero on {port}...") - - # Open reader: reader_type=1 (uFR @ 1Mbps), port_interface=1 (serial/CP210x) - status = ufr.ReaderOpenEx(1, port.encode(), 1, None) - if status != UFR_OK: - print(f"ERROR: Failed to open reader, status=0x{status:04X}") + reader_list = readers() + if not reader_list: + print("No PCSC readers found. Is pcscd running?") sys.exit(1) - import time - time.sleep(0.5) - print("Reader connected.") + print("Available readers:") + for i, r in enumerate(reader_list): + print(f" [{i}] {r}") - # Enter ISO15693 transparent mode (tx_crc=1, rx_crc=1, rf_timeout=10000us, uart_timeout=500ms) - status = ufr.card_transceive_mode_start(1, 1, 10000, 500) - if status != UFR_OK: - print(f"ERROR: Failed to enter transceive mode, status=0x{status:04X}") - ufr.ReaderClose() - sys.exit(1) + # Detect reader type + acr1552_reader = None + any_reader = None + for r in reader_list: + name = str(r) + if name.startswith("ACS ACR1552"): + acr1552_reader = r + break + if any_reader is None: + any_reader = r - print("ISO15693 transparent mode active.") + target_reader = acr1552_reader or any_reader + reader_name = str(target_reader) + is_acr1552 = acr1552_reader is not None - # --- Step 1: Inventory --- - print("\n=== ISO15693 Inventory ===") - uids = inventory() - - if not uids: - print(" No tags found. Trying non-addressed mode as fallback...") - print("\n=== Tag (non-addressed) ===") - read_and_check_tag(None) + print(f"\nUsing: {reader_name}") + if is_acr1552: + if not acr1552_available(): + print("ERROR: ber_tlv package required for ACR1552. Install: pip install ber-tlv") + sys.exit(1) + print("Mode: ACR1552 transparent ISO15693 (full NXP config access)") else: - print(f" Found {len(uids)} tag(s):") - for i, uid in enumerate(uids): - print(f" [{i}] UID: {decode_uid(uid)}") + print("Mode: Standard PCSC (basic tag info only)") - # --- Step 2: Read each tag --- - all_ok = True - for i, uid in enumerate(uids): - print(f"\n{'='*50}") - print(f"=== Tag {i}: {decode_uid(uid)} ===") - print(f"{'='*50}") - ok = read_and_check_tag(uid) - if not ok: - all_ok = False + # Connect to tag + print("\nWaiting for tag...") + request = CardRequest(timeout=30, readers=[target_reader], cardType=AnyCardType()) + try: + card = request.waitforcard() + except Exception as e: + print(f"No tag found: {e}") + sys.exit(1) - if len(uids) > 1: - print(f"\n{'='*50}") - if all_ok: - print("All tags pass xblink config check.") - else: - print("Some tags have config mismatches.") + card.connection.connect() + atr = bytes(card.connection.getATR()) + print(f"Connected! ATR: {hex_str(atr)}") - # Cleanup - ufr.card_transceive_mode_stop() - ufr.ReaderClose() - print("\nReader closed.") + # Verify ISO15693 tag (check ATR for 03 06 0B = ISO 15693 Part 3) + if b'\x03\x06\x0B' in atr: + print("Tag type: ISO 15693") + elif b'\x03\x06\x03' in atr: + print("Tag type: ISO 14443-3A") + else: + print("Tag type: unknown (continuing anyway)") + + # Run appropriate flow + if is_acr1552: + run_with_acr1552(card.connection) + else: + run_with_generic_pcsc(card.connection, reader_name) + + card.connection.disconnect() + print("\nDone.") if __name__ == "__main__":