From 8a9d90210a0f4dc23bce35fdbf9a1ad1b050da7a Mon Sep 17 00:00:00 2001 From: Romain Tartiere Date: Fri, 8 Jan 2010 11:24:30 +0000 Subject: [PATCH] Add MAD CRC support. --- Makefile.am | 2 + freefare_internal.h | 30 ++++++ mad.c | 98 ++++++++++++++--- test/test_mad.c | 254 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 372 insertions(+), 12 deletions(-) create mode 100644 freefare_internal.h diff --git a/Makefile.am b/Makefile.am index ec3de78..b08082b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -10,5 +10,7 @@ libfreefare_la_SOURCES = mifare_classic.c mad.c mifare_application.c libfreefare_la_HEADERS = freefare.h libfreefare_ladir = $(includedir) +EXTRA_DIST = freefare_internal.h + pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libfreefare.pc diff --git a/freefare_internal.h b/freefare_internal.h new file mode 100644 index 0000000..3fa401c --- /dev/null +++ b/freefare_internal.h @@ -0,0 +1,30 @@ +/* + * 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$ + */ + +#ifndef __FREEFARE_INTERNAL_H__ +#define __FREEFARE_INTERNAL_H__ + +struct mad_sector_0x00; +struct mad_sector_0x10; + +void crc8 (uint8_t *crc, const uint8_t value); +uint8_t sector_0x00_crc8 (Mad mad); +uint8_t sector_0x10_crc8 (Mad mad); + +#endif /* !__FREEFARE_INTERNAL_H__ */ diff --git a/mad.c b/mad.c index 0233026..ad17aaa 100644 --- a/mad.c +++ b/mad.c @@ -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 @@ -27,22 +27,40 @@ */ #include "config.h" +#include + #include #include #include #include +#include "mad.h" + +/* + * XXX The documentation says the preset is 0xE3, but the various card dumps + * and the documentation example MAD CRC can be verified only with a CRC + * preset of 0x67. + * + * This is still under investigation: + * http://www.libnfc.org/community/post/667/ + * http://discussion.forum.nokia.com/forum/showthread.php?t=181702#14 + */ +#define CRC_PRESET 0x67 + +#define SECTOR_0X00_AIDS 15 +#define SECTOR_0X10_AIDS 23 + struct mad_sector_0x00 { uint8_t crc; uint8_t info; - MadAid aids[15]; + MadAid aids[SECTOR_0X00_AIDS]; }; struct mad_sector_0x10 { uint8_t crc; uint8_t info; - MadAid aids[23]; + MadAid aids[SECTOR_0X10_AIDS]; }; struct mad { @@ -74,6 +92,57 @@ mad_new (uint8_t version) return mad; } +/* + * Compute CRC. + */ +void +crc8 (uint8_t *crc, const uint8_t value) +{ + /* x^8 + x^4 + x^3 + x^2 + 1 => 0x11d */ + const uint8_t poly = 0x1d; + + for (int current_bit = 7; current_bit >= 0; current_bit--) { + int bit_out = (*crc) & 0x80; + *crc = ((*crc) << 1) | (( value >> (current_bit)) & 0x01); + + if (bit_out) + *crc ^= poly; + + } +} + +uint8_t +sector_0x00_crc8 (Mad mad) +{ + uint8_t crc = CRC_PRESET; + + crc8 (&crc, mad->sector_0x00.info); + + for (int n = 0; n < SECTOR_0X00_AIDS; n++) { + crc8 (&crc, mad->sector_0x00.aids[n].application_code); + crc8 (&crc, mad->sector_0x00.aids[n].function_cluster_code); + } + crc8 (&crc, 0x00); + + return crc; +} + +uint8_t +sector_0x10_crc8 (Mad mad) +{ + uint8_t crc = CRC_PRESET; + + crc8 (&crc, mad->sector_0x10.info); + + for (int n = 0; n < SECTOR_0X10_AIDS; n++) { + crc8 (&crc, mad->sector_0x10.aids[n].application_code); + crc8 (&crc, mad->sector_0x10.aids[n].function_cluster_code); + } + crc8 (&crc, 0x00); + + return crc; +} + /* * Read a MAD from the provided MIFARE tag. */ @@ -125,6 +194,11 @@ mad_read (MifareClassicTag tag) goto error; memcpy (&(mad->sector_0x00) + sizeof (data), data, sizeof (data)); + uint8_t crc = mad->sector_0x00.crc; + uint8_t computed_crc = sector_0x00_crc8 (mad); + if (crc != computed_crc) + goto error; + /* Read MAD data at 0x10 (MAD2) */ if (mad->version == 2) { @@ -144,12 +218,12 @@ mad_read (MifareClassicTag tag) 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''. - */ + crc = mad->sector_0x10.crc; + computed_crc = sector_0x10_crc8 (mad); + if (crc != computed_crc) + goto error; + } return mad; @@ -164,10 +238,6 @@ error: 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) @@ -211,6 +281,8 @@ mad_write (MifareClassicTag tag, Mad mad, MifareClassicKey key_b_sector_00, Mifa return -1; } + mad->sector_0x10.crc = sector_0x10_crc8 (mad); + 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)); @@ -223,6 +295,8 @@ mad_write (MifareClassicTag tag, Mad mad, MifareClassicKey key_b_sector_00, Mifa } + mad->sector_0x00.crc = sector_0x00_crc8 (mad); + 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; diff --git a/test/test_mad.c b/test/test_mad.c index 347fd53..e971fa8 100644 --- a/test/test_mad.c +++ b/test/test_mad.c @@ -1,5 +1,7 @@ #include "test.h" +#include "../freefare_internal.h" + DEFINE_TEST(test_mad) { int res; @@ -49,3 +51,255 @@ DEFINE_TEST(test_mad) } while (0); } +#define CRC_PRESET 0x67 + +DEFINE_TEST(test_mad_crc8_basic) +{ + do { + uint8_t crc; + const uint8_t crc_value = 0x42; + + /* Insert data */ + crc = 0x00; + crc8(&crc, crc_value); + assertEqualInt (crc, crc_value); + + /* Insert data with leading zeros */ + crc = 0x00; + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, crc_value); + assertEqualInt (crc, crc_value); + + /* Check integrity */ + crc = CRC_PRESET; + crc8(&crc, crc_value); + crc8(&crc, 0x00); + uint8_t save = crc; + + crc = CRC_PRESET; + crc8(&crc, crc_value); + crc8(&crc, save); + assertEqualInt (crc, 0x00); + + } while (0); +} + +/* + * The following MAD values where extracted from documentation. + */ +DEFINE_TEST(test_mad_crc8_doc_example) +{ + do { + /* Preset */ + uint8_t crc = CRC_PRESET; + + /* Block 1 -- 0x01 - 0x07 */ + crc8(&crc, 0x01); + crc8(&crc, 0x01); + crc8(&crc, 0x08); + crc8(&crc, 0x01); + crc8(&crc, 0x08); + crc8(&crc, 0x01); + crc8(&crc, 0x08); + + /* Block 2 -- 0x08 - 0x0f */ + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x04); + crc8(&crc, 0x00); + + /* Block 3 -- 0x00 - 0x07 */ + crc8(&crc, 0x03); + crc8(&crc, 0x10); + crc8(&crc, 0x03); + crc8(&crc, 0x10); + crc8(&crc, 0x02); + crc8(&crc, 0x10); + crc8(&crc, 0x02); + crc8(&crc, 0x10); + + /* Block 3 -- 0x08 - 0x0f */ + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x11); + crc8(&crc, 0x30); + + /* Append zeros of augmented message */ + crc8(&crc, 0x00); + + assertEqualInt (crc, 0x89); + + } while (0); +} + +/* + * The following MAD values where extracted from a MIFARE dump. + */ +DEFINE_TEST(test_mad_crc8_real_example_1) +{ + do { + /* Preset */ + uint8_t crc = CRC_PRESET; + + /* Block 1 -- 0x01 - 0x07 */ + crc8(&crc, 0x01); + crc8(&crc, 0x03); + crc8(&crc, 0xe1); + crc8(&crc, 0x03); + crc8(&crc, 0xe1); + crc8(&crc, 0x03); + crc8(&crc, 0xe1); + + /* Block 2 -- 0x08 - 0x0f */ + crc8(&crc, 0x03); + crc8(&crc, 0xe1); + crc8(&crc, 0x03); + crc8(&crc, 0xe1); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + + /* Block 3 -- 0x00 - 0x07 */ + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + + /* Block 3 -- 0x08 - 0x0f */ + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + + /* Append zeros of augmented message */ + crc8(&crc, 0x00); + + assertEqualInt (crc, 0xc4); + + } while (0); +} + +/* + * The following MAD values where extracted from a MIFARE dump. + */ +DEFINE_TEST(test_mad_crc8_real_example_2) +{ + do { + /* Preset */ + uint8_t crc = CRC_PRESET; + + /* Block 1 -- 0x01 - 0x07 */ + crc8(&crc, 0x01); + crc8(&crc, 0x03); + crc8(&crc, 0xe1); + crc8(&crc, 0x03); + crc8(&crc, 0xe1); + crc8(&crc, 0x03); + crc8(&crc, 0xe1); + + /* Block 2 -- 0x08 - 0x0f */ + crc8(&crc, 0x03); + crc8(&crc, 0xe1); + crc8(&crc, 0x03); + crc8(&crc, 0xe1); + crc8(&crc, 0x03); + crc8(&crc, 0xe1); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + + /* Block 3 -- 0x00 - 0x07 */ + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + + /* Block 3 -- 0x08 - 0x0f */ + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + crc8(&crc, 0x00); + + /* Append zeros of augmented message */ + crc8(&crc, 0x00); + + assertEqualInt (crc, 0xab); + + } while (0); +} + +DEFINE_TEST (test_mad_sector_0x00_crc8) +{ + int res; + + do { + Mad mad = mad_new (1); + assert (mad != NULL); + + if (mad) { + res = mad_set_card_publisher_sector (mad, 0x01); + + /* Block 1 */ + MadAid aid1 = { 0x08, 0x01 }; + mad_set_aid (mad, 1, aid1); + mad_set_aid (mad, 2, aid1); + mad_set_aid (mad, 3, aid1); + + /* Block 2 */ + MadAid empty_aid = { 0x00, 0x00 }; + mad_set_aid (mad, 4, empty_aid); + mad_set_aid (mad, 5, empty_aid); + mad_set_aid (mad, 6, empty_aid); + MadAid aid2 = { 0x00, 0x04 }; + mad_set_aid (mad, 7, aid2); + + /* Block 3 */ + MadAid aid3 = { 0x10, 0x03 }; + mad_set_aid (mad, 8, aid3); + mad_set_aid (mad, 9, aid3); + MadAid aid4 = { 0x10, 0x02 }; + mad_set_aid (mad, 10, aid4); + mad_set_aid (mad, 11, aid4); + + mad_set_aid (mad, 12, empty_aid); + mad_set_aid (mad, 13, empty_aid); + mad_set_aid (mad, 14, empty_aid); + MadAid aid5 = { 0x30, 0x11 }; + mad_set_aid (mad, 15, aid5); + + res = sector_0x00_crc8 (mad); + assertEqualInt(0x89, res); + } + + mad_free (mad); + + } while (0); +}