1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // See LICENSE.txt for the text of the license.
15 //-----------------------------------------------------------------------------
16 // Low frequency EM4x70 commands
17 //-----------------------------------------------------------------------------
19 #include "cmdlfem4x70.h"
21 #include "cmdparser.h" // command_t
22 #include "cliparser.h"
23 #include "fileutils.h"
24 #include "commonutil.h"
28 #include "util_posix.h" // msleep()
30 #define LOCKBIT_0 BITMASK(6)
31 #define LOCKBIT_1 BITMASK(7)
33 #define BYTE_ARRAY_INDEX_TO_BLOCK(x) ((31-(x))/2)
35 // TODO: Optional: use those unique structures in a union, call it em4x70_data_t, but add a first
36 // common header field that includes the command itself (to improve debugging / validation).
37 typedef struct _em4x70_tag_info_t
{
39 /// The full data on an em4x70 the tag.
40 /// [31] == Block 15 MSB == UM2₆₃..UM2₅₆
41 /// [30] == Block 15 LSB == UM2₅₅..UM2₄₈
42 /// [29] == Block 14 MSB == UM2₄₇..UM2₄₀
43 /// [28] == Block 14 LSB == UM2₃₉..UM2₃₂
44 /// [27] == Block 13 MSB == UM2₃₁..UM2₂₄
45 /// [26] == Block 13 LSB == UM2₂₃..UM2₁₆
46 /// [25] == Block 12 MSB == UM2₁₅..UM2₀₈
47 /// [24] == Block 12 LSB == UM2₀₇..UM2₀₀
48 /// [23] == Block 11 MSB == Pin₃₁..Pin₂₄
49 /// [22] == Block 11 LSB == Pin₂₃..Pin₁₆
50 /// [21] == Block 10 MSB == Pin₁₅..Pin₀₈
51 /// [20] == Block 10 LSB == Pin₀₇..Pin₀₀
52 /// [19] == Block 9 MSB == Key₉₅..Key₈₈
53 /// [18] == Block 9 LSB == Key₈₇..Key₈₀
54 /// [17] == Block 8 MSB == Key₇₉..Key₇₂
55 /// [16] == Block 8 LSB == Key₇₁..Key₆₄
56 /// [15] == Block 7 MSB == Key₆₃..Key₅₆
57 /// [14] == Block 7 LSB == Key₅₅..Key₄₈
58 /// [13] == Block 6 MSB == Key₄₇..Key₄₀
59 /// [12] == Block 6 LSB == Key₃₉..Key₃₂
60 /// [11] == Block 5 MSB == Key₃₁..Key₂₄
61 /// [10] == Block 5 LSB == Key₂₃..Key₁₆
62 /// [ 9] == Block 4 MSB == Key₁₅..Key₀₈
63 /// [ 8] == Block 4 LSB == Key₀₇..Key₀₀
64 /// [ 7] == Block 3 MSB == ID₃₁..ID₂₄
65 /// [ 6] == Block 3 LSB == ID₂₃..ID₁₆
66 /// [ 5] == Block 2 MSB == ID₁₅..ID₀₈
67 /// [ 4] == Block 2 LSB == ID₀₇..ID₀₀
68 /// [ 3] == Block 1 MSB == L₁ L₀ UM1₂₉..UM1₂₄
69 /// [ 2] == Block 1 LSB == UM1₂₃..UM1₁₆
70 /// [ 1] == Block 0 MSB == UM1₁₅..UM1₀₈
71 /// [ 0] == Block 0 LSB == UM1₀₇..UM1₀₀
74 /// When moving to C++, strongly consider adding
75 /// const helper functions to extract a given
76 /// block of data into native uint16_t.
77 /// See also the em4x70_print_info_result() function
78 /// for visual presentation of the data.
83 typedef struct _em4x70_cmd_input_info_t
{
85 } em4x70_cmd_input_info_t
;
87 typedef struct _em4x70_cmd_input_writeblock_t
{
91 } em4x70_cmd_input_writeblock_t
;
93 typedef struct _em4x70_cmd_input_brute_t
{
98 uint8_t partial_key_start
[2];
99 } em4x70_cmd_input_brute_t
;
101 typedef struct _em4x70_cmd_output_brute_t
{
103 /// The returned data is big endian (MSB first).
105 /// partial_key[0] == Key₉₅..Key₈₈ == Block 9 MSB
106 /// partial_key[1] == Key₈₇..Key₈₀ == Block 9 LSB
108 /// partial_key[0] == Key₇₉..Key₇₂ == Block 8 MSB
109 /// partial_key[1] == Key₇₁..Key₆₄ == Block 8 LSB
111 /// partial_key[15] == Key₆₃..Key₅₆ == Block 7 MSB
112 /// partial_key[14] == Key₅₅..Key₄₈ == Block 7 LSB
114 uint8_t partial_key
[2];
115 } em4x70_cmd_output_brute_t
;
117 typedef struct _em4x70_cmd_input_unlock_t
{
120 } em4x70_cmd_input_unlock_t
;
122 typedef struct _em4x70_cmd_input_auth_t
{
126 } em4x70_cmd_input_auth_t
;
128 typedef struct _em4x70_cmd_output_auth_t
{
130 } em4x70_cmd_output_auth_t
;
132 typedef struct _em4x70_cmd_input_setpin_t
{
135 } em4x70_cmd_input_setpin_t
;
137 typedef struct _em4x70_cmd_input_setkey_t
{
140 } em4x70_cmd_input_setkey_t
;
142 // There is no output data when writing a new key
143 typedef struct _em4x70_cmd_input_recover_t
{
144 ID48LIB_KEY key
; // only the first 6 bytes (48 bits) are considered valid
148 bool parity
; // if true, add parity bit to commands sent to tag
149 bool verify
; // if true, tag must be present
150 } em4x70_cmd_input_recover_t
;
152 // largest seen "in the wild" was 6
153 #define MAXIMUM_ID48_RECOVERED_KEY_COUNT 10
155 typedef struct _em4x70_cmd_output_recover_t
{
156 uint8_t potential_key_count
;
157 ID48LIB_KEY potential_keys
[MAXIMUM_ID48_RECOVERED_KEY_COUNT
];
158 } em4x70_cmd_output_recover_t
;
160 typedef struct _em4x70_cmd_input_verify_auth_t
{
165 } em4x70_cmd_input_verify_auth_t
;
167 typedef struct _em4x70_cmd_input_calculate_t
{
170 } em4x70_cmd_input_calculate_t
;
171 typedef struct _em4x70_cmd_output_calculate_t
{
174 } em4x70_cmd_output_calculate_t
;
177 static void fill_buffer_prng_bytes(void *buffer
, size_t byte_count
) {
178 if (byte_count
== 0) {
182 srand((unsigned) time(NULL
));
183 for (size_t i
= 0; i
< byte_count
; i
++) {
184 ((uint8_t *)buffer
)[i
] = (uint8_t)rand();
188 static void em4x70_print_info_result(const em4x70_tag_info_t
*data
) {
189 PrintAndLogEx(NORMAL
, "");
190 PrintAndLogEx(INFO
, "--- " _CYAN_("Tag Information") " ---------------------------");
191 PrintAndLogEx(INFO
, "Block | data | info");
192 PrintAndLogEx(INFO
, "------+----------+-----------------------------");
193 PrintAndLogEx(INFO
, " %2d | %02X %02X | %s", 15, data
->Raw
[31], data
->Raw
[30], "UM2");
194 PrintAndLogEx(INFO
, " %2d | %02X %02X | %s", 14, data
->Raw
[29], data
->Raw
[28], "UM2");
195 PrintAndLogEx(INFO
, " %2d | %02X %02X | %s", 13, data
->Raw
[27], data
->Raw
[26], "UM2");
196 PrintAndLogEx(INFO
, " %2d | %02X %02X | %s", 12, data
->Raw
[25], data
->Raw
[24], "UM2");
197 PrintAndLogEx(INFO
, "------+----------+-----------------------------");
198 PrintAndLogEx(INFO
, " %2d | -- -- | %s", 11, "PIN write only");
199 PrintAndLogEx(INFO
, " %2d | -- -- | %s", 10, "PIN write only");
200 PrintAndLogEx(INFO
, "------+----------+-----------------------------");
201 PrintAndLogEx(INFO
, " %2d | -- -- | %s", 9, "KEY write only");
202 PrintAndLogEx(INFO
, " %2d | -- -- | %s", 8, "KEY write only");
203 PrintAndLogEx(INFO
, " %2d | -- -- | %s", 7, "KEY write only");
204 PrintAndLogEx(INFO
, " %2d | -- -- | %s", 6, "KEY write only");
205 PrintAndLogEx(INFO
, " %2d | -- -- | %s", 5, "KEY write only");
206 PrintAndLogEx(INFO
, " %2d | -- -- | %s", 4, "KEY write only");
207 PrintAndLogEx(INFO
, "------+----------+-----------------------------");
208 PrintAndLogEx(INFO
, " %2d | %02X %02X | %s", 3, data
->Raw
[ 7], data
->Raw
[ 6], "ID");
209 PrintAndLogEx(INFO
, " %2d | %02X %02X | %s", 2, data
->Raw
[ 5], data
->Raw
[ 4], "ID");
210 PrintAndLogEx(INFO
, "------+----------+-----------------------------");
211 PrintAndLogEx(INFO
, " %2d | %02X %02X | %s", 1, data
->Raw
[ 3], data
->Raw
[ 2], "UM1");
212 PrintAndLogEx(INFO
, " %2d | %02X %02X | %s", 0, data
->Raw
[ 1], data
->Raw
[ 0], "UM1");
213 PrintAndLogEx(INFO
, "------+----------+-----------------------------");
214 PrintAndLogEx(INFO
, "");
216 PrintAndLogEx(INFO
, "Tag ID: %02X %02X %02X %02X", data
->Raw
[7], data
->Raw
[6], data
->Raw
[5], data
->Raw
[4]);
217 PrintAndLogEx(INFO
, "Lockbit 0: %d", (data
->Raw
[3] & LOCKBIT_0
) ? 1 : 0);
218 PrintAndLogEx(INFO
, "Lockbit 1: %d", (data
->Raw
[3] & LOCKBIT_1
) ? 1 : 0);
219 PrintAndLogEx(INFO
, "Tag is %s.", (data
->Raw
[3] & LOCKBIT_0
) ? _RED_("LOCKED") : _GREEN_("UNLOCKED"));
220 PrintAndLogEx(INFO
, "");
221 PrintAndLogEx(NORMAL
, "");
224 static int get_em4x70_info(const em4x70_cmd_input_info_t
*opts
, em4x70_tag_info_t
*data_out
) {
226 memset(data_out
, 0, sizeof(em4x70_tag_info_t
));
228 // TODO: change firmware to use per-cmd structures
229 em4x70_data_t edata
= { .parity
= opts
->use_parity
};
230 clearCommandBuffer();
231 SendCommandNG(CMD_LF_EM4X70_INFO
, (uint8_t *)&edata
, sizeof(em4x70_data_t
));
232 PacketResponseNG resp
;
233 if (WaitForResponseTimeout(CMD_LF_EM4X70_INFO
, &resp
, TIMEOUT
) == false) {
236 if (resp
.status
== PM3_SUCCESS
) {
237 memcpy(data_out
, resp
.data
.asBytes
, sizeof(em4x70_tag_info_t
));
242 static int writeblock_em4x70(const em4x70_cmd_input_writeblock_t
*opts
, em4x70_tag_info_t
*data_out
) {
244 memset(data_out
, 0, sizeof(em4x70_tag_info_t
));
246 // TODO: change firmware to use per-cmd structures
247 em4x70_data_t etd
= {0};
248 etd
.address
= opts
->block
;
249 etd
.word
= BYTES2UINT16(opts
->value
);
250 etd
.parity
= opts
->use_parity
;
252 clearCommandBuffer();
253 SendCommandNG(CMD_LF_EM4X70_WRITE
, (uint8_t *)&etd
, sizeof(etd
));
254 PacketResponseNG resp
;
255 if (WaitForResponseTimeout(CMD_LF_EM4X70_WRITE
, &resp
, TIMEOUT
) == false) {
258 if (resp
.status
== PM3_SUCCESS
) {
259 memcpy(data_out
, resp
.data
.asBytes
, sizeof(em4x70_tag_info_t
));
264 static int auth_em4x70(const em4x70_cmd_input_auth_t
*opts
, em4x70_cmd_output_auth_t
*data_out
) {
265 memset(data_out
, 0, sizeof(ID48LIB_GRN
));
267 // TODO: change firmware to use per-cmd structures
268 em4x70_data_t etd
= {0};
269 etd
.parity
= opts
->use_parity
;
270 memcpy(&etd
.rnd
[0], &opts
->rn
.rn
[0], 7);
271 memcpy(&etd
.frnd
[0], &opts
->frn
.frn
[0], 4);
273 clearCommandBuffer();
274 SendCommandNG(CMD_LF_EM4X70_AUTH
, (uint8_t *)&etd
, sizeof(etd
));
275 PacketResponseNG resp
;
276 if (WaitForResponseTimeout(CMD_LF_EM4X70_AUTH
, &resp
, TIMEOUT
) == false) {
279 if (resp
.status
== PM3_SUCCESS
) {
280 // Response is 20-bit from tag
281 // HACKHACK -- It appears the byte order differs from what is expected?
282 data_out
->grn
.grn
[0] = resp
.data
.asBytes
[2];
283 data_out
->grn
.grn
[1] = resp
.data
.asBytes
[1];
284 data_out
->grn
.grn
[2] = resp
.data
.asBytes
[0];
285 //memcpy(data_out, &resp.data.asBytes[0], sizeof(ID48LIB_GRN));
290 static int setkey_em4x70(const em4x70_cmd_input_setkey_t
*opts
) {
292 // TODO: change firmware to use per-cmd structures
293 em4x70_data_t etd
= {0};
294 etd
.parity
= opts
->use_parity
;
295 memcpy(&etd
.crypt_key
[0], &opts
->key
.k
[0], 12);
297 clearCommandBuffer();
298 SendCommandNG(CMD_LF_EM4X70_SETKEY
, (uint8_t *)&etd
, sizeof(etd
));
299 PacketResponseNG resp
;
300 if (WaitForResponseTimeout(CMD_LF_EM4X70_SETKEY
, &resp
, TIMEOUT
) == false) {
306 static int brute_em4x70(const em4x70_cmd_input_brute_t
*opts
, em4x70_cmd_output_brute_t
*data_out
) {
307 memset(data_out
, 0, sizeof(em4x70_cmd_output_brute_t
));
309 // TODO: change firmware to use per-cmd structures
310 em4x70_data_t etd
= {0};
311 etd
.parity
= opts
->use_parity
;
312 etd
.address
= opts
->block
;
313 memcpy(&etd
.rnd
[0], &opts
->rn
.rn
[0], 7);
314 memcpy(&etd
.frnd
[0], &opts
->frn
.frn
[0], 4);
316 // TODO: FIX THIS MESS WITH BYTE ORDER CHANGING BACK AND FORTH!
317 // Just use byte arrays when sending to the firmware.
318 // Lowers the cognitive load AND makes it easier to understand.
319 // opts structure stored value in BIG ENDIAN
320 // Note that the FIRMWARE side will swap the byte order back to BIG ENDIAN.
321 // (yes, this is a bit of a mess, but it is what it is for now...)
322 uint16_t start_key_be
= (opts
->partial_key_start
[0] << 8) | opts
->partial_key_start
[1];
323 etd
.start_key
= start_key_be
;
325 clearCommandBuffer();
326 PacketResponseNG resp
;
327 SendCommandNG(CMD_LF_EM4X70_BRUTE
, (uint8_t *)&etd
, sizeof(etd
));
329 uint32_t timeout
= 0;
332 if (kbd_enter_pressed()) {
333 SendCommandNG(CMD_BREAK_LOOP
, NULL
, 0);
334 return PM3_EOPABORTED
;
337 if (WaitForResponseTimeout(CMD_LF_EM4X70_BRUTE
, &resp
, TIMEOUT
)) {
338 if (resp
.status
== PM3_SUCCESS
) {
339 memcpy(data_out
, resp
.data
.asBytes
, sizeof(em4x70_cmd_output_brute_t
));
344 // NOTE: It takes about 11 seconds per 0x0100 authentication attempts.
345 // Thus, each block takes a maximum of 256 * 11 seconds == 46m56s.
346 // A timeout of 60 minutes corresponds to ~14 seconds per 0x0100 auths,
347 // which is ~25% margin. Plus, on average, it takes half that
348 // amount of time (for a random value in the key block).
349 if (timeout
> ((60u * 60000u) / TIMEOUT
)) {
350 PrintAndLogEx(WARNING
, "\nNo response from Proxmark3. Aborting...");
357 static int unlock_em4x70(const em4x70_cmd_input_unlock_t
*opts
, em4x70_tag_info_t
*data_out
) {
358 memset(data_out
, 0, sizeof(em4x70_tag_info_t
));
360 // TODO: change firmware to use per-cmd structures
361 em4x70_data_t etd
= {0};
362 etd
.parity
= opts
->use_parity
;
363 etd
.pin
= BYTES2UINT32(opts
->pin
);
365 clearCommandBuffer();
366 SendCommandNG(CMD_LF_EM4X70_UNLOCK
, (uint8_t *)&etd
, sizeof(etd
));
367 PacketResponseNG resp
;
368 if (WaitForResponseTimeout(CMD_LF_EM4X70_UNLOCK
, &resp
, TIMEOUT
) == false) {
371 if (resp
.status
== PM3_SUCCESS
) {
372 memcpy(data_out
, resp
.data
.asBytes
, sizeof(em4x70_tag_info_t
));
377 static int setpin_em4x70(const em4x70_cmd_input_setpin_t
*opts
, em4x70_tag_info_t
*data_out
) {
378 memset(data_out
, 0, sizeof(em4x70_tag_info_t
));
380 // TODO: change firmware to use per-cmd structures
381 em4x70_data_t etd
= {0};
382 etd
.parity
= opts
->use_parity
;
383 etd
.pin
= BYTES2UINT32(opts
->pin
);
385 clearCommandBuffer();
386 SendCommandNG(CMD_LF_EM4X70_SETPIN
, (uint8_t *)&etd
, sizeof(etd
));
387 PacketResponseNG resp
;
388 if (WaitForResponseTimeout(CMD_LF_EM4X70_SETPIN
, &resp
, TIMEOUT
) == false) {
391 if (resp
.status
== PM3_SUCCESS
) {
392 memcpy(data_out
, resp
.data
.asBytes
, sizeof(em4x70_tag_info_t
));
397 static int recover_em4x70(const em4x70_cmd_input_recover_t
*opts
, em4x70_cmd_output_recover_t
*data_out
) {
398 memset(data_out
, 0, sizeof(em4x70_cmd_output_recover_t
));
400 // The library is stateful. First must initialize its internal context.
401 id48lib_key_recovery_init(&opts
->key
, &opts
->nonce
, &opts
->frn
, &opts
->grn
);
403 // repeatedly call id48lib_key_recovery_next() to get the next potential key
405 int result
= PM3_SUCCESS
;
407 while ((PM3_SUCCESS
== result
) && id48lib_key_recovery_next(&q
)) {
409 if (data_out
->potential_key_count
>= MAXIMUM_ID48_RECOVERED_KEY_COUNT
) {
410 result
= PM3_EOVFLOW
;
412 data_out
->potential_keys
[data_out
->potential_key_count
] = q
;
413 ++data_out
->potential_key_count
;
417 if ((PM3_SUCCESS
== result
) && (data_out
->potential_key_count
== 0)) {
418 result
= PM3_EFAILED
;
423 static int verify_auth_em4x70(const em4x70_cmd_input_verify_auth_t
*opts
) {
424 em4x70_cmd_input_auth_t opts_auth
= {
425 .use_parity
= opts
->use_parity
,
430 em4x70_cmd_output_auth_t tag_grn
;
432 int result
= auth_em4x70(&opts_auth
, &tag_grn
);
434 if (PM3_SUCCESS
== result
) {
435 if (memcmp(&opts
->grn
, &tag_grn
, sizeof(ID48LIB_GRN
)) != 0) {
436 result
= PM3_EWRONGANSWER
;
443 static int CmdEM4x70Info(const char *Cmd
) {
445 // invoke reading of a EM4x70 tag which has to be on the antenna because
446 // decoding is done by the device (not on client side)
447 CLIParserContext
*ctx
;
448 CLIParserInit(&ctx
, "lf em 4x70 info",
449 "Tag Information EM4x70\n"
450 " Tag variants include ID48 automotive transponder.\n"
451 " ID48 does not use command parity (default).\n"
452 " V4070 and EM4170 do require parity bit.",
454 "lf em 4x70 info --par -> adds parity bit to command\n"
459 arg_lit0(NULL
, "par", "Add parity bit when sending commands"),
463 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
464 em4x70_cmd_input_info_t opts
= {
465 .use_parity
= arg_get_lit(ctx
, 0),
469 // Client command line parsing and validation complete ... now use the helper function
470 em4x70_tag_info_t info
;
471 int result
= get_em4x70_info(&opts
, &info
);
473 if (result
== PM3_ETIMEOUT
) {
474 PrintAndLogEx(WARNING
, "Timeout while waiting for reply.");
475 } else if (result
== PM3_SUCCESS
) {
476 em4x70_print_info_result(&info
);
478 PrintAndLogEx(FAILED
, "Reading ( " _RED_("fail") " )");
483 static int CmdEM4x70Write(const char *Cmd
) {
485 // write one block/word (16 bits) to the tag at given block address (0-15)
486 CLIParserContext
*ctx
;
487 CLIParserInit(&ctx
, "lf em 4x70 write",
489 "lf em 4x70 write -b 15 -d c0de -> write 'c0de' to block 15\n"
490 "lf em 4x70 write -b 15 -d c0de --par -> adds parity bit to commands\n"
495 arg_lit0(NULL
, "par", "Add parity bit when sending commands"),
496 arg_int1("b", "block", "<dec>", "block/word address, dec"),
497 arg_str1("d", "data", "<hex>", "data, 2 bytes"),
501 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
503 em4x70_cmd_input_writeblock_t opts
= {
504 .use_parity
= arg_get_lit(ctx
, 1),
505 .block
= arg_get_int_def(ctx
, 2, 1),
506 .value
= {0}, // hex value macro exits function, so cannot be initialized here
509 CLIGetHexWithReturn(ctx
, 3, opts
.value
, &value_len
);
512 if (opts
.block
>= EM4X70_NUM_BLOCKS
) {
513 PrintAndLogEx(FAILED
, "block has to be within range [0, 15], got %d", opts
.block
);
516 if (value_len
!= 2) {
517 PrintAndLogEx(FAILED
, "word/data length must be 2 bytes, got %d", value_len
);
521 // Client command line parsing and validation complete ... now use the helper function
522 em4x70_tag_info_t info
;
523 int result
= writeblock_em4x70(&opts
, &info
);
525 if (result
== PM3_ETIMEOUT
) {
526 PrintAndLogEx(WARNING
, "Timeout while waiting for reply.");
527 } else if (result
== PM3_SUCCESS
) {
528 em4x70_print_info_result(&info
);
530 PrintAndLogEx(FAILED
, "Writing ( " _RED_("fail") " )");
535 static int CmdEM4x70Brute(const char *Cmd
) {
537 // From paper "Dismantling Megamos Crypto", Roel Verdult, Flavio D. Garcia and Barıs¸ Ege.
538 // Partial Key-Update Attack (optimized version)
539 CLIParserContext
*ctx
;
540 CLIParserInit(&ctx
, "lf em 4x70 brute",
541 "Optimized partial key-update attack of 16-bit key block 7, 8 or 9 of an EM4x70\n"
542 "This attack does NOT write anything to the tag.\n"
543 "Before starting this attack, 0000 must be written to the 16-bit key block: 'lf em 4x70 write -b 9 -d 0000'.\n"
544 "After success, the 16-bit key block have to be restored with the key found: 'lf em 4x70 write -b 9 -d c0de'\n",
545 "lf em 4x70 brute -b 9 --rnd 45F54ADA252AAC --frn 4866BB70 --> bruteforcing key bits k95...k80 (pm3 test key)\n"
546 "lf em 4x70 brute -b 8 --rnd 3FFE1FB6CC513F --frn F355F1A0 --> bruteforcing key bits k79...k64 (research paper key)\n"
547 "lf em 4x70 brute -b 7 --rnd 7D5167003571F8 --frn 982DBCC0 --> bruteforcing key bits k63...k48 (autorecovery test key)\n"
551 arg_lit0(NULL
, "par", "Add parity bit when sending commands"),
552 arg_int1("b", "block", "<dec>", "block/word address, dec"),
553 arg_str1(NULL
, "rnd", "<hex>", "Random 56-bit"),
554 arg_str1(NULL
, "frn", "<hex>", "F(RN) 28-bit as 4 hex bytes"),
555 arg_str0("s", "start", "<hex>", "Start bruteforce enumeration from this key value"),
558 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
560 em4x70_cmd_input_brute_t opts
= {
561 .use_parity
= arg_get_lit(ctx
, 1),
562 .block
= arg_get_int_def(ctx
, 2, 0),
563 .rn
= {{0}}, // hex value macro exits function, so cannot be initialized here
564 .frn
= {{0}}, // hex value macro exits function, so cannot be initialized here
565 .partial_key_start
= {0}, // hex value macro exits function, so cannot be initialized here
568 if (opts
.block
< 7 || opts
.block
> 9) {
569 PrintAndLogEx(FAILED
, "block has to be within range [7, 9], got %d", opts
.block
);
575 CLIGetHexWithReturn(ctx
, 3, opts
.rn
.rn
, &rnd_len
);
578 CLIGetHexWithReturn(ctx
, 4, opts
.frn
.frn
, &frnd_len
);
580 // would prefer to use above CLIGetHexWithReturn(), but it does not
581 // appear to support optional arguments.
582 uint32_t start_key
= 0;
583 int res
= arg_get_u32_hexstr_def_nlen(ctx
, 5, 0, &start_key
, 2, true); // this stores in NATIVE ENDIAN
585 PrintAndLogEx(WARNING
, "start key parameter must be in range [0, FFFF]");
591 // opts structure takes value in BIG ENDIAN form
592 opts
.partial_key_start
[0] = (uint8_t)((start_key
>> 8) & 0xFF);
593 opts
.partial_key_start
[1] = (uint8_t)((start_key
>> 0) & 0xFF);
596 PrintAndLogEx(FAILED
, "Random number length must be 7 bytes, got %d", rnd_len
);
601 PrintAndLogEx(FAILED
, "F(RN) length must be 4 bytes, got %d", frnd_len
);
605 // Client command line parsing and validation complete ... now use the helper function
606 PrintAndLogEx(INFO
, "Press " _GREEN_("pm3 button") " or " _GREEN_("<Enter>") " to exit");
607 em4x70_cmd_output_brute_t data
;
608 int result
= brute_em4x70(&opts
, &data
);
609 if (result
== PM3_EOPABORTED
) {
610 PrintAndLogEx(DEBUG
, "User aborted");
611 } else if (result
== PM3_ETIMEOUT
) {
612 PrintAndLogEx(WARNING
, "\nNo response from Proxmark3. Aborting...");
613 } else if (result
== PM3_SUCCESS
) {
614 PrintAndLogEx(INFO
, "Partial Key Response... %02X %02X", data
.partial_key
[0], data
.partial_key
[1]);
616 PrintAndLogEx(FAILED
, "Bruteforce of partial key ( " _RED_("fail") " )");
621 static int CmdEM4x70Unlock(const char *Cmd
) {
623 // send pin code to device, unlocking it for writing
624 CLIParserContext
*ctx
;
625 CLIParserInit(&ctx
, "lf em 4x70 unlock",
626 "Unlock EM4x70 by sending PIN\n"
627 "Default pin may be:\n"
630 "lf em 4x70 unlock -p 11223344 -> Unlock with PIN\n"
631 "lf em 4x70 unlock -p 11223344 --par -> Unlock with PIN using parity commands\n"
635 arg_lit0(NULL
, "par", "Add parity bit when sending commands"),
636 arg_str1("p", "pin", "<hex>", "pin, 4 bytes"),
640 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
642 em4x70_cmd_input_unlock_t opts
= {
643 .use_parity
= arg_get_lit(ctx
, 1),
644 .pin
= {0}, // hex value macro exits function, so cannot be initialized here
647 CLIGetHexWithReturn(ctx
, 2, opts
.pin
, &pin_len
);
651 PrintAndLogEx(FAILED
, "PIN length must be 4 bytes, got %d", pin_len
);
655 // Client command line parsing and validation complete ... now use the helper function
656 em4x70_tag_info_t info
;
657 int result
= unlock_em4x70(&opts
, &info
);
659 if (result
== PM3_ETIMEOUT
) {
660 PrintAndLogEx(WARNING
, "Timeout while waiting for reply.");
661 } else if (result
== PM3_SUCCESS
) {
662 em4x70_print_info_result(&info
);
664 PrintAndLogEx(FAILED
, "Unlocking tag ( " _RED_("fail") " )");
669 static int CmdEM4x70Auth(const char *Cmd
) {
671 // Authenticate transponder
672 // Send 56-bit random number + pre-computed f(rnd, k) to transponder.
673 // Transponder will respond with a response
674 CLIParserContext
*ctx
;
676 CLIParserInit(&ctx
, "lf em 4x70 auth",
677 "Authenticate against an EM4x70 by sending random number (RN) and F(RN)\n"
678 " If F(RN) is incorrect based on the tag key, the tag will not respond\n"
679 " If F(RN) is correct based on the tag key, the tag will give a 20-bit response\n",
680 "lf em 4x70 auth --rnd 45F54ADA252AAC --frn 4866BB70 --> (using pm3 test key)\n"
681 "lf em 4x70 auth --rnd 3FFE1FB6CC513F --frn F355F1A0 --> (using research paper key)\n"
682 "lf em 4x70 auth --rnd 7D5167003571F8 --frn 982DBCC0 --> (autorecovery test key)\n"
687 arg_lit0(NULL
, "par", "Add parity bit when sending commands"),
688 arg_str1(NULL
, "rnd", "<hex>", "Random 56-bit"),
689 arg_str1(NULL
, "frn", "<hex>", "F(RN) 28-bit as 4 hex bytes"),
693 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
695 em4x70_cmd_input_auth_t opts
= {
696 .use_parity
= arg_get_lit(ctx
, 1),
697 .rn
= {{0}}, // hex value macro exits function, so cannot be initialized here
698 .frn
= {{0}}, // hex value macro exits function, so cannot be initialized here
701 CLIGetHexWithReturn(ctx
, 2, opts
.rn
.rn
, &rn_len
);
704 CLIGetHexWithReturn(ctx
, 3, opts
.frn
.frn
, &frn_len
);
707 PrintAndLogEx(FAILED
, "Random number length must be 7 bytes, got %d", rn_len
);
711 PrintAndLogEx(FAILED
, "F(RN) length must be 4 bytes, got %d", frn_len
);
715 // Client command line parsing and validation complete ... now use the helper function
716 em4x70_cmd_output_auth_t data
;
717 int result
= auth_em4x70(&opts
, &data
);
719 if (PM3_SUCCESS
== result
) {
720 PrintAndLogEx(INFO
, "Tag Auth Response: %02X %02X %02X", data
.grn
.grn
[0], data
.grn
.grn
[1], data
.grn
.grn
[2]);
721 } else if (PM3_ETIMEOUT
== result
) {
722 PrintAndLogEx(WARNING
, "Timeout while waiting for reply.");
724 PrintAndLogEx(FAILED
, "TAG Authentication ( " _RED_("fail") " )");
729 static int CmdEM4x70SetPIN(const char *Cmd
) {
730 CLIParserContext
*ctx
;
731 CLIParserInit(&ctx
, "lf em 4x70 setpin",
733 "lf em 4x70 setpin -p 11223344 -> Write new PIN\n"
734 "lf em 4x70 setpin -p 11223344 --par -> Write new PIN using parity commands\n"
738 arg_lit0(NULL
, "par", "Add parity bit when sending commands"),
739 arg_str1("p", "pin", "<hex>", "pin, 4 bytes"),
742 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
744 em4x70_cmd_input_setpin_t opts
= {
745 .use_parity
= arg_get_lit(ctx
, 1),
746 .pin
= {0}, // hex value macro exits function, so cannot be initialized here
750 CLIGetHexWithReturn(ctx
, 2, opts
.pin
, &pin_len
);
754 PrintAndLogEx(FAILED
, "PIN length must be 4 bytes, got %d", pin_len
);
758 // Client command line parsing and validation complete ... now use the helper function
759 em4x70_tag_info_t info
;
761 int result
= setpin_em4x70(&opts
, &info
);
763 if (result
== PM3_ETIMEOUT
) {
764 PrintAndLogEx(WARNING
, "Timeout while waiting for reply.");
765 } else if (result
== PM3_SUCCESS
) {
766 em4x70_print_info_result(&info
);
767 PrintAndLogEx(INFO
, "Writing new PIN ( " _GREEN_("ok") " )");
769 PrintAndLogEx(FAILED
, "Writing new PIN ( " _RED_("fail") " )");
774 static int CmdEM4x70SetKey(const char *Cmd
) {
775 CLIParserContext
*ctx
;
776 CLIParserInit(&ctx
, "lf em 4x70 setkey",
777 "Write new 96-bit key to tag\n",
778 "lf em 4x70 setkey -k F32AA98CF5BE4ADFA6D3480B (pm3 test key)\n"
779 "lf em 4x70 setkey -k A090A0A02080000000000000 (research paper key)\n"
780 "lf em 4x70 setkey -k 022A028C02BE000102030405 (autorecovery test key)\n"
785 arg_lit0(NULL
, "par", "Add parity bit when sending commands"),
786 arg_str1("k", "key", "<hex>", "Key as 12 hex bytes"),
790 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
792 em4x70_cmd_input_setkey_t opts
= {
793 .use_parity
= arg_get_lit(ctx
, 1),
794 .key
= {{0}}, // hex value macro exits function, so cannot be initialized here
797 CLIGetHexWithReturn(ctx
, 2, opts
.key
.k
, &key_len
);
800 PrintAndLogEx(FAILED
, "Key length must be 12 bytes, got %d", key_len
);
804 // Client command line parsing and validation complete ... now use the helper function
805 int result
= setkey_em4x70(&opts
);
807 if (PM3_ETIMEOUT
== result
) {
808 PrintAndLogEx(WARNING
, "Timeout while waiting for reply.");
810 } else if (PM3_SUCCESS
!= result
) {
811 PrintAndLogEx(FAILED
, "Writing new key " _RED_("fail"));
815 PrintAndLogEx(INFO
, "Writing new key ( " _GREEN_("ok") " )");
817 // Now verify authentication using the new key, to ensure it was correctly written
818 em4x70_cmd_input_verify_auth_t opts_v
= {
819 .use_parity
= opts
.use_parity
,
820 //.rn = opts_auth.rn,
821 //.frn = opts_auth.frn,
824 fill_buffer_prng_bytes(&opts_v
.rn
, sizeof(ID48LIB_NONCE
));
825 id48lib_generator(&opts
.key
, &opts_v
.rn
, &opts_v
.frn
, &opts_v
.grn
);
827 // dump the auth command to the screen, to enable the user to manually check validity
829 "Verifying auth for new key: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
830 " --> " _YELLOW_("lf em 4x70 auth --rnd %02X%02X%02X%02X%02X%02X%02X --frn %02X%02X%02X%02X")
832 opts
.key
.k
[ 0], opts
.key
.k
[ 1], opts
.key
.k
[ 2], opts
.key
.k
[ 3], opts
.key
.k
[ 4], opts
.key
.k
[ 5],
833 opts
.key
.k
[ 6], opts
.key
.k
[ 7], opts
.key
.k
[ 8], opts
.key
.k
[ 9], opts
.key
.k
[10], opts
.key
.k
[11],
850 result
= verify_auth_em4x70(&opts_v
);
852 if (PM3_ETIMEOUT
== result
) {
853 PrintAndLogEx(WARNING
, "Timeout while waiting for reply.");
855 } else if (PM3_SUCCESS
!= result
) {
856 PrintAndLogEx(FAILED
, "Authenticating with new key ( " _RED_("fail") " )");
859 PrintAndLogEx(INFO
, "Authenticating with new key ( " _GREEN_("ok") " )");
864 typedef struct _em4x70_recovery_data_t
{
866 em4x70_cmd_input_recover_t opts
;
867 em4x70_cmd_output_recover_t data
;
869 uint8_t keys_validated_count
;
870 ID48LIB_NONCE alt_nonce
;
871 ID48LIB_FRN alt_frn
[MAXIMUM_ID48_RECOVERED_KEY_COUNT
];
872 ID48LIB_GRN alt_grn
[MAXIMUM_ID48_RECOVERED_KEY_COUNT
];
873 bool potential_keys_validated
[MAXIMUM_ID48_RECOVERED_KEY_COUNT
];
874 } em4x70_recovery_data_t
;
876 static int CmdEM4x70Recover_ParseArgs(const char *Cmd
, em4x70_cmd_input_recover_t
*out_results
) {
878 memset(out_results
, 0, sizeof(em4x70_cmd_input_recover_t
));
880 int result
= PM3_SUCCESS
;
882 CLIParserContext
*ctx
;
885 "lf em 4x70 recover",
886 "After obtaining key bits 95..48 (such as via 'lf em 4x70 brute'), this command will recover\n"
887 "key bits 47..00. By default, this process does NOT require a tag to be present.\n"
889 "By default, the potential keys are shown (typically 1-6) along with a corresponding\n"
890 "'lf em 4x70 auth' command that will authenticate, if that potential key is correct.\n"
891 "The user can copy/paste these commands when the tag is present to manually check\n"
892 "which of the potential keys is correct.\n"
894 // "If the `--verify` option is provided, the tag must be present. The rnd/frn parameters will\n"
895 // "be used to authenticate against the tag, and then any potential keys will be automatically\n"
896 // "be checked for correctness against the tag, reducing manual steps.\n"
898 "lf em 4x70 recover --key F32AA98CF5BE --rnd 45F54ADA252AAC --frn 4866BB70 --grn 9BD180 (pm3 test key)\n"
899 "lf em 4x70 recover --key A090A0A02080 --rnd 3FFE1FB6CC513F --frn F355F1A0 --grn 609D60 (research paper key)\n"
900 "lf em 4x70 recover --key 022A028C02BE --rnd 7D5167003571F8 --frn 982DBCC0 --grn 36C0E0 (autorecovery test key)\n"
905 arg_lit0(NULL
, "par", "Add parity bit when sending commands"),
906 arg_str1("k", "key", "<hex>", "Key as 6 hex bytes"),
907 arg_str1(NULL
, "rnd", "<hex>", "Random 56-bit"),
908 arg_str1(NULL
, "frn", "<hex>", "F(RN) 28-bit as 4 hex bytes"),
909 arg_str1(NULL
, "grn", "<hex>", "G(RN) 20-bit as 3 hex bytes"),
910 //arg_lit0(NULL, "verify", "automatically use tag for validation"),
914 // do the command line arguments even parse?
915 if (CLIParserParseString(ctx
, Cmd
, argtable
, arg_getsize(argtable
), true)) {
919 int key_len
= 0; // must be 6 bytes hex data
920 int rnd_len
= 0; // must be 7 bytes hex data
921 int frn_len
= 0; // must be 4 bytes hex data
922 int grn_len
= 0; // must be 3 bytes hex data
924 // if all OK so far, convert to internal data structure
925 if (PM3_SUCCESS
== result
) {
926 // magic number == index in argtable above. Fragile technique!
927 out_results
->parity
= arg_get_lit(ctx
, 1);
928 if (CLIParamHexToBuf(arg_get_str(ctx
, 2), &(out_results
->key
.k
[0]), 12, &key_len
)) {
931 if (CLIParamHexToBuf(arg_get_str(ctx
, 3), &(out_results
->nonce
.rn
[0]), 7, &rnd_len
)) {
934 if (CLIParamHexToBuf(arg_get_str(ctx
, 4), &(out_results
->frn
.frn
[0]), 4, &frn_len
)) {
937 if (CLIParamHexToBuf(arg_get_str(ctx
, 5), &(out_results
->grn
.grn
[0]), 3, &grn_len
)) {
940 //out_results->verify = arg_get_lit(ctx, 6);
943 // if all OK so far, do additional parameter validation
944 if (PM3_SUCCESS
== result
) {
945 // Validate number of bytes read for hex data
947 PrintAndLogEx(FAILED
, "Key length must be 6 bytes, got %d", key_len
);
948 result
= PM3_EINVARG
;
952 PrintAndLogEx(FAILED
, "Random number length must be 7 bytes, got %d", rnd_len
);
953 result
= PM3_EINVARG
;
957 PrintAndLogEx(FAILED
, "F(RN) length must be 4 bytes, got %d", frn_len
);
958 result
= PM3_EINVARG
;
962 PrintAndLogEx(FAILED
, "G(RN) length must be 3 bytes, got %d", grn_len
);
963 result
= PM3_EINVARG
;
972 static int CmdEM4x70Recover(const char *Cmd
) {
973 // From paper "Dismantling Megamos Crypto", Roel Verdult, Flavio D. Garcia and Barıs¸ Ege.
974 // Partial Key-Update Attack -- final 48 bits (after optimized version gets k95..k48)
975 em4x70_recovery_data_t recover_ctx
= {0};
976 int result
= PM3_SUCCESS
;
978 result
= CmdEM4x70Recover_ParseArgs(Cmd
, &recover_ctx
.opts
);
979 // recover the potential keys -- no more than a few seconds
980 if (PM3_SUCCESS
== result
) {
982 result
= recover_em4x70(&recover_ctx
.opts
, &recover_ctx
.data
);
983 if (PM3_EOVFLOW
== result
) {
984 PrintAndLogEx(ERR
, "Found more than %d potential keys. This is unexpected and likely a code failure.", MAXIMUM_ID48_RECOVERED_KEY_COUNT
);
985 } else if (PM3_SUCCESS
!= result
) {
986 PrintAndLogEx(ERR
, "No potential keys recovered. This is unexpected and likely a code failure.");
990 // generate alternate authentication for each potential key -- no error paths, sub-second execution
991 if (PM3_SUCCESS
== result
) {
993 fill_buffer_prng_bytes(&recover_ctx
.alt_nonce
, sizeof(ID48LIB_NONCE
));
994 for (uint8_t i
= 0; i
< recover_ctx
.data
.potential_key_count
; ++i
) {
995 // generate the alternate frn/grn for the alternate nonce
996 id48lib_generator(&recover_ctx
.data
.potential_keys
[i
], &recover_ctx
.alt_nonce
, &recover_ctx
.alt_frn
[i
], &recover_ctx
.alt_grn
[i
]);
1000 // display alternate authentication for each potential key -- no error paths
1001 if (PM3_SUCCESS
== result
) {
1003 PrintAndLogEx(INFO
, "Recovered %d potential keys:", recover_ctx
.data
.potential_key_count
);
1004 for (uint8_t i
= 0; i
< recover_ctx
.data
.potential_key_count
; ++i
) {
1005 // generate an alternative authentication based on the potential key
1006 // and the alternate nonce.
1007 ID48LIB_KEY q
= recover_ctx
.data
.potential_keys
[i
];
1008 ID48LIB_FRN alt_frn
= recover_ctx
.alt_frn
[i
];
1009 ID48LIB_GRN alt_grn
= recover_ctx
.alt_grn
[i
];
1011 // dump the results to screen, to enable the user to manually check validity
1013 "Potential Key #%d: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
1014 " --> " _YELLOW_("lf em 4x70 auth --rnd %02X%02X%02X%02X%02X%02X%02X --frn %02X%02X%02X%02X")
1015 " --> %02X%02X%02X",
1017 q
.k
[ 0], q
.k
[ 1], q
.k
[ 2], q
.k
[ 3], q
.k
[ 4], q
.k
[ 5],
1018 q
.k
[ 6], q
.k
[ 7], q
.k
[ 8], q
.k
[ 9], q
.k
[10], q
.k
[11],
1019 recover_ctx
.alt_nonce
.rn
[0],
1020 recover_ctx
.alt_nonce
.rn
[1],
1021 recover_ctx
.alt_nonce
.rn
[2],
1022 recover_ctx
.alt_nonce
.rn
[3],
1023 recover_ctx
.alt_nonce
.rn
[4],
1024 recover_ctx
.alt_nonce
.rn
[5],
1025 recover_ctx
.alt_nonce
.rn
[6],
1037 // which of those keys actually validates?
1038 if (PM3_SUCCESS
== result
&& recover_ctx
.opts
.verify
) {
1039 // TODO: automatic verification against a present tag.
1040 // Updates ctx.potential_keys_validated[10] and ctx.keys_validated_count
1041 PrintAndLogEx(WARNING
, "Automatic verification against tag is not yet implemented.");
1042 // 0. verify a tag is present
1043 // 1. verify the parameters provided authenticate against the tag
1044 // if not, print "Authentication failed. Verify the current tag matches parameters provided."
1045 // print the authentication command used (allows user to easily copy/paste)
1047 // 2. for each potential key:
1048 // a. Attempt to authentic against the tag using alt_nonce and alt_frn[i]
1049 // b. verify tag's response is alt_grn[i]
1050 // c. if successful, set ctx.potential_keys_validated[i] = true and increment ctx.keys_validated_count
1052 // All validation done... now just interpret the results....
1054 // 3. if ctx.keys_validated_count == 0, print "No keys recovered. Check tag for good coupling (position, etc)?"
1055 // 4. if ctx.keys_validated_count >= 2, print "Multiple keys recovered. Run command again (will use different alt nonce)?"
1056 // 5. if ctx.keys_validated_count == 1, print "Found key: " ...
1061 static int CmdEM4x70AutoRecover_ParseArgs(const char *Cmd
, em4x70_cmd_input_recover_t
*out_results
) {
1062 memset(out_results
, 0, sizeof(em4x70_cmd_input_recover_t
));
1064 int result
= PM3_SUCCESS
;
1065 // The following key is found quickly, and has multiple potential keys.
1066 // Useful for quicker testing, as this function could take over 2 hours.
1067 // lf em 4x70 setkey -k 001200340055BAADCAFEF00D
1068 // lf em 4x70 autorecover --rnd 1782779E7E3BC8 --frn 00357080 --grn F3C480
1069 CLIParserContext
*ctx
;
1072 "lf em 4x70 autorecover",
1073 "This command will perform automatic recovery of the key from a writable tag.\n"
1074 "All steps are possible to do manually. The corresponding sequence, if done\n"
1075 "manually, is as follows:\n"
1076 "1. Verify passed parameters authenticate with the tag (safety check)\n"
1077 " " _YELLOW_("lf em 4x70 auth --rnd <rnd_1> --frn <frn_1>") "\n"
1078 "2. Brute force the key bits in block 9\n"
1079 " " _YELLOW_("lf em 4x70 write -b 9 -d 0000") "\n"
1080 " " _YELLOW_("lf em 4x70 recover -b 9 --rnd <rnd_1> --frn <frn_1>") "\n"
1081 " " _YELLOW_("lf em 4x70 write -b 9 -d <key_block_9>") "\n"
1082 "3. Brute force the key bits in block 8\n"
1083 " " _YELLOW_("lf em 4x70 write -b 8 -d 0000") "\n"
1084 " " _YELLOW_("lf em 4x70 recover -b 8 --rnd <rnd_1> --frn <frn_1>") "\n"
1085 " " _YELLOW_("lf em 4x70 write -b 8 -d <key_block_8>") "\n"
1086 "4. Brute force the key bits in block 7\n"
1087 " " _YELLOW_("lf em 4x70 write -b 7 -d 0000)") "\n"
1088 " " _YELLOW_("lf em 4x70 recover -b 7 --rnd <rnd_1> --frn <frn_1>") "\n"
1089 " " _YELLOW_("lf em 4x70 write -b 7 -d <key_block_7>") "\n"
1090 "5. Recover potential values of the lower 48 bits of the key\n"
1091 " " _YELLOW_("lf em 4x70 recover --key <key_block_9><key_block_8><key_block_7> --rnd <rnd_1> --frn <frn_1>") "\n"
1092 "6. Verify which potential key is actually on the tag (using a different rnd/frn combination)\n"
1093 " " _YELLOW_("lf em 4x70 auth --rnd <rnd_2> --frn <frn_N>") "\n"
1094 "7. Print the validated key\n"
1096 "This command simply requires the rnd/frn/grn from a single known-good authentication.\n"
1100 // "If the `--verify` option is provided, the tag must be present. The rnd/frn parameters will\n"
1101 // "be used to authenticate against the tag, and then any potential keys will be automatically\n"
1102 // "be checked for correctness against the tag, reducing manual steps.\n"
1104 "lf em 4x70 autorecover --rnd 45F54ADA252AAC --frn 4866BB70 --grn 9BD180 (pm3 test key)\n"
1105 "lf em 4x70 autorecover --rnd 3FFE1FB6CC513F --frn F355F1A0 --grn 609D60 (research paper key)\n"
1106 "lf em 4x70 autorecover --rnd 7D5167003571F8 --frn 982DBCC0 --grn 36C0E0 (autorecovery test key)\n"
1109 void *argtable
[] = {
1111 arg_lit0(NULL
, "par", "Add parity bit when sending commands"),
1112 arg_str1(NULL
, "rnd", "<hex>", "Random 56-bit from known-good authentication"),
1113 arg_str1(NULL
, "frn", "<hex>", "F(RN) 28-bit as 4 hex bytes from known-good authentication"),
1114 arg_str1(NULL
, "grn", "<hex>", "G(RN) 20-bit as 3 hex bytes from known-good authentication"),
1115 //arg_lit0(NULL, "verify", "automatically use tag for validation"),
1119 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1121 int rnd_len
= 0; // must be 7 bytes hex data
1122 int frn_len
= 0; // must be 4 bytes hex data
1123 int grn_len
= 0; // must be 3 bytes hex data
1124 out_results
->parity
= arg_get_lit(ctx
, 1);
1125 CLIGetHexWithReturn(ctx
, 2, out_results
->nonce
.rn
, &rnd_len
);
1126 CLIGetHexWithReturn(ctx
, 3, out_results
->frn
.frn
, &frn_len
);
1127 CLIGetHexWithReturn(ctx
, 4, out_results
->grn
.grn
, &grn_len
);
1131 PrintAndLogEx(FAILED
, "Random number length must be 7 bytes, got %d", rnd_len
);
1132 result
= PM3_EINVARG
;
1136 PrintAndLogEx(FAILED
, "F(RN) length must be 4 bytes, got %d", frn_len
);
1137 result
= PM3_EINVARG
;
1141 PrintAndLogEx(FAILED
, "G(RN) length must be 3 bytes, got %d", grn_len
);
1142 result
= PM3_EINVARG
;
1147 static int CmdEM4x70AutoRecover(const char *Cmd
) {
1148 em4x70_cmd_input_recover_t opts
= {0};
1149 em4x70_cmd_output_recover_t data
= {0};
1150 em4x70_tag_info_t tag_info
= {0};
1152 int result
= CmdEM4x70AutoRecover_ParseArgs(Cmd
, &opts
);
1153 // 0. Parse the command line
1154 if (PM3_SUCCESS
!= result
) {
1158 // The parameters are valid. Per Iceman's direct request, the code has been updated
1159 // to immediately exit on errors. Unfortunately, this requirement limits the clarity
1160 // of summarizing the failure (and providing options for recovery in case of failures)
1161 // at a single point at the end of the function. It will also undoubtedly reduce
1162 // code coverage numbers, when those are tracked.
1164 // As to clarity, if failures occurred in steps 2-4, it was expected that the cleanup
1165 // code would, in a single location, verify if the original authentication worked.
1166 // If so, then the tag was left in a good state (even if an error occurred).
1167 // If not, then at least it would be possible for the user to restart manually, and
1168 // to be given clear instructions on how to do that to return the tag to its original
1171 // TODO: Wrap this entire function in another function, whose sole purpose is to
1172 // perform that additional cleanup? Not a great solution. Pity, as the
1173 // cleanup code was much more helpful than the below print statements.
1174 int last_successful_step
= 0;
1175 char rnd_string
[14 + 1] = {0};
1176 char frn_string
[ 8 + 1] = {0};
1177 char grn_string
[ 6 + 1] = {0};
1178 // These strings will be re-used often, are safe to pre-allocate, and make later PrintAndLogEx() calls cleaner.
1179 snprintf(rnd_string
, 15, "%02X%02X%02X%02X%02X%02X%02X", opts
.nonce
.rn
[0], opts
.nonce
.rn
[1], opts
.nonce
.rn
[2], opts
.nonce
.rn
[3], opts
.nonce
.rn
[4], opts
.nonce
.rn
[5], opts
.nonce
.rn
[6]);
1180 snprintf(frn_string
, 9, "%02X%02X%02X%02X", opts
.frn
.frn
[0], opts
.frn
.frn
[1], opts
.frn
.frn
[2], opts
.frn
.frn
[3]);
1181 snprintf(grn_string
, 7, "%02X%02X%02X", opts
.grn
.grn
[0], opts
.grn
.grn
[1], opts
.grn
.grn
[2]);
1183 // 1. Verify passed parameters authenticate with the tag (safety check)
1184 // lf em 4x70 auth --rnd <rnd_1> --frn <frn_1>
1185 if (PM3_SUCCESS
== result
) {
1186 PrintAndLogEx(INFO
, "Step 1. Verifying passed parameters authenticate with the tag (safety check)");
1187 PrintAndLogEx(HINT
, " " _YELLOW_("lf em 4x70 auth --rnd %s --frn %s"), rnd_string
, frn_string
);
1189 em4x70_cmd_input_auth_t opts_auth
= {
1190 .use_parity
= opts
.parity
,
1195 em4x70_cmd_output_auth_t tag_grn
;
1197 result
= auth_em4x70(&opts_auth
, &tag_grn
);
1199 if (PM3_ETIMEOUT
== result
) {
1200 PrintAndLogEx(WARNING
, "Timeout while waiting for reply.");
1202 } else if (PM3_SUCCESS
!= result
) {
1203 PrintAndLogEx(FAILED
, "Authenticating with provided values ( " _RED_("fail") " )");
1205 } else if (memcmp(&opts
.grn
, &tag_grn
, sizeof(ID48LIB_GRN
)) != 0) {
1206 PrintAndLogEx(FAILED
, "Authenticating with new key returned %02x %02x %02x"
1207 , tag_grn
.grn
.grn
[0]
1208 , tag_grn
.grn
.grn
[1]
1209 , tag_grn
.grn
.grn
[2]
1211 PrintAndLogEx(FAILED
, "Expected %s [maybe 5 lsb of key wrong?] ( " _RED_("fail") " )", grn_string
);
1212 result
= PM3_EWRONGANSWER
;
1215 last_successful_step
= 1;
1218 // 2/3/4. Brute force the key bits in block 7,8,9
1219 // lf em 4x70 write -b N -d 0000
1220 // lf em 4x70 brute -b N --rnd <rnd_1> --frn <frn_1>
1221 // lf em 4x70 write -b N -d <key_block_N>
1222 for (uint8_t block
= 9; (PM3_SUCCESS
== result
) && (block
> 6); --block
) {
1228 em4x70_cmd_output_brute_t brute
= {0};
1230 // lf em 4x70 write -b N -d 0000
1231 if (PM3_SUCCESS
== result
) {
1232 PrintAndLogEx(INFO
, "Step %d. Brute force the key bits in block %d", step
, block
);
1233 PrintAndLogEx(HINT
, " " _YELLOW_("lf em 4x70 write -b %d -d 0000"), block
);
1235 em4x70_cmd_input_writeblock_t opt_write_zeros
= {
1236 .use_parity
= opts
.parity
,
1238 .value
= {0x00, 0x00},
1241 result
= writeblock_em4x70(&opt_write_zeros
, &tag_info
);
1243 if (PM3_ETIMEOUT
== result
) {
1244 PrintAndLogEx(FAILED
, "Timeout while waiting for reply.");
1245 PrintAndLogEx(HINT
, "Block %d data may have been overwritten. Manually restart at step %d", block
, step
);
1247 } else if (PM3_SUCCESS
!= result
) {
1248 PrintAndLogEx(FAILED
, "Writing block %d ( " _RED_("fail") " )", block
);
1249 PrintAndLogEx(HINT
, "Block %d data was overwritten. Manually restart at step %d", block
, step
);
1254 // lf em 4x70 brute -b N --rnd <rnd_1> --frn <frn_1>
1255 if (PM3_SUCCESS
== result
) {
1256 PrintAndLogEx(HINT
, " " _YELLOW_("lf em 4x70 brute -b %d --rnd %s --frn %s"), block
, rnd_string
, frn_string
);
1258 em4x70_cmd_input_brute_t opts_brute
= {
1259 .use_parity
= opts
.parity
,
1263 .partial_key_start
= {0},
1266 result
= brute_em4x70(&opts_brute
, &brute
);
1268 if (PM3_ETIMEOUT
== result
) {
1269 PrintAndLogEx(FAILED
, "Timeout while waiting for reply.");
1270 PrintAndLogEx(HINT
, "Block %d data was overwritten. Manually restart at step %d", block
, step
);
1272 } else if (PM3_SUCCESS
!= result
) {
1273 PrintAndLogEx(FAILED
, "Writing block %d ( " _RED_("fail") " )", block
);
1274 PrintAndLogEx(HINT
, "Block %d data was overwritten. Manually restart at step %d", block
, step
);
1277 PrintAndLogEx(INFO
, " Found: Partial key in block %d is " _GREEN_("%02X%02X")
1279 , brute
.partial_key
[0]
1280 , brute
.partial_key
[1]
1282 // Save the partial key...
1284 opts
.key
.k
[0] = brute
.partial_key
[0];
1285 opts
.key
.k
[1] = brute
.partial_key
[1];
1286 } else if (block
== 8) {
1287 opts
.key
.k
[2] = brute
.partial_key
[0];
1288 opts
.key
.k
[3] = brute
.partial_key
[1];
1289 } else if (block
== 7) {
1290 opts
.key
.k
[4] = brute
.partial_key
[0];
1291 opts
.key
.k
[5] = brute
.partial_key
[1];
1295 // lf em 4x70 write -b N -d <key_block_N>
1296 if (PM3_SUCCESS
== result
) {
1297 PrintAndLogEx(HINT
, " " _YELLOW_("lf em 4x70 write -b %d -d %02X%02X"), block
, brute
.partial_key
[0], brute
.partial_key
[1]);
1299 em4x70_cmd_input_writeblock_t opt_write_zeros
= {
1300 .use_parity
= opts
.parity
,
1302 .value
= {brute
.partial_key
[0], brute
.partial_key
[1]},
1305 result
= writeblock_em4x70(&opt_write_zeros
, &tag_info
);
1307 if (PM3_ETIMEOUT
== result
) {
1308 PrintAndLogEx(FAILED
, "Timeout while waiting for reply.");
1309 PrintAndLogEx(HINT
, "Block %d data (" _GREEN_("%02X%02X") ") may need to be rewritten", block
, brute
.partial_key
[0], brute
.partial_key
[1]);
1311 } else if (PM3_SUCCESS
!= result
) {
1312 PrintAndLogEx(FAILED
, "Writing block %d ( " _RED_("fail") " )", block
);
1313 PrintAndLogEx(HINT
, "Block %d data (" _GREEN_("%02X%02X") ") may need to be rewritten", block
, brute
.partial_key
[0], brute
.partial_key
[1]);
1318 if (PM3_SUCCESS
== result
) {
1319 last_successful_step
= step
;
1322 // The good news is that, if the above succeeded, then from this point forward, the tag remains in a known-good state.
1324 char key_string
[24 + 1] = {0}; // holds partial key initially, full key later
1325 snprintf(key_string
, 25, "%02X%02X%02X%02X%02X%02X", opts
.key
.k
[0], opts
.key
.k
[1], opts
.key
.k
[2], opts
.key
.k
[3], opts
.key
.k
[4], opts
.key
.k
[5]);
1327 // 5. Recover potential values of the lower 48 bits of the key
1328 // lf em 4x70 recover --key <key_block_9><key_block_8><key_block_7> --rnd <rnd_1> --frn <frn_1>
1329 if (PM3_SUCCESS
== result
) {
1330 PrintAndLogEx(INFO
, "Step 5. Recover potential values of the lower 48 bits of the key");
1331 PrintAndLogEx(HINT
, " " _YELLOW_("lf em 4x70 recover --key %s --rnd %s --frn %s --grn %s"), key_string
, rnd_string
, frn_string
, grn_string
);
1333 result
= recover_em4x70(&opts
, &data
);
1335 if (PM3_EOVFLOW
== result
) {
1336 PrintAndLogEx(ERR
, "Found more than %d potential keys. This is unexpected and likely a code failure.", MAXIMUM_ID48_RECOVERED_KEY_COUNT
);
1338 } else if (PM3_SUCCESS
!= result
) {
1339 PrintAndLogEx(ERR
, "No potential keys recovered. This is unexpected and likely a code failure.");
1342 PrintAndLogEx(INFO
, " Found " _GREEN_("%d") " potential keys", data
.potential_key_count
);
1343 for (uint8_t idx
= 0; idx
< data
.potential_key_count
; ++idx
) {
1344 ID48LIB_KEY q
= data
.potential_keys
[idx
];
1345 PrintAndLogEx(DEBUG
, " Potential Key %d: %s %02X%02X%02X%02X%02X%02X"
1356 last_successful_step
= 5;
1360 // 6. Verify which potential key is actually on the tag (using a different rnd/frn combination)
1361 // lf em 4x70 auth --rnd <rnd_2> --frn <frn_N>
1362 if (PM3_SUCCESS
== result
) {
1363 PrintAndLogEx(INFO
, "Step 6. Verify which potential key is actually on the tag");
1365 em4x70_cmd_input_verify_auth_t opts_v
= {
1366 .use_parity
= opts
.parity
,
1372 // TODO: retry a few time, if >1 key validated with the new nonce
1373 bool continue_loop
= true;
1374 bool found_one_key
= false;
1375 bool found_more_than_one_key
= false;
1376 uint8_t first_validated_key_idx
= 0xFF;
1378 for (uint8_t attempt
= 0; continue_loop
&& (attempt
< 10); ++attempt
) {
1379 continue_loop
= false;
1380 found_one_key
= false;
1381 found_more_than_one_key
= false;
1382 first_validated_key_idx
= 0xFF;
1383 fill_buffer_prng_bytes(&opts_v
.rn
, sizeof(ID48LIB_NONCE
));
1385 for (uint8_t i
= 0; i
< data
.potential_key_count
; ++i
) {
1386 // generate the alternate frn/grn for this key + nonce combo
1387 id48lib_generator(&data
.potential_keys
[i
], &opts_v
.rn
, &opts_v
.frn
, &opts_v
.grn
);
1389 int tmpResult
= verify_auth_em4x70(&opts_v
);
1390 if (PM3_SUCCESS
== tmpResult
) {
1391 if (!found_one_key
) {
1392 first_validated_key_idx
= i
;
1393 found_one_key
= true;
1395 found_more_than_one_key
= true;
1400 if (found_one_key
== false) {
1401 PrintAndLogEx(WARNING
, "No potential keys validated. Will try again with different nonce");
1402 continue_loop
= true;
1403 msleep(2000); // delay 2 seconds ... in case tag was bumped, etc.
1404 } else if (found_more_than_one_key
) {
1405 PrintAndLogEx(WARNING
, "Multiple potential keys validated. Will try different nonce");
1406 continue_loop
= true;
1407 msleep(2000); // delay 2 seconds ... in case tag was bumped, etc.
1409 last_successful_step
= 6;
1413 if ((found_one_key
== false) || found_more_than_one_key
) {
1414 PrintAndLogEx(FAILED
, "Unable to recover any of the multiple potential keys");
1415 PrintAndLogEx(FAILED
, "Check tag for good coupling / position!");
1418 // print the validated key to the string buffer (for step 7)
1419 ID48LIB_KEY q
= data
.potential_keys
[first_validated_key_idx
];
1420 snprintf(key_string
, 25, "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
1421 q
.k
[ 0], q
.k
[ 1], q
.k
[ 2], q
.k
[ 3], q
.k
[ 4], q
.k
[ 5],
1422 q
.k
[ 6], q
.k
[ 7], q
.k
[ 8], q
.k
[ 9], q
.k
[10], q
.k
[11]
1426 // 7. Print the validated key
1427 if (PM3_SUCCESS
== result
) {
1428 PrintAndLogEx(SUCCESS
, "Recovered key... " _GREEN_("%s"), key_string
);
1429 last_successful_step
= 7;
1432 // For posterity, step 7 used to do the following:
1433 // 7. Print the validated key --OR-- Print that the tag is still OK --OR-- Print instructions on what to retry to recover tag to a good state
1434 // If success ... print the final key
1435 // Else if authentication works with original rnd/frn ... print
1436 // Else warn user that the tag is no longer in original state, and print steps to return it to a good state.
1437 (void)last_successful_step
;
1441 static int CmdEM4x70Calc_ParseArgs(const char *Cmd
, em4x70_cmd_input_calculate_t
*out_results
) {
1443 memset(out_results
, 0, sizeof(em4x70_cmd_input_calculate_t
));
1445 int result
= PM3_SUCCESS
;
1447 CLIParserContext
*ctx
;
1451 "Calculates both the reader and tag challenge for a user-provided key and rnd.\n"
1453 "lf em 4x70 calc --key F32AA98CF5BE4ADFA6D3480B --rnd 45F54ADA252AAC (pm3 test key)\n" // --frn 4866BB70 --grn 9BD180
1454 "lf em 4x70 calc --key A090A0A02080000000000000 --rnd 3FFE1FB6CC513F (research paper key)\n" // --frn F355F1A0 --grn 609D60
1455 "lf em 4x70 calc --key 022A028C02BE000102030405 --rnd 7D5167003571F8 (autorecovery test key)\n" // --frn 982DBCC0 --grn 36C0E0
1457 void *argtable
[] = {
1459 arg_str1(NULL
, "key", "<hex>", "Key 96-bit as 12 hex bytes"),
1460 arg_str1(NULL
, "rnd", "<hex>", "56-bit random value sent to tag for authentication"),
1463 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1464 int key_len
= 0; // must be 12 bytes hex data
1465 int rnd_len
= 0; // must be 7 bytes hex data
1467 // These macros hide early function return on error ... including free'ing ctx.
1468 CLIGetHexWithReturn(ctx
, 1, out_results
->key
.k
, &key_len
);
1469 CLIGetHexWithReturn(ctx
, 2, out_results
->rn
.rn
, &rnd_len
);
1472 if (key_len
!= 12) {
1473 PrintAndLogEx(FAILED
, "Key length must be 12 bytes, got %d", key_len
);
1474 result
= PM3_EINVARG
;
1478 PrintAndLogEx(FAILED
, "Random number length must be 7 bytes, got %d", rnd_len
);
1479 result
= PM3_EINVARG
;
1484 static int CmdEM4x70Calc(const char *Cmd
) {
1485 em4x70_cmd_input_calculate_t opts
= {0};
1486 em4x70_cmd_output_calculate_t data
= {0};
1488 // 0. Parse the command line
1489 int result
= CmdEM4x70Calc_ParseArgs(Cmd
, &opts
);
1490 if (PM3_SUCCESS
!= result
) {
1494 // There are no failure paths. All inputs are valid, and ID48LIB doesn't add any failure paths.
1495 id48lib_generator(&opts
.key
, &opts
.rn
, &data
.frn
, &data
.grn
);
1497 char key_string
[24 + 1] = {0};
1498 char rnd_string
[14 + 1] = {0};
1499 char frn_string
[ 8 + 1] = {0};
1500 char grn_string
[ 6 + 1] = {0};
1504 "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
1505 opts
.key
.k
[ 0], opts
.key
.k
[ 1], opts
.key
.k
[ 2], opts
.key
.k
[ 3],
1506 opts
.key
.k
[ 4], opts
.key
.k
[ 5], opts
.key
.k
[ 6], opts
.key
.k
[ 7],
1507 opts
.key
.k
[ 8], opts
.key
.k
[ 9], opts
.key
.k
[10], opts
.key
.k
[11]
1511 "%02X%02X%02X%02X%02X%02X%02X",
1512 opts
.rn
.rn
[0], opts
.rn
.rn
[1], opts
.rn
.rn
[2], opts
.rn
.rn
[3], opts
.rn
.rn
[4], opts
.rn
.rn
[5], opts
.rn
.rn
[6]
1517 data
.frn
.frn
[0], data
.frn
.frn
[1], data
.frn
.frn
[2], data
.frn
.frn
[3]
1522 data
.grn
.grn
[0], data
.grn
.grn
[1], data
.grn
.grn
[2]
1525 PrintAndLogEx(SUCCESS
, "KEY: %s RND: %s FRN: " _GREEN_("%s") " GRN: " _GREEN_("%s"), key_string
, rnd_string
, frn_string
, grn_string
);
1529 // Must be declared to be used in the table,
1530 // but cannot be defined yet because it uses the table.
1531 static int CmdHelp(const char *Cmd
);
1533 static command_t CommandTable
[] = {
1534 {"help", CmdHelp
, AlwaysAvailable
, "This help"},
1535 {"brute", CmdEM4x70Brute
, IfPm3EM4x70
, "Bruteforce EM4X70 to find partial key"},
1536 {"info", CmdEM4x70Info
, IfPm3EM4x70
, "Tag information EM4x70"},
1537 {"write", CmdEM4x70Write
, IfPm3EM4x70
, "Write EM4x70"},
1538 {"unlock", CmdEM4x70Unlock
, IfPm3EM4x70
, "Unlock EM4x70 for writing"},
1539 {"auth", CmdEM4x70Auth
, IfPm3EM4x70
, "Authenticate EM4x70"},
1540 {"setpin", CmdEM4x70SetPIN
, IfPm3EM4x70
, "Write PIN"},
1541 {"setkey", CmdEM4x70SetKey
, IfPm3EM4x70
, "Write key"},
1542 {"calc", CmdEM4x70Calc
, AlwaysAvailable
, "Calculate EM4x70 challenge and response"},
1543 {"recover", CmdEM4x70Recover
, AlwaysAvailable
, "Recover remaining key from partial key"},
1544 {"autorecover", CmdEM4x70AutoRecover
, IfPm3EM4x70
, "Recover entire key from writable tag"},
1545 {NULL
, NULL
, NULL
, NULL
}
1548 static int CmdHelp(const char *Cmd
) {
1549 (void)Cmd
; // Cmd is not used so far
1550 CmdsHelp(CommandTable
);
1554 ///////////////////////////////////////////////////////////////////////////////
1555 // Only two functions need to be non-static:
1557 // * detect_4x70_block()
1558 int CmdLFEM4X70(const char *Cmd
) {
1559 clearCommandBuffer();
1560 return CmdsParse(CommandTable
, Cmd
);
1563 // used by `lf search` and `search`, this is a quick test for EM4x70 tag
1564 // In alignment with other tags implementations, this also dumps basic information
1565 // about the tag, if one is found.
1566 // Use helper function `get_em4x70_info()` if wanting to limit / avoid output.
1567 bool detect_4x70_block(void) {
1568 em4x70_tag_info_t info
;
1569 em4x70_cmd_input_info_t opts
= { 0 };
1571 int result
= get_em4x70_info(&opts
, &info
);
1573 if (result
== PM3_ETIMEOUT
) { // consider removing this output?
1574 PrintAndLogEx(WARNING
, "Timeout while waiting for reply.");
1576 return result
== PM3_SUCCESS
;