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); +} +