Skip to content

Commit 607ea09

Browse files
committed
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
1 parent 262eace commit 607ea09

File tree

8 files changed

+149
-12
lines changed

8 files changed

+149
-12
lines changed

examples/mifare-ultralight-info.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ main(int argc, char *argv[])
6161
res = mifare_ultralightc_authenticate(tag, key);
6262
if (res != 0) {
6363
MifareDESFireKey diversified_key = NULL;
64-
MifareKeyDeriver deriver = mifare_key_deriver_new_an10922(key, MIFARE_KEY_2K3DES);
64+
MifareKeyDeriver deriver = mifare_key_deriver_new_an10922(key, MIFARE_KEY_2K3DES, AN10922_FLAG_DEFAULT);
6565

6666
mifare_key_deriver_begin(deriver);
6767
mifare_key_deriver_update_uid(deriver, tag);

examples/mifare-ultralightc-diversify.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ main(int argc, char *argv[])
3030
uint8_t key1_3des_data[16] = { 0x49, 0x45, 0x4D, 0x4B, 0x41, 0x45, 0x52, 0x42, 0x21, 0x4E, 0x41, 0x43, 0x55, 0x4F, 0x59, 0x46 };
3131
MifareDESFireKey master_key = mifare_desfire_3des_key_new(key1_3des_data);
3232
MifareDESFireKey derived_key = NULL;
33-
MifareKeyDeriver deriver = mifare_key_deriver_new_an10922(master_key, MIFARE_KEY_2K3DES);
33+
MifareKeyDeriver deriver = mifare_key_deriver_new_an10922(master_key, MIFARE_KEY_2K3DES, AN10922_FLAG_DEFAULT);
3434
bool undiversify = (argc == 2 && strcmp("--undiversify",argv[1]) == 0);
3535

3636
if (argc > 2 || (argc == 2 && strcmp("--undiversify",argv[1]) != 0)) {

libfreefare/freefare.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,10 @@ typedef enum mifare_key_type {
540540
struct mifare_key_deriver;
541541
typedef struct mifare_key_deriver *MifareKeyDeriver;
542542

543-
MifareKeyDeriver mifare_key_deriver_new_an10922(MifareDESFireKey master_key, MifareKeyType output_key_type);
543+
#define AN10922_FLAG_DEFAULT 0
544+
#define AN10922_FLAG_EMULATE_ISSUE_91 (1<<1)
545+
546+
MifareKeyDeriver mifare_key_deriver_new_an10922(MifareDESFireKey master_key, MifareKeyType output_key_type, int flags);
544547
int mifare_key_deriver_begin(MifareKeyDeriver deriver);
545548
int mifare_key_deriver_update_data(MifareKeyDeriver deriver, const uint8_t *data, size_t len);
546549
int mifare_key_deriver_update_uid(MifareKeyDeriver deriver, FreefareTag tag);

libfreefare/freefare_internal.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ size_t enciphered_data_length(const FreefareTag tag, const size_t nbytes, int
134134

135135
void cmac_generate_subkeys(MifareDESFireKey key);
136136
void cmac(const MifareDESFireKey key, uint8_t *ivect, const uint8_t *data, size_t len, uint8_t *cmac);
137+
void cmac_an10922(const MifareDESFireKey key, uint8_t *ivect, const uint8_t *data, size_t len, uint8_t *cmac);
137138
void *assert_crypto_buffer_size(FreefareTag tag, size_t nbytes);
138139

139140
#define MIFARE_ULTRALIGHT_PAGE_COUNT 0x10
@@ -213,8 +214,9 @@ struct mifare_desfire_tag {
213214
struct mifare_key_deriver {
214215
MifareDESFireKey master_key;
215216
MifareKeyType output_key_type;
216-
uint8_t m[48];
217+
uint8_t m[32];
217218
int len;
219+
int flags;
218220
};
219221

220222
MifareDESFireKey mifare_desfire_session_key_new(const uint8_t rnda[], const uint8_t rndb[], MifareDESFireKey authentication_key);

libfreefare/mifare_desfire_crypto.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,40 @@ cmac(const MifareDESFireKey key, uint8_t *ivect, const uint8_t *data, size_t len
144144
free(buffer);
145145
}
146146

147+
void
148+
cmac_an10922(const MifareDESFireKey key, uint8_t *ivect, const uint8_t *data, size_t len, uint8_t *cmac)
149+
{
150+
int kbs = key_block_size(key);
151+
int buffer_len = kbs*2;
152+
153+
// Contract for this function requires that the data fit in two blocks.
154+
if (len > buffer_len)
155+
abort();
156+
157+
uint8_t *buffer = malloc(buffer_len);
158+
159+
if (!buffer)
160+
abort();
161+
162+
memcpy(buffer, data, len);
163+
164+
if (len != buffer_len) {
165+
buffer[len++] = 0x80;
166+
while (len != buffer_len) {
167+
buffer[len++] = 0x00;
168+
}
169+
xor(key->cmac_sk2, buffer + len - kbs, kbs);
170+
} else {
171+
xor(key->cmac_sk1, buffer + len - kbs, kbs);
172+
}
173+
174+
mifare_cypher_blocks_chained(NULL, key, ivect, buffer, len, MCD_SEND, MCO_ENCYPHER);
175+
176+
memcpy(cmac, ivect, kbs);
177+
178+
free(buffer);
179+
}
180+
147181
#define CRC32_PRESET 0xFFFFFFFF
148182

149183
static void

libfreefare/mifare_key_deriver.3

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ Mifare card manipulation library (libfreefare, \-lfreefare)
5050
.Sh SYNOPSIS
5151
.In freefare.h
5252
.Ft MifareKeyDeriver
53-
.Fn mifare_key_deriver_new_an10922 "MifareDESFireKey master_key" "MifareKeyType output_key_type"
53+
.Fn mifare_key_deriver_new_an10922 "MifareDESFireKey master_key" "MifareKeyType output_key_type" "int flags"
5454
.Ft int
5555
.Fn mifare_key_deriver_begin "MifareKeyDeriver deriver"
5656
.Ft int
@@ -83,7 +83,11 @@ The
8383
function alocates a new key deriver object which can be used to generate
8484
diversified keys from
8585
.Va master_key
86-
in accordinance with AN10922.
86+
in accordinance with AN10922 when the flags field is is set to AN10922_FLAG_DEFAULT.
87+
When the flags field is set to AN10922_FLAG_EMULATE_ISSUE_91, the resulting key
88+
deriver will use the non-AN10922-compliant key derivation that was originally being
89+
used by this API. All new deployments should use AN10922_FLAG_DEFAULT. See issue #91 for
90+
more information.
8791
.Pp
8892
The
8993
.Fn mifare_key_deriver_begin

libfreefare/mifare_key_deriver.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
#define AN10922_DIV_3K3DES_3 0x33
1818

1919
MifareKeyDeriver
20-
mifare_key_deriver_new_an10922(MifareDESFireKey master_key, MifareKeyType output_key_type)
20+
mifare_key_deriver_new_an10922(MifareDESFireKey master_key, MifareKeyType output_key_type, int flags)
2121
{
2222
MifareKeyDeriver deriver = NULL;
2323
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
5757
deriver->master_key = master_key;
5858
deriver->output_key_type = output_key_type;
5959
cmac_generate_subkeys(deriver->master_key);
60+
deriver->flags = flags;
6061
}
6162

6263
return deriver;
@@ -91,7 +92,7 @@ mifare_key_deriver_update_data(MifareKeyDeriver deriver, const uint8_t *data, si
9192
return -1;
9293
}
9394

94-
if (len > sizeof(deriver->m) - deriver->len) {
95+
if (len > key_block_size(deriver->master_key) * 2 - deriver->len) {
9596
deriver->len = 0; // Remember that we have an error.
9697
errno = EOVERFLOW;
9798
return -1;
@@ -167,7 +168,13 @@ deriver_cmac(MifareKeyDeriver deriver, uint8_t* output)
167168
{
168169
uint8_t ivect[24];
169170
memset(ivect, 0, sizeof(ivect));
170-
cmac(deriver->master_key, ivect, deriver->m, deriver->len, output);
171+
if (deriver->flags & AN10922_FLAG_EMULATE_ISSUE_91) {
172+
// Restores the old non-AN10922-compiant derivation
173+
// that was fixed by issue #91.
174+
cmac(deriver->master_key, ivect, deriver->m, deriver->len, output);
175+
} else {
176+
cmac_an10922(deriver->master_key, ivect, deriver->m, deriver->len, output);
177+
}
171178
}
172179

173180
static uint8_t

test/test_mifare_key_deriver_an10922.c

Lines changed: 90 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ test_mifare_key_deriver_an10922_aes128(void)
2222
version = mifare_desfire_key_get_version(key);
2323
cut_assert_equal_int(key1_aes128_version, version, cut_message("Wrong master key version"));
2424

25-
deriver = mifare_key_deriver_new_an10922(key, MIFARE_KEY_AES128);
25+
deriver = mifare_key_deriver_new_an10922(key, MIFARE_KEY_AES128, AN10922_FLAG_DEFAULT);
2626

2727
ret = mifare_key_deriver_begin(deriver);
2828
cut_assert_equal_int(ret, 0, cut_message("mifare_key_deriver_begin failed"));
@@ -52,6 +52,93 @@ test_mifare_key_deriver_an10922_aes128(void)
5252
mifare_desfire_key_free(key);
5353
}
5454

55+
void
56+
test_mifare_key_deriver_an10922_aes128_short_m(void)
57+
{
58+
MifareDESFireKey key = NULL;
59+
MifareDESFireKey derived_key = NULL;
60+
MifareKeyDeriver deriver = NULL;
61+
int version, ret;
62+
63+
// These test vectors came from AN10957, pages 13-14
64+
uint8_t key1_aes128_data[16] = { 0xf3, 0xf9, 0x37, 0x76, 0x98, 0x70, 0x7b, 0x68, 0x8e, 0xaf, 0x84, 0xab, 0xe3, 0x9e, 0x37, 0x91 };
65+
uint8_t key1_aes128_version = 16;
66+
uint8_t key1_aes128_derived_data[16] = { 0x0b, 0xb4, 0x08, 0xba, 0xff, 0x98, 0xb6, 0xee, 0x9f, 0x2e, 0x15, 0x85, 0x77, 0x7f, 0x6a, 0x51 };
67+
uint8_t key1_aes128_check_m[] = { 0x01, 0x04, 0xde, 0xad, 0xbe, 0xef, 0xfe, 0xed };
68+
69+
key = mifare_desfire_aes_key_new_with_version(key1_aes128_data, key1_aes128_version);
70+
71+
version = mifare_desfire_key_get_version(key);
72+
cut_assert_equal_int(key1_aes128_version, version, cut_message("Wrong master key version"));
73+
74+
deriver = mifare_key_deriver_new_an10922(key, MIFARE_KEY_AES128, AN10922_FLAG_DEFAULT);
75+
76+
ret = mifare_key_deriver_begin(deriver);
77+
cut_assert_equal_int(ret, 0, cut_message("mifare_key_deriver_begin failed"));
78+
79+
ret = mifare_key_deriver_update_cstr(deriver, "\x04\xde\xad\xbe\xef\xfe\xed");
80+
cut_assert_equal_int(ret, 0, cut_message("mifare_key_deriver_update failed"));
81+
82+
derived_key = mifare_key_deriver_end(deriver);
83+
cut_assert_not_null(derived_key, cut_message("mifare_key_deriver_end failed"));
84+
85+
cut_assert_equal_memory(key1_aes128_check_m, sizeof(key1_aes128_check_m), deriver->m, deriver->len, cut_message("Wrong CMAC message"));
86+
87+
version = mifare_desfire_key_get_version(derived_key);
88+
cut_assert_equal_int(key1_aes128_version, version, cut_message("Wrong derived key version"));
89+
90+
cut_assert_equal_int(derived_key->type, MIFARE_KEY_AES128, cut_message("Wrong derived key type"));
91+
92+
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"));
93+
mifare_key_deriver_free(deriver);
94+
mifare_desfire_key_free(derived_key);
95+
mifare_desfire_key_free(key);
96+
}
97+
98+
void
99+
test_mifare_key_deriver_an10922_aes128_issue_91(void)
100+
{
101+
MifareDESFireKey key = NULL;
102+
MifareDESFireKey derived_key = NULL;
103+
MifareKeyDeriver deriver = NULL;
104+
int version, ret;
105+
106+
// These test vectors came from AN10957, pages 13-14; EXCEPT that the derived
107+
// data reflects the use of the AN10922_FLAG_EMULATE_ISSUE_91 flag.
108+
uint8_t key1_aes128_data[16] = { 0xf3, 0xf9, 0x37, 0x76, 0x98, 0x70, 0x7b, 0x68, 0x8e, 0xaf, 0x84, 0xab, 0xe3, 0x9e, 0x37, 0x91 };
109+
uint8_t key1_aes128_version = 16;
110+
uint8_t key1_aes128_derived_data[16] = { 0x72, 0x1e, 0x2c, 0x01, 0xe8, 0x1a, 0xf8, 0x5d, 0x81, 0x56, 0x33, 0x96, 0x9c, 0xea, 0x26, 0x07 };
111+
uint8_t key1_aes128_check_m[] = { 0x01, 0x04, 0xde, 0xad, 0xbe, 0xef, 0xfe, 0xed };
112+
113+
key = mifare_desfire_aes_key_new_with_version(key1_aes128_data, key1_aes128_version);
114+
115+
version = mifare_desfire_key_get_version(key);
116+
cut_assert_equal_int(key1_aes128_version, version, cut_message("Wrong master key version"));
117+
118+
deriver = mifare_key_deriver_new_an10922(key, MIFARE_KEY_AES128, AN10922_FLAG_EMULATE_ISSUE_91);
119+
120+
ret = mifare_key_deriver_begin(deriver);
121+
cut_assert_equal_int(ret, 0, cut_message("mifare_key_deriver_begin failed"));
122+
123+
ret = mifare_key_deriver_update_cstr(deriver, "\x04\xde\xad\xbe\xef\xfe\xed");
124+
cut_assert_equal_int(ret, 0, cut_message("mifare_key_deriver_update failed"));
125+
126+
derived_key = mifare_key_deriver_end(deriver);
127+
cut_assert_not_null(derived_key, cut_message("mifare_key_deriver_end failed"));
128+
129+
cut_assert_equal_memory(key1_aes128_check_m, sizeof(key1_aes128_check_m), deriver->m, deriver->len, cut_message("Wrong CMAC message"));
130+
131+
version = mifare_desfire_key_get_version(derived_key);
132+
cut_assert_equal_int(key1_aes128_version, version, cut_message("Wrong derived key version"));
133+
134+
cut_assert_equal_int(derived_key->type, MIFARE_KEY_AES128, cut_message("Wrong derived key type"));
135+
136+
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"));
137+
mifare_key_deriver_free(deriver);
138+
mifare_desfire_key_free(derived_key);
139+
mifare_desfire_key_free(key);
140+
}
141+
55142
void
56143
test_mifare_key_deriver_an10922_2k3des(void)
57144
{
@@ -67,7 +154,7 @@ test_mifare_key_deriver_an10922_2k3des(void)
67154

68155
key = mifare_desfire_3des_key_new_with_version(key1_2k3des_data);
69156

70-
deriver = mifare_key_deriver_new_an10922(key, MIFARE_KEY_2K3DES);
157+
deriver = mifare_key_deriver_new_an10922(key, MIFARE_KEY_2K3DES, AN10922_FLAG_DEFAULT);
71158

72159
ret = mifare_key_deriver_begin(deriver);
73160
cut_assert_equal_int(ret, 0, cut_message("mifare_key_deriver_begin failed"));
@@ -112,7 +199,7 @@ test_mifare_key_deriver_an10922_3k3des(void)
112199

113200
key = mifare_desfire_3k3des_key_new_with_version(key1_3k3des_data);
114201

115-
deriver = mifare_key_deriver_new_an10922(key, MIFARE_KEY_3K3DES);
202+
deriver = mifare_key_deriver_new_an10922(key, MIFARE_KEY_3K3DES, AN10922_FLAG_DEFAULT);
116203

117204
ret = mifare_key_deriver_begin(deriver);
118205
cut_assert_equal_int(ret, 0, cut_message("mifare_key_deriver_begin failed"));

0 commit comments

Comments
 (0)