1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2018 Merlok
3 // Copyright (C) 2018 drHatson
5 // This code is licensed to you under the terms of the GNU GPL, version 2 or,
6 // at your option, any later version. See the LICENSE.txt file for the text of
8 //-----------------------------------------------------------------------------
9 // High frequency MIFARE Plus commands
10 //-----------------------------------------------------------------------------
14 #include "cmdparser.h" // command_t
15 #include "commonutil.h" // ARRAYLEN
20 #include "mifare/mifare4.h"
21 #include "mifare/mad.h"
23 #include "cliparser.h"
24 #include "mifare/mifaredefault.h"
25 #include "util_posix.h"
26 #include "fileutils.h"
27 #include "protocols.h"
28 #include "crypto/libpcrypto.h"
30 static const uint8_t DefaultKey
[16] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
31 uint16_t CardAddresses
[] = {0x9000, 0x9001, 0x9002, 0x9003, 0x9004, 0xA000, 0xA001, 0xA080, 0xA081, 0xC000, 0xC001};
43 static int CmdHelp(const char *Cmd
);
46 The 7 MSBits (= n) code the storage size itself based on 2^n,
47 the LSBit is set to '0' if the size is exactly 2^n
48 and set to '1' if the storage size is between 2^n and 2^(n+1).
49 For this version of DESFire the 7 MSBits are set to 0x0C (2^12 = 4096) and the LSBit is '0'.
51 static char *getCardSizeStr(uint8_t fsize
) {
53 static char buf
[40] = {0x00};
56 uint16_t usize
= 1 << ((fsize
>> 1) + 1);
57 uint16_t lsize
= 1 << (fsize
>> 1);
61 snprintf(retStr
, sizeof(buf
), "0x%02X (" _GREEN_("%d - %d bytes") ")", fsize
, usize
, lsize
);
63 snprintf(retStr
, sizeof(buf
), "0x%02X (" _GREEN_("%d bytes") ")", fsize
, lsize
);
67 static char *getProtocolStr(uint8_t id
, bool hw
) {
69 static char buf
[50] = {0x00};
73 snprintf(retStr
, sizeof(buf
), "0x%02X (" _YELLOW_("ISO 14443-3 MIFARE, 14443-4") ")", id
);
74 } else if (id
== 0x05) {
76 snprintf(retStr
, sizeof(buf
), "0x%02X (" _YELLOW_("ISO 14443-2, 14443-3") ")", id
);
78 snprintf(retStr
, sizeof(buf
), "0x%02X (" _YELLOW_("ISO 14443-3, 14443-4") ")", id
);
80 snprintf(retStr
, sizeof(buf
), "0x%02X (" _YELLOW_("Unknown") ")", id
);
85 static char *getVersionStr(uint8_t major
, uint8_t minor
) {
87 static char buf
[40] = {0x00};
91 snprintf(retStr
, sizeof(buf
), "%x.%x (" _GREEN_("DESFire MF3ICD40") ")", major
, minor
);
92 else if (major
== 0x01 && minor
== 0x00)
93 snprintf(retStr
, sizeof(buf
), "%x.%x (" _GREEN_("DESFire EV1") ")", major
, minor
);
94 else if (major
== 0x12 && minor
== 0x00)
95 snprintf(retStr
, sizeof(buf
), "%x.%x (" _GREEN_("DESFire EV2") ")", major
, minor
);
96 else if (major
== 0x33 && minor
== 0x00)
97 snprintf(retStr
, sizeof(buf
), "%x.%x (" _GREEN_("DESFire EV3") ")", major
, minor
);
98 else if (major
== 0x30 && minor
== 0x00)
99 snprintf(retStr
, sizeof(buf
), "%x.%x (" _GREEN_("DESFire Light") ")", major
, minor
);
100 else if (major
== 0x11 && minor
== 0x00)
101 snprintf(retStr
, sizeof(buf
), "%x.%x (" _GREEN_("Plus EV1") ")", major
, minor
);
103 snprintf(retStr
, sizeof(buf
), "%x.%x (" _YELLOW_("Unknown") ")", major
, minor
);
107 static char *getTypeStr(uint8_t type
) {
109 static char buf
[40] = {0x00};
114 snprintf(retStr
, sizeof(buf
), "0x%02X (" _YELLOW_("DESFire") ")", type
);
117 snprintf(retStr
, sizeof(buf
), "0x%02X (" _YELLOW_("Plus") ")", type
);
120 snprintf(retStr
, sizeof(buf
), "0x%02X (" _YELLOW_("Ultralight") ")", type
);
123 snprintf(retStr
, sizeof(buf
), "0x%02X (" _YELLOW_("NTAG") ")", type
);
131 static nxp_cardtype_t
getCardType(uint8_t major
, uint8_t minor
) {
134 if (major
== 0x00 && minor
== 0x00)
135 return DESFIRE_MF3ICD40
;
138 if (major
== 0x01 && minor
== 0x00)
142 if (major
== 0x12 && minor
== 0x00)
146 if (major
== 0x33 && minor
== 0x00)
150 if (major
== 0x30 && minor
== 0x00)
151 return DESFIRE_LIGHT
;
154 if (major
== 0x11 && minor
== 0x00)
161 static int plus_print_signature(uint8_t *uid
, uint8_t uidlen
, uint8_t *signature
, int signature_len
) {
163 // ref: MIFARE Plus EV1 Originality Signature Validation
164 #define PUBLIC_PLUS_ECDA_KEYLEN 57
165 const ecdsa_publickey_t nxp_plus_public_keys
[] = {
166 {"MIFARE Plus EV1", "044409ADC42F91A8394066BA83D872FB1D16803734E911170412DDF8BAD1A4DADFD0416291AFE1C748253925DA39A5F39A1C557FFACD34C62E"},
167 {"MIFARE Pluc Ev_x", "04BB49AE4447E6B1B6D21C098C1538B594A11A4A1DBF3D5E673DEACDEB3CC512D1C08AFA1A2768CE20A200BACD2DC7804CD7523A0131ABF607"}
171 bool is_valid
= false;
173 for (i
= 0; i
< ARRAYLEN(nxp_plus_public_keys
); i
++) {
176 uint8_t key
[PUBLIC_PLUS_ECDA_KEYLEN
];
177 param_gethex_to_eol(nxp_plus_public_keys
[i
].value
, 0, key
, PUBLIC_PLUS_ECDA_KEYLEN
, &dl
);
179 int res
= ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP224R1
, key
, uid
, uidlen
, signature
, signature_len
, false);
180 is_valid
= (res
== 0);
185 PrintAndLogEx(NORMAL
, "");
186 PrintAndLogEx(INFO
, "--- " _CYAN_("Tag Signature"));
188 if (is_valid
== false || i
== ARRAYLEN(nxp_plus_public_keys
)) {
189 PrintAndLogEx(INFO
, " Elliptic curve parameters: NID_secp224r1");
190 PrintAndLogEx(INFO
, " TAG IC Signature: %s", sprint_hex_inrow(signature
, 16));
191 PrintAndLogEx(INFO
, " : %s", sprint_hex_inrow(signature
+ 16, 16));
192 PrintAndLogEx(INFO
, " : %s", sprint_hex_inrow(signature
+ 32, 16));
193 PrintAndLogEx(INFO
, " : %s", sprint_hex_inrow(signature
+ 48, signature_len
- 48));
194 PrintAndLogEx(SUCCESS
, " Signature verification: " _RED_("failed"));
198 PrintAndLogEx(INFO
, " IC signature public key name: " _GREEN_("%s"), nxp_plus_public_keys
[i
].desc
);
199 PrintAndLogEx(INFO
, "IC signature public key value: %.32s", nxp_plus_public_keys
[i
].value
);
200 PrintAndLogEx(INFO
, " : %.32s", nxp_plus_public_keys
[i
].value
+ 32);
201 PrintAndLogEx(INFO
, " : %.32s", nxp_plus_public_keys
[i
].value
+ 64);
202 PrintAndLogEx(INFO
, " : %.32s", nxp_plus_public_keys
[i
].value
+ 96);
203 PrintAndLogEx(INFO
, " Elliptic curve parameters: NID_secp224r1");
204 PrintAndLogEx(INFO
, " TAG IC Signature: %s", sprint_hex_inrow(signature
, 16));
205 PrintAndLogEx(INFO
, " : %s", sprint_hex_inrow(signature
+ 16, 16));
206 PrintAndLogEx(INFO
, " : %s", sprint_hex_inrow(signature
+ 32, 16));
207 PrintAndLogEx(INFO
, " : %s", sprint_hex_inrow(signature
+ 48, signature_len
- 48));
208 PrintAndLogEx(SUCCESS
, " Signature verification: " _GREEN_("successful"));
212 static int get_plus_signature(uint8_t *signature
, int *signature_len
) {
214 mfpSetVerboseMode(false);
216 uint8_t data
[59] = {0};
217 int resplen
= 0, retval
= PM3_SUCCESS
;
218 MFPGetSignature(true, false, data
, sizeof(data
), &resplen
);
221 memcpy(signature
, data
+ 1, 56);
227 mfpSetVerboseMode(false);
231 static int plus_print_version(uint8_t *version
) {
232 PrintAndLogEx(SUCCESS
, " UID: " _GREEN_("%s"), sprint_hex(version
+ 14, 7));
233 PrintAndLogEx(SUCCESS
, " Batch number: " _GREEN_("%s"), sprint_hex(version
+ 21, 5));
234 PrintAndLogEx(SUCCESS
, " Production date: week " _GREEN_("%02x") " / " _GREEN_("20%02x"), version
[7 + 7 + 7 + 5], version
[7 + 7 + 7 + 5 + 1]);
235 PrintAndLogEx(NORMAL
, "");
236 PrintAndLogEx(INFO
, "--- " _CYAN_("Hardware Information"));
237 PrintAndLogEx(INFO
, " Raw : %s", sprint_hex(version
, 7));
238 PrintAndLogEx(INFO
, " Vendor Id: " _YELLOW_("%s"), getTagInfo(version
[0]));
239 PrintAndLogEx(INFO
, " Type: %s", getTypeStr(version
[1]));
240 PrintAndLogEx(INFO
, " Subtype: " _YELLOW_("0x%02X"), version
[2]);
241 PrintAndLogEx(INFO
, " Version: %s", getVersionStr(version
[3], version
[4]));
242 PrintAndLogEx(INFO
, " Storage size: %s", getCardSizeStr(version
[5]));
243 PrintAndLogEx(INFO
, " Protocol: %s", getProtocolStr(version
[6], true));
244 PrintAndLogEx(NORMAL
, "");
245 PrintAndLogEx(INFO
, "--- " _CYAN_("Software Information"));
246 PrintAndLogEx(INFO
, " Raw : %s", sprint_hex(version
+ 7, 6));
247 PrintAndLogEx(INFO
, " Vendor Id: " _YELLOW_("%s"), getTagInfo(version
[7]));
248 PrintAndLogEx(INFO
, " Type: %s", getTypeStr(version
[8]));
249 PrintAndLogEx(INFO
, " Subtype: " _YELLOW_("0x%02X"), version
[9]);
250 PrintAndLogEx(INFO
, " Version: " _YELLOW_("%d.%d"), version
[10], version
[11]);
251 PrintAndLogEx(INFO
, " Storage size: %s", getCardSizeStr(version
[12]));
252 PrintAndLogEx(INFO
, " Protocol: %s", getProtocolStr(version
[13], false));
255 static int get_plus_version(uint8_t *version
, int *version_len
) {
257 int resplen
= 0, retval
= PM3_SUCCESS
;
258 mfpSetVerboseMode(false);
259 MFPGetVersion(true, false, version
, *version_len
, &resplen
);
260 mfpSetVerboseMode(false);
262 *version_len
= resplen
;
269 static int CmdHFMFPInfo(const char *Cmd
) {
270 CLIParserContext
*ctx
;
271 CLIParserInit(&ctx
, "hf mfp info",
272 "Get info from MIFARE Plus tags",
279 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
281 PrintAndLogEx(NORMAL
, "");
282 PrintAndLogEx(INFO
, "--- " _CYAN_("Tag Information") " ---------------------------");
283 PrintAndLogEx(INFO
, "-------------------------------------------------------------");
286 SendCommandMIX(CMD_HF_ISO14443A_READER
, ISO14A_CONNECT
, 0, 0, NULL
, 0);
287 PacketResponseNG resp
;
288 WaitForResponse(CMD_ACK
, &resp
);
290 iso14a_card_select_t card
;
291 memcpy(&card
, (iso14a_card_select_t
*)resp
.data
.asBytes
, sizeof(iso14a_card_select_t
));
293 uint64_t select_status
= resp
.oldarg
[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision
296 bool supportVersion
= false;
297 bool supportSignature
= false;
300 uint8_t version
[30] = {0};
301 int version_len
= sizeof(version
);
302 if (get_plus_version(version
, &version_len
) == PM3_SUCCESS
) {
303 plus_print_version(version
);
304 supportVersion
= true;
306 // info about 14a part
307 infoHF14A(false, false, false);
314 // Signature originality check
315 uint8_t signature
[56] = {0};
316 int signature_len
= sizeof(signature
);
317 if (get_plus_signature(signature
, &signature_len
) == PM3_SUCCESS
) {
318 plus_print_signature(card
.uid
, card
.uidlen
, signature
, signature_len
);
319 supportSignature
= true;
322 if (select_status
== 1 || select_status
== 2) {
324 PrintAndLogEx(INFO
, "--- " _CYAN_("Fingerprint"));
328 if (supportVersion
) {
330 int cardtype
= getCardType(version
[3], version
[4]);
333 if (supportSignature
) {
334 PrintAndLogEx(INFO
, " Tech: " _GREEN_("MIFARE Plus EV1"));
336 PrintAndLogEx(INFO
, " Tech: " _YELLOW_("MIFARE Plus SE/X"));
344 // MIFARE Type Identification Procedure
345 // https://www.nxp.com/docs/en/application-note/AN10833.pdf
346 uint16_t ATQA
= card
.atqa
[0] + (card
.atqa
[1] << 8);
349 PrintAndLogEx(INFO
, " SIZE: " _GREEN_("2K") " (%s UID)", (ATQA
& 0x0040) ? "7" : "4");
353 PrintAndLogEx(INFO
, " SIZE: " _GREEN_("4K") " (%s UID)", (ATQA
& 0x0040) ? "7" : "4");
357 uint8_t SLmode
= 0xFF;
359 if (card
.sak
== 0x08) {
360 PrintAndLogEx(INFO
, " SAK: " _GREEN_("2K 7b UID"));
361 if (select_status
== 2) SLmode
= 1;
363 if (card
.sak
== 0x18) {
364 PrintAndLogEx(INFO
, " SAK: " _GREEN_("4K 7b UID"));
365 if (select_status
== 2) SLmode
= 1;
367 if (card
.sak
== 0x10) {
368 PrintAndLogEx(INFO
, " SAK: " _GREEN_("2K"));
369 if (select_status
== 2) SLmode
= 2;
371 if (card
.sak
== 0x11) {
372 PrintAndLogEx(INFO
, " SAK: " _GREEN_("4K"));
373 if (select_status
== 2) SLmode
= 2;
377 if (card
.sak
== 0x20) {
378 if (card
.ats_len
> 0) {
379 PrintAndLogEx(INFO
, " SAK: " _GREEN_("MIFARE Plus SL0/SL3") " or " _GREEN_("MIFARE DESFire"));
382 uint8_t data
[250] = {0};
384 // https://github.com/Proxmark/proxmark3/blob/master/client/luascripts/mifarePlus.lua#L161
385 uint8_t cmd
[3 + 16] = {0xa8, 0x90, 0x90, 0x00};
386 int res
= ExchangeRAW14a(cmd
, sizeof(cmd
), true, false, data
, sizeof(data
), &datalen
, false);
388 // DESFire answers 0x1C or 67 00
389 // Plus answers 0x0B, 0x09, 0x06
390 // Which tag answers 6D 00 ??
391 if (data
[0] != 0x0b && data
[0] != 0x09 && data
[0] != 0x1C && data
[0] != 0x67 && data
[0] != 0x6d) {
392 PrintAndLogEx(INFO
, _RED_("Send copy to iceman of this command output!"));
393 PrintAndLogEx(INFO
, "data: %s", sprint_hex(data
, datalen
));
396 if ((memcmp(data
, "\x67\x00", 2) == 0) ||
397 (memcmp(data
, "\x1C\x83\x0C", 3) == 0)
399 PrintAndLogEx(INFO
, " result: " _RED_("MIFARE DESFire"));
400 PrintAndLogEx(HINT
, "Hint: Try " _YELLOW_("`hf mfdes info`"));
403 } else if (memcmp(data
, "\x6D\x00", 2) == 0) {
406 PrintAndLogEx(INFO
, " result: " _GREEN_("MIFARE Plus SL0/SL3"));
409 if (!res
&& datalen
> 1 && data
[0] == 0x09) {
416 // How do we detect SL0 / SL1 / SL2 / SL3 modes?!?
417 PrintAndLogEx(INFO
, "--- " _CYAN_("Security Level (SL)"));
420 PrintAndLogEx(SUCCESS
, " SL mode: " _YELLOW_("SL%d"), SLmode
);
422 PrintAndLogEx(WARNING
, " SL mode: " _YELLOW_("unknown"));
425 PrintAndLogEx(INFO
, " SL 0: initial delivery configuration, used for card personalization");
428 PrintAndLogEx(INFO
, " SL 1: backwards functional compatibility mode (with MIFARE Classic 1K / 4K) with an optional AES authentication");
431 PrintAndLogEx(INFO
, " SL 2: 3-Pass Authentication based on AES followed by MIFARE CRYPTO1 authentication, communication secured by MIFARE CRYPTO1");
434 PrintAndLogEx(INFO
, " SL 3: 3-Pass authentication based on AES, data manipulation commands secured by AES encryption and an AES based MACing method.");
441 PrintAndLogEx(INFO
, "\tMifare Plus info not available.");
443 PrintAndLogEx(NORMAL
, "");
448 static int CmdHFMFPWritePerso(const char *Cmd
) {
449 CLIParserContext
*ctx
;
450 CLIParserInit(&ctx
, "hf mfp wrp",
451 "Executes Write Perso command. Can be used in SL0 mode only.",
452 "hf mfp wrp --ki 4000 --key 000102030405060708090a0b0c0d0e0f -> write key (00..0f) to key number 4000 \n"
453 "hf mfp wrp --ki 4000 -> write default key(0xff..0xff) to key number 4000");
457 arg_lit0("v", "verbose", "show internal data."),
458 arg_str1("i", "ki", "<hex>", " key number, 2 hex bytes"),
459 arg_strx0(NULL
, "key", "<hex>", " key, 16 hex bytes"),
462 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
464 bool verbose
= arg_get_lit(ctx
, 1);
466 uint8_t keyNum
[64] = {0};
468 CLIGetHexWithReturn(ctx
, 2, keyNum
, &keyNumLen
);
470 uint8_t key
[64] = {0};
472 CLIGetHexWithReturn(ctx
, 3, key
, &keyLen
);
475 mfpSetVerboseMode(verbose
);
478 memmove(key
, DefaultKey
, 16);
482 if (keyNumLen
!= 2) {
483 PrintAndLogEx(ERR
, "Key number length must be 2 bytes instead of: %d", keyNumLen
);
487 PrintAndLogEx(ERR
, "Key length must be 16 bytes instead of: %d", keyLen
);
491 uint8_t data
[250] = {0};
494 int res
= MFPWritePerso(keyNum
, key
, true, false, data
, sizeof(data
), &datalen
);
496 PrintAndLogEx(ERR
, "Exchange error: %d", res
);
501 PrintAndLogEx(ERR
, "Command must return 3 bytes instead of: %d", datalen
);
505 if (data
[0] != 0x90) {
506 PrintAndLogEx(ERR
, "Command error: %02x %s", data
[0], mfpGetErrorDescription(data
[0]));
509 PrintAndLogEx(INFO
, "Write (" _GREEN_("ok") " )");
514 static int CmdHFMFPInitPerso(const char *Cmd
) {
515 CLIParserContext
*ctx
;
516 CLIParserInit(&ctx
, "hf mfp initp",
517 "Executes Write Perso command for all card's keys. Can be used in SL0 mode only.",
518 "hf mfp initp --key 000102030405060708090a0b0c0d0e0f -> fill all the keys with key (00..0f)\n"
519 "hf mfp initp -vv -> fill all the keys with default key(0xff..0xff) and show all the data exchange");
523 arg_litn("v", "verbose", 0, 2, "show internal data."),
524 arg_strx0("k", "key", "<hex>", "key, 16 hex bytes"),
527 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
529 bool verbose
= arg_get_lit(ctx
, 1);
530 bool verbose2
= arg_get_lit(ctx
, 1) > 1;
532 uint8_t key
[256] = {0};
534 CLIGetHexWithReturn(ctx
, 2, key
, &keyLen
);
537 if (keyLen
&& keyLen
!= 16) {
538 PrintAndLogEx(ERR
, "Key length must be 16 bytes instead of: %d", keyLen
);
543 memmove(key
, DefaultKey
, 16);
545 uint8_t keyNum
[2] = {0};
546 uint8_t data
[250] = {0};
550 mfpSetVerboseMode(verbose2
);
551 for (uint16_t sn
= 0x4000; sn
< 0x4050; sn
++) {
553 keyNum
[1] = sn
& 0xff;
554 res
= MFPWritePerso(keyNum
, key
, (sn
== 0x4000), true, data
, sizeof(data
), &datalen
);
555 if (!res
&& (datalen
== 3) && data
[0] == 0x09) {
556 PrintAndLogEx(INFO
, "2K card detected.");
559 if (res
|| (datalen
!= 3) || data
[0] != 0x90) {
560 PrintAndLogEx(ERR
, "Write error on address %04x", sn
);
565 mfpSetVerboseMode(verbose
);
566 for (int i
= 0; i
< ARRAYLEN(CardAddresses
); i
++) {
567 keyNum
[0] = CardAddresses
[i
] >> 8;
568 keyNum
[1] = CardAddresses
[i
] & 0xff;
569 res
= MFPWritePerso(keyNum
, key
, false, true, data
, sizeof(data
), &datalen
);
570 if (!res
&& (datalen
== 3) && data
[0] == 0x09) {
571 PrintAndLogEx(WARNING
, "Skipped[%04x]...", CardAddresses
[i
]);
573 if (res
|| (datalen
!= 3) || data
[0] != 0x90) {
574 PrintAndLogEx(ERR
, "Write error on address %04x", CardAddresses
[i
]);
584 PrintAndLogEx(INFO
, "Done!");
588 static int CmdHFMFPCommitPerso(const char *Cmd
) {
589 CLIParserContext
*ctx
;
590 CLIParserInit(&ctx
, "hf mfp commitp",
591 "Executes Commit Perso command. Can be used in SL0 mode only.",
593 // "hf mfp commitp --sl 1"
598 arg_lit0("v", "verbose", "show internal data."),
599 // arg_int0(NULL, "sl", "<dec>", "SL mode"),
602 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
603 bool verbose
= arg_get_lit(ctx
, 1);
604 // int slmode = arg_get_int(ctx, 2);
607 mfpSetVerboseMode(verbose
);
609 uint8_t data
[250] = {0};
612 int res
= MFPCommitPerso(true, false, data
, sizeof(data
), &datalen
);
614 PrintAndLogEx(ERR
, "Exchange error: %d", res
);
619 PrintAndLogEx(ERR
, "Command must return 3 bytes instead of: %d", datalen
);
623 if (data
[0] != 0x90) {
624 PrintAndLogEx(ERR
, "Command error: %02x %s", data
[0], mfpGetErrorDescription(data
[0]));
627 PrintAndLogEx(INFO
, "Switch level ( " _GREEN_("ok") " )");
631 static int CmdHFMFPAuth(const char *Cmd
) {
632 uint8_t keyn
[250] = {0};
634 uint8_t key
[250] = {0};
637 CLIParserContext
*ctx
;
638 CLIParserInit(&ctx
, "hf mfp auth",
639 "Executes AES authentication command for Mifare Plus card",
640 "hf mfp auth --ki 4000 --key 000102030405060708090a0b0c0d0e0f -> executes authentication\n"
641 "hf mfp auth --ki 9003 --key FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -v -> executes authentication and shows all the system data");
645 arg_lit0("v", "verbose", "show internal data."),
646 arg_str1(NULL
, "ki", "<hex>", "key number, 2 hex bytes"),
647 arg_str1(NULL
, "key", "<hex>", "key, 16 hex bytes"),
650 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
652 bool verbose
= arg_get_lit(ctx
, 1);
653 CLIGetHexWithReturn(ctx
, 2, keyn
, &keynlen
);
654 CLIGetHexWithReturn(ctx
, 3, key
, &keylen
);
658 PrintAndLogEx(ERR
, "ERROR: <key number> must be 2 bytes long instead of: %d", keynlen
);
663 PrintAndLogEx(ERR
, "ERROR: <key> must be 16 bytes long instead of: %d", keylen
);
667 return MifareAuth4(NULL
, keyn
, key
, true, false, true, verbose
, false);
670 static int CmdHFMFPRdbl(const char *Cmd
) {
671 CLIParserContext
*ctx
;
672 CLIParserInit(&ctx
, "hf mfp rdbl",
673 "Reads several blocks from Mifare Plus card",
674 "hf mfp rdbl --blk 0 --key 000102030405060708090a0b0c0d0e0f -> executes authentication and read block 0 data\n"
675 "hf mfp rdbl --blk 1 -v -> executes authentication and shows sector 1 data with default key 0xFF..0xFF");
679 arg_lit0("v", "verbose", "show internal data"),
680 arg_int0("n", "count", "<dec>", "blocks count (by default 1)"),
681 arg_lit0("b", "keyb", "use key B (by default keyA)"),
682 arg_lit0("p", "plain", "plain communication mode between reader and card"),
683 arg_int1(NULL
, "blk", "<dec>", "block number (0..255)"),
684 arg_str0(NULL
, "key", "<hex>", "Key, 16 hex bytes"),
687 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
689 bool verbose
= arg_get_lit(ctx
, 1);
690 int blocksCount
= arg_get_int_def(ctx
, 2, 1);
691 bool keyB
= arg_get_lit(ctx
, 3);
692 int plain
= arg_get_lit(ctx
, 4);
693 uint32_t blockn
= arg_get_int(ctx
, 5);
695 uint8_t keyn
[2] = {0};
696 uint8_t key
[250] = {0};
698 CLIGetHexWithReturn(ctx
, 6, key
, &keylen
);
701 mfpSetVerboseMode(verbose
);
704 memmove(key
, DefaultKey
, 16);
709 PrintAndLogEx(ERR
, "<block number> must be in range [0..255] instead of: %d", blockn
);
714 PrintAndLogEx(ERR
, "<key> must be 16 bytes long instead of: %d", keylen
);
718 // 3 blocks - wo iso14443-4 chaining
719 if (blocksCount
> 3) {
720 PrintAndLogEx(ERR
, "blocks count must be less than 3 instead of: %d", blocksCount
);
724 if (blocksCount
> 1 && mfIsSectorTrailer(blockn
)) {
725 PrintAndLogEx(WARNING
, "WARNING: trailer!");
728 uint8_t sectorNum
= mfSectorNum(blockn
& 0xff);
729 uint16_t uKeyNum
= 0x4000 + sectorNum
* 2 + (keyB
? 1 : 0);
730 keyn
[0] = uKeyNum
>> 8;
731 keyn
[1] = uKeyNum
& 0xff;
733 PrintAndLogEx(INFO
, "--block:%d sector[%d]:%02x key:%04x", blockn
, mfNumBlocksPerSector(sectorNum
), sectorNum
, uKeyNum
);
735 mf4Session_t mf4session
;
736 int res
= MifareAuth4(&mf4session
, keyn
, key
, true, true, true, verbose
, false);
738 PrintAndLogEx(ERR
, "Authentication error: %d", res
);
742 uint8_t data
[250] = {0};
744 uint8_t mac
[8] = {0};
745 res
= MFPReadBlock(&mf4session
, plain
, blockn
& 0xff, blocksCount
, false, false, data
, sizeof(data
), &datalen
, mac
);
747 PrintAndLogEx(ERR
, "Read error: %d", res
);
751 if (datalen
&& data
[0] != 0x90) {
752 PrintAndLogEx(ERR
, "Card read error: %02x %s", data
[0], mfpGetErrorDescription(data
[0]));
756 if (datalen
!= 1 + blocksCount
* 16 + 8 + 2) {
757 PrintAndLogEx(ERR
, "Error return length:%d", datalen
);
762 for (int i
= 0; i
< blocksCount
; i
++) {
763 PrintAndLogEx(INFO
, "data[%03d]: %s", indx
, sprint_hex(&data
[1 + i
* 16], 16));
765 if (mfIsSectorTrailer(indx
) && i
!= blocksCount
- 1) {
766 PrintAndLogEx(INFO
, "data[%03d]: ------------------- trailer -------------------", indx
);
771 if (memcmp(&data
[blocksCount
* 16 + 1], mac
, 8)) {
772 PrintAndLogEx(WARNING
, "WARNING: mac not equal...");
773 PrintAndLogEx(WARNING
, "MAC card: %s", sprint_hex(&data
[blocksCount
* 16 + 1], 8));
774 PrintAndLogEx(WARNING
, "MAC reader: %s", sprint_hex(mac
, 8));
777 PrintAndLogEx(INFO
, "MAC: %s", sprint_hex(&data
[blocksCount
* 16 + 1], 8));
783 static int CmdHFMFPRdsc(const char *Cmd
) {
784 CLIParserContext
*ctx
;
785 CLIParserInit(&ctx
, "hf mfp rdsc",
786 "Reads one sector from Mifare Plus card",
787 "hf mfp rdsc --sn 0 --key 000102030405060708090a0b0c0d0e0f -> executes authentication and read sector 0 data\n"
788 "hf mfp rdsc --sn 1 -v -> executes authentication and shows sector 1 data with default key");
792 arg_lit0("v", "verbose", "show internal data."),
793 arg_lit0("b", "keyb", "use key B (by default keyA)."),
794 arg_lit0("p", "plain", "plain communication mode between reader and card."),
795 arg_int1(NULL
, "sn", "<dec>", "sector number (0..255)"),
796 arg_str0("k", "key", "<hex>", "key, 16 hex bytes"),
799 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
801 bool verbose
= arg_get_lit(ctx
, 1);
802 bool keyB
= arg_get_lit(ctx
, 2);
803 bool plain
= arg_get_lit(ctx
, 3);
804 uint32_t sectorNum
= arg_get_int(ctx
, 4);
805 uint8_t keyn
[2] = {0};
806 uint8_t key
[250] = {0};
808 CLIGetHexWithReturn(ctx
, 5, key
, &keylen
);
811 mfpSetVerboseMode(verbose
);
814 memmove(key
, DefaultKey
, 16);
818 if (sectorNum
> 39) {
819 PrintAndLogEx(ERR
, "<sector number> must be in range [0..39] instead of: %d", sectorNum
);
824 PrintAndLogEx(ERR
, "<key> must be 16 bytes long instead of: %d", keylen
);
828 uint16_t uKeyNum
= 0x4000 + sectorNum
* 2 + (keyB
? 1 : 0);
829 keyn
[0] = uKeyNum
>> 8;
830 keyn
[1] = uKeyNum
& 0xff;
832 PrintAndLogEx(INFO
, "--sector[%d]:%02x key:%04x", mfNumBlocksPerSector(sectorNum
), sectorNum
, uKeyNum
);
834 mf4Session_t mf4session
;
835 int res
= MifareAuth4(&mf4session
, keyn
, key
, true, true, true, verbose
, false);
837 PrintAndLogEx(ERR
, "Authentication error: %d", res
);
841 uint8_t data
[250] = {0};
843 uint8_t mac
[8] = {0};
844 for (int n
= mfFirstBlockOfSector(sectorNum
); n
< mfFirstBlockOfSector(sectorNum
) + mfNumBlocksPerSector(sectorNum
); n
++) {
845 res
= MFPReadBlock(&mf4session
, plain
, n
& 0xff, 1, false, true, data
, sizeof(data
), &datalen
, mac
);
847 PrintAndLogEx(ERR
, "Read error: %d", res
);
852 if (datalen
&& data
[0] != 0x90) {
853 PrintAndLogEx(ERR
, "Card read error: %02x %s", data
[0], mfpGetErrorDescription(data
[0]));
857 if (datalen
!= 1 + 16 + 8 + 2) {
858 PrintAndLogEx(ERR
, "Error return length:%d", datalen
);
863 PrintAndLogEx(INFO
, "data[%03d]: %s", n
, sprint_hex(&data
[1], 16));
865 if (memcmp(&data
[1 + 16], mac
, 8)) {
866 PrintAndLogEx(WARNING
, "WARNING: mac on block %d not equal...", n
);
867 PrintAndLogEx(WARNING
, "MAC card: %s", sprint_hex(&data
[1 + 16], 8));
868 PrintAndLogEx(WARNING
, "MAC reader: %s", sprint_hex(mac
, 8));
871 PrintAndLogEx(INFO
, "MAC: %s", sprint_hex(&data
[1 + 16], 8));
879 static int CmdHFMFPWrbl(const char *Cmd
) {
880 CLIParserContext
*ctx
;
881 CLIParserInit(&ctx
, "hf mfp wrbl",
882 "Writes one block to Mifare Plus card",
883 "hf mfp wrbl --blk 1 -d ff0000000000000000000000000000ff --key 000102030405060708090a0b0c0d0e0f -> writes block 1 data\n"
884 "hf mfp wrbl --blk 2 -d ff0000000000000000000000000000ff -v -> writes block 2 data with default key 0xFF..0xFF"
889 arg_lit0("v", "verbose", "show internal data."),
890 arg_lit0("b", "keyb", "use key B (by default keyA)."),
891 arg_int1(NULL
, "blk", "<dec>", "block number (0..255)"),
892 arg_str1("d", "data", "<hex>", "data, 16 hex bytes"),
893 arg_str0("k", "key", "<hex>", "key, 16 hex bytes"),
896 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
898 bool verbose
= arg_get_lit(ctx
, 1);
899 bool keyB
= arg_get_lit(ctx
, 2);
900 uint32_t blockNum
= arg_get_int(ctx
, 3);
902 uint8_t datain
[250] = {0};
904 CLIGetHexWithReturn(ctx
, 4, datain
, &datainlen
);
906 uint8_t key
[250] = {0};
908 CLIGetHexWithReturn(ctx
, 5, key
, &keylen
);
911 uint8_t keyn
[2] = {0};
913 mfpSetVerboseMode(verbose
);
916 memmove(key
, DefaultKey
, 16);
920 if (blockNum
> 255) {
921 PrintAndLogEx(ERR
, "<block number> must be in range [0..255] instead of: %d", blockNum
);
926 PrintAndLogEx(ERR
, "<key> must be 16 bytes long instead of: %d", keylen
);
930 if (datainlen
!= 16) {
931 PrintAndLogEx(ERR
, "<data> must be 16 bytes long instead of: %d", datainlen
);
935 uint8_t sectorNum
= mfSectorNum(blockNum
& 0xff);
936 uint16_t uKeyNum
= 0x4000 + sectorNum
* 2 + (keyB
? 1 : 0);
937 keyn
[0] = uKeyNum
>> 8;
938 keyn
[1] = uKeyNum
& 0xff;
940 PrintAndLogEx(INFO
, "--block:%d sector[%d]:%02x key:%04x", blockNum
& 0xff, mfNumBlocksPerSector(sectorNum
), sectorNum
, uKeyNum
);
942 mf4Session_t mf4session
;
943 int res
= MifareAuth4(&mf4session
, keyn
, key
, true, true, true, verbose
, false);
945 PrintAndLogEx(ERR
, "Authentication error: %d", res
);
949 uint8_t data
[250] = {0};
951 uint8_t mac
[8] = {0};
952 res
= MFPWriteBlock(&mf4session
, blockNum
& 0xff, datain
, false, false, data
, sizeof(data
), &datalen
, mac
);
954 PrintAndLogEx(ERR
, "Write error: %d", res
);
959 if (datalen
!= 3 && (datalen
!= 3 + 8)) {
960 PrintAndLogEx(ERR
, "Error return length:%d", datalen
);
965 if (datalen
&& data
[0] != 0x90) {
966 PrintAndLogEx(ERR
, "Card write error: %02x %s", data
[0], mfpGetErrorDescription(data
[0]));
971 if (memcmp(&data
[1], mac
, 8)) {
972 PrintAndLogEx(WARNING
, "WARNING: mac not equal...");
973 PrintAndLogEx(WARNING
, "MAC card: %s", sprint_hex(&data
[1], 8));
974 PrintAndLogEx(WARNING
, "MAC reader: %s", sprint_hex(mac
, 8));
977 PrintAndLogEx(INFO
, "MAC: %s", sprint_hex(&data
[1], 8));
981 PrintAndLogEx(INFO
, "Write ( " _GREEN_("ok") " )");
985 #define AES_KEY_LEN 16
986 #define MAX_KEYS_LIST_LEN 1024
988 static int MFPKeyCheck(uint8_t startSector
, uint8_t endSector
, uint8_t startKeyAB
, uint8_t endKeyAB
,
989 uint8_t keyList
[MAX_KEYS_LIST_LEN
][AES_KEY_LEN
], size_t keyListLen
, uint8_t foundKeys
[2][64][AES_KEY_LEN
+ 1],
992 bool selectCard
= true;
993 uint8_t keyn
[2] = {0};
995 // sector number from 0
996 for (uint8_t sector
= startSector
; sector
<= endSector
; sector
++) {
998 for (uint8_t keyAB
= startKeyAB
; keyAB
<= endKeyAB
; keyAB
++) {
999 // main cycle with key check
1000 for (int i
= 0; i
< keyListLen
; i
++) {
1003 if (verbose
== false)
1004 PrintAndLogEx(NORMAL
, "." NOLF
);
1006 if (kbd_enter_pressed()) {
1007 PrintAndLogEx(WARNING
, "\naborted via keyboard!\n");
1009 return PM3_EOPABORTED
;
1013 uint16_t uKeyNum
= 0x4000 + sector
* 2 + keyAB
;
1014 keyn
[0] = uKeyNum
>> 8;
1015 keyn
[1] = uKeyNum
& 0xff;
1017 for (int retry
= 0; retry
< 4; retry
++) {
1018 res
= MifareAuth4(NULL
, keyn
, keyList
[i
], selectCard
, true, false, false, true);
1023 PrintAndLogEx(WARNING
, "\nretried[%d]...", retry
);
1025 PrintAndLogEx(NORMAL
, "R" NOLF
);
1033 PrintAndLogEx(WARNING
, "\nsector %02d key %d [%s] res: %d", sector
, keyAB
, sprint_hex_inrow(keyList
[i
], 16), res
);
1035 // key for [sector,keyAB] found
1038 PrintAndLogEx(INFO
, "\nFound key for sector %d key %s [%s]", sector
, keyAB
== 0 ? "A" : "B", sprint_hex_inrow(keyList
[i
], 16));
1040 PrintAndLogEx(NORMAL
, "+" NOLF
);
1042 foundKeys
[keyAB
][sector
][0] = 0x01;
1043 memcpy(&foundKeys
[keyAB
][sector
][1], keyList
[i
], AES_KEY_LEN
);
1050 // 5 - auth error (rnd not equal)
1053 PrintAndLogEx(ERR
, "\nExchange error. Aborted.");
1055 PrintAndLogEx(NORMAL
, "E" NOLF
);
1058 return PM3_ECARDEXCHANGE
;
1070 static void Fill2bPattern(uint8_t keyList
[MAX_KEYS_LIST_LEN
][AES_KEY_LEN
], uint32_t *keyListLen
, uint32_t *startPattern
) {
1071 for (uint32_t pt
= *startPattern
; pt
< 0x10000; pt
++) {
1072 keyList
[*keyListLen
][0] = (pt
>> 8) & 0xff;
1073 keyList
[*keyListLen
][1] = pt
& 0xff;
1074 memcpy(&keyList
[*keyListLen
][2], &keyList
[*keyListLen
][0], 2);
1075 memcpy(&keyList
[*keyListLen
][4], &keyList
[*keyListLen
][0], 4);
1076 memcpy(&keyList
[*keyListLen
][8], &keyList
[*keyListLen
][0], 8);
1079 if (*keyListLen
== MAX_KEYS_LIST_LEN
)
1085 static int CmdHFMFPChk(const char *Cmd
) {
1087 uint8_t keyList
[MAX_KEYS_LIST_LEN
][AES_KEY_LEN
] = {{0}};
1088 uint32_t keyListLen
= 0;
1089 uint8_t foundKeys
[2][64][AES_KEY_LEN
+ 1] = {{{0}}};
1091 CLIParserContext
*ctx
;
1092 CLIParserInit(&ctx
, "hf mfp chk",
1093 "Checks keys with Mifare Plus card.",
1094 "hf mfp chk -k 000102030405060708090a0b0c0d0e0f -> check key on sector 0 as key A and B\n"
1095 "hf mfp chk -s 2 -a -> check default key list on sector 2, key A\n"
1096 "hf mfp chk -d mfp_default_keys -s0 -e6 -> check keys from dictionary against sectors 0-6\n"
1097 "hf mfp chk --pattern1b -j keys -> check all 1-byte keys pattern and save found keys to json\n"
1098 "hf mfp chk --pattern2b --startp2b FA00 -> check all 2-byte keys pattern. Start from key FA00FA00...FA00");
1100 void *argtable
[] = {
1102 arg_lit0("a", "keya", "check only key A (by default check all keys)."),
1103 arg_lit0("b", "keyb", "check only key B (by default check all keys)."),
1104 arg_int0("s", "startsec", "Start sector Num (0..255)", NULL
),
1105 arg_int0("e", "endsec", "End sector Num (0..255)", NULL
),
1106 arg_str0("k", "key", "<Key>", "Key for checking (HEX 16 bytes)"),
1107 arg_str0("d", "dict", "<file>", "file with keys dictionary"),
1108 arg_lit0(NULL
, "pattern1b", "check all 1-byte combinations of key (0000...0000, 0101...0101, 0202...0202, ...)"),
1109 arg_lit0(NULL
, "pattern2b", "check all 2-byte combinations of key (0000...0000, 0001...0001, 0002...0002, ...)"),
1110 arg_str0(NULL
, "startp2b", "<Pattern>", "Start key (2-byte HEX) for 2-byte search (use with `--pattern2b`)"),
1111 arg_str0("j", "json", "<file>", "json file to save keys"),
1112 arg_lit0("v", "verbose", "verbose mode."),
1115 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1117 bool keyA
= arg_get_lit(ctx
, 1);
1118 bool keyB
= arg_get_lit(ctx
, 2);
1119 uint8_t startSector
= arg_get_int_def(ctx
, 3, 0);
1120 uint8_t endSector
= arg_get_int_def(ctx
, 4, 0);
1122 uint8_t vkey
[16] = {0};
1124 CLIGetHexWithReturn(ctx
, 5, vkey
, &vkeylen
);
1126 if (vkeylen
== 16) {
1127 memcpy(&keyList
[keyListLen
], vkey
, 16);
1130 PrintAndLogEx(ERR
, "Specified key must have 16 bytes length.");
1136 uint8_t dict_filename
[FILE_PATH_SIZE
+ 2] = {0};
1137 int dict_filenamelen
= 0;
1138 if (CLIParamStrToBuf(arg_get_str(ctx
, 6), dict_filename
, FILE_PATH_SIZE
, &dict_filenamelen
)) {
1139 PrintAndLogEx(FAILED
, "File name too long or invalid.");
1144 bool pattern1b
= arg_get_lit(ctx
, 7);
1145 bool pattern2b
= arg_get_lit(ctx
, 8);
1147 if (pattern1b
&& pattern2b
) {
1148 PrintAndLogEx(ERR
, "Pattern search mode must be 2-byte or 1-byte only.");
1153 if (dict_filenamelen
&& (pattern1b
|| pattern2b
)) {
1154 PrintAndLogEx(ERR
, "Pattern search mode and dictionary mode can't be used in one command.");
1159 uint32_t startPattern
= 0x0000;
1160 uint8_t vpattern
[2];
1161 int vpatternlen
= 0;
1162 CLIGetHexWithReturn(ctx
, 9, vpattern
, &vpatternlen
);
1163 if (vpatternlen
> 0) {
1164 if (vpatternlen
<= 2) {
1165 startPattern
= (vpattern
[0] << 8) + vpattern
[1];
1167 PrintAndLogEx(ERR
, "Pattern must be 2-byte length.");
1172 PrintAndLogEx(WARNING
, "Pattern entered, but search mode not is 2-byte search.");
1175 uint8_t jsonname
[250] = {0};
1176 int jsonnamelen
= 0;
1177 if (CLIParamStrToBuf(arg_get_str(ctx
, 10), jsonname
, sizeof(jsonname
), &jsonnamelen
)) {
1178 PrintAndLogEx(ERR
, "Invalid json name.");
1182 jsonname
[jsonnamelen
] = 0;
1184 bool verbose
= arg_get_lit(ctx
, 11);
1188 uint8_t startKeyAB
= 0;
1189 uint8_t endKeyAB
= 1;
1196 if (endSector
< startSector
)
1197 endSector
= startSector
;
1199 // 1-byte pattern search mode
1201 for (int i
= 0; i
< 0x100; i
++)
1202 memset(keyList
[i
], i
, 16);
1207 // 2-byte pattern search mode
1209 Fill2bPattern(keyList
, &keyListLen
, &startPattern
);
1212 size_t endFilePosition
= 0;
1213 if (dict_filenamelen
) {
1214 uint32_t keycnt
= 0;
1215 res
= loadFileDICTIONARYEx((char *)dict_filename
, keyList
, sizeof(keyList
), NULL
, 16, &keycnt
, 0, &endFilePosition
, true);
1216 keyListLen
= keycnt
;
1217 if (endFilePosition
)
1218 PrintAndLogEx(SUCCESS
, "First part of dictionary successfully loaded.");
1221 if (keyListLen
== 0) {
1222 for (int i
= 0; i
< g_mifare_plus_default_keys_len
; i
++) {
1223 if (hex_to_bytes(g_mifare_plus_default_keys
[i
], keyList
[keyListLen
], 16) != 16)
1230 if (keyListLen
== 0) {
1231 PrintAndLogEx(ERR
, "Key list is empty. Nothing to check.");
1234 PrintAndLogEx(INFO
, "Loaded " _YELLOW_("%"PRIu32
) " keys", keyListLen
);
1237 if (verbose
== false)
1238 PrintAndLogEx(NORMAL
, "Search keys");
1241 res
= MFPKeyCheck(startSector
, endSector
, startKeyAB
, endKeyAB
, keyList
, keyListLen
, foundKeys
, verbose
);
1242 if (res
== PM3_EOPABORTED
)
1244 if (pattern2b
&& startPattern
< 0x10000) {
1245 if (verbose
== false)
1246 PrintAndLogEx(NORMAL
, "p" NOLF
);
1249 Fill2bPattern(keyList
, &keyListLen
, &startPattern
);
1252 if (dict_filenamelen
&& endFilePosition
) {
1253 if (verbose
== false)
1254 PrintAndLogEx(NORMAL
, "d" NOLF
);
1256 uint32_t keycnt
= 0;
1257 res
= loadFileDICTIONARYEx((char *)dict_filename
, keyList
, sizeof(keyList
), NULL
, 16, &keycnt
, endFilePosition
, &endFilePosition
, false);
1258 keyListLen
= keycnt
;
1263 if (verbose
== false)
1264 PrintAndLogEx(NORMAL
, "");
1267 bool printedHeader
= false;
1268 for (uint8_t sector
= startSector
; sector
<= endSector
; sector
++) {
1269 if (foundKeys
[0][sector
][0] || foundKeys
[1][sector
][0]) {
1270 if (!printedHeader
) {
1271 PrintAndLogEx(NORMAL
, "");
1272 PrintAndLogEx(INFO
, "-------+--------------------------------+---------------------------------");
1273 PrintAndLogEx(INFO
, "|sector| key A | key B |");
1274 PrintAndLogEx(INFO
, "|------+--------------------------------+--------------------------------|");
1275 printedHeader
= true;
1277 PrintAndLogEx(INFO
, "| %02d |%32s|%32s|",
1279 (foundKeys
[0][sector
][0] == 0) ? "------ " : sprint_hex_inrow(&foundKeys
[0][sector
][1], AES_KEY_LEN
),
1280 (foundKeys
[1][sector
][0] == 0) ? "------ " : sprint_hex_inrow(&foundKeys
[1][sector
][1], AES_KEY_LEN
));
1284 PrintAndLogEx(INFO
, "No keys found(");
1286 PrintAndLogEx(INFO
, "'------+--------------------------------+--------------------------------'\n");
1288 // save keys to json
1289 if ((jsonnamelen
> 0) && printedHeader
) {
1291 SendCommandMIX(CMD_HF_ISO14443A_READER
, ISO14A_CONNECT
, 0, 0, NULL
, 0);
1293 PacketResponseNG resp
;
1294 WaitForResponse(CMD_ACK
, &resp
);
1296 iso14a_card_select_t card
;
1297 memcpy(&card
, (iso14a_card_select_t
*)resp
.data
.asBytes
, sizeof(iso14a_card_select_t
));
1299 uint64_t select_status
= resp
.oldarg
[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision
1301 uint8_t data
[10 + 1 + 2 + 1 + 256 + 2 * 64 * (AES_KEY_LEN
+ 1)] = {0};
1303 if (select_status
== 1 || select_status
== 2) {
1304 memcpy(data
, card
.uid
, card
.uidlen
);
1305 data
[10] = card
.sak
;
1306 data
[11] = card
.atqa
[1];
1307 data
[12] = card
.atqa
[0];
1308 atslen
= card
.ats_len
;
1310 memcpy(&data
[14], card
.ats
, atslen
);
1313 // length: UID(10b)+SAK(1b)+ATQA(2b)+ATSlen(1b)+ATS(atslen)+foundKeys[2][64][AES_KEY_LEN + 1]
1314 memcpy(&data
[14 + atslen
], foundKeys
, 2 * 64 * (AES_KEY_LEN
+ 1));
1315 saveFileJSON((char *)jsonname
, jsfMfPlusKeys
, data
, 64, NULL
);
1321 static int CmdHFMFPMAD(const char *Cmd
) {
1323 CLIParserContext
*ctx
;
1324 CLIParserInit(&ctx
, "hf mfp mad",
1325 "Checks and prints Mifare Application Directory (MAD)",
1326 "hf mfp mad -> shows MAD if exists\n"
1327 "hf mfp mad -a e103 -k d3f7d3f7d3f7d3f7d3f7d3f7d3f7d3f7 -> shows NDEF data if exists");
1329 void *argtable
[] = {
1331 arg_lit0("v", "verbose", "show technical data"),
1332 arg_str0(NULL
, "aid", "<aid>", "print all sectors with aid"),
1333 arg_str0("k", "key", "<key>", "key for printing sectors"),
1334 arg_lit0("b", "keyb", "use key B for access printing sectors (by default: key A)"),
1335 arg_lit0(NULL
, "be", "(optional, BigEndian)"),
1336 arg_lit0(NULL
, "dch", "decode Card Holder information"),
1339 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1341 bool verbose
= arg_get_lit(ctx
, 1);
1342 uint8_t aid
[2] = {0};
1344 CLIGetHexWithReturn(ctx
, 2, aid
, &aidlen
);
1345 uint8_t key
[16] = {0};
1347 CLIGetHexWithReturn(ctx
, 3, key
, &keylen
);
1348 bool keyB
= arg_get_lit(ctx
, 4);
1349 bool swapmad
= arg_get_lit(ctx
, 5);
1350 bool decodeholder
= arg_get_lit(ctx
, 6);
1354 if (aidlen
!= 2 && !decodeholder
&& keylen
> 0) {
1355 PrintAndLogEx(WARNING
, "Using default MAD keys instead");
1358 uint8_t sector0
[16 * 4] = {0};
1359 uint8_t sector10
[16 * 4] = {0};
1361 if (mfpReadSector(MF_MAD1_SECTOR
, MF_KEY_A
, (uint8_t *)g_mifarep_mad_key
, sector0
, verbose
)) {
1362 PrintAndLogEx(NORMAL
, "");
1363 PrintAndLogEx(ERR
, "error, read sector 0. card don't have MAD or don't have MAD on default keys");
1367 PrintAndLogEx(NORMAL
, "");
1368 PrintAndLogEx(INFO
, "--- " _CYAN_("Mifare App Directory Information") " ----------------");
1369 PrintAndLogEx(INFO
, "-----------------------------------------------------");
1372 PrintAndLogEx(SUCCESS
, "Raw:");
1373 for (int i
= 0; i
< 4; i
++)
1374 PrintAndLogEx(INFO
, "[%d] %s", i
, sprint_hex(§or0
[i
* 16], 16));
1377 bool haveMAD2
= false;
1378 MAD1DecodeAndPrint(sector0
, swapmad
, verbose
, &haveMAD2
);
1381 if (mfpReadSector(MF_MAD2_SECTOR
, MF_KEY_A
, (uint8_t *)g_mifarep_mad_key
, sector10
, verbose
)) {
1382 PrintAndLogEx(NORMAL
, "");
1383 PrintAndLogEx(ERR
, "error, read sector 0x10. card don't have MAD or don't have MAD on default keys");
1387 MAD2DecodeAndPrint(sector10
, swapmad
, verbose
);
1390 if (aidlen
== 2 || decodeholder
) {
1391 uint16_t mad
[7 + 8 + 8 + 8 + 8] = {0};
1393 if (MADDecode(sector0
, sector10
, mad
, &madlen
, swapmad
)) {
1394 PrintAndLogEx(ERR
, "can't decode MAD");
1398 // copy default NDEF key
1399 uint8_t akey
[16] = {0};
1400 memcpy(akey
, g_mifarep_ndef_key
, 16);
1402 // user specified key
1404 memcpy(akey
, key
, 16);
1407 uint16_t aaid
= 0x0004;
1409 aaid
= (aid
[0] << 8) + aid
[1];
1410 PrintAndLogEx(NORMAL
, "");
1411 PrintAndLogEx(INFO
, "-------------- " _CYAN_("AID 0x%04x") " ---------------", aaid
);
1413 for (int i
= 0; i
< madlen
; i
++) {
1414 if (aaid
== mad
[i
]) {
1415 uint8_t vsector
[16 * 4] = {0};
1416 if (mfpReadSector(i
+ 1, keyB
? MF_KEY_B
: MF_KEY_A
, akey
, vsector
, false)) {
1417 PrintAndLogEx(NORMAL
, "");
1418 PrintAndLogEx(ERR
, "error, read sector %d error", i
+ 1);
1422 for (int j
= 0; j
< (verbose
? 4 : 3); j
++)
1423 PrintAndLogEx(NORMAL
, " [%03d] %s", (i
+ 1) * 4 + j
, sprint_hex(&vsector
[j
* 16], 16));
1430 PrintAndLogEx(NORMAL
, "");
1431 PrintAndLogEx(INFO
, "-------- " _CYAN_("Card Holder Info 0x%04x") " --------", aaid
);
1433 uint8_t data
[4096] = {0};
1436 for (int i
= 0; i
< madlen
; i
++) {
1437 if (aaid
== mad
[i
]) {
1439 uint8_t vsector
[16 * 4] = {0};
1440 if (mfReadSector(i
+ 1, keyB
? MF_KEY_B
: MF_KEY_A
, akey
, vsector
)) {
1441 PrintAndLogEx(NORMAL
, "");
1442 PrintAndLogEx(ERR
, "error, read sector %d", i
+ 1);
1446 memcpy(&data
[datalen
], vsector
, 16 * 3);
1452 PrintAndLogEx(WARNING
, "no Card Holder Info data");
1455 MADCardHolderInfoDecode(data
, datalen
, verbose
);
1461 int CmdHFMFPNDEFRead(const char *Cmd
) {
1463 CLIParserContext
*ctx
;
1464 CLIParserInit(&ctx
, "hf mfp ndefread",
1465 "Prints NFC Data Exchange Format (NDEF)",
1466 "hf mfp ndefread -> shows NDEF data\n"
1467 "hf mfp ndefread -vv -> shows NDEF parsed and raw data\n"
1468 "hf mfp ndefread -a e103 -k d3f7d3f7d3f7d3f7d3f7d3f7d3f7d3f7 -> shows NDEF data with custom AID and key");
1470 void *argtable
[] = {
1472 arg_litn("v", "verbose", 0, 2, "show technical data"),
1473 arg_str0(NULL
, "aid", "<aid>", "replace default aid for NDEF"),
1474 arg_str0("k", "key", "<key>", "replace default key for NDEF"),
1475 arg_lit0("b", "keyb", "use key B for access sectors (by default: key A)"),
1478 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1480 bool verbose
= arg_get_lit(ctx
, 1);
1481 bool verbose2
= arg_get_lit(ctx
, 1) > 1;
1482 uint8_t aid
[2] = {0};
1484 CLIGetHexWithReturn(ctx
, 2, aid
, &aidlen
);
1485 uint8_t key
[16] = {0};
1487 CLIGetHexWithReturn(ctx
, 3, key
, &keylen
);
1488 bool keyB
= arg_get_lit(ctx
, 4);
1492 uint16_t ndefAID
= 0xe103;
1494 ndefAID
= (aid
[0] << 8) + aid
[1];
1496 uint8_t ndefkey
[16] = {0};
1497 memcpy(ndefkey
, g_mifarep_ndef_key
, 16);
1499 memcpy(ndefkey
, key
, 16);
1502 uint8_t sector0
[16 * 4] = {0};
1503 uint8_t sector10
[16 * 4] = {0};
1504 uint8_t data
[4096] = {0};
1508 PrintAndLogEx(INFO
, "reading MAD v1 sector");
1510 if (mfpReadSector(MF_MAD1_SECTOR
, MF_KEY_A
, (uint8_t *)g_mifarep_mad_key
, sector0
, verbose
)) {
1511 PrintAndLogEx(ERR
, "error, read sector 0. card don't have MAD or don't have MAD on default keys");
1512 PrintAndLogEx(HINT
, "Try " _YELLOW_("`hf mfp ndefread -k `") " with your custom key");
1516 bool haveMAD2
= false;
1517 int res
= MADCheck(sector0
, NULL
, verbose
, &haveMAD2
);
1518 if (res
!= PM3_SUCCESS
) {
1519 PrintAndLogEx(ERR
, "MAD error %d", res
);
1526 PrintAndLogEx(INFO
, "reading MAD v2 sector");
1528 if (mfpReadSector(MF_MAD2_SECTOR
, MF_KEY_A
, (uint8_t *)g_mifarep_mad_key
, sector10
, verbose
)) {
1529 PrintAndLogEx(ERR
, "error, read sector 0x10. card don't have MAD or don't have MAD on default keys");
1530 PrintAndLogEx(HINT
, "Try " _YELLOW_("`hf mfp ndefread -k `") " with your custom key");
1535 uint16_t mad
[7 + 8 + 8 + 8 + 8] = {0};
1537 res
= MADDecode(sector0
, (haveMAD2
? sector10
: NULL
), mad
, &madlen
, false);
1538 if (res
!= PM3_SUCCESS
) {
1539 PrintAndLogEx(ERR
, "can't decode MAD");
1543 PrintAndLogEx(INFO
, "reading data from tag");
1544 for (int i
= 0; i
< madlen
; i
++) {
1545 if (ndefAID
== mad
[i
]) {
1546 uint8_t vsector
[16 * 4] = {0};
1547 if (mfpReadSector(i
+ 1, keyB
? MF_KEY_B
: MF_KEY_A
, ndefkey
, vsector
, false)) {
1548 PrintAndLogEx(ERR
, "error, reading sector %d", i
+ 1);
1552 memcpy(&data
[datalen
], vsector
, 16 * 3);
1555 PrintAndLogEx(INPLACE
, "%d", i
);
1558 PrintAndLogEx(NORMAL
, "");
1561 PrintAndLogEx(ERR
, "no NDEF data");
1566 PrintAndLogEx(NORMAL
, "");
1567 PrintAndLogEx(INFO
, "--- " _CYAN_("MF Plus NDEF raw") " ----------------");
1568 print_buffer(data
, datalen
, 1);
1571 NDEFDecodeAndPrint(data
, datalen
, verbose
);
1572 PrintAndLogEx(HINT
, "Try " _YELLOW_("`hf mfp ndefread -vv`") " for more details");
1576 static command_t CommandTable
[] = {
1577 {"help", CmdHelp
, AlwaysAvailable
, "This help"},
1578 {"info", CmdHFMFPInfo
, IfPm3Iso14443a
, "Info about Mifare Plus tag"},
1579 {"wrp", CmdHFMFPWritePerso
, IfPm3Iso14443a
, "Write Perso command"},
1580 {"initp", CmdHFMFPInitPerso
, IfPm3Iso14443a
, "Fills all the card's keys"},
1581 {"commitp", CmdHFMFPCommitPerso
, IfPm3Iso14443a
, "Move card to SL1 or SL3 mode"},
1582 {"auth", CmdHFMFPAuth
, IfPm3Iso14443a
, "Authentication"},
1583 {"rdbl", CmdHFMFPRdbl
, IfPm3Iso14443a
, "Read blocks"},
1584 {"rdsc", CmdHFMFPRdsc
, IfPm3Iso14443a
, "Read sectors"},
1585 {"wrbl", CmdHFMFPWrbl
, IfPm3Iso14443a
, "Write blocks"},
1586 {"chk", CmdHFMFPChk
, IfPm3Iso14443a
, "Check keys"},
1587 {"mad", CmdHFMFPMAD
, IfPm3Iso14443a
, "Checks and prints MAD"},
1588 {"ndefread", CmdHFMFPNDEFRead
, IfPm3Iso14443a
, "Prints NDEF records from card"},
1589 {NULL
, NULL
, 0, NULL
}
1592 static int CmdHelp(const char *Cmd
) {
1593 (void)Cmd
; // Cmd is not used so far
1594 CmdsHelp(CommandTable
);
1598 int CmdHFMFP(const char *Cmd
) {
1599 clearCommandBuffer();
1600 return CmdsParse(CommandTable
, Cmd
);