Merge de libfreefare-ultralight branch into trunk (Mifare UltraLightC support).

This commit is contained in:
Romain Tartiere 2010-12-28 11:30:31 +00:00
parent e4057ef163
commit 82a7b8df66
8 changed files with 210 additions and 32 deletions

View file

@ -151,6 +151,7 @@ linkedman = \
mifare_ultralight.3 mifare_ultralight_get_uid.3 \
mifare_ultralight.3 mifare_ultralight_read.3 \
mifare_ultralight.3 mifare_ultralight_write.3 \
mifare_ultralight.3 mifare_ultralightc_authenticate.3 \
tlv.3 tlv_decode.3 \
tlv.3 tlv_encode.3

View file

@ -29,11 +29,12 @@
#define NXP_MANUFACTURER_CODE 0x04
struct supported_tag supported_tags[] = {
{ CLASSIC_1K, "Mifare Classic 1k", 0x08, 0, 0, { 0x00 } },
{ CLASSIC_4K, "Mifare Classic 4k", 0x18, 0, 0, { 0x00 } },
{ CLASSIC_4K, "Mifare Classic 4k (Emulated)", 0x38, 0, 0, { 0x00 } },
{ DESFIRE, "Mifare DESFire", 0x20, 5, 4, { 0x75, 0x77, 0x81, 0x02 /*, 0xXX */ }},
{ ULTRALIGHT, "Mifare UltraLight", 0x00, 0, 0, { 0x00 } },
{ CLASSIC_1K, "Mifare Classic 1k", 0x08, 0, 0, { 0x00 }, NULL },
{ CLASSIC_4K, "Mifare Classic 4k", 0x18, 0, 0, { 0x00 }, NULL },
{ CLASSIC_4K, "Mifare Classic 4k (Emulated)", 0x38, 0, 0, { 0x00 }, NULL },
{ DESFIRE, "Mifare DESFire", 0x20, 5, 4, { 0x75, 0x77, 0x81, 0x02 /*, 0xXX */ }, NULL},
{ ULTRALIGHT_C, "Mifare UltraLightC", 0x00, 0, 0, { 0x00 }, is_mifare_ultralightc_on_reader },
{ ULTRALIGHT, "Mifare UltraLight", 0x00, 0, 0, { 0x00 }, NULL },
};
/*
@ -51,7 +52,9 @@ freefare_tag_new (nfc_device_t *device, nfc_iso14443a_info_t nai)
if (((nai.szUidLen == 4) || (nai.abtUid[0] == NXP_MANUFACTURER_CODE)) &&
(nai.btSak == supported_tags[i].SAK) &&
(!supported_tags[i].ATS_min_length || ((nai.szAtsLen >= supported_tags[i].ATS_min_length) &&
(0 == memcmp (nai.abtAts, supported_tags[i].ATS, supported_tags[i].ATS_compare_length))))) {
(0 == memcmp (nai.abtAts, supported_tags[i].ATS, supported_tags[i].ATS_compare_length)) &&
((supported_tags[i].check_tag_on_reader == NULL) ||
supported_tags[i].check_tag_on_reader(device, nai))))) {
tag_info = &(supported_tags[i]);
found = true;
@ -72,6 +75,7 @@ freefare_tag_new (nfc_device_t *device, nfc_iso14443a_info_t nai)
tag = mifare_desfire_tag_new ();
break;
case ULTRALIGHT:
case ULTRALIGHT_C:
tag = mifare_ultralight_tag_new ();
break;
}
@ -201,6 +205,7 @@ freefare_free_tag (MifareTag tag)
mifare_desfire_tag_free (tag);
break;
case ULTRALIGHT:
case ULTRALIGHT_C:
mifare_ultralight_tag_free (tag);
break;
}

View file

@ -32,7 +32,7 @@
enum mifare_tag_type {
ULTRALIGHT,
// ULTRALIGHT_C,
ULTRALIGHT_C,
// MINI,
CLASSIC_1K,
CLASSIC_4K,
@ -46,6 +46,9 @@ enum mifare_tag_type {
struct mifare_tag;
typedef struct mifare_tag *MifareTag;
struct mifare_desfire_key;
typedef struct mifare_desfire_key *MifareDESFireKey;
typedef uint8_t MifareUltralightPageNumber;
typedef unsigned char MifareUltralightPage[4];
@ -66,6 +69,9 @@ int mifare_ultralight_disconnect (MifareTag tag);
int mifare_ultralight_read (MifareTag tag, const MifareUltralightPageNumber page, MifareUltralightPage *data);
int mifare_ultralight_write (MifareTag tag, const MifareUltralightPageNumber page, const MifareUltralightPage data);
int mifare_ultralightc_authenticate (MifareTag tag, const MifareDESFireKey key);
bool is_mifare_ultralightc_on_reader (nfc_device_t *device, nfc_iso14443a_info_t nai);
typedef unsigned char MifareClassicBlock[16];
typedef uint8_t MifareClassicSectorNumber;
@ -247,9 +253,6 @@ uint32_t mifare_desfire_aid_get_aid (MifareDESFireAID aid);
uint8_t mifare_desfire_last_picc_error (MifareTag tag);
struct mifare_desfire_key;
typedef struct mifare_desfire_key *MifareDESFireKey;
#pragma pack (push)
#pragma pack (1)
struct mifare_desfire_version_info {

View file

@ -146,6 +146,7 @@ typedef enum {
void *mifare_cryto_preprocess_data (MifareTag tag, void *data, size_t *nbytes, off_t offset, int communication_settings);
void *mifare_cryto_postprocess_data (MifareTag tag, void *data, ssize_t *nbytes, int communication_settings);
void mifare_cypher_single_block (MifareDESFireKey key, uint8_t *data, uint8_t *ivect, MifareCryptoDirection direction, MifareCryptoOperation operation, size_t block_size);
void mifare_cypher_blocks_chained (MifareTag tag, MifareDESFireKey key, uint8_t *ivect, uint8_t *data, size_t data_size, MifareCryptoDirection direction, MifareCryptoOperation operation);
void rol (uint8_t *data, const size_t len);
void desfire_crc32 (const uint8_t *data, const size_t len, uint8_t *crc);
@ -159,7 +160,11 @@ void cmac_generate_subkeys (MifareDESFireKey key);
void cmac (const MifareDESFireKey key, uint8_t *ivect, const uint8_t *data, size_t len, uint8_t *cmac);
void *assert_crypto_buffer_size (MifareTag tag, size_t nbytes);
#define MIFARE_ULTRALIGHT_PAGE_COUNT 16
#define MIFARE_ULTRALIGHT_PAGE_COUNT 0x10
#define MIFARE_ULTRALIGHT_C_PAGE_COUNT 0x30
#define MIFARE_ULTRALIGHT_C_PAGE_COUNT_READ 0x2B
// Max PAGE_COUNT of the Ultralight Family:
#define MIFARE_ULTRALIGHT_MAX_PAGE_COUNT 0x30
struct supported_tag {
enum mifare_tag_type type;
@ -168,6 +173,7 @@ struct supported_tag {
uint8_t ATS_min_length;
uint8_t ATS_compare_length;
uint8_t ATS[5];
bool (*check_tag_on_reader) (nfc_device_t *, nfc_iso14443a_info_t);
};
/*
@ -245,8 +251,8 @@ struct mifare_ultralight_tag {
struct mifare_tag __tag;
/* mifare_ultralight_read() reads 4 pages at a time (wrapping) */
MifareUltralightPage cache[MIFARE_ULTRALIGHT_PAGE_COUNT + 3];
uint8_t cached_pages[MIFARE_ULTRALIGHT_PAGE_COUNT];
MifareUltralightPage cache[MIFARE_ULTRALIGHT_MAX_PAGE_COUNT + 3];
uint8_t cached_pages[MIFARE_ULTRALIGHT_MAX_PAGE_COUNT];
};
/*
@ -260,7 +266,9 @@ struct mifare_ultralight_tag {
#define ASSERT_MIFARE_CLASSIC(tag) do { if ((tag->tag_info->type != CLASSIC_1K) && (tag->tag_info->type != CLASSIC_4K)) return errno = ENODEV, -1; } while (0)
#define ASSERT_MIFARE_DESFIRE(tag) do { if (tag->tag_info->type != DESFIRE) return errno = ENODEV, -1; } while (0)
#define ASSERT_MIFARE_ULTRALIGHT(tag) do { if (tag->tag_info->type != ULTRALIGHT) return errno = ENODEV, -1; } while (0)
#define IS_MIFARE_ULTRALIGHT_C(tag) (tag->tag_info->type == ULTRALIGHT_C)
#define ASSERT_MIFARE_ULTRALIGHT(tag) do { if ((tag->tag_info->type != ULTRALIGHT) && (! IS_MIFARE_ULTRALIGHT_C(tag))) return errno = ENODEV, -1; } while (0)
#define ASSERT_MIFARE_ULTRALIGHT_C(tag) do { if (! IS_MIFARE_ULTRALIGHT_C(tag)) return errno = ENODEV, -1; } while (0)
/*
* MifareTag cast macros

View file

@ -71,7 +71,6 @@
#define CMAC_LENGTH 8
static void xor (const uint8_t *ivect, uint8_t *data, const size_t len);
static void mifare_cypher_single_block (MifareDESFireKey key, uint8_t *data, uint8_t *ivect, MifareCryptoDirection direction, MifareCryptoOperation operation, size_t block_size);
static void desfire_crc32_byte (uint32_t *crc, const uint8_t value);
static size_t key_macing_length (MifareDESFireKey key);
@ -608,7 +607,7 @@ mifare_cryto_postprocess_data (MifareTag tag, void *data, ssize_t *nbytes, int c
return res;
}
static void
void
mifare_cypher_single_block (MifareDESFireKey key, uint8_t *data, uint8_t *ivect, MifareCryptoDirection direction, MifareCryptoOperation operation, size_t block_size)
{
AES_KEY k;

View file

@ -45,7 +45,18 @@
#include <freefare.h>
#include "freefare_internal.h"
#define ASSERT_VALID_PAGE(page) do { if (page >= MIFARE_ULTRALIGHT_PAGE_COUNT) return errno = EINVAL, -1; } while (0)
#define ASSERT_VALID_PAGE(tag, page, mode_write) \
do { \
if (IS_MIFARE_ULTRALIGHT_C(tag)) { \
if (mode_write) { \
if (page >= MIFARE_ULTRALIGHT_C_PAGE_COUNT) return errno = EINVAL, -1; \
} else { \
if (page >= MIFARE_ULTRALIGHT_C_PAGE_COUNT_READ) return errno = EINVAL, -1; \
} \
} else { \
if (page >= MIFARE_ULTRALIGHT_PAGE_COUNT) return errno = EINVAL, -1; \
} \
} while (0)
#define ULTRALIGHT_TRANSCEIVE(tag, msg, res) \
do { \
@ -57,6 +68,25 @@
DEBUG_XFER (res, __##res##_n, "<=== "); \
} while (0)
#define ULTRALIGHT_TRANSCEIVE_RAW(tag, msg, res) \
do { \
errno = 0; \
if (!nfc_configure (tag->device, NDO_EASY_FRAMING, false)) { \
errno = EIO; \
return -1; \
} \
DEBUG_XFER (msg, __##msg##_n, "===> "); \
if (!(nfc_initiator_transceive_bytes (tag->device, msg, __##msg##_n, res, &__##res##_n))) { \
nfc_configure (tag->device, NDO_EASY_FRAMING, true); \
return errno = EIO, -1; \
} \
DEBUG_XFER (res, __##res##_n, "<=== "); \
if (!nfc_configure (tag->device, NDO_EASY_FRAMING, true)) { \
errno = EIO; \
return -1; \
} \
} while (0)
/*
* Memory management functions.
@ -106,7 +136,7 @@ mifare_ultralight_connect (MifareTag tag)
};
if (nfc_initiator_select_passive_target (tag->device, modulation, tag->info.abtUid, tag->info.szUidLen, &pnti)) {
tag->active = 1;
for (int i = 0; i < MIFARE_ULTRALIGHT_PAGE_COUNT; i++)
for (int i = 0; i < MIFARE_ULTRALIGHT_MAX_PAGE_COUNT; i++)
MIFARE_ULTRALIGHT(tag)->cached_pages[i] = 0;
} else {
errno = EIO;
@ -149,7 +179,7 @@ mifare_ultralight_read (MifareTag tag, MifareUltralightPageNumber page, MifareUl
{
ASSERT_ACTIVE (tag);
ASSERT_MIFARE_ULTRALIGHT (tag);
ASSERT_VALID_PAGE (page);
ASSERT_VALID_PAGE (tag, page, false);
if (!MIFARE_ULTRALIGHT(tag)->cached_pages[page]) {
BUFFER_INIT (cmd, 2);
@ -161,13 +191,19 @@ mifare_ultralight_read (MifareTag tag, MifareUltralightPageNumber page, MifareUl
ULTRALIGHT_TRANSCEIVE (tag, cmd, res);
/* Handle wrapped pages */
for (int i = MIFARE_ULTRALIGHT_PAGE_COUNT; i <= page + 3; i++) {
memcpy (MIFARE_ULTRALIGHT(tag)->cache[i % MIFARE_ULTRALIGHT_PAGE_COUNT], MIFARE_ULTRALIGHT(tag)->cache[i], sizeof (MifareUltralightPage));
int iPageCount;
if (IS_MIFARE_ULTRALIGHT_C(tag)) {
iPageCount = MIFARE_ULTRALIGHT_C_PAGE_COUNT_READ;
} else {
iPageCount = MIFARE_ULTRALIGHT_PAGE_COUNT;
}
for (int i = iPageCount; i <= page + 3; i++) {
memcpy (MIFARE_ULTRALIGHT(tag)->cache[i % iPageCount], MIFARE_ULTRALIGHT(tag)->cache[i], sizeof (MifareUltralightPage));
}
/* Mark pages as cached */
for (int i = page; i <= page + 3; i++) {
MIFARE_ULTRALIGHT(tag)->cached_pages[i % MIFARE_ULTRALIGHT_PAGE_COUNT] = 1;
MIFARE_ULTRALIGHT(tag)->cached_pages[i % iPageCount] = 1;
}
}
@ -183,7 +219,7 @@ mifare_ultralight_write (MifareTag tag, const MifareUltralightPageNumber page, c
{
ASSERT_ACTIVE (tag);
ASSERT_MIFARE_ULTRALIGHT (tag);
ASSERT_VALID_PAGE (page);
ASSERT_VALID_PAGE (tag, page, true);
uint8_t cmd[6];
cmd[0] = 0xA2;
@ -201,3 +237,96 @@ mifare_ultralight_write (MifareTag tag, const MifareUltralightPageNumber page, c
return 0;
}
/*
* Authenticate to the provided MIFARE tag.
*/
int
mifare_ultralightc_authenticate (MifareTag tag, const MifareDESFireKey key)
{
ASSERT_ACTIVE (tag);
ASSERT_MIFARE_ULTRALIGHT_C (tag);
BUFFER_INIT (cmd1, 2);
BUFFER_INIT (res, 9);
BUFFER_APPEND (cmd1, 0x1A);
BUFFER_APPEND (cmd1, 0x00);
ULTRALIGHT_TRANSCEIVE_RAW(tag, cmd1, res);
uint8_t PICC_E_RndB[8];
memcpy (PICC_E_RndB, res+1, 8);
uint8_t PICC_RndB[8];
memcpy (PICC_RndB, PICC_E_RndB, 8);
uint8_t ivect[8];
memset (ivect, '\0', sizeof (ivect));
mifare_cypher_single_block (key, PICC_RndB, ivect, MCD_RECEIVE, MCO_DECYPHER, 8);
uint8_t PCD_RndA[8];
DES_random_key ((DES_cblock*)&PCD_RndA);
uint8_t PCD_r_RndB[8];
memcpy (PCD_r_RndB, PICC_RndB, 8);
rol (PCD_r_RndB, 8);
uint8_t token[16];
memcpy (token, PCD_RndA, 8);
memcpy (token+8, PCD_r_RndB, 8);
size_t offset = 0;
while (offset < 16) {
mifare_cypher_single_block (key, token + offset, ivect, MCD_SEND, MCO_ENCYPHER, 8);
offset += 8;
}
BUFFER_INIT (cmd2, 17);
BUFFER_APPEND (cmd2, 0xAF);
BUFFER_APPEND_BYTES (cmd2, token, 16);
ULTRALIGHT_TRANSCEIVE_RAW(tag, cmd2, res);
uint8_t PICC_E_RndA_s[8];
memcpy (PICC_E_RndA_s, res+1, 8);
uint8_t PICC_RndA_s[8];
memcpy (PICC_RndA_s, PICC_E_RndA_s, 8);
mifare_cypher_single_block (key, PICC_RndA_s, ivect, MCD_RECEIVE, MCO_DECYPHER, 8);
uint8_t PCD_RndA_s[8];
memcpy (PCD_RndA_s, PCD_RndA, 8);
rol (PCD_RndA_s, 8);
if (0 != memcmp (PCD_RndA_s, PICC_RndA_s, 8)) {
return -1;
}
// XXX Should we store the state "authenticated" in the tag struct??
return 0;
}
/*
* Callback for freefare_tag_new to test presence of a MIFARE UltralightC on the reader.
*/
bool
is_mifare_ultralightc_on_reader (nfc_device_t *device, nfc_iso14443a_info_t nai)
{
bool ret;
uint8_t cmd_step1[2];
uint8_t res_step1[9];
cmd_step1[0] = 0x1A;
cmd_step1[1] = 0x00;
nfc_target_t pnti;
nfc_modulation_t modulation = {
.nmt = NMT_ISO14443A,
.nbr = NBR_106
};
nfc_initiator_select_passive_target (device, modulation, nai.abtUid, nai.szUidLen, &pnti);
nfc_configure (device, NDO_EASY_FRAMING, false);
size_t n;
ret = nfc_initiator_transceive_bytes (device, cmd_step1, sizeof (cmd_step1), res_step1, &n);
nfc_configure (device, NDO_EASY_FRAMING, true);
nfc_initiator_deselect_target (device);
return ret;
}

View file

@ -48,7 +48,8 @@ cut_setup ()
tag = NULL;
for (int i=0; tags[i]; i++) {
if (freefare_get_tag_type(tags[i]) == ULTRALIGHT) {
if ((freefare_get_tag_type(tags[i]) == ULTRALIGHT) ||
(freefare_get_tag_type(tags[i]) == ULTRALIGHT_C)) {
tag = tags[i];
res = mifare_ultralight_connect (tag);
cut_assert_equal_int (0, res, cut_message ("mifare_ultralight_connect() failed"));

View file

@ -76,11 +76,17 @@ test_mifare_ultralight_invalid_page (void)
int res;
MifareUltralightPage page = { 0x00, 0x00, 0x00, 0x00 };
res = mifare_ultralight_read (tag, 16, &page);
int invalid_page;
if (IS_MIFARE_ULTRALIGHT_C (tag)) {
invalid_page = MIFARE_ULTRALIGHT_C_PAGE_COUNT;
} else {
invalid_page = MIFARE_ULTRALIGHT_PAGE_COUNT;
}
res = mifare_ultralight_read (tag, invalid_page, &page);
cut_assert_equal_int (-1, res, cut_message ("mifare_ultralight_read() succeeded"));
cut_assert_equal_int (EINVAL, errno, cut_message ("Wrong errno value"));
res = mifare_ultralight_write (tag, 16, page);
res = mifare_ultralight_write (tag, invalid_page, page);
cut_assert_equal_int (-1, res, cut_message ("mifare_ultralight_write() succeeded"));
cut_assert_equal_int (EINVAL, errno, cut_message ("Wrong errno value"));
}
@ -125,20 +131,26 @@ test_mifare_ultralight_cache_wrap (void)
{
int res;
MifareUltralightPage page;
res = mifare_ultralight_read (tag, 15, &page);
int last_page;
if (IS_MIFARE_ULTRALIGHT_C (tag)) {
// Last 4 blocks are for 3DES key and cannot be read, read will wrap from 0x2b
last_page = MIFARE_ULTRALIGHT_C_PAGE_COUNT_READ -1;
// Actually engineering samples require auth to read above page 0x28 so we skip the test entirely
cut_omit("mifare_ultralight_read() on last page skipped on UltralightC");
} else {
last_page = MIFARE_ULTRALIGHT_PAGE_COUNT -1;
}
res = mifare_ultralight_read (tag, last_page, &page);
cut_assert_equal_int (0, res, cut_message ("mifare_ultralight_read() failed"));
/* Check cached pages consistency */
for (int i = 0; i <= 2; i++) {
cut_assert_equal_int (1, MIFARE_ULTRALIGHT(tag)->cached_pages[i], cut_message ("Wrong page cache value for tag->cached_pages[%d]", i));
}
for (int i = 3; i <= 14; i++) {
for (int i = 3; i < last_page; i++) {
cut_assert_equal_int (0, MIFARE_ULTRALIGHT(tag)->cached_pages[i], cut_message ("Wrong page cache value for tag->cached_pages[%d]", i));
}
for (int i = 15; i < MIFARE_ULTRALIGHT_PAGE_COUNT; i++) {
cut_assert_equal_int (1, MIFARE_ULTRALIGHT(tag)->cached_pages[i], cut_message ("Wrong page cache value for tag->cached_pages[%d]", i));
}
cut_assert_equal_int (1, MIFARE_ULTRALIGHT(tag)->cached_pages[last_page], cut_message ("Wrong page cache value for tag->cached_pages[%d]", last_page));
}
void
@ -162,4 +174,24 @@ test_mifare_ultralight_tag_friendly_name (void)
cut_assert_not_null (name, cut_message ("freefare_get_tag_friendly_name() failed"));
}
void
test_mifare_ultralightc_authenticate (void)
{
int res;
MifareDESFireKey key;
if (tag->tag_info->type == ULTRALIGHT_C) {
uint8_t key1_3des_data[16] = { 0x49, 0x45, 0x4D, 0x4B, 0x41, 0x45, 0x52, 0x42, 0x21, 0x4E, 0x41, 0x43, 0x55, 0x4F, 0X59, 0x46 };
key = mifare_desfire_3des_key_new (key1_3des_data);
res = mifare_ultralightc_authenticate (tag, key);
cut_assert_equal_int (0, res, cut_message ("mifare_ultralightc_authenticate() failed"));
mifare_desfire_key_free (key);
MifareUltralightPage page;
int last_page = MIFARE_ULTRALIGHT_C_PAGE_COUNT_READ -1;
res = mifare_ultralight_read (tag, last_page, &page);
cut_assert_equal_int (0, res, cut_message ("mifare_ultralight_read() failed"));
} else {
cut_omit("mifare_ultralightc_authenticate() skipped on Ultralight");
}
}