diff --git a/NEWS b/NEWS index 0403fa0..30f62aa 100644 --- a/NEWS +++ b/NEWS @@ -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 diff --git a/TODO b/TODO index 0bc2f94..85cf217 100644 --- a/TODO +++ b/TODO @@ -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. diff --git a/configure.ac b/configure.ac index 7f95cd0..4578c2a 100644 --- a/configure.ac +++ b/configure.ac @@ -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" diff --git a/examples/mifare-desfire-ev1-configure-random-uid.c b/examples/mifare-desfire-ev1-configure-random-uid.c index 21fa1ad..ba159d5 100644 --- a/examples/mifare-desfire-ev1-configure-random-uid.c +++ b/examples/mifare-desfire-ev1-configure-random-uid.c @@ -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; } diff --git a/libfreefare/freefare.c b/libfreefare/freefare.c index a7af1f4..3e9a242 100644 --- a/libfreefare/freefare.c +++ b/libfreefare/freefare.c @@ -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; +} diff --git a/libfreefare/freefare.h b/libfreefare/freefare.h index c153a3b..df997c7 100644 --- a/libfreefare/freefare.h +++ b/libfreefare/freefare.h @@ -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); diff --git a/libfreefare/freefare_internal.h b/libfreefare/freefare_internal.h index 5fd67f9..5f15e15 100644 --- a/libfreefare/freefare_internal.h +++ b/libfreefare/freefare_internal.h @@ -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); diff --git a/libfreefare/mifare_desfire.c b/libfreefare/mifare_desfire.c index 8200782..0afda92 100644 --- a/libfreefare/mifare_desfire.c +++ b/libfreefare/mifare_desfire.c @@ -93,18 +93,19 @@ struct mifare_desfire_raw_file_settings { #pragma pack (pop) #define MAX_APPLICATION_COUNT 28 -#define MAX_FILE_COUNT 16 +#define MAX_FILE_COUNT 32 + +#define CMAC_LENGTH 8 static struct mifare_desfire_file_settings cached_file_settings[MAX_FILE_COUNT]; static bool cached_file_settings_current[MAX_FILE_COUNT]; +static int authenticate (MifareTag tag, uint8_t cmd, uint8_t key_no, MifareDESFireKey key); static int create_file1 (MifareTag tag, uint8_t command, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t file_size); static int create_file2 (MifareTag tag, uint8_t command, uint8_t file_no, uint8_t communication_settings, uint16_t access_rights, uint32_t record_size, uint32_t max_number_of_records); static ssize_t write_data (MifareTag tag, uint8_t command, uint8_t file_no, off_t offset, size_t length, void *data, int cs); static ssize_t read_data (MifareTag tag, uint8_t command, uint8_t file_no, off_t offset, size_t length, void *data, int cs); -#define MAX_FRAME_SIZE 60 - #define NOT_YET_AUTHENTICATED 255 #define ASSERT_AUTHENTICATED(tag) \ @@ -116,7 +117,7 @@ static ssize_t read_data (MifareTag tag, uint8_t command, uint8_t file_no, off_ /* * XXX: cs < 0 is a CommunicationSettings detection error. Other values are - * user erros. We may need to distinguish them. + * user errors. We may need to distinguish them. */ #define ASSERT_CS(cs) \ do { \ @@ -145,7 +146,12 @@ static uint8_t __msg[MAX_FRAME_SIZE] = { 0x90, 0x00, 0x00, 0x00, 0x00, /* ..., * /* CLA INS P1 P2 Lc PAYLOAD LE*/ static uint8_t __res[MAX_FRAME_SIZE]; -#define FRAME_PAYLOAD_SIZE (MAX_FRAME_SIZE - 6) +uint8_t cmac_cmd_buf[4096]; +uint8_t cmac_res_buf[4096]; +size_t cmac_cmd_len = 0; +size_t cmac_res_len = 0; + +#define FRAME_PAYLOAD_SIZE (MAX_FRAME_SIZE - 5) /* * Transmit the message msg to the NFC tag and receive the response res. The @@ -166,6 +172,7 @@ static uint8_t __res[MAX_FRAME_SIZE]; __msg[4] = msg_len - 1; \ memcpy (__msg + 5, msg + 1, msg_len - 1); \ } \ + /* reply length */ \ __msg[__len-1] = 0x00; \ MIFARE_DESFIRE (tag)->last_picc_error = OPERATION_OK; \ DEBUG_XFER (__msg, __len, "===> "); \ @@ -173,12 +180,12 @@ static uint8_t __res[MAX_FRAME_SIZE]; return errno = EIO, -1; \ } \ DEBUG_XFER (__res, __##res##_n, "<=== "); \ - memcpy (res, __res, __##res##_n - 2); \ res[__##res##_n-2] = __res[__##res##_n-1]; \ - __##res##_n-=1; \ - if ((1 == __##res##_n) && (OPERATION_OK != res[__##res##_n-1]) && (ADDITIONAL_FRAME != res[__##res##_n-1])) { \ - return MIFARE_DESFIRE (tag)->last_picc_error = res[__##res##_n-1], -1; \ + __##res##_n--; \ + if ((1 == __##res##_n) && (ADDITIONAL_FRAME != res[__##res##_n-1]) && (OPERATION_OK != res[__##res##_n-1])) { \ + return MIFARE_DESFIRE (tag)->last_picc_error = res[0], -1; \ } \ + memcpy (res, __res, __##res##_n - 1); \ } while (0) @@ -186,7 +193,6 @@ static uint8_t __res[MAX_FRAME_SIZE]; * Miscellaneous low-level memory manipulation functions. */ -static void *memdup (void *p, size_t n); static int32_t le24toh (uint8_t data[3]); int @@ -223,16 +229,6 @@ le24toh (uint8_t data[3]) return (data[2] << 16) | (data[1] << 8) | data[0]; } -static void * -memdup (void *p, size_t n) -{ - void *res; - if ((res = malloc (n))) { - memcpy (res, p, n); - } - return res; -} - /* * Memory management functions. @@ -296,7 +292,7 @@ mifare_desfire_connect (MifareTag tag) MIFARE_DESFIRE (tag)->last_picc_error = OPERATION_OK; MIFARE_DESFIRE (tag)->last_pcd_error = OPERATION_OK; MIFARE_DESFIRE (tag)->authenticated_key_no = NOT_YET_AUTHENTICATED; - MIFARE_DESFIRE (tag)->block_number = 0; + MIFARE_DESFIRE (tag)->selected_application = 0; } else { errno = EIO; return -1; @@ -324,8 +320,8 @@ mifare_desfire_disconnect (MifareTag tag) -int -mifare_desfire_authenticate (MifareTag tag, uint8_t key_no, MifareDESFireKey key) +static int +authenticate (MifareTag tag, uint8_t cmd, uint8_t key_no, MifareDESFireKey key) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); @@ -339,54 +335,59 @@ mifare_desfire_authenticate (MifareTag tag, uint8_t key_no, MifareDESFireKey key MIFARE_DESFIRE (tag)->session_key = NULL; BUFFER_INIT (cmd1, 2); - BUFFER_INIT (res, 9); + BUFFER_INIT (res, 17); - BUFFER_APPEND (cmd1, 0x0A); + BUFFER_APPEND (cmd1, cmd); BUFFER_APPEND (cmd1, key_no); DESFIRE_TRANSCEIVE (tag, cmd1, res); + size_t key_length = __res_n - 1; - uint8_t PICC_E_RndB[8]; - memcpy (PICC_E_RndB, res, 8); + uint8_t PICC_E_RndB[16]; + memcpy (PICC_E_RndB, res, key_length); - uint8_t PICC_RndB[8]; - memcpy (PICC_RndB, PICC_E_RndB, 8); - mifare_cbc_des (key, MIFARE_DESFIRE (tag)->ivect, PICC_RndB, 8, MD_RECEIVE, 0); + uint8_t PICC_RndB[16]; + memcpy (PICC_RndB, PICC_E_RndB, key_length); + mifare_cbc_des (key, MIFARE_DESFIRE (tag)->ivect, PICC_RndB, key_length, MD_RECEIVE, 0); - uint8_t PCD_RndA[8]; - RAND_bytes (PCD_RndA, 8); + uint8_t PCD_RndA[16]; + RAND_bytes (PCD_RndA, 16); - uint8_t PCD_r_RndB[8]; - memcpy (PCD_r_RndB, PICC_RndB, 8); - rol (PCD_r_RndB, 8); + uint8_t PCD_r_RndB[16]; + memcpy (PCD_r_RndB, PICC_RndB, key_length); + rol (PCD_r_RndB, key_length); - uint8_t token[16]; - memcpy (token, PCD_RndA, 8); - memcpy (token+8, PCD_r_RndB, 8); + uint8_t token[32]; + memcpy (token, PCD_RndA, key_length); + memcpy (token+key_length, PCD_r_RndB, key_length); - mifare_cbc_des (key, MIFARE_DESFIRE (tag)->ivect, token, 16, MD_SEND, 0); + mifare_cbc_des (key, MIFARE_DESFIRE (tag)->ivect, token, 2 * key_length, MD_SEND, (0x0A == cmd) ? 0 : 1); - BUFFER_INIT (cmd2, 17); + BUFFER_INIT (cmd2, 33); BUFFER_APPEND (cmd2, 0xAF); - BUFFER_APPEND_BYTES (cmd2, token, 16); + BUFFER_APPEND_BYTES (cmd2, token, 2*key_length); DESFIRE_TRANSCEIVE (tag, cmd2, res); - uint8_t PICC_E_RndA_s[8]; - memcpy (PICC_E_RndA_s, res, 8); + uint8_t PICC_E_RndA_s[16]; + memcpy (PICC_E_RndA_s, res, key_length); - uint8_t PICC_RndA_s[8]; - memcpy (PICC_RndA_s, PICC_E_RndA_s, 8); - mifare_cbc_des (key, MIFARE_DESFIRE (tag)->ivect, PICC_RndA_s, 8, MD_RECEIVE, 0); + uint8_t PICC_RndA_s[16]; + memcpy (PICC_RndA_s, PICC_E_RndA_s, key_length); + mifare_cbc_des (key, MIFARE_DESFIRE (tag)->ivect, PICC_RndA_s, key_length, MD_RECEIVE, 0); - uint8_t PCD_RndA_s[8]; - memcpy (PCD_RndA_s, PCD_RndA, 8); - rol (PCD_RndA_s, 8); + uint8_t PCD_RndA_s[key_length]; + memcpy (PCD_RndA_s, PCD_RndA, key_length); + rol (PCD_RndA_s, key_length); - if (0 != memcmp (PCD_RndA_s, PICC_RndA_s, 8)) { + if (0 != memcmp (PCD_RndA_s, PICC_RndA_s, key_length)) { +#ifdef WITH_DEBUG + hexdump (PCD_RndA_s, key_length, "PCD ", 0); + hexdump (PICC_RndA_s, key_length, "PICC ", 0); +#endif return -1; } @@ -394,9 +395,24 @@ mifare_desfire_authenticate (MifareTag tag, uint8_t key_no, MifareDESFireKey key MIFARE_DESFIRE (tag)->session_key = mifare_desfire_session_key_new (PCD_RndA, PICC_RndB, key); memset (MIFARE_DESFIRE (tag)->ivect, 0, MAX_CRYPTO_BLOCK_SIZE); + if (MIFARE_DESFIRE (tag)->session_key->type == T_AES) + cmac_generate_subkeys (MIFARE_DESFIRE (tag)->session_key); + return 0; } +int +mifare_desfire_authenticate (MifareTag tag, uint8_t key_no, MifareDESFireKey key) +{ + return authenticate (tag, 0x0A, key_no, key); +} + +int +mifare_desfire_authenticate_aes (MifareTag tag, uint8_t key_no, MifareDESFireKey key) +{ + return authenticate (tag, 0xAA, key_no, key); +} + int mifare_desfire_change_key_settings (MifareTag tag, uint8_t settings) { @@ -404,22 +420,18 @@ mifare_desfire_change_key_settings (MifareTag tag, uint8_t settings) ASSERT_MIFARE_DESFIRE (tag); ASSERT_AUTHENTICATED (tag); - BUFFER_INIT (cmd, 9); - BUFFER_INIT (res, 1); + BUFFER_INIT (cmd, 9 + CMAC_LENGTH); + BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0x54); + BUFFER_APPEND (cmd, settings); - uint8_t data[8]; + char *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 1, MDCM_ENCIPHERED | ENC_COMMAND); - data[0] = settings; - iso14443a_crc (data, 1, data + 1); - memset (data+3, 0, 5); + DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); - mifare_cbc_des (MIFARE_DESFIRE (tag)->session_key, MIFARE_DESFIRE (tag)->ivect, data, 8, MD_SEND, 0); - - BUFFER_APPEND_BYTES (cmd, data, 8); - - DESFIRE_TRANSCEIVE (tag, cmd, res); + ssize_t n = __res_n; + p = mifare_cryto_postprocess_data (tag, res, &n, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); return 0; } @@ -431,16 +443,21 @@ mifare_desfire_get_key_settings (MifareTag tag, uint8_t *settings, uint8_t *max_ ASSERT_MIFARE_DESFIRE (tag); BUFFER_INIT (cmd, 1); - BUFFER_INIT (res, 3); + BUFFER_INIT (res, 3 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0x45); - DESFIRE_TRANSCEIVE (tag, cmd, res); + char *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 1, MDCM_PLAIN | CMAC_COMMAND); + + DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); + + ssize_t n = __res_n; + p = mifare_cryto_postprocess_data (tag, res, &n, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); if (settings) *settings = res[0]; if (max_keys) - *max_keys = res[1]; + *max_keys = res[1] & 0x0F; return 0; } @@ -452,47 +469,90 @@ mifare_desfire_change_key (MifareTag tag, uint8_t key_no, MifareDESFireKey new_k ASSERT_MIFARE_DESFIRE (tag); ASSERT_AUTHENTICATED (tag); - BUFFER_INIT (cmd, 1+1+24); - BUFFER_INIT (res, 1); + BUFFER_INIT (cmd, 42); + BUFFER_INIT (res, 1 + CMAC_LENGTH); + + key_no &= 0x0F; + + /* + * Because new crypto methods can be setup only at application creation, + * changing the card master key to one of them require a key_no tweak. + */ + if (0x000000 == MIFARE_DESFIRE (tag)->selected_application) { + switch (new_key->type) { + case T_DES: + case T_3DES: + break; + case T_AES: + key_no |= 0x80; + break; + } + } BUFFER_APPEND (cmd, 0xC4); BUFFER_APPEND (cmd, key_no); - uint8_t data[24]; + int new_key_length; + switch (new_key->type) { + case T_DES: + case T_3DES: + case T_AES: + new_key_length = 16; + break; + } - if (MIFARE_DESFIRE (tag)->authenticated_key_no != key_no) { + memcpy (cmd + __cmd_n, new_key->data, new_key_length); + + if ((MIFARE_DESFIRE (tag)->authenticated_key_no & 0x0f) != (key_no & 0x0f)) { if (old_key) { - memcpy (data, old_key->data, 16); - } else { - memset (data, 0, 16); - } - for (int n=0; n<16; n++) { - data[n] ^= new_key->data[n]; - } - // Append XORed data CRC - iso14443a_crc (data, 16, data+16); - // Append new key CRC - iso14443a_crc (new_key->data, 16, data+18); - // Padding - for (int n=20; n<24; n++) { - data[n] = 0x00; - } - } else { - memcpy (data, new_key->data, 16); - // Append new key CRC - iso14443a_crc (data, 16, data+16); - - // Padding - for (int n=18; n<24; n++) { - data[n] = 0x00; + for (int n = 0; n < new_key_length; n++) { + cmd[__cmd_n + n] ^= old_key->data[n]; + } } } - mifare_cbc_des (MIFARE_DESFIRE (tag)->session_key, MIFARE_DESFIRE (tag)->ivect, data, 24, MD_SEND, 0); + __cmd_n += new_key_length; - BUFFER_APPEND_BYTES (cmd, data, 24); + if (new_key->type == T_AES) + cmd[__cmd_n++] = new_key->aes_version; - DESFIRE_TRANSCEIVE (tag, cmd, res); + if ((MIFARE_DESFIRE (tag)->authenticated_key_no & 0x0f) != (key_no & 0x0f)) { + switch (MIFARE_DESFIRE (tag)->session_key->type) { + case T_DES: + case T_3DES: + iso14443a_crc_append (cmd + 2, __cmd_n - 2); + __cmd_n += 2; + iso14443a_crc (new_key->data, new_key_length, cmd + __cmd_n); + __cmd_n += 2; + break; + case T_AES: + desfire_crc32_append (cmd, __cmd_n); + __cmd_n += 4; + + desfire_crc32 (new_key->data, new_key_length, cmd + __cmd_n); + __cmd_n += 4; + break; + } + } else { + switch (MIFARE_DESFIRE (tag)->session_key->type) { + case T_DES: + case T_3DES: + iso14443a_crc_append (cmd + 2 , __cmd_n - 2); + __cmd_n += 2; + break; + case T_AES: + desfire_crc32_append (cmd, __cmd_n); + __cmd_n += 4; + break; + } + } + + uint8_t * p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 2, MDCM_ENCIPHERED | ENC_COMMAND | NO_CRC); + + DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); + + ssize_t sn = __res_n; + mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); return 0; } @@ -512,7 +572,7 @@ mifare_desfire_get_key_version (MifareTag tag, uint8_t key_no, uint8_t *version) BUFFER_APPEND (cmd, 0x64); BUFFER_APPEND (cmd, key_no); - BUFFER_INIT (res, 2); + BUFFER_INIT (res, 2 + CMAC_LENGTH); DESFIRE_TRANSCEIVE (tag, cmd, res); @@ -524,37 +584,53 @@ mifare_desfire_get_key_version (MifareTag tag, uint8_t key_no, uint8_t *version) int -mifare_desfire_create_application (MifareTag tag, MifareDESFireAID aid, uint8_t settings, uint8_t key_no) +create_application (MifareTag tag, MifareDESFireAID aid, uint8_t settings1, uint8_t settings2, uint16_t iso_file_id, char *iso_file_name) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); - BUFFER_INIT (cmd, 6); - BUFFER_INIT (res, 1); + BUFFER_INIT (cmd, 22); + BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0xCA); BUFFER_APPEND_LE (cmd, aid->data, sizeof (aid->data), sizeof (aid->data)); - BUFFER_APPEND (cmd, settings); - BUFFER_APPEND (cmd, key_no); + BUFFER_APPEND (cmd, settings1); + BUFFER_APPEND (cmd, settings2); - DESFIRE_TRANSCEIVE (tag, cmd, res); + uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); + + DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); + + ssize_t sn = __res_n; + mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); return 0; } +int +mifare_desfire_create_application (MifareTag tag, MifareDESFireAID aid, uint8_t settings, uint8_t key_no) +{ + return create_application (tag, aid, settings, key_no, 0, NULL); +} + int mifare_desfire_delete_application (MifareTag tag, MifareDESFireAID aid) { ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); - BUFFER_INIT (cmd, 4); - BUFFER_INIT (res, 1); + BUFFER_INIT (cmd, 4 + CMAC_LENGTH); + BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0xDA); BUFFER_APPEND_LE (cmd, aid->data, sizeof (aid->data), sizeof (aid->data)); - DESFIRE_TRANSCEIVE (tag, cmd, res); + uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); + + DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); + + ssize_t sn = __res_n; + p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); /* * If we have deleted the current application, we are not authenticated @@ -580,28 +656,43 @@ mifare_desfire_get_application_ids (MifareTag tag, MifareDESFireAID *aids[], siz BUFFER_APPEND (cmd, 0x6A); - DESFIRE_TRANSCEIVE (tag, cmd, res); - *count = (BUFFER_SIZE (res)-1)/3; - *aids = malloc ((*count + 1) * sizeof (MifareDESFireAID)); - for (size_t i = 0; (3*i + 1) < BUFFER_SIZE (res); i++) { - (*aids)[i] = memdup (res + 3*i, 3); - } + uint8_t buffer[3*MAX_APPLICATION_COUNT + CMAC_LENGTH + 1]; + *count = 0; + + uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); + + DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); + + // FIXME This code needs refactoring! + memcpy (buffer, res, __res_n); if (res[__res_n-1] == 0xAF) { - cmd[0] = 0xAF; - DESFIRE_TRANSCEIVE (tag, cmd, res); - *count += (BUFFER_SIZE (res)-1) / 3; + off_t offset = __res_n - 1; + p[0] = 0xAF; + DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); - MifareDESFireAID *p; - if ((p = realloc (*aids, (*count + 1) * sizeof (MifareDESFireAID)))) { - *aids = p; - - for (size_t i = 0; (3*i) < BUFFER_SIZE (res); i++) { - (*aids)[19+i] = memdup (res + 3*i, 3); - } - } + memcpy ((uint8_t *)buffer + offset, res, __res_n); + __res_n += offset; } + ssize_t sn = __res_n; + p = mifare_cryto_postprocess_data (tag, buffer, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); + + *count = (sn - 1)/3; + + *aids = malloc (sn - 1); + if (!(*aids = malloc ((*count + 1) * sizeof (MifareDESFireAID)))) + return -1; + + for (size_t i = 0; i < *count; i++) { + if (!((*aids)[i] = memdup (p + 3 * i, 3))) { + while (i--) { + free ((*aids)[i]); + } + free (aids); + return -1; + } + } (*aids)[*count] = NULL; return 0; @@ -631,13 +722,18 @@ mifare_desfire_select_application (MifareTag tag, MifareDESFireAID aid) aid = &null_aid; } - BUFFER_INIT (cmd, 4); - BUFFER_INIT (res, 1); + BUFFER_INIT (cmd, 4 + CMAC_LENGTH); + BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0x5A); BUFFER_APPEND_LE (cmd, aid->data, sizeof (aid->data), sizeof (aid->data)); - DESFIRE_TRANSCEIVE (tag, cmd, res); + uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); + + DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); + + ssize_t sn = __res_n; + mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND); for (int n = 0; n < MAX_FILE_COUNT; n++) cached_file_settings_current[n] = false; @@ -645,6 +741,8 @@ mifare_desfire_select_application (MifareTag tag, MifareDESFireAID aid) free (MIFARE_DESFIRE (tag)->session_key); MIFARE_DESFIRE (tag)->session_key = NULL; + MIFARE_DESFIRE (tag)->selected_application = aid->data[0] | aid->data[1] << 8 | aid->data[2] << 16; + return 0; } @@ -655,12 +753,21 @@ mifare_desfire_format_picc (MifareTag tag) ASSERT_MIFARE_DESFIRE (tag); ASSERT_AUTHENTICATED (tag); - BUFFER_INIT (cmd, 1); - BUFFER_INIT (res, 1); + BUFFER_INIT (cmd, 1 + CMAC_LENGTH); + BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0xFC); - DESFIRE_TRANSCEIVE (tag, cmd, res); + uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); + + DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); + + ssize_t sn = __res_n; + mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); + + free (MIFARE_DESFIRE (tag)->session_key); + MIFARE_DESFIRE (tag)->session_key = NULL; + MIFARE_DESFIRE (tag)->selected_application = 0x000000; return 0; } @@ -677,19 +784,53 @@ mifare_desfire_get_version (MifareTag tag, struct mifare_desfire_version_info *v ASSERT_NOT_NULL (version_info); BUFFER_INIT (cmd, 1); - BUFFER_INIT (res, 15); /* 8, 8, then 15 byte results */ + BUFFER_INIT (res, 14 + CMAC_LENGTH); /* 8, 8, then 15 byte results */ + + char buffer[28 + CMAC_LENGTH + 1]; BUFFER_APPEND (cmd, 0x60); + uint8_t *b = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); DESFIRE_TRANSCEIVE (tag, cmd, res); memcpy (&(version_info->hardware), res, 7); + memcpy (buffer, res, 7); cmd[0] = 0xAF; DESFIRE_TRANSCEIVE (tag, cmd, res); memcpy (&(version_info->software), res, 7); + memcpy (buffer + 7, res, 7); DESFIRE_TRANSCEIVE (tag, cmd, res); memcpy (&(version_info->uid), res, 14); + memcpy (buffer + 14, res, __res_n); + + ssize_t sn = 28 + CMAC_LENGTH + 1; + b = mifare_cryto_postprocess_data (tag, buffer, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); + + return 0; +} + +int +mifare_desfire_free_mem (MifareTag tag, uint32_t *size) +{ + ASSERT_ACTIVE (tag); + ASSERT_MIFARE_DESFIRE (tag); + + ASSERT_NOT_NULL (size); + + BUFFER_INIT (cmd, 1); + BUFFER_INIT (res, 4 + CMAC_LENGTH); + + BUFFER_APPEND (cmd, 0x6E); + + uint8_t *b = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); + + DESFIRE_TRANSCEIVE2 (tag, b, __cmd_n, res); + + ssize_t sn = __res_n; + b = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); + + *size = res[1] | (res[2] << 8) | (res[3] << 16); return 0; } @@ -704,14 +845,20 @@ mifare_desfire_get_file_ids (MifareTag tag, uint8_t *files[], size_t *count) ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); - BUFFER_INIT (cmd, 1); - BUFFER_INIT (res, 16); + BUFFER_INIT (cmd, 1 + CMAC_LENGTH); + BUFFER_INIT (res, 16 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0x6F); - DESFIRE_TRANSCEIVE (tag, cmd, res); - *count = BUFFER_SIZE (res) - 1; + uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); + + DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); + + ssize_t sn = __res_n; + p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); + + *count = sn - 1; if (!(*files = malloc (*count))) { errno = ENOMEM; @@ -733,16 +880,21 @@ mifare_desfire_get_file_settings (MifareTag tag, uint8_t file_no, struct mifare_ return 0; } - BUFFER_INIT (cmd, 2); - BUFFER_INIT (res, 18); + BUFFER_INIT (cmd, 2 + CMAC_LENGTH); + BUFFER_INIT (res, 18 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0xF5); BUFFER_APPEND (cmd, file_no); + + uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); - DESFIRE_TRANSCEIVE (tag, cmd, res); + DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); + + ssize_t sn = __res_n; + p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); struct mifare_desfire_raw_file_settings raw_settings; - memcpy (&raw_settings, res, BUFFER_SIZE (res)-1); + memcpy (&raw_settings, p, sn - 1); settings->file_type = raw_settings.file_type; settings->communication_settings = raw_settings.communication_settings; @@ -787,34 +939,34 @@ mifare_desfire_change_file_settings (MifareTag tag, uint8_t file_no, uint8_t com cached_file_settings_current[file_no] = false; if (MDAR_CHANGE_AR(settings.access_rights) == MDAR_FREE) { - BUFFER_INIT (cmd, 5); - BUFFER_INIT (res, 1); + BUFFER_INIT (cmd, 5 + CMAC_LENGTH); + BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0x5F); BUFFER_APPEND (cmd, file_no); BUFFER_APPEND (cmd, communication_settings); BUFFER_APPEND_LE (cmd, access_rights, 2, sizeof (uint16_t)); - DESFIRE_TRANSCEIVE (tag, cmd, res); + uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); + DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); + + ssize_t sn = __res_n; + p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); } else { BUFFER_INIT (cmd, 10); - BUFFER_INIT (res, 1); - - uint8_t data[8]; + BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0x5F); BUFFER_APPEND (cmd, file_no); + BUFFER_APPEND (cmd, communication_settings); + BUFFER_APPEND_LE (cmd, access_rights, 2, sizeof (uint16_t)); - data[0] = communication_settings; - uint16_t le_ar = htole16 (access_rights); - memcpy (data + 1, &le_ar, sizeof (le_ar)); - iso14443a_crc (data, 3, data+3); - memset (data + 5, 0, 3); - mifare_cbc_des (MIFARE_DESFIRE (tag)->session_key, MIFARE_DESFIRE (tag)->ivect, data, 8, MD_SEND, 0); + uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 2, MDCM_ENCIPHERED | ENC_COMMAND); - BUFFER_APPEND_BYTES (cmd, data, 8); + DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); - DESFIRE_TRANSCEIVE (tag, cmd, res); + ssize_t sn = __res_n; + p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); } return 0; @@ -826,8 +978,8 @@ create_file1 (MifareTag tag, uint8_t command, uint8_t file_no, uint8_t communica ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); - BUFFER_INIT (cmd, 8); - BUFFER_INIT (res, 1); + BUFFER_INIT (cmd, 8 + CMAC_LENGTH); + BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, command); BUFFER_APPEND (cmd, file_no); @@ -835,7 +987,12 @@ create_file1 (MifareTag tag, uint8_t command, uint8_t file_no, uint8_t communica BUFFER_APPEND_LE (cmd, access_rights, 2, sizeof (uint16_t)); BUFFER_APPEND_LE (cmd, file_size, 3, sizeof (uint32_t)); - DESFIRE_TRANSCEIVE (tag, cmd, res); + char *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); + + DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); + + ssize_t sn = __res_n; + p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); cached_file_settings_current[file_no] = false; @@ -860,8 +1017,8 @@ mifare_desfire_create_value_file (MifareTag tag, uint8_t file_no, uint8_t commun ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); - BUFFER_INIT (cmd, 18); - BUFFER_INIT (res, 1); + BUFFER_INIT (cmd, 18 + CMAC_LENGTH); + BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0xCC); BUFFER_APPEND (cmd, file_no); @@ -872,7 +1029,12 @@ mifare_desfire_create_value_file (MifareTag tag, uint8_t file_no, uint8_t commun BUFFER_APPEND_LE (cmd, value, 4, sizeof (int32_t)); BUFFER_APPEND (cmd, limited_credit_enable); - DESFIRE_TRANSCEIVE (tag, cmd, res); + char *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, communication_settings | CMAC_COMMAND); + + DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); + + ssize_t sn = __res_n; + p = mifare_cryto_postprocess_data (tag, res, &sn, communication_settings | CMAC_COMMAND | CMAC_VERIFY); cached_file_settings_current[file_no] = false; @@ -885,8 +1047,8 @@ create_file2 (MifareTag tag, uint8_t command, uint8_t file_no, uint8_t communica ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); - BUFFER_INIT (cmd, 11); - BUFFER_INIT (res, 1); + BUFFER_INIT (cmd, 11 + CMAC_LENGTH); + BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, command); BUFFER_APPEND (cmd, file_no); @@ -895,7 +1057,12 @@ create_file2 (MifareTag tag, uint8_t command, uint8_t file_no, uint8_t communica BUFFER_APPEND_LE (cmd, record_size, 3, sizeof (uint32_t)); BUFFER_APPEND_LE (cmd, max_number_of_records, 3, sizeof (uint32_t)); - DESFIRE_TRANSCEIVE (tag, cmd, res); + char *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, communication_settings | CMAC_COMMAND); + + DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); + + ssize_t sn = __res_n; + p = mifare_cryto_postprocess_data (tag, res, &sn, communication_settings | CMAC_COMMAND | CMAC_VERIFY); cached_file_settings_current[file_no] = false; @@ -920,13 +1087,18 @@ mifare_desfire_delete_file (MifareTag tag, uint8_t file_no) ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); - BUFFER_INIT (cmd, 2); - BUFFER_INIT (res, 1); + BUFFER_INIT (cmd, 2 + CMAC_LENGTH); + BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0xDF); BUFFER_APPEND (cmd, file_no); - DESFIRE_TRANSCEIVE (tag, cmd, res); + uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); + + DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); + + ssize_t sn = __res_n; + p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); return 0; } @@ -939,9 +1111,7 @@ mifare_desfire_delete_file (MifareTag tag, uint8_t file_no) static ssize_t read_data (MifareTag tag, uint8_t command, uint8_t file_no, off_t offset, size_t length, void *data, int cs) { - ssize_t bytes_read = 0; - - void *p = data; + size_t bytes_received = 0; ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); @@ -955,39 +1125,34 @@ read_data (MifareTag tag, uint8_t command, uint8_t file_no, off_t offset, size_t BUFFER_APPEND_LE (cmd, offset, 3, sizeof (off_t)); BUFFER_APPEND_LE (cmd, length, 3, sizeof (size_t)); - if (cs) { - if (!(p = assert_crypto_buffer_size (tag, MAX_FRAME_SIZE - 1))) - return -1; + // FIXME This is somewhat done in mifare_cryto_preprocess_data (or should be!!) + uint8_t ocs = cs; + if ((MIFARE_DESFIRE (tag)->session_key) && (MIFARE_DESFIRE (tag)->session_key->type == T_AES) && (cs | MDCM_MACED)) { + cs = MDCM_PLAIN; } + uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 8, cs | CMAC_COMMAND); + cs= ocs; do { - ssize_t frame_bytes; + DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); - DESFIRE_TRANSCEIVE (tag, cmd, res); + size_t frame_bytes = BUFFER_SIZE (res) - 1; + memcpy ((uint8_t *)data + bytes_received, res, frame_bytes); + bytes_received += frame_bytes; - frame_bytes = BUFFER_SIZE (res) - 1; - memcpy ((uint8_t *)p + bytes_read, res, frame_bytes); - bytes_read += frame_bytes; + p[0] = 0xAF; + __cmd_n = 1; + } while (0xAF == res[__res_n-1]); - if (res[__res_n-1] == 0xAF) { - if (p != data) { - // If we are handling memory, request more for next frame. - if (!(p = assert_crypto_buffer_size (tag, bytes_read + MAX_FRAME_SIZE - 1))) - return -1; + ((uint8_t *)data)[bytes_received++] = 0x00; - } - BUFFER_CLEAR (cmd); - BUFFER_APPEND (cmd, 0xAF); - } + ssize_t sr = bytes_received; + uint8_t uds = 0; + if (!length) + uds |= UNSPECIFIED_DATA_LENGTH; + p = mifare_cryto_postprocess_data (tag, data, &sr, cs | CMAC_COMMAND | CMAC_VERIFY | MAC_VERIFY); - } while (res[__res_n-1] != 0x00); - - if (cs) { - if (mifare_cryto_postprocess_data (tag, p, &bytes_read, cs)) - memcpy (data, p, bytes_read); - } - - return bytes_read; + return sr - 1; } ssize_t @@ -1014,8 +1179,8 @@ write_data (MifareTag tag, uint8_t command, uint8_t file_no, off_t offset, size_ ASSERT_MIFARE_DESFIRE (tag); ASSERT_CS (cs); - BUFFER_INIT (cmd, 8 + length); - BUFFER_INIT (res, 1); + BUFFER_INIT (cmd, 8 + length + CMAC_LENGTH); + BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, command); BUFFER_APPEND (cmd, file_no); @@ -1023,10 +1188,10 @@ write_data (MifareTag tag, uint8_t command, uint8_t file_no, off_t offset, size_ BUFFER_APPEND_LE (cmd, length, 3, sizeof (size_t)); BUFFER_APPEND_BYTES (cmd, data, length); - p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 8, cs); + p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 8, cs | MAC_COMMAND | CMAC_COMMAND | ENC_COMMAND); BUFFER_INIT(d, FRAME_PAYLOAD_SIZE); - bytes_left = FRAME_PAYLOAD_SIZE; + bytes_left = FRAME_PAYLOAD_SIZE - 8; while (bytes_send < __cmd_n) { size_t frame_bytes = MIN(bytes_left, __cmd_n - bytes_send); @@ -1045,6 +1210,9 @@ write_data (MifareTag tag, uint8_t command, uint8_t file_no, off_t offset, size_ bytes_left = FRAME_PAYLOAD_SIZE - 1; } + ssize_t sn = __res_n; + p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_MACED | CMAC_COMMAND | CMAC_VERIFY); + if (0x00 == res[__res_n-1]) { // Remove header length bytes_send -= 8; @@ -1082,30 +1250,22 @@ mifare_desfire_get_value_ex (MifareTag tag, uint8_t file_no, int32_t *value, int if (!value) return errno = EINVAL, -1; - void *p; - ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); ASSERT_CS (cs); - BUFFER_INIT (cmd, 2); - BUFFER_INIT (res, 9); + BUFFER_INIT (cmd, 2 + CMAC_LENGTH); + BUFFER_INIT (res, 9 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0x6C); BUFFER_APPEND (cmd, file_no); - DESFIRE_TRANSCEIVE (tag, cmd, res); + uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); - p = (uint8_t *)res; + DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); - if (cs) { - ssize_t rdl = BUFFER_SIZE (res) - 1; - p = mifare_cryto_postprocess_data (tag, p, &rdl, cs); - if (rdl != 4) { - printf ("invalid data length"); - return -1; - } - } + ssize_t sn = __res_n; + p = mifare_cryto_postprocess_data (tag, res, &sn, cs | CMAC_COMMAND | CMAC_VERIFY | MAC_VERIFY); *value = le32toh (*(int32_t *)(p)); @@ -1125,16 +1285,19 @@ mifare_desfire_credit_ex (MifareTag tag, uint8_t file_no, int32_t amount, int cs ASSERT_MIFARE_DESFIRE (tag); ASSERT_CS (cs); - BUFFER_INIT (cmd, 10); - BUFFER_INIT (res, 1); + BUFFER_INIT (cmd, 10 + CMAC_LENGTH); + BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0x0C); BUFFER_APPEND (cmd, file_no); BUFFER_APPEND_LE (cmd, amount, 4, sizeof (int32_t)); - uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 2, cs); + uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 2, cs | MAC_COMMAND | CMAC_COMMAND | ENC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); + ssize_t sn = __res_n; + p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); + cached_file_settings_current[file_no] = false; return 0; @@ -1152,16 +1315,19 @@ mifare_desfire_debit_ex (MifareTag tag, uint8_t file_no, int32_t amount, int cs) ASSERT_MIFARE_DESFIRE (tag); ASSERT_CS (cs); - BUFFER_INIT (cmd, 10); - BUFFER_INIT (res, 1); + BUFFER_INIT (cmd, 10 + CMAC_LENGTH); + BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0xDC); BUFFER_APPEND (cmd, file_no); BUFFER_APPEND_LE (cmd, amount, 4, sizeof (int32_t)); - uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 2, cs); + uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 2, cs | MAC_COMMAND | CMAC_COMMAND | ENC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); + ssize_t sn = __res_n; + p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); + cached_file_settings_current[file_no] = false; return 0; @@ -1179,16 +1345,19 @@ mifare_desfire_limited_credit_ex (MifareTag tag, uint8_t file_no, int32_t amount ASSERT_MIFARE_DESFIRE (tag); ASSERT_CS (cs); - BUFFER_INIT (cmd, 10); - BUFFER_INIT (res, 1); + BUFFER_INIT (cmd, 10 + CMAC_LENGTH); + BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0x1C); BUFFER_APPEND (cmd, file_no); BUFFER_APPEND_LE (cmd, amount, 4, sizeof (int32_t)); - uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 2, cs); + uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 2, cs | MAC_COMMAND | CMAC_COMMAND | ENC_COMMAND); DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); + ssize_t sn = __res_n; + p = mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); + cached_file_settings_current[file_no] = false; return 0; @@ -1223,13 +1392,18 @@ mifare_desfire_clear_record_file (MifareTag tag, uint8_t file_no) ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); - BUFFER_INIT (cmd, 2); - BUFFER_INIT (res, 1); + BUFFER_INIT (cmd, 2 + CMAC_LENGTH); + BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0xEB); BUFFER_APPEND (cmd, file_no); - DESFIRE_TRANSCEIVE (tag, cmd, res); + uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); + + DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); + + ssize_t sn = __res_n; + mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); cached_file_settings_current[file_no] = false; @@ -1242,12 +1416,17 @@ mifare_desfire_commit_transaction (MifareTag tag) ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); - BUFFER_INIT (cmd, 1); - BUFFER_INIT (res, 1); + BUFFER_INIT (cmd, 1 + CMAC_LENGTH); + BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0xC7); - DESFIRE_TRANSCEIVE (tag, cmd, res); + uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); + + DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); + + ssize_t sn = __res_n; + mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); return 0; } @@ -1258,12 +1437,17 @@ mifare_desfire_abort_transaction (MifareTag tag) ASSERT_ACTIVE (tag); ASSERT_MIFARE_DESFIRE (tag); - BUFFER_INIT (cmd, 1); - BUFFER_INIT (res, 1); + BUFFER_INIT (cmd, 1 + CMAC_LENGTH); + BUFFER_INIT (res, 1 + CMAC_LENGTH); BUFFER_APPEND (cmd, 0xA7); - DESFIRE_TRANSCEIVE (tag, cmd, res); + uint8_t *p = mifare_cryto_preprocess_data (tag, cmd, &__cmd_n, 0, MDCM_PLAIN | CMAC_COMMAND); + + DESFIRE_TRANSCEIVE2 (tag, p, __cmd_n, res); + + ssize_t sn = __res_n; + mifare_cryto_postprocess_data (tag, res, &sn, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); return 0; } diff --git a/libfreefare/mifare_desfire_authenticate.c b/libfreefare/mifare_desfire_authenticate.c index 8d08913..b851818 100644 --- a/libfreefare/mifare_desfire_authenticate.c +++ b/libfreefare/mifare_desfire_authenticate.c @@ -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 +#endif + +#if defined(HAVE_SYS_ENDIAN_H) +# include +#endif + +#if defined(HAVE_ENDIAN_H) +# include +#endif + +#if defined(HAVE_COREFOUNDATION_COREFOUNDATION_H) +# include +#endif + +#if defined(HAVE_BYTESWAP_H) +# include +#endif + + +#if defined(HAVE_SYS_TYPES_H) +# include +#endif + +#include #include +#include #include #include +#ifdef WITH_DEBUG +# include +#endif + #include #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: - break; + 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; diff --git a/libfreefare/mifare_desfire_key.c b/libfreefare/mifare_desfire_key.c index e1d04b8..8a9836f 100644 --- a/libfreefare/mifare_desfire_key.c +++ b/libfreefare/mifare_desfire_key.c @@ -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; diff --git a/test/Makefile.am b/test/Makefile.am index 204a279..586c3ba 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -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 diff --git a/test/common/mifare_desfire_auto_authenticate.c b/test/common/mifare_desfire_auto_authenticate.c index 35b2a00..8aa7f00 100644 --- a/test/common/mifare_desfire_auto_authenticate.c +++ b/test/common/mifare_desfire_auto_authenticate.c @@ -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 */ - res = mifare_desfire_authenticate (tag, key_no, 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); diff --git a/test/common/mifare_desfire_auto_authenticate.h b/test/common/mifare_desfire_auto_authenticate.h index 5bec992..fa0c1c3 100644 --- a/test/common/mifare_desfire_auto_authenticate.h +++ b/test/common/mifare_desfire_auto_authenticate.h @@ -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); diff --git a/test/mifare_desfire_ev1_fixture.c b/test/mifare_desfire_ev1_fixture.c new file mode 100644 index 0000000..d87845a --- /dev/null +++ b/test/mifare_desfire_ev1_fixture.c @@ -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 + * + * $Id$ + */ + +#include +#include + +#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); +} + diff --git a/test/mifare_desfire_ev1_fixture.h b/test/mifare_desfire_ev1_fixture.h new file mode 100644 index 0000000..214bf9c --- /dev/null +++ b/test/mifare_desfire_ev1_fixture.h @@ -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 + * + * $Id$ + */ + +extern MifareTag tag; diff --git a/test/test_mifare_desfire.c b/test/test_mifare_desfire.c index 9e5eaa9..35e8ef6 100644 --- a/test/test_mifare_desfire.c +++ b/test/test_mifare_desfire.c @@ -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()"); diff --git a/test/test_mifare_desfire_aes.c b/test/test_mifare_desfire_aes.c new file mode 100644 index 0000000..90efda7 --- /dev/null +++ b/test/test_mifare_desfire_aes.c @@ -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 + * + * $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 +#include + +#include +#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); +} diff --git a/test/test_mifare_desfire_ev1.c b/test/test_mifare_desfire_ev1.c new file mode 100644 index 0000000..af0291b --- /dev/null +++ b/test/test_mifare_desfire_ev1.c @@ -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 + * + * $Id$ + */ + +#include +#include +#include +#include + +#include +#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); +} diff --git a/test/test_mifare_desfire_ev1_aes.c b/test/test_mifare_desfire_ev1_aes.c new file mode 100644 index 0000000..638cf9b --- /dev/null +++ b/test/test_mifare_desfire_ev1_aes.c @@ -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 + * + * $Id$ + */ + +#include +#include +#include +#include + +#include +#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