diff --git a/Makefile.am b/Makefile.am
index c201c08..37e22a1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -6,8 +6,8 @@ AM_LDFLAGS = @LIBNFC_LIBS@
lib_LTLIBRARIES = libfreefare.la
-libfreefare_la_SOURCES = mifare_classic.c
-libfreefare_la_HEADERS = mifare_classic.h
+libfreefare_la_SOURCES = mifare_classic.c mifare_application_directory.c
+libfreefare_la_HEADERS = mifare_classic.h mifare_application_directory.h
libfreefare_ladir = $(includedir)
pkgconfigdir = $(libdir)/pkgconfig
diff --git a/mifare_application_directory.c b/mifare_application_directory.c
new file mode 100644
index 0000000..6947c23
--- /dev/null
+++ b/mifare_application_directory.c
@@ -0,0 +1,348 @@
+/*-
+ * Copyright (C) 2009, 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$
+ */
+
+/*
+ * This implementation was written based on information provided by the
+ * following document:
+ *
+ * AN10787
+ * MIFARE Application Directory (MAD)
+ * Rev. 04 - 5 March 2009
+ */
+
+#include
+#include
+#include
+
+#include "mifare_application_directory.h"
+
+struct aid {
+ uint8_t function_cluster_code;
+ uint8_t application_code;
+};
+
+struct mad_sector_0x00 {
+ uint8_t crc;
+ uint8_t info;
+ struct aid aids[15];
+};
+
+struct mad_sector_0x10 {
+ uint8_t crc;
+ uint8_t info;
+ struct aid aids[23];
+};
+
+struct mad {
+ struct mad_sector_0x00 sector_0x00;
+ struct mad_sector_0x10 sector_0x10;
+ uint8_t version;
+};
+
+/* Read key A */
+const MifareClassicKey mad_key_a = {
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5
+};
+
+/*
+ * Allocate an empty new MAD.
+ */
+Mad
+mad_new (uint8_t version)
+{
+ Mad mad = malloc (sizeof (*mad));
+
+ if (!mad)
+ return NULL;
+
+ mad->version = version;
+ memset (&(mad->sector_0x00), '\0', sizeof (mad->sector_0x00));
+ memset (&(mad->sector_0x10), '\0', sizeof (mad->sector_0x10));
+
+ return mad;
+}
+
+/*
+ * Read a MAD from the provided MIFARE tag.
+ */
+Mad
+mad_read (MifareClassicTag tag)
+{
+ Mad mad = malloc (sizeof (*mad));
+
+ if (!mad)
+ goto error;
+
+ /* Authenticate using MAD key A */
+ if (mifare_classic_authenticate (tag, 0x03, mad_key_a, MFC_KEY_A) < 0) {
+ goto error;
+ }
+
+ /* Read first sector trailer block */
+ MifareClassicBlock data;
+ if (mifare_classic_read (tag, 0x03, &data) < 0) {
+ goto error;
+ }
+ uint8_t gpb = data[9];
+
+ /* Check MAD availability (DA bit) */
+ if (!(gpb & 0x80)) {
+ goto error;
+ }
+
+ /* Get MAD version (ADV bits) */
+ switch (gpb & 0x03) {
+ case 0x01:
+ mad->version = 1;
+ break;
+ case 0x02:
+ mad->version = 2;
+ break;
+ default:
+ /* MAD enabled but version not supported */
+ errno = ENOTSUP;
+ goto error;
+ }
+
+ /* Read MAD data at 0x00 (MAD1, MAD2) */
+ if (mifare_classic_read (tag, 0x01, &data) < 0)
+ goto error;
+ memcpy (&(mad->sector_0x00), data, sizeof (data));
+
+ if (mifare_classic_read (tag, 0x02, &data) < 0)
+ goto error;
+ memcpy (&(mad->sector_0x00) + sizeof (data), data, sizeof (data));
+
+ /* Read MAD data at 0x10 (MAD2) */
+ if (mad->version == 2) {
+
+ /* Authenticate using MAD key A */
+ if (mifare_classic_authenticate (tag, 0x43, mad_key_a, MFC_KEY_A) < 0) {
+ goto error;
+ }
+
+ if (mifare_classic_read (tag, 0x40, &data) < 0)
+ goto error;
+ memcpy (&(mad->sector_0x10), data, sizeof (data));
+
+ if (mifare_classic_read (tag, 0x41, &data) < 0)
+ goto error;
+ memcpy (&(mad->sector_0x10) + sizeof (data), data, sizeof (data));
+
+ if (mifare_classic_read (tag, 0x42, &data) < 0)
+ goto error;
+ memcpy (&(mad->sector_0x10) + sizeof (data) * 2, data, sizeof (data));
+ }
+
+ /*
+ * FIXME 3.7 CRC calculation states ``This code (CRC) should be checked
+ * whenever the MAD is read in order to ensure data integrity''.
+ */
+
+ return mad;
+
+error:
+ free (mad);
+ return NULL;
+}
+
+/*
+ * Write the mad to the provided MIFARE tad using the provided Key-B keys.
+ */
+int
+mad_write (MifareClassicTag tag, Mad mad, MifareClassicKey key_b_sector_00, MifareClassicKey key_b_sector_10)
+{
+ /*
+ * FIXME Since the CRC SHOULD be checked, it SHOULD be written, right?
+ */
+
+ MifareClassicBlock data;
+
+ if (mifare_classic_authenticate (tag, 0x00, key_b_sector_00, MFC_KEY_B) < 0)
+ return -1;
+
+ if ((1 != mifare_classic_get_data_block_permission (tag, 0x01, MCAB_W, MFC_KEY_B)) ||
+ (1 != mifare_classic_get_data_block_permission (tag, 0x02, MCAB_W, MFC_KEY_B)) ||
+ (1 != mifare_classic_get_trailer_block_permission (tag, 0x03, MCAB_WRITE_KEYA, MFC_KEY_B)) ||
+ (1 != mifare_classic_get_trailer_block_permission (tag, 0x03, MCAB_WRITE_ACCESS_BITS, MFC_KEY_B))) {
+ errno = EPERM;
+ return -1;
+ }
+
+ uint8_t gpb = 0x80;
+
+ /*
+ * FIXME Handle mono-application cards
+ */
+ gpb |= 0x40;
+
+ /* Write MAD version */
+ switch (mad->version) {
+ case 1:
+ gpb |= 0x01;
+ break;
+ case 2:
+ gpb |= 0x02;
+ break;
+ }
+
+ if (2 == mad->version) {
+ if (mifare_classic_authenticate (tag, 0x40, key_b_sector_10, MFC_KEY_B) < 0)
+ return -1;
+
+ if ((1 != mifare_classic_get_data_block_permission (tag, 0x40, MCAB_W, MFC_KEY_B)) ||
+ (1 != mifare_classic_get_data_block_permission (tag, 0x41, MCAB_W, MFC_KEY_B)) ||
+ (1 != mifare_classic_get_data_block_permission (tag, 0x42, MCAB_W, MFC_KEY_B)) ||
+ (1 != mifare_classic_get_trailer_block_permission (tag, 0x43, MCAB_WRITE_KEYA, MFC_KEY_B)) ||
+ (1 != mifare_classic_get_trailer_block_permission (tag, 0x43, MCAB_WRITE_ACCESS_BITS, MFC_KEY_B))) {
+ errno = EPERM;
+ return -1;
+ }
+
+ memcpy (data, &(mad->sector_0x10), sizeof (data));
+ if (mifare_classic_write (tag, 0x40, data) < 0) return -1;
+ memcpy (data, &(mad->sector_0x10) + sizeof (data), sizeof (data));
+ if (mifare_classic_write (tag, 0x41, data) < 0) return -1;
+ memcpy (data, &(mad->sector_0x10) + sizeof (data) * 2, sizeof (data));
+ if (mifare_classic_write (tag, 0x42, data) < 0) return -1;
+
+ mifare_classic_trailer_block (&data, mad_key_a, 0x0, 0x1, 0x1, 0x6, 0x00, key_b_sector_10);
+ if (mifare_classic_write (tag, 0x42, data) < 0) return -1;
+
+ }
+
+ if (mifare_classic_authenticate (tag, 0x00, key_b_sector_00, MFC_KEY_B) < 0) return -1;
+ memcpy (data, &(mad->sector_0x00), sizeof (data));
+ if (mifare_classic_write (tag, 0x01, data) < 0) return -1;
+ memcpy (data, &(mad->sector_0x00) + sizeof (data), sizeof (data));
+ if (mifare_classic_write (tag, 0x02, data) < 0) return -1;
+
+ mifare_classic_trailer_block (&data, mad_key_a, 0x0, 0x1, 0x1, 0x6, gpb, key_b_sector_00);
+ if (mifare_classic_write (tag, 0x03, data) < 0) return -1;
+
+ return 0;
+}
+
+/*
+ * Return a MAD version.
+ */
+int
+mad_get_version (Mad mad)
+{
+ return mad->version;
+}
+
+/*
+ * Set a MAD version.
+ */
+void
+mad_set_version (Mad mad, 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));
+ }
+ mad->version = version;
+}
+
+/*
+ * Return the MAD card publisher sector.
+ */
+MifareSector
+mad_get_card_publisher_sector(Mad mad)
+{
+ return (mad->sector_0x00.info & 0x3f);
+}
+
+/*
+ * Set the MAD card publisher sector.
+ */
+int
+mad_set_card_publisher_sector(Mad mad, MifareSector cps)
+{
+ if (((mad->version == 2) && (cps > 0x27)) | (mad->version == 1) && (cps > 0x0f)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ mad->sector_0x00.info = (cps & 0x3f);
+ return 0;
+}
+
+/*
+ * Get the provided sector's application identifier.
+ */
+int
+mad_get_aid(Mad mad, MifareSector sector, uint8_t *function_cluster_code, uint8_t *application_code)
+{
+ if (sector > 0x27) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (sector > 0x0f) {
+ if (mad->version != 2) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ *function_cluster_code = mad->sector_0x10.aids[sector - 0x0f - 1].function_cluster_code;
+ *application_code = mad->sector_0x10.aids[sector - 0x0f - 1].application_code;
+ } else {
+ *function_cluster_code = mad->sector_0x00.aids[sector - 1].function_cluster_code;
+ *application_code = mad->sector_0x00.aids[sector - 1].application_code;
+ }
+
+ return 0;
+}
+
+/*
+ * Set the provided sector's application identifier.
+ */
+int
+mad_set_aid(Mad mad, MifareSector sector, uint8_t function_cluster_code, uint8_t application_code)
+{
+ if (sector > 0x27) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (sector > 0x0f) {
+ if (mad->version != 2) {
+ errno = EINVAL;
+ return -1;
+ }
+ mad->sector_0x00.aids[sector - 0x0f - 1].function_cluster_code = function_cluster_code;
+ mad->sector_0x00.aids[sector - 0x0f - 1].application_code = application_code;
+ } else {
+ mad->sector_0x00.aids[sector - 1].function_cluster_code = function_cluster_code;
+ mad->sector_0x00.aids[sector - 1].application_code = application_code;
+ }
+
+ return 0;
+}
+
+/*
+ * Free memory allocated by mad_new() and mad_read().
+ */
+void
+mad_free (Mad mad)
+{
+ free (mad);
+}
diff --git a/mifare_application_directory.h b/mifare_application_directory.h
new file mode 100644
index 0000000..3b68f30
--- /dev/null
+++ b/mifare_application_directory.h
@@ -0,0 +1,40 @@
+/*-
+ * Copyright (C) 2009, 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$
+ */
+#ifndef __MIFARE_APPLICATION_DIRECTORY_H__
+#define __MIFARE_APPLICATION_DIRECTORY_H__
+
+#include "mifare_classic.h"
+
+typedef uint8_t MifareSector;
+
+struct mad;
+typedef struct mad *Mad;
+
+Mad mad_new (uint8_t version);
+Mad mad_read (MifareClassicTag tag);
+int mad_write (MifareClassicTag tag, Mad mad, MifareClassicKey key_b_sector_00, MifareClassicKey key_b_sector_10);
+int mad_get_version (Mad mad);
+void mad_set_version (Mad mad, uint8_t version);
+MifareSector mad_get_card_publisher_sector(Mad mad);
+int mad_set_card_publisher_sector(Mad mad, MifareSector cps);
+int mad_get_aid(Mad mad, MifareSector sector, uint8_t *function_cluster_code, uint8_t *application_code);
+int mad_set_aid(Mad mad, MifareSector sector, uint8_t function_cluster_code, uint8_t application_code);
+void mad_free (Mad mad);
+
+#endif /* !__MIFARE_APPLICATION_DIRECTORY_H__ */
diff --git a/test/Makefile b/test/Makefile
index 72239db..21cf625 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -2,14 +2,15 @@ LMF_SRCDIR= ${.CURDIR}/..
.PATH: ${LMF_SRCDIR}
#LMF_SRCS!= ${MAKE} -f ${LMF_SRCDIR}/Makefile -V SRCS
-LMF_SRCS= ../mifare_classic.c
+LMF_SRCS= ../mifare_classic.c ../mifare_application_directory.c
TESTS= test_read_sector_0.c \
test_authenticate.c \
test_value_block.c \
test_access_bits.c \
test_format.c \
- test_create_trailer_block.c
+ test_create_trailer_block.c \
+ test_mad.c
SRCS= ${LMF_SRCS} \
${TESTS} \
diff --git a/test/test.h b/test/test.h
index 65159bc..c6aeb59 100644
--- a/test/test.h
+++ b/test/test.h
@@ -205,6 +205,7 @@ int read_open_memory2(struct archive *, void *, size_t, size_t);
#include
#include
+#include
int mifare_classic_test_setup (MifareClassicTag *tag);
int mifare_classic_test_teardown (MifareClassicTag tag);
diff --git a/test/test_mad.c b/test/test_mad.c
new file mode 100644
index 0000000..b95d1fd
--- /dev/null
+++ b/test/test_mad.c
@@ -0,0 +1,45 @@
+#include "test.h"
+
+DEFINE_TEST(mad)
+{
+ int res;
+
+ do {
+ Mad mad = mad_new (1);
+ assert (mad != NULL);
+
+ if (mad) {
+ assertEqualInt (mad_get_version (mad), 1);
+ mad_set_version (mad, 2);
+ assertEqualInt (mad_get_version (mad), 2);
+
+ assertEqualInt (0, mad_get_card_publisher_sector (mad));
+
+ res = mad_set_card_publisher_sector (mad, 13);
+ assertEqualInt (res, 0);
+ assertEqualInt (13, mad_get_card_publisher_sector (mad));
+
+ res = mad_set_card_publisher_sector (mad, 0xff);
+ assertEqualInt (res, -1);
+ assertEqualInt (13, mad_get_card_publisher_sector (mad));
+
+ uint8_t fcc, ac;
+ res = mad_get_aid (mad, 3, &fcc, &ac);
+ assertEqualInt (res, 0);
+ assertEqualInt (fcc, 0);
+ assertEqualInt (ac, 0);
+
+ res = mad_set_aid (mad, 3, 0xc0, 0x42);
+ assertEqualInt (res, 0);
+
+ res = mad_get_aid (mad, 3, &fcc, &ac);
+ assertEqualInt (res, 0);
+ assertEqualInt (fcc, 0xc0);
+ assertEqualInt (ac, 0x42);
+
+ mad_free (mad);
+ }
+
+ } while (0);
+}
+