From 5f7f8ffe2a992088ce35b508122202429ad4910e Mon Sep 17 00:00:00 2001 From: Romain Tartiere Date: Tue, 23 Feb 2010 02:12:18 +0000 Subject: [PATCH] Add support for MIFARE Classic 4K. - New mifare_classic_first_sector_block(), mifare_classic_last_sector_block() functions to ease detection of sectors boundaries; - New unit tests for mifare_classic_first_sector_block() and mifare_classic_last_sector_block(); - Start to update the API for consistently using blocks and not mixing blocks and sectors with mifare_classic_*() functions; - Update the mifare-classic-format(1) example to handle MIFARE Classic 1k and 4k. Many thanks to Johann Dantant from SpringCard for giving me MIFARE Classic 4k cards. --- examples/mifare-classic-format.c | 101 ++++++++++++++++++++----------- libfreefare/freefare.h | 2 +- libfreefare/freefare_internal.h | 4 +- libfreefare/mifare_classic.c | 81 +++++++++++++++++++------ test/test_mifare_classic.c | 20 ++++++ 5 files changed, 151 insertions(+), 57 deletions(-) diff --git a/examples/mifare-classic-format.c b/examples/mifare-classic-format.c index 7d0cd58..daff854 100644 --- a/examples/mifare-classic-format.c +++ b/examples/mifare-classic-format.c @@ -26,8 +26,6 @@ #include -#define block_address(sector, block) ((sector * 4) + block) - MifareClassicKey default_keys[] = { { 0xff,0xff,0xff,0xff,0xff,0xff }, { 0xd3,0xf7,0xd3,0xf7,0xd3,0xf7 }, @@ -38,34 +36,61 @@ MifareClassicKey default_keys[] = { { 0xaa,0xbb,0xcc,0xdd,0xee,0xff }, { 0x00,0x00,0x00,0x00,0x00,0x00 } }; +int format_mifare_classic_1k (MifareTag tag); +int format_mifare_classic_4k (MifareTag tag); +int try_format_sector (MifareTag tag, MifareClassicBlockNumber block); int -try_format_sector (MifareTag tag, MifareSectorNumber sector) +format_mifare_classic_1k (MifareTag tag) +{ + for (int sector = 0; sector < 16; sector++) { + if (!try_format_sector (tag, sector * 4)) + return 0; + } + return 1; +} + +int +format_mifare_classic_4k (MifareTag tag) +{ + for (int sector = 0; sector < 32; sector++) { + if (!try_format_sector (tag, sector * 4)) + return 0; + } + for (int sector = 0; sector < 8; sector++) { + if (!try_format_sector (tag, 128 + sector * 16)) + return 0; + } + return 1; +} + +int +try_format_sector (MifareTag tag, MifareClassicBlockNumber block) { for (int i = 0; i < (sizeof (default_keys) / sizeof (MifareClassicKey)); i++) { - printf (" s=%d i=%d \n", sector, i); - if ((0 == mifare_classic_connect (tag)) && (0 == mifare_classic_authenticate (tag, block_address (sector, 0), default_keys[i], MFC_KEY_A))) { - if (0 == mifare_classic_format_sector (tag, sector)) { + if ((0 == mifare_classic_connect (tag)) && (0 == mifare_classic_authenticate (tag, block, default_keys[i], MFC_KEY_A))) { + if (0 == mifare_classic_format_sector (tag, block)) { mifare_classic_disconnect (tag); - return 0; + return 1; } else if (EIO == errno) { - err (EXIT_FAILURE, "sector %d", sector); + err (EXIT_FAILURE, "block %d", block); } mifare_classic_disconnect (tag); } - if ((0 == mifare_classic_connect (tag)) && (0 == mifare_classic_authenticate (tag, block_address (sector, 0), default_keys[i], MFC_KEY_B))) { - if (0 == mifare_classic_format_sector (tag, sector)) { + if ((0 == mifare_classic_connect (tag)) && (0 == mifare_classic_authenticate (tag, block, default_keys[i], MFC_KEY_B))) { + if (0 == mifare_classic_format_sector (tag, block)) { mifare_classic_disconnect (tag); - return 0; + return 1; } else if (EIO == errno) { - err (EXIT_FAILURE, "sector %d", sector); + err (EXIT_FAILURE, "block %d", block); } mifare_classic_disconnect (tag); } } - return -1; + warnx ("No known authentication key for block %d", block); + return 0; } int @@ -74,7 +99,6 @@ main(int argc, char *argv[]) int error = 0; nfc_device_t *device = NULL; MifareTag *tags = NULL; - MifareTag *tag = NULL; device = nfc_connect (NULL); if (!device) @@ -86,34 +110,39 @@ main(int argc, char *argv[]) errx (EXIT_FAILURE, "Error listing MIFARE classic tag."); } - if (!tags[0]) { - freefare_free_tags (tags); - nfc_disconnect (device); - errx (EXIT_FAILURE, "No MIFARE tag on NFC device."); - } + for (int i = 0; (!error) && tags[i]; i++) { + switch (freefare_get_tag_type (tags[i])) { + case CLASSIC_1K: + case CLASSIC_4K: + break; + default: + continue; + } - tag = tags; + char *tag_uid = mifare_classic_get_uid (tags[i]); + char buffer[BUFSIZ]; - if ((freefare_get_tag_type (*tag) != CLASSIC_1K) && - (freefare_get_tag_type (*tag) != CLASSIC_4K)) { - errx (EXIT_FAILURE, "Not a MIFARE Classic tag."); - } + printf ("Found MIFARE Classic %s. Format [yN] ", tag_uid); + fgets (buffer, BUFSIZ, stdin); + bool format = ((buffer[0] == 'y') || (buffer[0] == 'Y')); - while (*tag) { - char *tag_uid = mifare_classic_get_uid (*tag); - - /* FIXME get the tag size */ - size_t sector_count = 15; - - for (size_t n = 0; n < sector_count; n++) { - if (try_format_sector (*tag, n) < 0) { - warnx ("%s: Can't format sector %ld (0x%02lx)", tag_uid, n, n); - error = 1; + if (format) { + switch (freefare_get_tag_type (tags[i])) { + case CLASSIC_1K: + if (!format_mifare_classic_1k (tags[i])) + error = 1; + break; + case CLASSIC_4K: + if (!format_mifare_classic_4k (tags[i])) + error = 1; + break; + default: + /* Keep compiler quiet */ + break; } } - free(tag_uid); - tag++; + free (tag_uid); } freefare_free_tags (tags); diff --git a/libfreefare/freefare.h b/libfreefare/freefare.h index cfce82f..105cc7c 100644 --- a/libfreefare/freefare.h +++ b/libfreefare/freefare.h @@ -88,7 +88,7 @@ int mifare_classic_transfer (MifareTag tag, const MifareClassicBlockNumber blo int mifare_classic_get_trailer_block_permission (MifareTag tag, const MifareClassicBlockNumber block, const uint16_t permission, const MifareClassicKeyType key_type); int mifare_classic_get_data_block_permission (MifareTag tag, const MifareClassicBlockNumber block, const unsigned char permission, const MifareClassicKeyType key_type); -int mifare_classic_format_sector (MifareTag tag, const MifareSectorNumber sector); +int mifare_classic_format_sector (MifareTag tag, const MifareClassicBlockNumber block); char* mifare_classic_get_uid(MifareTag tag); void mifare_classic_trailer_block (MifareClassicBlock *block, const MifareClassicKey key_a, const uint8_t ab_0, const uint8_t ab_1, const uint8_t ab_2, const uint8_t ab_tb, const uint8_t gpb, const MifareClassicKey key_b); diff --git a/libfreefare/freefare_internal.h b/libfreefare/freefare_internal.h index 8e99567..a247c72 100644 --- a/libfreefare/freefare_internal.h +++ b/libfreefare/freefare_internal.h @@ -30,6 +30,8 @@ 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); +MifareClassicBlockNumber mifare_classic_first_sector_block (MifareClassicBlockNumber block); +MifareClassicBlockNumber mifare_classic_last_sector_block (MifareClassicBlockNumber block); #define MIFARE_ULTRALIGHT_PAGE_COUNT 16 @@ -83,7 +85,7 @@ struct mifare_ultralight_tag { #define ASSERT_INACTIVE(tag) do { if (tag->active) return errno = ENXIO, -1; } while (0) #define ASSERT_MIFARE_ULTRALIGHT(tag) do { if (tag->type != ULTRALIGHT) return errno = ENODEV, -1; } while (0) -#define ASSERT_MIFARE_CLASSIC(tag) do { if (tag->type != CLASSIC_1K) return errno = ENODEV, -1; } while (0) +#define ASSERT_MIFARE_CLASSIC(tag) do { if ((tag->type != CLASSIC_1K) && (tag->type != CLASSIC_4K)) return errno = ENODEV, -1; } while (0) /* * MifareTag cast macros diff --git a/libfreefare/mifare_classic.c b/libfreefare/mifare_classic.c index ac47dca..fd52d75 100644 --- a/libfreefare/mifare_classic.c +++ b/libfreefare/mifare_classic.c @@ -472,7 +472,7 @@ get_block_access_bits (MifareTag tag, const MifareClassicBlockNumber block, Mifa uint16_t sector_access_bits, sector_access_bits_; - MifareClassicBlockNumber trailer = ((block) / 4) * 4 + 3; + MifareClassicBlockNumber trailer = mifare_classic_last_sector_block (block); if (MIFARE_CLASSIC(tag)->cached_access_bits.sector_trailer_block_number == trailer) { /* cache hit! */ @@ -504,9 +504,9 @@ get_block_access_bits (MifareTag tag, const MifareClassicBlockNumber block, Mifa *block_access_bits = 0; /* ,-------C3 * |,------C2 - * ||,---- C1 + * ||,---- C1 * ||| */ - uint16_t block_access_bits_mask = 0x0111 << (block % 4); + uint16_t block_access_bits_mask = 0x0111 << ((block == trailer) ? 3 : ((block < 128) ? block : ((block - 128) % 16) / 5) % 4); /* ||| * ||`---------------. * |`---------------.| @@ -570,20 +570,29 @@ mifare_classic_get_data_block_permission (MifareTag tag, const MifareClassicBloc * Reset a MIFARE target sector to factory default. */ int -mifare_classic_format_sector (MifareTag tag, const MifareSectorNumber sector) +mifare_classic_format_sector (MifareTag tag, const MifareClassicBlockNumber block) { - MifareClassicBlockNumber first_sector_block = sector * 4; + MifareClassicBlockNumber first_sector_block = mifare_classic_first_sector_block (block); + MifareClassicBlockNumber last_sector_block = mifare_classic_last_sector_block (block); + /* * Check that the current key allow us to rewrite data and trailer blocks. */ - if (((sector != 0) && (mifare_classic_get_data_block_permission(tag, first_sector_block, MCAB_W, MIFARE_CLASSIC(tag)->last_authentication_key_type) != 1)) || - (mifare_classic_get_data_block_permission(tag, first_sector_block + 1, MCAB_W, MIFARE_CLASSIC(tag)->last_authentication_key_type) != 1) || - (mifare_classic_get_data_block_permission(tag, first_sector_block + 2, MCAB_W, MIFARE_CLASSIC(tag)->last_authentication_key_type) != 1) || - (mifare_classic_get_trailer_block_permission(tag, first_sector_block + 3, MCAB_WRITE_KEYA, MIFARE_CLASSIC(tag)->last_authentication_key_type) != 1) || - (mifare_classic_get_trailer_block_permission(tag, first_sector_block + 3, MCAB_WRITE_ACCESS_BITS, MIFARE_CLASSIC(tag)->last_authentication_key_type) != 1) || - (mifare_classic_get_trailer_block_permission(tag, first_sector_block + 3, MCAB_WRITE_KEYB, MIFARE_CLASSIC(tag)->last_authentication_key_type) != 1)) { - errno = EPERM; - return -1; + + if (first_sector_block == 0) { + /* First block is read-only */ + first_sector_block = 1; + } + + for (int n = first_sector_block; n < last_sector_block; n++) { + if (mifare_classic_get_data_block_permission(tag, n, MCAB_W, MIFARE_CLASSIC(tag)->last_authentication_key_type) != 1) { + return errno = EPERM, -1; + } + } + if ((mifare_classic_get_trailer_block_permission(tag, last_sector_block, MCAB_WRITE_KEYA, MIFARE_CLASSIC(tag)->last_authentication_key_type) != 1) || + (mifare_classic_get_trailer_block_permission(tag, last_sector_block, MCAB_WRITE_ACCESS_BITS, MIFARE_CLASSIC(tag)->last_authentication_key_type) != 1) || + (mifare_classic_get_trailer_block_permission(tag, last_sector_block, MCAB_WRITE_KEYB, MIFARE_CLASSIC(tag)->last_authentication_key_type) != 1)) { + return errno = EPERM, -1; } MifareClassicBlock empty_data_block; @@ -596,12 +605,13 @@ mifare_classic_format_sector (MifareTag tag, const MifareSectorNumber sector) 0xff, 0xff, 0xff, 0xff, 0xff, 0xff /* Key B */ }; - if (((sector != 0) && (mifare_classic_write (tag, first_sector_block, empty_data_block) < 0)) || - (mifare_classic_write (tag, first_sector_block + 1, empty_data_block) < 0) || - (mifare_classic_write (tag, first_sector_block + 2, empty_data_block) < 0) || - (mifare_classic_write (tag, first_sector_block + 3, default_trailer_block) < 0)) { - errno = EIO; - return -1; + for (int n = first_sector_block; n < last_sector_block; n++) { + if (mifare_classic_write (tag, n, empty_data_block) < 0) { + return errno = EIO, -1; + } + } + if (mifare_classic_write (tag, last_sector_block, default_trailer_block) < 0) { + return errno = EIO, -1; } return 0; @@ -619,6 +629,39 @@ mifare_classic_get_uid(MifareTag tag) return uid; } +/* + * Get the sector's first block number in the provided block's sector. + */ +MifareClassicBlockNumber +mifare_classic_first_sector_block (MifareClassicBlockNumber block) +{ + int res; + if (block < 128) { + res = (block / 4) * 4; + } else { + res = ((block - 128) / 16) * 16 + 128; + } + + return res; +} + +/* + * Get the sector's last block number (aka trailer block) in the provided + * block's sector. + */ +MifareClassicBlockNumber +mifare_classic_last_sector_block (MifareClassicBlockNumber block) +{ + int res; + if (block < 128) { + res = (block / 4) * 4 + 3; + } else { + res = ((block - 128) / 16) * 16 + 15 + 128; + } + + return res; +} + /* * Generates a MIFARE trailer block. */ diff --git a/test/test_mifare_classic.c b/test/test_mifare_classic.c index 534ac25..aa17719 100644 --- a/test/test_mifare_classic.c +++ b/test/test_mifare_classic.c @@ -21,6 +21,7 @@ #include #include +#include "freefare_internal.h" #include "mifare_classic_fixture.h" @@ -337,3 +338,22 @@ test_mifare_classic_get_uid (void) free (uid); } + +void +test_mifare_classic_sector_boundaries (void) +{ + cut_notify ("No MIFARE Classic target is required for this test"); + for (int i=0; i < 32; i++) { + for (int j=0; j < 4; j++) { + cut_assert_equal_int (4 * i, mifare_classic_first_sector_block (4 * i), cut_message ("Wrong first block number for block %d", i)); + cut_assert_equal_int (4 * i + 3, mifare_classic_last_sector_block (4 * i + j), cut_message ("Wrong last block number for block %d", i)); + } + } + + for (int i=0; i < 8; i++) { + for (int j=0; j < 16; j++) { + cut_assert_equal_int (128 + 16 * i, mifare_classic_first_sector_block (128 + 16 * i), cut_message ("Wrong last block number for block %d", i)); + cut_assert_equal_int (128 + 16 * i + 15, mifare_classic_last_sector_block (128 + 16 * i + j), cut_message ("Wrong last block number for block %d", i)); + } + } +}