diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 68fab6b..91a29e0 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,11 +1,13 @@ # $Id$ set(EXAMPLES-SOURCES mifare-classic-format + mifare-classic-read-ndef mifare-classic-write-ndef mifare-desfire-access mifare-desfire-create-ndef mifare-desfire-format mifare-desfire-info + mifare-desfire-read-ndef mifare-desfire-write-ndef mifare-desfire-ev1-configure-ats mifare-desfire-ev1-configure-default-key diff --git a/examples/Makefile.am b/examples/Makefile.am index d8cc667..67825a0 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -13,6 +13,7 @@ bin_PROGRAMS = mifare-classic-format \ mifare-desfire-ev1-configure-random-uid \ mifare-desfire-format \ mifare-desfire-info \ + mifare-desfire-read-ndef \ mifare-desfire-write-ndef mifare_classic_format_SOURCES = mifare-classic-format.c @@ -45,6 +46,9 @@ mifare_desfire_format_LDADD = -lnfc $(top_builddir)/libfreefare/libfreefare.la mifare_desfire_info_SOURCES = mifare-desfire-info.c mifare_desfire_info_LDADD = -lnfc $(top_builddir)/libfreefare/libfreefare.la -lm +mifare_desfire_read_ndef_SOURCES = mifare-desfire-read-ndef.c +mifare_desfire_read_ndef_LDADD = -lnfc $(top_builddir)/libfreefare/libfreefare.la + mifare_desfire_write_ndef_SOURCES = mifare-desfire-write-ndef.c mifare_desfire_write_ndef_LDADD = -lnfc $(top_builddir)/libfreefare/libfreefare.la diff --git a/examples/mifare-classic-read-ndef.c b/examples/mifare-classic-read-ndef.c index 9131de6..50210e1 100644 --- a/examples/mifare-classic-read-ndef.c +++ b/examples/mifare-classic-read-ndef.c @@ -47,11 +47,18 @@ #define MIN(a,b) ((a < b) ? a: b) +struct { + bool interactive; +} read_options = { + .interactive = true +}; + void usage(char *progname) { fprintf (stderr, "usage: %s -o FILE\n", progname); fprintf (stderr, "\nOptions:\n"); + fprintf (stderr, " -y Do not ask for confirmation\n"); fprintf (stderr, " -o Extract NDEF message if available in FILE\n"); } @@ -65,13 +72,16 @@ main(int argc, char *argv[]) int ch; char *ndef_output = NULL; - while ((ch = getopt (argc, argv, "ho:")) != -1) { + while ((ch = getopt (argc, argv, "hyo:")) != -1) { switch (ch) { case 'h': usage(argv[0]); exit (EXIT_SUCCESS); break; - case 'o': + case 'y': + read_options.interactive = false; + break; + case 'o': ndef_output = optarg; break; case '?': @@ -137,9 +147,16 @@ main(int argc, char *argv[]) char *tag_uid = freefare_get_tag_uid (tags[i]); char buffer[BUFSIZ]; - fprintf (message_stream, "Found %s with UID %s. Read NDEF [yN] ", freefare_get_tag_friendly_name (tags[i]), tag_uid); - fgets (buffer, BUFSIZ, stdin); - bool read_ndef = ((buffer[0] == 'y') || (buffer[0] == 'Y')); + fprintf (message_stream, "Found %s with UID %s. ", freefare_get_tag_friendly_name (tags[i]), tag_uid); + + bool read_ndef = true; + if (read_options.interactive) { + printf ("Read NDEF [yN] "); + fgets (buffer, BUFSIZ, stdin); + read_ndef = ((buffer[0] == 'y') || (buffer[0] == 'Y')); + } else { + printf ("\n"); + } if (read_ndef) { // NFCForum card has a MAD, load it. diff --git a/examples/mifare-desfire-read-ndef.c b/examples/mifare-desfire-read-ndef.c new file mode 100644 index 0000000..7f1c3c1 --- /dev/null +++ b/examples/mifare-desfire-read-ndef.c @@ -0,0 +1,245 @@ +/*- + * Copyright (C) 2010, Audrey Diacre. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser 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 Lesser General Public License + * along with this program. If not, see + * + * $Id$ + */ + +#include "config.h" + +#include +#include +#include +#include + +#include + +/* + * This example was written based on information provided by the + * following documents: + * + * Mifare DESFire as Type 4 Tag + * NFC Forum Type 4 Tag Extensions for Mifare DESFire + * Rev. 1.1 - 21 August 2007 + * + */ + +// Note that it is using specific Desfire commands, not ISO7816 NDEF Tag Type4 commands + +uint8_t key_data_app[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +uint8_t *cc_data; +uint8_t *ndef_msg; +uint16_t ndef_msg_len; + +struct { + bool interactive; +} read_options = { + .interactive = true +}; + +void +usage(char *progname) +{ + fprintf (stderr, "usage: %s [-y] -o FILE [-k 11223344AABBCCDD]\n", progname); + fprintf (stderr, "\nOptions:\n"); + fprintf (stderr, " -y Do not ask for confirmation\n"); + fprintf (stderr, " -o Extract NDEF message if available in FILE\n"); + fprintf (stderr, " -k Provide another NDEF Tag Application key than the default one\n"); +} + +int +main(int argc, char *argv[]) +{ + int ch; + int error = EXIT_SUCCESS; + nfc_device *device = NULL; + MifareTag *tags = NULL; + + char *ndef_output = NULL; + while ((ch = getopt (argc, argv, "hyo:k:")) != -1) { + switch (ch) { + case 'h': + usage(argv[0]); + exit (EXIT_SUCCESS); + break; + case 'y': + read_options.interactive = false; + break; + case 'o': + ndef_output = optarg; + break; + case 'k': + if (strlen(optarg) != 16) { + usage(argv[0]); + exit (EXIT_FAILURE); + } + uint64_t n = strtoull(optarg, NULL, 16); + int i; + for (i=7; i>=0; i--) { + key_data_app[i] = (uint8_t) n; + n >>= 8; + } + break; + default: + usage(argv[0]); + exit (EXIT_FAILURE); + } + } + // Remaining args, if any, are in argv[optind .. (argc-1)] + + printf ("NOTE: This application reads a NDEF payload from a Mifare DESFire formatted as NFC Forum Type 4 Tag.\n"); + + if (ndef_output == NULL) { + 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_connstring devices[8]; + size_t device_count; + + nfc_init(NULL); + + device_count = nfc_list_devices (NULL, devices, 8); + if (device_count <= 0) + errx (EXIT_FAILURE, "No NFC device found."); + + for (size_t d = 0; d < device_count; d++) { + device = nfc_open (NULL, devices[d]); + + if (!device) { + warnx ("nfc_open() failed."); + error = EXIT_FAILURE; + continue; + } + + tags = freefare_get_tags (device); + if (!tags) { + nfc_close (device); + errx (EXIT_FAILURE, "Error listing tags."); + } + + for (int i = 0; (!error) && tags[i]; i++) { + if (DESFIRE != freefare_get_tag_type (tags[i])) + continue; + + char *tag_uid = freefare_get_tag_uid (tags[i]); + char buffer[BUFSIZ]; + + fprintf (message_stream, "Found %s with UID %s. ", freefare_get_tag_friendly_name (tags[i]), tag_uid); + + bool read_ndef = true; + if (read_options.interactive) { + printf ("Read NDEF [yN] "); + fgets (buffer, BUFSIZ, stdin); + read_ndef = ((buffer[0] == 'y') || (buffer[0] == 'Y')); + } else { + printf ("\n"); + } + + if (read_ndef) { + int res; + + res = mifare_desfire_connect (tags[i]); + if (res < 0) { + warnx ("Can't connect to Mifare DESFire target."); + error = EXIT_FAILURE; + break; + } + + MifareDESFireKey key_app; + key_app = mifare_desfire_des_key_new_with_version (key_data_app); + + // Mifare DESFire SelectApplication (Select application) + MifareDESFireAID aid = mifare_desfire_aid_new(0xEEEE10); + res = mifare_desfire_select_application(tags[i], aid); + if (res < 0) + errx (EXIT_FAILURE, "Application selection failed. Try mifare-desfire-create-ndef before running %s.", argv[0]); + free (aid); + + // Authentication with NDEF Tag Application master key (Authentication with key 0) + res = mifare_desfire_authenticate (tags[i], 0, key_app); + if (res < 0) + errx (EXIT_FAILURE, "Authentication with NDEF Tag Application master key failed"); + + // Read Capability Container file E103 + uint8_t lendata[2]; + res = mifare_desfire_read_data (tags[i], 0x03, 0, 2, lendata); + if (res < 0) + errx (EXIT_FAILURE, "Read CC len failed"); + uint16_t cclen = (((uint16_t) lendata[0]) << 8) + ((uint16_t) lendata[1]); + if (cclen < 15) + errx (EXIT_FAILURE, "CC too short IMHO"); + if (!(cc_data = malloc(cclen))) + errx (EXIT_FAILURE, "malloc"); + res = mifare_desfire_read_data (tags[i], 0x03, 0, cclen, cc_data); + if (res < 0) + errx (EXIT_FAILURE, "Read CC data failed"); + // Search NDEF File Control TLV + uint8_t off = 7; + while (((off+7) < cclen) && (cc_data[off] != 0x04)) { + // Skip TLV + off += cc_data[off+1] + 2; + } + if (off+7 >= cclen) + errx (EXIT_FAILURE, "CC does not contain expected NDEF File Control TLV"); + if (cc_data[off+2] != 0xE1) + errx (EXIT_FAILURE, "Unknown NDEF File reference in CC"); + uint8_t file_no = cc_data[off+3]; + uint16_t ndefmaxlen = (((uint16_t) cc_data[off+4]) << 8) + ((uint16_t) cc_data[off+5]); + fprintf (message_stream, "Max NDEF size: %i bytes\n", ndefmaxlen); + if (!(ndef_msg = malloc(ndefmaxlen))) + errx (EXIT_FAILURE, "malloc"); + + res = mifare_desfire_read_data (tags[i], file_no, 0, 2, lendata); + if (res < 0) + errx (EXIT_FAILURE, "Read NDEF len failed"); + ndef_msg_len = (((uint16_t) lendata[0]) << 8) + ((uint16_t) lendata[1]); + fprintf (message_stream, "NDEF size: %i bytes\n", ndef_msg_len); + if (ndef_msg_len + 2 > ndefmaxlen) + errx (EXIT_FAILURE, "Declared NDEF size larger than max NDEF size"); + res = mifare_desfire_read_data (tags[i], file_no, 2, ndef_msg_len, ndef_msg); + if (res < 0) + errx (EXIT_FAILURE, "Read data failed"); + if (fwrite (ndef_msg, 1, ndef_msg_len, ndef_stream) != ndef_msg_len) + errx (EXIT_FAILURE, "Write to file failed"); + free (cc_data); + free (ndef_msg); + mifare_desfire_key_free (key_app); + + mifare_desfire_disconnect (tags[i]); + } + free (tag_uid); + } + freefare_free_tags (tags); + nfc_close (device); + } + nfc_exit(NULL); + exit (error); +} diff --git a/examples/mifare-desfire-write-ndef.c b/examples/mifare-desfire-write-ndef.c index d57f3b1..d468205 100644 --- a/examples/mifare-desfire-write-ndef.c +++ b/examples/mifare-desfire-write-ndef.c @@ -48,6 +48,7 @@ const uint8_t ndef_default_msg[33] = { 0x67 }; +uint8_t *cc_data; uint8_t *ndef_msg; size_t ndef_msg_len; @@ -216,20 +217,43 @@ main(int argc, char *argv[]) if (res < 0) errx (EXIT_FAILURE, "Authentication with NDEF Tag Application master key failed"); -// TODO: read and check Capability Container -// Steps not implemented at this stage: -// Read and parse CC (E103) -// - Read NDEF file pointer (can be != E104) - uint8_t file_no = 0x04; -// - Check available space for storing NDEF -// ndef_msg_len + 2 <= max_len ? -// - Check is writing is allowed and propose to overrule it if needed + // Read Capability Container file E103 + uint8_t lendata[2]; + res = mifare_desfire_read_data (tags[i], 0x03, 0, 2, lendata); + if (res < 0) + errx (EXIT_FAILURE, "Read CC len failed"); + uint16_t cclen = (((uint16_t) lendata[0]) << 8) + ((uint16_t) lendata[1]); + if (cclen < 15) + errx (EXIT_FAILURE, "CC too short IMHO"); + if (!(cc_data = malloc(cclen))) + errx (EXIT_FAILURE, "malloc"); + res = mifare_desfire_read_data (tags[i], 0x03, 0, cclen, cc_data); + if (res < 0) + errx (EXIT_FAILURE, "Read CC data failed"); + // Search NDEF File Control TLV + uint8_t off = 7; + while (((off+7) < cclen) && (cc_data[off] != 0x04)) { + // Skip TLV + off += cc_data[off+1] + 2; + } + if (off+7 >= cclen) + errx (EXIT_FAILURE, "CC does not contain expected NDEF File Control TLV"); + if (cc_data[off+2] != 0xE1) + errx (EXIT_FAILURE, "Unknown NDEF File reference in CC"); + uint8_t file_no = cc_data[off+3]; + uint16_t ndefmaxlen = (((uint16_t) cc_data[off+4]) << 8) + ((uint16_t) cc_data[off+5]); + fprintf (stdout, "Max NDEF size: %i bytes\n", ndefmaxlen); + if (!(ndef_msg = malloc(ndefmaxlen))) + errx (EXIT_FAILURE, "malloc"); + if (ndef_msg_len > ndefmaxlen) + errx (EXIT_FAILURE, "Supplied NDEF larger than max NDEF size"); //Mifare DESFire WriteData to write the content of the NDEF File with NLEN equal to NDEF Message length and NDEF Message res = mifare_desfire_write_data(tags[i], file_no, 0, ndef_msg_len, (uint8_t *) ndef_msg); if (res < 0) errx (EXIT_FAILURE, " Write data failed"); + free(cc_data); mifare_desfire_key_free (key_app); mifare_desfire_disconnect (tags[i]);