Skip to content

Commit

Permalink
Added examples/mifare-desfire-ev1-change-picc-key
Browse files Browse the repository at this point in the history
Note: Work in progress.

This example demonstrates how to change the PICC key for
a DESFire EV1 card, while also aiming to be a generally useful
tool for locking/unlocking DESFire credentials.
  • Loading branch information
darconeous committed Oct 29, 2019
1 parent 607ea09 commit 7c609e3
Show file tree
Hide file tree
Showing 5 changed files with 272 additions and 9 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ examples/mifare-classic-read-ndef
examples/mifare-classic-write-ndef
examples/mifare-desfire-access
examples/mifare-desfire-create-ndef
examples/mifare-desfire-ev1-change-picc-key
examples/mifare-desfire-ev1-configure-ats
examples/mifare-desfire-ev1-configure-default-key
examples/mifare-desfire-ev1-configure-random-uid
Expand All @@ -36,6 +37,7 @@ examples/mifare-desfire-info
examples/mifare-desfire-read-ndef
examples/mifare-desfire-write-ndef
examples/mifare-ultralight-info
examples/mifare-ultralightc-diversify
examples/ntag-detect
examples/ntag-removeauth
examples/ntag-setauth
Expand Down
4 changes: 4 additions & 0 deletions examples/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ bin_PROGRAMS = felica-lite-dump \
mifare-desfire-ev1-configure-ats \
mifare-desfire-ev1-configure-default-key \
mifare-desfire-ev1-configure-random-uid \
mifare-desfire-ev1-change-picc-key \
mifare-desfire-format \
mifare-desfire-info \
mifare-desfire-read-ndef \
Expand Down Expand Up @@ -49,6 +50,9 @@ mifare_desfire_ev1_configure_ats_LDADD = $(top_builddir)/libfreefare/libfreefare
mifare_desfire_ev1_configure_default_key_SOURCES = mifare-desfire-ev1-configure-default-key.c
mifare_desfire_ev1_configure_default_key_LDADD = $(top_builddir)/libfreefare/libfreefare.la

mifare_desfire_ev1_change_picc_key_SOURCES = mifare-desfire-ev1-change-picc-key.c
mifare_desfire_ev1_change_picc_key_LDADD = $(top_builddir)/libfreefare/libfreefare.la

mifare_desfire_ev1_configure_random_uid_SOURCES = mifare-desfire-ev1-configure-random-uid.c
mifare_desfire_ev1_configure_random_uid_LDADD = $(top_builddir)/libfreefare/libfreefare.la

Expand Down
250 changes: 250 additions & 0 deletions examples/mifare-desfire-ev1-change-picc-key.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
#if defined(HAVE_CONFIG_H)
#include "config.h"
#endif

#include <err.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>

#include <nfc/nfc.h>

#include <freefare.h>

uint8_t null_key_data[8];

uint8_t new_key_version = 0x00;

MifareDESFireKey old_picc_key;
MifareDESFireKey new_picc_key;

#define NEW_KEY_VERSION new_key_version

struct {
bool interactive;
} configure_options = {
.interactive = true
};

static void
usage(char *progname)
{
fprintf(stderr, "usage: %s [-y]\n", progname);
fprintf(stderr, "\nOptions:\n");
fprintf(stderr, " -y Do not ask for confirmation\n");
fprintf(stderr, " -k Existing PICC key (Default is all zeros)\n");
fprintf(stderr, " -n New PICC key (Default is all zeros)\n");
fprintf(stderr, " -v New PICC key version (default is zero)\n");
}

#define strnequal(x, y, n) (strncmp(x, y, n) == 0)

static inline bool
strhasprefix(const char* str, const char* prefix) {
return strnequal(str, prefix, strlen(prefix));
}

MifareDESFireKey read_hex_desfire_key(const char* optarg) {
uint8_t buffer[24];
int i;
uint64_t n;

bool is_des = true;

if (strhasprefix(optarg, "DES:")) {
is_des = true;
optarg += 4;
} else if (strhasprefix(optarg, "AES:")) {
is_des = false;
optarg += 4;
}

size_t len = strlen(optarg);
size_t div16 = len / 16;

if (div16 < 1 || div16 > 3 || (len % 16) != 0) {
fprintf(stderr,"Bad key length\n");
exit(EXIT_FAILURE);
}

if (div16 >= 1) {
n = strtoull(optarg, NULL, 16);
for (i = 7; i >= 0; i--) {
buffer[i] = (uint8_t) n;
n >>= 8;
}
}

if (div16 >= 2) {
n = strtoull(optarg+8, NULL, 16);
for (i = 7; i >= 0; i--) {
buffer[i+8] = (uint8_t) n;
n >>= 8;
}
}

if (div16 == 3) {
n = strtoull(optarg+16, NULL, 16);
for (i = 7; i >= 0; i--) {
buffer[i+16] = (uint8_t) n;
n >>= 8;
}
}

if (is_des && div16 == 1) {
return mifare_desfire_des_key_new(buffer);
}
if (is_des && div16 == 2) {
return mifare_desfire_3des_key_new(buffer);
}
if (is_des && div16 == 3) {
return mifare_desfire_3k3des_key_new(buffer);
}
if (!is_des && div16 == 2) {
return mifare_desfire_aes_key_new(buffer);
}
fprintf(stderr,"Bad key length\n");
exit(EXIT_FAILURE);
}

int
main(int argc, char *argv[])
{
int ch;
int error = EXIT_SUCCESS;
nfc_device *device = NULL;
FreefareTag *tags = NULL;
bool should_diversify_new = false;

old_picc_key = mifare_desfire_des_key_new(null_key_data);
new_picc_key = mifare_desfire_des_key_new(null_key_data);

while ((ch = getopt(argc, argv, "hyk:n:v:D")) != -1) {
switch (ch) {
case 'h':
usage(argv[0]);
exit(EXIT_SUCCESS);
break;
case 'y':
configure_options.interactive = false;
break;
case 'k':
mifare_desfire_key_free(old_picc_key);
old_picc_key = read_hex_desfire_key(optarg);
break;
case 'n':
mifare_desfire_key_free(new_picc_key);
new_picc_key = read_hex_desfire_key(optarg);
break;
case 'v':
errno = 0;
new_key_version = (uint8_t)strtol(optarg, NULL, 0);
if (errno != 0) {
perror("strtol");
exit(EXIT_FAILURE);
}
break;
case 'D':
should_diversify_new = true;
break;
default:
usage(argv[0]);
exit(EXIT_FAILURE);
}
}
// Remaining args, if any, are in argv[optind .. (argc-1)]

mifare_desfire_key_set_version(new_picc_key, new_key_version);

MifareKeyDeriver new_deriver = NULL;
if (should_diversify_new) {
new_deriver = mifare_key_deriver_new_an10922(new_picc_key, mifare_desfire_key_get_type(new_picc_key), AN10922_FLAG_DEFAULT);
}

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, 8);
if (device_count <= 0)
errx(EXIT_FAILURE, "No NFC device found.");

for (size_t d = 0; (!error) && (d < device_count); d++) {
device = nfc_open(context, devices[d]);
if (!device) {
warnx("nfc_open() failed.");
error = EXIT_FAILURE;
continue;
}

tags = freefare_get_tags(device);
if (!tags) {
nfc_close(device);
errx(EXIT_FAILURE, "Error listing Mifare DESFire tags.");
}

for (int i = 0; (!error) && tags[i]; i++) {
if (MIFARE_DESFIRE != freefare_get_tag_type(tags[i]))
continue;

char *tag_uid = freefare_get_tag_uid(tags[i]);
char buffer[BUFSIZ];

int res;

res = mifare_desfire_connect(tags[i]);
if (res < 0) {
warnx("Can't connect to Mifare DESFire target.");
error = EXIT_FAILURE;
break;
}

printf("Found %s with UID %s. ", freefare_get_tag_friendly_name(tags[i]), tag_uid);
bool do_it = true;

if (configure_options.interactive) {
printf("Change PICC key? [yN] ");
fgets(buffer, BUFSIZ, stdin);
do_it = ((buffer[0] == 'y') || (buffer[0] == 'Y'));
} else {
printf("\n");
}

if (do_it) {

res = mifare_desfire_authenticate(tags[i], 0, old_picc_key);
if (res < 0) {
freefare_perror(tags[i], "mifare_desfire_authenticate");
error = EXIT_FAILURE;
break;
}

res = mifare_desfire_change_key(tags[i], 0, new_picc_key, old_picc_key);
if (res < 0) {
freefare_perror(tags[i], "mifare_desfire_change_key");
error = EXIT_FAILURE;
break;
}

}

mifare_desfire_disconnect(tags[i]);
free(tag_uid);
}

freefare_free_tags(tags);
nfc_close(device);
}

mifare_desfire_key_free(old_picc_key);
mifare_desfire_key_free(new_picc_key);

nfc_exit(context);
exit(error);
}
19 changes: 10 additions & 9 deletions libfreefare/freefare.h
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,15 @@ int mifare_desfire_clear_record_file(FreefareTag tag, uint8_t file_no);
int mifare_desfire_commit_transaction(FreefareTag tag);
int mifare_desfire_abort_transaction(FreefareTag tag);

typedef enum mifare_key_type {
MIFARE_KEY_DES,
MIFARE_KEY_2K3DES,
MIFARE_KEY_3K3DES,
MIFARE_KEY_AES128,

MIFARE_KEY_LAST = MIFARE_KEY_AES128
} MifareKeyType;

MifareDESFireKey mifare_desfire_des_key_new(const uint8_t value[8]);
MifareDESFireKey mifare_desfire_3des_key_new(const uint8_t value[16]);
MifareDESFireKey mifare_desfire_des_key_new_with_version(const uint8_t value[8]);
Expand All @@ -521,22 +530,14 @@ MifareDESFireKey mifare_desfire_aes_key_new(const uint8_t value[16]);
MifareDESFireKey mifare_desfire_aes_key_new_with_version(const uint8_t value[16], uint8_t version);
uint8_t mifare_desfire_key_get_version(MifareDESFireKey key);
void mifare_desfire_key_set_version(MifareDESFireKey key, uint8_t version);
MifareKeyType mifare_desfire_key_get_type(MifareDESFireKey key);
void mifare_desfire_key_free(MifareDESFireKey key);

uint8_t *tlv_encode(const uint8_t type, const uint8_t *istream, uint16_t isize, size_t *osize);
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;

Expand Down
6 changes: 6 additions & 0 deletions libfreefare/mifare_desfire_key.c
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,12 @@ mifare_desfire_session_key_new(const uint8_t rnda[], const uint8_t rndb[], Mifar
return key;
}

MifareKeyType
mifare_desfire_key_get_type(MifareDESFireKey key)
{
return key->type;
}

void
mifare_desfire_key_free(MifareDESFireKey key)
{
Expand Down

0 comments on commit 7c609e3

Please sign in to comment.