Properly handle edge cases in AN10922 key diversification
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
This commit is contained in:
parent
262eacedca
commit
ec91014ebf
8 changed files with 149 additions and 12 deletions
|
@ -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);
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -167,7 +168,13 @@ deriver_cmac(MifareKeyDeriver deriver, uint8_t* output)
|
|||
{
|
||||
uint8_t ivect[24];
|
||||
memset(ivect, 0, sizeof(ivect));
|
||||
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
|
||||
|
|
|
@ -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"));
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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"));
|
||||
|
@ -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"));
|
||||
|
|
Loading…
Reference in a new issue