From 9d88c18833450a91b97fc6ac91c1d983ed3a8681 Mon Sep 17 00:00:00 2001 From: Robert Quattlebaum Date: Fri, 5 Jan 2018 13:03:42 -0800 Subject: [PATCH] Support for AN10922 key derivation This commit implements [AN10922][] key diversification, as described in issue #77. [AN10922]: https://www.nxp.com/docs/en/application-note/AN10922.pdf --- examples/Makefile.am | 4 + examples/mifare-ultralight-info.c | 23 +- examples/mifare-ultralightc-diversify.c | 112 +++++++++ libfreefare/CMakeLists.txt | 1 + libfreefare/Makefile.am | 2 + libfreefare/freefare.h | 23 ++ libfreefare/freefare_internal.h | 14 +- libfreefare/mifare_desfire.c | 50 ++-- libfreefare/mifare_desfire_crypto.c | 24 +- libfreefare/mifare_desfire_key.c | 60 ++--- libfreefare/mifare_key_deriver.3 | 169 +++++++++++++ libfreefare/mifare_key_deriver.c | 301 ++++++++++++++++++++++++ libfreefare/mifare_ultralight.c | 2 +- test/Makefile.am | 4 + test/test_mifare_key_deriver_an10922.c | 143 +++++++++++ 15 files changed, 864 insertions(+), 68 deletions(-) create mode 100644 examples/mifare-ultralightc-diversify.c create mode 100644 libfreefare/mifare_key_deriver.3 create mode 100644 libfreefare/mifare_key_deriver.c create mode 100644 test/test_mifare_key_deriver_an10922.c diff --git a/examples/Makefile.am b/examples/Makefile.am index cd1387d..11a18ec 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -16,6 +16,7 @@ bin_PROGRAMS = felica-lite-dump \ mifare-desfire-read-ndef \ mifare-desfire-write-ndef \ mifare-ultralight-info \ + mifare-ultralightc-diversify \ ntag-detect \ ntag-removeauth \ ntag-setauth \ @@ -66,6 +67,9 @@ mifare_desfire_write_ndef_LDADD = $(top_builddir)/libfreefare/libfreefare.la mifare_ultralight_info_SOURCES = mifare-ultralight-info.c mifare_ultralight_info_LDADD = $(top_builddir)/libfreefare/libfreefare.la +mifare_ultralightc_diversify_SOURCES = mifare-ultralightc-diversify.c +mifare_ultralightc_diversify_LDADD = $(top_builddir)/libfreefare/libfreefare.la + ntag_detect_SOURCES = ntag-detect.c ntag_detect_LDADD = $(top_builddir)/libfreefare/libfreefare.la diff --git a/examples/mifare-ultralight-info.c b/examples/mifare-ultralight-info.c index 18f6a00..e9b3c49 100644 --- a/examples/mifare-ultralight-info.c +++ b/examples/mifare-ultralight-info.c @@ -59,7 +59,28 @@ main(int argc, char *argv[]) if (mifare_ultralight_connect(tag) < 0) errx(EXIT_FAILURE, "Error connecting to tag."); res = mifare_ultralightc_authenticate(tag, key); - printf("Authentication with default key: %s\n", res ? "fail" : "success"); + if (res != 0) { + MifareDESFireKey diversified_key = NULL; + MifareKeyDeriver deriver = mifare_key_deriver_new_an10922(key, MIFARE_KEY_2K3DES); + + mifare_key_deriver_begin(deriver); + mifare_key_deriver_update_uid(deriver, tag); + diversified_key = mifare_key_deriver_end(deriver); + + // Disconnect and reconnect. + mifare_ultralight_disconnect(tag); + if (mifare_ultralight_connect(tag) < 0) + errx(EXIT_FAILURE, "Error connecting to tag."); + + res = mifare_ultralightc_authenticate(tag, diversified_key); + + printf("Authentication with default key: %s\n", res ? "fail" : "success (diversified)"); + + mifare_desfire_key_free(diversified_key); + mifare_key_deriver_free(deriver); + } else { + printf("Authentication with default key: success\n"); + } mifare_desfire_key_free(key); mifare_ultralight_disconnect(tag); } diff --git a/examples/mifare-ultralightc-diversify.c b/examples/mifare-ultralightc-diversify.c new file mode 100644 index 0000000..631b60b --- /dev/null +++ b/examples/mifare-ultralightc-diversify.c @@ -0,0 +1,112 @@ +#include +#include +#include + +#include + +#include + +static int +swap_keys(FreefareTag tag, MifareDESFireKey new_key, MifareDESFireKey old_key) +{ + int res; + res = mifare_ultralightc_authenticate(tag, old_key); + MifareUltralightPage data; + + if (res != 0) { + mifare_ultralight_disconnect(tag); + mifare_ultralight_connect(tag); + } + + return mifare_ultralightc_set_key(tag, new_key); +} + +int +main(int argc, char *argv[]) +{ + int error = EXIT_SUCCESS; + nfc_device *device = NULL; + FreefareTag *tags = NULL; + uint8_t key1_3des_data[16] = { 0x49, 0x45, 0x4D, 0x4B, 0x41, 0x45, 0x52, 0x42, 0x21, 0x4E, 0x41, 0x43, 0x55, 0x4F, 0x59, 0x46 }; + MifareDESFireKey master_key = mifare_desfire_3des_key_new(key1_3des_data); + MifareDESFireKey derived_key = NULL; + MifareKeyDeriver deriver = mifare_key_deriver_new_an10922(master_key, MIFARE_KEY_2K3DES); + bool undiversify = (argc == 2 && strcmp("--undiversify",argv[1]) == 0); + + if (argc > 2 || (argc == 2 && strcmp("--undiversify",argv[1]) != 0)) { + errx(EXIT_FAILURE, "usage: %s [--undiversify]", argv[0]); + } + + nfc_connstring devices[8]; + size_t device_count; + + nfc_context *context; + nfc_init(&context); + if (context == NULL) + errx(EXIT_FAILURE, "Unable to init libnfc (malloc)"); + + device_count = nfc_list_devices(context, devices, sizeof(devices) / sizeof(*devices)); + if (device_count <= 0) + errx(EXIT_FAILURE, "No NFC device found"); + + for (size_t d = 0; d < device_count; d++) { + if (!(device = nfc_open(context, devices[d]))) { + warnx("nfc_open() failed."); + error = EXIT_FAILURE; + continue; + } + + if (!(tags = freefare_get_tags(device))) { + nfc_close(device); + errx(EXIT_FAILURE, "Error listing tags."); + } + + for (int i = 0; (!error) && tags[i]; i++) { + int res; + FreefareTag tag = tags[i]; + char *tag_uid = freefare_get_tag_uid(tag); + + switch (freefare_get_tag_type(tag)) { + case MIFARE_ULTRALIGHT_C: + if (mifare_ultralight_connect(tag) < 0) { + errx(EXIT_FAILURE, "Error connecting to tag %s.", tag_uid); + } + break; + default: + continue; + } + + if (mifare_key_deriver_begin(deriver) < 0) { + errx(EXIT_FAILURE, "Error starting key diversification"); + } + + if (mifare_key_deriver_update_uid(deriver, tag) < 0) { + errx(EXIT_FAILURE, "Error with key diversification"); + } + + if ((derived_key = mifare_key_deriver_end(deriver)) == NULL) { + errx(EXIT_FAILURE, "Error with key diversification"); + } + + if (undiversify) { + res = swap_keys(tag, master_key, derived_key); + } else { + res = swap_keys(tag, derived_key, master_key); + } + + printf("%siversification of tag with UID %s %s.\n", undiversify?"Und":"D", tag_uid, res?"FAILED":"succeded"); + + mifare_desfire_key_free(derived_key); + mifare_ultralight_disconnect(tag); + free(tag_uid); + } + + freefare_free_tags(tags); + nfc_close(device); + } + + mifare_desfire_key_free(master_key); + mifare_key_deriver_free(deriver); + nfc_exit(context); + exit(error); +} diff --git a/libfreefare/CMakeLists.txt b/libfreefare/CMakeLists.txt index 312b9dc..2015f22 100644 --- a/libfreefare/CMakeLists.txt +++ b/libfreefare/CMakeLists.txt @@ -10,6 +10,7 @@ set(LIBRARY_SOURCES mifare_desfire_crypto mifare_desfire_error mifare_desfire_key + mifare_key_deriver mifare_ultralight ntag21x tlv diff --git a/libfreefare/Makefile.am b/libfreefare/Makefile.am index cddb74b..5238c4b 100644 --- a/libfreefare/Makefile.am +++ b/libfreefare/Makefile.am @@ -12,6 +12,7 @@ libfreefare_la_SOURCES = felica.c \ mifare_desfire_crypto.c \ mifare_desfire_error.c \ mifare_desfire_key.c \ + mifare_key_deriver.c \ mad.c \ mifare_application.c \ ntag21x.c \ @@ -39,6 +40,7 @@ man_MANS = freefare.3 \ mifare_desfire.3 \ mifare_desfire_aid.3 \ mifare_desfire_key.3 \ + mifare_key_deriver.3 \ mifare_ultralight.3 \ ntag21x.3 \ tlv.3 diff --git a/libfreefare/freefare.h b/libfreefare/freefare.h index 85b1668..914ab87 100644 --- a/libfreefare/freefare.h +++ b/libfreefare/freefare.h @@ -469,6 +469,7 @@ int mifare_desfire_set_configuration(FreefareTag tag, bool disable_format, boo int mifare_desfire_set_default_key(FreefareTag tag, MifareDESFireKey key); int mifare_desfire_set_ats(FreefareTag tag, uint8_t *ats); int mifare_desfire_get_card_uid(FreefareTag tag, char **uid); +int mifare_desfire_get_card_uid_raw(FreefareTag tag, uint8_t uid[7]); int mifare_desfire_get_file_ids(FreefareTag tag, uint8_t **files, size_t *count); int mifare_desfire_get_iso_file_ids(FreefareTag tag, uint16_t **files, size_t *count); int mifare_desfire_get_file_settings(FreefareTag tag, uint8_t file_no, struct mifare_desfire_file_settings *settings); @@ -521,6 +522,28 @@ uint8_t *tlv_decode(const uint8_t *istream, uint8_t *type, uint16_t *size); size_t tlv_record_length(const uint8_t *istream, size_t *field_length_size, size_t *field_value_size); uint8_t *tlv_append(uint8_t *a, uint8_t *b); +typedef enum mifare_key_type { + MIFARE_KEY_DES, + MIFARE_KEY_2K3DES, + MIFARE_KEY_3K3DES, + MIFARE_KEY_AES128, + + MIFARE_KEY_LAST = MIFARE_KEY_AES128 +} MifareKeyType; + +struct mifare_key_deriver; +typedef struct mifare_key_deriver *MifareKeyDeriver; + +MifareKeyDeriver mifare_key_deriver_new_an10922(MifareDESFireKey master_key, MifareKeyType output_key_type); +int mifare_key_deriver_begin(MifareKeyDeriver deriver); +int mifare_key_deriver_update_data(MifareKeyDeriver deriver, const uint8_t *data, size_t len); +int mifare_key_deriver_update_uid(MifareKeyDeriver deriver, FreefareTag tag); +int mifare_key_deriver_update_aid(MifareKeyDeriver deriver, MifareDESFireAID aid); +int mifare_key_deriver_update_cstr(MifareKeyDeriver deriver, const char *cstr); +MifareDESFireKey mifare_key_deriver_end(MifareKeyDeriver deriver); +int mifare_key_deriver_end_raw(MifareKeyDeriver deriver, uint8_t* diversified_bytes, size_t data_max_len); +void mifare_key_deriver_free(MifareKeyDeriver state); + #ifdef __cplusplus } #endif // __cplusplus diff --git a/libfreefare/freefare_internal.h b/libfreefare/freefare_internal.h index 6b1c604..5f966b0 100644 --- a/libfreefare/freefare_internal.h +++ b/libfreefare/freefare_internal.h @@ -185,12 +185,7 @@ struct mifare_desfire_aid { struct mifare_desfire_key { uint8_t data[24]; - enum { - T_DES, - T_3DES, - T_3K3DES, - T_AES - } type; + MifareKeyType type; DES_key_schedule ks1; DES_key_schedule ks2; DES_key_schedule ks3; @@ -215,6 +210,13 @@ struct mifare_desfire_tag { uint32_t selected_application; }; +struct mifare_key_deriver { + MifareDESFireKey master_key; + MifareKeyType output_key_type; + uint8_t m[48]; + int len; +}; + MifareDESFireKey mifare_desfire_session_key_new(const uint8_t rnda[], const uint8_t rndb[], MifareDESFireKey authentication_key); const char *mifare_desfire_error_lookup(uint8_t error); diff --git a/libfreefare/mifare_desfire.c b/libfreefare/mifare_desfire.c index 462111f..d6c19b4 100644 --- a/libfreefare/mifare_desfire.c +++ b/libfreefare/mifare_desfire.c @@ -437,14 +437,14 @@ int mifare_desfire_authenticate(FreefareTag tag, uint8_t key_no, MifareDESFireKey key) { switch (key->type) { - case T_DES: - case T_3DES: + case MIFARE_KEY_DES: + case MIFARE_KEY_2K3DES: return authenticate(tag, AUTHENTICATE_LEGACY, key_no, key); break; - case T_3K3DES: + case MIFARE_KEY_3K3DES: return authenticate(tag, AUTHENTICATE_ISO, key_no, key); break; - case T_AES: + case MIFARE_KEY_AES128: return authenticate(tag, AUTHENTICATE_AES, key_no, key); break; } @@ -534,13 +534,13 @@ mifare_desfire_change_key(FreefareTag tag, uint8_t key_no, MifareDESFireKey new_ */ if (0x000000 == MIFARE_DESFIRE(tag)->selected_application) { switch (new_key->type) { - case T_DES: - case T_3DES: + case MIFARE_KEY_DES: + case MIFARE_KEY_2K3DES: break; - case T_3K3DES: + case MIFARE_KEY_3K3DES: key_no |= 0x40; break; - case T_AES: + case MIFARE_KEY_AES128: key_no |= 0x80; break; } @@ -551,12 +551,12 @@ mifare_desfire_change_key(FreefareTag tag, uint8_t key_no, MifareDESFireKey new_ int new_key_length; switch (new_key->type) { - case T_DES: - case T_3DES: - case T_AES: + case MIFARE_KEY_DES: + case MIFARE_KEY_2K3DES: + case MIFARE_KEY_AES128: new_key_length = 16; break; - case T_3K3DES: + case MIFARE_KEY_3K3DES: new_key_length = 24; break; } @@ -573,7 +573,7 @@ mifare_desfire_change_key(FreefareTag tag, uint8_t key_no, MifareDESFireKey new_ __cmd_n += new_key_length; - if (new_key->type == T_AES) + if (new_key->type == MIFARE_KEY_AES128) cmd[__cmd_n++] = new_key->aes_version; if ((MIFARE_DESFIRE(tag)->authenticated_key_no & 0x0f) != (key_no & 0x0f)) { @@ -1037,12 +1037,12 @@ mifare_desfire_set_default_key(FreefareTag tag, MifareDESFireKey key) BUFFER_APPEND(cmd, 0x01); size_t key_data_length; switch (key->type) { - case T_DES: - case T_3DES: - case T_AES: + case MIFARE_KEY_DES: + case MIFARE_KEY_2K3DES: + case MIFARE_KEY_AES128: key_data_length = 16; break; - case T_3K3DES: + case MIFARE_KEY_3K3DES: key_data_length = 24; break; } @@ -1101,7 +1101,7 @@ mifare_desfire_set_ats(FreefareTag tag, uint8_t *ats) } int -mifare_desfire_get_card_uid(FreefareTag tag, char **uid) +mifare_desfire_get_card_uid_raw(FreefareTag tag, uint8_t uid[]) { ASSERT_ACTIVE(tag); @@ -1122,6 +1122,20 @@ mifare_desfire_get_card_uid(FreefareTag tag, char **uid) if (!p) return errno = EINVAL, -1; + memcpy(uid, p, 7); + + return 0; +} + +int +mifare_desfire_get_card_uid(FreefareTag tag, char **uid) +{ + uint8_t p[7]; + + if (mifare_desfire_get_card_uid_raw(tag, p) < 0) { + return -1; + } + if (!(*uid = malloc(2 * 7 + 1))) { return -1; } diff --git a/libfreefare/mifare_desfire_crypto.c b/libfreefare/mifare_desfire_crypto.c index 4c8dc36..9f08da5 100644 --- a/libfreefare/mifare_desfire_crypto.c +++ b/libfreefare/mifare_desfire_crypto.c @@ -184,12 +184,12 @@ key_block_size(const MifareDESFireKey key) size_t block_size; switch (key->type) { - case T_DES: - case T_3DES: - case T_3K3DES: + case MIFARE_KEY_DES: + case MIFARE_KEY_2K3DES: + case MIFARE_KEY_3K3DES: block_size = 8; break; - case T_AES: + case MIFARE_KEY_AES128: block_size = 16; break; } @@ -206,12 +206,12 @@ key_macing_length(const MifareDESFireKey key) size_t mac_length; switch (key->type) { - case T_DES: - case T_3DES: + case MIFARE_KEY_DES: + case MIFARE_KEY_2K3DES: mac_length = MAC_LENGTH; break; - case T_3K3DES: - case T_AES: + case MIFARE_KEY_3K3DES: + case MIFARE_KEY_AES128: mac_length = CMAC_LENGTH; break; } @@ -669,7 +669,7 @@ mifare_cypher_single_block(MifareDESFireKey key, uint8_t *data, uint8_t *ivect, uint8_t edata[MAX_CRYPTO_BLOCK_SIZE]; switch (key->type) { - case T_DES: + case MIFARE_KEY_DES: switch (operation) { case MCO_ENCYPHER: DES_ecb_encrypt((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT); @@ -679,7 +679,7 @@ mifare_cypher_single_block(MifareDESFireKey key, uint8_t *data, uint8_t *ivect, break; } break; - case T_3DES: + case MIFARE_KEY_2K3DES: switch (operation) { case MCO_ENCYPHER: DES_ecb_encrypt((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT); @@ -693,7 +693,7 @@ mifare_cypher_single_block(MifareDESFireKey key, uint8_t *data, uint8_t *ivect, break; } break; - case T_3K3DES: + case MIFARE_KEY_3K3DES: switch (operation) { case MCO_ENCYPHER: DES_ecb_encrypt((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT); @@ -707,7 +707,7 @@ mifare_cypher_single_block(MifareDESFireKey key, uint8_t *data, uint8_t *ivect, break; } break; - case T_AES: + case MIFARE_KEY_AES128: switch (operation) { case MCO_ENCYPHER: AES_set_encrypt_key(key->data, 8 * 16, &k); diff --git a/libfreefare/mifare_desfire_key.c b/libfreefare/mifare_desfire_key.c index 7a0a9be..1c0b405 100644 --- a/libfreefare/mifare_desfire_key.c +++ b/libfreefare/mifare_desfire_key.c @@ -13,7 +13,7 @@ update_key_schedules(MifareDESFireKey key) { DES_set_key((DES_cblock *)key->data, &(key->ks1)); DES_set_key((DES_cblock *)(key->data + 8), &(key->ks2)); - if (T_3K3DES == key->type) { + if (MIFARE_KEY_3K3DES == key->type) { DES_set_key((DES_cblock *)(key->data + 16), &(key->ks3)); } } @@ -34,7 +34,7 @@ mifare_desfire_des_key_new_with_version(const uint8_t value[8]) MifareDESFireKey key; if ((key = malloc(sizeof(struct mifare_desfire_key)))) { - key->type = T_DES; + key->type = MIFARE_KEY_DES; memcpy(key->data, value, 8); memcpy(key->data + 8, value, 8); update_key_schedules(key); @@ -60,7 +60,7 @@ mifare_desfire_3des_key_new_with_version(const uint8_t value[16]) MifareDESFireKey key; if ((key = malloc(sizeof(struct mifare_desfire_key)))) { - key->type = T_3DES; + key->type = MIFARE_KEY_2K3DES; memcpy(key->data, value, 16); update_key_schedules(key); } @@ -83,7 +83,7 @@ mifare_desfire_3k3des_key_new_with_version(const uint8_t value[24]) MifareDESFireKey key; if ((key = malloc(sizeof(struct mifare_desfire_key)))) { - key->type = T_3K3DES; + key->type = MIFARE_KEY_3K3DES; memcpy(key->data, value, 24); update_key_schedules(key); } @@ -103,7 +103,7 @@ mifare_desfire_aes_key_new_with_version(const uint8_t value[16], uint8_t version if ((key = malloc(sizeof(struct mifare_desfire_key)))) { memcpy(key->data, value, 16); - key->type = T_AES; + key->type = MIFARE_KEY_AES128; key->aes_version = version; } return key; @@ -114,7 +114,7 @@ mifare_desfire_key_get_version(MifareDESFireKey key) { uint8_t version = 0; - if (key->type == T_AES) + if (key->type == MIFARE_KEY_AES128) return key->aes_version; for (int n = 0; n < 8; n++) { @@ -127,7 +127,7 @@ mifare_desfire_key_get_version(MifareDESFireKey key) void mifare_desfire_key_set_version(MifareDESFireKey key, uint8_t version) { - if (key->type == T_AES) { + if (key->type == MIFARE_KEY_AES128) { key->aes_version = version; return; } @@ -137,27 +137,27 @@ mifare_desfire_key_set_version(MifareDESFireKey key, uint8_t version) key->data[n] &= 0xfe; key->data[n] |= version_bit; switch (key->type) { - case T_DES: - // DESFire cards always treat DES keys as special cases of 2K3DES - // keys. The DESFire functional specification explicitly points - // out that if the subkeys of a 2K3DES key are exactly identical - // (including parity bits), then (and only then) is the key treated - // as a DES key for authentication purposes. Specifically, the - // version/parity bits must be idential, as well as the rest of the - // key, otherwise the PICC will treat it as a 2K3DES key. This - // next line ensure that. + case MIFARE_KEY_DES: + // DESFire cards always treat DES keys as special cases of 2K3DES + // keys. The DESFire functional specification explicitly points + // out that if the subkeys of a 2K3DES key are exactly identical + // (including parity bits), then (and only then) is the key treated + // as a DES key for authentication purposes. Specifically, the + // version/parity bits must be idential, as well as the rest of the + // key, otherwise the PICC will treat it as a 2K3DES key. This + // next line ensure that. key->data[n + 8] = key->data[n]; break; - case T_3DES: - // But what if we really did want the PICC to treat the key as a - // real 2K3DES key, even if the actual 56 bits of the subkeys did - // match? To ensure that such as case still works (largely because - // the datasheet implies authentication would behave differently - // otherwise), we need to ensure that the parity bits on the subkeys - // explicitly do not match. The easiest way to ensure that is to - // always write the bits of `~version` to the parity bits of the - // second subkey. Note that this would only have an effect at the - // PICC level if the subkeys were otherwise identical. + case MIFARE_KEY_2K3DES: + // But what if we really did want the PICC to treat the key as a + // real 2K3DES key, even if the actual 56 bits of the subkeys did + // match? To ensure that such as case still works (largely because + // the datasheet implies authentication would behave differently + // otherwise), we need to ensure that the parity bits on the subkeys + // explicitly do not match. The easiest way to ensure that is to + // always write the bits of `~version` to the parity bits of the + // second subkey. Note that this would only have an effect at the + // PICC level if the subkeys were otherwise identical. key->data[n + 8] &= 0xfe; key->data[n + 8] |= !version_bit; break; @@ -175,19 +175,19 @@ mifare_desfire_session_key_new(const uint8_t rnda[], const uint8_t rndb[], Mifar uint8_t buffer[24]; switch (authentication_key->type) { - case T_DES: + case MIFARE_KEY_DES: memcpy(buffer, rnda, 4); memcpy(buffer + 4, rndb, 4); key = mifare_desfire_des_key_new_with_version(buffer); break; - case T_3DES: + case MIFARE_KEY_2K3DES: memcpy(buffer, rnda, 4); memcpy(buffer + 4, rndb, 4); memcpy(buffer + 8, rnda + 4, 4); memcpy(buffer + 12, rndb + 4, 4); key = mifare_desfire_3des_key_new_with_version(buffer); break; - case T_3K3DES: + case MIFARE_KEY_3K3DES: memcpy(buffer, rnda, 4); memcpy(buffer + 4, rndb, 4); memcpy(buffer + 8, rnda + 6, 4); @@ -196,7 +196,7 @@ mifare_desfire_session_key_new(const uint8_t rnda[], const uint8_t rndb[], Mifar memcpy(buffer + 20, rndb + 12, 4); key = mifare_desfire_3k3des_key_new(buffer); break; - case T_AES: + case MIFARE_KEY_AES128: memcpy(buffer, rnda, 4); memcpy(buffer + 4, rndb, 4); memcpy(buffer + 8, rnda + 12, 4); diff --git a/libfreefare/mifare_key_deriver.3 b/libfreefare/mifare_key_deriver.3 new file mode 100644 index 0000000..686ce5e --- /dev/null +++ b/libfreefare/mifare_key_deriver.3 @@ -0,0 +1,169 @@ +.\" Copyright (C) 2018 Robert Quattlebaum +.\" +.\" 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 +.\" +.Dd January 4, 2018 +.Dt MIFARE_KEY_DERIVER 3 +.Os +.\" _ _ +.\" | \ | | __ _ _ __ ___ ___ +.\" | \| |/ _` | '_ ` _ \ / _ \ +.\" | |\ | (_| | | | | | | __/ +.\" |_| \_|\__,_|_| |_| |_|\___| +.\" +.Sh NAME +.Nm mifare_key_deriver_new_an10922 , +.Nm mifare_key_deriver_begin , +.Nm mifare_key_deriver_update_data , +.Nm mifare_key_deriver_update_uid , +.Nm mifare_key_deriver_update_aid , +.Nm mifare_key_deriver_update_cstr , +.Nm mifare_key_deriver_end , +.Nm mifare_key_deriver_end_raw , +.Nm mifare_key_deriver_free , +.Nd Mifare Key Derivation Functions +.\" _ _ _ +.\" | | (_) |__ _ __ __ _ _ __ _ _ +.\" | | | | '_ \| '__/ _` | '__| | | | +.\" | |___| | |_) | | | (_| | | | |_| | +.\" |_____|_|_.__/|_| \__,_|_| \__, | +.\" |___/ +.Sh LIBRARY +Mifare card manipulation library (libfreefare, \-lfreefare) +.\" ____ _ +.\" / ___| _ _ _ __ ___ _ __ ___(_)___ +.\" \___ \| | | | '_ \ / _ \| '_ \/ __| / __| +.\" ___) | |_| | | | | (_) | |_) \__ \ \__ \ +.\" |____/ \__, |_| |_|\___/| .__/|___/_|___/ +.\" |___/ |_| +.Sh SYNOPSIS +.In freefare.h +.Ft MifareKeyDeriver +.Fn mifare_key_deriver_new_an10922 "MifareDESFireKey master_key" "MifareKeyType output_key_type" +.Ft int +.Fn mifare_key_deriver_begin "MifareKeyDeriver deriver" +.Ft int +.Fn mifare_key_deriver_update_data "MifareKeyDeriver deriver" "const uint8_t *data" "size_t len" +.Ft int +.Fn mifare_key_deriver_update_uid "MifareKeyDeriver deriver" "FreefareTag tag" +.Ft int +.Fn mifare_key_deriver_update_aid "MifareKeyDeriver deriver" "MifareDESFireAID aid" +.Ft int +.Fn mifare_key_deriver_update_cstr "MifareKeyDeriver deriver" "const char *cstr" +.Ft MifareDESFireKey +.Fn mifare_key_deriver_end "MifareKeyDeriver deriver" +.Ft int +.Fn mifare_key_deriver_end_raw "MifareKeyDeriver deriver" "uint8_t* derived_data" "size_t data_max_len" +.Ft void +.Fn mifare_key_deriver_free "MifareKeyDeriver deriver" +.\" ____ _ _ _ +.\" | _ \ ___ ___ ___ _ __(_)_ __ | |_(_) ___ _ __ +.\" | | | |/ _ \/ __|/ __| '__| | '_ \| __| |/ _ \| '_ \ +.\" | |_| | __/\__ \ (__| | | | |_) | |_| | (_) | | | | +.\" |____/ \___||___/\___|_| |_| .__/ \__|_|\___/|_| |_| +.\" |_| +.Sh DESCRIPTION +The +.Fn mifare_key_deriver_* +family of functions allows for the diversification of Mifare DESFire keys. +.Pp +The +.Fn mifare_key_deriver_new_an10922 +function alocates a new key deriver object which can be used to generate +diversified keys from +.Va master_key +in accordinance with AN10922. +.Pp +The +.Fn mifare_key_deriver_begin +function marks the start of the derivation of a new diversified key. +.Pp +The +.Fn mifare_key_deriver_update_data , +.Fn mifare_key_deriver_update_uid , +.Fn mifare_key_deriver_update_aid +and +.Fn mifare_key_deriver_update_cstr +functions are used to specify the information that should be used to derive +the diversified key from the master key. +.Pp +The +.Fn mifare_key_deriver_end +function marks the end of the derivation and returns the new diversified key. +It is the responsibility of the caller to to free the returned key by calling +.Fn mifare_desfire_key_free . +.Fn mifare_key_deriver_end_raw +is a variant used to directly fetch the raw bytes of the derived key. +.Pp +.\" ____ _ _ +.\" | _ \ ___| |_ _ _ _ __ _ __ __ ____ _| |_ _ ___ ___ +.\" | |_) / _ \ __| | | | '__| '_ \ \ \ / / _` | | | | |/ _ \/ __| +.\" | _ < __/ |_| |_| | | | | | | \ V / (_| | | |_| | __/\__ \ +.\" |_| \_\___|\__|\__,_|_| |_| |_| \_/ \__,_|_|\__,_|\___||___/ +.\" +.Sh RETURN VALUES +.Fn mifare_key_deriver_new_an10922 +returns the allocated key deriver or +.Va NULL +on failure. +.Pp +The +.Fn mifare_key_deriver_begin , +.Fn mifare_key_deriver_update_data , +.Fn mifare_key_deriver_update_uid , +.Fn mifare_key_deriver_update_aid +and +.Fn mifare_key_deriver_update_cstr +functions return +.Va 0 +on success and +.Va -1 +on failure. +.Pp +The +.Fn mifare_key_deriver_end +function returns the new diversified key on success and +.Va NULL +on failure. It is the responsibility of the +caller to to free the returned key by calling +.Fn mifare_desfire_key_free . +.Pp +The +.Fn mifare_key_deriver_end_raw +function returns +.Va -1 +on failure. On success, it returns the number of bytes that were derived. If +.Va data_max_len +is smaller than the return value, then no bytes were written to +.Va derived_data . +.Pp +Upon failure, all methods update +.Va errno +with the appropriate error code. +.\" ____ _ +.\" / ___| ___ ___ __ _| |___ ___ +.\" \___ \ / _ \/ _ \ / _` | / __|/ _ \ +.\" ___) | __/ __/ | (_| | \__ \ (_) | +.\" |____/ \___|\___| \__,_|_|___/\___/ +.\" +.Sh SEE ALSO +.Xr mifare_desfire_key 3 +.\" _ _ _ +.\" / \ _ _| |_| |__ ___ _ __ ___ +.\" / _ \| | | | __| '_ \ / _ \| '__/ __| +.\" / ___ \ |_| | |_| | | | (_) | | \__ \ +.\" /_/ \_\__,_|\__|_| |_|\___/|_| |___/ +.\" +.Sh AUTHORS +.An Robert Quattlebaum darco@deepdarc.com diff --git a/libfreefare/mifare_key_deriver.c b/libfreefare/mifare_key_deriver.c new file mode 100644 index 0000000..d79db87 --- /dev/null +++ b/libfreefare/mifare_key_deriver.c @@ -0,0 +1,301 @@ +#include +#include +#include + +#include + +#include +#include "freefare_internal.h" + +#define AN10922_DIV_AES128 0x01 +#define AN10922_DIV_AES192_1 0x11 +#define AN10922_DIV_AES192_2 0x12 +#define AN10922_DIV_2K3DES_1 0x21 +#define AN10922_DIV_2K3DES_2 0x22 +#define AN10922_DIV_3K3DES_1 0x31 +#define AN10922_DIV_3K3DES_2 0x32 +#define AN10922_DIV_3K3DES_3 0x33 + +MifareKeyDeriver +mifare_key_deriver_new_an10922(MifareDESFireKey master_key, MifareKeyType output_key_type) +{ + MifareKeyDeriver deriver = NULL; + const int master_key_block_size = key_block_size(master_key); + + switch(output_key_type) { + case MIFARE_KEY_AES128: + if (master_key_block_size != 16) { + errno = EINVAL; + return NULL; + } + break; + + case MIFARE_KEY_2K3DES: + if ((master_key_block_size != 8) && (master_key_block_size != 16)) { + errno = EINVAL; + return NULL; + } + break; + + case MIFARE_KEY_3K3DES: + if (master_key_block_size != 8) { + errno = EINVAL; + return NULL; + } + break; + + case MIFARE_KEY_DES: + // AN10922 doesn't define a DIV constant for + // deriving plain 56-bit DES keys. + default: + // Unsupported output key type. + errno = EINVAL; + return NULL; + } + + if ((deriver = malloc(sizeof(struct mifare_key_deriver)))) { + deriver->master_key = master_key; + deriver->output_key_type = output_key_type; + cmac_generate_subkeys(deriver->master_key); + } + + return deriver; +} + +void +mifare_key_deriver_free(MifareKeyDeriver deriver) +{ + memset(deriver, 0, sizeof(*deriver)); + free(deriver); +} + +int +mifare_key_deriver_begin(MifareKeyDeriver deriver) +{ + memset(deriver->m, 0, sizeof(deriver->m)); + + // We skip byte zero for the DIV constant, which + // we will fill out in the call to end(). We also + // use len==0 as an overflow error condition. + deriver->len = 1; + + return 0; +} + +int +mifare_key_deriver_update_data(MifareKeyDeriver deriver, const uint8_t *data, size_t len) +{ + if (deriver->len == 0) { + // Overflow from previous update call. + errno = EOVERFLOW; + return -1; + } + + if (len > sizeof(deriver->m) - deriver->len) { + deriver->len = 0; // Remember that we have an error. + errno = EOVERFLOW; + return -1; + } + + memcpy(deriver->m + deriver->len, data, len); + deriver->len += (int)len; + + return 0; +} + +int +mifare_key_deriver_update_cstr(MifareKeyDeriver deriver, const char *cstr) +{ + return mifare_key_deriver_update_data(deriver, (const uint8_t*)cstr, strlen(cstr)); +} + +int +mifare_key_deriver_update_aid(MifareKeyDeriver deriver, MifareDESFireAID aid) +{ + return mifare_key_deriver_update_data(deriver, aid->data, sizeof(aid->data)); +} + +int +mifare_key_deriver_update_uid(MifareKeyDeriver deriver, FreefareTag tag) +{ + int ret = 0; + const uint8_t* uid_data = NULL; + uint8_t desfire_uid[7]; + uint8_t uid_len = 0; + + switch (tag->info.nm.nmt) { + case NMT_FELICA: + uid_data = tag->info.nti.nfi.abtId; + uid_len = 8; + break; + case NMT_ISO14443A: + uid_data = tag->info.nti.nai.abtUid; + uid_len = tag->info.nti.nai.szUidLen; + break; + case NMT_DEP: + case NMT_ISO14443B2CT: + case NMT_ISO14443B2SR: + case NMT_ISO14443B: + case NMT_ISO14443BI: + case NMT_JEWEL: + case NMT_BARCODE: + ret = -1; + errno = EINVAL; + break; + } + + if ((uid_len == 4) && (freefare_get_tag_type(tag) == MIFARE_DESFIRE)) { + // DESFire card is using random UID. We need + // to explicitly get the real static UID. + + if (mifare_desfire_get_card_uid_raw(tag, desfire_uid) < 0) { + ret = -1; + } else { + uid_data = desfire_uid; + } + } + + if (ret >= 0) { + ret = mifare_key_deriver_update_data(deriver, uid_data, uid_len); + } + + return ret; +} + +static void +deriver_cmac(MifareKeyDeriver deriver, uint8_t* output) +{ + uint8_t ivect[24]; + memset(ivect, 0, sizeof(ivect)); + cmac(deriver->master_key, ivect, deriver->m, deriver->len, output); +} + +static uint8_t +get_key_type_data_len(MifareKeyType type) +{ + switch(type) { + case MIFARE_KEY_AES128: + case MIFARE_KEY_2K3DES: + return 16; + + case MIFARE_KEY_DES: + return 8; + + case MIFARE_KEY_3K3DES: + return 24; + } + + // This should never happen. + return 0; +} + +static uint8_t +get_key_data_len(MifareDESFireKey key) +{ + return get_key_type_data_len(key->type); +} + +int +mifare_key_deriver_end_raw(MifareKeyDeriver deriver, uint8_t* diversified_bytes, size_t max_len) +{ + const uint8_t len = get_key_type_data_len(deriver->output_key_type); + const int master_key_block_size = key_block_size(deriver->master_key); + uint8_t data[24]; + + if (deriver->len == 0) { + // Overflow from previous update call. + // We must not emit a key if there was a previous error, + // otherwise bugs may go unnoticed. + errno = EOVERFLOW; + return -1; + } + + if (len == 0) { + errno = EINVAL; + return -1; + } + + if (max_len > len) { + max_len = len; + } + + memset(data, 0, sizeof(data)); + + if ((master_key_block_size == 16) && (deriver->output_key_type == MIFARE_KEY_AES128)) { + deriver->m[0] = AN10922_DIV_AES128; + deriver_cmac(deriver, data); + + } else if ((master_key_block_size == 16) && (deriver->output_key_type == MIFARE_KEY_2K3DES)) { + // This technically isn't defined in AN10922, but it is + // straightforward adaptation that is useful for diversifying + // MIFARE Ultralight C keys. + deriver->m[0] = AN10922_DIV_2K3DES_1; + deriver_cmac(deriver, data); + + } else if ((master_key_block_size == 8) && (deriver->output_key_type == MIFARE_KEY_2K3DES)) { + deriver->m[0] = AN10922_DIV_2K3DES_1; + deriver_cmac(deriver, data + 0); + deriver->m[0] = AN10922_DIV_2K3DES_2; + deriver_cmac(deriver, data + 8); + + } else if ((master_key_block_size == 8) && (deriver->output_key_type == MIFARE_KEY_3K3DES)) { + deriver->m[0] = AN10922_DIV_3K3DES_1; + deriver_cmac(deriver, data + 0); + deriver->m[0] = AN10922_DIV_3K3DES_2; + deriver_cmac(deriver, data + 8); + deriver->m[0] = AN10922_DIV_3K3DES_3; + deriver_cmac(deriver, data + 16); + + } else { + // AN10922 doesn't describe how to perform this derivation. + errno = EINVAL; + return -1; + } + + memcpy(diversified_bytes, data, max_len); + + // Wipe key info from stack + memset(data, 0, sizeof(data)); + + return len; +} + +MifareDESFireKey +mifare_key_deriver_end(MifareKeyDeriver deriver) +{ + MifareDESFireKey ret = NULL; + uint8_t data[24]; + int len = mifare_key_deriver_end_raw(deriver, data, sizeof(data)); + + if (len <= 0) { + return NULL; + } + + switch (deriver->output_key_type) { + case MIFARE_KEY_AES128: + ret = mifare_desfire_aes_key_new_with_version(data, 0); + break; + + case MIFARE_KEY_DES: + ret = mifare_desfire_des_key_new(data); + break; + + case MIFARE_KEY_2K3DES: + ret = mifare_desfire_3des_key_new(data); + break; + + case MIFARE_KEY_3K3DES: + ret = mifare_desfire_3k3des_key_new(data); + break; + } + + // Update the key version + if (ret != NULL) { + mifare_desfire_key_set_version(ret, mifare_desfire_key_get_version(deriver->master_key)); + } + + // Wipe key info from stack + memset(data, 0, sizeof(data)); + + return ret; +} diff --git a/libfreefare/mifare_ultralight.c b/libfreefare/mifare_ultralight.c index f85b478..f71555e 100644 --- a/libfreefare/mifare_ultralight.c +++ b/libfreefare/mifare_ultralight.c @@ -340,7 +340,7 @@ mifare_ultralightc_set_key(FreefareTag tag, MifareDESFireKey key) { MifareUltralightPage data; - if (key->type != T_3DES) { + if (key->type != MIFARE_KEY_2K3DES) { errno = EINVAL; return -1; } diff --git a/test/Makefile.am b/test/Makefile.am index 797d192..9a3c62d 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -24,6 +24,7 @@ cutter_unit_test_libs = \ test_mifare_desfire_des.la \ test_mifare_desfire_ev1.la \ test_mifare_desfire_key.la \ + test_mifare_key_deriver_an10922.la \ test_mifare_ultralight.la \ test_tlv.la @@ -89,6 +90,9 @@ test_mifare_desfire_ev1_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la \ test_mifare_desfire_key_la_SOURCES = test_mifare_desfire_key.c test_mifare_desfire_key_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la +test_mifare_key_deriver_an10922_la_SOURCES = test_mifare_key_deriver_an10922.c +test_mifare_key_deriver_an10922_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la + test_mifare_ultralight_la_SOURCES = test_mifare_ultralight.c \ mifare_ultralight_fixture.c \ fixture.h diff --git a/test/test_mifare_key_deriver_an10922.c b/test/test_mifare_key_deriver_an10922.c new file mode 100644 index 0000000..bc4a965 --- /dev/null +++ b/test/test_mifare_key_deriver_an10922.c @@ -0,0 +1,143 @@ +#include + +#include +#include "freefare_internal.h" + +void +test_mifare_key_deriver_an10922_aes128(void) +{ + MifareDESFireKey key = NULL; + MifareDESFireKey derived_key = NULL; + MifareKeyDeriver deriver = NULL; + int version, ret; + + // These test vectors come from NCP's AN10922, section 2.2.1 + uint8_t key1_aes128_data[16] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0XEE, 0xFF }; + uint8_t key1_aes128_version = 16; + uint8_t key1_aes128_derived_data[16] = { 0xA8, 0xDD, 0x63, 0xA3, 0xB8, 0x9D, 0x54, 0xB3, 0x7C, 0xA8, 0x02, 0x47, 0x3F, 0xDA, 0x91, 0x75 }; + uint8_t key1_aes128_check_m[] = { 0x01, 0x04, 0x78, 0x2E, 0x21, 0x80, 0x1D, 0x80, 0x30, 0x42, 0xF5, 0x4E, 0x58, 0x50, 0x20, 0x41, 0x62, 0x75 }; + + key = mifare_desfire_aes_key_new_with_version(key1_aes128_data, key1_aes128_version); + + version = mifare_desfire_key_get_version(key); + cut_assert_equal_int(key1_aes128_version, version, cut_message("Wrong master key version")); + + deriver = mifare_key_deriver_new_an10922(key, MIFARE_KEY_AES128); + + ret = mifare_key_deriver_begin(deriver); + cut_assert_equal_int(ret, 0, cut_message("mifare_key_deriver_begin failed")); + + ret = mifare_key_deriver_update_cstr(deriver, "\x04\x78\x2E\x21\x80\x1D\x80"); + cut_assert_equal_int(ret, 0, cut_message("mifare_key_deriver_update failed")); + + ret = mifare_key_deriver_update_cstr(deriver, "\x30\x42\xF5"); + cut_assert_equal_int(ret, 0, cut_message("mifare_key_deriver_update failed")); + + ret = mifare_key_deriver_update_cstr(deriver, "NXP Abu"); + cut_assert_equal_int(ret, 0, cut_message("mifare_key_deriver_update failed")); + + derived_key = mifare_key_deriver_end(deriver); + cut_assert_not_null(derived_key, cut_message("mifare_key_deriver_end failed")); + + cut_assert_equal_memory(key1_aes128_check_m, sizeof(key1_aes128_check_m), deriver->m, deriver->len, cut_message("Wrong CMAC message")); + + version = mifare_desfire_key_get_version(derived_key); + cut_assert_equal_int(key1_aes128_version, version, cut_message("Wrong derived key version")); + + cut_assert_equal_int(derived_key->type, MIFARE_KEY_AES128, cut_message("Wrong derived key type")); + + cut_assert_equal_memory(key1_aes128_derived_data, sizeof(key1_aes128_derived_data), derived_key->data, sizeof(key1_aes128_derived_data), cut_message("Wrong derived key")); + mifare_key_deriver_free(deriver); + mifare_desfire_key_free(derived_key); + mifare_desfire_key_free(key); +} + +void +test_mifare_key_deriver_an10922_2k3des(void) +{ + MifareDESFireKey key = NULL; + MifareDESFireKey derived_key = NULL; + MifareKeyDeriver deriver = NULL; + int version, ret; + + // These test vectors come from NCP's AN10922, section 2.4.1 + uint8_t key1_2k3des_data[16] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0XEE, 0xFF }; + uint8_t key1_2k3des_derived_data[16] = { 0x16, 0xf9, 0x58, 0x7d, 0x9e, 0x89, 0x10, 0xc9, 0x6b, 0x96, 0x49, 0xd0, 0x07, 0x10, 0x7d, 0xd6 }; + uint8_t key1_2k3des_check_m[] = { 0x22, 0x04, 0x78, 0x2E, 0x21, 0x80, 0x1D, 0x80, 0x30, 0x42, 0xF5, 0x4E, 0x58, 0x50, 0x20, 0x41 }; + + key = mifare_desfire_3des_key_new_with_version(key1_2k3des_data); + + deriver = mifare_key_deriver_new_an10922(key, MIFARE_KEY_2K3DES); + + ret = mifare_key_deriver_begin(deriver); + cut_assert_equal_int(ret, 0, cut_message("mifare_key_deriver_begin failed")); + + ret = mifare_key_deriver_update_cstr(deriver, "\x04\x78\x2E\x21\x80\x1D\x80"); + cut_assert_equal_int(ret, 0, cut_message("mifare_key_deriver_update failed")); + + ret = mifare_key_deriver_update_cstr(deriver, "\x30\x42\xF5"); + cut_assert_equal_int(ret, 0, cut_message("mifare_key_deriver_update failed")); + + ret = mifare_key_deriver_update_cstr(deriver, "NXP A"); + cut_assert_equal_int(ret, 0, cut_message("mifare_key_deriver_update failed")); + + derived_key = mifare_key_deriver_end(deriver); + cut_assert_not_null(derived_key, cut_message("mifare_key_deriver_end failed")); + + cut_assert_equal_memory(key1_2k3des_check_m, sizeof(key1_2k3des_check_m), deriver->m, deriver->len, cut_message("Wrong CMAC message")); + + version = mifare_desfire_key_get_version(derived_key); + cut_assert_equal_int(mifare_desfire_key_get_version(key), version, cut_message("Wrong derived key version")); + + cut_assert_equal_int(derived_key->type, MIFARE_KEY_2K3DES, cut_message("Wrong derived key type")); + + cut_assert_equal_memory(key1_2k3des_derived_data, sizeof(key1_2k3des_derived_data), derived_key->data, sizeof(key1_2k3des_derived_data), cut_message("Wrong derived key")); + mifare_key_deriver_free(deriver); + mifare_desfire_key_free(derived_key); + mifare_desfire_key_free(key); +} + +void +test_mifare_key_deriver_an10922_3k3des(void) +{ + MifareDESFireKey key = NULL; + MifareDESFireKey derived_key = NULL; + MifareKeyDeriver deriver = NULL; + int version, ret; + + // These test vectors come from NCP's AN10922, section 2.5.1 + uint8_t key1_3k3des_data[24] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0XEE, 0xFF, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; + uint8_t key1_3k3des_derived_data[24] = { 0x2E, 0x0D, 0xD0, 0x37, 0x74, 0xD3, 0xFA, 0x9B, 0x57, 0x05, 0xAB, 0x0B, 0xDA, 0x91, 0xCA, 0x0B, 0x55, 0xB8, 0xE0, 0x7F, 0xCD, 0xBF, 0x10, 0xEC }; + uint8_t key1_3k3des_check_m[] = { 0x33, 0x04, 0x78, 0x2E, 0x21, 0x80, 0x1D, 0x80, 0x30, 0x42, 0xF5, 0x4E, 0x58, 0x50 }; + + key = mifare_desfire_3k3des_key_new_with_version(key1_3k3des_data); + + deriver = mifare_key_deriver_new_an10922(key, MIFARE_KEY_3K3DES); + + ret = mifare_key_deriver_begin(deriver); + cut_assert_equal_int(ret, 0, cut_message("mifare_key_deriver_begin failed")); + + ret = mifare_key_deriver_update_cstr(deriver, "\x04\x78\x2E\x21\x80\x1D\x80"); + cut_assert_equal_int(ret, 0, cut_message("mifare_key_deriver_update failed")); + + ret = mifare_key_deriver_update_cstr(deriver, "\x30\x42\xF5"); + cut_assert_equal_int(ret, 0, cut_message("mifare_key_deriver_update failed")); + + ret = mifare_key_deriver_update_cstr(deriver, "NXP"); + cut_assert_equal_int(ret, 0, cut_message("mifare_key_deriver_update failed")); + + derived_key = mifare_key_deriver_end(deriver); + cut_assert_not_null(derived_key, cut_message("mifare_key_deriver_end failed")); + + cut_assert_equal_memory(key1_3k3des_check_m, sizeof(key1_3k3des_check_m), deriver->m, deriver->len, cut_message("Wrong CMAC message")); + + version = mifare_desfire_key_get_version(derived_key); + cut_assert_equal_int(mifare_desfire_key_get_version(key), version, cut_message("Wrong derived key version")); + + cut_assert_equal_int(derived_key->type, MIFARE_KEY_3K3DES, cut_message("Wrong derived key type")); + + cut_assert_equal_memory(key1_3k3des_derived_data, sizeof(key1_3k3des_derived_data), derived_key->data, sizeof(key1_3k3des_derived_data), cut_message("Wrong derived key")); + mifare_key_deriver_free(deriver); + mifare_desfire_key_free(derived_key); + mifare_desfire_key_free(key); +}