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