/*- * Free/Libre Near Field Communication (NFC) library * * Libnfc historical contributors: * Copyright (C) 2009 Roel Verdult * Copyright (C) 2009-2013 Romuald Conty * Copyright (C) 2010-2012 Romain Tartière * Copyright (C) 2010-2013 Philippe Teuwen * Copyright (C) 2012-2013 Ludovic Rousseau * See AUTHORS file for a more comprehensive list of contributors. * Additional contributors of this file: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * 1) Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2 )Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Note that this license only applies on the examples, NFC library itself is under LGPL * */ /** * @file nfc-read-forum-tag3.c * @brief Extract NDEF Message from a NFC Forum Tag Type 3 * This utility extract (if available) the NDEF Message contained in an NFC Forum Tag Type 3. */ /* * This implementation was written based on information provided by the * following documents: * * NFC Forum Type 3 Tag Operation Specification * Technical Specification * NFCForum-TS-Type-3-Tag_1.1 - 2011-06-28 */ #ifdef HAVE_CONFIG_H # include "config.h" #endif // HAVE_CONFIG_H #include #include #include #include #include #include "nfc-utils.h" #if defined(WIN32) /* mingw compiler */ #include #endif static nfc_device *pnd; static nfc_context *context; static void print_usage(char *progname) { fprintf(stderr, "usage: %s [-q] -o FILE\n", progname); fprintf(stderr, "\nOptions:\n"); fprintf(stderr, " -o FILE Extract NDEF message if available in FILE\n"); fprintf(stderr, " -o - Extract NDEF message if available to stdout\n"); fprintf(stderr, " -q Be quiet, don't display Attribute Block parsing info\n"); } static void stop_select(int sig) { (void) sig; if (pnd != NULL) { nfc_abort_command(pnd); } else { nfc_exit(context); exit(EXIT_FAILURE); } } static void build_felica_frame(const nfc_felica_info nfi, const uint8_t command, const uint8_t *payload, const size_t payload_len, uint8_t *frame, size_t *frame_len) { frame[0] = 1 + 1 + 8 + payload_len; *frame_len = frame[0]; frame[1] = command; memcpy(frame + 2, nfi.abtId, 8); memcpy(frame + 10, payload, payload_len); } #define CHECK 0x06 static int nfc_forum_tag_type3_check(nfc_device *dev, const nfc_target *nt, const uint16_t block, const uint8_t block_count, uint8_t *data, size_t *data_len) { uint8_t payload[1024] = { 1, // Services 0x0B, 0x00, // NFC Forum Tag Type 3's Service code block_count, 0x80, block, // block 0 }; size_t payload_len = 1 + 2 + 1; for (uint8_t b = 0; b < block_count; b++) { if (block < 0x100) { payload[payload_len++] = 0x80; payload[payload_len++] = block + b; } else { payload[payload_len++] = 0x00; payload[payload_len++] = (block + b) >> 8; payload[payload_len++] = (block + b) & 0xff; } } uint8_t frame[1024]; size_t frame_len = sizeof(frame); build_felica_frame(nt->nti.nfi, CHECK, payload, payload_len, frame, &frame_len); uint8_t rx[1024]; int res; if ((res = nfc_initiator_transceive_bytes(dev, frame, frame_len, rx, sizeof(rx), 0)) < 0) { return res; } const int res_overhead = 1 + 1 + 8 + 2; // 1+1+8+2: LEN + CMD + NFCID2 + STATUS if (res < res_overhead) { // Not enough data return -1; } uint8_t felica_res_len = rx[0]; if (res != felica_res_len) { // Error while receiving felica frame return -1; } if ((CHECK + 1) != rx[1]) { // Command return does not match return -1; } if (0 != memcmp(&rx[2], nt->nti.nfi.abtId, 8)) { // NFCID2 does not match return -1; } const uint8_t status_flag1 = rx[10]; const uint8_t status_flag2 = rx[11]; if ((status_flag1) || (status_flag2)) { // Felica card's error fprintf(stderr, "Status bytes: %02x, %02x\n", status_flag1, status_flag2); return -1; } // const uint8_t res_block_count = res[12]; *data_len = res - res_overhead + 1; // +1 => block count is stored on 1 byte memcpy(data, &rx[res_overhead + 1], *data_len); return *data_len; } int main(int argc, char *argv[]) { (void)argc; (void)argv; int ch; bool quiet = false; char *ndef_output = NULL; while ((ch = getopt(argc, argv, "hqo:")) != -1) { switch (ch) { case 'h': print_usage(argv[0]); exit(EXIT_SUCCESS); case 'q': quiet = true; break; case 'o': ndef_output = optarg; break; default: print_usage(argv[0]); exit(EXIT_FAILURE); } } if (ndef_output == NULL) { print_usage(argv[0]); exit(EXIT_FAILURE); } FILE *message_stream = NULL; FILE *ndef_stream = NULL; if ((strlen(ndef_output) == 1) && (ndef_output[0] == '-')) { message_stream = stderr; ndef_stream = stdout; } else { message_stream = stdout; ndef_stream = fopen(ndef_output, "wb"); if (!ndef_stream) { fprintf(stderr, "Could not open file %s.\n", ndef_output); exit(EXIT_FAILURE); } } nfc_init(&context); if (context == NULL) { ERR("Unable to init libnfc (malloc)\n"); exit(EXIT_FAILURE); } pnd = nfc_open(context, NULL); if (pnd == NULL) { ERR("Unable to open NFC device"); fclose(ndef_stream); nfc_exit(context); exit(EXIT_FAILURE); } if (!quiet) { fprintf(message_stream, "NFC device: %s opened\n", nfc_device_get_name(pnd)); } nfc_modulation nm = { .nmt = NMT_FELICA, .nbr = NBR_212, }; signal(SIGINT, stop_select); nfc_target nt; if (nfc_initiator_init(pnd) < 0) { nfc_perror(pnd, "nfc_initiator_init"); fclose(ndef_stream); nfc_close(pnd); nfc_exit(context); exit(EXIT_FAILURE); } if (!quiet) { fprintf(message_stream, "Place your NFC Forum Tag Type 3 in the field...\n"); } // Polling payload (SENSF_REQ) must be present (see NFC Digital Protol) const uint8_t *pbtSensfReq = (uint8_t *)"\x00\xff\xff\x01\x00"; if (nfc_initiator_select_passive_target(pnd, nm, pbtSensfReq, 5, &nt) <= 0) { nfc_perror(pnd, "nfc_initiator_select_passive_target"); fclose(ndef_stream); nfc_close(pnd); nfc_exit(context); exit(EXIT_FAILURE); } // Check if System Code equals 0x12fc const uint8_t abtNfcForumSysCode[] = { 0x12, 0xfc }; if (0 != memcmp(nt.nti.nfi.abtSysCode, abtNfcForumSysCode, 2)) { // Retry with special polling const uint8_t *pbtSensfReqNfcForum = (uint8_t *)"\x00\x12\xfc\x01\x00"; if (nfc_initiator_select_passive_target(pnd, nm, pbtSensfReqNfcForum, 5, &nt) <= 0) { nfc_perror(pnd, "nfc_initiator_select_passive_target"); fclose(ndef_stream); nfc_close(pnd); nfc_exit(context); exit(EXIT_FAILURE); } // Check again if System Code equals 0x12fc if (0 != memcmp(nt.nti.nfi.abtSysCode, abtNfcForumSysCode, 2)) { fprintf(stderr, "Tag is not NFC Forum Tag Type 3 compliant.\n"); fclose(ndef_stream); nfc_close(pnd); nfc_exit(context); exit(EXIT_FAILURE); } } //print_nfc_felica_info(nt.nti.nfi, true); if ((nfc_device_set_property_bool(pnd, NP_EASY_FRAMING, false) < 0) || (nfc_device_set_property_bool(pnd, NP_INFINITE_SELECT, false) < 0)) { nfc_perror(pnd, "nfc_device_set_property_bool"); fclose(ndef_stream); nfc_close(pnd); nfc_exit(context); exit(EXIT_FAILURE); } uint8_t data[1024]; size_t data_len = sizeof(data); if (nfc_forum_tag_type3_check(pnd, &nt, 0, 1, data, &data_len) <= 0) { nfc_perror(pnd, "nfc_forum_tag_type3_check"); fclose(ndef_stream); nfc_close(pnd); nfc_exit(context); exit(EXIT_FAILURE); } const int ndef_major_version = (data[0] & 0xf0) >> 4; const int ndef_minor_version = (data[0] & 0x0f); const int ndef_nbr = data[1]; const int ndef_nbw = data[2]; const int ndef_nmaxb = (data[3] << 8) + data[4]; const int ndef_writeflag = data[9]; const int ndef_rwflag = data[10]; uint32_t ndef_data_len = (data[11] << 16) + (data[12] << 8) + data[13]; uint16_t ndef_calculated_checksum = 0; for (size_t n = 0; n < 14; n++) ndef_calculated_checksum += data[n]; const uint16_t ndef_checksum = (data[14] << 8) + data[15]; if (!quiet) { fprintf(message_stream, "NDEF Attribute Block:\n"); fprintf(message_stream, "* Mapping version: %d.%d\n", ndef_major_version, ndef_minor_version); fprintf(message_stream, "* Maximum nr of blocks to read by Check Command: %3d block%s\n", ndef_nbr, ndef_nbr > 1 ? "s" : ""); fprintf(message_stream, "* Maximum nr of blocks to write by Update Command: %3d block%s\n", ndef_nbw, ndef_nbw > 1 ? "s" : ""); fprintf(message_stream, "* Maximum nr of blocks available for NDEF data: %3d block%s (%d bytes)\n", ndef_nmaxb, ndef_nmaxb > 1 ? "s" : "", ndef_nmaxb * 16); fprintf(message_stream, "* NDEF writing state: "); switch (ndef_writeflag) { case 0x00: fprintf(message_stream, "finished (0x00)\n"); break; case 0x0f: fprintf(message_stream, "in progress (0x0F)\n"); break; default: fprintf(message_stream, "invalid (0x%02X)\n", ndef_writeflag); break; } fprintf(message_stream, "* NDEF Access Attribute: "); switch (ndef_rwflag) { case 0x00: fprintf(message_stream, "Read only (0x00)\n"); break; case 0x01: fprintf(message_stream, "Read/Write (0x01)\n"); break; default: fprintf(message_stream, "invalid (0x%02X)\n", ndef_rwflag); break; } fprintf(message_stream, "* NDEF message length: %d bytes\n", ndef_data_len); if (ndef_calculated_checksum != ndef_checksum) { fprintf(message_stream, "* Checksum: fail (0x%04X != 0x%04X)\n", ndef_calculated_checksum, ndef_checksum); } else { fprintf(message_stream, "* Checksum: ok (0x%04X)\n", ndef_checksum); } } if (ndef_calculated_checksum != ndef_checksum) { fprintf(stderr, "Error: Checksum failed! Exiting now.\n"); fclose(ndef_stream); nfc_close(pnd); nfc_exit(context); exit(EXIT_FAILURE); } if (!ndef_data_len) { fprintf(stderr, "Error: empty NFC Forum Tag Type 3, nothing to read!\n"); fclose(ndef_stream); nfc_close(pnd); nfc_exit(context); exit(EXIT_FAILURE); } const uint8_t block_max_per_check = data[1]; const uint16_t block_count_to_check = (ndef_data_len / 16) + 1; data_len = 0; for (uint16_t b = 0; b < ((block_count_to_check - 1) / block_max_per_check + 1); b += block_max_per_check) { size_t size = sizeof(data) - data_len; if (!nfc_forum_tag_type3_check(pnd, &nt, 1 + b, MIN(block_max_per_check, (block_count_to_check - (b * block_max_per_check))), data + data_len, &size)) { nfc_perror(pnd, "nfc_forum_tag_type3_check"); fclose(ndef_stream); nfc_close(pnd); nfc_exit(context); exit(EXIT_FAILURE); } data_len += size; } if (fwrite(data, 1, ndef_data_len, ndef_stream) != ndef_data_len) { fprintf(stderr, "Error: could not write to file.\n"); fclose(ndef_stream); nfc_close(pnd); nfc_exit(context); exit(EXIT_FAILURE); } else { if (!quiet) { fprintf(stderr, "%i bytes written to %s\n", ndef_data_len, ndef_output); } } fclose(ndef_stream); nfc_close(pnd); nfc_exit(context); exit(EXIT_SUCCESS); }