Skip to content

Commit

Permalink
Support for AN10922 key derivation
Browse files Browse the repository at this point in the history
This commit implements [AN10922][] key diversification, as described in issue nfc-tools#77.

[AN10922]: https://www.nxp.com/docs/en/application-note/AN10922.pdf
  • Loading branch information
darconeous committed Jan 9, 2018
1 parent 3d398dc commit 9d88c18
Show file tree
Hide file tree
Showing 15 changed files with 864 additions and 68 deletions.
4 changes: 4 additions & 0 deletions examples/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down Expand Up @@ -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

Expand Down
23 changes: 22 additions & 1 deletion examples/mifare-ultralight-info.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
112 changes: 112 additions & 0 deletions examples/mifare-ultralightc-diversify.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#include <err.h>
#include <stdlib.h>
#include <string.h>

#include <nfc/nfc.h>

#include <freefare.h>

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);
}
1 change: 1 addition & 0 deletions libfreefare/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ set(LIBRARY_SOURCES
mifare_desfire_crypto
mifare_desfire_error
mifare_desfire_key
mifare_key_deriver
mifare_ultralight
ntag21x
tlv
Expand Down
2 changes: 2 additions & 0 deletions libfreefare/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down Expand Up @@ -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
Expand Down
23 changes: 23 additions & 0 deletions libfreefare/freefare.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down
14 changes: 8 additions & 6 deletions libfreefare/freefare_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);

Expand Down
50 changes: 32 additions & 18 deletions libfreefare/mifare_desfire.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand All @@ -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)) {
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);

Expand All @@ -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;
}
Expand Down

0 comments on commit 9d88c18

Please sign in to comment.