Skip to content

Commit

Permalink
Properly handle edge cases in AN10922 key diversification
Browse files Browse the repository at this point in the history
This commit fixes issue #91.

[AN10922][] specifies the key diversification algorithms used by the
MIFARE SAM AV3. Support for these algorithms was added to
`libfreefare` via pull-request #79.

However, while every attempt was made to write a faithful
implementation, the implemented code did not properly handle cases
where the diversification data was less than or equal to the block
size of the cipher: 16 bytes for AES, and 8 bytes for DES. This
bug was identified in issue #91.

This commit addresses this problem while providing a way to revert to
the previous behavior in cases where it is necessary to maintain
previous deployments. This was accomplished by introducing a new
`flags` parameter to the `mifare_key_deriver_new_an10922` method.

Normally, `flags` should simply be set to `AN10922_FLAG_DEFAULT`.
However, if the previous behavior is required, it should be set to
`AN10922_FLAG_EMULATE_ISSUE_91`.

[AN10922][] does not include any test vectors that might have helped to
identify this problem earlier. However, [AN10957][] (pages 13-14) was
found to have a suitable example usage of [AN10922][] with an
appropriately short value for *M* that we are using as a test vector
to verify correct behavior.

Note that the issue being addressed here is not a security issue:
using the `AN10922_FLAG_EMULATE_ISSUE_91` should not be any less
secure than using `AN10922_FLAG_DEFAULT`.

[AN10922]: https://www.nxp.com/docs/en/application-note/AN10922.pdf
[AN10957]: https://www.nxp.com/docs/en/application-note/AN10957.pdf
  • Loading branch information
darconeous committed Oct 29, 2019
1 parent 262eace commit ec91014
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 12 deletions.
2 changes: 1 addition & 1 deletion examples/mifare-ultralight-info.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ main(int argc, char *argv[])
res = mifare_ultralightc_authenticate(tag, key);
if (res != 0) {
MifareDESFireKey diversified_key = NULL;
MifareKeyDeriver deriver = mifare_key_deriver_new_an10922(key, MIFARE_KEY_2K3DES);
MifareKeyDeriver deriver = mifare_key_deriver_new_an10922(key, MIFARE_KEY_2K3DES, AN10922_FLAG_DEFAULT);

mifare_key_deriver_begin(deriver);
mifare_key_deriver_update_uid(deriver, tag);
Expand Down
2 changes: 1 addition & 1 deletion examples/mifare-ultralightc-diversify.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ main(int argc, char *argv[])
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);
MifareKeyDeriver deriver = mifare_key_deriver_new_an10922(master_key, MIFARE_KEY_2K3DES, AN10922_FLAG_DEFAULT);
bool undiversify = (argc == 2 && strcmp("--undiversify",argv[1]) == 0);

if (argc > 2 || (argc == 2 && strcmp("--undiversify",argv[1]) != 0)) {
Expand Down
5 changes: 4 additions & 1 deletion libfreefare/freefare.h
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,10 @@ typedef enum mifare_key_type {
struct mifare_key_deriver;
typedef struct mifare_key_deriver *MifareKeyDeriver;

MifareKeyDeriver mifare_key_deriver_new_an10922(MifareDESFireKey master_key, MifareKeyType output_key_type);
#define AN10922_FLAG_DEFAULT 0
#define AN10922_FLAG_EMULATE_ISSUE_91 (1<<1)

MifareKeyDeriver mifare_key_deriver_new_an10922(MifareDESFireKey master_key, MifareKeyType output_key_type, int flags);
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);
Expand Down
4 changes: 3 additions & 1 deletion libfreefare/freefare_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ size_t enciphered_data_length(const FreefareTag tag, const size_t nbytes, int

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 cmac_an10922(const MifareDESFireKey key, uint8_t *ivect, const uint8_t *data, size_t len, uint8_t *cmac);
void *assert_crypto_buffer_size(FreefareTag tag, size_t nbytes);

#define MIFARE_ULTRALIGHT_PAGE_COUNT 0x10
Expand Down Expand Up @@ -213,8 +214,9 @@ struct mifare_desfire_tag {
struct mifare_key_deriver {
MifareDESFireKey master_key;
MifareKeyType output_key_type;
uint8_t m[48];
uint8_t m[32];
int len;
int flags;
};

MifareDESFireKey mifare_desfire_session_key_new(const uint8_t rnda[], const uint8_t rndb[], MifareDESFireKey authentication_key);
Expand Down
34 changes: 34 additions & 0 deletions libfreefare/mifare_desfire_crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,40 @@ cmac(const MifareDESFireKey key, uint8_t *ivect, const uint8_t *data, size_t len
free(buffer);
}

void
cmac_an10922(const MifareDESFireKey key, uint8_t *ivect, const uint8_t *data, size_t len, uint8_t *cmac)
{
int kbs = key_block_size(key);
int buffer_len = kbs*2;

// Contract for this function requires that the data fit in two blocks.
if (len > buffer_len)
abort();

uint8_t *buffer = malloc(buffer_len);

if (!buffer)
abort();

memcpy(buffer, data, len);

if (len != buffer_len) {
buffer[len++] = 0x80;
while (len != buffer_len) {
buffer[len++] = 0x00;
}
xor(key->cmac_sk2, buffer + len - kbs, kbs);
} else {
xor(key->cmac_sk1, buffer + len - kbs, kbs);
}

mifare_cypher_blocks_chained(NULL, key, ivect, buffer, len, MCD_SEND, MCO_ENCYPHER);

memcpy(cmac, ivect, kbs);

free(buffer);
}

#define CRC32_PRESET 0xFFFFFFFF

static void
Expand Down
8 changes: 6 additions & 2 deletions libfreefare/mifare_key_deriver.3
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ 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"
.Fn mifare_key_deriver_new_an10922 "MifareDESFireKey master_key" "MifareKeyType output_key_type" "int flags"
.Ft int
.Fn mifare_key_deriver_begin "MifareKeyDeriver deriver"
.Ft int
Expand Down Expand Up @@ -83,7 +83,11 @@ The
function alocates a new key deriver object which can be used to generate
diversified keys from
.Va master_key
in accordinance with AN10922.
in accordinance with AN10922 when the flags field is is set to AN10922_FLAG_DEFAULT.
When the flags field is set to AN10922_FLAG_EMULATE_ISSUE_91, the resulting key
deriver will use the non-AN10922-compliant key derivation that was originally being
used by this API. All new deployments should use AN10922_FLAG_DEFAULT. See issue #91 for
more information.
.Pp
The
.Fn mifare_key_deriver_begin
Expand Down
13 changes: 10 additions & 3 deletions libfreefare/mifare_key_deriver.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
#define AN10922_DIV_3K3DES_3 0x33

MifareKeyDeriver
mifare_key_deriver_new_an10922(MifareDESFireKey master_key, MifareKeyType output_key_type)
mifare_key_deriver_new_an10922(MifareDESFireKey master_key, MifareKeyType output_key_type, int flags)
{
MifareKeyDeriver deriver = NULL;
const int master_key_block_size = key_block_size(master_key);
Expand Down Expand Up @@ -57,6 +57,7 @@ mifare_key_deriver_new_an10922(MifareDESFireKey master_key, MifareKeyType output
deriver->master_key = master_key;
deriver->output_key_type = output_key_type;
cmac_generate_subkeys(deriver->master_key);
deriver->flags = flags;
}

return deriver;
Expand Down Expand Up @@ -91,7 +92,7 @@ mifare_key_deriver_update_data(MifareKeyDeriver deriver, const uint8_t *data, si
return -1;
}

if (len > sizeof(deriver->m) - deriver->len) {
if (len > key_block_size(deriver->master_key) * 2 - deriver->len) {
deriver->len = 0; // Remember that we have an error.
errno = EOVERFLOW;
return -1;
Expand Down Expand Up @@ -167,7 +168,13 @@ 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);
if (deriver->flags & AN10922_FLAG_EMULATE_ISSUE_91) {
// Restores the old non-AN10922-compiant derivation
// that was fixed by issue #91.
cmac(deriver->master_key, ivect, deriver->m, deriver->len, output);
} else {
cmac_an10922(deriver->master_key, ivect, deriver->m, deriver->len, output);
}
}

static uint8_t
Expand Down
93 changes: 90 additions & 3 deletions test/test_mifare_key_deriver_an10922.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ test_mifare_key_deriver_an10922_aes128(void)
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);
deriver = mifare_key_deriver_new_an10922(key, MIFARE_KEY_AES128, AN10922_FLAG_DEFAULT);

ret = mifare_key_deriver_begin(deriver);
cut_assert_equal_int(ret, 0, cut_message("mifare_key_deriver_begin failed"));
Expand Down Expand Up @@ -52,6 +52,93 @@ test_mifare_key_deriver_an10922_aes128(void)
mifare_desfire_key_free(key);
}

void
test_mifare_key_deriver_an10922_aes128_short_m(void)
{
MifareDESFireKey key = NULL;
MifareDESFireKey derived_key = NULL;
MifareKeyDeriver deriver = NULL;
int version, ret;

// These test vectors came from AN10957, pages 13-14
uint8_t key1_aes128_data[16] = { 0xf3, 0xf9, 0x37, 0x76, 0x98, 0x70, 0x7b, 0x68, 0x8e, 0xaf, 0x84, 0xab, 0xe3, 0x9e, 0x37, 0x91 };
uint8_t key1_aes128_version = 16;
uint8_t key1_aes128_derived_data[16] = { 0x0b, 0xb4, 0x08, 0xba, 0xff, 0x98, 0xb6, 0xee, 0x9f, 0x2e, 0x15, 0x85, 0x77, 0x7f, 0x6a, 0x51 };
uint8_t key1_aes128_check_m[] = { 0x01, 0x04, 0xde, 0xad, 0xbe, 0xef, 0xfe, 0xed };

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, AN10922_FLAG_DEFAULT);

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\xde\xad\xbe\xef\xfe\xed");
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_aes128_issue_91(void)
{
MifareDESFireKey key = NULL;
MifareDESFireKey derived_key = NULL;
MifareKeyDeriver deriver = NULL;
int version, ret;

// These test vectors came from AN10957, pages 13-14; EXCEPT that the derived
// data reflects the use of the AN10922_FLAG_EMULATE_ISSUE_91 flag.
uint8_t key1_aes128_data[16] = { 0xf3, 0xf9, 0x37, 0x76, 0x98, 0x70, 0x7b, 0x68, 0x8e, 0xaf, 0x84, 0xab, 0xe3, 0x9e, 0x37, 0x91 };
uint8_t key1_aes128_version = 16;
uint8_t key1_aes128_derived_data[16] = { 0x72, 0x1e, 0x2c, 0x01, 0xe8, 0x1a, 0xf8, 0x5d, 0x81, 0x56, 0x33, 0x96, 0x9c, 0xea, 0x26, 0x07 };
uint8_t key1_aes128_check_m[] = { 0x01, 0x04, 0xde, 0xad, 0xbe, 0xef, 0xfe, 0xed };

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, AN10922_FLAG_EMULATE_ISSUE_91);

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\xde\xad\xbe\xef\xfe\xed");
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)
{
Expand All @@ -67,7 +154,7 @@ test_mifare_key_deriver_an10922_2k3des(void)

key = mifare_desfire_3des_key_new_with_version(key1_2k3des_data);

deriver = mifare_key_deriver_new_an10922(key, MIFARE_KEY_2K3DES);
deriver = mifare_key_deriver_new_an10922(key, MIFARE_KEY_2K3DES, AN10922_FLAG_DEFAULT);

ret = mifare_key_deriver_begin(deriver);
cut_assert_equal_int(ret, 0, cut_message("mifare_key_deriver_begin failed"));
Expand Down Expand Up @@ -112,7 +199,7 @@ test_mifare_key_deriver_an10922_3k3des(void)

key = mifare_desfire_3k3des_key_new_with_version(key1_3k3des_data);

deriver = mifare_key_deriver_new_an10922(key, MIFARE_KEY_3K3DES);
deriver = mifare_key_deriver_new_an10922(key, MIFARE_KEY_3K3DES, AN10922_FLAG_DEFAULT);

ret = mifare_key_deriver_begin(deriver);
cut_assert_equal_int(ret, 0, cut_message("mifare_key_deriver_begin failed"));
Expand Down

0 comments on commit ec91014

Please sign in to comment.