diff --git a/ChangeLog b/ChangeLog index 497d5aa..fe49567 100644 --- a/ChangeLog +++ b/ChangeLog @@ -33,6 +33,7 @@ Improvements: - Add support for PN532 on RPi3 UART - Add support for cross-compilation of 32b & 64b versions of the library for Windows - Add pn533_usb to the kernel modules blacklist + - Allows for sending empty data in nfc_initiator_transceive_bits - driver i2c: respect proper timing specifications - driver i2c: add retry on error mechanism - nfc-mfclassic: improvements fo magic cards @@ -42,6 +43,7 @@ Improvements: - nfc-mfultralight: add automatic modes and --check-magic - nfc-mfultralight: add support for magic gen2 cards - nfc-mfultralight: add option to specify UID + - nfc-barcode: new command to read NFC Barcodes (Tag-Talks-First) Changes: - nfc_get_supported_baud_rate() takes now a "mode" parameter diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 35a852b..3f8fb87 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,5 +1,6 @@ SET(EXAMPLES-SOURCES nfc-anticol + nfc-barcode nfc-dep-initiator nfc-dep-target nfc-emulate-forum-tag2 diff --git a/examples/Makefile.am b/examples/Makefile.am index c767532..42baa29 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -2,6 +2,7 @@ SUBDIRS = pn53x-tamashell-scripts bin_PROGRAMS = \ nfc-anticol \ + nfc-barcode \ nfc-dep-initiator \ nfc-dep-target \ nfc-emulate-forum-tag2 \ @@ -35,6 +36,10 @@ nfc_anticol_SOURCES = nfc-anticol.c nfc_anticol_LDADD = $(top_builddir)/libnfc/libnfc.la \ $(top_builddir)/utils/libnfcutils.la +nfc_barcode_SOURCES = nfc-barcode.c +nfc_barcode_LDADD = $(top_builddir)/libnfc/libnfc.la \ + $(top_builddir)/utils/libnfcutils.la + nfc_relay_SOURCES = nfc-relay.c nfc_relay_LDADD = $(top_builddir)/libnfc/libnfc.la \ $(top_builddir)/utils/libnfcutils.la @@ -87,6 +92,7 @@ quick_start_example2_LDADD = $(top_builddir)/libnfc/libnfc.la \ dist_man_MANS = \ nfc-anticol.1 \ + nfc-barcode.1 \ nfc-dep-initiator.1 \ nfc-dep-target.1 \ nfc-emulate-tag.1 \ diff --git a/examples/nfc-barcode.1 b/examples/nfc-barcode.1 new file mode 100644 index 0000000..84190e3 --- /dev/null +++ b/examples/nfc-barcode.1 @@ -0,0 +1,35 @@ +.TH nfc-barcode 1 "May, 2017" "libnfc" "libnfc's examples" +.SH NAME +nfc-barcode \- NFC Barcode (Tag-Talks-First) reader +.SH SYNOPSIS +.B nfc-barcode +.SH DESCRIPTION +.B nfc-barcode +is a utility to read NFC Barcodes + +.SH OPTIONS +.B -d +Decode content, if possible. + +.B -v +Verbose. + +.SH BUGS +Please report any bugs on the +.B libnfc +issue tracker at: +.br +.BR https://github.com/nfc-tools/libnfc/issues +.SH LICENCE +.B libnfc +is licensed under the GNU Lesser General Public License (LGPL), version 3. +.br +.B libnfc-utils +and +.B libnfc-examples +are covered by the the BSD 2-Clause license. +.SH AUTHORS +Philippe Teuwen +.PP +This manual page was written by Philippe Teuwen . +It is licensed under the terms of the GNU GPL (version 2 or later). diff --git a/examples/nfc-barcode.c b/examples/nfc-barcode.c new file mode 100644 index 0000000..b4d299b --- /dev/null +++ b/examples/nfc-barcode.c @@ -0,0 +1,291 @@ +/*- + * Free/Libre Near Field Communication (NFC) library + * + * Libnfc historical contributors: + * Copyright (C) 2017 Philippe Teuwen + * 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-barcode.c + * @brief Reads a NFC Barcode tag + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif // HAVE_CONFIG_H + +#include +#include +#include +#include +#include +#include + +#include + +#include "utils/nfc-utils.h" + +#define MAX_FRAME_LEN 264 + +static nfc_device *pnd; + +bool verbose = false; +bool decode = false; + +static void +print_usage(char *argv[]) +{ + printf("Usage: %s [OPTIONS]\n", argv[0]); + printf("Options:\n"); + printf("\t-h\tHelp. Print this message.\n"); + printf("\t-q\tVerbose mode.\n"); + printf("\t-d\tDecode content.\n"); +} + +static int +bits2barcode(const uint8_t *pbtData, const size_t szBits, const uint8_t *pbtDataPar, uint8_t *pbtBarcode, const size_t szBarcode) +{ + uint8_t uRemainder; + size_t szPos; + size_t szBytes = szBits / 8; + size_t off = 0; + uint8_t i; + for (i = 0; i < szBarcode; i++) { + pbtBarcode[i] = 0; + } + + // Reinject S bit + pbtBarcode[off / 8] |= 1 << (7 - (off % 8)); + off++; + + for (szPos = 0; szPos < szBytes; szPos++) { + for (i = 0; i < 8; i++) { + pbtBarcode[off / 8] |= ((pbtData[szPos] >> i) & 1) << (7 - (off % 8)); + off++; + } + pbtBarcode[off / 8] |= pbtDataPar[szPos] << (7 - (off % 8)); + off++; + } + uRemainder = szBits % 8; + for (i = 0; i < uRemainder; i++) { + pbtBarcode[off / 8] |= ((pbtData[szPos] >> i) & 1) << (7 - (off % 8)); + off++; + } + return off; +} + +static bool +validate_crc(uint8_t *pbtBarcode, const size_t szBarcode) +{ + if (szBarcode % 128 != 0) { + printf("Warning, NFC Barcode seems incomplete, skipping CRC\n"); + return false; + } + uint8_t pbtCrc[2]; + iso14443a_crc(pbtBarcode, (szBarcode / 8) - 2, pbtCrc); + if (verbose) + printf("Computed CRC: %02X %02X\n", pbtCrc[1], pbtCrc[0]); + return (pbtCrc[1] == pbtBarcode[(szBarcode / 8) - 2]) && (pbtCrc[0] == pbtBarcode[(szBarcode / 8) - 1]); +} + +static bool +decode_barcode(uint8_t *pbtBarcode, const size_t szBarcode) +{ + if (szBarcode % 128 != 0) { + printf("Warning, NFC Barcode seems incomplete, skipping decoding\n"); + return false; + } + if (verbose) { + printf("Manufacturer ID field: %02X\n", pbtBarcode[0]); + switch (pbtBarcode[0]) { + case 0xb7: + printf("Manufacturer: Thinfilm\n"); + break; + default: + printf("Manufacturer: unknown\n"); + break; + } + } + if (verbose) { + printf("Data Format Field: %02X\n", pbtBarcode[1]); + } + switch (pbtBarcode[1]) { + case 0: + printf("Data Format Field: Reserved for allocation by tag manufacturer\n"); + return false; + break; + case 1: + case 2: + case 3: + case 4: + switch (pbtBarcode[1]) { + case 1: + printf("http://www."); + break; + case 2: + printf("https://www."); + break; + case 3: + printf("http://"); + break; + case 4: + printf("https://"); + break; + } + for (uint8_t i = 2; i < 15; i++) { + if ((pbtBarcode[i] == 0xfe) || (i == 14)) { + pbtBarcode[i] = '\n'; + pbtBarcode[i + 1] = 0; + break; + } + } + printf("%s", (char *)pbtBarcode + 2); + break; + case 5: + printf("EPC: "); + for (uint8_t i = 0; i < 12; i++) { + printf("%02x", pbtBarcode[i + 2]); + } + printf("\n"); + break; + default: + printf("Data Format Field: unknown (%02X)\n", pbtBarcode[1]); + printf("Data:"); + for (uint8_t i = 2; i < (szBarcode / 8) - 2; i++) { + printf("%02x", pbtBarcode[i]); + } + printf("\n"); + break; + } + return true; +} + +int +main(int argc, char *argv[]) +{ + int arg; + uint8_t abtRx[MAX_FRAME_LEN]; + uint8_t abtRxPar[MAX_FRAME_LEN]; + uint8_t pbtBarcode[64]; + + // Get commandline options + for (arg = 1; arg < argc; arg++) { + if (0 == strcmp(argv[arg], "-h")) { + print_usage(argv); + exit(EXIT_SUCCESS); + } else if (0 == strcmp(argv[arg], "-v")) { + verbose = true; + } else if (0 == strcmp(argv[arg], "-d")) { + decode = true; + } else { + ERR("%s is not supported option.", argv[arg]); + print_usage(argv); + exit(EXIT_FAILURE); + } + } + + nfc_context *context; + nfc_init(&context); + if (context == NULL) { + ERR("Unable to init libnfc (malloc)"); + exit(EXIT_FAILURE); + } + + // Try to open the NFC reader + pnd = nfc_open(context, NULL); + + if (pnd == NULL) { + ERR("Error opening NFC reader"); + nfc_exit(context); + exit(EXIT_FAILURE); + } + + // Initialise NFC device as "initiator" + if (nfc_initiator_init(pnd) < 0) { + nfc_perror(pnd, "nfc_initiator_init"); + nfc_close(pnd); + nfc_exit(context); + exit(EXIT_FAILURE); + } + + printf("NFC reader: %s opened\n\n", nfc_device_get_name(pnd)); + + if ((nfc_device_set_property_bool(pnd, NP_ACTIVATE_FIELD, false) < 0) || + (nfc_device_set_property_bool(pnd, NP_HANDLE_CRC, false) < 0) || + (nfc_device_set_property_bool(pnd, NP_HANDLE_PARITY, false) < 0)) { + nfc_perror(pnd, "nfc_device_set_property_bool"); + nfc_close(pnd); + nfc_exit(context); + exit(EXIT_FAILURE); + } + int res; + if ((res = nfc_initiator_transceive_bits(pnd, NULL, 0, NULL, abtRx, sizeof(abtRx), abtRxPar)) < 0) { + if (verbose) + nfc_perror(pnd, "nfc_initiator_transceive_bits"); + printf("No NFC Barcode found\n"); + nfc_close(pnd); + nfc_exit(context); + exit(EXIT_FAILURE); + } + nfc_close(pnd); + nfc_exit(context); + if (verbose) + print_hex_par(abtRx, res, abtRxPar); + res = bits2barcode(abtRx, res, abtRxPar, pbtBarcode, sizeof(pbtBarcode)); + + if (res % 128 != 0) { + printf("Error, NFC Barcode seems incomplete, received %u bits\n", res); + if (verbose) { + print_hex_bits(pbtBarcode, res); + } + exit(EXIT_FAILURE); + } + + if (validate_crc(pbtBarcode, res)) { + if (verbose) { + printf("CRC correct\n"); + } + } else { + printf("CRC error\n"); + if (verbose) { + print_hex_bits(pbtBarcode, res); + } + exit(EXIT_FAILURE); + } + + if (verbose || ! decode) { + for (uint8_t i = 0; i < res / 8; i++) { + printf("%02x", pbtBarcode[i]); + } + printf("\n"); + } + if (decode) { + decode_barcode(pbtBarcode, res); + } + exit(EXIT_SUCCESS); +} diff --git a/libnfc/chips/pn53x.c b/libnfc/chips/pn53x.c index 34cf4ad..a83d3f0 100644 --- a/libnfc/chips/pn53x.c +++ b/libnfc/chips/pn53x.c @@ -1333,7 +1333,7 @@ pn53x_initiator_transceive_bits(struct nfc_device *pnd, const uint8_t *pbtTx, co uint8_t abtCmd[PN53x_EXTENDED_FRAME__DATA_MAX_LEN] = { InCommunicateThru }; // Check if we should prepare the parity bits ourself - if (!pnd->bPar) { + if ((!pnd->bPar) && (szTxBits > 0)) { // Convert data with parity to a frame if ((res = pn53x_wrap_frame(pbtTx, szTxBits, pbtTxPar, abtCmd + 1)) < 0) return res;