/** * NFC utils - lsnfc * * Copyright (C) 2009, 2010, Romuald Conty * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see */ /* * This implementation was written based on information provided by the * following documents: * * MIFARE Type Identification Procedure * AN10833 * Rev. 3.1 — 07 July 2009 * Application note * * ISO14443 tags list * http://www.libnfc.org/documentation/hardware/tags/iso14443 */ #include #include #include #include #include static nfc_device_t *pnd; static byte_t abtFelica[5] = { 0x00, 0xff, 0xff, 0x00, 0x00 }; #define ERR(x, ...) printf("ERROR: " x "\n", __VA_ARGS__ ) #define MAX_DEVICE_COUNT 16 #define MAX_ATS_LENGTH 32 typedef const char* (*identication_hook)(const nfc_target_info_t nti); struct iso14443a_tag { uint8_t SAK; const char *name; size_t ATS_length; uint8_t ATS[MAX_ATS_LENGTH]; identication_hook identication_fct; }; /* Theses values comes from http://www.libnfc.org/documentation/hardware/tags/iso14443 Manufacturer Product ATQA SAK ATS (called ATR for contact smartcards) NXP MIFARE Mini 00 04 09 MIFARE Classic 1K 00 04 08 MIFARE Classic 4K 00 02 18 MIFARE Ultralight 00 44 00 MIFARE DESFire 03 44 20 06 75 77 81 02 80 MIFARE DESFire EV1 03 44 20 06 75 77 81 02 80 JCOP31 03 04 28 38 77 b1 4a 43 4f 50 33 31 JCOP31 v2.4.1 00 48 20 78 77 b1 02 4a 43 4f 50 76 32 34 31 JCOP41 v2.2 00 48 20 38 33 b1 4a 43 4f 50 34 31 56 32 32 JCOP41 v2.3.1 00 04 28 38 33 b1 4a 43 4f 50 34 31 56 32 33 31 Infineon MIFARE Classic 1K 00 04 88 Gemplus MPCOS 00 02 98 Innovision R&T Jewel 0C 00 */ void print_hex (byte_t * pbtData, size_t szData) { for (size_t i = 0; i < szData; i++) { printf ("%02x", pbtData[i]); } } const char* mifare_ultralight_identification(const nfc_target_info_t nti) { byte_t abtCmd[2]; byte_t abtRx[265]; size_t szRxLen; abtCmd[0] = 0x30; // MIFARE Ultralight READ command abtCmd[1] = 0x10; // block address (1K=0x00..0x39, 4K=0x00..0xff) if (!nfc_initiator_transceive_dep_bytes(pnd,abtCmd,2,abtRx,&szRxLen)) { // READ command of 0x10 failed, we consider that Ultralight does have 0x10 address, so it's a "simple" Ultralight (i.e. not a Ultralight C) // When a READ failed, the tag returns in HALT state, so we need to reselect tag nfc_initiator_select_passive_target(pnd, NM_ISO14443A_106, nti.nai.abtUid, nti.nai.szUidLen, NULL); return ""; } return " C"; } struct iso14443a_tag iso14443a_tags[] = { { 0x00, "NXP MIFARE UltraLight", 0, { 0 }, mifare_ultralight_identification }, { 0x09, "NXP MIFARE Mini", 0, { 0 }, NULL }, { 0x08, "NXP MIFARE Classic 1k", 0, { 0 }, NULL }, { 0x18, "NXP MIFARE Classic 4k", 0, { 0 }, NULL }, { 0x08, "NXP MIFARE Plus 1k", 0, { 0 }, NULL }, { 0x18, "NXP MIFARE Plus 4k", 0, { 0 }, NULL }, { 0x10, "NXP MIFARE Plus 1k", 0, { 0 }, NULL }, { 0x11, "NXP MIFARE Plus 4k", 0, { 0 }, NULL }, { 0x20, "NXP MIFARE Plus 1k", 0, { 0 }, NULL }, { 0x20, "NXP MIFARE Plus 4k", 0, { 0 }, NULL }, { 0x20, "NXP MIFARE Plus 1k/4k", 11, { 0x75, 0x77, 0x80, 0x02, 0xc1, 0x05, 0x2f, 0x2f, 0x01, 0xbc, 0xd6}, NULL }, { 0x20, "NXP MIFARE Plus 2k/4k", 11, { 0x75, 0x77, 0x80, 0x02, 0xc1, 0x05, 0x2f, 0x2f, 0x01, 0xbc, 0xd6 }, NULL }, { 0x88, "Infineon MIFARE Classic 1k", 0, { 0 }, NULL }, { 0x38, "Nokia MIFARE Classic 4k (Emulated)", 0, { 0 }, NULL }, { 0x20, "NXP MIFARE DESFire", 5, { 0x75, 0x77, 0x81, 0x02, 0x80 }, NULL }, { 0x28, "NXP JCOP31", 0, { 0 }, NULL }, /* @todo handle ATS to be able to know which one is it. */ { 0x20, "NXP JCOP31 or JCOP41", 0, { 0 }, NULL }, { 0x28, "NXP JCOP41", 0, { 0 }, NULL }, { 0x98, "Gemplus MPCOS", 0, { 0 }, NULL }, /* @note I'm not sure that Jewel can be detected using this modulation but I haven't Jewel tags to test. */ { 0x98, "Innovision R&T Jewel", 0, { 0 }, NULL }, }; int main (int argc, const char *argv[]) { nfc_target_info_t nti; uint8_t device_count = 0; uint8_t device_tag_count = 0; // per device uint8_t tag_count = 0; // total nfc_device_desc_t *pnddDevices; size_t szFound; (void)(argc, argv); // Try to open the NFC device if (!(pnddDevices = malloc (MAX_DEVICE_COUNT * sizeof (*pnddDevices)))) { ERR ("%s", "malloc() failed\n"); return EXIT_FAILURE; } nfc_list_devices (pnddDevices, MAX_DEVICE_COUNT, &szFound); if (szFound == 0) { ERR ("%s", "No device found."); } for (size_t i = 0; i < szFound; i++) { pnd = nfc_connect (&(pnddDevices[i])); device_count++; device_tag_count = 0; if (pnd == NULL) { ERR ("%s", "Unable to connect to NFC device."); return EXIT_FAILURE; } nfc_initiator_init (pnd); // Drop the field for a while nfc_configure (pnd, NDO_ACTIVATE_FIELD, false); // Let the reader only try once to find a tag nfc_configure (pnd, NDO_INFINITE_SELECT, false); // Configure the CRC and Parity settings nfc_configure (pnd, NDO_HANDLE_CRC, true); nfc_configure (pnd, NDO_HANDLE_PARITY, true); // Enable field so more power consuming cards can power themselves up nfc_configure (pnd, NDO_ACTIVATE_FIELD, true); bool no_more_tag = false; printf ("device = %s\n", pnd->acName); do { if (nfc_initiator_select_passive_target (pnd, NM_ISO14443A_106, NULL, 0, &nti)) { printf (" ISO14443A: "); const char *tag_name = NULL; const char *additionnal_info = NULL; for (size_t i = 0; i < sizeof (iso14443a_tags) / sizeof (struct iso14443a_tag); i++) { if ( (nti.nai.btSak == iso14443a_tags[i].SAK) ) { // printf("DBG: iso14443a_tags[i].ATS_length = %d , nti.nai.szAtsLen = %d", iso14443a_tags[i].ATS_length, nti.nai.szAtsLen); if ( iso14443a_tags[i].identication_fct != NULL ) { additionnal_info = iso14443a_tags[i].identication_fct(nti); } if( iso14443a_tags[i].ATS_length == 0 ) { tag_name = (iso14443a_tags[i].name); break; } if( iso14443a_tags[i].ATS_length == nti.nai.szAtsLen ) { if ( memcmp( nti.nai.abtAts, iso14443a_tags[i].ATS, iso14443a_tags[i].ATS_length ) == 0 ) { tag_name = (iso14443a_tags[i].name); break; } } } } if( tag_name != NULL ) { printf("%s%s (UID=", tag_name, additionnal_info); print_hex (nti.nai.abtUid, nti.nai.szUidLen); printf (")\n"); } else { printf ("Unknown ISO14443A tag type: "); printf ("ATQA (SENS_RES): "); print_hex (nti.nai.abtAtqa, 2); printf (", UID (NFCID%c): ", (nti.nai.abtUid[0] == 0x08 ? '3' : '1')); print_hex (nti.nai.abtUid, nti.nai.szUidLen); printf (", SAK (SEL_RES): "); print_hex (&nti.nai.btSak, 1); if (nti.nai.szAtsLen) { printf (", ATS (ATR): "); print_hex (nti.nai.abtAts, nti.nai.szAtsLen); } printf ("\n"); } nfc_initiator_deselect_target (pnd); device_tag_count++; } else if (nfc_initiator_select_passive_target (pnd, NM_FELICA_212, abtFelica, 5, &nti) || nfc_initiator_select_passive_target (pnd, NM_FELICA_424, abtFelica, 5, &nti)) { printf (" Felica: "); printf ("ID (NFCID2): "); print_hex (nti.nfi.abtId, 8); printf (", Parameter (PAD): "); print_hex (nti.nfi.abtPad, 8); printf ("\n"); nfc_initiator_deselect_target (pnd); device_tag_count++; } else if (nfc_initiator_select_passive_target (pnd, NM_ISO14443B_106, (byte_t *) "\x00", 1, &nti)) { printf (" ISO14443B: "); printf ("ATQB: "); print_hex (nti.nbi.abtAtqb, 12); printf (", ID: "); print_hex (nti.nbi.abtId, 4); printf (", CID: %02x", nti.nbi.btCid); if (nti.nbi.szInfLen > 0) { printf (", INF: "); print_hex (nti.nbi.abtInf, nti.nbi.szInfLen); } printf (", PARAMS: %02x %02x %02x %02x", nti.nbi.btParam1, nti.nbi.btParam2, nti.nbi.btParam3, nti.nbi.btParam4); printf ("\n"); nfc_initiator_deselect_target (pnd); device_tag_count++; } else if (nfc_initiator_select_passive_target (pnd, NM_JEWEL_106, NULL, 0, &nti)) { printf (" Jewel: No test results yet"); nfc_initiator_deselect_target (pnd); device_tag_count++; } else { no_more_tag = true; } } while (no_more_tag != true); printf ("%d tag(s) on device.\n\n", device_tag_count); tag_count += device_tag_count; // Disable field nfc_configure (pnd, NDO_ACTIVATE_FIELD, false); nfc_disconnect (pnd); } if (device_count > 1) { printf ("Total: %d tag(s) on %d device(s).\n", tag_count, device_count); } return EXIT_SUCCESS; }