diff --git a/AUTHORS b/AUTHORS index 85d1f74..7bb6b28 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,2 +1,3 @@ rconty@il4p.fr:Romuald Conty romain.tartiere:Romain Tartière +rtartiere@il4p.fr:Romain Tartière diff --git a/HACKING b/HACKING index 314e58d..7260caf 100644 --- a/HACKING +++ b/HACKING @@ -40,7 +40,7 @@ infrastructure ready for hacking the new card support: - Edit libfreefare/freefare.3: - Add your tag to the `mifare_tag_type' enum documentation; - Edit libfreefare/freefare_internal.h: - - Add a new _tag struct. It's very first member shall be `struct + - Add a new _tag struct. It's very first member SHALL be `struct mifare_tag __tag'; - Add a _tag_new() and a _tag_free() function prototype; - Add a ASSERT_() macro to check the tag's type; @@ -52,9 +52,12 @@ infrastructure ready for hacking the new card support: - Edit the freefare_free_tags() function so that it calls _tag_free() to free your tags; - Create libfreefare/.c and implement all that's missing: - - _tag_new() SHALL allocate all data-structure the tag may need to + - _tag_new() MUST allocate all data-structure the tag may need to use during it's lifetime. We do not want to have any function to fail - later because the running system is out of resources; + later because the running system is out of resources. Buffers for + cryptographic operations on random amount of data MAY however be + (re)allocated on demand, in such case refrain from shrinking + unnecessarily the buffer size. - _connect() SHOULD initialise data allocated by _tag_new(). Keep in mind that a single tag may be disconnected from and connected to again, without being freed in the meantime. Since all memory diff --git a/Makefile.am b/Makefile.am index 6986db1..de16f27 100644 --- a/Makefile.am +++ b/Makefile.am @@ -20,3 +20,8 @@ dist-hook: fi EXTRA_DIST = HACKING +CLEANFILES = coverage.info +clean-local: clean-local-coverage +.PHONY: clean-local-coverage +clean-local-coverage: + -rm -rf coverage diff --git a/README b/README index 12dfff4..e7a75e3 100644 --- a/README +++ b/README @@ -4,6 +4,7 @@ manipulations. Supported tags: - Mifare Classic 1k - Mifare Classic 4k + - Mifare DESFire 4K - Mifare Ultralight Supported features: diff --git a/configure.ac b/configure.ac index e58a150..f2c80ed 100644 --- a/configure.ac +++ b/configure.ac @@ -18,17 +18,19 @@ AC_C_INLINE AC_HEADER_STDBOOL AC_TYPE_INT16_T AC_TYPE_INT32_T +AC_TYPE_OFF_T AC_TYPE_UINT8_T AC_TYPE_UINT16_T AC_TYPE_UINT32_T AC_TYPE_SIZE_T +AC_TYPE_SSIZE_T # Checks for library functions. AC_FUNC_MALLOC AC_FUNC_REALLOC AC_CHECK_HEADERS([sys/types.h]) -AC_CHECK_FUNCS([memset letoh32 htole32]) +AC_CHECK_FUNCS([memset letoh32 htole32 pow strerror]) AC_CHECK_HEADERS([endian.h sys/endian.h]) if test $ac_cv_header_endian_h = "no" -a $ac_cv_header_sys_endian_h = "no"; then @@ -42,6 +44,10 @@ AC_DEFINE([_BSD_SOURCE], [1], [Define on BSD to activate all library features]) CFLAGS="$CFLAGS -std=c99" +# Crypto functions for MIFARE DesFire support are provided by OpenSSL. +AC_CHECK_LIB([ssl], [DES_ecb_encrypt], [], [AC_MSG_ERROR([Cannot find libssl.])]) +AC_CHECK_HEADER([openssl/des.h], [], [AC_MSG_ERROR([Cannot find libssl headers.])]) + # Checks for pkg-config modules. LIBNFC_REQUIRED_VERSION=1.3.4 PKG_CHECK_MODULES([LIBNFC], [libnfc >= $LIBNFC_REQUIRED_VERSION], [], [AC_MSG_ERROR([libnfc >= $LIBNFC_REQUIRED_VERSION is mandatory.])]) @@ -62,5 +68,9 @@ if test x$ac_cv_enable_coverage = xyes; then CFLAGS="$CFLAGS -O0 -fprofile-arcs -ftest-coverage" fi -AC_OUTPUT([Makefile examples/Makefile libfreefare/Makefile libfreefare.pc test/Makefile]) +AC_OUTPUT([Makefile + examples/Makefile + libfreefare/Makefile + libfreefare.pc + test/Makefile]) diff --git a/examples/Makefile.am b/examples/Makefile.am index 526e9c2..15dcc43 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -2,10 +2,24 @@ AM_CFLAGS = -I. -I$(top_srcdir)/libfreefare @LIBNFC_CFLAGS@ AM_LDFLAGS = @LIBNFC_LIBS@ bin_PROGRAMS = mifare-classic-format \ - mifare-classic-write-ndef + mifare-classic-write-ndef \ + mifare-desfire-access \ + mifare-desfire-format \ + mifare-desfire-info mifare_classic_format_SOURCES = mifare-classic-format.c -mifare_classic_format_LDADD = -lnfc $(top_builddir)/libfreefare/libfreefare.la +mifare_classic_format_LDADD = -lnfc $(top_builddir)/libfreefare/libfreefare.la -lssl mifare_classic_write_ndef_SOURCES = mifare-classic-write-ndef.c mifare_classic_write_ndef_LDADD = -lnfc $(top_builddir)/libfreefare/libfreefare.la + +mifare_desfire_access_SOURCES = mifare-desfire-access.c +mifare_desfire_access_LDADD = -lnfc $(top_builddir)/libfreefare/libfreefare.la -lssl + +mifare_desfire_format_SOURCES = mifare-desfire-format.c +mifare_desfire_format_LDADD = -lnfc $(top_builddir)/libfreefare/libfreefare.la -lssl + +mifare_desfire_info_SOURCES = mifare-desfire-info.c +mifare_desfire_info_LDADD = -lnfc $(top_builddir)/libfreefare/libfreefare.la -lssl -lm + +CLEANFILES= *.gcno diff --git a/examples/mifare-classic-format.c b/examples/mifare-classic-format.c index 36997be..d9b3081 100644 --- a/examples/mifare-classic-format.c +++ b/examples/mifare-classic-format.c @@ -138,11 +138,10 @@ int main(int argc, char *argv[]) { int ch; - int error = 0; + int error = EXIT_SUCCESS; nfc_device_t *device = NULL; MifareTag *tags = NULL; - (void)argc, (void)argv; while ((ch = getopt (argc, argv, "fhy")) != -1) { switch (ch) { case 'f': @@ -163,14 +162,25 @@ main(int argc, char *argv[]) argc -= optind; argv += optind; - device = nfc_connect (NULL); - if (!device) - errx (EXIT_FAILURE, "No NFC device found."); + nfc_device_desc_t devices[8]; + size_t device_count; + + nfc_list_devices (devices, 8, &device_count); + if (!device_count) + errx (EXIT_FAILURE, "No NFC device found."); + + for (size_t d = 0; d < device_count; d++) { + device = nfc_connect (&(devices[d])); + if (!device) { + warnx ("nfc_connect() failed."); + error = EXIT_FAILURE; + continue; + } tags = freefare_get_tags (device); if (!tags) { nfc_disconnect (device); - errx (EXIT_FAILURE, "Error listing MIFARE classic tag."); + errx (EXIT_FAILURE, "Error listing Mifare Classic tag."); } for (int i = 0; (!error) && tags[i]; i++) { @@ -233,6 +243,7 @@ main(int argc, char *argv[]) freefare_free_tags (tags); nfc_disconnect (device); + } exit (error); } diff --git a/examples/mifare-classic-write-ndef.c b/examples/mifare-classic-write-ndef.c index 66da7b4..9ab2b52 100644 --- a/examples/mifare-classic-write-ndef.c +++ b/examples/mifare-classic-write-ndef.c @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -124,9 +125,25 @@ main(int argc, char *argv[]) (void)argc; (void)argv; - device = nfc_connect (NULL); - if (!device) - errx (EXIT_FAILURE, "No NFC device found."); + struct mifare_classic_key_and_type *card_write_keys; + if (!(card_write_keys = malloc (40 * sizeof (*card_write_keys)))) { + err (EXIT_FAILURE, "malloc"); + } + + nfc_device_desc_t devices[8]; + size_t device_count; + + nfc_list_devices (devices, 8, &device_count); + if (!device_count) + errx (EXIT_FAILURE, "No NFC device found."); + + for (size_t d = 0; d < device_count; d++) { + device = nfc_connect (&(devices[d])); + if (!device) { + warnx ("nfc_connect() failed."); + error = EXIT_FAILURE; + continue; + } tags = freefare_get_tags (device); if (!tags) { @@ -134,11 +151,6 @@ main(int argc, char *argv[]) errx (EXIT_FAILURE, "Error listing MIFARE classic tag."); } - struct mifare_classic_key_and_type *card_write_keys; - if (!(card_write_keys = malloc (40 * sizeof (*card_write_keys)))) { - err (EXIT_FAILURE, "malloc"); - } - for (int i = 0; (!error) && tags[i]; i++) { switch (freefare_get_tag_type (tags[i])) { case CLASSIC_1K: @@ -290,6 +302,7 @@ error: freefare_free_tags (tags); nfc_disconnect (device); + } free (card_write_keys); diff --git a/examples/mifare-desfire-access.c b/examples/mifare-desfire-access.c new file mode 100644 index 0000000..2ff0b64 --- /dev/null +++ b/examples/mifare-desfire-access.c @@ -0,0 +1,139 @@ +/*- + * Copyright (C) 2010, Romain Tartiere. + * + * 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 +#include +#include +#include +#include + +#include + +#include + +uint8_t key_data_null[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +int +main(int argc, char *argv[]) +{ + int error = EXIT_SUCCESS; + nfc_device_t *device = NULL; + MifareTag *tags = NULL; + + if (argc > 1) + errx (EXIT_FAILURE, "usage: %s", argv[0]); + + nfc_device_desc_t devices[8]; + size_t device_count; + + nfc_list_devices (devices, 8, &device_count); + if (!device_count) + errx (EXIT_FAILURE, "No NFC device found."); + + for (size_t d = 0; d < device_count; d++) { + device = nfc_connect (&(devices[d])); + if (!device) { + warnx ("nfc_connect() failed."); + error = EXIT_FAILURE; + continue; + } + + tags = freefare_get_tags (device); + if (!tags) { + nfc_disconnect (device); + errx (EXIT_FAILURE, "Error listing tags."); + } + + for (int i = 0; (!error) && tags[i]; i++) { + switch (freefare_get_tag_type (tags[i])) { + case DESFIRE_4K: + break; + default: + continue; + } + + int res; + char *tag_uid = freefare_get_tag_uid (tags[i]); + + res = mifare_desfire_connect (tags[i]); + if (res < 0) { + warnx ("Can't connect to Mifare DESFire target."); + error = EXIT_FAILURE; + break; + } + + + MifareDESFireKey key = mifare_desfire_des_key_new_with_version (key_data_null); + res = mifare_desfire_authenticate (tags[i], 0, key); + if (res < 0) + errx (EXIT_FAILURE, "Authentication on master application failed"); + + MifareDESFireAID aid = mifare_desfire_aid_new (0x12, 0x34, 0x5); + res = mifare_desfire_create_application (tags[i], aid, 0xFF, 0x1); + if (res < 0) + errx (EXIT_FAILURE, "Application creation failed"); + + res = mifare_desfire_select_application (tags[i], aid); + if (res < 0) + errx (EXIT_FAILURE, "Application selection failed"); + + res = mifare_desfire_authenticate (tags[i], 0, key); + if (res < 0) + errx (EXIT_FAILURE, "Authentication on application failed"); + + res = mifare_desfire_create_std_data_file (tags[i], 1, MDCM_FULLDES, 0x0000, 20); + if (res < 0) + errx (EXIT_FAILURE, "File creation failed"); + + char *s= "Hello World"; + res = mifare_desfire_write_data (tags[i], 1, 0, strlen (s), s); + if (res < 0) + errx (EXIT_FAILURE, "File write failed"); + + char buffer[20]; + res = mifare_desfire_read_data (tags[i], 1, 0, 0, buffer); + if (res < 0) + errx (EXIT_FAILURE, "File read failed"); + + res = mifare_desfire_select_application (tags[i], NULL); + if (res < 0) + errx (EXIT_FAILURE, "Master application selection failed"); + + res = mifare_desfire_authenticate (tags[i], 0, key); + if (res < 0) + errx (EXIT_FAILURE, "Authentication on master application failed"); + + res = mifare_desfire_format_picc (tags[i]); + if (res < 0) + errx (EXIT_FAILURE, "PICC format failed"); + + mifare_desfire_key_free (key); + free (tag_uid); + free (aid); + + mifare_desfire_disconnect (tags[i]); + } + + freefare_free_tags (tags); + nfc_disconnect (device); + } + + exit (error); +} /* main() */ + diff --git a/examples/mifare-desfire-format.c b/examples/mifare-desfire-format.c new file mode 100644 index 0000000..8b4ba5c --- /dev/null +++ b/examples/mifare-desfire-format.c @@ -0,0 +1,152 @@ +/*- + * Copyright (C) 2010, Romain Tartiere. + * + * 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 + +#include + +#include + +uint8_t null_key_data[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +struct { + bool interactive; +} format_options = { + .interactive = true +}; + +void +usage(char *progname) +{ + fprintf (stderr, "usage: %s [-y]\n", progname); + fprintf (stderr, "\nOptions:\n"); + fprintf (stderr, " -y Do not ask for confirmation (dangerous)\n"); +} + +int +main(int argc, char *argv[]) +{ + int ch; + int error = EXIT_SUCCESS; + nfc_device_t *device = NULL; + MifareTag *tags = NULL; + + while ((ch = getopt (argc, argv, "hy")) != -1) { + switch (ch) { + case 'h': + usage(argv[0]); + exit (EXIT_SUCCESS); + break; + case 'y': + format_options.interactive = false; + break; + default: + usage(argv[0]); + exit (EXIT_FAILURE); + } + } + argc -= optind; + argv += optind; + + nfc_device_desc_t devices[8]; + size_t device_count; + + nfc_list_devices (devices, 8, &device_count); + if (!device_count) + errx (EXIT_FAILURE, "No NFC device found."); + + for (size_t d = 0; d < device_count; d++) { + device = nfc_connect (&(devices[d])); + if (!device) { + warnx ("nfc_connect() failed."); + error = EXIT_FAILURE; + continue; + } + + tags = freefare_get_tags (device); + if (!tags) { + nfc_disconnect (device); + errx (EXIT_FAILURE, "Error listing Mifare DESFire tags."); + } + + for (int i = 0; (!error) && tags[i]; i++) { + switch (freefare_get_tag_type (tags[i])) { + case DESFIRE_4K: + break; + default: + continue; + } + + char *tag_uid = freefare_get_tag_uid (tags[i]); + char buffer[BUFSIZ]; + + printf ("Found %s with UID %s.", freefare_get_tag_friendly_name (tags[i]), tag_uid); + bool format = true; + if (format_options.interactive) { + printf ("Format [yN] "); + fgets (buffer, BUFSIZ, stdin); + format = ((buffer[0] == 'y') || (buffer[0] == 'Y')); + } else { + printf ("\n"); + } + + if (format) { + int res; + MifareDESFireKey default_key = mifare_desfire_des_key_new_with_version (null_key_data); + + res = mifare_desfire_connect (tags[i]); + if (res < 0) { + warnx ("Can't connect to Mifare DESFire target."); + error = EXIT_FAILURE; + break; + } + + res = mifare_desfire_authenticate (tags[i], 0, default_key); + if (res < 0) { + warnx ("Can't authenticate on Mifare DESFire target."); + error = EXIT_FAILURE; + break; + } + + res = mifare_desfire_format_picc (tags[i]); + if (res < 0) { + warn ("Can't format PICC."); + error = EXIT_FAILURE; + break; + } + + mifare_desfire_disconnect (tags[i]); + } + + free (tag_uid); + } + + freefare_free_tags (tags); + nfc_disconnect (device); + } + + exit (error); +} /* main() */ + diff --git a/examples/mifare-desfire-info.c b/examples/mifare-desfire-info.c new file mode 100644 index 0000000..c5b52c2 --- /dev/null +++ b/examples/mifare-desfire-info.c @@ -0,0 +1,118 @@ +/*- + * Copyright (C) 2010, Romain Tartiere. + * + * 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 +#include +#include +#include +#include + +#include + +#include + +int +main(int argc, char *argv[]) +{ + int error = EXIT_SUCCESS; + nfc_device_t *device = NULL; + MifareTag *tags = NULL; + + if (argc > 1) + errx (EXIT_FAILURE, "usage: %s", argv[0]); + + nfc_device_desc_t devices[8]; + size_t device_count; + + nfc_list_devices (devices, 8, &device_count); + if (!device_count) + errx (EXIT_FAILURE, "No NFC device found."); + + for (size_t d = 0; d < device_count; d++) { + device = nfc_connect (&(devices[d])); + if (!device) { + warnx ("nfc_connect() failed."); + error = EXIT_FAILURE; + continue; + } + + tags = freefare_get_tags (device); + if (!tags) { + nfc_disconnect (device); + errx (EXIT_FAILURE, "Error listing tags."); + } + + for (int i = 0; (!error) && tags[i]; i++) { + switch (freefare_get_tag_type (tags[i])) { + case DESFIRE_4K: + break; + default: + continue; + } + + int res; + char *tag_uid = freefare_get_tag_uid (tags[i]); + + struct mifare_desfire_version_info info; + + res = mifare_desfire_connect (tags[i]); + if (res < 0) { + warnx ("Can't connect to Mifare DESFire target."); + error = 1; + break; + } + + res = mifare_desfire_get_version (tags[i], &info); + if (res < 0) { + warnx ("Can't get Mifare DESFire version information."); + error = 1; + break; + } + + printf ("===> Version information for tag %s:\n", tag_uid); + printf ("UID: 0x%02x%02x%02x%02x%02x%02x%02x\n", info.uid[0], info.uid[1], info.uid[2], info.uid[3], info.uid[4], info.uid[5], info.uid[6]); + printf ("Batch number: 0x%02x%02x%02x%02x%02x\n", info.batch_number[0], info.batch_number[1], info.batch_number[2], info.batch_number[3], info.batch_number[4]); + printf ("Production date: week %x, 20%02x\n", info.production_week, info.production_year); + printf ("Hardware Information:\n"); + printf (" Vendor ID: 0x%02x\n", info.hardware.vendor_id); + printf (" Type: 0x%02x\n", info.hardware.type); + printf (" Subtype: 0x%02x\n", info.hardware.subtype); + printf (" Version: %d.%d\n", info.hardware.version_major, info.hardware.version_minor); + printf (" Storage size: 0x%02x (%s%d bytes)\n", info.hardware.storage_size, (info.hardware.storage_size & 1) ? ">" : "=", (int)pow (2, info.hardware.storage_size >> 1)); + printf (" Protocol: 0x%02x\n", info.hardware.protocol); + printf ("Software Information:\n"); + printf (" Vendor ID: 0x%02x\n", info.software.vendor_id); + printf (" Type: 0x%02x\n", info.software.type); + printf (" Subtype: 0x%02x\n", info.software.subtype); + printf (" Version: %d.%d\n", info.software.version_major, info.software.version_minor); + printf (" Storage size: 0x%02x (%s%d bytes)\n", info.software.storage_size, (info.software.storage_size & 1) ? ">" : "=", (int)pow (2, info.software.storage_size >> 1)); + printf (" Protocol: 0x%02x\n", info.software.protocol); + + free (tag_uid); + + mifare_desfire_disconnect (tags[i]); + } + + freefare_free_tags (tags); + nfc_disconnect (device); + } + + exit (error); +} /* main() */ + diff --git a/libfreefare/Makefile.am b/libfreefare/Makefile.am index 1ceb52a..73e7b5b 100644 --- a/libfreefare/Makefile.am +++ b/libfreefare/Makefile.am @@ -1,69 +1,133 @@ -AM_CFLAGS = -I. @LIBNFC_CFLAGS@ +AM_CFLAGS = @LIBNFC_CFLAGS@ AM_LDFLAGS = @LIBNFC_LIBS@ lib_LTLIBRARIES = libfreefare.la +# TODO: Remove hexdump.c +# XXX: Remove hexdump.c on systems with a hexdump(1) function in libutil. libfreefare_la_SOURCES = freefare.c \ + hexdump.c \ mifare_classic.c \ mifare_ultralight.c \ + mifare_desfire.c \ + mifare_desfire_aid.c \ + mifare_desfire_authenticate.c \ + mifare_desfire_key.c \ mad.c \ + desfire_error.c \ mifare_application.c \ tlv.c + +# TODO: Remove this +# XXX: Uncomment on FreeBSD to link with libutil which provides hexdump(1). +# libfreefare_la_LIBADD = -lutil + libfreefare_la_HEADERS = freefare.h libfreefare_ladir = $(includedir) man_MANS = freefare.3 \ - mifare_ultralight.3 \ - mifare_classic.3 \ mad.3 \ mifare_application.3 \ + mifare_classic.3 \ + mifare_desfire.3 \ + mifare_desfire_key.3 \ + mifare_ultralight.3 \ tlv.3 linkedman = \ - freefare.3 freefare_get_tags.3 \ + freefare.3 freefare_free_tags.3 \ + freefare.3 freefare_get_tag_friendly_name.3 \ freefare.3 freefare_get_tag_type.3 \ freefare.3 freefare_get_tag_uid.3 \ - freefare.3 freefare_get_tag_friendly_name.3 \ - freefare.3 freefare_free_tags.3 \ - mifare_ultralight.3 mifare_ultralight_connect.3 \ - mifare_ultralight.3 mifare_ultralight_disconnect.3 \ - mifare_ultralight.3 mifare_ultralight_read.3 \ - mifare_ultralight.3 mifare_ultralight_write.3 \ - mifare_ultralight.3 mifare_ultralight_get_uid.3 \ - mifare_classic.3 mifare_classic_connect.3 \ - mifare_classic.3 mifare_classic_disconnect.3 \ - mifare_classic.3 mifare_classic_authenticate.3 \ - mifare_classic.3 mifare_classic_read.3 \ - mifare_classic.3 mifare_classic_init_value.3 \ - mifare_classic.3 mifare_classic_read_value.3 \ - mifare_classic.3 mifare_classic_write.3 \ - mifare_classic.3 mifare_classic_increment.3 \ - mifare_classic.3 mifare_classic_decrement.3 \ - mifare_classic.3 mifare_classic_restore.3 \ - mifare_classic.3 mifare_classic_transfer.3 \ - mifare_classic.3 mifare_classic_get_trailer_block_permission.3 \ - mifare_classic.3 mifare_classic_get_data_block_permission.3 \ - mifare_classic.3 mifare_classic_format_sector.3 \ - mifare_classic.3 mifare_classic_get_uid.3 \ - mifare_classic.3 mifare_classic_trailer_block.3 \ - mad.3 mad_new.3 \ - mad.3 mad_read.3 \ - mad.3 mad_write.3 \ - mad.3 mad_get_version.3 \ - mad.3 mad_set_version.3 \ - mad.3 mad_get_card_publisher_sector.3 \ - mad.3 mad_set_card_publisher_sector.3 \ - mad.3 mad_get_aid.3 \ - mad.3 mad_set_aid.3 \ - mad.3 mad_free.3 \ + freefare.3 freefare_get_tags.3 \ mad.3 mad_application_read.3 \ mad.3 mad_application_write.3 \ + mad.3 mad_free.3 \ + mad.3 mad_get_aid.3 \ + mad.3 mad_get_card_publisher_sector.3 \ + mad.3 mad_get_version.3 \ + mad.3 mad_new.3 \ + mad.3 mad_read.3 \ + mad.3 mad_set_aid.3 \ + mad.3 mad_set_card_publisher_sector.3 \ + mad.3 mad_set_version.3 \ + mad.3 mad_write.3 \ mifare_application.3 mifare_application_alloc.3 \ - mifare_application.3 mifare_application_free.3 \ mifare_application.3 mifare_application_find.3 \ - tlv.3 tlv_encode.3 \ - tlv.3 tlv_decode.3 + mifare_application.3 mifare_application_free.3 \ + mifare_classic.3 mifare_classic_authenticate.3 \ + mifare_classic.3 mifare_classic_connect.3 \ + mifare_classic.3 mifare_classic_decrement.3 \ + mifare_classic.3 mifare_classic_disconnect.3 \ + mifare_classic.3 mifare_classic_format_sector.3 \ + mifare_classic.3 mifare_classic_get_data_block_permission.3 \ + mifare_classic.3 mifare_classic_get_trailer_block_permission.3 \ + mifare_classic.3 mifare_classic_get_uid.3 \ + mifare_classic.3 mifare_classic_increment.3 \ + mifare_classic.3 mifare_classic_init_value.3 \ + mifare_classic.3 mifare_classic_read.3 \ + mifare_classic.3 mifare_classic_read_value.3 \ + mifare_classic.3 mifare_classic_restore.3 \ + mifare_classic.3 mifare_classic_trailer_block.3 \ + mifare_classic.3 mifare_classic_transfer.3 \ + mifare_classic.3 mifare_classic_write.3 \ + mifare_desfire.3 mifare_desfire_abort_transaction.3 \ + mifare_desfire.3 mifare_desfire_authenticate.3 \ + mifare_desfire.3 mifare_desfire_change_file_settings.3 \ + mifare_desfire.3 mifare_desfire_change_key.3 \ + mifare_desfire.3 mifare_desfire_change_key_settings.3 \ + mifare_desfire.3 mifare_desfire_clear_record_file.3 \ + mifare_desfire.3 mifare_desfire_commit_transaction.3 \ + mifare_desfire.3 mifare_desfire_connect.3 \ + mifare_desfire.3 mifare_desfire_create_application.3 \ + mifare_desfire.3 mifare_desfire_create_backup_data_file.3 \ + mifare_desfire.3 mifare_desfire_create_cyclic_record_file.3 \ + mifare_desfire.3 mifare_desfire_create_linear_record_file.3 \ + mifare_desfire.3 mifare_desfire_create_std_data_file.3 \ + mifare_desfire.3 mifare_desfire_create_value_file.3 \ + mifare_desfire.3 mifare_desfire_credit.3 \ + mifare_desfire.3 mifare_desfire_credit_ex.3 \ + mifare_desfire.3 mifare_desfire_debit.3 \ + mifare_desfire.3 mifare_desfire_debit_ex.3 \ + mifare_desfire.3 mifare_desfire_delete_application.3 \ + mifare_desfire.3 mifare_desfire_delete_file.3 \ + mifare_desfire.3 mifare_desfire_disconnect.3 \ + mifare_desfire.3 mifare_desfire_format_picc.3 \ + mifare_desfire.3 mifare_desfire_free_application_ids.3 \ + mifare_desfire.3 mifare_desfire_get_application_ids.3 \ + mifare_desfire.3 mifare_desfire_get_file_ids.3 \ + mifare_desfire.3 mifare_desfire_get_file_settings.3 \ + mifare_desfire.3 mifare_desfire_get_key_settings.3 \ + mifare_desfire.3 mifare_desfire_get_key_version.3 \ + mifare_desfire.3 mifare_desfire_get_value.3 \ + mifare_desfire.3 mifare_desfire_get_value_ex.3 \ + mifare_desfire.3 mifare_desfire_get_version.3 \ + mifare_desfire.3 mifare_desfire_limited_credit.3 \ + mifare_desfire.3 mifare_desfire_limited_credit_ex.3 \ + mifare_desfire.3 mifare_desfire_read_data.3 \ + mifare_desfire.3 mifare_desfire_read_data_ex.3 \ + mifare_desfire.3 mifare_desfire_read_records.3 \ + mifare_desfire.3 mifare_desfire_read_records_ex.3 \ + mifare_desfire.3 mifare_desfire_select_application.3 \ + mifare_desfire.3 mifare_desfire_write_data.3 \ + mifare_desfire.3 mifare_desfire_write_data_ex.3 \ + mifare_desfire.3 mifare_desfire_write_record.3 \ + mifare_desfire.3 mifare_desfire_write_record_ex.3 \ + mifare_desfire_key.3 mifare_desfire_3des_key_new.3 \ + mifare_desfire_key.3 mifare_desfire_3des_key_new_with_version.3 \ + mifare_desfire_key.3 mifare_desfire_des_key_new.3 \ + mifare_desfire_key.3 mifare_desfire_des_key_new_with_version.3 \ + mifare_desfire_key.3 mifare_desfire_key_free.3 \ + mifare_desfire_key.3 mifare_desfire_key_get_version.3 \ + mifare_desfire_key.3 mifare_desfire_key_set_version.3 \ + mifare_ultralight.3 mifare_ultralight_connect.3 \ + mifare_ultralight.3 mifare_ultralight_disconnect.3 \ + mifare_ultralight.3 mifare_ultralight_get_uid.3 \ + mifare_ultralight.3 mifare_ultralight_read.3 \ + mifare_ultralight.3 mifare_ultralight_write.3 \ + tlv.3 tlv_decode.3 \ + tlv.3 tlv_encode.3 install-data-hook: (cd $(DESTDIR)$(man3dir); for i in `echo $(linkedman) | xargs -n2 echo | awk '{print $$2}'`; do rm -f $$i; done; echo $(linkedman) | xargs -n2 $(LN_S)) @@ -72,3 +136,4 @@ uninstall-hook: (cd $(DESTDIR)$(man3dir); for i in `echo $(linkedman) | xargs -n2 echo | awk '{print $$2}'`; do rm -f $$i; done;) EXTRA_DIST = freefare_internal.h $(man_MANS) +CLEANFILES = *.gcno diff --git a/libfreefare/desfire_error.c b/libfreefare/desfire_error.c new file mode 100644 index 0000000..0a301cf --- /dev/null +++ b/libfreefare/desfire_error.c @@ -0,0 +1,69 @@ +/*- + * Copyright (C) 2010, Romain Tartiere, Romuald Conty. + * + * 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 + +#include + +#include + +#define EM(e) { e, #e } + +static struct error_message { + uint8_t code; + char *message; +} error_messages[] = { + EM(OPERATION_OK), + EM(NO_CHANGES), + EM(OUT_OF_EEPROM_ERROR), + EM(ILLEGAL_COMMAND_CODE), + EM(INTEGRITY_ERROR), + EM(NO_SUCH_KEY), + EM(LENGTH_ERROR), + EM(PERMISSION_ERROR), + EM(PARAMETER_ERROR), + EM(APPLICATION_NOT_FOUND), + EM(APPL_INTEGRITY_ERROR), + EM(AUTHENTICATION_ERROR), + EM(ADDITIONAL_FRAME), + EM(BOUNDARY_ERROR), + EM(PICC_INTEGRITY_ERROR), + EM(COMMAND_ABORTED), + EM(PICC_DISABLED_ERROR), + EM(COUNT_ERROR), + EM(DUPLICATE_ERROR), + EM(EEPROM_ERROR), + EM(FILE_NOT_FOUND), + EM(FILE_INTEGRITY_ERROR), + { 0, NULL } +}; + +const char * +desfire_error_lookup (uint8_t code) +{ + struct error_message *e = error_messages; + while (e->message) { + if (e->code == code) + return (e->message); + e++; + } + + return "Invalid error code"; +} diff --git a/libfreefare/freefare.c b/libfreefare/freefare.c index 38fa170..5ee505f 100644 --- a/libfreefare/freefare.c +++ b/libfreefare/freefare.c @@ -1,4 +1,4 @@ -/* +/*- * Copyright (C) 2010, Romain Tartiere, Romuald Conty. * * This program is free software: you can redistribute it and/or modify it @@ -24,10 +24,11 @@ #include "freefare_internal.h" struct supported_tag supported_tags[] = { - { { 0x00, 0x44 }, 0x00, ULTRALIGHT, "Mifare UltraLight" }, { { 0x00, 0x04 }, 0x08, CLASSIC_1K, "Mifare Classic 1k" }, { { 0x00, 0x02 }, 0x18, CLASSIC_4K, "Mifare Classic 4k" }, { { 0x00, 0x02 }, 0x38, CLASSIC_4K, "Mifare Classic 4k (Emulated)" }, + { { 0x03, 0x44 }, 0x20, DESFIRE_4K, "Mifare DESFire 4k" }, + { { 0x00, 0x44 }, 0x00, ULTRALIGHT, "Mifare UltraLight" }, }; @@ -106,6 +107,9 @@ freefare_get_tags (nfc_device_t *device) case CLASSIC_4K: tags[tag_count-1] = mifare_classic_tag_new (); break; + case DESFIRE_4K: + tags[tag_count-1] = mifare_desfire_tag_new (); + break; case ULTRALIGHT: tags[tag_count-1] = mifare_ultralight_tag_new (); break; @@ -172,6 +176,9 @@ freefare_free_tag (MifareTag tag) case CLASSIC_4K: mifare_classic_tag_free (tag); break; + case DESFIRE_4K: + mifare_desfire_tag_free (tag); + break; case ULTRALIGHT: mifare_ultralight_tag_free (tag); break; diff --git a/libfreefare/freefare.h b/libfreefare/freefare.h index ac13ed2..3e22145 100644 --- a/libfreefare/freefare.h +++ b/libfreefare/freefare.h @@ -1,5 +1,5 @@ /*- - * Copyright (C) 2009, Romain Tartiere, Romuald Conty. + * Copyright (C) 2009, 2010, Romain Tartiere, Romuald Conty. * * 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 @@ -41,7 +41,7 @@ enum mifare_tag_type { // PLUS_X2K, // PLUS_X4K, // DESFIRE_2K, -// DESFIRE_4K, + DESFIRE_4K, // DESFIRE_8K }; @@ -152,6 +152,191 @@ void mifare_application_free (Mad mad, const MadAid aid); MifareClassicSectorNumber *mifare_application_find (Mad mad, const MadAid aid); +/* File types */ + +enum mifare_desfire_file_types { + MDFT_STANDARD_DATA_FILE = 0x00, + MDFT_BACKUP_DATA_FILE = 0x01, + MDFT_VALUE_FILE_WITH_BACKUP = 0x02, + MDFT_LINEAR_RECORD_FILE_WITH_BACKUP = 0x03, + MDFT_CYCLIC_RECORD_FILE_WITH_BACKUP = 0x04 +}; + +/* Communication mode */ + +#define MDCM_PLAIN 0x00 +#define MDCM_MACING 0x01 +#define MDCM_FULLDES 0x03 + +/* Access right */ + +#define MDAR(read,write,read_write,change_access_rights) ( \ + (read << 12) | \ + (write << 8) | \ + (read_write << 4) | \ + (change_access_rights) \ + ) +#define MDAR_READ(ar) (((ar) >> 12) & 0x0f) +#define MDAR_WRITE(ar) (((ar) >> 8) & 0x0f) +#define MDAR_READ_WRITE(ar) (((ar) >> 4) & 0x0f) +#define MDAR_CHANGE_AR(ar) ((ar) & 0x0f) + +#define MDAD_KEY0 0x0 +#define MDAD_KEY1 0x1 +#define MDAD_KEY2 0x2 +#define MDAD_KEY3 0x3 +#define MDAD_KEY4 0x4 +#define MDAD_KEY5 0x5 +#define MDAD_KEY6 0x6 +#define MDAD_KEY7 0x7 +#define MDAD_KEY8 0x8 +#define MDAD_KEY9 0x9 +#define MDAD_KEY10 0xa +#define MDAD_KEY11 0xb +#define MDAD_KEY12 0xc +#define MDAD_KEY13 0xd +#define MDAR_FREE 0xE +#define MDAR_DENY 0xF + +/* Status and error codes */ + +#define OPERATION_OK 0x00 +#define NO_CHANGES 0x0C +#define OUT_OF_EEPROM_ERROR 0x0E +#define ILLEGAL_COMMAND_CODE 0x1C +#define INTEGRITY_ERROR 0x1E +#define NO_SUCH_KEY 0x40 +#define LENGTH_ERROR 0x7E +#define PERMISSION_ERROR 0x9D +#define PARAMETER_ERROR 0x9E +#define APPLICATION_NOT_FOUND 0xA0 +#define APPL_INTEGRITY_ERROR 0xA1 +#define AUTHENTICATION_ERROR 0xAE +#define ADDITIONAL_FRAME 0xAF +#define BOUNDARY_ERROR 0xBE +#define PICC_INTEGRITY_ERROR 0xC1 +#define COMMAND_ABORTED 0xCA +#define PICC_DISABLED_ERROR 0xCD +#define COUNT_ERROR 0xCE +#define DUPLICATE_ERROR 0xDE +#define EEPROM_ERROR 0xEE +#define FILE_NOT_FOUND 0xF0 +#define FILE_INTEGRITY_ERROR 0xF1 + +struct mifare_desfire_aid; +typedef struct mifare_desfire_aid *MifareDESFireAID; + +MifareDESFireAID mifare_desfire_aid_new (uint8_t application_code, uint8_t function_cluster_code, uint8_t n); +MifareDESFireAID mifare_desfire_aid_new_with_mad_aid (MadAid mad_aid, uint8_t n); + +struct mifare_desfire_key; +typedef struct mifare_desfire_key *MifareDESFireKey; + +#pragma pack (push) +#pragma pack (1) +struct mifare_desfire_version_info { + struct { + uint8_t vendor_id; + uint8_t type; + uint8_t subtype; + uint8_t version_major; + uint8_t version_minor; + uint8_t storage_size; + uint8_t protocol; + } hardware; + struct { + uint8_t vendor_id; + uint8_t type; + uint8_t subtype; + uint8_t version_major; + uint8_t version_minor; + uint8_t storage_size; + uint8_t protocol; + } software; + uint8_t uid[7]; + uint8_t batch_number[5]; + uint8_t production_week; + uint8_t production_year; +}; +#pragma pack (pop) + +struct mifare_desfire_file_settings { + uint8_t file_type; + uint8_t communication_settings; + uint16_t access_rights; + union { + struct { + uint32_t file_size; + } standard_file; + struct { + int32_t lower_limit; + int32_t upper_limit; + int32_t limited_credit_value; + uint8_t limited_credit_enabled; + } value_file; + struct { + uint32_t record_size; + uint32_t max_number_of_records; + uint32_t current_number_of_records; + } linear_record_file; + } settings; +}; + +int mifare_desfire_connect (MifareTag tag); +int mifare_desfire_disconnect (MifareTag tag); +uint8_t mifare_desfire_get_last_error (MifareTag tag); + +int mifare_desfire_authenticate (MifareTag tag, uint8_t key_no, MifareDESFireKey key); +int mifare_desfire_change_key_settings (MifareTag tag, uint8_t settings); +int mifare_desfire_get_key_settings (MifareTag tag, uint8_t *settings, uint8_t *max_keys); +int mifare_desfire_change_key (MifareTag tag, uint8_t key_no, MifareDESFireKey new_key, MifareDESFireKey old_key); +int mifare_desfire_get_key_version (MifareTag tag, uint8_t key_no, uint8_t *version); +int mifare_desfire_create_application (MifareTag tag, MifareDESFireAID aid, uint8_t settings, uint8_t key_no); +int mifare_desfire_delete_application (MifareTag tag, MifareDESFireAID aid); +int mifare_desfire_get_application_ids (MifareTag tag, MifareDESFireAID *aids[], size_t *count); +void mifare_desfire_free_application_ids (MifareDESFireAID aids[]); +int mifare_desfire_select_application (MifareTag tag, MifareDESFireAID aid); +int mifare_desfire_format_picc (MifareTag tag); +int mifare_desfire_get_version (MifareTag tag, struct mifare_desfire_version_info *version_info); +int mifare_desfire_get_file_ids (MifareTag tag, uint8_t *files[], size_t *count); +int mifare_desfire_get_file_settings (MifareTag tag, uint8_t file_no, struct mifare_desfire_file_settings *settings); +int mifare_desfire_change_file_settings (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights); +int mifare_desfire_create_std_data_file (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t file_size); +int mifare_desfire_create_backup_data_file (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t file_size); +int mifare_desfire_create_value_file (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, int32_t lower_limit, int32_t upper_limit, int32_t value, uint8_t limited_credit_enable); +int mifare_desfire_create_linear_record_file (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t record_size, uint32_t max_number_of_records); +int mifare_desfire_create_cyclic_record_file (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t record_size, uint32_t max_number_of_records); +int mifare_desfire_delete_file (MifareTag tag, uint8_t file_no); + +ssize_t mifare_desfire_read_data (MifareTag tag, uint8_t file_no, off_t offset, size_t length, void *data); +ssize_t mifare_desfire_read_data_ex (MifareTag tag, uint8_t file_no, off_t offset, size_t length, void *data, int cs); +ssize_t mifare_desfire_write_data (MifareTag tag, uint8_t file_no, off_t offset, size_t length, void *data); +ssize_t mifare_desfire_write_data_ex (MifareTag tag, uint8_t file_no, off_t offset, size_t length, void *data, int cs); +int mifare_desfire_get_value (MifareTag tag, uint8_t file_no, int32_t *value); +int mifare_desfire_get_value_ex (MifareTag tag, uint8_t file_no, int32_t *value, int cs); +int mifare_desfire_credit (MifareTag tag, uint8_t file_no, int32_t amount); +int mifare_desfire_credit_ex (MifareTag tag, uint8_t file_no, int32_t amount, int cs); +int mifare_desfire_debit (MifareTag tag, uint8_t file_no, int32_t amount); +int mifare_desfire_debit_ex (MifareTag tag, uint8_t file_no, int32_t amount, int cs); +int mifare_desfire_limited_credit (MifareTag tag, uint8_t file_no, int32_t amount); +int mifare_desfire_limited_credit_ex (MifareTag tag, uint8_t file_no, int32_t amount, int cs); +ssize_t mifare_desfire_write_record (MifareTag tag, uint8_t file_no, off_t offset, size_t length, void *data); +ssize_t mifare_desfire_write_record_ex (MifareTag tag, uint8_t file_no, off_t offset, size_t length, void *data, int cs); +ssize_t mifare_desfire_read_records (MifareTag tag, uint8_t file_no, off_t offset, size_t length, void *data); +ssize_t mifare_desfire_read_records_ex (MifareTag tag, uint8_t file_no, off_t offset, size_t length, void *data, int cs); +int mifare_desfire_clear_record_file (MifareTag tag, uint8_t file_no); +int mifare_desfire_commit_transaction (MifareTag tag); +int mifare_desfire_abort_transaction (MifareTag tag); + +MifareDESFireKey mifare_desfire_des_key_new (uint8_t value[8]); +MifareDESFireKey mifare_desfire_3des_key_new (uint8_t value[16]); +MifareDESFireKey mifare_desfire_des_key_new_with_version (uint8_t value[8]); +MifareDESFireKey mifare_desfire_3des_key_new_with_version (uint8_t value[16]); +uint8_t mifare_desfire_key_get_version (MifareDESFireKey key); +void mifare_desfire_key_set_version (MifareDESFireKey key, uint8_t version); +void mifare_desfire_key_free (MifareDESFireKey key); + +const char *desfire_error_lookup (uint8_t error); uint8_t *tlv_encode (const uint8_t type, const uint8_t *istream, uint16_t isize, size_t *osize); uint8_t *tlv_decode (const uint8_t *istream, uint8_t *type, uint16_t *size); @@ -161,5 +346,4 @@ uint8_t *tlv_append (uint8_t *a, uint8_t *b); } #endif // __cplusplus - #endif /* !__FREEFARE_H__ */ diff --git a/libfreefare/freefare_internal.h b/libfreefare/freefare_internal.h index 2741bab..aefbaf0 100644 --- a/libfreefare/freefare_internal.h +++ b/libfreefare/freefare_internal.h @@ -1,4 +1,4 @@ -/* +/*- * Copyright (C) 2010, Romain Tartiere, Romuald Conty. * * This program is free software: you can redistribute it and/or modify it @@ -22,6 +22,8 @@ #include "config.h" +#include + /* * Endienness macros * @@ -39,10 +41,6 @@ * dealt with). */ -#if !defined(le32toh) && defined(letoh32) -# define le32toh(x) letoh32(x) -#endif - #if !defined(le32toh) && defined(bswap_32) # if BYTE_ORDER == LITTLE_ENDIAN # define be32toh(x) bswap_32(x) @@ -71,6 +69,8 @@ # endif #endif +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) struct mad_sector_0x00; struct mad_sector_0x10; @@ -78,11 +78,24 @@ struct mad_sector_0x10; void nxp_crc (uint8_t *crc, const uint8_t value); MifareTag mifare_classic_tag_new (void); void mifare_classic_tag_free (MifareTag tag); +MifareTag mifare_desfire_tag_new (void); +void mifare_desfire_tag_free (MifareTag tags); MifareTag mifare_ultralight_tag_new (void); void mifare_ultralight_tag_free (MifareTag tag); uint8_t sector_0x00_crc8 (Mad mad); uint8_t sector_0x10_crc8 (Mad mad); +typedef enum { + MD_SEND, + MD_RECEIVE +} MifareDirection; + +void *mifare_cryto_preprocess_data (MifareTag tag, void *data, size_t *nbytes, int communication_settings); +void *mifare_cryto_postprocess_data (MifareTag tag, void *data, ssize_t *nbytes, int communication_settings); +void mifare_cbc_des (MifareDESFireKey key, uint8_t *data, size_t data_size, MifareDirection direction, int mac); +void rol8(uint8_t *data); +void *assert_crypto_buffer_size (MifareTag tag, size_t nbytes); + #define MIFARE_ULTRALIGHT_PAGE_COUNT 16 struct supported_tag { @@ -123,6 +136,33 @@ struct mifare_classic_tag { } cached_access_bits; }; +struct mifare_desfire_aid { + uint8_t data[3]; +}; + +struct mifare_desfire_key { + uint8_t data[16]; + enum { + T_DES, + T_3DES + } type; + DES_key_schedule ks1; + DES_key_schedule ks2; +}; + +struct mifare_desfire_tag { + struct mifare_tag __tag; + + uint8_t last_picc_error; + char *last_pcd_error; + MifareDESFireKey session_key; + uint8_t authenticated_key_no; + uint8_t *crypto_buffer; + size_t crypto_buffer_size; +}; + +MifareDESFireKey mifare_desfire_session_key_new (uint8_t rnda[8], uint8_t rndb[8], MifareDESFireKey authentication_key); + struct mifare_ultralight_tag { struct mifare_tag __tag; @@ -140,8 +180,9 @@ struct mifare_ultralight_tag { #define ASSERT_ACTIVE(tag) do { if (!tag->active) return errno = ENXIO, -1; } while (0) #define ASSERT_INACTIVE(tag) do { if (tag->active) return errno = ENXIO, -1; } while (0) -#define ASSERT_MIFARE_ULTRALIGHT(tag) do { if (tag->tag_info->type != ULTRALIGHT) return errno = ENODEV, -1; } while (0) #define ASSERT_MIFARE_CLASSIC(tag) do { if ((tag->tag_info->type != CLASSIC_1K) && (tag->tag_info->type != CLASSIC_4K)) return errno = ENODEV, -1; } while (0) +#define ASSERT_MIFARE_DESFIRE(tag) do { if (tag->tag_info->type != DESFIRE_4K) return errno = ENODEV, -1; } while (0) +#define ASSERT_MIFARE_ULTRALIGHT(tag) do { if (tag->tag_info->type != ULTRALIGHT) return errno = ENODEV, -1; } while (0) /* * MifareTag cast macros @@ -150,6 +191,7 @@ struct mifare_ultralight_tag { * MifareTag structures to concrete Tags (e.g. MIFARE Classic tag). */ #define MIFARE_CLASSIC(tag) ((struct mifare_classic_tag *) tag) +#define MIFARE_DESFIRE(tag) ((struct mifare_desfire_tag *) tag) #define MIFARE_ULTRALIGHT(tag) ((struct mifare_ultralight_tag *) tag) /* diff --git a/libfreefare/hexdump.c b/libfreefare/hexdump.c new file mode 100644 index 0000000..ea79f0d --- /dev/null +++ b/libfreefare/hexdump.c @@ -0,0 +1,96 @@ +/*- + * Copyright (c) 1986, 1988, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * 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. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + * + * @(#)subr_prf.c 8.3 (Berkeley) 1/21/94 + */ + +//#include +//__FBSDID("$FreeBSD: stable/8/lib/libutil/hexdump.c 180161 2008-07-01 22:30:57Z jhb $"); + +#include +#include +#include + +void +hexdump(const void *ptr, int length, const char *hdr, int flags) +{ + int i, j, k; + int cols; + const unsigned char *cp; + char delim; + + if ((flags & HD_DELIM_MASK) != 0) + delim = (flags & HD_DELIM_MASK) >> 8; + else + delim = ' '; + + if ((flags & HD_COLUMN_MASK) != 0) + cols = flags & HD_COLUMN_MASK; + else + cols = 16; + + cp = ptr; + for (i = 0; i < length; i+= cols) { + if (hdr != NULL) + printf("%s", hdr); + + if ((flags & HD_OMIT_COUNT) == 0) + printf("%04x ", i); + + if ((flags & HD_OMIT_HEX) == 0) { + for (j = 0; j < cols; j++) { + k = i + j; + if (k < length) + printf("%c%02x", delim, cp[k]); + else + printf(" "); + } + } + + if ((flags & HD_OMIT_CHARS) == 0) { + printf(" |"); + for (j = 0; j < cols; j++) { + k = i + j; + if (k >= length) + printf(" "); + else if (cp[k] >= ' ' && cp[k] <= '~') + printf("%c", cp[k]); + else + printf("."); + } + printf("|"); + } + printf("\n"); + } +} + diff --git a/libfreefare/mad.c b/libfreefare/mad.c index 4f176ba..f15ad3f 100644 --- a/libfreefare/mad.c +++ b/libfreefare/mad.c @@ -32,6 +32,7 @@ #include #include #include +#include #include @@ -46,8 +47,6 @@ #define SECTOR_0X00_AIDS 15 #define SECTOR_0X10_AIDS 23 -#define MIN(a, b) ( (a < b) ? a : b ) - struct mad_sector_0x00 { uint8_t crc; uint8_t info; @@ -83,8 +82,8 @@ mad_new (uint8_t version) return NULL; mad->version = version; - memset (&(mad->sector_0x00), '\0', sizeof (mad->sector_0x00)); - memset (&(mad->sector_0x10), '\0', sizeof (mad->sector_0x10)); + bzero (&(mad->sector_0x00), sizeof (mad->sector_0x00)); + bzero (&(mad->sector_0x10), sizeof (mad->sector_0x10)); return mad; } @@ -331,7 +330,7 @@ mad_set_version (Mad mad, const uint8_t version) { if ((version == 2) && (mad->version == 1)) { /* We use a larger MAD so initialise the new blocks */ - memset (&(mad->sector_0x10), '\0', sizeof (mad->sector_0x10)); + bzero (&(mad->sector_0x10), sizeof (mad->sector_0x10)); } mad->version = version; } diff --git a/libfreefare/mifare_classic.c b/libfreefare/mifare_classic.c index 43b8cfa..c3f8e5c 100644 --- a/libfreefare/mifare_classic.c +++ b/libfreefare/mifare_classic.c @@ -55,6 +55,7 @@ #include #include #include +#include #include @@ -663,7 +664,7 @@ mifare_classic_format_sector (MifareTag tag, const MifareClassicSectorNumber sec } MifareClassicBlock empty_data_block; - memset (empty_data_block, '\x00', sizeof (empty_data_block)); + bzero (empty_data_block, sizeof (empty_data_block)); MifareClassicBlock default_trailer_block = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* Key A */ diff --git a/libfreefare/mifare_desfire.3 b/libfreefare/mifare_desfire.3 new file mode 100644 index 0000000..e618789 --- /dev/null +++ b/libfreefare/mifare_desfire.3 @@ -0,0 +1,525 @@ +.\" Copyright (C) 2010 Romain Tartiere +.\" +.\" 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$ +.\" +.Dd July 11, 2010 +.Dt MIFARE_DESFIRE 3 +.Os +.\" _ _ +.\" | \ | | __ _ _ __ ___ ___ +.\" | \| |/ _` | '_ ` _ \ / _ \ +.\" | |\ | (_| | | | | | | __/ +.\" |_| \_|\__,_|_| |_| |_|\___| +.\" +.Sh NAME +.Nm mifare_desfire_connect , +.Nm mifare_desfire_disconnect . +.\" +.Nm mifare_desfire_authenticate , +.Nm mifare_desfire_change_key_settings , +.Nm mifare_desfire_get_key_settings , +.Nm mifare_desfire_change_key , +.Nm mifare_desfire_get_key_version , +.\" +.Nm mifare_desfire_create_application , +.Nm mifare_desfire_delete_application , +.Nm mifare_desfire_get_application_ids , +.Nm mifare_desfire_free_application_ids , +.Nm mifare_desfire_select_application , +.\" +.Nm mifare_desfire_format_picc , +.\" +.Nm mifare_desfire_get_version , +.\" +.Nm mifare_desfire_get_file_ids , +.Nm mifare_desfire_get_file_settings , +.Nm mifare_desfire_change_file_settings , +.Nm mifare_desfire_create_std_data_file , +.Nm mifare_desfire_create_backup_data_file , +.Nm mifare_desfire_create_value_file , +.Nm mifare_desfire_create_linear_record_file , +.Nm mifare_desfire_create_cyclic_record_file , +.Nm mifare_desfire_delete_file , +.\" +.Nm mifare_desfire_read_data , +.Nm mifare_desfire_read_data_ex , +.Nm mifare_desfire_write_data , +.Nm mifare_desfire_write_data_ex , +.Nm mifare_desfire_get_value , +.Nm mifare_desfire_get_value_ex , +.Nm mifare_desfire_credit , +.Nm mifare_desfire_credit_ex , +.Nm mifare_desfire_debit , +.Nm mifare_desfire_debit_ex , +.Nm mifare_desfire_limited_credit , +.Nm mifare_desfire_limited_credit_ex , +.Nm mifare_desfire_write_record , +.Nm mifare_desfire_write_record_ex , +.Nm mifare_desfire_read_records , +.Nm mifare_desfire_read_records_ex , +.Nm mifare_desfire_clear_record_file , +.Nm mifare_desfire_commit_transaction , +.Nm mifare_desfire_abort_transaction , +.Nd Mifare DESFire Manipulation Functions +.\" _ _ _ +.\" | | (_) |__ _ __ __ _ _ __ _ _ +.\" | | | | '_ \| '__/ _` | '__| | | | +.\" | |___| | |_) | | | (_| | | | |_| | +.\" |_____|_|_.__/|_| \__,_|_| \__, | +.\" |___/ +.Sh LIBRARY +Mifare card manipulation library (libfreefare, \-lfreefare) +.\" ____ _ +.\" / ___| _ _ _ __ ___ _ __ ___(_)___ +.\" \___ \| | | | '_ \ / _ \| '_ \/ __| / __| +.\" ___) | |_| | | | | (_) | |_) \__ \ \__ \ +.\" |____/ \__, |_| |_|\___/| .__/|___/_|___/ +.\" |___/ |_| +.Sh SYNOPSIS +.In freefare.h +.Ft int +.Fn mifare_desfire_connect "MifareTag tag" +.Ft int +.Fn mifare_desfire_disconnect "MifareTag tag" +.\".Ft uint8_t +.\".Fn mifare_desfire_get_last_error "MifareTag tag" +.Ft int +.Fn mifare_desfire_authenticate "MifareTag tag" "uint8_t key_no" "MifareDESFireKey key" +.Ft int +.Fn mifare_desfire_change_key_settings "MifareTag tag" "uint8_t settings" +.Ft int +.Fn mifare_desfire_get_key_settings "MifareTag tag" "uint8_t *settings" "uint8_t *max_keys" +.Ft int +.Fn mifare_desfire_change_key "MifareTag tag" "uint8_t key_no" "MifareDESFireKey new_key" "MifareDESFireKey old_key" +.Ft int +.Fn mifare_desfire_get_key_version "MifareTag tag" "uint8_t key_no" "uint8_t *version" +.Ft int +.Fn mifare_desfire_create_application "MifareTag tag" "MifareDESFireAID aid" "uint8_t settings" "uint8_t key_no" +.Ft int +.Fn mifare_desfire_delete_application "MifareTag tag" "MifareDESFireAID aid" +.Ft int +.Fn mifare_desfire_get_application_ids "MifareTag tag" "MifareDESFireAID *aids[]" "size_t *count" +.Ft void +.Fn mifare_desfire_free_application_ids "MifareDESFireAID aids[]" +.Ft int +.Fn mifare_desfire_select_application "MifareTag tag" "MifareDESFireAID aid" +.Ft int +.Fn mifare_desfire_format_picc "MifareTag tag" +.Ft int +.Fn mifare_desfire_get_version "MifareTag tag" "struct mifare_desfire_version_info *version_info" +.Ft int +.Fn mifare_desfire_get_file_ids "MifareTag tag" "uint8_t *files[]" "size_t *count" +.Ft int +.Fn mifare_desfire_get_file_settings "MifareTag tag" "uint8_t file_no" "struct mifare_desfire_file_settings *settings" +.Ft int +.Fn mifare_desfire_change_file_settings "MifareTag tag" "uint8_t file_no" "uint8_t communication_settings" "uint16_t access_rights" +.Ft int +.Fn mifare_desfire_create_std_data_file "MifareTag tag" "uint8_t file_no" "uint8_t communication_settings" "uint16_t access_rights" "uint32_t file_size" +.Ft int +.Fn mifare_desfire_create_backup_data_file "MifareTag tag" "uint8_t file_no" "uint8_t communication_settings" "uint16_t access_rights" "uint32_t file_size" +.Ft int +.Fn mifare_desfire_create_value_file "MifareTag tag" "uint8_t file_no" "uint8_t communication_settings" "uint16_t access_rights" "int32_t lower_limit" "int32_t upper_limit" "int32_t value" "uint8_t limited_credit_enable" +.Ft int +.Fn mifare_desfire_create_linear_record_file "MifareTag tag" "uint8_t file_no" "uint8_t communication_settings" "uint16_t access_rights" "uint32_t record_size" "uint32_t max_number_of_records" +.Ft int +.Fn mifare_desfire_create_cyclic_record_file "MifareTag tag" "uint8_t file_no" "uint8_t communication_settings" "uint16_t access_rights" "uint32_t record_size" "uint32_t max_number_of_records" +.Ft int +.Fn mifare_desfire_delete_file "MifareTag tag" "uint8_t file_no" +.Ft ssize_t +.Fn mifare_desfire_read_data "MifareTag tag" "uint8_t file_no" "off_t offset" "size_t length" "void *data" +.Ft ssize_t +.Fn mifare_desfire_read_data_ex "MifareTag tag" "uint8_t file_no" "off_t offset" "size_t length" "void *data" "int cs" +.Ft ssize_t +.Fn mifare_desfire_write_data "MifareTag tag" "uint8_t file_no" "off_t offset" "size_t length" "void *data" +.Ft ssize_t +.Fn mifare_desfire_write_data_ex "MifareTag tag" "uint8_t file_no" "off_t offset" "size_t length" "void *data" "int cs" +.Ft int +.Fn mifare_desfire_get_value "MifareTag tag" "uint8_t file_no" "int32_t *value" +.Ft int +.Fn mifare_desfire_get_value_ex "MifareTag tag" "uint8_t file_no" "int32_t *value" "int cs" +.Ft int +.Fn mifare_desfire_credit "MifareTag tag" "uint8_t file_no" "int32_t amount" +.Ft int +.Fn mifare_desfire_credit_ex "MifareTag tag" "uint8_t file_no" "int32_t amount" "int cs" +.Ft int +.Fn mifare_desfire_debit "MifareTag tag" "uint8_t file_no" "int32_t amount" +.Ft int +.Fn mifare_desfire_debit_ex "MifareTag tag" "uint8_t file_no" "int32_t amount" "int cs" +.Ft int +.Fn mifare_desfire_limited_credit "MifareTag tag" "uint8_t file_no" "int32_t amount" +.Ft int +.Fn mifare_desfire_limited_credit_ex "MifareTag tag" "uint8_t file_no" "int32_t amount" "int cs" +.Ft ssize_t +.Fn mifare_desfire_write_record "MifareTag tag" "uint8_t file_no" "off_t offset" "size_t length" "void *data" +.Ft ssize_t +.Fn mifare_desfire_write_record_ex "MifareTag tag" "uint8_t file_no" "off_t offset" "size_t length" "void *data" "int cs" +.Ft ssize_t +.Fn mifare_desfire_read_records "MifareTag tag" "uint8_t file_no" "off_t offset" "size_t length" "void *data" +.Ft ssize_t +.Fn mifare_desfire_read_records_ex "MifareTag tag" "uint7_t file_no" "off_t offset" "size_t length" "void *data" "int cs" +.Ft int +.Fn mifare_desfire_clear_record_file "MifareTag tag" "uint8_t file_no" +.Ft int +.Fn mifare_desfire_commit_transaction "MifareTag tag" +.Ft int +.Fn mifare_desfire_abort_transaction "MifareTag tag" +.\" ____ _ _ _ +.\" | _ \ ___ ___ ___ _ __(_)_ __ | |_(_) ___ _ __ +.\" | | | |/ _ \/ __|/ __| '__| | '_ \| __| |/ _ \| '_ \ +.\" | |_| | __/\__ \ (__| | | | |_) | |_| | (_) | | | | +.\" |____/ \___||___/\___|_| |_| .__/ \__|_|\___/|_| |_| +.\" |_| +.Sh DESCRIPTION +The +.Fn mifare_desfire_* +functions allows management of Mifare DESFire tags. +.Pp +.Ss Card-level operations +The +.Fn mifare_desfire_connect +and +.Fn mifare_desfire_disconnect +functions activates and deactivates the provided +.Vt tag . +All +.Fn mifare_desfire_* +functions that operates on a +.Vt tag +require it to be on activated. +.Pp +After activation, the selected application is the master application. It is +possible to select another application using the +.Fn mifare_desfire_select_application +function (see bellow). +.Pp +The +.Fn mifare_desfire_get_version +function retrieve various information about the provided +.Vt tag , +including UID, batch number, production date, and hardware and software +information. Refer to the freefare.h header file for details about the +.Vt settings +field. +.Pp +The +.Fn mifare_desfire_format_picc +function resets +.Vt tag +to factory defaults. For this function to work, a previous authentication with +the card master key is required. +.Pp +.Ss Application-level operations +The +.Fn mifare_desfire_select_application +function makes the application identified by +.Vt aid +the active one. Further file operations will be performed in the context of +this application. After a call to +.Vt mifare_desfire_connect , +the default application is the card master application. It can be selected +again calling the +.Fn mifare_desfire_select_application +function either with an +.Vt aid +with all its fields set to 0, or by providing the NULL +.Vt aid . +.Pp +The +.Fn mifare_desfire_authenticate +function performs an authentication using the key number +.Vt key_no +on the card and the +.Vt key +(3)DES key on +.Vt tag . +.Pp +The +.Fn mifare_desfire_get_key_settings +function, returns the +.Vt settings +and the number of keys +.Vt max_keys +of the selected application. +.Pp +The +.Fn mifare_desfire_change_key_settings +function changes the selected application settings to +.Vt settings . +The application number of keys cannot be changed after the application has been +created. +.Pp +The +.Fn mifare_desfire_change_key +changes the key +.Vt key_no +from +.Vt old_key +to +.Vt new_key +on +.Vt tag . +Depending on the application settings, a previous authentication with the same +key or another key may be required. +.Pp +The +.Fn mifare_desfire_get_key_version +function retrieves the +.Vt version +of the key with number +.Vt key_no +of the selected application. +.Pp +The +.Fn mifare_desfire_create_application +function, creates an application with AID +.Vt aid , +the +.Vt settings +key settings and +.Vt key_no +authentication keys. Authentication keys are set to 0 after creation. +.Pp +The +.Fn mifare_desfire_delete_application +deletes the application identified by AID +.Vt aid . +.Pp +The +.Fn mifare_desfire_get_application_ids +function returns a list of all applications of the card. The +.Vt aids +array has to be freed after usage calling +.Fn mifare_desfire_free_application_ids . +.Pp +.Ss File-level operations +The +.Fn mifare_desfire_get_file_ids +function returns the list of +.Vt count +files in the selected application as +.Vt files . +The memory allocated for +.Vt files +has to be reclaimed using +.Xr free 3 . +.Pp +The +.Fn mifare_desfire_get_file_settings +function retrieves the +.Vt settings +of the file +.Vt file_no +of the selected application of +.Vt tag . +.Pp +The +.Fn mifare_desfire_change_file_settings +function change the +.Vt communication_settings +and +.Vt access_rights +of the file +.Vt file_no +of the selected application of +.Vt tag . +.Pp +The +.Fn mifare_desfire_create_* +family of functions create a new file +.Vt file_no +with the provided +.Vt communication_settings +and +.Vt access_rights +on +.Vt tag. +.Bl -tag -width indent +.It Fn mifare_desfire_create_std_data_file +creates a standard data file of size +.Vt file_size . +.It Fn mifare_desfire_create_backup_data_file +creates a backup data file of size +.Vt file_size . +.It Fn mifare_desfire_create_value_file +creates a value file of value +.Vt value +constrained in the range +.Vt lower_limit +.Vt upper_limit , +and with the +.Vt limited_credit_enable +settings. +.It Fn mifare_desfire_create_linear_record_file +creates a linear record file that can hold +.Vt max_number_of_records +records of size +.Vt record_size . +.It Fn mifare_desfire_create_cyclic_record_file +creates a cyclic record file that can hold +.Vt max_number_of_records +records of size +.Vt record_size . +.El +.Pp +The +.Fn mifare_desfire_delete_file +removes the file +.Vt file_no +from the selected application of +.Vt tag . +.Ss Data-level operations +The +.Fn mifare_desfire_read_data +function reads +.Vt length +bytes of data from offset +.Vt offset +of the file +.Vt file_no +and copies it to +.Vt data . +If +.Vt length +is set to 0, the file is read to end. The function returns the number of bytes +read. +.Pp +The +.Fn mifare_desfire_write_data +function writes +.Vt length +bytes of data from offset +.Vt offset +of the file +.Vt file_no +and copies it to +.Vt data . +The function returns the number of bytes written. +.Pp +The +.Fn mifare_desfire_get_value +reads the +.Vt value +of the file +.Vt file_no +of the selected application. +.Pp +The +.Fn mifare_desfire_credit +function adds +.Vt amount +to the value of the file +.Vt file_no +of the selected application. +.Pp +The +.Fn mifare_desfire_debit +function substracts +.Vt amount +to the value of the file +.Vt file_no +of the selected application. +.Pp +to the value of the file +.Vt file_no +of the selected application. +.Pp +The +.Fn mifare_desfire_limited_credit +function adds +.Vt amount +to the value of the file +.Vt file_no +of the selected application. +.Pp +The +.Fn mifare_desfire_write_record +function writes +.Vt length +records starting at record +.Vt offset +of +.Vt data +in the file +.Vt file_no +and returns the number of bytes written. +.Pp +The +.Fn mifare_desfire_read_records +function reads +.Vt length +records starting at record +.Vt offset +from the file +.Vt file_no +and copy them to +.Vt data , +returning the number of bytes read. +.Pp +The +.Fn mifare_desfire_clear_record_file +function erase all records from the file +.Vt file_no +of the selected application. +.Pp +The +.Fn mifare_desfire_commit_transaction +validates the set of pending changes on the +.Vt tag , +while the +.Fn mifare_desfire_abort_transaction +rollbacks the changes. +.Pp +All data-manipulation functions that read data from and write data to files +come with an +.Fn *_ex +variant (e.g. +.Fn mifare_desfire_read_data_ex ) +which accepts an extra parameter +.Vt cs +that defines the communication settings to use. If not provided, the library +will try to read-out this value from the file's configuration. Because reading +this information may be denied, the +.Fn *_ex +variant of functions still allows using the library for advanced usage. +.\" ____ _ _ +.\" | _ \ ___| |_ _ _ _ __ _ __ __ ____ _| |_ _ ___ ___ +.\" | |_) / _ \ __| | | | '__| '_ \ \ \ / / _` | | | | |/ _ \/ __| +.\" | _ < __/ |_| |_| | | | | | | \ V / (_| | | |_| | __/\__ \ +.\" |_| \_\___|\__|\__,_|_| |_| |_| \_/ \__,_|_|\__,_|\___||___/ +.\" +.Sh RETURN VALUES +Unless stated otherwise, all other functions return a value greater than or +equal to +.Va 0 +on success or +.Va -1 +on failure. +.\" ____ _ +.\" / ___| ___ ___ __ _| |___ ___ +.\" \___ \ / _ \/ _ \ / _` | / __|/ _ \ +.\" ___) | __/ __/ | (_| | \__ \ (_) | +.\" |____/ \___|\___| \__,_|_|___/\___/ +.\" +.Sh SEE ALSO +.Xr freefare 3 +.\" _ _ _ +.\" / \ _ _| |_| |__ ___ _ __ ___ +.\" / _ \| | | | __| '_ \ / _ \| '__/ __| +.\" / ___ \ |_| | |_| | | | (_) | | \__ \ +.\" /_/ \_\__,_|\__|_| |_|\___/|_| |___/ +.\" +.Sh AUTHORS +.An Romain Tartiere Aq romain@il4p.org diff --git a/libfreefare/mifare_desfire.c b/libfreefare/mifare_desfire.c new file mode 100644 index 0000000..a37b359 --- /dev/null +++ b/libfreefare/mifare_desfire.c @@ -0,0 +1,1247 @@ +/*- + * Copyright (C) 2010, Romain Tartiere, Romuald Conty. + * + * 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$ + */ + +/* + * http://ridrix.wordpress.com/2009/09/19/mifare-desfire-communication-example/ + */ + +#include "config.h" + +#if defined(HAVE_SYS_TYPES_H) +# include +#endif + +#if defined(HAVE_SYS_ENDIAN_H) +# include +#endif + +#if defined(HAVE_ENDIAN_H) +# include +#endif + +#if defined(HAVE_BYTESWAP_H) +# include +#endif + +#include +#include +#include +#include + +#include +#include "freefare_internal.h" + +// TODO Remove this +#include + +#pragma pack (push) +#pragma pack (1) +struct mifare_desfire_raw_file_settings { + uint8_t file_type; + uint8_t communication_settings; + uint16_t access_rights; + union { + struct { + uint8_t file_size[3]; + } standard_file; + struct { + int32_t lower_limit; + int32_t upper_limit; + int32_t limited_credit_value; + uint8_t limited_credit_enabled; + } value_file; + struct { + uint8_t record_size[3]; + uint8_t max_number_of_records[3]; + uint8_t current_number_of_records[3]; + } linear_record_file; + } settings; +}; +#pragma pack (pop) + +static int create_file1 (MifareTag tag, uint8_t command, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t file_size); +static int create_file2 (MifareTag tag, uint8_t command, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t record_size, uint32_t max_number_of_records); +static ssize_t write_data (MifareTag tag, uint8_t command, uint8_t file_no, off_t offset, size_t length, void *data, int cs); +static ssize_t read_data (MifareTag tag, uint8_t command, uint8_t file_no, off_t offset, size_t length, void *data, int cs); + +#define MAX_FRAME_SIZE 60 + +#define NOT_YET_AUTHENTICATED 255 + +#define ASSERT_AUTHENTICATED(tag) \ + do { \ + if (MIFARE_DESFIRE (tag)->authenticated_key_no == NOT_YET_AUTHENTICATED) \ + return errno = EINVAL, -1;\ + } while (0); + +#define ASSERT_NOT_NULL(argument) \ + do { \ + if (!argument) \ + return errno = EINVAL, -1; \ + } while (0); + + +/* + * Buffer management macros. + * + * The following macros ease setting-up and using buffers: + * BUFFER_INIT (data, 5); // data -> [ xx, xx, xx, xx, xx ] + * BUFFER_SIZE (data); // size -> 0 + * BUFFER_APPEND (data, 0x12); // data -> [ 12, xx, xx, xx, xx ] + * BUFFER_SIZE (data); // size -> 1 + * uint16_t x = 0x3456; // We suppose we are little endian + * BUFFER_APPEND_BYTES (data, x, 2); + * // data -> [ 12, 56, 34, xx, xx ] + * BUFFER_SIZE (data); // size -> 3 + * BUFFER_APPEND_LE (data, x, 2, sizeof (x)); + * // data -> [ 12, 56, 34, 34, 56 ] + * BUFFER_SIZE (data); // size -> 5 + */ + +/* + * Initialise a buffer named buffer_name of size bytes. + */ +#define BUFFER_INIT(buffer_name, size) \ + uint8_t buffer_name[size]; \ + size_t __##buffer_name##_n = 0 + +#define BUFFER_SIZE(buffer_name) (__##buffer_name##_n) + +#define BUFFER_CLEAR(buffer_name) (__##buffer_name##_n = 0) +/* + * Append one byte of data to the buffer buffer_name. + */ +#define BUFFER_APPEND(buffer_name, data) \ + do { \ + buffer_name[__##buffer_name##_n++] = data; \ + } while (0) + +/* + * Append size bytes of data to the buffer buffer_name. + */ +#define BUFFER_APPEND_BYTES(buffer_name, data, size) \ + do { \ + size_t __n = 0; \ + while (__n < size) { \ + buffer_name[__##buffer_name##_n++] = ((uint8_t *)data)[__n++]; \ + } \ + } while (0) + +/* + * Append data_size bytes of data at the end of the buffer. Since data is + * copied as a little endian value, the storage size of the value has to be + * passed as the field_size parameter. + * + * Example: to copy 24 bits of data from a 32 bits value: + * BUFFER_APPEND_LE (buffer, data, 3, 4); + */ + +// FIXME: remove debugging stuff +#if _BYTE_ORDER != _LITTLE_ENDIAN +#define BUFFER_APPEND_LE(buffer, data, data_size, field_size) \ + do { \ + printf ("append (%p, %lu, %p, %d (%d))\n", buffer, __##buffer##_n, (void *)(&data), (int) data_size, (int) field_size); \ + size_t __data_size = data_size; \ + size_t __field_size = field_size; \ + while (__field_size--, __data_size--) { \ + printf (" buffer[%lu] <- %02x\n", __##buffer##_n, ((uint8_t *)&data)[__field_size]); \ + buffer[__##buffer##_n++] = ((uint8_t *)&data)[__field_size]; \ + } \ + } while (0) +#else +#define BUFFER_APPEND_LE(buffer, data, data_size, field_size) \ + do { \ + memcpy (buffer + __##buffer##_n, &data, data_size); \ + __##buffer##_n += data_size; \ + } while (0) +#endif + + +/* + * Convenience macros. + */ + +/* + * Transmit the message msg to the NFC tag and receive the response res. The + * response buffer's size is set according to the quantity od data received. + */ +// FIXME: remove debugging stuff +#define DESFIRE_TRANSCEIVE(tag, msg, res) \ + do { \ + errno = 0; \ + MIFARE_DESFIRE (tag)->last_picc_error = OPERATION_OK; \ + hexdump (msg, __##msg##_n, "---> ", 0); \ + if (!(nfc_initiator_transceive_dep_bytes (tag->device, msg, __##msg##_n, res, &__##res##_n))) \ + return errno = EIO, -1; \ + hexdump (res, __##res##_n, "<--- ", 0); \ + if ((1 == __##res##_n) && (OPERATION_OK != res[0]) && (ADDITIONAL_FRAME != res[0])) \ + return MIFARE_DESFIRE (tag)->last_picc_error = res[0], -1; \ + } while (0) + + +/* + * Miscellaneous low-level memory manipulation functions. + */ + +static void *memdup (void *p, size_t n); +static int32_t le24toh (uint8_t data[3]); + +int +madame_soleil_get_read_communication_settings (MifareTag tag, uint8_t file_no) +{ + // FIXME: It might be forbiden to get file settings. + struct mifare_desfire_file_settings settings; + if (mifare_desfire_get_file_settings (tag, file_no, &settings)) + return -1; + + if ((MIFARE_DESFIRE (tag)->authenticated_key_no == MDAR_READ (settings.access_rights)) || + (MIFARE_DESFIRE (tag)->authenticated_key_no == MDAR_READ_WRITE (settings.access_rights))) + return settings.communication_settings; + else + return 0; +} + +int +madame_soleil_get_write_communication_settings (MifareTag tag, uint8_t file_no) +{ + // FIXME: It might be forbiden to get file settings. + struct mifare_desfire_file_settings settings; + if (mifare_desfire_get_file_settings (tag, file_no, &settings)) + return -1; + + if ((MIFARE_DESFIRE (tag)->authenticated_key_no == MDAR_WRITE (settings.access_rights)) || + (MIFARE_DESFIRE (tag)->authenticated_key_no == MDAR_READ_WRITE (settings.access_rights))) + return settings.communication_settings; + else + return 0; +} + +static int32_t +le24toh (uint8_t data[3]) +{ + return (data[2] << 16) | (data[1] << 8) | data[0]; +} + +static void * +memdup (void *p, size_t n) +{ + void *res; + if ((res = malloc (n))) { + memcpy (res, p, n); + } + return res; +} + + +/* + * Memory management functions. + */ + +/* + * Allocates and initialize a MIFARE DESFire tag. + */ +MifareTag +mifare_desfire_tag_new (void) +{ + MifareTag tag; + if ((tag= malloc (sizeof (struct mifare_desfire_tag)))) { + MIFARE_DESFIRE (tag)->last_picc_error = OPERATION_OK; + MIFARE_DESFIRE (tag)->last_pcd_error = NULL; + MIFARE_DESFIRE (tag)->session_key = NULL; + MIFARE_DESFIRE (tag)->crypto_buffer = NULL; + MIFARE_DESFIRE (tag)->crypto_buffer_size = 0; + } + return tag; +} + +/* + * Free the provided tag. + */ +void +mifare_desfire_tag_free (MifareTag tag) +{ + free (MIFARE_DESFIRE (tag)->session_key); + free (MIFARE_DESFIRE (tag)->last_pcd_error); + free (MIFARE_DESFIRE (tag)->crypto_buffer); + free (tag); +} + + +/* + * MIFARE card communication preparation functions + * + * The following functions send NFC commands to the initiator to prepare + * communication with a MIFARE card, and perform required cleannups after using + * the target. + */ + +/* + * Establish connection to the provided tag. + */ +int +mifare_desfire_connect (MifareTag tag) +{ + ASSERT_INACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + + nfc_target_info_t pnti; + if (nfc_initiator_select_passive_target (tag->device, NM_ISO14443A_106, tag->info.abtUid, 7, &pnti)) { + tag->active = 1; + free (MIFARE_DESFIRE (tag)->session_key); + MIFARE_DESFIRE (tag)->session_key = NULL; + MIFARE_DESFIRE (tag)->last_picc_error = OPERATION_OK; + MIFARE_DESFIRE (tag)->last_pcd_error = NULL; + MIFARE_DESFIRE (tag)->authenticated_key_no = NOT_YET_AUTHENTICATED; + } else { + errno = EIO; + return -1; + } + return 0; +} + +/* + * Terminate connection with the provided tag. + */ +int +mifare_desfire_disconnect (MifareTag tag) +{ + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + + free (MIFARE_DESFIRE (tag)->session_key); + MIFARE_DESFIRE(tag)->session_key = NULL; + + if (nfc_initiator_deselect_target (tag->device)) { + tag->active = 0; + } + return 0; +} + + + +uint8_t +mifare_desfire_get_last_error (MifareTag tag) +{ + return MIFARE_DESFIRE (tag)->last_picc_error; +} + + + +int +mifare_desfire_authenticate (MifareTag tag, uint8_t key_no, MifareDESFireKey key) +{ + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + + MIFARE_DESFIRE (tag)->last_picc_error = OPERATION_OK; + + MIFARE_DESFIRE (tag)->authenticated_key_no = NOT_YET_AUTHENTICATED; + free (MIFARE_DESFIRE (tag)->session_key); + MIFARE_DESFIRE (tag)->session_key = NULL; + + BUFFER_INIT (cmd1, 2); + BUFFER_INIT (res, 9); + + BUFFER_APPEND (cmd1, 0x0A); + BUFFER_APPEND (cmd1, key_no); + + DESFIRE_TRANSCEIVE (tag, cmd1, res); + + + uint8_t PICC_E_RndB[8]; + memcpy (PICC_E_RndB, res+1, 8); + + uint8_t PICC_RndB[8]; + memcpy (PICC_RndB, PICC_E_RndB, 8); + mifare_cbc_des (key, PICC_RndB, 8, MD_RECEIVE, 0); + + uint8_t PCD_RndA[8]; + DES_random_key ((DES_cblock*)&PCD_RndA); + + uint8_t PCD_r_RndB[8]; + memcpy (PCD_r_RndB, PICC_RndB, 8); + rol8 (PCD_r_RndB); + + uint8_t token[16]; + memcpy (token, PCD_RndA, 8); + memcpy (token+8, PCD_r_RndB, 8); + + mifare_cbc_des (key, token, 16, MD_SEND, 0); + + BUFFER_INIT (cmd2, 17); + + BUFFER_APPEND (cmd2, 0xAF); + BUFFER_APPEND_BYTES (cmd2, token, 16); + + DESFIRE_TRANSCEIVE (tag, cmd2, res); + + uint8_t PICC_E_RndA_s[8]; + memcpy (PICC_E_RndA_s, res+1, 8); + + uint8_t PICC_RndA_s[8]; + memcpy (PICC_RndA_s, PICC_E_RndA_s, 8); + mifare_cbc_des (key, PICC_RndA_s, 8, MD_RECEIVE, 0); + + uint8_t PCD_RndA_s[8]; + memcpy (PCD_RndA_s, PCD_RndA, 8); + rol8 (PCD_RndA_s); + + + if (0 != memcmp (PCD_RndA_s, PICC_RndA_s, 8)) { + return -1; + } + + MIFARE_DESFIRE (tag)->authenticated_key_no = key_no; + MIFARE_DESFIRE (tag)->session_key = mifare_desfire_session_key_new (PCD_RndA, PICC_RndB, key); + + return 0; +} + +int +mifare_desfire_change_key_settings (MifareTag tag, uint8_t settings) +{ + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + ASSERT_AUTHENTICATED (tag); + + BUFFER_INIT (cmd, 9); + BUFFER_INIT (res, 1); + + BUFFER_APPEND (cmd, 0x54); + + uint8_t data[8]; + + data[0] = settings; + iso14443a_crc (data, 1, data + 1); + bzero (data+3, 5); + + mifare_cbc_des (MIFARE_DESFIRE (tag)->session_key, data, 8, MD_SEND, 0); + + BUFFER_APPEND_BYTES (cmd, data, 8); + + DESFIRE_TRANSCEIVE (tag, cmd, res); + + return 0; +} + +int +mifare_desfire_get_key_settings (MifareTag tag, uint8_t *settings, uint8_t *max_keys) +{ + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + + BUFFER_INIT (cmd, 1); + BUFFER_INIT (res, 3); + + BUFFER_APPEND (cmd, 0x45); + + DESFIRE_TRANSCEIVE (tag, cmd, res); + + if (settings) + *settings = res[1]; + if (max_keys) + *max_keys = res[2]; + + return 0; +} + +int +mifare_desfire_change_key (MifareTag tag, uint8_t key_no, MifareDESFireKey new_key, MifareDESFireKey old_key) +{ + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + ASSERT_AUTHENTICATED (tag); + + BUFFER_INIT (cmd, 1+1+24); + BUFFER_INIT (res, 1); + + BUFFER_APPEND (cmd, 0xC4); + BUFFER_APPEND (cmd, key_no); + + uint8_t data[24]; + + if ((MIFARE_DESFIRE (tag)->authenticated_key_no != key_no) /* FIXME && (ChangeKey key != 0x0E)*/) { + if (old_key) { + memcpy (data, old_key->data, 16); + } else { + bzero (data, 16); + } + for (int n=0; n<16; n++) { + data[n] ^= new_key->data[n]; + } + // Append XORed data CRC + iso14443a_crc (data, 16, data+16); + // Append new key CRC + iso14443a_crc (new_key->data, 16, data+18); + // Padding + for (int n=20; n<24; n++) { + data[n] = 0x00; + } + } else { + memcpy (data, new_key->data, 16); + // Append new key CRC + iso14443a_crc (data, 16, data+16); + + // Padding + for (int n=18; n<24; n++) { + data[n] = 0x00; + } + } + + mifare_cbc_des (MIFARE_DESFIRE (tag)->session_key, data, 24, MD_SEND, 0); + + BUFFER_APPEND_BYTES (cmd, data, 24); + + DESFIRE_TRANSCEIVE (tag, cmd, res); + + return 0; +} + +/* + * Retrieve version information for a given key. + */ +int +mifare_desfire_get_key_version (MifareTag tag, uint8_t key_no, uint8_t *version) +{ + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + + ASSERT_NOT_NULL (version); + + BUFFER_INIT (cmd, 2); + BUFFER_APPEND (cmd, 0x64); + BUFFER_APPEND (cmd, key_no); + + BUFFER_INIT (res, 2); + + DESFIRE_TRANSCEIVE (tag, cmd, res); + + *version = res[1]; + + return 0; +} + + + +int +mifare_desfire_create_application (MifareTag tag, MifareDESFireAID aid, uint8_t settings, uint8_t key_no) +{ + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + + BUFFER_INIT (cmd, 6); + BUFFER_INIT (res, 1); + + BUFFER_APPEND (cmd, 0xCA); + BUFFER_APPEND_LE (cmd, aid->data, sizeof (aid->data), sizeof (aid->data)); + BUFFER_APPEND (cmd, settings); + BUFFER_APPEND (cmd, key_no); + + DESFIRE_TRANSCEIVE (tag, cmd, res); + + return 0; +} + +int +mifare_desfire_delete_application (MifareTag tag, MifareDESFireAID aid) +{ + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + + BUFFER_INIT (cmd, 4); + BUFFER_INIT (res, 1); + + BUFFER_APPEND (cmd, 0xDA); + BUFFER_APPEND_LE (cmd, aid->data, sizeof (aid->data), sizeof (aid->data)); + + DESFIRE_TRANSCEIVE (tag, cmd, res); + + return 0; +} + +int +mifare_desfire_get_application_ids (MifareTag tag, MifareDESFireAID *aids[], size_t *count) +{ + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + + BUFFER_INIT (cmd, 1); + BUFFER_INIT (res, MAX_FRAME_SIZE); + + BUFFER_APPEND (cmd, 0x6A); + + DESFIRE_TRANSCEIVE (tag, cmd, res); + *count = (BUFFER_SIZE (res)-1)/3; + *aids = malloc ((*count + 1) * sizeof (MifareDESFireAID)); + for (size_t i = 0; (3*i + 1) < BUFFER_SIZE (res); i++) { + (*aids)[i] = memdup (res + 3*i + 1, 3); + } + + if (res[0] == 0xAF) { + cmd[0] = 0xAF; + DESFIRE_TRANSCEIVE (tag, cmd, res); + *count += (BUFFER_SIZE (res)-1) / 3; + + MifareDESFireAID *p; + if ((p = realloc (*aids, (*count + 1) * sizeof (MifareDESFireAID)))) { + *aids = p; + + for (size_t i = 0; (3*i + 1) < BUFFER_SIZE (res); i++) { + (*aids)[19+i] = memdup (res + 3*i + 1, 3); + } + } + } + + (*aids)[*count] = NULL; + + return 0; +} + +void +mifare_desfire_free_application_ids (MifareDESFireAID aids[]) +{ + for (int i = 0; aids[i]; i++) + free (aids[i]); + free (aids); +} + +/* + * Select the application specified by aid for further operation. If aid is + * NULL, the master application is selected (equivalent to aid = 0x00000). + */ +int +mifare_desfire_select_application (MifareTag tag, MifareDESFireAID aid) +{ + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + + struct mifare_desfire_aid null_aid = { .data = { 0x00, 0x00, 0x00 } }; + + if (!aid) { + aid = &null_aid; + } + + BUFFER_INIT (cmd, 4); + BUFFER_INIT (res, 1); + + BUFFER_APPEND (cmd, 0x5A); + BUFFER_APPEND_LE (cmd, aid->data, sizeof (aid->data), sizeof (aid->data)); + + DESFIRE_TRANSCEIVE (tag, cmd, res); + + return 0; +} + +int +mifare_desfire_format_picc (MifareTag tag) +{ + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + ASSERT_AUTHENTICATED (tag); + + BUFFER_INIT (cmd, 1); + BUFFER_INIT (res, 1); + + BUFFER_APPEND (cmd, 0xFC); + + DESFIRE_TRANSCEIVE (tag, cmd, res); + + return 0; +} + +/* + * Retrieve version information form the PICC. + */ +int +mifare_desfire_get_version (MifareTag tag, struct mifare_desfire_version_info *version_info) +{ + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + + ASSERT_NOT_NULL (version_info); + + BUFFER_INIT (cmd, 1); + BUFFER_INIT (res, 15); /* 8, 8, then 15 byte results */ + + BUFFER_APPEND (cmd, 0x60); + + DESFIRE_TRANSCEIVE (tag, cmd, res); + memcpy (&(version_info->hardware), res+1, 7); + + cmd[0] = 0xAF; + DESFIRE_TRANSCEIVE (tag, cmd, res); + memcpy (&(version_info->software), res+1, 7); + + DESFIRE_TRANSCEIVE (tag, cmd, res); + memcpy (&(version_info->uid), res+1, 14); + + return 0; +} + + + +/* Application level commands */ + +int +mifare_desfire_get_file_ids (MifareTag tag, uint8_t *files[], size_t *count) +{ + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + + BUFFER_INIT (cmd, 1); + BUFFER_INIT (res, 16); + + BUFFER_APPEND (cmd, 0x6F); + + DESFIRE_TRANSCEIVE (tag, cmd, res); + + *count = BUFFER_SIZE (res) - 1; + + if (!(*files = malloc (*count))) { + errno = ENOMEM; + return -1; + } + memcpy (*files, res+1, *count); + + return 0; +} + +int +mifare_desfire_get_file_settings (MifareTag tag, uint8_t file_no, struct mifare_desfire_file_settings *settings) +{ + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + + BUFFER_INIT (cmd, 2); + BUFFER_INIT (res, 18); + + BUFFER_APPEND (cmd, 0xF5); + BUFFER_APPEND (cmd, file_no); + + DESFIRE_TRANSCEIVE (tag, cmd, res); + + struct mifare_desfire_raw_file_settings raw_settings; + memcpy (&raw_settings, res+1, BUFFER_SIZE (res)-1); + + settings->file_type = raw_settings.file_type; + settings->communication_settings = raw_settings.communication_settings; + settings->access_rights = le16toh (raw_settings.access_rights); + + switch (settings->file_type) { + case MDFT_STANDARD_DATA_FILE: + case MDFT_BACKUP_DATA_FILE: + settings->settings.standard_file.file_size = le24toh (raw_settings.settings.standard_file.file_size); + break; + case MDFT_VALUE_FILE_WITH_BACKUP: + settings->settings.value_file.lower_limit = le32toh (raw_settings.settings.value_file.lower_limit); + settings->settings.value_file.upper_limit = le32toh (raw_settings.settings.value_file.upper_limit); + settings->settings.value_file.limited_credit_value = le32toh (raw_settings.settings.value_file.limited_credit_value); + settings->settings.value_file.limited_credit_enabled = raw_settings.settings.value_file.limited_credit_enabled; + break; + case MDFT_LINEAR_RECORD_FILE_WITH_BACKUP: + case MDFT_CYCLIC_RECORD_FILE_WITH_BACKUP: + settings->settings.linear_record_file.record_size = le24toh (raw_settings.settings.linear_record_file.record_size); + settings->settings.linear_record_file.max_number_of_records = le24toh (raw_settings.settings.linear_record_file.max_number_of_records); + settings->settings.linear_record_file.current_number_of_records = le24toh (raw_settings.settings.linear_record_file.current_number_of_records); + break; + } + + return 0; +} + +int +mifare_desfire_change_file_settings (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights) +{ + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + + // TODO: Use a current application files settings cache. + struct mifare_desfire_file_settings settings; + int res = mifare_desfire_get_file_settings (tag, file_no, &settings); + if (res < 0) + return res; + + if (MDAR_CHANGE_AR(settings.access_rights) == MDAR_FREE) { + BUFFER_INIT (cmd, 5); + BUFFER_INIT (res, 1); + + BUFFER_APPEND (cmd, 0x5F); + BUFFER_APPEND (cmd, file_no); + BUFFER_APPEND (cmd, communication_settings); + BUFFER_APPEND_LE (cmd, access_rights, 2, sizeof (uint16_t)); + + DESFIRE_TRANSCEIVE (tag, cmd, res); + } else { + BUFFER_INIT (cmd, 10); + BUFFER_INIT (res, 1); + + uint8_t data[8]; + + BUFFER_APPEND (cmd, 0x5F); + BUFFER_APPEND (cmd, file_no); + + data[0] = communication_settings; + uint16_t le_ar = htole16 (access_rights); + memcpy (data + 1, &le_ar, sizeof (le_ar)); + iso14443a_crc (data, 3, data+3); + bzero (data + 5, 3); + mifare_cbc_des (MIFARE_DESFIRE (tag)->session_key, data, 8, MD_SEND, 0); + + BUFFER_APPEND_BYTES (cmd, data, 8); + + DESFIRE_TRANSCEIVE (tag, cmd, res); + } + + return 0; +} + +static int +create_file1 (MifareTag tag, uint8_t command, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t file_size) +{ + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + + BUFFER_INIT (cmd, 8); + BUFFER_INIT (res, 1); + + BUFFER_APPEND (cmd, command); + BUFFER_APPEND (cmd, file_no); + BUFFER_APPEND (cmd, communication_settings); + BUFFER_APPEND_LE (cmd, access_rights, 2, sizeof (uint16_t)); + BUFFER_APPEND_LE (cmd, file_size, 3, sizeof (uint32_t)); + + DESFIRE_TRANSCEIVE (tag, cmd, res); + + return 0; +} + +int +mifare_desfire_create_std_data_file (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t file_size) +{ + return create_file1 (tag, 0xCD, file_no, communication_settings, access_rights, file_size); +} + +int +mifare_desfire_create_backup_data_file (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t file_size) +{ + return create_file1 (tag, 0xCB, file_no, communication_settings, access_rights, file_size); +} + +int +mifare_desfire_create_value_file (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, int32_t lower_limit, int32_t upper_limit, int32_t value, uint8_t limited_credit_enable) +{ + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + + BUFFER_INIT (cmd, 18); + BUFFER_INIT (res, 1); + + BUFFER_APPEND (cmd, 0xCC); + BUFFER_APPEND (cmd, file_no); + BUFFER_APPEND (cmd, communication_settings); + BUFFER_APPEND_LE (cmd, access_rights, 2, sizeof (uint16_t)); + BUFFER_APPEND_LE (cmd, lower_limit, 4, sizeof (int32_t)); + BUFFER_APPEND_LE (cmd, upper_limit, 4, sizeof (int32_t)); + BUFFER_APPEND_LE (cmd, value, 4, sizeof (int32_t)); + BUFFER_APPEND (cmd, limited_credit_enable); + + DESFIRE_TRANSCEIVE (tag, cmd, res); + + return 0; +} + +static int +create_file2 (MifareTag tag, uint8_t command, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t record_size, uint32_t max_number_of_records) +{ + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + + BUFFER_INIT (cmd, 11); + BUFFER_INIT (res, 1); + + BUFFER_APPEND (cmd, command); + BUFFER_APPEND (cmd, file_no); + BUFFER_APPEND (cmd, communication_settings); + BUFFER_APPEND_LE (cmd, access_rights, 2, sizeof (uint16_t)); + BUFFER_APPEND_LE (cmd, record_size, 3, sizeof (uint32_t)); + BUFFER_APPEND_LE (cmd, max_number_of_records, 3, sizeof (uint32_t)); + + DESFIRE_TRANSCEIVE (tag, cmd, res); + + return 0; +} + +int +mifare_desfire_create_linear_record_file (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t record_size, uint32_t max_number_of_records) +{ + return create_file2 (tag, 0xC1, file_no, communication_settings, access_rights, record_size, max_number_of_records); +} + +int +mifare_desfire_create_cyclic_record_file (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t record_size, uint32_t max_number_of_records) +{ + return create_file2 (tag, 0xC0, file_no, communication_settings, access_rights, record_size, max_number_of_records); +} + +int +mifare_desfire_delete_file (MifareTag tag, uint8_t file_no) +{ + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + + BUFFER_INIT (cmd, 2); + BUFFER_INIT (res, 1); + + BUFFER_APPEND (cmd, 0xDF); + BUFFER_APPEND (cmd, file_no); + + DESFIRE_TRANSCEIVE (tag, cmd, res); + + return 0; +} + + +/* + * Data manipulation commands. + */ + +static ssize_t +read_data (MifareTag tag, uint8_t command, uint8_t file_no, off_t offset, size_t length, void *data, int cs) +{ + ssize_t bytes_read = 0; + + void *p = data; + + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + + BUFFER_INIT (cmd, 8); + BUFFER_INIT (res, MAX_FRAME_SIZE); + + BUFFER_APPEND (cmd, command); + BUFFER_APPEND (cmd, file_no); + BUFFER_APPEND_LE (cmd, offset, 3, sizeof (off_t)); + BUFFER_APPEND_LE (cmd, length, 3, sizeof (size_t)); + + if (cs) { + if (!(p = assert_crypto_buffer_size (tag, MAX_FRAME_SIZE - 1))) + return -1; + } + + do { + ssize_t frame_bytes; + + DESFIRE_TRANSCEIVE (tag, cmd, res); + + frame_bytes = BUFFER_SIZE (res) - 1; + memcpy ((uint8_t *)p + bytes_read, res + 1, frame_bytes); + bytes_read += frame_bytes; + + if (res[0] == 0xAF) { + if (p != data) { + // If we are handling memory, request more for next frame. + if (!(p = assert_crypto_buffer_size (tag, bytes_read + MAX_FRAME_SIZE - 1))) + return -1; + + } + BUFFER_CLEAR (cmd); + BUFFER_APPEND (cmd, 0xAF); + } + + } while (res[0] != 0x00); + + if (cs) { + if (mifare_cryto_postprocess_data (tag, p, &bytes_read, cs)) + memcpy (data, p, bytes_read); + } + + return bytes_read; +} + +ssize_t +mifare_desfire_read_data (MifareTag tag, uint8_t file_no, off_t offset, size_t length, void *data) +{ + return mifare_desfire_read_data_ex (tag, file_no, offset, length, data, madame_soleil_get_read_communication_settings (tag, file_no)); +} + +ssize_t +mifare_desfire_read_data_ex (MifareTag tag, uint8_t file_no, off_t offset, size_t length, void *data, int cs) +{ + return read_data (tag, 0xBD, file_no, offset, length, data, cs); +} + +static ssize_t +write_data (MifareTag tag, uint8_t command, uint8_t file_no, off_t offset, size_t length, void *data, int cs) +{ + size_t bytes_left; + size_t bytes_send = 0; + + void *p = data; + + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + + BUFFER_INIT (cmd, MAX_FRAME_SIZE); + BUFFER_INIT (res, 1); + + BUFFER_APPEND (cmd, command); + BUFFER_APPEND (cmd, file_no); + BUFFER_APPEND_LE (cmd, offset, 3, sizeof (off_t)); + BUFFER_APPEND_LE (cmd, length, 3, sizeof (size_t)); + + p = mifare_cryto_preprocess_data (tag, data, &length, cs); + + bytes_left = 52; + + while (bytes_send < length) { + size_t frame_bytes = MIN(bytes_left, length - bytes_send); + + BUFFER_APPEND_BYTES (cmd, (uint8_t *)p + bytes_send, frame_bytes); + + DESFIRE_TRANSCEIVE (tag, cmd, res); + + bytes_send += frame_bytes; + + if (0x00 == res[0]) + break; + + // PICC returned 0xAF and expects more data + BUFFER_CLEAR (cmd); + BUFFER_APPEND (cmd, 0xAF); + bytes_left = 0x59; + } + + if (0x00 != res[0]) { + // 0xAF (additionnal Frame) failure can happen here (wrong crypto method). + MIFARE_DESFIRE (tag)->last_picc_error = res[0]; + bytes_send = -1; + } + + return bytes_send; +} + +ssize_t +mifare_desfire_write_data (MifareTag tag, uint8_t file_no, off_t offset, size_t length, void *data) +{ + return mifare_desfire_write_data_ex (tag, file_no, offset, length, data, madame_soleil_get_write_communication_settings (tag, file_no)); +} + +ssize_t +mifare_desfire_write_data_ex (MifareTag tag, uint8_t file_no, off_t offset, size_t length, void *data, int cs) +{ + return write_data (tag, 0x3D, file_no, offset, length, data, cs); +} + +int +mifare_desfire_get_value (MifareTag tag, uint8_t file_no, int32_t *value) +{ + return mifare_desfire_get_value_ex (tag, file_no, value, madame_soleil_get_read_communication_settings (tag, file_no)); +} +int +mifare_desfire_get_value_ex (MifareTag tag, uint8_t file_no, int32_t *value, int cs) +{ + if (!value) + return errno = EINVAL, -1; + + void *p; + + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + + BUFFER_INIT (cmd, 2); + BUFFER_INIT (res, 9); + + BUFFER_APPEND (cmd, 0x6C); + BUFFER_APPEND (cmd, file_no); + + DESFIRE_TRANSCEIVE (tag, cmd, res); + + p = (uint8_t *)res + 1; + + if (cs) { + ssize_t rdl = BUFFER_SIZE (res) - 1; + p = mifare_cryto_postprocess_data (tag, p, &rdl, cs); + if (rdl != 4) { + printf ("invalid data length"); + return -1; + } + } + + *value = le32toh (*(int32_t *)(p)); + + return 0; +} + +int +mifare_desfire_credit (MifareTag tag, uint8_t file_no, int32_t amount) +{ + return mifare_desfire_credit_ex (tag, file_no, amount, madame_soleil_get_write_communication_settings (tag, file_no)); +} + +int +mifare_desfire_credit_ex (MifareTag tag, uint8_t file_no, int32_t amount, int cs) +{ + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + + BUFFER_INIT (cmd, 10); + BUFFER_INIT (res, 1); + + BUFFER_INIT (data, 4); + BUFFER_APPEND_LE (data, amount, 4, sizeof (int32_t)); + + BUFFER_APPEND (cmd, 0x0C); + BUFFER_APPEND (cmd, file_no); + size_t n = 4; + void *d = mifare_cryto_preprocess_data (tag, data, &n, cs); + BUFFER_APPEND_BYTES (cmd, d, n); + + DESFIRE_TRANSCEIVE (tag, cmd, res); + + return 0; +} + +int +mifare_desfire_debit (MifareTag tag, uint8_t file_no, int32_t amount) +{ + return mifare_desfire_debit_ex (tag, file_no, amount, madame_soleil_get_write_communication_settings (tag, file_no)); +} +int +mifare_desfire_debit_ex (MifareTag tag, uint8_t file_no, int32_t amount, int cs) +{ + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + + BUFFER_INIT (cmd, 10); + BUFFER_INIT (res, 1); + + BUFFER_INIT (data, 4); + BUFFER_APPEND_LE (data, amount, 4, sizeof (int32_t)); + + BUFFER_APPEND (cmd, 0xDC); + BUFFER_APPEND (cmd, file_no); + size_t n = 4; + void *d = mifare_cryto_preprocess_data (tag, data, &n, cs); + BUFFER_APPEND_BYTES (cmd, d, n); + + DESFIRE_TRANSCEIVE (tag, cmd, res); + + return 0; +} + +int +mifare_desfire_limited_credit (MifareTag tag, uint8_t file_no, int32_t amount) +{ + return mifare_desfire_limited_credit_ex (tag, file_no, amount, madame_soleil_get_write_communication_settings (tag, file_no)); +} +int +mifare_desfire_limited_credit_ex (MifareTag tag, uint8_t file_no, int32_t amount, int cs) +{ + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + + BUFFER_INIT (cmd, 10); + BUFFER_INIT (res, 1); + + BUFFER_INIT (data, 4); + BUFFER_APPEND_LE (data, amount, 4, sizeof (int32_t)); + + BUFFER_APPEND (cmd, 0x1C); + BUFFER_APPEND (cmd, file_no); + size_t n = 4; + void *d = mifare_cryto_preprocess_data (tag, data, &n, cs); + BUFFER_APPEND_BYTES (cmd, d, n); + + DESFIRE_TRANSCEIVE (tag, cmd, res); + + return 0; +} + +ssize_t +mifare_desfire_write_record (MifareTag tag, uint8_t file_no, off_t offset, size_t length, void *data) +{ + return mifare_desfire_write_record_ex (tag, file_no, offset, length, data, madame_soleil_get_write_communication_settings (tag, file_no)); +} +ssize_t +mifare_desfire_write_record_ex (MifareTag tag, uint8_t file_no, off_t offset, size_t length, void *data, int cs) +{ + return write_data (tag, 0x3B, file_no, offset, length, data, cs); +} + +ssize_t +mifare_desfire_read_records (MifareTag tag, uint8_t file_no, off_t offset, size_t length, void *data) +{ + return mifare_desfire_read_records_ex (tag, file_no, offset, length, data, madame_soleil_get_read_communication_settings (tag, file_no)); +} + +ssize_t +mifare_desfire_read_records_ex (MifareTag tag, uint8_t file_no, off_t offset, size_t length, void *data, int cs) +{ + return read_data (tag, 0xBB, file_no, offset, length, data, cs); +} + +int +mifare_desfire_clear_record_file (MifareTag tag, uint8_t file_no) +{ + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + + BUFFER_INIT (cmd, 2); + BUFFER_INIT (res, 1); + + BUFFER_APPEND (cmd, 0xEB); + BUFFER_APPEND (cmd, file_no); + + DESFIRE_TRANSCEIVE (tag, cmd, res); + + return 0; +} + +int +mifare_desfire_commit_transaction (MifareTag tag) +{ + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + + BUFFER_INIT (cmd, 1); + BUFFER_INIT (res, 1); + + BUFFER_APPEND (cmd, 0xC7); + + DESFIRE_TRANSCEIVE (tag, cmd, res); + + return 0; +} + +int +mifare_desfire_abort_transaction (MifareTag tag) +{ + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + + BUFFER_INIT (cmd, 1); + BUFFER_INIT (res, 1); + + BUFFER_APPEND (cmd, 0xA7); + + DESFIRE_TRANSCEIVE (tag, cmd, res); + + return 0; +} + diff --git a/libfreefare/mifare_desfire_aid.c b/libfreefare/mifare_desfire_aid.c new file mode 100644 index 0000000..62a7e7a --- /dev/null +++ b/libfreefare/mifare_desfire_aid.c @@ -0,0 +1,50 @@ +/*- + * Copyright (C) 2010, Romain Tartiere. + * + * 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 +#include + +#include +#include "freefare_internal.h" + +MifareDESFireAID +mifare_desfire_aid_new (uint8_t application_code, uint8_t function_cluster_code, uint8_t n) +{ + MadAid mad_aid = { application_code, function_cluster_code }; + return mifare_desfire_aid_new_with_mad_aid (mad_aid, n); +} + +MifareDESFireAID +mifare_desfire_aid_new_with_mad_aid (MadAid mad_aid, uint8_t n) +{ + + MifareDESFireAID res; + + if (n & 0xf0) + return errno = EINVAL, NULL; + + if ((res = malloc (sizeof (*res)))) { + res->data[0] = 0xf0 | (mad_aid.function_cluster_code >> 4); + res->data[1] = (uint8_t) (((mad_aid.function_cluster_code & 0x0f) << 4) | ((mad_aid.application_code & 0xf0) >> 4)); + res->data[2] = ((mad_aid.application_code & 0x0f) << 4) | n; + } + + return res; +} + diff --git a/libfreefare/mifare_desfire_authenticate.c b/libfreefare/mifare_desfire_authenticate.c new file mode 100644 index 0000000..ecd8f91 --- /dev/null +++ b/libfreefare/mifare_desfire_authenticate.c @@ -0,0 +1,297 @@ +/*- + * Copyright (C) 2010, Romain Tartiere. + * + * 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 "freefare_internal.h" + +static void xor8 (uint8_t *ivect, uint8_t *data); +static void mifare_des (MifareDESFireKey key, uint8_t *data, uint8_t *ivect, MifareDirection direction, int mac); + +static size_t padded_data_length (size_t nbytes); +static size_t maced_data_length (size_t nbytes); +static size_t enciphered_data_length (size_t nbytes); + +static void +xor8 (uint8_t *ivect, uint8_t *data) +{ + for (int i = 0; i < 8; i++) { + data[i] ^= ivect[i]; + } +} + +void +rol8(uint8_t *data) +{ + uint8_t first = data[0]; + for (int i = 0; i < 7; i++) { + data[i] = data[i+1]; + } + data[7] = first; +} + +/* + * Size required to store nbytes of data in a buffer of size n*8. + */ +static size_t +padded_data_length (size_t nbytes) +{ + if (nbytes % 8) + return ((nbytes / 8) + 1) * 8; + else + return nbytes; +} + +/* + * Buffer size required to MAC nbytes of data + */ +static size_t +maced_data_length (size_t nbytes) +{ + return nbytes + 4; +} +/* + * Buffer size required to encipher nbytes of data and a two bytes CRC. + */ +static size_t +enciphered_data_length (size_t nbytes) +{ + return padded_data_length (nbytes + 2); +} + + +/* + * Ensure that tag's crypto buffer is large enough to store nbytes of data. + */ +void * +assert_crypto_buffer_size (MifareTag tag, size_t nbytes) +{ + void *res = MIFARE_DESFIRE (tag)->crypto_buffer; + if (MIFARE_DESFIRE (tag)->crypto_buffer_size < nbytes) { + if ((res = realloc (MIFARE_DESFIRE (tag)->crypto_buffer, nbytes))) { + MIFARE_DESFIRE (tag)->crypto_buffer = res; + MIFARE_DESFIRE (tag)->crypto_buffer_size = nbytes; + } + } + return res; +} + +void * +mifare_cryto_preprocess_data (MifareTag tag, void *data, size_t *nbytes, int communication_settings) +{ + void *res; + uint8_t mac[4]; + size_t edl, mdl; + + switch (communication_settings) { + case 0: + case 2: + res = data; + break; + case 1: + edl = padded_data_length (*nbytes); + if (!(res = assert_crypto_buffer_size (tag, edl))) + abort(); + + // Fill in the crypto buffer with data ... + memcpy (res, data, *nbytes); + // ... and 0 padding + bzero ((uint8_t *)res + *nbytes, edl - *nbytes); + + mifare_cbc_des (MIFARE_DESFIRE (tag)->session_key, res, edl, MD_SEND, 1); + + memcpy (mac, (uint8_t *)res + edl - 8, 4); + + mdl = maced_data_length (*nbytes); + if (!(res = assert_crypto_buffer_size (tag, mdl))) + abort(); + + memcpy (res, data, *nbytes); + memcpy ((uint8_t *)res + *nbytes, mac, 4); + + *nbytes += 4; + + break; + case 3: + edl = enciphered_data_length (*nbytes); + if (!(res = assert_crypto_buffer_size (tag, edl))) + abort(); + + // Fill in the crypto buffer with data ... + memcpy (res, data, *nbytes); + // ... CRC ... + append_iso14443a_crc (res, *nbytes); + // ... and 0 padding + bzero ((uint8_t *)(res) + *nbytes + 2, edl - *nbytes - 2); + + *nbytes = edl; + + mifare_cbc_des (MIFARE_DESFIRE (tag)->session_key, res, *nbytes, MD_SEND, 0); + + break; + default: + res = NULL; + break; + } + + return res; +} + +void * +mifare_cryto_postprocess_data (MifareTag tag, void *data, ssize_t *nbytes, int communication_settings) +{ + void *res = data; + size_t edl; + void *edata; + + switch (communication_settings) { + case 0: + case 2: + break; + case 1: + *nbytes -= 4; + + edl = enciphered_data_length (*nbytes); + edata = malloc (edl); + + memcpy (edata, data, *nbytes); + bzero ((uint8_t *)edata + *nbytes, edl - *nbytes); + + mifare_cbc_des (MIFARE_DESFIRE (tag)->session_key, edata, edl, MD_SEND, 1); + /* ,^^^^^^^ + * No! This is not a typo! ---------------------------------' + */ + + if (0 != memcmp ((uint8_t *)data + *nbytes, (uint8_t *)edata + edl - 8, 4)) { + printf ("MACing not verified\n"); + *nbytes = -1; + res = NULL; + } + + free (edata); + + break; + case 3: + mifare_cbc_des (MIFARE_DESFIRE (tag)->session_key, res, *nbytes, MD_RECEIVE, 0); + + /* + * Look for the CRC and ensure it is following by NULL padding. We + * can't start by the end because the CRC is supposed to be 0 when + * verified, and accumulating 0's in it should not change it. + */ + bool verified = false; + int end_crc_pos = *nbytes - 7; // The CRC can be over two blocks + + do { + uint16_t crc; + iso14443a_crc (res, end_crc_pos, (uint8_t *)&crc); + if (!crc) { + verified = true; + for (int n = end_crc_pos; n < *nbytes; n++) { + uint8_t byte = ((uint8_t *)res)[n]; + if (!( (0x00 == byte) || ((0x80 == byte) && (n == end_crc_pos)) )) + verified = false; + } + } + if (verified) { + *nbytes = end_crc_pos - 2; + } else { + end_crc_pos++; + } + } while (!verified && (end_crc_pos < *nbytes)); + + if (!verified) { + printf ("(3)DES not verified\n"); + *nbytes = -1; + res = NULL; + } + + break; + default: + printf ("Unknown communication settings\n"); + *nbytes = -1; + res = NULL; + break; + + } + return res; +} + +static void +mifare_des (MifareDESFireKey key, uint8_t *data, uint8_t *ivect, MifareDirection direction, int mac) +{ + uint8_t ovect[8]; + + if (direction == MD_SEND) { + xor8 (ivect, data); + } else { + memcpy (ovect, data, 8); + } + uint8_t edata[8]; + + switch (key->type) { + case T_DES: + if (mac) { + DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT); + } else { + DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_DECRYPT); + } + memcpy (data, edata, 8); + break; + case T_3DES: + if (mac) { + DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT); + DES_ecb_encrypt ((DES_cblock *) edata, (DES_cblock *) data, &(key->ks2), DES_DECRYPT); + DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT); + } else { + DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_DECRYPT); + DES_ecb_encrypt ((DES_cblock *) edata, (DES_cblock *) data, &(key->ks2), DES_ENCRYPT); + DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_DECRYPT); + } + memcpy (data, edata, 8); + break; + } + + if (direction == MD_SEND) { + memcpy (ivect, data, 8); + } else { + xor8 (ivect, data); + memcpy (ivect, ovect, 8); + } +} + +void +mifare_cbc_des (MifareDESFireKey key, uint8_t *data, size_t data_size, MifareDirection direction, int mac) +{ + size_t offset = 0; + uint8_t ivect[8]; + bzero (ivect, sizeof (ivect)); + + while (offset < data_size) { + mifare_des (key, data + offset, ivect, direction, mac); + offset += 8; + } + +} diff --git a/libfreefare/mifare_desfire_key.3 b/libfreefare/mifare_desfire_key.3 new file mode 100644 index 0000000..35a1162 --- /dev/null +++ b/libfreefare/mifare_desfire_key.3 @@ -0,0 +1,134 @@ +.\" Copyright (C) 2010 Romain Tartiere +.\" +.\" 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$ +.\" +.Dd July 20, 2010 +.Dt MIFARE_DESFIRE_KEY 3 +.Os +.\" _ _ +.\" | \ | | __ _ _ __ ___ ___ +.\" | \| |/ _` | '_ ` _ \ / _ \ +.\" | |\ | (_| | | | | | | __/ +.\" |_| \_|\__,_|_| |_| |_|\___| +.\" +.Sh NAME +.Nm mifare_desfire_des_key_new , +.Nm mifare_desfire_3des_key_new , +.Nm mifare_desfire_des_key_new_with_version , +.Nm mifare_desfire_3des_key_new_with_version , +.Nm mifare_desfire_key_get_version , +.Nm mifare_desfire_key_set_version , +.Nm mifare_desfire_key_free +.Nd Mifare DESFire keys Manipulation Functions +.\" _ _ _ +.\" | | (_) |__ _ __ __ _ _ __ _ _ +.\" | | | | '_ \| '__/ _` | '__| | | | +.\" | |___| | |_) | | | (_| | | | |_| | +.\" |_____|_|_.__/|_| \__,_|_| \__, | +.\" |___/ +.Sh LIBRARY +Mifare card manipulation library (libfreefare, \-lfreefare) +.\" ____ _ +.\" / ___| _ _ _ __ ___ _ __ ___(_)___ +.\" \___ \| | | | '_ \ / _ \| '_ \/ __| / __| +.\" ___) | |_| | | | | (_) | |_) \__ \ \__ \ +.\" |____/ \__, |_| |_|\___/| .__/|___/_|___/ +.\" |___/ |_| +.Sh SYNOPSIS +.In freefare.h +.Ft MifareDESFireKey +.Fn mifare_desfire_des_key_new "uint8_t value[8]" +.Ft MifareDESFireKey +.Fn mifare_desfire_3des_key_new "uint8_t value[16]" +.Ft MifareDESFireKey +.Fn mifare_desfire_des_key_new_with_version "uint8_t value[8]" +.Ft MifareDESFireKey +.Fn mifare_desfire_3des_key_new_with_version "uint8_t value[16]" +.Ft uint8_t +.Fn mifare_desfire_key_get_version "MifareDESFireKey key" +.Ft void +.Fn mifare_desfire_key_set_version "MifareDESFireKey key" "uint8_t version" +.Ft void +.Fn mifare_desfire_key_free "MifareDESFireKey key" +.\" ____ _ _ _ +.\" | _ \ ___ ___ ___ _ __(_)_ __ | |_(_) ___ _ __ +.\" | | | |/ _ \/ __|/ __| '__| | '_ \| __| |/ _ \| '_ \ +.\" | |_| | __/\__ \ (__| | | | |_) | |_| | (_) | | | | +.\" |____/ \___||___/\___|_| |_| .__/ \__|_|\___/|_| |_| +.\" |_| +.Sh DESCRIPTION +The +.Fn mifare_desfire_key_* +family of functions allows management of Mifare DESFire keys. +.Pp +The +.Fn mifare_desfire_des_key_new +and +.Fn mifare_desfire_3des_key_new +alocate a new key with the provided data +.Va value . +The key version is set to +.Va 0 . +.Pp +The +.Fn mifare_desfire_des_key_new_with_version +and +.Fn mifare_desfire_3des_key_new_with_version +functions are equivalent to the +.Fn mifare_desfire_des_key_new +and +.Fn mifare_desfire_3des_key_new +functions except that the key version is set to +.Va version . +.Pp +The version of a +.Vt MifareDESFireKey +can be extracted using +.Fn mifare_desfire_key_get_version +and changed using +.Fn mifare_desfire_key_set_version. +.Pp +The +.Fn mifare_desfire_key_free +has to be called for each +.Va MifareDESFireKey +after usage to reclaim memory. +.\" ____ _ _ +.\" | _ \ ___| |_ _ _ _ __ _ __ __ ____ _| |_ _ ___ ___ +.\" | |_) / _ \ __| | | | '__| '_ \ \ \ / / _` | | | | |/ _ \/ __| +.\" | _ < __/ |_| |_| | | | | | | \ V / (_| | | |_| | __/\__ \ +.\" |_| \_\___|\__|\__,_|_| |_| |_| \_/ \__,_|_|\__,_|\___||___/ +.\" +.Sh RETURN VALUES +Key allocations functions return the allocaed key of +.Va NULL +on failure. +.\" ____ _ +.\" / ___| ___ ___ __ _| |___ ___ +.\" \___ \ / _ \/ _ \ / _` | / __|/ _ \ +.\" ___) | __/ __/ | (_| | \__ \ (_) | +.\" |____/ \___|\___| \__,_|_|___/\___/ +.\" +.Sh SEE ALSO +.Xr mifare_desfire 3 +.\" _ _ _ +.\" / \ _ _| |_| |__ ___ _ __ ___ +.\" / _ \| | | | __| '_ \ / _ \| '__/ __| +.\" / ___ \ |_| | |_| | | | (_) | | \__ \ +.\" /_/ \_\__,_|\__|_| |_|\___/|_| |___/ +.\" +.Sh AUTHORS +.An Romain Tartiere Aq romain@il4p.org diff --git a/libfreefare/mifare_desfire_key.c b/libfreefare/mifare_desfire_key.c new file mode 100644 index 0000000..94b3c8e --- /dev/null +++ b/libfreefare/mifare_desfire_key.c @@ -0,0 +1,142 @@ +/*- + * Copyright (C) 2010, Romain Tartiere. + * + * 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 +#include + +#include + +#include +#include "freefare_internal.h" + +static inline void update_key_schedules (MifareDESFireKey key); + +static inline void +update_key_schedules (MifareDESFireKey key) +{ + DES_set_key ((DES_cblock *)key->data, &(key->ks1)); + DES_set_key ((DES_cblock *)(key->data + 8), &(key->ks2)); +} + +MifareDESFireKey +mifare_desfire_des_key_new (uint8_t value[8]) +{ + uint8_t data[8]; + memcpy (data, value, 8); + for (int n=0; n < 8; n++) + data[n] &= 0xfe; + return mifare_desfire_des_key_new_with_version (data); +} + +MifareDESFireKey +mifare_desfire_des_key_new_with_version (uint8_t value[8]) +{ + MifareDESFireKey key; + + if ((key = malloc (sizeof (struct mifare_desfire_key)))) { + memcpy (key->data, value, 8); + memcpy (key->data+8, value, 8); + update_key_schedules (key); + key->type = T_DES; + } + return key; +} + +MifareDESFireKey +mifare_desfire_3des_key_new (uint8_t value[16]) +{ + uint8_t data[16]; + memcpy (data, value, 16); + for (int n=0; n < 8; n++) + data[n] &= 0xfe; + for (int n=8; n < 16; n++) + data[n] |= 0x01; + return mifare_desfire_3des_key_new_with_version (data); +} + +MifareDESFireKey +mifare_desfire_3des_key_new_with_version (uint8_t value[16]) +{ + MifareDESFireKey key; + + if ((key = malloc (sizeof (struct mifare_desfire_key)))) { + memcpy (key->data, value, 16); + update_key_schedules (key); + key->type = T_3DES; + } + return key; +} + +uint8_t +mifare_desfire_key_get_version (MifareDESFireKey key) +{ + uint8_t version = 0; + + for (int n = 0; n < 8; n++) { + version |= ((key->data[n] & 1) << (7 - n)); + } + + return version; +} + +void +mifare_desfire_key_set_version (MifareDESFireKey key, uint8_t version) +{ + for (int n = 0; n < 8; n++) { + uint8_t version_bit = ((version & (1 << (7-n))) >> (7-n)); + key->data[n] &= 0xfe; + key->data[n] |= version_bit; + if (key->type == T_DES) { + key->data[n+8] = key->data[n]; + } else { + // Write ~version to avoid turning a 3DES key into a DES key + key->data[n+8] &= 0xfe; + key->data[n+8] |= ~version_bit; + } + } +} + +MifareDESFireKey +mifare_desfire_session_key_new (uint8_t rnda[8], uint8_t rndb[8], MifareDESFireKey authentication_key) +{ + MifareDESFireKey key; + + uint8_t buffer[16]; + memcpy (buffer, rnda, 4); + memcpy (buffer+4, rndb, 4); + memcpy (buffer+8, rnda+4, 4); + memcpy (buffer+12, rndb+4, 4); + + switch (authentication_key->type) { + case T_DES: + key = mifare_desfire_des_key_new_with_version (buffer); + break; + case T_3DES: + key = mifare_desfire_3des_key_new_with_version (buffer); + break; + } + + return key; +} + +void +mifare_desfire_key_free (MifareDESFireKey key) +{ + free (key); +} diff --git a/libfreefare/mifare_ultralight.3 b/libfreefare/mifare_ultralight.3 index badd41d..25edfc0 100644 --- a/libfreefare/mifare_ultralight.3 +++ b/libfreefare/mifare_ultralight.3 @@ -45,6 +45,7 @@ Mifare card manipulation library (libfreefare, \-lfreefare) .\" |____/ \__, |_| |_|\___/| .__/|___/_|___/ .\" |___/ |_| .Sh SYNOPSIS +.In freefare.h .Ft int .Fn mifare_ultralight_connect "MifareTag tag" .Ft int diff --git a/libutil.h b/libutil.h new file mode 100644 index 0000000..515859b --- /dev/null +++ b/libutil.h @@ -0,0 +1,188 @@ +/* + * Copyright (c) 1996 Peter Wemm . + * All rights reserved. + * Copyright (c) 2002 Networks Associates Technology, Inc. + * All rights reserved. + * + * Portions of this software were developed for the FreeBSD Project by + * ThinkSec AS and NAI Labs, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 + * ("CBOSS"), as part of the DARPA CHATS research program. + * + * Redistribution and use in source and binary forms, with or without + * modification, is 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. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD: stable/8/lib/libutil/libutil.h 185548 2008-12-02 06:50:26Z peter $ + */ + +#ifndef _LIBUTIL_H_ +#define _LIBUTIL_H_ + +#define PROPERTY_MAX_NAME 64 +#define PROPERTY_MAX_VALUE 512 + +/* for properties.c */ +typedef struct _property { + struct _property *next; + char *name; + char *value; +} *properties; + +#ifdef _SYS_PARAM_H_ +/* for pidfile.c */ +struct pidfh { + int pf_fd; + char pf_path[MAXPATHLEN + 1]; + __dev_t pf_dev; + ino_t pf_ino; +}; +#endif + +/* Avoid pulling in all the include files for no need */ +struct termios; +struct winsize; +struct utmp; +struct in_addr; +struct kinfo_file; +struct kinfo_vmentry; + +__BEGIN_DECLS +void clean_environment(const char * const *_white, + const char * const *_more_white); +int extattr_namespace_to_string(int _attrnamespace, char **_string); +int extattr_string_to_namespace(const char *_string, int *_attrnamespace); +int flopen(const char *_path, int _flags, ...); +void hexdump(const void *ptr, int length, const char *hdr, int flags); +void login(struct utmp *_ut); +int login_tty(int _fd); +int logout(const char *_line); +void logwtmp(const char *_line, const char *_name, const char *_host); +void trimdomain(char *_fullhost, int _hostsize); +int openpty(int *_amaster, int *_aslave, char *_name, + struct termios *_termp, struct winsize *_winp); +int forkpty(int *_amaster, char *_name, + struct termios *_termp, struct winsize *_winp); +int humanize_number(char *_buf, size_t _len, int64_t _number, + const char *_suffix, int _scale, int _flags); +int expand_number(const char *_buf, int64_t *_num); +const char *uu_lockerr(int _uu_lockresult); +int uu_lock(const char *_ttyname); +int uu_unlock(const char *_ttyname); +int uu_lock_txfr(const char *_ttyname, pid_t _pid); +int _secure_path(const char *_path, uid_t _uid, gid_t _gid); +properties properties_read(int fd); +void properties_free(properties list); +char *property_find(properties list, const char *name); +char *auth_getval(const char *name); +int realhostname(char *host, size_t hsize, const struct in_addr *ip); +struct sockaddr; +int realhostname_sa(char *host, size_t hsize, struct sockaddr *addr, + int addrlen); + +int kld_isloaded(const char *name); +int kld_load(const char *name); +struct kinfo_file * + kinfo_getfile(pid_t _pid, int *_cntp); +struct kinfo_vmentry * + kinfo_getvmmap(pid_t _pid, int *_cntp); + +#ifdef _STDIO_H_ /* avoid adding new includes */ +char *fparseln(FILE *, size_t *, size_t *, const char[3], int); +#endif + +#ifdef _PWD_H_ +int pw_copy(int _ffd, int _tfd, const struct passwd *_pw, struct passwd *_old_pw); +struct passwd *pw_dup(const struct passwd *_pw); +int pw_edit(int _notsetuid); +int pw_equal(const struct passwd *_pw1, const struct passwd *_pw2); +void pw_fini(void); +int pw_init(const char *_dir, const char *_master); +char *pw_make(const struct passwd *_pw); +int pw_mkdb(const char *_user); +int pw_lock(void); +struct passwd *pw_scan(const char *_line, int _flags); +const char *pw_tempname(void); +int pw_tmp(int _mfd); +#endif + +#ifdef _GRP_H_ +int gr_equal(const struct group *gr1, const struct group *gr2); +char *gr_make(const struct group *gr); +struct group *gr_dup(const struct group *gr); +struct group *gr_scan(const char *line); +#endif + +#ifdef _SYS_PARAM_H_ +struct pidfh *pidfile_open(const char *path, mode_t mode, pid_t *pidptr); +int pidfile_write(struct pidfh *pfh); +int pidfile_close(struct pidfh *pfh); +int pidfile_remove(struct pidfh *pfh); +#endif + +__END_DECLS + +#define UU_LOCK_INUSE (1) +#define UU_LOCK_OK (0) +#define UU_LOCK_OPEN_ERR (-1) +#define UU_LOCK_READ_ERR (-2) +#define UU_LOCK_CREAT_ERR (-3) +#define UU_LOCK_WRITE_ERR (-4) +#define UU_LOCK_LINK_ERR (-5) +#define UU_LOCK_TRY_ERR (-6) +#define UU_LOCK_OWNER_ERR (-7) + +/* return values from realhostname() */ +#define HOSTNAME_FOUND (0) +#define HOSTNAME_INCORRECTNAME (1) +#define HOSTNAME_INVALIDADDR (2) +#define HOSTNAME_INVALIDNAME (3) + +/* fparseln(3) */ +#define FPARSELN_UNESCESC 0x01 +#define FPARSELN_UNESCCONT 0x02 +#define FPARSELN_UNESCCOMM 0x04 +#define FPARSELN_UNESCREST 0x08 +#define FPARSELN_UNESCALL 0x0f + +/* pw_scan() */ +#define PWSCAN_MASTER 0x01 +#define PWSCAN_WARN 0x02 + +/* humanize_number(3) */ +#define HN_DECIMAL 0x01 +#define HN_NOSPACE 0x02 +#define HN_B 0x04 +#define HN_DIVISOR_1000 0x08 + +#define HN_GETSCALE 0x10 +#define HN_AUTOSCALE 0x20 + +/* hexdump(3) */ +#define HD_COLUMN_MASK 0xff +#define HD_DELIM_MASK 0xff00 +#define HD_OMIT_COUNT (1 << 16) +#define HD_OMIT_HEX (1 << 17) +#define HD_OMIT_CHARS (1 << 18) + +#endif /* !_LIBUTIL_H_ */ diff --git a/test/Makefile.am b/test/Makefile.am index 73f1633..e370f41 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -17,6 +17,9 @@ noinst_LTLIBRARIES = \ test_mifare_classic.la \ test_mifare_classic_create_trailer_block.la \ test_mifare_classic_sector_boundaries.la \ + test_mifare_desfire.la \ + test_mifare_desfire_des.la \ + test_mifare_desfire_key.la \ test_mifare_ultralight.la \ test_tlv.la @@ -40,6 +43,17 @@ test_mifare_classic_sector_boundaries_la_LIBADD = $(top_builddir)/libfreefare/li test_mifare_classic_create_trailer_block_la_SOURCES = test_mifare_classic_create_trailer_block.c test_mifare_classic_create_trailer_block_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la +test_mifare_desfire_la_SOURCES = test_mifare_desfire.c \ + mifare_desfire_fixture.c \ + mifare_desfire_fixture.h +test_mifare_desfire_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la -lutil + +test_mifare_desfire_des_la_SOURCES = test_mifare_desfire_des.c +test_mifare_desfire_des_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la -lssl + +test_mifare_desfire_key_la_SOURCES = test_mifare_desfire_key.c +test_mifare_desfire_key_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la + test_mifare_ultralight_la_SOURCES = test_mifare_ultralight.c \ mifare_ultralight_fixture.c \ mifare_ultralight_fixture.h @@ -52,5 +66,6 @@ echo-cutter: @echo $(CUTTER) EXTRA_DIST = run-test.sh +CLEANFILES = *.gcno endif diff --git a/test/mifare_classic_fixture.c b/test/mifare_classic_fixture.c index 8ef8cf0..49f14cf 100644 --- a/test/mifare_classic_fixture.c +++ b/test/mifare_classic_fixture.c @@ -28,28 +28,39 @@ void cut_setup () { int res; + nfc_device_desc_t devices[8]; + size_t device_count; - device = nfc_connect (NULL); - if (!device) + nfc_list_devices (devices, 8, &device_count); + if (!device_count) cut_omit ("No device found"); - tags = freefare_get_tags (device); - cut_assert_not_null (tags, cut_message ("freefare_get_tags() failed")); + for (size_t i = 0; i < device_count; i++) { - tag = NULL; - for (int i=0; tags[i]; i++) { - if ((freefare_get_tag_type(tags[i]) == CLASSIC_1K) || - (freefare_get_tag_type(tags[i]) == CLASSIC_4K)) { - tag = tags[i]; - break; + device = nfc_connect (&(devices[i])); + if (!device) + cut_omit ("nfc_connect() failed"); + + tags = freefare_get_tags (device); + cut_assert_not_null (tags, cut_message ("freefare_get_tags() failed")); + + tag = NULL; + for (int i=0; tags[i]; i++) { + if ((freefare_get_tag_type(tags[i]) == CLASSIC_1K) || + (freefare_get_tag_type(tags[i]) == CLASSIC_4K)) { + tag = tags[i]; + res = mifare_classic_connect (tag); + cut_assert_equal_int (0, res, cut_message ("mifare_classic_connect() failed")); + return; + } } + nfc_disconnect (device); + device = NULL; + freefare_free_tags (tags); + tags = NULL; } - if (!tag) - cut_omit ("No MIFARE Classic tag on NFC device"); - - res = mifare_classic_connect (tag); - cut_assert_equal_int (0, res, cut_message ("mifare_classic_connect() failed")); + cut_omit ("No MIFARE Classic tag on NFC device"); } void @@ -58,8 +69,10 @@ cut_teardown () if (tag) mifare_classic_disconnect (tag); - if (tags) + if (tags) { freefare_free_tags (tags); + tags = NULL; + } if (device) nfc_disconnect (device); diff --git a/test/mifare_desfire_des.c b/test/mifare_desfire_des.c new file mode 100644 index 0000000..72a3a4d --- /dev/null +++ b/test/mifare_desfire_des.c @@ -0,0 +1,33 @@ +/*- + * Copyright (C) 2010, Romain Tartiere. + * + * 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 +#include +#include "freefare_internal.h" + +#include + +void +test_mifare_rol8 (void) +{ + char data[8] = "01234567"; + rol8 (data); + cut_assert_equal_memory (data, 8, "12345670", 8); +} + diff --git a/test/mifare_desfire_fixture.c b/test/mifare_desfire_fixture.c new file mode 100644 index 0000000..7eaeb68 --- /dev/null +++ b/test/mifare_desfire_fixture.c @@ -0,0 +1,79 @@ +/*- + * Copyright (C) 2010, Romain Tartiere. + * + * 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 +#include + +static nfc_device_t *device = NULL; +static MifareTag *tags = NULL; +MifareTag tag = NULL; + +void +cut_setup () +{ + int res; + nfc_device_desc_t devices[8]; + size_t device_count; + + nfc_list_devices (devices, 8, &device_count); + if (!device_count) + cut_omit ("No device found"); + + for (size_t i = 0; i < device_count; i++) { + + device = nfc_connect (&(devices[i])); + if (!device) + cut_omit ("nfc_connect() failed"); + + tags = freefare_get_tags (device); + cut_assert_not_null (tags, cut_message ("freefare_get_tags() failed")); + + tag = NULL; + for (int i=0; tags[i]; i++) { + if (freefare_get_tag_type(tags[i]) == DESFIRE_4K) { + tag = tags[i]; + res = mifare_desfire_connect (tag); + cut_assert_equal_int (0, res, cut_message ("mifare_desfire_connect() failed")); + return; + } + } + nfc_disconnect (device); + device = NULL; + freefare_free_tags (tags); + tags = NULL; + } + + cut_omit ("No MIFARE DESFire tag on NFC device"); +} + +void +cut_teardown () +{ + if (tag) + mifare_desfire_disconnect (tag); + + if (tags) { + freefare_free_tags (tags); + tags = NULL; + } + + if (device) + nfc_disconnect (device); +} + diff --git a/test/mifare_desfire_fixture.h b/test/mifare_desfire_fixture.h new file mode 100644 index 0000000..214bf9c --- /dev/null +++ b/test/mifare_desfire_fixture.h @@ -0,0 +1,20 @@ +/*- + * Copyright (C) 2010, Romain Tartiere. + * + * 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$ + */ + +extern MifareTag tag; diff --git a/test/mifare_ultralight_fixture.c b/test/mifare_ultralight_fixture.c index ee77c7c..625d584 100644 --- a/test/mifare_ultralight_fixture.c +++ b/test/mifare_ultralight_fixture.c @@ -28,27 +28,38 @@ void cut_setup () { int res; + nfc_device_desc_t devices[8]; + size_t device_count; - device = nfc_connect (NULL); - if (!device) + nfc_list_devices (devices, 8, &device_count); + if (!device_count) cut_omit ("No device found"); - tags = freefare_get_tags (device); - cut_assert_not_null (tags, cut_message ("freefare_get_tags() failed")); + for (size_t i = 0; i < device_count; i++) { - tag = NULL; - for (int i=0; tags[i]; i++) { - if (freefare_get_tag_type(tags[i]) == ULTRALIGHT) { - tag = tags[i]; - break; + device = nfc_connect (&(devices[i])); + if (!device) + cut_omit ("nfc_connect() failed"); + + tags = freefare_get_tags (device); + cut_assert_not_null (tags, cut_message ("freefare_get_tags() failed")); + + tag = NULL; + for (int i=0; tags[i]; i++) { + if (freefare_get_tag_type(tags[i]) == ULTRALIGHT) { + tag = tags[i]; + res = mifare_ultralight_connect (tag); + cut_assert_equal_int (0, res, cut_message ("mifare_ultralight_connect() failed")); + return; + } } + nfc_disconnect (device); + device = NULL; + freefare_free_tags (tags); + tags = NULL; } - if (!tag) - cut_omit ("No MIFARE UltraLight tag on NFC device"); - - res = mifare_ultralight_connect (tag); - cut_assert_equal_int (0, res, cut_message ("mifare_ultralight_connect() failed")); + cut_omit ("No MIFARE UltraLight tag on NFC device"); } void @@ -57,8 +68,10 @@ cut_teardown () if (tag) mifare_ultralight_disconnect (tag); - if (tags) + if (tags) { freefare_free_tags (tags); + tags = NULL; + } if (device) nfc_disconnect (device); diff --git a/test/test_mifare_application.c b/test/test_mifare_application.c index ced5567..f2c1ee0 100644 --- a/test/test_mifare_application.c +++ b/test/test_mifare_application.c @@ -27,6 +27,9 @@ test_mifare_application (void) MadAid aid = { 0x22, 0x42 }; Mad mad = mad_new (2); + + int i; + cut_assert_not_null (mad, cut_message ("mad_new() failed")); MifareClassicSectorNumber *s_alloc = mifare_application_alloc (mad, aid, 3*3*16); @@ -35,12 +38,12 @@ test_mifare_application (void) MifareClassicSectorNumber *s_found = mifare_application_find (mad, aid); cut_assert_not_null (s_found, cut_message ("mifare_application_alloc() failed")); - for (int i = 0; i < 3; i++) { + for (i = 0; s_alloc[i]; i++) { cut_assert_equal_int (s_alloc[i], s_found[i], cut_message ("Allocated and found blocks don't match at position %d", i)); } - cut_assert_equal_int (0, s_alloc[3], cut_message ("Invalid size")); - cut_assert_equal_int (0, s_found[3], cut_message ("Invalid size")); + cut_assert_equal_int (0, s_alloc[i], cut_message ("Invalid size")); + cut_assert_equal_int (0, s_found[i], cut_message ("Invalid size")); mifare_application_free (mad, aid); @@ -56,12 +59,14 @@ test_mifare_application (void) s_found = mifare_application_find (mad, aid); cut_assert_not_null (s_found, cut_message ("mifare_application_alloc() failed")); - for (int i = 0; i < 3; i++) { + + + for (i = 0; s_alloc[i]; i++) { cut_assert_equal_int (s_alloc[i], s_found[i], cut_message ("Allocated and found blocks don't match at position %d", i)); } - cut_assert_equal_int (0, s_alloc[3], cut_message ("Invalid size")); - cut_assert_equal_int (0, s_found[3], cut_message ("Invalid size")); + cut_assert_equal_int (0, s_alloc[i], cut_message ("Invalid size")); + cut_assert_equal_int (0, s_found[i], cut_message ("Invalid size")); mifare_application_free (mad, aid); diff --git a/test/test_mifare_classic.c b/test/test_mifare_classic.c index 1c9059a..505b0c5 100644 --- a/test/test_mifare_classic.c +++ b/test/test_mifare_classic.c @@ -19,6 +19,7 @@ #include #include +#include #include #include "freefare_internal.h" @@ -113,7 +114,7 @@ test_mifare_classic_format (void) }; MifareClassicBlock empty; - memset (empty, '\x00', sizeof (empty)); + bzero (empty, sizeof (empty)); res = mifare_classic_write (tag, 0x3c, data); cut_assert_equal_int (0, res, cut_message ("mifare_classic_write() failed")); diff --git a/test/test_mifare_desfire.c b/test/test_mifare_desfire.c new file mode 100644 index 0000000..504c5f4 --- /dev/null +++ b/test/test_mifare_desfire.c @@ -0,0 +1,969 @@ +/*- + * Copyright (C) 2010, Romain Tartiere. + * + * 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 +#include +#include +#include + +#include +#include "freefare_internal.h" + +#include "mifare_desfire_fixture.h" + +uint8_t key_data_null[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +uint8_t key_data_des[8] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' }; +uint8_t key_data_3des[16] = { 'C', 'a', 'r', 'd', ' ', 'M', 'a', 's', 't', 'e', 'r', ' ', 'K', 'e', 'y', '!' }; + +#define cut_assert_success(last_command) \ + do { \ + if ((res < 0) || (MIFARE_DESFIRE (tag)->last_picc_error != OPERATION_OK)) { \ + cut_fail ("%s returned %d, error: %s, errno: %s\n", last_command, res, desfire_error_lookup (MIFARE_DESFIRE (tag)->last_picc_error), strerror (errno)); \ + } \ + } while (0); + +void +test_mifare_desfire (void) +{ + int res; + + /* Select the master application */ + res = mifare_desfire_select_application (tag, NULL); + cut_assert_success ("mifare_desfire_select_application()"); + + /* Get version information */ + struct mifare_desfire_version_info version_info; + res = mifare_desfire_get_version (tag, &version_info); + cut_assert_success ("mifare_desfire_get_version()"); + + /* Determine which key is currently the master one */ + uint8_t key_version; + res = mifare_desfire_get_key_version (tag, 0, &key_version); + cut_assert_success ("mifare_desfire_get_key_version()"); + + MifareDESFireKey key; + + switch (key_version) { + case 0x00: + key = mifare_desfire_des_key_new_with_version (key_data_null); + break; + case 0xAA: + key = mifare_desfire_des_key_new_with_version (key_data_des); + break; + case 0xC7: + key = mifare_desfire_3des_key_new_with_version (key_data_3des); + break; + default: + cut_fail ("Unknown master key."); + } + + cut_assert_not_null (key, cut_message ("Cannot allocate key")); + + /* Authenticate with this key */ + res = mifare_desfire_authenticate (tag, 0, key); + cut_assert_success ("mifare_desfire_authenticate()"); + + mifare_desfire_key_free (key); + + /* + * This unit test change key settings to more restrictive ones, so reset + * them to factory defaults in case the previous run failed unexpectedly. + */ + res = mifare_desfire_change_key_settings (tag, 0xF); + cut_assert_success ("mifare_desfire_change_key_settings()"); + + /* Change master key to DES */ + key = mifare_desfire_des_key_new_with_version (key_data_des); + mifare_desfire_change_key (tag, 0, key, NULL); + cut_assert_success ("mifare_desfire_change_key()"); + + res = mifare_desfire_get_key_version (tag, 0, &key_version); + cut_assert_success ("mifare_desfire_get_key_version()"); + cut_assert_equal_int (0xAA, key_version, cut_message ("Wrong key_version value.")); + + res = mifare_desfire_authenticate (tag, 0, key); + cut_assert_success ("mifare_desfire_authenticate()"); + mifare_desfire_key_free (key); + + /* Change master key to 3DES */ + key = mifare_desfire_3des_key_new_with_version (key_data_3des); + mifare_desfire_change_key (tag, 0, key, NULL); + cut_assert_success ("mifare_desfire_change_key()"); + + res = mifare_desfire_get_key_version (tag, 0, &key_version); + cut_assert_success ("mifare_desfire_get_key_version()"); + cut_assert_equal_int (0xC7, key_version, cut_message ("Wrong key_version value.")); + + res = mifare_desfire_authenticate (tag, 0, key); + cut_assert_success ("mifare_desfire_authenticate()"); + + mifare_desfire_key_free (key); + + /* Wipeout the card */ + res = mifare_desfire_format_picc (tag); + cut_assert_success ("mifare_desfire_format_picc()"); + + + /* Create 3 applications */ + res = mifare_desfire_select_application (tag, NULL); + cut_assert_success ("mifare_desfire_select_application()"); + + MifareDESFireAID aid_a = mifare_desfire_aid_new (0xAA, 0xAA, 0xA); + cut_assert_not_null (aid_a, cut_message ("Cannot allocate AID")); +#ifdef SNAPPER + // FIXME: For some reason, 0xFF fails + res = mifare_desfire_create_application (tag, aid_a, 0xEF, 0); +#else + res = mifare_desfire_create_application (tag, aid_a, 0xFF, 0); +#endif + cut_assert_success ("mifare_desfire_create_application()"); + + MifareDESFireAID aid_b = mifare_desfire_aid_new (0xBB, 0xBB, 0xB); + cut_assert_not_null (aid_b, cut_message ("Cannot allocate AID")); + res = mifare_desfire_create_application (tag, aid_b, 0xEF, 6); + cut_assert_success ("mifare_desfire_create_application()"); + +#ifdef SNAPPER + // FIXME: For some reason 0xCC CC C fails when authenticating + MifareDESFireAID aid_c = mifare_desfire_aid_new (0x12, 0x34, 0x5); +#else + MifareDESFireAID aid_c = mifare_desfire_aid_new (0xCC, 0xCC, 0xC); +#endif + cut_assert_not_null (aid_c, cut_message ("Cannot allocate AID")); + res = mifare_desfire_create_application (tag, aid_c, 0xC2, 14); + cut_assert_success ("mifare_desfire_create_application()"); + + // Ensure we can find the created applications + MifareDESFireAID *aids = NULL; + size_t aid_count; + res = mifare_desfire_get_application_ids (tag, &aids, &aid_count); + cut_assert_success ("mifare_desfire_get_application_ids()"); + cut_assert_equal_int (3, aid_count, cut_message ("Wrong application count")); + mifare_desfire_free_application_ids (aids); + + // Create files in the application A + res = mifare_desfire_select_application (tag, aid_a); + cut_assert_success ("mifare_desfire_select_application()"); + + uint8_t std_data_file_id = 15; + + res = mifare_desfire_create_std_data_file (tag, std_data_file_id, MDCM_PLAIN, 0xEEEE, 100); + cut_assert_success ("mifare_desfire_create_std_data_file()"); + + res = mifare_desfire_create_backup_data_file (tag, 5, MDCM_PLAIN, 0xEEEE, 64); + cut_assert_success ("mifare_desfire_create_backup_data_file()"); + + res = mifare_desfire_create_value_file (tag, 4, MDCM_PLAIN, 0xEEEE, 0, 1000, 0, 0); + cut_assert_success ("mifare_desfire_create_value_file()"); + + res = mifare_desfire_create_cyclic_record_file (tag, 0, MDCM_PLAIN, 0xEEEE, 4, 10); + cut_assert_success ("mifare_desfire_create_cyclic_record_file()"); + + // Write some data in the standard data file + res = mifare_desfire_write_data (tag, std_data_file_id, 0, 30, (uint8_t *)"Some data to write to the card"); + cut_assert_success ("mifare_desfire_write_data()"); + cut_assert_equal_int (30, res, cut_message ("Wrong number of bytes writen")); + + res = mifare_desfire_write_data (tag, std_data_file_id, 34, 22, (uint8_t *)"Another block of data."); + cut_assert_success ("mifare_desfire_write_data()"); + cut_assert_equal_int (22, res, cut_message ("Wrong number of bytes writen")); + + // Make the file read-only + res = mifare_desfire_change_file_settings (tag, std_data_file_id, MDCM_PLAIN, 0xEFFF); + cut_assert_success ("mifare_desfire_change_file_settings()"); + + // Read a part of the file + uint8_t buffer[120]; + res = mifare_desfire_read_data (tag, std_data_file_id, 10, 50, &buffer); + cut_assert_success ("mifare_desfire_read_data()"); + cut_assert_equal_int (50, res, cut_message ("Wrong number of bytes read")); + cut_assert_equal_memory ("to write to the card\0\0\0\0Another block of data.\0\0\0\0", 50, buffer, 50, cut_message ("Wrong data")); + + // Read all the file at once + res = mifare_desfire_read_data (tag, std_data_file_id, 0, 0, &buffer); + cut_assert_success ("mifare_desfire_read_data()"); + cut_assert_equal_int (100, res, cut_message ("Wrong number of bytes read")); + cut_assert_equal_memory ("Some data to write to the" + " card\0\0\0\0Another block of" + " data.\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 100, buffer, 100, cut_message ("Wrong data")); + + // Try to overwrute the file + res = mifare_desfire_write_data (tag, std_data_file_id, 20, 5, (char *)"Test!"); + cut_assert_equal_int (-1, res, cut_message ("Wrong return value")); + cut_assert_equal_int (PERMISSION_ERROR, MIFARE_DESFIRE (tag)->last_picc_error, cut_message ("Wrong PICC error")); + + int32_t expected_value = 0; + for (int transaction = 0; transaction < 15; transaction++) { + + char data_buffer[3]; + + sprintf (data_buffer, "%02d", transaction); + + // Write to the backup file + res = mifare_desfire_write_data (tag, 5, 3*transaction, 3, data_buffer); + cut_assert_success ("mifare_desfire_write_data()"); + + // Manipulate the value file + res = mifare_desfire_credit (tag, 4, 100); + cut_assert_success ("mifare_desfire_credit()"); + + res = mifare_desfire_debit (tag, 4, 97); + cut_assert_success ("mifare_desfire_debit()"); + + // Write to the cyclic record file + res = mifare_desfire_write_record (tag, 0, 2, 2, data_buffer); + cut_assert_success ("mifare_desfire_write_record()"); + + // Overwrite the cyclic record file + res = mifare_desfire_write_record (tag, 0, 0, 2, (char *)"r."); + cut_assert_success("mifare_desfire_write_record()"); + + // Ensure that no content was changed yet + char ref_buffer[64]; + bzero (ref_buffer, sizeof (ref_buffer)); + for (int n = 0; n < transaction; n++) { + sprintf (ref_buffer + 3 * n, "%02d", n); + } + + res = mifare_desfire_read_data (tag, 5, 0, 0, buffer); + cut_assert_success ("mifare_desfire_read_data()"); + cut_assert_equal_int (64, res, cut_message ("Wrong number of bytes read")); + cut_assert_equal_memory (buffer, 64, ref_buffer, 64, cut_message ("Wrong data")); + + int32_t value; + res = mifare_desfire_get_value (tag, 4, &value); + cut_assert_success ("mifare_desfire_get_value()"); + cut_assert_equal_int (expected_value, value, cut_message ("Wrong value")); + + // Reading records from an empty file would abort the transaction + if (0 != transaction) { + // Get the latest record + res = mifare_desfire_read_records (tag, 0, 0, 1, buffer); + cut_assert_success ("mifare_desfire_read_records()"); + sprintf (ref_buffer, "r.%02d", transaction); + cut_assert_not_equal_memory (ref_buffer, 4, buffer, res, cut_message ("Wrong data")); + } + + // Commit ! + res = mifare_desfire_commit_transaction (tag); + cut_assert_success ("mifare_desfire_commit_transaction()"); + + + res = mifare_desfire_read_data (tag, 5, 3*transaction, 3, buffer); + cut_assert_success ("mifare_desfire_read_data()"); + cut_assert_equal_memory (data_buffer, 3, buffer, res, cut_message ("Wrong data")); + + expected_value += 3; + + res = mifare_desfire_get_value (tag, 4, &value); + cut_assert_success ("mifare_desfire_get_value()"); + cut_assert_equal_int (expected_value, value, cut_message ("Wrong value")); + + res = mifare_desfire_read_records (tag, 0, 0, 1, buffer); + cut_assert_success ("mifare_desfire_read_records()"); + sprintf (ref_buffer, "r.%02d", transaction); + cut_assert_equal_memory (ref_buffer, 4, buffer, res, cut_message ("Wrong data")); + } + + // Ensure limited credit is disabled + res = mifare_desfire_limited_credit (tag, 4, 20); + cut_assert_equal_int (-1, res, cut_message ("mifare_desfire_limited_credit() should fail")); + + // Get all files + uint8_t *files; + size_t file_count; + res = mifare_desfire_get_file_ids (tag, &files, &file_count); + cut_assert_success ("mifare_desfire_get_file_ids()"); + cut_assert_equal_int (4, file_count, cut_message ("Wrong number of files")); + + for (size_t i=0; ilast_picc_error, cut_message ("PICC error")); + + // The prebious failure has aborted the transaction, so clear + // record again. + res = mifare_desfire_clear_record_file (tag, 1); + cut_assert_success ("mifare_desfire_clear_record_file()"); + + res = mifare_desfire_commit_transaction (tag); + cut_assert_success ("mifare_desfire_commit_transaction()"); + nr = 0; + } + + res = mifare_desfire_write_record (tag, 1, 0, 25, "0123456789012345678901234"); + cut_assert_success ("mifare_desfire_write_record()"); + + res = mifare_desfire_write_record (tag, 1, 5, strlen (data_buffer2), data_buffer2); + cut_assert_success ("mifare_desfire_write_record()"); + } + nr++; + + // Modify the value file + res = mifare_desfire_debit (tag, 4, 1300); + cut_assert_success ("mifare_desfire_debit()"); + expected_value -= 1300; + res = mifare_desfire_credit (tag, 4, 20); + cut_assert_success ("mifare_desfire_credit()"); + expected_value += 20; + res = mifare_desfire_debit (tag, 4, 1700); + cut_assert_success ("mifare_desfire_debit()"); + expected_value -= 1700; + + // Commit + res = mifare_desfire_commit_transaction (tag); + cut_assert_success ("mifare_desfire_commit_transaction()"); + + // Refund the whole debited amount + res = mifare_desfire_limited_credit (tag, 4, 3000); + cut_assert_success ("mifare_desfire_limited_credit()"); + expected_value += 3000; + + // Commit + res = mifare_desfire_commit_transaction (tag); + cut_assert_success ("mifare_desfire_commit_transaction()"); + + // Authenticate with the key that allows reading + key = mifare_desfire_3des_key_new_with_version ((uint8_t *) "App.B Key #1. "); + res = mifare_desfire_authenticate (tag, 1, key); + cut_assert_success ("mifare_desfire_authenticate()"); + mifare_desfire_key_free (key); + + // Read first half of the file + res = mifare_desfire_read_data (tag, std_data_file_id, 0, 50, data_buffer3); + cut_assert_success ("mifare_desfire_read_data()"); + cut_assert_equal_int (50, res, cut_message ("length")); + cut_assert_equal_memory (data_buffer, 50, data_buffer3, res, cut_message ("data")); + + // Read second half of the file + res = mifare_desfire_read_data (tag, std_data_file_id, 50, 0, data_buffer3); + cut_assert_success ("mifare_desfire_read_data()"); + cut_assert_equal_int (50, res, cut_message ("length")); + cut_assert_equal_memory (data_buffer + 50, 50, data_buffer3, res, cut_message ("data")); + + // Get the value file current balance + int32_t value; + res = mifare_desfire_get_value (tag, 4, &value); + cut_assert_success ("mifare_desfire_get_value()"); + cut_assert_equal_int (expected_value, value, cut_message ("value")); + + // Get the number of records in the linear record file + struct mifare_desfire_file_settings settings; + res = mifare_desfire_get_file_settings (tag, 1, &settings); + cut_assert_success ("mifare_desfire_get_file_settings()"); + cut_assert_equal_int (MDFT_LINEAR_RECORD_FILE_WITH_BACKUP, settings.file_type, cut_message ("settings")); + cut_assert_equal_int (nr, settings.settings.linear_record_file.current_number_of_records, cut_message ("settings")); + + // Read the oldest record + res = mifare_desfire_read_records (tag, 1, nr - 1, 1, data_buffer3); + cut_assert_success ("mifare_desfire_read_records()"); + cut_assert_equal_int (25, res, cut_message ("length")); + + sprintf (data_buffer, "0123456789012345678901234"); + sprintf (data_buffer2, "Transaction #%d", transaction - nr + 1); + memcpy ((uint8_t *)data_buffer + 5, data_buffer2, strlen (data_buffer2)); + cut_assert_equal_memory (data_buffer, strlen (data_buffer), data_buffer3, res, cut_message ("data")); + + // Read all records + res = mifare_desfire_read_records (tag, 1, 0, 0, data_buffer3); + cut_assert_success ("mifare_desfire_read_records()"); + cut_assert_equal_int (25 * nr, res, cut_message ("length")); + } + + res = mifare_desfire_get_file_ids (tag, &files, &file_count); + cut_assert_success ("mifare_desfire_get_file_ids"); + cut_assert_equal_int (3, file_count, cut_message ("count")); + + for (size_t i = 0; i < file_count; i++) { + struct mifare_desfire_file_settings settings; + res = mifare_desfire_get_file_settings (tag, files[i], &settings); + cut_assert_success ("mifare_desfire_get_file_settings()"); + + switch (files[i]) { + case 1: + cut_assert_equal_int (MDFT_LINEAR_RECORD_FILE_WITH_BACKUP, settings.file_type, cut_message ("type")); + cut_assert_equal_int (MDCM_PLAIN, settings.communication_settings, cut_message ("cs")); + cut_assert_equal_int (0x1324, settings.access_rights, cut_message ("access_rights")); + cut_assert_equal_int (25, settings.settings.linear_record_file.record_size, cut_message ("record_size")); + cut_assert_equal_int (4, settings.settings.linear_record_file.max_number_of_records, cut_message ("max_number_of_records")); + cut_assert_equal_int (2, settings.settings.linear_record_file.current_number_of_records, cut_message ("current_number_of_records")); + break; + case 4: + cut_assert_equal_int (MDFT_VALUE_FILE_WITH_BACKUP , settings.file_type, cut_message ("type")); + cut_assert_equal_int (MDCM_PLAIN, settings.communication_settings, cut_message ("cs")); + cut_assert_equal_int (0x1324, settings.access_rights, cut_message ("access_rights")); + cut_assert_equal_int (-987654321, settings.settings.value_file.lower_limit, cut_message ("lower_limit")); + cut_assert_equal_int (-1000, settings.settings.value_file.upper_limit, cut_message ("upper_limit")); + cut_assert_equal_int (0, settings.settings.value_file.limited_credit_value, cut_message ("limited_credit_value")); + cut_assert_equal_int (1, settings.settings.value_file.limited_credit_enabled, cut_message ("limited_credit_enabled")); + break; + case 14: /* std_data_file_id */ + cut_assert_equal_int (MDFT_STANDARD_DATA_FILE, settings.file_type, cut_message ("type")); + cut_assert_equal_int (MDCM_PLAIN, settings.communication_settings, cut_message ("cs")); + cut_assert_equal_int (0x1234, settings.access_rights, cut_message ("access_rights")); + cut_assert_equal_int (100, settings.settings.standard_file.file_size, cut_message ("size")); + break; + default: + cut_fail ("file_no"); + + } + + res = mifare_desfire_delete_file (tag, files[i]); + cut_assert_success ("mifare_desfire_delete_file()"); + } + free (files); + + // Check there are no files anymore + res = mifare_desfire_get_file_ids (tag, &files, &file_count); + cut_assert_success ("mifare_desfire_get_file_ids"); + cut_assert_equal_int (0, file_count, cut_message ("count")); + + /* Delete application B */ + key = mifare_desfire_3des_key_new_with_version ((uint8_t *) "App.B Master Key"); + res = mifare_desfire_authenticate (tag, 0, key); + cut_assert_success ("mifare_desfire_authenticate()"); + mifare_desfire_key_free (key); + + res = mifare_desfire_delete_application (tag, aid_b); + cut_assert_success ("mifare_desfire_delete_application()"); + + res = mifare_desfire_get_application_ids (tag, &aids, &aid_count); + cut_assert_success ("mifare_desfire_get_application_ids()"); + cut_assert_equal_int (1, aid_count, cut_message ("Wrong AID count")); + mifare_desfire_free_application_ids (aids); + + /* Tests using application C */ + + res = mifare_desfire_select_application (tag, aid_c); + cut_assert_success ("mifare_desfire_select_application()"); + + key = mifare_desfire_des_key_new_with_version (key_data_null); + res = mifare_desfire_authenticate (tag, 12, key); + cut_assert_success ("mifare_desfire_authenticate()"); + + MifareDESFireKey new_key = mifare_desfire_3des_key_new_with_version ((uint8_t *)"App.C Key #1. "); + res = mifare_desfire_change_key (tag, 1, new_key, key); + cut_assert_success ("mifare_desfire_change_key()"); + mifare_desfire_key_free (new_key); + + new_key = mifare_desfire_3des_key_new_with_version ((uint8_t *)"App.C Key #2.. "); + res = mifare_desfire_change_key (tag, 2, new_key, key); + cut_assert_success ("mifare_desfire_change_key()"); + mifare_desfire_key_free (new_key); + + res = mifare_desfire_authenticate (tag, 0, key); + cut_assert_success ("mifare_desfire_authenticate()"); + mifare_desfire_key_free (key); + + + res = mifare_desfire_create_cyclic_record_file (tag, 6, MDCM_PLAIN, 0x12E0, 100, 22); + cut_assert_success ("mifare_desfire_create_cyclic_record_file()"); + + for (int transaction = 0; transaction < 50; transaction++) { + char data_buffer[100]; + char read_buffer[100]; + + uint8_t cs = transaction % 3; + if (cs == 2) cs++; + + key = mifare_desfire_des_key_new (key_data_null); + res = mifare_desfire_authenticate (tag, 0, key); + cut_assert_success ("mifare_desfire_authenticate()"); + mifare_desfire_key_free (key); + + res = mifare_desfire_change_file_settings (tag, 6, cs, 0x12E0); + cut_assert_success ("mifare_desfire_change_file_settings()"); + + if (transaction & 4) { + key = mifare_desfire_3des_key_new_with_version ((uint8_t *) "App.C Key #2.. "); + res = mifare_desfire_authenticate (tag, 2, key); + cut_assert_success ("mifare_desfire_authenticate()"); + mifare_desfire_key_free (key); + } else { + cs = 0; + } + + memset (data_buffer, '_', 100); + data_buffer[0] = transaction; + data_buffer[99] = transaction; + sprintf (data_buffer + 5, " Transaction #%d ", transaction); + res = mifare_desfire_write_record (tag, 6, 0, 100, data_buffer); + cut_assert_success ("mifare_desfire_write_record()"); + + if (transaction & 4) { + key = mifare_desfire_3des_key_new_with_version ((uint8_t *) "App.C Key #1. "); + res = mifare_desfire_authenticate (tag, 1, key); + cut_assert_success ("mifare_desfire_authenticate()"); + mifare_desfire_key_free (key); + } + + if (transaction % 7 == 0) { + res = mifare_desfire_abort_transaction (tag); + cut_assert_success ("mifare_desfire_abort_transaction()"); + + ssize_t n = mifare_desfire_read_records (tag, 6, 0, 1, read_buffer); + if (transaction == 0) { + cut_assert_equal_int (-1, n, cut_message ("Wrong return value")); + } else { + cut_assert_equal_int (100, n, cut_message ("Wrong return value")); + cut_assert_not_equal_memory (data_buffer, sizeof (data_buffer), read_buffer, sizeof (read_buffer), cut_message ("Wrong data")); + } + } else { + res = mifare_desfire_commit_transaction (tag); + cut_assert_success ("mifare_desfire_commit_transaction()"); + + ssize_t n = mifare_desfire_read_records (tag, 6, 0, 1, read_buffer); + cut_assert_equal_int (100, n, cut_message ("Wrong return value")); + cut_assert_equal_memory (data_buffer, sizeof (data_buffer), read_buffer, sizeof (read_buffer), cut_message ("Wrong data")); + } + } + + // Read each record + key = mifare_desfire_3des_key_new_with_version ((uint8_t *) "App.C Key #1. "); + res = mifare_desfire_authenticate (tag, 1, key); + cut_assert_success ("mifare_desfire_authenticate()"); + mifare_desfire_key_free (key); + + int t = 49; + for (int i = 0; i < 22; i++) { + char data_buffer[100]; + char ref_data_buffer[100]; + + if (0 == (t % 7)) + t--; + + memset (ref_data_buffer, '_', 100); + ref_data_buffer[0] = t; + ref_data_buffer[99] = t; + sprintf (ref_data_buffer + 5, " Transaction #%d ", t); + res = mifare_desfire_read_records (tag, 6, i, 1, data_buffer); + if (i == 21) { + cut_assert_equal_int (-1, res, cut_message ("return value")); + } else { + cut_assert_success ("mifare_desfire_read_records()"); + cut_assert_equal_memory (ref_data_buffer, 100, data_buffer, res, cut_message ("data")); + } + + t--; + } + + /* + * Change master key settings to require master key authentication for all + * card operations. Only allow to revert this. + */ + + res = mifare_desfire_select_application (tag, 0); + cut_assert_success ("mifare_desfire_select_application()"); + + key = mifare_desfire_3des_key_new_with_version (key_data_3des); + res = mifare_desfire_authenticate (tag, 0, key); + cut_assert_success ("mifare_desfire_authenticate()"); + mifare_desfire_key_free (key); + + res = mifare_desfire_change_key_settings (tag, 0x08); + cut_assert_success ("mifare_desfire_change_key_settings()"); + + /* Clear authentication */ + res = mifare_desfire_select_application (tag, 0); + cut_assert_success ("mifare_desfire_select_application()"); + + /* We should not be able to list applications now */ + res = mifare_desfire_get_application_ids (tag, &aids, &aid_count); + cut_assert_equal_int (-1, res, cut_message ("Wrong return value")); + cut_assert_equal_int (AUTHENTICATION_ERROR, MIFARE_DESFIRE (tag)->last_picc_error, cut_message ("Wrong PICC error")); + + /* Deleting an application should not be possible */ + res = mifare_desfire_delete_application (tag, aid_c); + cut_assert_equal_int (-1, res, cut_message ("Wrong return value")); + cut_assert_equal_int (AUTHENTICATION_ERROR, MIFARE_DESFIRE (tag)->last_picc_error, cut_message ("Wrong PICC error")); + + /* Creating an application should also be forbidden */ + MifareDESFireAID aid_d = mifare_desfire_aid_new (0xDD, 0xDD, 0xD); + res = mifare_desfire_create_application (tag, aid_d, 0xEF, 0); + cut_assert_equal_int (-1, res, cut_message ("Wrong return value")); + cut_assert_equal_int (AUTHENTICATION_ERROR, MIFARE_DESFIRE (tag)->last_picc_error, cut_message ("Wrong PICC error")); + + /* + * Now we retry authenticated with the master key. + */ + key = mifare_desfire_3des_key_new_with_version (key_data_3des); + res = mifare_desfire_authenticate (tag, 0, key); + cut_assert_success ("mifare_desfire_authenticate()"); + mifare_desfire_key_free (key); + + /* We should be able to list applications again */ + res = mifare_desfire_get_application_ids (tag, &aids, &aid_count); + cut_assert_success ("mifare_desfire_get_application_ids()"); + cut_assert_equal_int (1, aid_count, cut_message ("Wrong AID count")); + mifare_desfire_free_application_ids (aids); + + /* Deleting an application should be possible again */ + res = mifare_desfire_delete_application (tag, aid_c); + cut_assert_success ("mifare_desfire_delete_application()"); + + /* Creating an application should also be possible */ + res = mifare_desfire_create_application (tag, aid_d, 0xEF, 0); + cut_assert_success ("mifare_desfire_create_application()"); + + /* Revert master key settings to default */ + res = mifare_desfire_change_key_settings (tag, 0xF); + cut_assert_success ("mifare_desfire_change_key_settings()"); + + /* Change the master key back to the default one */ + key = mifare_desfire_3des_key_new_with_version (key_data_3des); + res = mifare_desfire_authenticate (tag, 0, key); + cut_assert_success ("mifare_desfire_authenticate()"); + mifare_desfire_key_free (key); + + key = mifare_desfire_des_key_new_with_version (key_data_null); + res = mifare_desfire_change_key (tag, 0, key, NULL); + cut_assert_success ("mifare_desfire_change_key()"); + + /* + * Delete everything from the card + */ + + res = mifare_desfire_authenticate (tag, 0, key); + cut_assert_success ("mifare_desfire_authenticate()"); + mifare_desfire_key_free (key); + + res = mifare_desfire_format_picc (tag); + cut_assert_success ("mifare_desfire_format_picc()"); + + free (aid_a); + free (aid_b); + free (aid_c); + free (aid_d); +} + +void +test_mifare_desfire_get_tag_friendly_name (void) +{ + const char *name = freefare_get_tag_friendly_name (tag); + + cut_assert_not_null (name, cut_message ("freefare_get_tag_friendly_name() failed")); +} + +#define NAID 28 + +void +test_mifare_desfire_get_many_application_ids (void) +{ + int res; + + MifareDESFireKey key = mifare_desfire_des_key_new_with_version (key_data_null); + res = mifare_desfire_authenticate (tag, 0, key); + cut_assert_success ("mifare_desfire_authenticate()"); + mifare_desfire_key_free (key); + + /* Wipeout the card */ + res = mifare_desfire_format_picc (tag); + cut_assert_success ("mifare_desfire_format_picc()"); + + for (int i = 0; i < NAID; i++) { + MifareDESFireAID aid = mifare_desfire_aid_new (i, i, i % 0x10); + + res = mifare_desfire_create_application (tag, aid, 0xff, 0); + cut_assert_success ("mifare_desfire_create_application()"); + + free (aid); + } + + + MifareDESFireAID *aids; + size_t aid_count; + + res = mifare_desfire_get_application_ids (tag, &aids, &aid_count); + cut_assert_success ("mifare_desfire_get_application_ids()"); + + cut_assert_equal_int (NAID, aid_count, cut_message ("count")); + + mifare_desfire_free_application_ids (aids); + + /* Wipeout the card */ + res = mifare_desfire_format_picc (tag); + cut_assert_success ("mifare_desfire_format_picc()"); +} + +void +test_mifare_desfire_des_macing(void) +{ + int res; + + MifareDESFireKey key = mifare_desfire_des_key_new_with_version (key_data_null); + res = mifare_desfire_authenticate (tag, 0, key); + cut_assert_success ("mifare_desfire_authenticate()"); + + MifareDESFireAID aid = mifare_desfire_aid_new (0x12, 0x34, 0x5); + res = mifare_desfire_create_application (tag, aid, 0xFF, 1); + cut_assert_success ("mifare_desfire_create_application()"); + + res = mifare_desfire_select_application (tag, aid); + cut_assert_success ("mifare_desfire_select_application"); + free (aid); + + res = mifare_desfire_authenticate (tag, 0, key); + cut_assert_success ("mifare_desfire_authenticate()"); + + res = mifare_desfire_create_std_data_file (tag, 1, MDCM_MACING, 0x0000, 20); + cut_assert_success ("mifare_desfire_create_std_data_file()"); + + char *s= "Hello World"; + res = mifare_desfire_write_data (tag, 1, 0, strlen (s), s); + cut_assert_success ("mifare_desfire_write_data()"); + + char buffer[20]; + res = mifare_desfire_read_data (tag, 1, 0, 0, buffer); + cut_assert_success ("mifare_desfire_read_data()"); + cut_assert_equal_int (20, res, cut_message ("retval")); + cut_assert_equal_string (s, buffer, cut_message ("value")); + + res = mifare_desfire_select_application (tag, NULL); + cut_assert_success ("mifare_desfire_select_application"); + res = mifare_desfire_authenticate (tag, 0, key); + cut_assert_success ("mifare_desfire_authenticate()"); + + /* Wipeout the card */ + res = mifare_desfire_format_picc (tag); + cut_assert_success ("mifare_desfire_format_picc()"); + + mifare_desfire_key_free (key); +} diff --git a/test/test_mifare_desfire_des.c b/test/test_mifare_desfire_des.c new file mode 100644 index 0000000..ef6c651 --- /dev/null +++ b/test/test_mifare_desfire_des.c @@ -0,0 +1,67 @@ +/*- + * Copyright (C) 2010, Romain Tartiere. + * + * 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 +#include +#include "freefare_internal.h" + +void +test_mifare_rol8 (void) +{ + uint8_t data[8] = "01234567"; + rol8 (data); + cut_assert_equal_memory ("12345670", 8, data, 8, cut_message ("Wrong data")); +} + +void +test_mifare_desfire_des_receive (void) +{ + uint8_t data[8] = { 0xd6, 0x59, 0xe1, 0x70, 0x43, 0xa8, 0x40, 0x68 }; + uint8_t key_data[8] = { 1, 1, 1, 1, 1, 1, 1, 1 }; + MifareDESFireKey key = mifare_desfire_des_key_new_with_version (key_data); + + uint8_t expected_data[8] = { 0x73, 0x0d, 0xdf, 0xad, 0xa4, 0xd2, 0x07, 0x89 }; + uint8_t expected_key[8] = { 1, 1, 1, 1, 1, 1, 1, 1 }; + + mifare_cbc_des (key, data, 8, MD_RECEIVE, 0); + + cut_assert_equal_memory (&expected_data, 8, &data, 8, cut_message ("Wrong data")); + cut_assert_equal_memory (&expected_key, 8, key->data, 8, cut_message ("Wrong key")); + + mifare_desfire_key_free (key); +} + + +void +test_mifare_desfire_des_send (void) +{ + uint8_t data[8] = { 0x73, 0x0d, 0xdf, 0xad, 0xa4, 0xd2, 0x07, 0x89 }; + uint8_t key_data[8] = { 1, 1, 1, 1, 1, 1, 1, 1 }; + MifareDESFireKey key = mifare_desfire_des_key_new_with_version (key_data); + + uint8_t expected_data[8] = { 0xd6, 0x59, 0xe1, 0x70, 0x43, 0xa8, 0x40, 0x68 }; + uint8_t expected_key[8] = { 1, 1, 1, 1, 1, 1, 1, 1 }; + + mifare_cbc_des (key, data, 8, MD_SEND, 0); + + cut_assert_equal_memory (&expected_data, 8, &data, 8, cut_message ("Wrong data")); + cut_assert_equal_memory (&expected_key, 8, key->data, 8, cut_message ("Wrong key")); + + mifare_desfire_key_free (key); +} diff --git a/test/test_mifare_desfire_key.c b/test/test_mifare_desfire_key.c new file mode 100644 index 0000000..5cd9941 --- /dev/null +++ b/test/test_mifare_desfire_key.c @@ -0,0 +1,98 @@ +/*- + * Copyright (C) 2010, Romain Tartiere. + * + * 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 + +#include + + +void +test_mifare_desfire_key (void) +{ + MifareDESFireKey key; + int version; + + uint8_t key1_des_data[8] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 }; + + key = mifare_desfire_des_key_new (key1_des_data); + version = mifare_desfire_key_get_version (key); + cut_assert_equal_int (0x00, version, cut_message ("Wrong MifareDESFireKey version")); + mifare_desfire_key_free (key); + + key = mifare_desfire_des_key_new_with_version (key1_des_data); + version = mifare_desfire_key_get_version (key); + cut_assert_equal_int (0x55, version, cut_message ("Wrong MifareDESFireKey version")); + mifare_desfire_key_set_version (key, 0xaa); + version = mifare_desfire_key_get_version (key); + cut_assert_equal_int (0xaa, version, cut_message ("Wrong MifareDESFireKey version")); + mifare_desfire_key_free (key); + + + uint8_t key2_des_data[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + key = mifare_desfire_des_key_new (key2_des_data); + version = mifare_desfire_key_get_version (key); + cut_assert_equal_int (0x00, version, cut_message ("Wrong MifareDESFireKey version")); + mifare_desfire_key_free (key); + + key = mifare_desfire_des_key_new_with_version (key2_des_data); + version = mifare_desfire_key_get_version (key); + cut_assert_equal_int (0x00, version, cut_message ("Wrong MifareDESFireKey version")); + mifare_desfire_key_free (key); + + + uint8_t key1_3des_data[16] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0XEE, 0xFF }; + + key = mifare_desfire_3des_key_new (key1_3des_data); + version = mifare_desfire_key_get_version (key); + cut_assert_equal_int (0x00, version, cut_message ("Wrong MifareDESFireKey version")); + mifare_desfire_key_free (key); + + key = mifare_desfire_3des_key_new_with_version (key1_3des_data); + version = mifare_desfire_key_get_version (key); + cut_assert_equal_int (0x55, version, cut_message ("Wrong MifareDESFireKey version")); + mifare_desfire_key_set_version (key, 0xaa); + version = mifare_desfire_key_get_version (key); + cut_assert_equal_int (0xaa, version, cut_message ("Wrong MifareDESFireKey version")); + mifare_desfire_key_free (key); + + uint8_t key2_3des_data[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0X01, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 }; + + key = mifare_desfire_3des_key_new (key2_3des_data); + version = mifare_desfire_key_get_version (key); + cut_assert_equal_int (0x00, version, cut_message ("Wrong MifareDESFireKey version")); + mifare_desfire_key_free (key); + + key = mifare_desfire_3des_key_new_with_version (key2_3des_data); + version = mifare_desfire_key_get_version (key); + cut_assert_equal_int (0x02, version, cut_message ("Wrong MifareDESFireKey version")); + mifare_desfire_key_free (key); + + uint8_t key3_3des_data[16] = { 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0X00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77 }; + + key = mifare_desfire_3des_key_new (key3_3des_data); + version = mifare_desfire_key_get_version (key); + cut_assert_equal_int (0x00, version, cut_message ("Wrong MifareDESFireKey version")); + mifare_desfire_key_free (key); + + key = mifare_desfire_3des_key_new_with_version (key3_3des_data); + version = mifare_desfire_key_get_version (key); + cut_assert_equal_int (0x10, version, cut_message ("Wrong MifareDESFireKey version")); + mifare_desfire_key_free (key); +}