Add support for Mifare DESFire EV1 with AES encryption.

This commit is contained in:
Romain Tartiere 2010-12-15 12:43:31 +00:00
parent 1070d9bfde
commit 42f9457d9f
19 changed files with 2238 additions and 268 deletions

11
NEWS
View file

@ -1,5 +1,16 @@
Changes between 0.2.2 and 0.2.3 [XX xxx XXXX]
*) New functions mifare_desfire_create_application_ex(),
mifare_desfire_create_application_iso(),
mifare_desfire_create_application_iso_ex(), mifare_desfire_get_df_names(),
mifare_desfire_free_mem(), mifare_desfire_set_configuration(),
mifare_desfire_set_default_key(), mifare_desfire_set_ats(),
mifare_desfire_get_card_uid(), mifare_desfire_get_iso_file_ids(),
mifare_desfire_create_std_data_file_iso(),
mifare_desfire_create_backup_data_file_iso(),
mifare_desfire_create_linear_record_file_iso() and
mifare_desfire_create_cyclic_record_file_iso() for Mifare DESFire EV1
targets manipulation.
*) Deprecate authentication information when deleting the currently selected
application

12
TODO
View file

@ -1,5 +1,13 @@
- Enforce valid parameters.
[ ] Enforce valid parameters.
Some functions will cause a crash when called with invalid parameters (e.g.
mifare_classic_authenticate() with a NULL key).
- MAD and MifareApplication functions are tied to mifare_clasic_* ones and
[ ] MAD and MifareApplication functions are tied to mifare_clasic_* ones and
some refactoring is required to have a consistent API.
[ ] The DESFIRE_TRANSCEIVE macro should be replaced by a function
DESFIRE_TRANSCEIVE was originaly a macro to enclose a nfc_initiator_trans-
ceive_bytes() call with debug lines. The functions has unexpectedly grown
up and is now between 25 and 100 lines of code (depending of my refactoring
mood). The main drawbacks are poor readability, redundant code in the
binary, debuggers don't cope well with macros, and by design it sucks.
[ ] libfreefare/mifare_desfire_authenticate.c should be renamed to a better name
such as libfreefare/mifare_desfire_crypto.c.

View file

@ -38,7 +38,7 @@ AC_FUNC_MALLOC
AC_FUNC_REALLOC
AC_CHECK_HEADERS([sys/types.h])
AC_CHECK_FUNCS([memset letoh32 htole32 pow strerror])
AC_CHECK_FUNCS([memset letoh32 htole32 pow strdup strerror])
AC_CHECK_HEADERS([endian.h sys/endian.h CoreFoundation/CoreFoundation.h])
if test $ac_cv_header_endian_h = "no" -a $ac_cv_header_sys_endian_h = "no" -a $ac_cv_header_CoreFoundation_CoreFoundation_h = "no"; then
@ -54,7 +54,7 @@ CFLAGS="$CFLAGS -std=c99"
# Crypto functions for MIFARE DesFire support are provided by OpenSSL.
AC_CHECK_LIB([crypto], [DES_ecb_encrypt], [], [AC_MSG_ERROR([Cannot find libcrypto.])])
AC_CHECK_HEADERS([openssl/des.h openssl/rand.h], [], [AC_MSG_ERROR([Cannot find openssl headers.])])
AC_CHECK_HEADERS([openssl/aes.h openssl/des.h openssl/rand.h], [], [AC_MSG_ERROR([Cannot find openssl headers.])])
# Checks for pkg-config modules.
LIBNFC_REQUIRED_VERSION="1.4.0"

View file

@ -173,7 +173,7 @@ main(int argc, char *argv[])
mifare_desfire_disconnect (tags[i]);
break;
default: // Should not happen
warnx ("Unsupported UID length %d.", tag_uid_len);
warnx ("Unsupported UID length %d.", (int) tag_uid_len);
error = EXIT_FAILURE;
break;
}

View file

@ -254,3 +254,19 @@ freefare_free_tags (MifareTag *tags)
free (tags);
}
}
/*
* Low-level API
*/
void *
memdup (const void *p, const size_t n)
{
void *res;
if ((res = malloc (n))) {
memcpy (res, p, n);
}
return res;
}

View file

@ -304,6 +304,7 @@ int mifare_desfire_connect (MifareTag tag);
int mifare_desfire_disconnect (MifareTag tag);
int mifare_desfire_authenticate (MifareTag tag, uint8_t key_no, MifareDESFireKey key);
int mifare_desfire_authenticate_aes (MifareTag tag, uint8_t key_no, MifareDESFireKey key);
int mifare_desfire_change_key_settings (MifareTag tag, uint8_t settings);
int mifare_desfire_get_key_settings (MifareTag tag, uint8_t *settings, uint8_t *max_keys);
int mifare_desfire_change_key (MifareTag tag, uint8_t key_no, MifareDESFireKey new_key, MifareDESFireKey old_key);
@ -315,6 +316,7 @@ void mifare_desfire_free_application_ids (MifareDESFireAID aids[]);
int mifare_desfire_select_application (MifareTag tag, MifareDESFireAID aid);
int mifare_desfire_format_picc (MifareTag tag);
int mifare_desfire_get_version (MifareTag tag, struct mifare_desfire_version_info *version_info);
int mifare_desfire_free_mem (MifareTag tag, uint32_t *size);
int mifare_desfire_get_file_ids (MifareTag tag, uint8_t *files[], size_t *count);
int mifare_desfire_get_file_settings (MifareTag tag, uint8_t file_no, struct mifare_desfire_file_settings *settings);
int mifare_desfire_change_file_settings (MifareTag tag, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights);
@ -349,6 +351,8 @@ MifareDESFireKey mifare_desfire_des_key_new (uint8_t value[8]);
MifareDESFireKey mifare_desfire_3des_key_new (uint8_t value[16]);
MifareDESFireKey mifare_desfire_des_key_new_with_version (uint8_t value[8]);
MifareDESFireKey mifare_desfire_3des_key_new_with_version (uint8_t value[16]);
MifareDESFireKey mifare_desfire_aes_key_new (uint8_t value[16]);
MifareDESFireKey mifare_desfire_aes_key_new_with_version (uint8_t value[16], uint8_t version);
uint8_t mifare_desfire_key_get_version (MifareDESFireKey key);
void mifare_desfire_key_set_version (MifareDESFireKey key, uint8_t version);
void mifare_desfire_key_free (MifareDESFireKey key);

View file

@ -96,7 +96,10 @@
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define MAX_CRYPTO_BLOCK_SIZE 8
#define MAX_CRYPTO_BLOCK_SIZE 16
#define MAX_FRAME_SIZE 60
void *memdup (const void *p, const size_t n);
struct mad_sector_0x00;
struct mad_sector_0x10;
@ -118,11 +121,38 @@ typedef enum {
#define MDCM_MASK 0x000F
#define CMAC_NONE 0
// Data send to the PICC is used to update the CMAC
#define CMAC_COMMAND 0x010
// Data received from the PICC is used to update the CMAC
#define CMAC_VERIFY 0x020
// MAC the command (when MDCM_MACED)
#define MAC_COMMAND 0x100
// The command returns a MAC to verify (when MDCM_MACED)
#define MAC_VERIFY 0x200
#define ENC_COMMAND 0x1000
#define NO_CRC 0x2000
#define UNSPECIFIED_DATA_LENGTH 0x4000
#define MAC_MASK 0x0F0
#define CMAC_MACK 0xF00
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_cbc_des (MifareDESFireKey key, uint8_t *ivect, uint8_t *data, size_t data_size, MifareDirection direction, int mac);
void rol (uint8_t *data, const size_t len);
void desfire_crc32 (const uint8_t *data, const size_t len, uint8_t *crc);
void desfire_crc32_append (uint8_t *data, const size_t len);
size_t key_block_size (const MifareDESFireKey key);
size_t padded_data_length (const size_t nbytes, const size_t block_size);
size_t maced_data_length (const MifareDESFireKey key, const size_t nbytes);
size_t enciphered_data_length (const MifareDESFireKey key, const size_t nbytes);
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
@ -175,23 +205,29 @@ struct mifare_desfire_key {
uint8_t data[16];
enum {
T_DES,
T_3DES
T_3DES,
T_AES
} type;
DES_key_schedule ks1;
DES_key_schedule ks2;
uint8_t aes_sk1[16];
uint8_t aes_sk2[16];
uint8_t aes_version;
};
struct mifare_desfire_tag {
struct mifare_tag __tag;
uint8_t last_picc_error;
uint8_t last_internal_error;
uint8_t last_pcd_error;
MifareDESFireKey session_key;
uint8_t authenticated_key_no;
uint8_t ivect[MAX_CRYPTO_BLOCK_SIZE];
uint8_t cmac[16];
uint8_t *crypto_buffer;
size_t crypto_buffer_size;
uint8_t block_number;
uint32_t selected_application;
};
MifareDESFireKey mifare_desfire_session_key_new (uint8_t rnda[8], uint8_t rndb[8], MifareDESFireKey authentication_key);

File diff suppressed because it is too large Load diff

View file

@ -17,22 +17,63 @@
* $Id$
*/
/*
* This implementation was written based on information provided by the
* following documents:
*
* NIST Special Publication 800-38B
* Recommendation for Block Cipher Modes of Operation: The CMAC Mode for Authentication
* May 2005
*/
#include "config.h"
#if defined(HAVE_SYS_TYPES_H)
# include <sys/types.h>
#endif
#if defined(HAVE_SYS_ENDIAN_H)
# include <sys/endian.h>
#endif
#if defined(HAVE_ENDIAN_H)
# include <endian.h>
#endif
#if defined(HAVE_COREFOUNDATION_COREFOUNDATION_H)
# include <CoreFoundation/CoreFoundation.h>
#endif
#if defined(HAVE_BYTESWAP_H)
# include <byteswap.h>
#endif
#if defined(HAVE_SYS_TYPES_H)
# include <sys/types.h>
#endif
#include <openssl/aes.h>
#include <openssl/des.h>
#include <err.h>
#include <string.h>
#include <strings.h>
#ifdef WITH_DEBUG
# include <libutil.h>
#endif
#include <freefare.h>
#include "freefare_internal.h"
#define MAC_LENGTH 4
#define CMAC_LENGTH 8
static void xor (const uint8_t *ivect, uint8_t *data, const size_t len);
static void mifare_des (MifareDESFireKey key, uint8_t *data, uint8_t *ivect, MifareDirection direction, int mac, size_t block_size);
static size_t padded_data_length (size_t nbytes, const size_t block_size);
static size_t maced_data_length (const MifareDESFireKey key, size_t nbytes);
static size_t enciphered_data_length (const MifareDESFireKey key, size_t nbytes);
static void desfire_crc32_byte (uint32_t *crc, const uint8_t value);
static size_t key_macing_length (MifareDESFireKey key);
static void
xor (const uint8_t *ivect, uint8_t *data, const size_t len)
@ -43,7 +84,7 @@ xor (const uint8_t *ivect, uint8_t *data, const size_t len)
}
void
rol(uint8_t *data, const size_t len)
rol (uint8_t *data, const size_t len)
{
uint8_t first = data[0];
for (size_t i = 0; i < len-1; i++) {
@ -52,6 +93,104 @@ rol(uint8_t *data, const size_t len)
data[len-1] = first;
}
void
lsl (uint8_t *data, size_t len)
{
for (size_t n = 0; n < len - 1; n++) {
data[n] = (data[n] << 1) | (data[n+1] >> 7);
}
data[len - 1] <<= 1;
}
void
cmac_generate_subkeys (MifareDESFireKey key)
{
uint8_t l[16];
bzero (l, 16);
uint8_t ivect[16];
bzero (ivect, 16);
mifare_cbc_des (key, ivect, l, 16, MD_RECEIVE, 1);
bool xor = false;
// Used to compute CMAC on complete blocks
memcpy (key->aes_sk1, l, 16);
xor = l[0] & 0x80;
lsl (key->aes_sk1, 16);
if (xor)
key->aes_sk1[15] ^= 0x87;
// Used to compute CMAC on the last block if non-complete
memcpy (key->aes_sk2, key->aes_sk1, 16);
xor = key->aes_sk1[0] & 0x80;
lsl (key->aes_sk2, 16);
if (xor)
key->aes_sk2[15] ^= 0x87;
}
void
cmac (const MifareDESFireKey key, uint8_t *ivect, const uint8_t *data, size_t len, uint8_t *cmac)
{
uint8_t *buffer = malloc (padded_data_length (len, key_block_size (key)));
if (!buffer)
abort();
memcpy (buffer, data, len);
if ((!len) || (len % 16)) {
buffer[len++] = 0x80;
while (len % 16) {
buffer[len++] = 0x00;
}
xor (key->aes_sk2, buffer + len - 16, 16);
} else {
xor (key->aes_sk1, buffer + len - 16, 16);
}
mifare_cbc_des (key, ivect, buffer, len, MD_SEND, 1);
memcpy (cmac, ivect, 16);
free (buffer);
}
#define CRC32_PRESET 0xFFFFFFFF
static void
desfire_crc32_byte (uint32_t *crc, const uint8_t value)
{
/* x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x + 1 */
const uint32_t poly = 0xEDB88320;
*crc ^= value;
for (int current_bit = 7; current_bit >= 0; current_bit--) {
int bit_out = (*crc) & 0x00000001;
*crc >>= 1;
if (bit_out)
*crc ^= poly;
}
}
void
desfire_crc32 (const uint8_t *data, const size_t len, uint8_t *crc)
{
uint32_t desfire_crc = CRC32_PRESET;
for (size_t i = 0; i < len; i++) {
desfire_crc32_byte (&desfire_crc, data[i]);
}
*((uint32_t *)(crc)) = htole32 (desfire_crc);
}
void
desfire_crc32_append (uint8_t *data, const size_t len)
{
desfire_crc32 (data, len, data + len);
}
size_t
key_block_size (const MifareDESFireKey key)
{
@ -62,15 +201,39 @@ key_block_size (const MifareDESFireKey key)
case T_3DES:
block_size = 8;
break;
case T_AES:
block_size = 16;
break;
}
return block_size;
}
/*
* Size required to store nbytes of data in a buffer of size n*block_size.
* Size of MACing produced with the key.
*/
static size_t
key_macing_length (const MifareDESFireKey key)
{
size_t mac_length;
switch (key->type) {
case T_DES:
case T_3DES:
mac_length = MAC_LENGTH;
break;
case T_AES:
mac_length = CMAC_LENGTH;
break;
}
return mac_length;
}
/*
* Size required to store nbytes of data in a buffer of size n*block_size.
*/
size_t
padded_data_length (const size_t nbytes, const size_t block_size)
{
if (nbytes % block_size)
@ -82,22 +245,15 @@ padded_data_length (const size_t nbytes, const size_t block_size)
/*
* Buffer size required to MAC nbytes of data
*/
static size_t
size_t
maced_data_length (const MifareDESFireKey key, const size_t nbytes)
{
size_t mac_length;
switch (key->type) {
case T_DES:
case T_3DES:
mac_length = 4;
break;
}
return nbytes + mac_length;
return nbytes + key_macing_length (key);
}
/*
* Buffer size required to encipher nbytes of data and a two bytes CRC.
*/
static size_t
size_t
enciphered_data_length (const MifareDESFireKey key, const size_t nbytes)
{
size_t crc_length;
@ -106,6 +262,9 @@ enciphered_data_length (const MifareDESFireKey key, const size_t nbytes)
case T_3DES:
crc_length = 2;
break;
case T_AES:
crc_length = 4;
break;
}
size_t block_size = key_block_size (key);
@ -136,6 +295,7 @@ mifare_cryto_preprocess_data (MifareTag tag, void *data, size_t *nbytes, off_t o
void *res = data;
uint8_t mac[4];
size_t edl, mdl;
bool append_mac = true;
MifareDESFireKey key = MIFARE_DESFIRE (tag)->session_key;
if (!key)
@ -143,8 +303,28 @@ mifare_cryto_preprocess_data (MifareTag tag, void *data, size_t *nbytes, off_t o
switch (communication_settings & MDCM_MASK) {
case MDCM_PLAIN:
if ((T_DES == key->type) || (T_3DES == key->type))
break;
/*
* When using new authentication methods, PLAIN data transmission from
* the PICC to the PCD are CMACed, so we have to maintain the
* cruptographic initialisation vector up-to-date to chaeck data
* integrity later.
*
* The only difference with CMACed data transmission is that the CMAC
* is not addpended to the data send byt the PCD to the PICC.
*/
append_mac = false;
/* pass through */
case MDCM_MACED:
switch (key->type) {
case T_DES:
case T_3DES:
if (!(communication_settings & MAC_COMMAND))
break;
edl = padded_data_length (*nbytes - offset, key_block_size (MIFARE_DESFIRE (tag)->session_key)) + offset;
if (!(res = assert_crypto_buffer_size (tag, edl)))
abort();
@ -158,25 +338,69 @@ mifare_cryto_preprocess_data (MifareTag tag, void *data, size_t *nbytes, off_t o
memcpy (mac, (uint8_t *)res + edl - 8, 4);
// Copy again provided data (was overwritten by mifare_cbc_des)
memcpy (res, data, *nbytes);
// Append MAC
mdl = maced_data_length (MIFARE_DESFIRE (tag)->session_key, *nbytes - offset) + offset;
if (!(res = assert_crypto_buffer_size (tag, mdl)))
abort();
memcpy (res, data, *nbytes);
memcpy ((uint8_t *)res + *nbytes, mac, 4);
*nbytes += 4;
break;
case T_AES:
if (!(communication_settings & CMAC_COMMAND))
break;
cmac (key, MIFARE_DESFIRE (tag)->ivect, res, *nbytes, MIFARE_DESFIRE (tag)->cmac);
if (append_mac) {
mdl = maced_data_length (key, *nbytes);
if (!(res = assert_crypto_buffer_size (tag, mdl)))
abort();
memcpy (res, data, *nbytes);
memcpy ((uint8_t *) res + *nbytes, MIFARE_DESFIRE (tag)->cmac, CMAC_LENGTH);
*nbytes += CMAC_LENGTH;
}
break;
}
break;
case MDCM_ENCIPHERED:
/* |<-------------- data -------------->|
* |<--- offset -->| |
* +-----+---------+--------------------+-----+---------+
* | CMD + HEADERS | DATA TO BE SECURED | CRC | PADDING |
* +-----+---------+--------------------+-----+---------+ ----------------
* | |<~~~~v~~~~~~~~~~~~~>| ^ | | (DES / 3DES)
* | | `---- crc16() ----' | |
* | | | ^ | | ----- *or* -----
* |<~~~~~~~~~~~~~~~~~~~~v~~~~~~~~~~~~~>| ^ | | (3K3DES / AES)
* | `---- crc32() ----' | |
* | | ---- *then* ----
* |<---------------------------------->|
* encypher()/decypher()
*/
switch (key->type) {
case T_DES:
case T_3DES:
if (!(communication_settings & ENC_COMMAND))
break;
edl = enciphered_data_length (MIFARE_DESFIRE (tag)->session_key, *nbytes - offset) + offset;
if (!(res = assert_crypto_buffer_size (tag, edl)))
abort();
// Fill in the crypto buffer with data ...
memcpy (res, data, *nbytes);
if (!(communication_settings & NO_CRC)) {
// ... CRC ...
iso14443a_crc_append ((uint8_t *)res + offset, *nbytes - offset);
} else {
bzero ((uint8_t *) res + *nbytes, 2);
}
// ... and 0 padding
memset ((uint8_t *)(res) + *nbytes + 2, 0, edl - *nbytes - 2);
@ -184,8 +408,40 @@ mifare_cryto_preprocess_data (MifareTag tag, void *data, size_t *nbytes, off_t o
mifare_cbc_des (MIFARE_DESFIRE (tag)->session_key, MIFARE_DESFIRE (tag)->ivect, (uint8_t *) res + offset, *nbytes - offset, MD_SEND, 0);
break;
case T_AES:
if (!(communication_settings & NO_CRC)) {
edl = enciphered_data_length (MIFARE_DESFIRE (tag)->session_key, *nbytes + 4 - offset) + offset;
} else {
edl = enciphered_data_length (MIFARE_DESFIRE (tag)->session_key, *nbytes - offset) + offset;
}
if (!(res = assert_crypto_buffer_size (tag, edl)))
abort();
// Fill in the crypto buffer with data ...
memcpy (res, data, *nbytes);
size_t pdl;
if (!(communication_settings & NO_CRC)) {
desfire_crc32_append (res, *nbytes);
pdl = padded_data_length (*nbytes - offset + 4, key_block_size (MIFARE_DESFIRE (tag)->session_key));
bzero ((uint8_t *)res + *nbytes + 4, (offset + pdl) - (*nbytes + 4));
} else {
pdl = padded_data_length (*nbytes - offset, key_block_size (MIFARE_DESFIRE (tag)->session_key));
bzero ((uint8_t *)res + *nbytes, (offset + pdl) - (*nbytes));
}
mifare_cbc_des (key, MIFARE_DESFIRE (tag)->ivect, (uint8_t *)res + offset, pdl, MD_SEND, 1);
*nbytes = offset + pdl;
break;
}
break;
default:
warnx ("Unknown communication settings");
#if WITH_DEBUG
abort ();
#endif
*nbytes = -1;
res = NULL;
break;
}
@ -198,7 +454,13 @@ mifare_cryto_postprocess_data (MifareTag tag, void *data, ssize_t *nbytes, int c
{
void *res = data;
size_t edl;
void *edata;
void *edata = NULL;
uint8_t first_cmac_byte;
MifareDESFireKey key = MIFARE_DESFIRE (tag)->session_key;
if (!key)
return data;
// Return directly if we just have a status code.
if (1 == *nbytes)
@ -206,35 +468,86 @@ mifare_cryto_postprocess_data (MifareTag tag, void *data, ssize_t *nbytes, int c
switch (communication_settings & MDCM_MASK) {
case MDCM_PLAIN:
break;
case MDCM_MACED:
*nbytes -= 4;
edl = enciphered_data_length (MIFARE_DESFIRE (tag)->session_key, *nbytes);
if ((T_DES == key->type) || (T_3DES == key->type))
break;
/* pass through */
case MDCM_MACED:
switch (key->type) {
case T_DES:
case T_3DES:
if (communication_settings & MAC_VERIFY) {
*nbytes -= key_macing_length (key);
edl = enciphered_data_length (MIFARE_DESFIRE (tag)->session_key, *nbytes - 1);
edata = malloc (edl);
memcpy (edata, data, *nbytes);
memset ((uint8_t *)edata + *nbytes, 0, edl - *nbytes);
memcpy (edata, data, *nbytes - 1);
memset ((uint8_t *)edata + *nbytes - 1, 0, edl - *nbytes + 1);
mifare_cbc_des (MIFARE_DESFIRE (tag)->session_key, MIFARE_DESFIRE (tag)->ivect, edata, edl, MD_SEND, 1);
/* ,^^^^^^^
* No! This is not a typo! --------------------------------------------------------------'
*/
if (0 != memcmp ((uint8_t *)data + *nbytes, (uint8_t *)edata + edl - 8, 4)) {
printf ("MACing not verified\n");
if (0 != memcmp ((uint8_t *)data + *nbytes - 1, (uint8_t *)edata + edl - 8, 4)) {
warnx ("MACing not verified");
#if WITH_DEBUG
hexdump ((uint8_t *)data + *nbytes - 1, 4, "Expect ", 0);
hexdump ((uint8_t *)edata + edl - 8, 4, "Actual ", 0);
abort ();
#endif
*nbytes = -1;
res = NULL;
}
}
break;
case T_AES:
if (!(communication_settings & CMAC_COMMAND))
break;
if (communication_settings & CMAC_VERIFY) {
if (*nbytes < 9) {
// XXX: Can't we avoid abort() -ing?
warnx ("No room for CMAC!");
abort ();
}
first_cmac_byte = ((uint8_t *)data)[*nbytes - 9];
((uint8_t *)data)[*nbytes - 9] = ((uint8_t *)data)[*nbytes-1];
}
int n = (communication_settings & CMAC_VERIFY) ? 8 : 0;
cmac (key, MIFARE_DESFIRE (tag)->ivect, ((uint8_t *)data), *nbytes - n, MIFARE_DESFIRE (tag)->cmac);
if (communication_settings & CMAC_VERIFY) {
((uint8_t *)data)[*nbytes - 9] = first_cmac_byte;
if (0 != memcmp (MIFARE_DESFIRE (tag)->cmac, (uint8_t *)data + *nbytes - 9, 8)) {
#if WITH_DEBUG
warnx ("CMAC NOT verified :-(");
hexdump ((uint8_t *)data + *nbytes - 9, 8, "Expect ", 0);
hexdump (MIFARE_DESFIRE (tag)->cmac, 8, "Actual ", 0);
abort ();
#endif
*nbytes = -1;
res = NULL;
} else {
*nbytes -= 8;
}
}
break;
}
free (edata);
break;
case MDCM_ENCIPHERED:
switch (key->type) {
case T_DES:
case T_3DES:
mifare_cbc_des (MIFARE_DESFIRE (tag)->session_key, MIFARE_DESFIRE (tag)->ivect, res, *nbytes, MD_RECEIVE, 0);
/*
* Look for the CRC and ensure it is following by NULL padding. We
* Look for the CRC and ensure it is followed by NULL padding. We
* can't start by the end because the CRC is supposed to be 0 when
* verified, and accumulating 0's in it should not change it.
*/
@ -246,7 +559,7 @@ mifare_cryto_postprocess_data (MifareTag tag, void *data, ssize_t *nbytes, int c
iso14443a_crc (res, end_crc_pos, (uint8_t *)&crc);
if (!crc) {
verified = true;
for (int n = end_crc_pos; n < *nbytes; n++) {
for (int n = end_crc_pos; n < *nbytes - 1; n++) {
uint8_t byte = ((uint8_t *)res)[n];
if (!( (0x00 == byte) || ((0x80 == byte) && (n == end_crc_pos)) ))
verified = false;
@ -254,20 +567,59 @@ mifare_cryto_postprocess_data (MifareTag tag, void *data, ssize_t *nbytes, int c
}
if (verified) {
*nbytes = end_crc_pos - 2;
((uint8_t *)data)[(*nbytes)++] = 0x00;
} else {
end_crc_pos++;
}
} while (!verified && (end_crc_pos < *nbytes));
} while (!verified && (end_crc_pos < *nbytes - 1));
if (!verified) {
printf ("(3)DES not verified\n");
warnx ("(3)DES not verified");
#if WITH_DEBUG
abort ();
#endif
*nbytes = -1;
res = NULL;
}
break;
case T_AES:
(*nbytes)--;
mifare_cbc_des (MIFARE_DESFIRE (tag)->session_key, MIFARE_DESFIRE (tag)->ivect, res, *nbytes, MD_RECEIVE, 0);
uint8_t *p = ((uint8_t *)res) + *nbytes - 1;
while (!*p) {
p--;
}
if (UNSPECIFIED_DATA_LENGTH && (*p == 0x80)) {
p--;
}
p -= 3;
uint8_t crc_ref[4];
memcpy (crc_ref, p, 4);
*p++ = ((uint8_t *)res)[*nbytes];
uint8_t crc[4];
desfire_crc32 (res, p - (uint8_t *)res, crc);
if (memcmp (crc, crc_ref, 4)) {
warnx ("AES CRC32 not verified in AES stream");
#if WITH_DEBUG
hexdump (crc_ref, 4, "Expect ", 0);
hexdump (crc, 4, "Actual ", 0);
abort ();
#endif
*nbytes = -1;
res = NULL;
}
*nbytes = p - (uint8_t *)res;
}
break;
default:
printf ("Unknown communication settings\n");
warnx ("Unknown communication settings");
#if WITH_DEBUG
abort ();
#endif
*nbytes = -1;
res = NULL;
break;
@ -308,6 +660,17 @@ mifare_des (MifareDESFireKey key, uint8_t *data, uint8_t *ivect, MifareDirection
DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_DECRYPT);
}
break;
case T_AES:
if (mac) {
AES_KEY k;
AES_set_encrypt_key (key->data, 8*16, &k);
AES_encrypt (data, edata, &k);
} else {
AES_KEY k;
AES_set_decrypt_key (key->data, 8*16, &k);
AES_decrypt (data, edata, &k);
}
break;
}
memcpy (data, edata, block_size);
@ -331,6 +694,9 @@ mifare_cbc_des (MifareDESFireKey key, uint8_t *ivect, uint8_t *data, size_t data
memset (ivect, 0, MAX_CRYPTO_BLOCK_SIZE);
block_size = 8;
break;
case T_AES:
block_size = 16;
break;
}
size_t offset = 0;

View file

@ -83,6 +83,25 @@ mifare_desfire_3des_key_new_with_version (uint8_t value[16])
return key;
}
MifareDESFireKey
mifare_desfire_aes_key_new (uint8_t value[16])
{
return mifare_desfire_aes_key_new_with_version (value, 0);
}
MifareDESFireKey
mifare_desfire_aes_key_new_with_version (uint8_t value[16], uint8_t version)
{
MifareDESFireKey key;
if ((key = malloc (sizeof (struct mifare_desfire_key)))) {
memcpy (key->data, value, 16);
key->type = T_AES;
key->aes_version = version;
}
return key;
}
uint8_t
mifare_desfire_key_get_version (MifareDESFireKey key)
{
@ -132,6 +151,13 @@ mifare_desfire_session_key_new (uint8_t rnda[8], uint8_t rndb[8], MifareDESFireK
memcpy (buffer+12, rndb+4, 4);
key = mifare_desfire_3des_key_new_with_version (buffer);
break;
case T_AES:
memcpy (buffer, rnda, 4);
memcpy (buffer+4, rndb, 4);
memcpy (buffer+8, rnda+12, 4);
memcpy (buffer+12, rndb+12, 4);
key = mifare_desfire_aes_key_new (buffer);
break;
}
return key;

View file

@ -19,8 +19,10 @@ noinst_LTLIBRARIES = \
test_mifare_classic_create_trailer_block.la \
test_mifare_classic_sector_boundaries.la \
test_mifare_desfire.la \
test_mifare_desfire_aes.la \
test_mifare_desfire_aid.la \
test_mifare_desfire_des.la \
test_mifare_desfire_ev1.la \
test_mifare_desfire_key.la \
test_mifare_ultralight.la \
test_tlv.la
@ -51,12 +53,22 @@ test_mifare_desfire_la_SOURCES = test_mifare_desfire.c \
test_mifare_desfire_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la \
$(top_builddir)/test/common/libtestcommon.la
test_mifare_desfire_aes_la_SOURCES = test_mifare_desfire_aes.c
test_mifare_desfire_aes_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la
test_mifare_desfire_aid_la_SOURCES = test_mifare_desfire_aid.c
test_mifare_desfire_aid_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la
test_mifare_desfire_des_la_SOURCES = test_mifare_desfire_des.c
test_mifare_desfire_des_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la
test_mifare_desfire_ev1_la_SOURCES = test_mifare_desfire_ev1.c \
test_mifare_desfire_ev1_aes.c \
mifare_desfire_ev1_fixture.c \
mifare_desfire_ev1_fixture.h
test_mifare_desfire_ev1_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la \
$(top_builddir)/test/common/libtestcommon.la
test_mifare_desfire_key_la_SOURCES = test_mifare_desfire_key.c
test_mifare_desfire_key_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la

View file

@ -26,6 +26,9 @@
uint8_t key_data_null[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
uint8_t key_data_des[8] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' };
uint8_t key_data_3des[16] = { 'C', 'a', 'r', 'd', ' ', 'M', 'a', 's', 't', 'e', 'r', ' ', 'K', 'e', 'y', '!' };
uint8_t key_data_aes[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
const uint8_t key_data_aes_version = 0x42;
void
mifare_desfire_auto_authenticate (MifareTag tag, uint8_t key_no)
@ -41,6 +44,9 @@ mifare_desfire_auto_authenticate (MifareTag tag, uint8_t key_no)
case 0x00:
key = mifare_desfire_des_key_new_with_version (key_data_null);
break;
case 0x42:
key = mifare_desfire_aes_key_new_with_version (key_data_aes, key_data_aes_version);
break;
case 0xAA:
key = mifare_desfire_des_key_new_with_version (key_data_des);
break;
@ -54,7 +60,15 @@ mifare_desfire_auto_authenticate (MifareTag tag, uint8_t key_no)
cut_assert_not_null (key, cut_message ("Cannot allocate key"));
/* Authenticate with this key */
switch (key_version) {
case 0x00:
case 0xAA:
case 0xC7:
res = mifare_desfire_authenticate (tag, key_no, key);
break;
case 0x42:
res = mifare_desfire_authenticate_aes (tag, key_no, key);
}
cut_assert_equal_int (0, res, cut_message ("mifare_desfire_authenticate()"));
mifare_desfire_key_free (key);

View file

@ -23,6 +23,8 @@
extern uint8_t key_data_null[8];
extern uint8_t key_data_des[8];
extern uint8_t key_data_3des[16];
extern uint8_t key_data_aes[16];
extern const uint8_t key_data_aes_version;
void mifare_desfire_auto_authenticate (MifareTag tag, uint8_t key_no);

View file

@ -0,0 +1,91 @@
/*-
* Copyright (C) 2010, Romain Tartiere.
*
* 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 <http://www.gnu.org/licenses/>
*
* $Id$
*/
#include <cutter.h>
#include <freefare.h>
#include "mifare_desfire_ev1_fixture.h"
static nfc_device_t *device = NULL;
static MifareTag *tags = NULL;
MifareTag tag = NULL;
void
cut_setup ()
{
int res;
nfc_device_desc_t devices[8];
size_t device_count;
nfc_list_devices (devices, 8, &device_count);
if (!device_count)
cut_omit ("No device found");
for (size_t i = 0; i < device_count; i++) {
device = nfc_connect (&(devices[i]));
if (!device)
cut_omit ("nfc_connect() failed");
tags = freefare_get_tags (device);
cut_assert_not_null (tags, cut_message ("freefare_get_tags() failed"));
tag = NULL;
for (int i=0; tags[i]; i++) {
if (freefare_get_tag_type(tags[i]) == DESFIRE) {
tag = tags[i];
res = mifare_desfire_connect (tag);
cut_assert_equal_int (0, res, cut_message ("mifare_desfire_connect() failed"));
struct mifare_desfire_version_info version_info;
res = mifare_desfire_get_version (tag, &version_info);
cut_assert_equal_int (0, res, cut_message ("mifare_desfire_get_version"));
if ((version_info.hardware.version_major >= 1) &&
(version_info.software.version_major >= 1)) {
return;
}
mifare_desfire_disconnect (tag);
}
}
nfc_disconnect (device);
device = NULL;
freefare_free_tags (tags);
tags = NULL;
}
cut_omit ("No MIFARE DESFire EV1 tag on NFC device");
}
void
cut_teardown ()
{
if (tag)
mifare_desfire_disconnect (tag);
if (tags) {
freefare_free_tags (tags);
tags = NULL;
}
if (device)
nfc_disconnect (device);
}

View file

@ -0,0 +1,20 @@
/*-
* Copyright (C) 2010, Romain Tartiere.
*
* 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 <http://www.gnu.org/licenses/>
*
* $Id$
*/
extern MifareTag tag;

View file

@ -889,6 +889,13 @@ test_mifare_desfire_des_macing (void)
mifare_desfire_auto_authenticate (tag, 0);
MifareDESFireKey key = mifare_desfire_des_key_new_with_version (key_data_null);
res = mifare_desfire_change_key (tag, 0, key, NULL);
cut_assert_success ("mifare_desfire_change_key()");
res = mifare_desfire_authenticate (tag, 0, key);
cut_assert_success ("mifare_desfire_authenticate()");
MifareDESFireAID aid = mifare_desfire_aid_new (0x00123456);
res = mifare_desfire_create_application (tag, aid, 0xFF, 1);
cut_assert_success ("mifare_desfire_create_application()");
@ -897,7 +904,6 @@ test_mifare_desfire_des_macing (void)
cut_assert_success ("mifare_desfire_select_application");
free (aid);
MifareDESFireKey key = mifare_desfire_des_key_new_with_version (key_data_null);
res = mifare_desfire_authenticate (tag, 0, key);
cut_assert_success ("mifare_desfire_authenticate()");

View file

@ -0,0 +1,201 @@
/*-
* 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 <http://www.gnu.org/licenses/>
*
* $Id$
*/
/*
* These tests implement examples provided in the following documents:
*
* NIST Special Publication 800-38B
* Recommendation for Block Cipher Modes of Operation: The CMAC Mode for Authentication
* May 2005
*/
#include <cutter.h>
#include <strings.h>
#include <freefare.h>
#include "freefare_internal.h"
uint8_t key_data[] = {
0x2b, 0x7e, 0x15, 0x16,
0x28, 0xae, 0xd2, 0xa6,
0xab, 0xf7, 0x15, 0x88,
0x09, 0xcf, 0x4f, 0x3c
};
void
test_mifare_desfire_aes_generate_subkeys (void)
{
uint8_t sk1[] = {
0xfb, 0xee, 0xd6, 0x18,
0x35, 0x71, 0x33, 0x66,
0x7c, 0x85, 0xe0, 0x8f,
0x72, 0x36, 0xa8, 0xde
};
uint8_t sk2[] = {
0xf7, 0xdd, 0xac, 0x30,
0x6a, 0xe2, 0x66, 0xcc,
0xf9, 0x0b, 0xc1, 0x1e,
0xe4, 0x6d, 0x51, 0x3b
};
MifareDESFireKey key = mifare_desfire_aes_key_new (key_data);
cmac_generate_subkeys (key);
cut_assert_equal_memory (sk1, 16, key->aes_sk1, 16, cut_message ("Wrong sub-key 1"));
cut_assert_equal_memory (sk2, 16, key->aes_sk2, 16, cut_message ("Wrong sub-key 2"));
mifare_desfire_key_free (key);
}
void
test_mifare_desfire_aes_cmac_empty (void)
{
MifareDESFireKey key = mifare_desfire_aes_key_new (key_data);
cmac_generate_subkeys (key);
uint8_t ivect[16];
bzero (ivect, sizeof (ivect));
uint8_t expected_cmac[] = {
0xbb, 0x1d, 0x69, 0x29,
0xe9, 0x59, 0x37, 0x28,
0x7f, 0xa3, 0x7d, 0x12,
0x9b, 0x75, 0x67, 0x46
};
uint8_t my_cmac[16];
cmac (key, ivect, NULL, 0, my_cmac);
cut_assert_equal_memory (expected_cmac, 16, my_cmac, 16, cut_message ("Wrong CMAC"));
mifare_desfire_key_free (key);
}
void
test_mifare_desfire_aes_cmac_128 (void)
{
MifareDESFireKey key = mifare_desfire_aes_key_new (key_data);
cmac_generate_subkeys (key);
uint8_t ivect[16];
bzero (ivect, sizeof (ivect));
uint8_t message[] = {
0x6b, 0xc1, 0xbe, 0xe2,
0x2e, 0x40, 0x9f, 0x96,
0xe9, 0x3d, 0x7e, 0x11,
0x73, 0x93, 0x17, 0x2a
};
uint8_t expected_cmac[] = {
0x07, 0x0a, 0x16, 0xb4,
0x6b, 0x4d, 0x41, 0x44,
0xf7, 0x9b, 0xdd, 0x9d,
0xd0, 0x4a, 0x28, 0x7c
};
uint8_t my_cmac[16];
cmac (key, ivect, message, 16, my_cmac);
cut_assert_equal_memory (expected_cmac, 16, my_cmac, sizeof (message), cut_message ("Wrong CMAC"));
mifare_desfire_key_free (key);
}
void
test_mifare_desfire_aes_cmac_320 (void)
{
MifareDESFireKey key = mifare_desfire_aes_key_new (key_data);
cmac_generate_subkeys (key);
uint8_t ivect[16];
bzero (ivect, sizeof (ivect));
uint8_t message[] = {
0x6b, 0xc1, 0xbe, 0xe2,
0x2e, 0x40, 0x9f, 0x96,
0xe9, 0x3d, 0x7e, 0x11,
0x73, 0x93, 0x17, 0x2a,
0xae, 0x2d, 0x8a, 0x57,
0x1e, 0x03, 0xac, 0x9c,
0x9e, 0xb7, 0x6f, 0xac,
0x45, 0xaf, 0x8e, 0x51,
0x30, 0xc8, 0x1c, 0x46,
0xa3, 0x5c, 0xe4, 0x11
};
uint8_t expected_cmac[] = {
0xdf, 0xa6, 0x67, 0x47,
0xde, 0x9a, 0xe6, 0x30,
0x30, 0xca, 0x32, 0x61,
0x14, 0x97, 0xc8, 0x27
};
uint8_t my_cmac[16];
cmac (key, ivect, message, sizeof (message), my_cmac);
cut_assert_equal_memory (expected_cmac, 16, my_cmac, 16, cut_message ("Wrong CMAC"));
mifare_desfire_key_free (key);
}
void
test_mifare_desfire_aes_cmac_512 (void)
{
MifareDESFireKey key = mifare_desfire_aes_key_new (key_data);
cmac_generate_subkeys (key);
uint8_t ivect[16];
bzero (ivect, sizeof (ivect));
uint8_t message[] = {
0x6b, 0xc1, 0xbe, 0xe2,
0x2e, 0x40, 0x9f, 0x96,
0xe9, 0x3d, 0x7e, 0x11,
0x73, 0x93, 0x17, 0x2a,
0xae, 0x2d, 0x8a, 0x57,
0x1e, 0x03, 0xac, 0x9c,
0x9e, 0xb7, 0x6f, 0xac,
0x45, 0xaf, 0x8e, 0x51,
0x30, 0xc8, 0x1c, 0x46,
0xa3, 0x5c, 0xe4, 0x11,
0xe5, 0xfb, 0xc1, 0x19,
0x1a, 0x0a, 0x52, 0xef,
0xf6, 0x9f, 0x24, 0x45,
0xdf, 0x4f, 0x9b, 0x17,
0xad, 0x2b, 0x41, 0x7b,
0xe6, 0x6c, 0x37, 0x10
};
uint8_t expected_cmac[] = {
0x51, 0xf0, 0xbe, 0xbf,
0x7e, 0x3b, 0x9d, 0x92,
0xfc, 0x49, 0x74, 0x17,
0x79, 0x36, 0x3c, 0xfe
};
uint8_t my_cmac[16];
cmac (key, ivect, message, sizeof (message), my_cmac);
cut_assert_equal_memory (expected_cmac, 16, my_cmac, 16, cut_message ("Wrong CMAC"));
mifare_desfire_key_free (key);
}

View file

@ -0,0 +1,151 @@
/*-
* Copyright (C) 2010, Romain Tartiere.
*
* 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 <http://www.gnu.org/licenses/>
*
* $Id$
*/
#include <cutter.h>
#include <errno.h>
#include <string.h>
#include <strings.h>
#include <freefare.h>
#include "freefare_internal.h"
#include "mifare_desfire_ev1_fixture.h"
#include "common/mifare_desfire_auto_authenticate.h"
#define cut_assert_success(last_command) \
do { \
if ((res < 0) || (MIFARE_DESFIRE (tag)->last_picc_error != OPERATION_OK)) { \
cut_fail ("%s returned %d, error: %s, errno: %s\n", last_command, res, mifare_desfire_error_lookup (MIFARE_DESFIRE (tag)->last_picc_error), strerror (errno)); \
} \
} while (0)
void
test_mifare_desfire_ev1_aes2 (void)
{
int res;
MifareDESFireKey key;
mifare_desfire_auto_authenticate (tag, 0);
// Setup the AES key
key = mifare_desfire_aes_key_new_with_version (key_data_aes, key_data_aes_version);
res = mifare_desfire_change_key (tag, 0x80, key, NULL);
cut_assert_success ("mifare_desfire_change_key");
mifare_desfire_key_free (key);
// Authenticate with the AES key
key = mifare_desfire_aes_key_new_with_version (key_data_aes, key_data_aes_version);
res = mifare_desfire_authenticate_aes (tag, 0, key);
cut_assert_success ("mifare_desfire_authenticate");
mifare_desfire_key_free (key);
res = mifare_desfire_format_picc (tag);
cut_assert_success ("mifare_desfire_format_picc()");
key = mifare_desfire_aes_key_new_with_version (key_data_aes, key_data_aes_version);
res = mifare_desfire_authenticate_aes (tag, 0, key);
cut_assert_success ("mifare_desfire_authenticate");
mifare_desfire_key_free (key);
uint32_t size;
res = mifare_desfire_free_mem (tag, &size);
cut_assert_success ("mifare_desfire_free_mem");
// Do some commands to check CMAC is properly handled
res = mifare_desfire_free_mem (tag, &size);
cut_assert_success ("mifare_desfire_free_mem");
struct mifare_desfire_version_info info;
res = mifare_desfire_get_version (tag, &info);
cut_assert_success ("mifare_desfire_get_version");
res = mifare_desfire_change_key_settings (tag, 0x0F);
cut_assert_success ("mifare_desfire_change_key_settings");
res = mifare_desfire_free_mem (tag, &size);
cut_assert_success ("mifare_desfire_free_mem");
MifareDESFireAID aid = mifare_desfire_aid_new (0x112233);
mifare_desfire_delete_application (tag, aid);
res = mifare_desfire_create_application (tag, aid, 0xff, 0x81);
cut_assert_success ("mifare_desfire_create_application");
res = mifare_desfire_select_application (tag, aid);
cut_assert_success ("mifare_desfire_select_application");
key = mifare_desfire_aes_key_new (key_data_aes);
res = mifare_desfire_authenticate_aes (tag, 0, key);
cut_assert_success ("mifare_desfire_authenticate");
free (key);
key = mifare_desfire_aes_key_new_with_version (key_data_aes, key_data_aes_version);
res = mifare_desfire_change_key (tag, 0x00, key, NULL);
cut_assert_success ("mifare_desfire_change_key");
mifare_desfire_key_free (key);
key = mifare_desfire_aes_key_new (key_data_aes);
res = mifare_desfire_authenticate_aes (tag, 0, key);
cut_assert_success ("mifare_desfire_authenticate");
free (key);
res = mifare_desfire_create_std_data_file (tag, 1, MDCM_MACED, 0x0000, 512);
if ((mifare_desfire_last_picc_error (tag) != DUPLICATE_ERROR) && (mifare_desfire_last_picc_error(tag) != OPERATION_OK))
cut_assert_success ("mifare_desfire_create_std_data_file");
char sample_data[] = "Hello World! I'm a string that is probably too long "
"to feet in a single frame. For this reason, it will be split and like"
"ly, some failure in the algorirthm should trigger an error in this uni"
"t test.";
res = mifare_desfire_write_data_ex (tag, 1, 0, strlen (sample_data), sample_data, MDCM_MACED);
cut_assert_success ("mifare_desfire_write_data");
char buffer[1024];
res = mifare_desfire_read_data_ex (tag, 1, 0, 27, buffer, MDCM_MACED);
cut_assert_success ("mifare_desfire_read_data");
cut_assert_equal_memory (buffer, res, sample_data, 27, cut_message ("AES crypto failed"));
uint8_t s, c;
res = mifare_desfire_get_key_settings (tag, &s, &c);
cut_assert_success ("mifare_desfire_get__key_settings");
res = mifare_desfire_read_data_ex (tag, 1, 27, 27, buffer, MDCM_MACED);
cut_assert_success ("mifare_desfire_read_data");
cut_assert_equal_memory (buffer, res, sample_data + 27, 27, cut_message ("AES crypto failed"));
res = mifare_desfire_read_data_ex (tag, 1, 0, 0, buffer, MDCM_MACED);
cut_assert_success ("mifare_desfire_read_data");
cut_assert_equal_memory (buffer, strlen (buffer), sample_data, strlen (sample_data), cut_message ("AES crypto failed"));
// Revert to the default DES key
res = mifare_desfire_select_application (tag, NULL);
cut_assert_success ("mifare_desfire_select_application");
key = mifare_desfire_aes_key_new_with_version (key_data_aes, key_data_aes_version);
res = mifare_desfire_authenticate_aes (tag, 0, key);
cut_assert_success ("mifare_desfire_authenticate");
mifare_desfire_key_free (key);
key = mifare_desfire_des_key_new (key_data_null);
res = mifare_desfire_change_key (tag, 0x00, key, NULL);
cut_assert_success ("mifare_desfire_change_key");
mifare_desfire_key_free (key);
}

View file

@ -0,0 +1,822 @@
/*-
* Copyright (C) 2010, Romain Tartiere.
*
* 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 <http://www.gnu.org/licenses/>
*
* $Id$
*/
#include <cutter.h>
#include <errno.h>
#include <string.h>
#include <strings.h>
#include <freefare.h>
#include "freefare_internal.h"
#include "mifare_desfire_ev1_fixture.h"
#include "common/mifare_desfire_auto_authenticate.h"
#define cut_assert_success(last_command) \
do { \
if ((res < 0) || (mifare_desfire_last_picc_error (tag) != OPERATION_OK)) { \
cut_fail ("%s returned %d, error: %s, errno: %s\n", last_command, res, mifare_desfire_error_lookup (mifare_desfire_last_picc_error (tag)), strerror (errno)); \
} \
} while (0)
void
test_mifare_desfire_ev1_aes (void)
{
int res;
/* Select the master application */
res = mifare_desfire_select_application (tag, NULL);
cut_assert_success ("mifare_desfire_select_application()");
/* Get version information */
struct mifare_desfire_version_info version_info;
res = mifare_desfire_get_version (tag, &version_info);
cut_assert_success ("mifare_desfire_get_version()");
mifare_desfire_auto_authenticate (tag, 0);
/*
* This unit test change key settings to more restrictive ones, so reset
* them to factory defaults in case the previous run failed unexpectedly.
*/
res = mifare_desfire_change_key_settings (tag, 0xF);
cut_assert_success ("mifare_desfire_change_key_settings()");
res = mifare_desfire_change_key_settings (tag, 0xF);
cut_assert_success ("mifare_desfire_change_key_settings()");
/* Change master key to AES */
MifareDESFireKey key = mifare_desfire_aes_key_new_with_version (key_data_aes, key_data_aes_version);
mifare_desfire_change_key (tag, 0, key, NULL);
cut_assert_success ("mifare_desfire_change_key()");
uint8_t key_version;
// res = mifare_desfire_get_key_version (tag, 0, &key_version);
// cut_assert_success ("mifare_desfire_get_key_version()");
// cut_assert_equal_int (key_data_aes_version, key_version, cut_message ("Wrong key_version value."));
res = mifare_desfire_authenticate_aes (tag, 0, key);
cut_assert_success ("mifare_desfire_authenticate_aes()");
mifare_desfire_key_free (key);
/* Wipeout the card */
res = mifare_desfire_format_picc (tag);
cut_assert_success ("mifare_desfire_format_picc()");
/* Create 3 applications */
res = mifare_desfire_select_application (tag, NULL);
cut_assert_success ("mifare_desfire_select_application()");
MifareDESFireAID aid_a = mifare_desfire_aid_new (0x00AAAAAA);
cut_assert_not_null (aid_a, cut_message ("Cannot allocate AID"));
res = mifare_desfire_create_application (tag, aid_a, 0xFF, 0);
cut_assert_success ("mifare_desfire_create_application()");
MifareDESFireAID aid_b = mifare_desfire_aid_new (0x00BBBBBB);
cut_assert_not_null (aid_b, cut_message ("Cannot allocate AID"));
res = mifare_desfire_create_application (tag, aid_b, 0xEF, 0x80 | 6);
cut_assert_success ("mifare_desfire_create_application()");
MifareDESFireAID aid_c = mifare_desfire_aid_new (0x00CCCCCC);
cut_assert_not_null (aid_c, cut_message ("Cannot allocate AID"));
res = mifare_desfire_create_application (tag, aid_c, 0xC2, 0x80 | 14);
cut_assert_success ("mifare_desfire_create_application()");
// Ensure we can find the created applications
MifareDESFireAID *aids = NULL;
size_t aid_count;
res = mifare_desfire_get_application_ids (tag, &aids, &aid_count);
cut_assert_success ("mifare_desfire_get_application_ids()");
cut_assert_equal_int (3, aid_count, cut_message ("Wrong application count"));
mifare_desfire_free_application_ids (aids);
// Create files in the application A
res = mifare_desfire_select_application (tag, aid_a);
cut_assert_success ("mifare_desfire_select_application()");
uint8_t std_data_file_id = 15;
res = mifare_desfire_create_std_data_file (tag, std_data_file_id, MDCM_PLAIN, 0xEEEE, 100);
cut_assert_success ("mifare_desfire_create_std_data_file()");
res = mifare_desfire_create_backup_data_file (tag, 5, MDCM_PLAIN, 0xEEEE, 64);
cut_assert_success ("mifare_desfire_create_backup_data_file()");
res = mifare_desfire_create_value_file (tag, 4, MDCM_PLAIN, 0xEEEE, 0, 1000, 0, 0);
cut_assert_success ("mifare_desfire_create_value_file()");
res = mifare_desfire_create_cyclic_record_file (tag, 0, MDCM_PLAIN, 0xEEEE, 4, 10);
cut_assert_success ("mifare_desfire_create_cyclic_record_file()");
// Write some data in the standard data file
res = mifare_desfire_write_data (tag, std_data_file_id, 0, 30, (uint8_t *)"Some data to write to the card");
cut_assert_success ("mifare_desfire_write_data()");
cut_assert_equal_int (30, res, cut_message ("Wrong number of bytes writen"));
res = mifare_desfire_write_data (tag, std_data_file_id, 34, 22, (uint8_t *)"Another block of data.");
cut_assert_success ("mifare_desfire_write_data()");
cut_assert_equal_int (22, res, cut_message ("Wrong number of bytes writen"));
// Make the file read-only
res = mifare_desfire_change_file_settings (tag, std_data_file_id, MDCM_PLAIN, 0xEFFF);
cut_assert_success ("mifare_desfire_change_file_settings()");
// Read a part of the file
uint8_t buffer[120];
res = mifare_desfire_read_data (tag, std_data_file_id, 10, 50, &buffer);
cut_assert_success ("mifare_desfire_read_data()");
cut_assert_equal_int (50, res, cut_message ("Wrong number of bytes read"));
cut_assert_equal_memory ("to write to the card\0\0\0\0Another block of data.\0\0\0\0", 50, buffer, 50, cut_message ("Wrong data"));
// Read all the file at once
res = mifare_desfire_read_data (tag, std_data_file_id, 0, 0, &buffer);
cut_assert_success ("mifare_desfire_read_data()");
cut_assert_equal_int (100, res, cut_message ("Wrong number of bytes read"));
cut_assert_equal_memory ("Some data to write to the"
" card\0\0\0\0Another block of"
" data.\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 100, buffer, 100, cut_message ("Wrong data"));
// Try to overwrute the file
res = mifare_desfire_write_data (tag, std_data_file_id, 20, 5, (char *)"Test!");
cut_assert_equal_int (-1, res, cut_message ("Wrong return value"));
cut_assert_equal_int (PERMISSION_ERROR, mifare_desfire_last_picc_error (tag), cut_message ("Wrong PICC error"));
int32_t expected_value = 0;
for (int transaction = 0; transaction < 15; transaction++) {
char data_buffer[3];
sprintf (data_buffer, "%02d", transaction);
// Write to the backup file
res = mifare_desfire_write_data (tag, 5, 3*transaction, 3, data_buffer);
cut_assert_success ("mifare_desfire_write_data()");
// Manipulate the value file
res = mifare_desfire_credit (tag, 4, 100);
cut_assert_success ("mifare_desfire_credit()");
res = mifare_desfire_debit (tag, 4, 97);
cut_assert_success ("mifare_desfire_debit()");
// Write to the cyclic record file
res = mifare_desfire_write_record (tag, 0, 2, 2, data_buffer);
cut_assert_success ("mifare_desfire_write_record()");
// Overwrite the cyclic record file
res = mifare_desfire_write_record (tag, 0, 0, 2, (char *)"r.");
cut_assert_success("mifare_desfire_write_record()");
// Ensure that no content was changed yet
char ref_buffer[64];
bzero (ref_buffer, sizeof (ref_buffer));
for (int n = 0; n < transaction; n++) {
sprintf (ref_buffer + 3 * n, "%02d", n);
}
res = mifare_desfire_read_data (tag, 5, 0, 0, buffer);
cut_assert_success ("mifare_desfire_read_data()");
cut_assert_equal_int (64, res, cut_message ("Wrong number of bytes read"));
cut_assert_equal_memory (buffer, 64, ref_buffer, 64, cut_message ("Wrong data"));
int32_t value;
res = mifare_desfire_get_value (tag, 4, &value);
cut_assert_success ("mifare_desfire_get_value()");
cut_assert_equal_int (expected_value, value, cut_message ("Wrong value"));
// Reading records from an empty file would abort the transaction
if (0 != transaction) {
// Get the latest record
res = mifare_desfire_read_records (tag, 0, 0, 1, buffer);
cut_assert_success ("mifare_desfire_read_records()");
sprintf (ref_buffer, "r.%02d", transaction);
cut_assert_not_equal_memory (ref_buffer, 4, buffer, res, cut_message ("Wrong data"));
}
// Commit !
res = mifare_desfire_commit_transaction (tag);
cut_assert_success ("mifare_desfire_commit_transaction()");
res = mifare_desfire_read_data (tag, 5, 3*transaction, 3, buffer);
cut_assert_success ("mifare_desfire_read_data()");
cut_assert_equal_memory (data_buffer, 3, buffer, res, cut_message ("Wrong data"));
expected_value += 3;
res = mifare_desfire_get_value (tag, 4, &value);
cut_assert_success ("mifare_desfire_get_value()");
cut_assert_equal_int (expected_value, value, cut_message ("Wrong value"));
res = mifare_desfire_read_records (tag, 0, 0, 1, buffer);
cut_assert_success ("mifare_desfire_read_records()");
sprintf (ref_buffer, "r.%02d", transaction);
cut_assert_equal_memory (ref_buffer, 4, buffer, res, cut_message ("Wrong data"));
}
// Ensure limited credit is disabled
res = mifare_desfire_limited_credit (tag, 4, 20);
cut_assert_equal_int (-1, res, cut_message ("mifare_desfire_limited_credit() should fail"));
// Get all files
uint8_t *files;
size_t file_count;
res = mifare_desfire_get_file_ids (tag, &files, &file_count);
cut_assert_success ("mifare_desfire_get_file_ids()");
cut_assert_equal_int (4, file_count, cut_message ("Wrong number of files"));
for (size_t i=0; i<file_count;i++) {
if ((files[i] != 0) && (files[i] != 4) &&
(files[i] != 5) && (files[i] != 15)) {
cut_fail ("File %d should not exist.", files[i]);
}
struct mifare_desfire_file_settings settings;
res = mifare_desfire_get_file_settings (tag, files[i], &settings);
cut_assert_success ("mifare_desfire_get_file_settings()");
switch (files[i]) {
case 0:
cut_assert_equal_int (MDFT_CYCLIC_RECORD_FILE_WITH_BACKUP, settings.file_type, cut_message ("Wrong file type"));
cut_assert_equal_int (MDCM_PLAIN, settings.communication_settings, cut_message ("Wrong communication settings"));
cut_assert_equal_int (4, settings.settings.linear_record_file.record_size, cut_message ("Wrong record size"));
cut_assert_equal_int (10, settings.settings.linear_record_file.max_number_of_records, cut_message ("Wrong max number of records"));
cut_assert_equal_int (9, settings.settings.linear_record_file.current_number_of_records, cut_message ("Wrong current number of records"));
break;
case 4:
cut_assert_equal_int (MDFT_VALUE_FILE_WITH_BACKUP, settings.file_type, cut_message ("Wrong file type"));
cut_assert_equal_int (MDCM_PLAIN, settings.communication_settings, cut_message ("Wrong communication settings"));
cut_assert_equal_int (0, settings.settings.value_file.lower_limit, cut_message ("Wrong lower limit"));
cut_assert_equal_int (1000, settings.settings.value_file.upper_limit, cut_message ("Wrong upper limit"));
cut_assert_equal_int (97, settings.settings.value_file.limited_credit_value, cut_message ("Wrong limited_credit value"));
cut_assert_equal_int (0, settings.settings.value_file.limited_credit_enabled, cut_message ("Wrong limited_credit enable state"));
break;
case 5:
cut_assert_equal_int (MDFT_BACKUP_DATA_FILE, settings.file_type, cut_message ("Wrong file type"));
cut_assert_equal_int (MDCM_PLAIN, settings.communication_settings, cut_message ("Wrong communication settings"));
cut_assert_equal_int (64, settings.settings.standard_file.file_size, cut_message ("Wrong file size"));
break;
case 15:
cut_assert_equal_int (MDFT_STANDARD_DATA_FILE, settings.file_type, cut_message ("Wrong file type"));
cut_assert_equal_int (MDCM_PLAIN, settings.communication_settings, cut_message ("Wrong communication settings"));
cut_assert_equal_int (100, settings.settings.standard_file.file_size, cut_message ("Wrong file size"));
break;
default:
cut_fail ("Wow! Cosmic ray!");
}
res = mifare_desfire_delete_file (tag, files[i]);
cut_assert_success ("mifare_desfire_delete_file()");
}
free (files);
// All files should have been removed
res = mifare_desfire_get_file_ids (tag, &files, &file_count);
cut_assert_success ("mifare_desfire_get_file_ids()");
cut_assert_equal_int (0, file_count, cut_message ("Wrong number of files"));
// Delete application A
res = mifare_desfire_select_application (tag, 0);
cut_assert_success ("mifare_desfire_select_application()");
key = mifare_desfire_aes_key_new (key_data_aes);
res = mifare_desfire_authenticate_aes (tag, 0, key);
cut_assert_success ("mifare_desfire_authenticate()");
mifare_desfire_key_free (key);
res = mifare_desfire_delete_application (tag, aid_a);
cut_assert_success ("mifare_desfire_delete_application()");
// Ensure application A was deleted
res = mifare_desfire_get_application_ids (tag, &aids, &aid_count);
cut_assert_success ("mifare_desfire_get_application_ids()");
cut_assert_equal_int (2, aid_count, cut_message ("Wrong application count"));
mifare_desfire_free_application_ids (aids);
// Change application B keys
res = mifare_desfire_select_application (tag, aid_b);
cut_assert_success ("mifare_desfire_select_application()");
key = mifare_desfire_aes_key_new (key_data_aes);
res = mifare_desfire_authenticate_aes (tag, 0, key);
cut_assert_success ("mifare_desfire_authenticate_aes()");
mifare_desfire_key_free (key);
// Use an AES application master key
key = mifare_desfire_aes_key_new_with_version ((uint8_t *) "App.B Master Key", 0x83);
res = mifare_desfire_change_key (tag, 0, key, NULL);
cut_assert_success ("mifare_desfire_change_key()");
mifare_desfire_key_free (key);
/* Authenticate with the new master key */
key = mifare_desfire_aes_key_new ((uint8_t *) "App.B Master Key");
res = mifare_desfire_authenticate_aes (tag, 0, key);
cut_assert_success ("mifare_desfire_authenticate()");
mifare_desfire_key_free (key);
res = mifare_desfire_get_key_version (tag, 0, &key_version);
cut_assert_success ("mifare_desfire_get_key_version()");
cut_assert_equal_int (0x83, key_version, cut_message ("Wrong key version"));
/* Change key #1 */
key = mifare_desfire_aes_key_new (key_data_aes);
res = mifare_desfire_authenticate_aes (tag, 1, key);
cut_assert_success ("mifare_desfire_authenticate()");
mifare_desfire_key_free (key);
key = mifare_desfire_aes_key_new ((uint8_t *) "Another AES key!");
res = mifare_desfire_change_key (tag, 1, key, NULL);
cut_assert_success ("mifare_desfire_change_key()");
mifare_desfire_key_free (key);
/* Change key #5 */
key = mifare_desfire_aes_key_new (key_data_aes);
res = mifare_desfire_authenticate_aes (tag, 5, key);
cut_assert_success ("mifare_desfire_authenticate()");
mifare_desfire_key_free (key);
key = mifare_desfire_aes_key_new ((uint8_t *) "B's Chg Keys Key");
res = mifare_desfire_change_key (tag, 5, key, NULL);
cut_assert_success ("mifare_desfire_change_key()");
mifare_desfire_key_free (key);
/* Set key #5 as the change key */
key = mifare_desfire_aes_key_new ((uint8_t *) "App.B Master Key");
res = mifare_desfire_authenticate_aes (tag, 0, key);
cut_assert_success ("mifare_desfire_authenticate_aes()");
mifare_desfire_key_free (key);
res = mifare_desfire_change_key_settings (tag, 0x5F);
cut_assert_success ("mifare_desfire_change_key_settings()");
uint8_t key_settings;
uint8_t max_keys;
res = mifare_desfire_get_key_settings (tag, &key_settings, &max_keys);
cut_assert_success ("mifare_desfire_get_key_settings()");
cut_assert_equal_int (0x5F, key_settings, cut_message ("Wrong key settings"));
cut_assert_equal_int (6, max_keys, cut_message ("Wrong maximum number of keys"));
/* Change key #1 to #4 using the three key procedure. */
key = mifare_desfire_aes_key_new ((uint8_t *) "B's Chg Keys Key");
res = mifare_desfire_authenticate_aes (tag, 5, key);
cut_assert_success ("mifare_desfire_authenticate_aes()");
mifare_desfire_key_free (key);
key = mifare_desfire_aes_key_new ((uint8_t *)"App.B Key #1. ");
MifareDESFireKey key1 = mifare_desfire_aes_key_new ((uint8_t *) "Another AES key!");
res = mifare_desfire_change_key (tag, 1, key, key1);
cut_assert_success ("mifare_desfire_change_key()");
mifare_desfire_key_free (key);
mifare_desfire_key_free (key1);
key = mifare_desfire_aes_key_new ((uint8_t *)"App.B Key #2.. ");
res = mifare_desfire_change_key (tag, 2, key, NULL);
cut_assert_success ("mifare_desfire_change_key()");
mifare_desfire_key_free (key);
key = mifare_desfire_aes_key_new ((uint8_t *)"App.B Key #3... ");
res = mifare_desfire_change_key (tag, 3, key, NULL);
cut_assert_success ("mifare_desfire_change_key()");
mifare_desfire_key_free (key);
key = mifare_desfire_aes_key_new ((uint8_t *)"App.B Key #4....");
res = mifare_desfire_change_key (tag, 4, key, NULL);
cut_assert_success ("mifare_desfire_change_key()");
mifare_desfire_key_free (key);
std_data_file_id--;
res = mifare_desfire_create_std_data_file (tag, std_data_file_id, MDCM_PLAIN, 0x1234, 100);
cut_assert_success ("mifare_desfire_create_std_data_file()");
expected_value = -1000000;
res = mifare_desfire_create_value_file (tag, 4, 0, 0x1324, -987654321, -1000, expected_value, 1);
cut_assert_success ("mifare_desfire_create_value_file()");
res = mifare_desfire_create_linear_record_file (tag, 1, 0, 0x1324, 25, 4);
int nr = 0;
for (int transaction = 0; transaction < 7; transaction++) {
uint8_t cs = transaction % 3;
if (cs == 2) cs++;
key = mifare_desfire_aes_key_new ((uint8_t *) "App.B Key #4....");
res = mifare_desfire_authenticate_aes (tag, 4, key);
cut_assert_success ("mifare_desfire_authenticate_aes()");
mifare_desfire_key_free (key);
res = mifare_desfire_change_file_settings (tag, std_data_file_id, cs, 0x1234);
cut_assert_success ("mifare_desfire_change_file_settings()");
res = mifare_desfire_change_file_settings (tag, 4, cs, 0x1324);
cut_assert_success ("mifare_desfire_change_file_settings()");
res = mifare_desfire_change_file_settings (tag, 1, cs, 0x1324);
cut_assert_success ("mifare_desfire_change_file_settings()");
// Authenticate witht he write key
key = mifare_desfire_aes_key_new ((uint8_t *) "App.B Key #2.. ");
res = mifare_desfire_authenticate_aes (tag, 2, key);
cut_assert_success ("mifare_desfire_authenticate_aes()");
mifare_desfire_key_free (key);
char data_buffer[100];
char data_buffer2[100];
char data_buffer3[100];
for (int i = 0; i < 100; i++)
data_buffer[i] = transaction + i;
res = mifare_desfire_write_data (tag, std_data_file_id, 0, 100, data_buffer);
cut_assert_success ("mifare_desfire_write_data()");
sprintf (data_buffer2, "Transaction #%d", transaction);
res = mifare_desfire_write_data (tag, std_data_file_id, 5, strlen (data_buffer2), data_buffer2);
cut_assert_success ("mifare_desfire_write_data()");
memcpy (data_buffer + 5, data_buffer2, strlen (data_buffer2));
// Write to the linear record. When it's full, erase it and restart.
for (int i = 0; i < 2; i++) {
if ((transaction % 2 == 1) && (i == 1)) {
res = mifare_desfire_clear_record_file (tag, 1);
cut_assert_success ("mifare_desfire_clear_record_file()");
sprintf (data_buffer3, "Test invalid write");
res = mifare_desfire_write_record (tag, 1, 0, strlen (data_buffer3), data_buffer3);
cut_assert_equal_int (-1, res, cut_message ("error code"));
cut_assert_equal_int (PERMISSION_ERROR, mifare_desfire_last_picc_error (tag), cut_message ("PICC error"));
// The previous failure has aborted the transaction, so
// re-authenticate, then clear record again.
key = mifare_desfire_aes_key_new ((uint8_t *) "App.B Key #2.. ");
res = mifare_desfire_authenticate_aes (tag, 2, key);
cut_assert_success ("mifare_desfire_authenticate_aes");
mifare_desfire_key_free (key);
res = mifare_desfire_clear_record_file (tag, 1);
cut_assert_success ("mifare_desfire_clear_record_file()");
res = mifare_desfire_commit_transaction (tag);
cut_assert_success ("mifare_desfire_commit_transaction()");
nr = 0;
}
res = mifare_desfire_write_record (tag, 1, 0, 25, "0123456789012345678901234");
cut_assert_success ("mifare_desfire_write_record()");
res = mifare_desfire_write_record (tag, 1, 5, strlen (data_buffer2), data_buffer2);
cut_assert_success ("mifare_desfire_write_record()");
}
nr++;
// Modify the value file
res = mifare_desfire_debit (tag, 4, 1300);
cut_assert_success ("mifare_desfire_debit()");
expected_value -= 1300;
res = mifare_desfire_credit (tag, 4, 20);
cut_assert_success ("mifare_desfire_credit()");
expected_value += 20;
res = mifare_desfire_debit (tag, 4, 1700);
cut_assert_success ("mifare_desfire_debit()");
expected_value -= 1700;
// Commit
res = mifare_desfire_commit_transaction (tag);
cut_assert_success ("mifare_desfire_commit_transaction()");
// Refund the whole debited amount
res = mifare_desfire_limited_credit (tag, 4, 3000);
cut_assert_success ("mifare_desfire_limited_credit()");
expected_value += 3000;
// Commit
res = mifare_desfire_commit_transaction (tag);
cut_assert_success ("mifare_desfire_commit_transaction()");
// Authenticate with the key that allows reading
key = mifare_desfire_aes_key_new ((uint8_t *) "App.B Key #1. ");
res = mifare_desfire_authenticate_aes (tag, 1, key);
cut_assert_success ("mifare_desfire_authenticate_aes()");
mifare_desfire_key_free (key);
// Read first half of the file
res = mifare_desfire_read_data (tag, std_data_file_id, 0, 50, data_buffer3);
cut_assert_success ("mifare_desfire_read_data()");
cut_assert_equal_int (50, res, cut_message ("length"));
cut_assert_equal_memory (data_buffer, 50, data_buffer3, res, cut_message ("data"));
// Read second half of the file
res = mifare_desfire_read_data (tag, std_data_file_id, 50, 0, data_buffer3);
cut_assert_success ("mifare_desfire_read_data()");
cut_assert_equal_int (50, res, cut_message ("length"));
cut_assert_equal_memory (data_buffer + 50, 50, data_buffer3, res, cut_message ("data"));
// Get the value file current balance
int32_t value;
res = mifare_desfire_get_value (tag, 4, &value);
cut_assert_success ("mifare_desfire_get_value()");
cut_assert_equal_int (expected_value, value, cut_message ("value"));
// Get the number of records in the linear record file
struct mifare_desfire_file_settings settings;
res = mifare_desfire_get_file_settings (tag, 1, &settings);
cut_assert_success ("mifare_desfire_get_file_settings()");
cut_assert_equal_int (MDFT_LINEAR_RECORD_FILE_WITH_BACKUP, settings.file_type, cut_message ("settings"));
cut_assert_equal_int (nr, settings.settings.linear_record_file.current_number_of_records, cut_message ("settings"));
// Read the oldest record
res = mifare_desfire_read_records (tag, 1, nr - 1, 1, data_buffer3);
cut_assert_success ("mifare_desfire_read_records()");
cut_assert_equal_int (25, res, cut_message ("length"));
sprintf (data_buffer, "0123456789012345678901234");
sprintf (data_buffer2, "Transaction #%d", transaction - nr + 1);
memcpy ((uint8_t *)data_buffer + 5, data_buffer2, strlen (data_buffer2));
cut_assert_equal_memory (data_buffer, strlen (data_buffer), data_buffer3, res, cut_message ("data"));
// Read all records
res = mifare_desfire_read_records (tag, 1, 0, 0, data_buffer3);
cut_assert_success ("mifare_desfire_read_records()");
cut_assert_equal_int (25 * nr, res, cut_message ("length"));
}
res = mifare_desfire_get_file_ids (tag, &files, &file_count);
cut_assert_success ("mifare_desfire_get_file_ids");
cut_assert_equal_int (3, file_count, cut_message ("count"));
for (size_t i = 0; i < file_count; i++) {
struct mifare_desfire_file_settings settings;
res = mifare_desfire_get_file_settings (tag, files[i], &settings);
cut_assert_success ("mifare_desfire_get_file_settings()");
switch (files[i]) {
case 1:
cut_assert_equal_int (MDFT_LINEAR_RECORD_FILE_WITH_BACKUP, settings.file_type, cut_message ("type"));
cut_assert_equal_int (MDCM_PLAIN, settings.communication_settings, cut_message ("cs"));
cut_assert_equal_int (0x1324, settings.access_rights, cut_message ("access_rights"));
cut_assert_equal_int (25, settings.settings.linear_record_file.record_size, cut_message ("record_size"));
cut_assert_equal_int (4, settings.settings.linear_record_file.max_number_of_records, cut_message ("max_number_of_records"));
cut_assert_equal_int (2, settings.settings.linear_record_file.current_number_of_records, cut_message ("current_number_of_records"));
break;
case 4:
cut_assert_equal_int (MDFT_VALUE_FILE_WITH_BACKUP , settings.file_type, cut_message ("type"));
cut_assert_equal_int (MDCM_PLAIN, settings.communication_settings, cut_message ("cs"));
cut_assert_equal_int (0x1324, settings.access_rights, cut_message ("access_rights"));
cut_assert_equal_int (-987654321, settings.settings.value_file.lower_limit, cut_message ("lower_limit"));
cut_assert_equal_int (-1000, settings.settings.value_file.upper_limit, cut_message ("upper_limit"));
cut_assert_equal_int (0, settings.settings.value_file.limited_credit_value, cut_message ("limited_credit_value"));
cut_assert_equal_int (1, settings.settings.value_file.limited_credit_enabled, cut_message ("limited_credit_enabled"));
break;
case 14: /* std_data_file_id */
cut_assert_equal_int (MDFT_STANDARD_DATA_FILE, settings.file_type, cut_message ("type"));
cut_assert_equal_int (MDCM_PLAIN, settings.communication_settings, cut_message ("cs"));
cut_assert_equal_int (0x1234, settings.access_rights, cut_message ("access_rights"));
cut_assert_equal_int (100, settings.settings.standard_file.file_size, cut_message ("size"));
break;
default:
cut_fail ("file_no");
}
res = mifare_desfire_delete_file (tag, files[i]);
cut_assert_success ("mifare_desfire_delete_file()");
}
free (files);
// Check there are no files anymore
res = mifare_desfire_get_file_ids (tag, &files, &file_count);
cut_assert_success ("mifare_desfire_get_file_ids");
cut_assert_equal_int (0, file_count, cut_message ("count"));
/* Delete application B */
key = mifare_desfire_aes_key_new ((uint8_t *) "App.B Master Key");
res = mifare_desfire_authenticate_aes (tag, 0, key);
cut_assert_success ("mifare_desfire_authenticate_aes()");
mifare_desfire_key_free (key);
res = mifare_desfire_delete_application (tag, aid_b);
cut_assert_success ("mifare_desfire_delete_application()");
res = mifare_desfire_get_application_ids (tag, &aids, &aid_count);
cut_assert_success ("mifare_desfire_get_application_ids()");
cut_assert_equal_int (1, aid_count, cut_message ("Wrong AID count"));
mifare_desfire_free_application_ids (aids);
/* Tests using application C */
res = mifare_desfire_select_application (tag, aid_c);
cut_assert_success ("mifare_desfire_select_application()");
key = mifare_desfire_aes_key_new (key_data_aes);
res = mifare_desfire_authenticate_aes (tag, 12, key);
cut_assert_success ("mifare_desfire_authenticate_aes()");
MifareDESFireKey new_key = mifare_desfire_aes_key_new ((uint8_t *)"App.C Key #1. ");
res = mifare_desfire_change_key (tag, 1, new_key, key);
cut_assert_success ("mifare_desfire_change_key()");
mifare_desfire_key_free (new_key);
new_key = mifare_desfire_aes_key_new ((uint8_t *)"App.C Key #2.. ");
res = mifare_desfire_change_key (tag, 2, new_key, key);
cut_assert_success ("mifare_desfire_change_key()");
mifare_desfire_key_free (new_key);
res = mifare_desfire_authenticate_aes (tag, 0, key);
cut_assert_success ("mifare_desfire_authenticate_aes()");
mifare_desfire_key_free (key);
res = mifare_desfire_create_cyclic_record_file (tag, 6, MDCM_PLAIN, 0x12E0, 100, 22);
cut_assert_success ("mifare_desfire_create_cyclic_record_file()");
for (int transaction = 0; transaction < 50; transaction++) {
char data_buffer[100];
char read_buffer[100];
uint8_t cs = transaction % 3;
if (cs == 2) cs++;
key = mifare_desfire_aes_key_new (key_data_aes);
res = mifare_desfire_authenticate_aes (tag, 0, key);
cut_assert_success ("mifare_desfire_authenticate_aes()");
mifare_desfire_key_free (key);
res = mifare_desfire_change_file_settings (tag, 6, cs, 0x12E0);
cut_assert_success ("mifare_desfire_change_file_settings()");
if (transaction & 4) {
key = mifare_desfire_aes_key_new ((uint8_t *) "App.C Key #2.. ");
res = mifare_desfire_authenticate_aes (tag, 2, key);
cut_assert_success ("mifare_desfire_authenticate_aes()");
mifare_desfire_key_free (key);
} else {
cs = 0;
}
memset (data_buffer, '_', 100);
data_buffer[0] = transaction;
data_buffer[99] = transaction;
sprintf (data_buffer + 5, " Transaction #%d ", transaction);
res = mifare_desfire_write_record (tag, 6, 0, 100, data_buffer);
cut_assert_success ("mifare_desfire_write_record()");
if (transaction & 4) {
key = mifare_desfire_aes_key_new ((uint8_t *) "App.C Key #1. ");
res = mifare_desfire_authenticate_aes (tag, 1, key);
cut_assert_success ("mifare_desfire_authenticate_aes()");
mifare_desfire_key_free (key);
}
if (transaction % 7 == 0) {
res = mifare_desfire_abort_transaction (tag);
cut_assert_success ("mifare_desfire_abort_transaction()");
ssize_t n = mifare_desfire_read_records (tag, 6, 0, 1, read_buffer);
if (transaction == 0) {
cut_assert_equal_int (-1, n, cut_message ("Wrong return value"));
} else {
cut_assert_equal_int (100, n, cut_message ("Wrong return value"));
cut_assert_not_equal_memory (data_buffer, sizeof (data_buffer), read_buffer, sizeof (read_buffer), cut_message ("Wrong data"));
}
} else {
res = mifare_desfire_commit_transaction (tag);
cut_assert_success ("mifare_desfire_commit_transaction()");
ssize_t n = mifare_desfire_read_records (tag, 6, 0, 1, read_buffer);
cut_assert_equal_int (100, n, cut_message ("Wrong return value"));
cut_assert_equal_memory (data_buffer, sizeof (data_buffer), read_buffer, sizeof (read_buffer), cut_message ("Wrong data"));
}
}
// Read each record
key = mifare_desfire_aes_key_new ((uint8_t *) "App.C Key #1. ");
res = mifare_desfire_authenticate_aes (tag, 1, key);
cut_assert_success ("mifare_desfire_authenticate_aes()");
mifare_desfire_key_free (key);
int t = 49;
for (int i = 0; i < 22; i++) {
char data_buffer[100];
char ref_data_buffer[100];
if (0 == (t % 7))
t--;
memset (ref_data_buffer, '_', 100);
ref_data_buffer[0] = t;
ref_data_buffer[99] = t;
sprintf (ref_data_buffer + 5, " Transaction #%d ", t);
res = mifare_desfire_read_records (tag, 6, i, 1, data_buffer);
if (i == 21) {
cut_assert_equal_int (-1, res, cut_message ("return value"));
} else {
cut_assert_success ("mifare_desfire_read_records()");
cut_assert_equal_memory (ref_data_buffer, 100, data_buffer, res, cut_message ("data"));
}
t--;
}
/*
* Change master key settings to require master key authentication for all
* card operations. Only allow to revert this.
*/
res = mifare_desfire_select_application (tag, 0);
cut_assert_success ("mifare_desfire_select_application()");
key = mifare_desfire_aes_key_new (key_data_aes);
res = mifare_desfire_authenticate_aes (tag, 0, key);
cut_assert_success ("mifare_desfire_authenticate_aes()");
mifare_desfire_key_free (key);
res = mifare_desfire_change_key_settings (tag, 0x08);
cut_assert_success ("mifare_desfire_change_key_settings()");
/* Clear authentication */
res = mifare_desfire_select_application (tag, 0);
cut_assert_success ("mifare_desfire_select_application()");
/* We should not be able to list applications now */
res = mifare_desfire_get_application_ids (tag, &aids, &aid_count);
cut_assert_equal_int (-1, res, cut_message ("Wrong return value"));
cut_assert_equal_int (AUTHENTICATION_ERROR, mifare_desfire_last_picc_error (tag), cut_message ("Wrong PICC error"));
/* Deleting an application should not be possible */
res = mifare_desfire_delete_application (tag, aid_c);
cut_assert_equal_int (-1, res, cut_message ("Wrong return value"));
cut_assert_equal_int (AUTHENTICATION_ERROR, mifare_desfire_last_picc_error (tag), cut_message ("Wrong PICC error"));
/* Creating an application should also be forbidden */
MifareDESFireAID aid_d = mifare_desfire_aid_new (0x00DDDDDD);
res = mifare_desfire_create_application (tag, aid_d, 0xEF, 0);
cut_assert_equal_int (-1, res, cut_message ("Wrong return value"));
cut_assert_equal_int (AUTHENTICATION_ERROR, mifare_desfire_last_picc_error (tag), cut_message ("Wrong PICC error"));
/*
* Now we retry authenticated with the master key.
*/
key = mifare_desfire_aes_key_new (key_data_aes);
res = mifare_desfire_authenticate_aes (tag, 0, key);
cut_assert_success ("mifare_desfire_authenticate_aes()");
mifare_desfire_key_free (key);
/* We should be able to list applications again */
res = mifare_desfire_get_application_ids (tag, &aids, &aid_count);
cut_assert_success ("mifare_desfire_get_application_ids()");
cut_assert_equal_int (1, aid_count, cut_message ("Wrong AID count"));
mifare_desfire_free_application_ids (aids);
/* Deleting an application should be possible again */
res = mifare_desfire_delete_application (tag, aid_c);
cut_assert_success ("mifare_desfire_delete_application()");
/* Creating an application should also be possible */
res = mifare_desfire_create_application (tag, aid_d, 0xEF, 0);
cut_assert_success ("mifare_desfire_create_application()");
/* Revert master key settings to default */
res = mifare_desfire_change_key_settings (tag, 0xF);
cut_assert_success ("mifare_desfire_change_key_settings()");
/* Change the master key back to the default one */
key = mifare_desfire_aes_key_new (key_data_aes);
res = mifare_desfire_authenticate_aes (tag, 0, key);
cut_assert_success ("mifare_desfire_authenticate_aes()");
mifare_desfire_key_free (key);
key = mifare_desfire_des_key_new_with_version (key_data_null);
res = mifare_desfire_change_key (tag, 0, key, NULL);
cut_assert_success ("mifare_desfire_change_key()");
/*
* Delete everything from the card
*/
res = mifare_desfire_authenticate (tag, 0, key);
cut_assert_success ("mifare_desfire_authenticate()");
mifare_desfire_key_free (key);
res = mifare_desfire_format_picc (tag);
cut_assert_success ("mifare_desfire_format_picc()");
free (aid_a);
free (aid_b);
free (aid_c);
free (aid_d);
}