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.
This commit is contained in:
Romain Tartiere 2010-02-23 02:12:18 +00:00
parent 24a9198f41
commit 5f7f8ffe2a
5 changed files with 151 additions and 57 deletions

View file

@ -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);

View file

@ -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

View file

@ -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.
*/