Skip to content

Commit 972a676

Browse files
committed
Added examples/mifare-desfire-ev1-change-picc-key
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.
1 parent ec91014 commit 972a676

File tree

5 files changed

+274
-9
lines changed

5 files changed

+274
-9
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ examples/mifare-classic-read-ndef
2828
examples/mifare-classic-write-ndef
2929
examples/mifare-desfire-access
3030
examples/mifare-desfire-create-ndef
31+
examples/mifare-desfire-ev1-change-picc-key
3132
examples/mifare-desfire-ev1-configure-ats
3233
examples/mifare-desfire-ev1-configure-default-key
3334
examples/mifare-desfire-ev1-configure-random-uid
@@ -36,6 +37,7 @@ examples/mifare-desfire-info
3637
examples/mifare-desfire-read-ndef
3738
examples/mifare-desfire-write-ndef
3839
examples/mifare-ultralight-info
40+
examples/mifare-ultralightc-diversify
3941
examples/ntag-detect
4042
examples/ntag-removeauth
4143
examples/ntag-setauth

examples/Makefile.am

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ bin_PROGRAMS = felica-lite-dump \
1111
mifare-desfire-ev1-configure-ats \
1212
mifare-desfire-ev1-configure-default-key \
1313
mifare-desfire-ev1-configure-random-uid \
14+
mifare-desfire-ev1-change-picc-key \
1415
mifare-desfire-format \
1516
mifare-desfire-info \
1617
mifare-desfire-read-ndef \
@@ -49,6 +50,9 @@ mifare_desfire_ev1_configure_ats_LDADD = $(top_builddir)/libfreefare/libfreefare
4950
mifare_desfire_ev1_configure_default_key_SOURCES = mifare-desfire-ev1-configure-default-key.c
5051
mifare_desfire_ev1_configure_default_key_LDADD = $(top_builddir)/libfreefare/libfreefare.la
5152

53+
mifare_desfire_ev1_change_picc_key_SOURCES = mifare-desfire-ev1-change-picc-key.c
54+
mifare_desfire_ev1_change_picc_key_LDADD = $(top_builddir)/libfreefare/libfreefare.la
55+
5256
mifare_desfire_ev1_configure_random_uid_SOURCES = mifare-desfire-ev1-configure-random-uid.c
5357
mifare_desfire_ev1_configure_random_uid_LDADD = $(top_builddir)/libfreefare/libfreefare.la
5458

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
#if defined(HAVE_CONFIG_H)
2+
#include "config.h"
3+
#endif
4+
5+
#include <err.h>
6+
#include <errno.h>
7+
#include <stdlib.h>
8+
#include <string.h>
9+
#include <unistd.h>
10+
#include <stdbool.h>
11+
12+
#include <nfc/nfc.h>
13+
14+
#include <freefare.h>
15+
16+
uint8_t null_key_data[8];
17+
18+
uint8_t new_key_version = 0x00;
19+
20+
MifareDESFireKey old_picc_key;
21+
MifareDESFireKey new_picc_key;
22+
23+
#define NEW_KEY_VERSION new_key_version
24+
25+
struct {
26+
bool interactive;
27+
} configure_options = {
28+
.interactive = true
29+
};
30+
31+
static void
32+
usage(char *progname)
33+
{
34+
fprintf(stderr, "usage: %s [-y]\n", progname);
35+
fprintf(stderr, "\nOptions:\n");
36+
fprintf(stderr, " -y Do not ask for confirmation\n");
37+
fprintf(stderr, " -k Existing PICC key (Default is all zeros)\n");
38+
fprintf(stderr, " -n New PICC key (Default is all zeros)\n");
39+
fprintf(stderr, " -v New PICC key version (default is zero)\n");
40+
}
41+
42+
#define strnequal(x, y, n) (strncmp(x, y, n) == 0)
43+
44+
static inline bool
45+
strhasprefix(const char* str, const char* prefix)
46+
{
47+
return strnequal(str, prefix, strlen(prefix));
48+
}
49+
50+
MifareDESFireKey read_hex_desfire_key(const char* optarg)
51+
{
52+
uint8_t buffer[24];
53+
int i;
54+
uint64_t n;
55+
56+
bool is_des = true;
57+
58+
if (strhasprefix(optarg, "DES:")) {
59+
is_des = true;
60+
optarg += 4;
61+
} else if (strhasprefix(optarg, "AES:")) {
62+
is_des = false;
63+
optarg += 4;
64+
}
65+
66+
size_t len = strlen(optarg);
67+
size_t div16 = len / 16;
68+
69+
if (div16 < 1 || div16 > 3 || (len % 16) != 0) {
70+
fprintf(stderr,"Bad key length\n");
71+
exit(EXIT_FAILURE);
72+
}
73+
74+
if (div16 >= 1) {
75+
n = strtoull(optarg, NULL, 16);
76+
for (i = 7; i >= 0; i--) {
77+
buffer[i] = (uint8_t) n;
78+
n >>= 8;
79+
}
80+
}
81+
82+
if (div16 >= 2) {
83+
n = strtoull(optarg+8, NULL, 16);
84+
for (i = 7; i >= 0; i--) {
85+
buffer[i+8] = (uint8_t) n;
86+
n >>= 8;
87+
}
88+
}
89+
90+
if (div16 == 3) {
91+
n = strtoull(optarg+16, NULL, 16);
92+
for (i = 7; i >= 0; i--) {
93+
buffer[i+16] = (uint8_t) n;
94+
n >>= 8;
95+
}
96+
}
97+
98+
if (is_des && div16 == 1) {
99+
return mifare_desfire_des_key_new(buffer);
100+
}
101+
if (is_des && div16 == 2) {
102+
return mifare_desfire_3des_key_new(buffer);
103+
}
104+
if (is_des && div16 == 3) {
105+
return mifare_desfire_3k3des_key_new(buffer);
106+
}
107+
if (!is_des && div16 == 2) {
108+
return mifare_desfire_aes_key_new(buffer);
109+
}
110+
fprintf(stderr,"Bad key length\n");
111+
exit(EXIT_FAILURE);
112+
}
113+
114+
int
115+
main(int argc, char *argv[])
116+
{
117+
int ch;
118+
int error = EXIT_SUCCESS;
119+
nfc_device *device = NULL;
120+
FreefareTag *tags = NULL;
121+
bool should_diversify_new = false;
122+
123+
old_picc_key = mifare_desfire_des_key_new(null_key_data);
124+
new_picc_key = mifare_desfire_des_key_new(null_key_data);
125+
126+
while ((ch = getopt(argc, argv, "hyk:n:v:D")) != -1) {
127+
switch (ch) {
128+
case 'h':
129+
usage(argv[0]);
130+
exit(EXIT_SUCCESS);
131+
break;
132+
case 'y':
133+
configure_options.interactive = false;
134+
break;
135+
case 'k':
136+
mifare_desfire_key_free(old_picc_key);
137+
old_picc_key = read_hex_desfire_key(optarg);
138+
break;
139+
case 'n':
140+
mifare_desfire_key_free(new_picc_key);
141+
new_picc_key = read_hex_desfire_key(optarg);
142+
break;
143+
case 'v':
144+
errno = 0;
145+
new_key_version = (uint8_t)strtol(optarg, NULL, 0);
146+
if (errno != 0) {
147+
perror("strtol");
148+
exit(EXIT_FAILURE);
149+
}
150+
break;
151+
case 'D':
152+
should_diversify_new = true;
153+
break;
154+
default:
155+
usage(argv[0]);
156+
exit(EXIT_FAILURE);
157+
}
158+
}
159+
// Remaining args, if any, are in argv[optind .. (argc-1)]
160+
161+
mifare_desfire_key_set_version(new_picc_key, new_key_version);
162+
163+
MifareKeyDeriver new_deriver = NULL;
164+
if (should_diversify_new) {
165+
new_deriver = mifare_key_deriver_new_an10922(new_picc_key, mifare_desfire_key_get_type(new_picc_key), AN10922_FLAG_DEFAULT);
166+
}
167+
168+
nfc_connstring devices[8];
169+
size_t device_count;
170+
171+
nfc_context *context;
172+
nfc_init(&context);
173+
if (context == NULL)
174+
errx(EXIT_FAILURE, "Unable to init libnfc (malloc)");
175+
176+
device_count = nfc_list_devices(context, devices, 8);
177+
if (device_count <= 0)
178+
errx(EXIT_FAILURE, "No NFC device found.");
179+
180+
for (size_t d = 0; (!error) && (d < device_count); d++) {
181+
device = nfc_open(context, devices[d]);
182+
if (!device) {
183+
warnx("nfc_open() failed.");
184+
error = EXIT_FAILURE;
185+
continue;
186+
}
187+
188+
tags = freefare_get_tags(device);
189+
if (!tags) {
190+
nfc_close(device);
191+
errx(EXIT_FAILURE, "Error listing Mifare DESFire tags.");
192+
}
193+
194+
for (int i = 0; (!error) && tags[i]; i++) {
195+
if (MIFARE_DESFIRE != freefare_get_tag_type(tags[i]))
196+
continue;
197+
198+
char *tag_uid = freefare_get_tag_uid(tags[i]);
199+
char buffer[BUFSIZ];
200+
201+
int res;
202+
203+
res = mifare_desfire_connect(tags[i]);
204+
if (res < 0) {
205+
warnx("Can't connect to Mifare DESFire target.");
206+
error = EXIT_FAILURE;
207+
break;
208+
}
209+
210+
printf("Found %s with UID %s. ", freefare_get_tag_friendly_name(tags[i]), tag_uid);
211+
bool do_it = true;
212+
213+
if (configure_options.interactive) {
214+
printf("Change PICC key? [yN] ");
215+
fgets(buffer, BUFSIZ, stdin);
216+
do_it = ((buffer[0] == 'y') || (buffer[0] == 'Y'));
217+
} else {
218+
printf("\n");
219+
}
220+
221+
if (do_it) {
222+
223+
res = mifare_desfire_authenticate(tags[i], 0, old_picc_key);
224+
if (res < 0) {
225+
freefare_perror(tags[i], "mifare_desfire_authenticate");
226+
error = EXIT_FAILURE;
227+
break;
228+
}
229+
230+
res = mifare_desfire_change_key(tags[i], 0, new_picc_key, old_picc_key);
231+
if (res < 0) {
232+
freefare_perror(tags[i], "mifare_desfire_change_key");
233+
error = EXIT_FAILURE;
234+
break;
235+
}
236+
237+
}
238+
239+
mifare_desfire_disconnect(tags[i]);
240+
free(tag_uid);
241+
}
242+
243+
freefare_free_tags(tags);
244+
nfc_close(device);
245+
}
246+
247+
mifare_desfire_key_free(old_picc_key);
248+
mifare_desfire_key_free(new_picc_key);
249+
250+
nfc_exit(context);
251+
exit(error);
252+
}

libfreefare/freefare.h

+10-9
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,15 @@ int mifare_desfire_clear_record_file(FreefareTag tag, uint8_t file_no);
511511
int mifare_desfire_commit_transaction(FreefareTag tag);
512512
int mifare_desfire_abort_transaction(FreefareTag tag);
513513

514+
typedef enum mifare_key_type {
515+
MIFARE_KEY_DES,
516+
MIFARE_KEY_2K3DES,
517+
MIFARE_KEY_3K3DES,
518+
MIFARE_KEY_AES128,
519+
520+
MIFARE_KEY_LAST = MIFARE_KEY_AES128
521+
} MifareKeyType;
522+
514523
MifareDESFireKey mifare_desfire_des_key_new(const uint8_t value[8]);
515524
MifareDESFireKey mifare_desfire_3des_key_new(const uint8_t value[16]);
516525
MifareDESFireKey mifare_desfire_des_key_new_with_version(const uint8_t value[8]);
@@ -521,22 +530,14 @@ MifareDESFireKey mifare_desfire_aes_key_new(const uint8_t value[16]);
521530
MifareDESFireKey mifare_desfire_aes_key_new_with_version(const uint8_t value[16], uint8_t version);
522531
uint8_t mifare_desfire_key_get_version(MifareDESFireKey key);
523532
void mifare_desfire_key_set_version(MifareDESFireKey key, uint8_t version);
533+
MifareKeyType mifare_desfire_key_get_type(MifareDESFireKey key);
524534
void mifare_desfire_key_free(MifareDESFireKey key);
525535

526536
uint8_t *tlv_encode(const uint8_t type, const uint8_t *istream, uint16_t isize, size_t *osize);
527537
uint8_t *tlv_decode(const uint8_t *istream, uint8_t *type, uint16_t *size);
528538
size_t tlv_record_length(const uint8_t *istream, size_t *field_length_size, size_t *field_value_size);
529539
uint8_t *tlv_append(uint8_t *a, uint8_t *b);
530540

531-
typedef enum mifare_key_type {
532-
MIFARE_KEY_DES,
533-
MIFARE_KEY_2K3DES,
534-
MIFARE_KEY_3K3DES,
535-
MIFARE_KEY_AES128,
536-
537-
MIFARE_KEY_LAST = MIFARE_KEY_AES128
538-
} MifareKeyType;
539-
540541
struct mifare_key_deriver;
541542
typedef struct mifare_key_deriver *MifareKeyDeriver;
542543

libfreefare/mifare_desfire_key.c

+6
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,12 @@ mifare_desfire_session_key_new(const uint8_t rnda[], const uint8_t rndb[], Mifar
208208
return key;
209209
}
210210

211+
MifareKeyType
212+
mifare_desfire_key_get_type(MifareDESFireKey key)
213+
{
214+
return key->type;
215+
}
216+
211217
void
212218
mifare_desfire_key_free(MifareDESFireKey key)
213219
{

0 commit comments

Comments
 (0)