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 // High frequency MIFARE Plus commands
17 //-----------------------------------------------------------------------------
21 #include "cmdparser.h" // command_t
22 #include "commonutil.h" // ARRAYLEN
27 #include "mifare/mifare4.h"
28 #include "mifare/mad.h"
30 #include "cliparser.h"
31 #include "mifare/mifaredefault.h"
32 #include "util_posix.h"
33 #include "fileutils.h"
34 #include "protocols.h"
35 #include "crypto/libpcrypto.h"
36 #include "cmdhfmf.h" // printblock, header
39 static const uint8_t mfp_default_key
[16] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
40 static uint16_t mfp_card_adresses
[] = {0x9000, 0x9001, 0x9002, 0x9003, 0x9004, 0x9006, 0x9007, 0xA000, 0xA001, 0xA080, 0xA081, 0xC000, 0xC001};
42 #define MFP_KEY_FILE_SIZE 14 + (2 * 64 * (AES_KEY_LEN + 1))
44 static int CmdHelp(const char *Cmd
);
47 The 7 MSBits (= n) code the storage size itself based on 2^n,
48 the LSBit is set to '0' if the size is exactly 2^n
49 and set to '1' if the storage size is between 2^n and 2^(n+1).
50 For this version of DESFire the 7 MSBits are set to 0x0C (2^12 = 4096) and the LSBit is '0'.
52 static char *getCardSizeStr(uint8_t fsize
) {
54 static char buf
[40] = {0x00};
57 uint16_t usize
= 1 << ((fsize
>> 1) + 1);
58 uint16_t lsize
= 1 << (fsize
>> 1);
62 snprintf(retStr
, sizeof(buf
), "0x%02X ( " _GREEN_("%d - %d bytes") " )", fsize
, usize
, lsize
);
64 snprintf(retStr
, sizeof(buf
), "0x%02X ( " _GREEN_("%d bytes") " )", fsize
, lsize
);
68 static char *getProtocolStr(uint8_t id
, bool hw
) {
70 static char buf
[50] = {0x00};
74 snprintf(retStr
, sizeof(buf
), "0x%02X ( " _YELLOW_("ISO 14443-3 MIFARE, 14443-4") " )", id
);
75 } else if (id
== 0x05) {
77 snprintf(retStr
, sizeof(buf
), "0x%02X ( " _YELLOW_("ISO 14443-2, 14443-3") " )", id
);
79 snprintf(retStr
, sizeof(buf
), "0x%02X ( " _YELLOW_("ISO 14443-3, 14443-4") " )", id
);
81 snprintf(retStr
, sizeof(buf
), "0x%02X ( " _YELLOW_("Unknown") " )", id
);
86 static char *getVersionStr(uint8_t type
, uint8_t major
, uint8_t minor
) {
88 static char buf
[40] = {0x00};
91 if (type
== 0x01 && major
== 0x00)
92 snprintf(retStr
, sizeof(buf
), "%x.%x ( " _GREEN_("DESFire MF3ICD40") " )", major
, minor
);
93 else if (major
== 0x10 && minor
== 0x00)
94 snprintf(retStr
, sizeof(buf
), "%x.%x ( " _GREEN_("NTAG413DNA") " )", major
, minor
);
95 else if (type
== 0x01 && major
== 0x01 && minor
== 0x00)
96 snprintf(retStr
, sizeof(buf
), "%x.%x ( " _GREEN_("DESFire EV1") " )", major
, minor
);
97 else if (type
== 0x01 && major
== 0x12 && minor
== 0x00)
98 snprintf(retStr
, sizeof(buf
), "%x.%x ( " _GREEN_("DESFire EV2") " )", major
, minor
);
99 else if (type
== 0x01 && major
== 0x22 && minor
== 0x00)
100 snprintf(retStr
, sizeof(buf
), "%x.%x ( " _GREEN_("DESFire EV2 XL") " )", major
, minor
);
101 else if (type
== 0x01 && major
== 0x42 && minor
== 0x00)
102 snprintf(retStr
, sizeof(buf
), "%x.%x ( " _GREEN_("DESFire EV2") " )", major
, minor
);
103 else if (type
== 0x01 && major
== 0x33 && minor
== 0x00)
104 snprintf(retStr
, sizeof(buf
), "%x.%x ( " _GREEN_("DESFire EV3") " )", major
, minor
);
105 else if (type
== 0x01 && major
== 0x30 && minor
== 0x00)
106 snprintf(retStr
, sizeof(buf
), "%x.%x ( " _GREEN_("DESFire Light") " )", major
, minor
);
107 else if (type
== 0x02 && major
== 0x11 && minor
== 0x00)
108 snprintf(retStr
, sizeof(buf
), "%x.%x ( " _GREEN_("Plus EV1") " )", major
, minor
);
109 else if (type
== 0x02 && major
== 0x22 && minor
== 0x00)
110 snprintf(retStr
, sizeof(buf
), "%x.%x ( " _GREEN_("Plus EV2") " )", major
, minor
);
112 snprintf(retStr
, sizeof(buf
), "%x.%x ( " _YELLOW_("Unknown") " )", major
, minor
);
116 static char *getTypeStr(uint8_t type
) {
118 static char buf
[40] = {0x00};
123 snprintf(retStr
, sizeof(buf
), "0x%02X ( " _YELLOW_("DESFire") " )", type
);
126 snprintf(retStr
, sizeof(buf
), "0x%02X ( " _YELLOW_("Plus") " )", type
);
129 snprintf(retStr
, sizeof(buf
), "0x%02X ( " _YELLOW_("Ultralight") " )", type
);
132 snprintf(retStr
, sizeof(buf
), "0x%02X ( " _YELLOW_("NTAG") " )", type
);
135 snprintf(retStr
, sizeof(buf
), "0x%02X ( " _YELLOW_("Smartcard") " )", type
);
143 static nxp_cardtype_t
getCardType(uint8_t type
, uint8_t major
, uint8_t minor
) {
146 if (type
== 0x01 && major
== 0x00 && minor
== 0x02)
147 return DESFIRE_MF3ICD40
;
150 if (type
== 0x01 && major
== 0x01 && minor
== 0x00)
154 if (type
== 0x01 && major
== 0x12 && minor
== 0x00)
157 if (type
== 0x01 && major
== 0x22 && minor
== 0x00)
158 return DESFIRE_EV2_XL
;
161 if (type
== 0x01 && major
== 0x33 && minor
== 0x00)
165 if (type
== 0x08 && major
== 0x30 && minor
== 0x00)
166 return DESFIRE_LIGHT
;
168 // combo card DESFire / EMV
169 if (type
== 0x81 && major
== 0x42 && minor
== 0x00)
173 if (type
== 0x02 && major
== 0x11 && minor
== 0x00)
177 if (type
== 0x02 && major
== 0x22 && minor
== 0x00)
181 if (type
== 0x04 && major
== 0x10 && minor
== 0x00)
185 if (type
== 0x04 && major
== 0x30 && minor
== 0x00)
192 static int plus_print_signature(uint8_t *uid
, uint8_t uidlen
, uint8_t *signature
, int signature_len
) {
194 // ref: MIFARE Plus EV1 Originality Signature Validation
195 #define PUBLIC_PLUS_ECDA_KEYLEN 57
196 const ecdsa_publickey_t nxp_plus_public_keys
[] = {
197 {"MIFARE Plus EV1", "044409ADC42F91A8394066BA83D872FB1D16803734E911170412DDF8BAD1A4DADFD0416291AFE1C748253925DA39A5F39A1C557FFACD34C62E"},
198 {"MIFARE Plus Ev2", "04BB49AE4447E6B1B6D21C098C1538B594A11A4A1DBF3D5E673DEACDEB3CC512D1C08AFA1A2768CE20A200BACD2DC7804CD7523A0131ABF607"},
199 {"MIFARE Plus Troika", "040F732E0EA7DF2B38F791BF89425BF7DCDF3EE4D976669E3831F324FF15751BD52AFF1782F72FF2731EEAD5F63ABE7D126E03C856FFB942AF"}
203 bool is_valid
= false;
205 for (i
= 0; i
< ARRAYLEN(nxp_plus_public_keys
); i
++) {
208 uint8_t key
[PUBLIC_PLUS_ECDA_KEYLEN
];
209 param_gethex_to_eol(nxp_plus_public_keys
[i
].value
, 0, key
, PUBLIC_PLUS_ECDA_KEYLEN
, &dl
);
211 int res
= ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP224R1
, key
, uid
, uidlen
, signature
, signature_len
, false);
212 is_valid
= (res
== 0);
217 PrintAndLogEx(NORMAL
, "");
218 PrintAndLogEx(INFO
, "--- " _CYAN_("Tag Signature"));
220 if (is_valid
== false || i
== ARRAYLEN(nxp_plus_public_keys
)) {
221 PrintAndLogEx(INFO
, " Elliptic curve parameters: NID_secp224r1");
222 PrintAndLogEx(INFO
, " TAG IC Signature: %s", sprint_hex_inrow(signature
, 16));
223 PrintAndLogEx(INFO
, " : %s", sprint_hex_inrow(signature
+ 16, 16));
224 PrintAndLogEx(INFO
, " : %s", sprint_hex_inrow(signature
+ 32, 16));
225 PrintAndLogEx(INFO
, " : %s", sprint_hex_inrow(signature
+ 48, signature_len
- 48));
226 PrintAndLogEx(SUCCESS
, " Signature verification: " _RED_("failed"));
230 PrintAndLogEx(INFO
, " IC signature public key name: " _GREEN_("%s"), nxp_plus_public_keys
[i
].desc
);
231 PrintAndLogEx(INFO
, "IC signature public key value: %.32s", nxp_plus_public_keys
[i
].value
);
232 PrintAndLogEx(INFO
, " : %.32s", nxp_plus_public_keys
[i
].value
+ 32);
233 PrintAndLogEx(INFO
, " : %.32s", nxp_plus_public_keys
[i
].value
+ 64);
234 PrintAndLogEx(INFO
, " : %.32s", nxp_plus_public_keys
[i
].value
+ 96);
235 PrintAndLogEx(INFO
, " Elliptic curve parameters: NID_secp224r1");
236 PrintAndLogEx(INFO
, " TAG IC Signature: %s", sprint_hex_inrow(signature
, 16));
237 PrintAndLogEx(INFO
, " : %s", sprint_hex_inrow(signature
+ 16, 16));
238 PrintAndLogEx(INFO
, " : %s", sprint_hex_inrow(signature
+ 32, 16));
239 PrintAndLogEx(INFO
, " : %s", sprint_hex_inrow(signature
+ 48, signature_len
- 48));
240 PrintAndLogEx(SUCCESS
, " Signature verification: " _GREEN_("successful"));
244 static int get_plus_signature(uint8_t *signature
, int *signature_len
) {
246 mfpSetVerboseMode(false);
248 uint8_t data
[59] = {0};
249 int resplen
= 0, retval
= PM3_SUCCESS
;
250 MFPGetSignature(true, false, data
, sizeof(data
), &resplen
);
253 memcpy(signature
, data
+ 1, 56);
264 static int plus_print_version(uint8_t *version
) {
265 PrintAndLogEx(SUCCESS
, "UID: " _GREEN_("%s"), sprint_hex(version
+ 14, 7));
266 PrintAndLogEx(SUCCESS
, "Batch number: " _GREEN_("%s"), sprint_hex(version
+ 21, 5));
267 PrintAndLogEx(SUCCESS
, "Production date: week " _GREEN_("%02x") " / " _GREEN_("20%02x"), version
[7 + 7 + 7 + 5], version
[7 + 7 + 7 + 5 + 1]);
268 PrintAndLogEx(NORMAL
, "");
269 PrintAndLogEx(INFO
, "--- " _CYAN_("Hardware Information"));
270 PrintAndLogEx(INFO
, " Raw : %s", sprint_hex(version
, 7));
271 PrintAndLogEx(INFO
, " Vendor Id: " _YELLOW_("%s"), getTagInfo(version
[0]));
272 PrintAndLogEx(INFO
, " Type: %s", getTypeStr(version
[1]));
273 PrintAndLogEx(INFO
, " Subtype: " _YELLOW_("0x%02X"), version
[2]);
274 PrintAndLogEx(INFO
, " Version: %s", getVersionStr(version
[1], version
[3], version
[4]));
275 PrintAndLogEx(INFO
, " Storage size: %s", getCardSizeStr(version
[5]));
276 PrintAndLogEx(INFO
, " Protocol: %s", getProtocolStr(version
[6], true));
277 PrintAndLogEx(NORMAL
, "");
278 PrintAndLogEx(INFO
, "--- " _CYAN_("Software Information"));
279 PrintAndLogEx(INFO
, " Raw : %s", sprint_hex(version
+ 7, 6));
280 PrintAndLogEx(INFO
, " Vendor Id: " _YELLOW_("%s"), getTagInfo(version
[7]));
281 PrintAndLogEx(INFO
, " Type: %s", getTypeStr(version
[8]));
282 PrintAndLogEx(INFO
, " Subtype: " _YELLOW_("0x%02X"), version
[9]);
283 PrintAndLogEx(INFO
, " Version: " _YELLOW_("%d.%d"), version
[10], version
[11]);
284 PrintAndLogEx(INFO
, " Storage size: %s", getCardSizeStr(version
[12]));
285 PrintAndLogEx(INFO
, " Protocol: %s", getProtocolStr(version
[13], false));
289 static int get_plus_version(uint8_t *version
, int *version_len
) {
291 int resplen
= 0, retval
= PM3_SUCCESS
;
292 mfpSetVerboseMode(false);
293 MFPGetVersion(true, false, version
, *version_len
, &resplen
);
295 *version_len
= resplen
;
302 static int CmdHFMFPInfo(const char *Cmd
) {
303 CLIParserContext
*ctx
;
304 CLIParserInit(&ctx
, "hf mfp info",
305 "Get info from MIFARE Plus tags",
312 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
314 PrintAndLogEx(NORMAL
, "");
315 PrintAndLogEx(INFO
, "--- " _CYAN_("Tag Information") " ---------------------------");
318 SendCommandMIX(CMD_HF_ISO14443A_READER
, ISO14A_CONNECT
, 0, 0, NULL
, 0);
319 PacketResponseNG resp
;
320 if (WaitForResponseTimeout(CMD_ACK
, &resp
, 2000) == false) {
321 PrintAndLogEx(DEBUG
, "iso14443a card select timeout");
326 iso14a_card_select_t card
;
327 memcpy(&card
, (iso14a_card_select_t
*)resp
.data
.asBytes
, sizeof(iso14a_card_select_t
));
329 uint64_t select_status
= resp
.oldarg
[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision
331 bool supportVersion
= false;
332 bool supportSignature
= false;
335 uint8_t version
[30] = {0};
336 int version_len
= sizeof(version
);
337 if (get_plus_version(version
, &version_len
) == PM3_SUCCESS
) {
338 plus_print_version(version
);
339 supportVersion
= true;
341 // info about 14a part, historical bytes.
342 infoHF14A(false, false, false);
345 // Signature originality check
346 uint8_t signature
[56] = {0};
347 int signature_len
= sizeof(signature
);
348 if (get_plus_signature(signature
, &signature_len
) == PM3_SUCCESS
) {
349 plus_print_signature(card
.uid
, card
.uidlen
, signature
, signature_len
);
350 supportSignature
= true;
353 if (select_status
== 1 || select_status
== 2) {
355 PrintAndLogEx(INFO
, "--- " _CYAN_("Fingerprint"));
359 if (supportVersion
) {
361 int cardtype
= getCardType(version
[1], version
[3], version
[4]);
364 if (supportSignature
) {
365 PrintAndLogEx(INFO
, "Tech..... " _GREEN_("MIFARE Plus EV1"));
367 PrintAndLogEx(INFO
, "Tech..... " _YELLOW_("MIFARE Plus SE/X"));
373 if (supportSignature
) {
374 PrintAndLogEx(INFO
, "Tech..... " _GREEN_("MIFARE Plus EV2"));
376 PrintAndLogEx(INFO
, "Tech..... " _YELLOW_("MIFARE Plus EV2 ???"));
381 case DESFIRE_MF3ICD40
:
386 case DESFIRE_LIGHT
: {
387 PrintAndLogEx(HINT
, "Card seems to be MIFARE DESFire. Try " _YELLOW_("`hf mfdes info`"));
388 PrintAndLogEx(NORMAL
, "");
393 PrintAndLogEx(INFO
, "Tech..... Unknown ( " _YELLOW_("%u") " )", cardtype
);
399 // MIFARE Type Identification Procedure
400 // https://www.nxp.com/docs/en/application-note/AN10833.pdf
401 uint16_t ATQA
= card
.atqa
[0] + (card
.atqa
[1] << 8);
404 PrintAndLogEx(INFO
, "Size..... " _GREEN_("2K") " (%s UID)", (ATQA
& 0x0040) ? "7" : "4");
408 PrintAndLogEx(INFO
, "Size..... " _GREEN_("4K") " (%s UID)", (ATQA
& 0x0040) ? "7" : "4");
412 uint8_t SLmode
= 0xFF;
414 if (card
.sak
== 0x08) {
415 PrintAndLogEx(INFO
, "SAK...... " _GREEN_("2K 7b UID"));
416 if (select_status
== 2) SLmode
= 1;
418 if (card
.sak
== 0x18) {
419 PrintAndLogEx(INFO
, "SAK...... " _GREEN_("4K 7b UID"));
420 if (select_status
== 2) SLmode
= 1;
422 if (card
.sak
== 0x10) {
423 PrintAndLogEx(INFO
, "SAK...... " _GREEN_("2K"));
424 if (select_status
== 2) SLmode
= 2;
426 if (card
.sak
== 0x11) {
427 PrintAndLogEx(INFO
, "SAK...... " _GREEN_("4K"));
428 if (select_status
== 2) SLmode
= 2;
432 if (card
.sak
== 0x20) {
433 if (card
.ats_len
> 0) {
434 PrintAndLogEx(INFO
, "SAK...... " _GREEN_("MIFARE Plus SL0/SL3") " or " _GREEN_("MIFARE DESFire"));
437 uint8_t data
[128] = {0};
439 // https://github.com/Proxmark/proxmark3/blob/master/client/luascripts/mifarePlus.lua#L161
440 uint8_t cmd
[3 + 16] = {0xa8, 0x90, 0x90, 0x00};
441 int res
= ExchangeRAW14a(cmd
, sizeof(cmd
), true, false, data
, sizeof(data
), &datalen
, false);
442 if (res
!= PM3_SUCCESS
) {
443 PrintAndLogEx(INFO
, "Identification failed");
444 PrintAndLogEx(NORMAL
, "");
448 // DESFire answers 0x1C or 67 00
449 // Plus answers 0x0B, 0x09, 0x06
450 // 6D00 is "INS code not supported" in APDU
459 PrintAndLogEx(INFO
, _RED_("Send copy to iceman of this command output!"));
460 PrintAndLogEx(INFO
, "Data... %s", sprint_hex(data
, datalen
));
463 if ((memcmp(data
, "\x67\x00", 2) == 0) || // wrong length
464 (memcmp(data
, "\x1C\x83\x0C", 3) == 0) // desfire answers
466 PrintAndLogEx(INFO
, "Result... " _RED_("MIFARE DESFire"));
467 PrintAndLogEx(NORMAL
, "");
471 // } else if (memcmp(data, "\x68\x82", 2) == 0) { // Secure message not supported
472 } else if (memcmp(data
, "\x6D\x00", 2) == 0) {
473 // } else if (memcmp(data, "\x6E\x00", 2) == 0) { // Class not supported
476 PrintAndLogEx(INFO
, "Result... " _GREEN_("MIFARE Plus SL0/SL3"));
479 if ((datalen
> 1) && (data
[0] == 0x09)) {
487 // How do we detect SL0 / SL1 / SL2 / SL3 modes?!?
488 PrintAndLogEx(INFO
, "--- " _CYAN_("Security Level (SL)"));
491 PrintAndLogEx(SUCCESS
, "SL mode... " _YELLOW_("SL%d"), SLmode
);
493 PrintAndLogEx(WARNING
, "SL mode... " _YELLOW_("unknown"));
497 PrintAndLogEx(INFO
, "SL 0: initial delivery configuration, used for card personalization");
500 PrintAndLogEx(INFO
, "SL 1: backwards functional compatibility mode (with MIFARE Classic 1K / 4K) with an optional AES authentication");
503 PrintAndLogEx(INFO
, "SL 2: 3-Pass Authentication based on AES followed by MIFARE CRYPTO1 authentication, communication secured by MIFARE CRYPTO1");
506 PrintAndLogEx(INFO
, "SL 3: 3-Pass authentication based on AES, data manipulation commands secured by AES encryption and an AES based MACing method.");
513 PrintAndLogEx(INFO
, "MIFARE Plus info not available");
515 PrintAndLogEx(NORMAL
, "");
520 static int CmdHFMFPWritePerso(const char *Cmd
) {
521 CLIParserContext
*ctx
;
522 CLIParserInit(&ctx
, "hf mfp wrp",
523 "Executes Write Perso command. Can be used in SL0 mode only.",
524 "Use this command to program AES keys, as well as personalize other data on the tag.\n"
526 "* Address 00 [00-FF]: Memory blocks (as well as ACLs and Crypto1 keys)\n"
527 "* Address 40 [00-40]: AES sector keys\n"
528 "* Address 90 [00-04]: AES administrative keys\n"
529 "* Address A0 [00, 01, 80, 81]: Virtual Card keys\n"
530 "* Address B0 [00-03]: Configuration data (DO NOT TOUCH B003)\n"
532 "hf mfp wrp --adr 4000 --data 000102030405060708090a0b0c0d0e0f -> write key (00..0f) to key number 4000 \n"
533 "hf mfp wrp --adr 4000 -> write default key(0xff..0xff) to key number 4000\n"
534 "hf mfp wrp --adr b000 -d FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> allow 255 commands without MAC in configuration block (B000)\n"
535 "hf mfp wrp --adr 0003 -d 1234561234567F078869B0B1B2B3B4B5 -> write crypto1 keys A: 123456123456 and B: B0B1B2B3B4B5 to block 3\n");
539 arg_lit0("v", "verbose", "Verbose output"),
540 arg_str1("a", "adr", "<hex>", "Address, 2 hex bytes"),
541 arg_str0("d", "data", "<hex>", "Data, 16 hex bytes"),
544 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
546 bool verbose
= arg_get_lit(ctx
, 1);
548 uint8_t addr
[64] = {0};
550 CLIGetHexWithReturn(ctx
, 2, addr
, &addrLen
);
552 uint8_t datain
[64] = {0};
554 CLIGetHexWithReturn(ctx
, 3, datain
, &datainLen
);
557 mfpSetVerboseMode(verbose
);
560 memmove(datain
, mfp_default_key
, 16);
565 PrintAndLogEx(ERR
, "Address length must be 2 bytes. Got %d", addrLen
);
568 if (datainLen
!= 16) {
569 PrintAndLogEx(ERR
, "Data length must be 16 bytes. Got %d", datainLen
);
573 uint8_t data
[250] = {0};
576 int res
= MFPWritePerso(addr
, datain
, true, false, data
, sizeof(data
), &datalen
);
578 PrintAndLogEx(ERR
, "Exchange error: %d", res
);
583 PrintAndLogEx(ERR
, "Command must return 3 bytes. Got %d", datalen
);
587 if (data
[0] != 0x90) {
588 PrintAndLogEx(ERR
, "Command error: %02x %s", data
[0], mfpGetErrorDescription(data
[0]));
592 PrintAndLogEx(INFO
, "Write ( " _GREEN_("ok") " )");
596 static int CmdHFMFPInitPerso(const char *Cmd
) {
597 CLIParserContext
*ctx
;
598 CLIParserInit(&ctx
, "hf mfp initp",
599 "Executes Write Perso command for all card's keys. Can be used in SL0 mode only.",
600 "hf mfp initp --key 000102030405060708090a0b0c0d0e0f -> fill all the keys with key (00..0f)\n"
601 "hf mfp initp -vv -> fill all the keys with default key(0xff..0xff) and show all the data exchange");
605 arg_litn("v", "verbose", 0, 2, "Verbose output"),
606 arg_str0("k", "key", "<hex>", "Key, 16 hex bytes"),
609 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
611 bool verbose
= arg_get_lit(ctx
, 1);
612 bool verbose2
= arg_get_lit(ctx
, 1) > 1;
614 uint8_t key
[256] = {0};
616 CLIGetHexWithReturn(ctx
, 2, key
, &keylen
);
619 if (keylen
&& keylen
!= 16) {
620 PrintAndLogEx(FAILED
, "Key length must be 16 bytes. Got %d", keylen
);
625 memmove(key
, mfp_default_key
, sizeof(mfp_default_key
));
628 uint8_t keyNum
[2] = {0};
629 uint8_t data
[250] = {0};
633 mfpSetVerboseMode(verbose2
);
634 for (uint16_t sn
= 0x4000; sn
< 0x4050; sn
++) {
636 keyNum
[1] = sn
& 0xff;
637 res
= MFPWritePerso(keyNum
, key
, (sn
== 0x4000), true, data
, sizeof(data
), &datalen
);
638 if (!res
&& (datalen
== 3) && data
[0] == 0x09) {
639 PrintAndLogEx(INFO
, "2K card detected.");
642 if (res
|| (datalen
!= 3) || data
[0] != 0x90) {
643 PrintAndLogEx(ERR
, "Write error on address %04x", sn
);
648 mfpSetVerboseMode(verbose
);
649 for (int i
= 0; i
< ARRAYLEN(mfp_card_adresses
); i
++) {
650 keyNum
[0] = mfp_card_adresses
[i
] >> 8;
651 keyNum
[1] = mfp_card_adresses
[i
] & 0xff;
652 res
= MFPWritePerso(keyNum
, key
, false, true, data
, sizeof(data
), &datalen
);
653 if (!res
&& (datalen
== 3) && data
[0] == 0x09) {
654 PrintAndLogEx(WARNING
, "Skipped[%04x]...", mfp_card_adresses
[i
]);
656 if (res
|| (datalen
!= 3) || data
[0] != 0x90) {
657 PrintAndLogEx(ERR
, "Write error on address %04x", mfp_card_adresses
[i
]);
667 PrintAndLogEx(INFO
, "Done!");
671 static int CmdHFMFPCommitPerso(const char *Cmd
) {
672 CLIParserContext
*ctx
;
673 CLIParserInit(&ctx
, "hf mfp commitp",
674 "Executes Commit Perso command. Can be used in SL0 mode only.\n"
675 "OBS! This command will not be executed if \n"
676 "CardConfigKey, CardMasterKey and L3SwitchKey AES keys are not written.",
678 // "hf mfp commitp --sl 1"
683 arg_lit0("v", "verbose", "Verbose output"),
684 // arg_int0(NULL, "sl", "<dec>", "SL mode"),
687 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
688 bool verbose
= arg_get_lit(ctx
, 1);
689 // int slmode = arg_get_int(ctx, 2);
692 mfpSetVerboseMode(verbose
);
694 uint8_t data
[250] = {0};
697 int res
= MFPCommitPerso(true, false, data
, sizeof(data
), &datalen
);
699 PrintAndLogEx(ERR
, "Exchange error: %d", res
);
704 PrintAndLogEx(ERR
, "Command must return 3 bytes. Got %d", datalen
);
708 if (data
[0] != 0x90) {
709 PrintAndLogEx(ERR
, "Command error: %02x %s", data
[0], mfpGetErrorDescription(data
[0]));
712 PrintAndLogEx(INFO
, "Switched security level ( " _GREEN_("ok") " )");
716 static int CmdHFMFPAuth(const char *Cmd
) {
717 uint8_t keyn
[250] = {0};
719 uint8_t key
[250] = {0};
722 CLIParserContext
*ctx
;
723 CLIParserInit(&ctx
, "hf mfp auth",
724 "Executes AES authentication command for MIFARE Plus card",
725 "hf mfp auth --ki 4000 --key 000102030405060708090a0b0c0d0e0f -> executes authentication\n"
726 "hf mfp auth --ki 9003 --key FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -v -> executes authentication and shows all the system data");
730 arg_lit0("v", "verbose", "Verbose output"),
731 arg_str1(NULL
, "ki", "<hex>", "Key number, 2 hex bytes"),
732 arg_str1(NULL
, "key", "<hex>", "Key, 16 hex bytes"),
735 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
737 bool verbose
= arg_get_lit(ctx
, 1);
738 CLIGetHexWithReturn(ctx
, 2, keyn
, &keynlen
);
739 CLIGetHexWithReturn(ctx
, 3, key
, &keylen
);
743 PrintAndLogEx(ERR
, "ERROR: <key number> must be 2 bytes. Got %d", keynlen
);
748 PrintAndLogEx(ERR
, "ERROR: <key> must be 16 bytes. Got %d", keylen
);
752 return MifareAuth4(NULL
, keyn
, key
, true, false, true, verbose
, false);
754 static int data_crypt(mf4Session_t
*mf4session
, uint8_t *dati
, uint8_t *dato
, bool rev
) {
756 memcpy(kenc
, mf4session
->Kenc
, 16);
758 memcpy(ti
, mf4session
->TI
, 4);
760 uint8_t IV
[16] = {0, 0, 0x00, 0x00, 0x00, 0, 0x00, 0x00, 0x00, 0};
762 ctr
[0] = (uint8_t)(mf4session
->R_Ctr
& 0xff);
763 for (int i
= 0; i
< 9; i
+= 4) {memcpy(&IV
[i
], ctr
, 1);}
764 memcpy(&IV
[12], ti
, 4); // For reads TI is LS
766 ctr
[0] = (uint8_t)(mf4session
->W_Ctr
& 0xff);
767 for (int i
= 3; i
< 16; i
+= 4) {memcpy(&IV
[i
], ctr
, 1);}
768 memcpy(&IV
[0], ti
, 4); // For writes TI is MS
771 aes_decode(IV
, kenc
, dati
, dato
, 16);
773 aes_encode(IV
, kenc
, dati
, dato
, 16);
777 static int CmdHFMFPRdbl(const char *Cmd
) {
778 CLIParserContext
*ctx
;
779 CLIParserInit(&ctx
, "hf mfp rdbl",
780 "Reads blocks from MIFARE Plus card",
781 "hf mfp rdbl --blk 0 --key 000102030405060708090a0b0c0d0e0f -> executes authentication and read block 0 data\n"
782 "hf mfp rdbl --blk 1 -v -> executes authentication and shows sector 1 data with default key 0xFF..0xFF");
786 arg_lit0("v", "verbose", "Verbose output"),
787 arg_int0("n", "count", "<dec>", "Blocks count (def: 1)"),
788 arg_lit0("b", "keyb", "Use key B (def: keyA)"),
789 arg_lit0("p", "plain", "Do not use encrypted communication mode between reader and card"),
790 arg_lit0(NULL
, "nmc", "Do not append MAC to command"),
791 arg_lit0(NULL
, "nmr", "Do not expect MAC in reply"),
792 arg_int1(NULL
, "blk", "<0..255>", "Block number"),
793 arg_str0("k", "key", "<hex>", "Key, 16 hex bytes"),
796 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
798 bool verbose
= arg_get_lit(ctx
, 1);
799 int blocksCount
= arg_get_int_def(ctx
, 2, 1);
800 bool keyB
= arg_get_lit(ctx
, 3);
801 bool plain
= arg_get_lit(ctx
, 4);
802 bool nomaccmd
= arg_get_lit(ctx
, 5);
803 bool nomacres
= arg_get_lit(ctx
, 6);
804 uint32_t blockn
= arg_get_int(ctx
, 7);
806 uint8_t keyn
[2] = {0};
807 uint8_t key
[250] = {0};
809 CLIGetHexWithReturn(ctx
, 8, key
, &keylen
);
812 mfpSetVerboseMode(verbose
);
815 memmove(key
, mfp_default_key
, 16);
820 PrintAndLogEx(ERR
, "<block number> must be in range [0..255]. got %d", blockn
);
825 PrintAndLogEx(ERR
, "<key> must be 16 bytes. Got %d", keylen
);
829 // 3 blocks - wo iso14443-4 chaining
830 if (blocksCount
> 3) {
831 PrintAndLogEx(ERR
, "blocks count must be less than 3. Got %d", blocksCount
);
835 if (blocksCount
> 1 && mfIsSectorTrailer(blockn
)) {
836 PrintAndLogEx(WARNING
, "WARNING: trailer!");
839 uint8_t sectorNum
= mfSectorNum(blockn
& 0xff);
840 uint16_t uKeyNum
= 0x4000 + sectorNum
* 2 + (keyB
? 1 : 0);
841 keyn
[0] = uKeyNum
>> 8;
842 keyn
[1] = uKeyNum
& 0xff;
844 PrintAndLogEx(INFO
, "--block:%d sector[%u]:%02x key:%04x", blockn
, mfNumBlocksPerSector(sectorNum
), sectorNum
, uKeyNum
);
846 mf4Session_t mf4session
;
847 int res
= MifareAuth4(&mf4session
, keyn
, key
, true, true, true, verbose
, false);
849 PrintAndLogEx(ERR
, "Authentication error: %d", res
);
853 uint8_t data
[250] = {0};
855 uint8_t mac
[8] = {0};
856 res
= MFPReadBlock(&mf4session
, plain
, nomaccmd
, nomacres
, blockn
& 0xff, blocksCount
, false, false, data
, sizeof(data
), &datalen
, mac
);
858 PrintAndLogEx(ERR
, "Read error: %d", res
);
862 if (datalen
&& data
[0] != 0x90) {
863 PrintAndLogEx(ERR
, "Card read error: %02x %s", data
[0], mfpGetErrorDescription(data
[0]));
866 //PrintAndLogEx(INFO, "%i", 8 && (!macres || 0xff));
867 if (datalen
!= 1 + blocksCount
* 16 + (nomacres
? 0 : 8) + 2) {
868 PrintAndLogEx(ERR
, "Error return length: %d", datalen
);
872 if (!plain
) data_crypt(&mf4session
, &data
[1], &data
[1], true);
873 uint8_t sector
= mfSectorNum(blockn
);
874 mf_print_sector_hdr(sector
);
877 for (int i
= 0; i
< blocksCount
; i
++) {
878 mf_print_block_one(indx
, data
+ 1 + (i
* MFBLOCK_SIZE
), verbose
);
882 if (memcmp(&data
[(blocksCount
* 16) + 1], mac
, 8) && !nomacres
) {
883 PrintAndLogEx(WARNING
, "WARNING: mac not equal...");
884 PrintAndLogEx(WARNING
, "MAC card... " _YELLOW_("%s"), sprint_hex_inrow(&data
[1 + (blocksCount
* MFBLOCK_SIZE
)], 8));
885 PrintAndLogEx(WARNING
, "MAC reader... " _YELLOW_("%s"), sprint_hex_inrow(mac
, sizeof(mac
)));
886 } else if (!nomacres
) {
888 PrintAndLogEx(INFO
, "MAC... " _YELLOW_("%s"), sprint_hex_inrow(&data
[1 + (blocksCount
* MFBLOCK_SIZE
)], 8));
891 PrintAndLogEx(NORMAL
, "");
895 static int CmdHFMFPRdsc(const char *Cmd
) {
896 CLIParserContext
*ctx
;
897 CLIParserInit(&ctx
, "hf mfp rdsc",
898 "Reads one sector from MIFARE Plus card",
899 "hf mfp rdsc -s 0 --key 000102030405060708090a0b0c0d0e0f -> executes authentication and read sector 0 data\n"
900 "hf mfp rdsc -s 1 -v -> executes authentication and shows sector 1 data with default key");
904 arg_lit0("v", "verbose", "Verbose output"),
905 arg_lit0("b", "keyb", "Use key B (def: keyA)"),
906 arg_lit0("p", "plain", "Do not use encrypted communication mode between reader and card"),
907 arg_lit0(NULL
, "nmc", "Do not append MAC to command"),
908 arg_lit0(NULL
, "nmr", "Do not expect MAC in reply"),
909 arg_int1("s", "sn", "<0..255>", "Sector number"),
910 arg_str0("k", "key", "<hex>", "Key, 16 hex bytes"),
913 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
915 bool verbose
= arg_get_lit(ctx
, 1);
916 bool keyB
= arg_get_lit(ctx
, 2);
917 bool plain
= arg_get_lit(ctx
, 3);
918 bool nomaccmd
= arg_get_lit(ctx
, 4);
919 bool nomacres
= arg_get_lit(ctx
, 5);
920 uint32_t sectorNum
= arg_get_int(ctx
, 6);
921 uint8_t keyn
[2] = {0};
922 uint8_t key
[250] = {0};
924 CLIGetHexWithReturn(ctx
, 7, key
, &keylen
);
927 mfpSetVerboseMode(verbose
);
930 memmove(key
, mfp_default_key
, 16);
934 if (sectorNum
> 39) {
935 PrintAndLogEx(ERR
, "<sector number> must be in range [0..39]. Got %d", sectorNum
);
940 PrintAndLogEx(ERR
, "<key> must be 16 bytes. Got %d", keylen
);
944 uint16_t uKeyNum
= 0x4000 + sectorNum
* 2 + (keyB
? 1 : 0);
945 keyn
[0] = uKeyNum
>> 8;
946 keyn
[1] = uKeyNum
& 0xff;
948 PrintAndLogEx(INFO
, "--sector[%u]:%02x key:%04x", mfNumBlocksPerSector(sectorNum
), sectorNum
, uKeyNum
);
950 mf4Session_t mf4session
;
951 int res
= MifareAuth4(&mf4session
, keyn
, key
, true, true, true, verbose
, false);
953 PrintAndLogEx(ERR
, "Authentication error: %d", res
);
957 uint8_t data
[250] = {0};
959 uint8_t mac
[8] = {0};
961 mf_print_sector_hdr(sectorNum
);
963 for (int blockno
= mfFirstBlockOfSector(sectorNum
); blockno
< mfFirstBlockOfSector(sectorNum
) + mfNumBlocksPerSector(sectorNum
); blockno
++) {
965 res
= MFPReadBlock(&mf4session
, plain
, nomaccmd
, nomacres
, blockno
& 0xff, 1, false, true, data
, sizeof(data
), &datalen
, mac
);
967 PrintAndLogEx(ERR
, "Read error: %d", res
);
972 if (datalen
&& data
[0] != 0x90) {
973 PrintAndLogEx(ERR
, "Card read error: %02x %s", data
[0], mfpGetErrorDescription(data
[0]));
978 if (datalen
!= 1 + MFBLOCK_SIZE
+ (nomacres
? 0 : 8) + 2) {
979 PrintAndLogEx(ERR
, "Error return length:%d", datalen
);
983 if (!plain
) data_crypt(&mf4session
, &data
[1], &data
[1], true);
984 mf_print_block_one(blockno
, data
+ 1, verbose
);
986 if (memcmp(&data
[1 + 16], mac
, 8) && !nomacres
) {
987 PrintAndLogEx(WARNING
, "WARNING: mac on block %d not equal...", blockno
);
988 PrintAndLogEx(WARNING
, "MAC card... " _YELLOW_("%s"), sprint_hex_inrow(&data
[1 + MFBLOCK_SIZE
], 8));
989 PrintAndLogEx(WARNING
, "MAC reader... " _YELLOW_("%s"), sprint_hex_inrow(mac
, sizeof(mac
)));
990 } else if (!nomacres
) {
992 PrintAndLogEx(INFO
, "MAC... " _YELLOW_("%s"), sprint_hex_inrow(&data
[1 + MFBLOCK_SIZE
], 8));
996 PrintAndLogEx(NORMAL
, "");
1001 static int CmdHFMFPWrbl(const char *Cmd
) {
1002 CLIParserContext
*ctx
;
1003 CLIParserInit(&ctx
, "hf mfp wrbl",
1004 "Writes one block to MIFARE Plus card",
1005 "hf mfp wrbl --blk 1 -d ff0000000000000000000000000000ff --key 000102030405060708090a0b0c0d0e0f -> write block 1 data\n"
1006 "hf mfp wrbl --blk 2 -d ff0000000000000000000000000000ff -v -> write block 2 data with default key 0xFF..0xFF"
1009 void *argtable
[] = {
1011 arg_lit0("v", "verbose", "Verbose output"),
1012 arg_lit0("b", "keyb", "Use key B (def: keyA)"),
1013 arg_int1(NULL
, "blk", "<0..255>", "Block number"),
1014 arg_lit0("p", "plain", "Do not use encrypted transmission"),
1015 arg_lit0(NULL
, "nmr", "Do not expect MAC in response"),
1016 arg_str1("d", "data", "<hex>", "Data, 16 hex bytes"),
1017 arg_str0("k", "key", "<hex>", "Key, 16 hex bytes"),
1020 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
1022 bool verbose
= arg_get_lit(ctx
, 1);
1023 bool keyB
= arg_get_lit(ctx
, 2);
1024 uint32_t blockNum
= arg_get_int(ctx
, 3);
1025 bool plain
= arg_get_lit(ctx
, 4);
1026 bool nomacres
= arg_get_lit(ctx
, 5);
1028 uint8_t datain
[250] = {0};
1030 CLIGetHexWithReturn(ctx
, 6, datain
, &datainlen
);
1032 uint8_t key
[250] = {0};
1034 CLIGetHexWithReturn(ctx
, 7, key
, &keylen
);
1037 uint8_t keyn
[2] = {0};
1039 mfpSetVerboseMode(verbose
);
1042 memmove(key
, mfp_default_key
, 16);
1046 if (blockNum
> 255) {
1047 PrintAndLogEx(ERR
, "<block number> must be in range [0..255]. Got %d", blockNum
);
1052 PrintAndLogEx(ERR
, "<key> must be 16 bytes. Got %d", keylen
);
1056 if (datainlen
!= 16) {
1057 PrintAndLogEx(ERR
, "<data> must be 16 bytes. Got %d", datainlen
);
1061 uint8_t sectorNum
= mfSectorNum(blockNum
& 0xff);
1062 uint16_t uKeyNum
= 0x4000 + sectorNum
* 2 + (keyB
? 1 : 0);
1063 keyn
[0] = uKeyNum
>> 8;
1064 keyn
[1] = uKeyNum
& 0xff;
1066 PrintAndLogEx(INFO
, "--block:%d sector[%u]:%02x key:%04x", blockNum
& 0xff, mfNumBlocksPerSector(sectorNum
), sectorNum
, uKeyNum
);
1068 mf4Session_t mf4session
;
1069 int res
= MifareAuth4(&mf4session
, keyn
, key
, true, true, true, verbose
, false);
1071 PrintAndLogEx(ERR
, "Authentication error: %d", res
);
1074 if (!plain
) data_crypt(&mf4session
, &datain
[0], &datain
[0], false);
1075 uint8_t data
[250] = {0};
1077 uint8_t mac
[8] = {0};
1078 res
= MFPWriteBlock(&mf4session
, plain
, nomacres
, blockNum
& 0xff, 0x00, datain
, false, false, data
, sizeof(data
), &datalen
, mac
);
1080 PrintAndLogEx(ERR
, "Write error: %d", res
);
1085 if (datalen
!= 3 && (datalen
!= 3 + (nomacres
? 0 : 8))) {
1086 PrintAndLogEx(ERR
, "Error return length:%d", datalen
);
1091 if (datalen
&& data
[0] != 0x90) {
1092 PrintAndLogEx(ERR
, "Card write error: %02x %s", data
[0], mfpGetErrorDescription(data
[0]));
1097 if (memcmp(&data
[1], mac
, 8) && !nomacres
) {
1098 PrintAndLogEx(WARNING
, "WARNING: mac not equal...");
1099 PrintAndLogEx(WARNING
, "MAC card: %s", sprint_hex(&data
[1], 8));
1100 PrintAndLogEx(WARNING
, "MAC reader: %s", sprint_hex(mac
, 8));
1101 } else if (!nomacres
) {
1103 PrintAndLogEx(INFO
, "MAC: %s", sprint_hex(&data
[1], 8));
1107 PrintAndLogEx(INFO
, "Write ( " _GREEN_("ok") " )");
1111 static int CmdHFMFPChKey(const char *Cmd
) {
1112 CLIParserContext
*ctx
;
1113 CLIParserInit(&ctx
, "hf mfp chkey",
1114 "Change the keys on a Mifare Plus tag",
1115 "This requires the key that can update the key that you are trying to update.\n"
1116 "hf mfp chkey --ki 401f -d FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF --key A0A1A2A3A4A5A6A7A0A1A2A3A4A5A6A7 -> Change key B for Sector 15 from MAD to default\n"
1117 "hf mfp chkey --ki 9000 -d 32F9351A1C02B35FF97E0CA943F814F6 --key FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> Change card master key to custom from default"
1120 void *argtable
[] = {
1122 arg_lit0("v", "verbose", "Verbose output"),
1123 arg_lit0(NULL
, "nmr", "Do not expect MAC in response"),
1124 arg_str1(NULL
, "ki", "<hex>", "Key Index, 2 hex bytes"),
1125 arg_str0("k", "key", "<hex>", "Current sector key, 16 hex bytes"),
1126 arg_lit0("b", "typeb", "Sector key is key B"),
1127 arg_str1("d", "data", "<hex>", "New key, 16 hex bytes"),
1130 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
1132 bool verbose
= arg_get_lit(ctx
, 1);
1133 bool nomacres
= arg_get_lit(ctx
, 2);
1135 uint8_t keyn
[250] = {0};
1137 uint8_t ki
[250] = {0};
1139 CLIGetHexWithReturn(ctx
, 3, ki
, &kilen
);
1141 uint8_t key
[250] = {0};
1143 CLIGetHexWithReturn(ctx
, 4, key
, &keylen
);
1145 bool usekeyb
= arg_get_lit(ctx
, 5);
1146 uint8_t datain
[250] = {0};
1148 CLIGetHexWithReturn(ctx
, 6, datain
, &datainlen
);
1152 mfpSetVerboseMode(verbose
);
1155 memmove(key
, mfp_default_key
, 16);
1160 PrintAndLogEx(ERR
, "<key> must be 16 bytes. Got %d", keylen
);
1164 if (datainlen
!= 16) {
1165 PrintAndLogEx(ERR
, "<data> must be 16 bytes. Got %d", datainlen
);
1168 mf4Session_t mf4session
;
1170 if (ki
[0] == 0x40) { // Only if we are working with sector keys
1172 keyn
[1] = (ki
[1] % 2 == 0) ? ki
[1] + 1 : ki
[1]; // If we change using key B, check if KI is key A
1174 keyn
[1] = (ki
[1] % 2 == 0) ? ki
[1] : ki
[1] - 1; // If we change using key A, check if KI is key A
1176 } else {keyn
[1] = ki
[1];}
1178 PrintAndLogEx(INFO
, "--key index:", sprint_hex(keyn
, 2));
1180 int res
= MifareAuth4(&mf4session
, keyn
, key
, true, true, true, verbose
, false);
1182 PrintAndLogEx(ERR
, "Authentication error: %d", res
);
1185 data_crypt(&mf4session
, &datain
[0], &datain
[0], false);
1186 uint8_t data
[250] = {0};
1188 uint8_t mac
[8] = {0};
1189 res
= MFPWriteBlock(&mf4session
, false, nomacres
, ki
[1], ki
[0], datain
, false, false, data
, sizeof(data
), &datalen
, mac
);
1191 PrintAndLogEx(ERR
, "Write error: %d", res
);
1196 if (datalen
!= 3 && (datalen
!= 3 + (nomacres
? 0 : 8))) {
1197 PrintAndLogEx(ERR
, "Error return length:%d", datalen
);
1202 if (datalen
&& data
[0] != 0x90) {
1203 PrintAndLogEx(ERR
, "Card write error: %02x %s", data
[0], mfpGetErrorDescription(data
[0]));
1208 if (memcmp(&data
[1], mac
, 8) && !nomacres
) {
1209 PrintAndLogEx(WARNING
, "WARNING: mac not equal...");
1210 PrintAndLogEx(WARNING
, "MAC card: %s", sprint_hex(&data
[1], 8));
1211 PrintAndLogEx(WARNING
, "MAC reader: %s", sprint_hex(mac
, 8));
1212 } else if (!nomacres
) {
1214 PrintAndLogEx(INFO
, "MAC: %s", sprint_hex(&data
[1], 8));
1218 PrintAndLogEx(INFO
, "Key update ( " _GREEN_("ok") " )");
1222 static int CmdHFMFPChConf(const char *Cmd
) {
1223 CLIParserContext
*ctx
;
1224 CLIParserInit(&ctx
, "hf mfp chconf",
1225 "Change the configuration on a Mifare Plus tag. DANGER!",
1226 "This requires Card Master Key (9000) or Card Configuration Key (9001).\n"
1227 "Configuration block info can be found below.\n"
1228 "* Block B000 (00; CMK): Max amount of commands without MAC (byte 0), as well as plain mode access (unknown).\n"
1229 "* Block B001 (01; CCK): Installation identifier for Virtual Card. Please consult NXP for data.\n"
1230 "* Block B002 (02; CCK): ATS data.\n"
1231 "* Block B003 (03; CCK): Use Random ID in SL3, decide whether proximity check is mandatory.\n * DO NOT WRITE THIS BLOCK UNDER ANY CIRCUMSTANCES! Risk of bricking.\n"
1232 "More configuration tips to follow. Check JMY600 Series IC Card Module.\n"
1233 "hf mfp chconf -c 00 -d 10ffffffffffffffffffffffffffffff --key A0A1A2A3A4A5A6A7A0A1A2A3A4A5A6A7 -> Allow 16 commands without MAC in a single transaction."
1236 void *argtable
[] = {
1238 arg_lit0("v", "verbose", "Verbose output"),
1239 arg_lit0(NULL
, "nmr", "Do not expect MAC in response"),
1240 arg_int1("c", "conf", "<hex>", "Config block number, 0-3"),
1241 arg_str0("k", "key", "<hex>", "Card key, 16 hex bytes"),
1242 arg_lit0(NULL
, "cck", "Auth as Card Configuration key instead of Card Master Key"),
1243 arg_str1("d", "data", "<hex>", "New configuration data, 16 hex bytes"),
1246 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
1248 bool verbose
= arg_get_lit(ctx
, 1);
1249 bool nomacres
= arg_get_lit(ctx
, 2);
1251 uint8_t keyn
[250] = {0};
1252 uint32_t blockNum
= arg_get_int(ctx
, 3);
1254 uint8_t key
[250] = {0};
1256 CLIGetHexWithReturn(ctx
, 4, key
, &keylen
);
1257 bool usecck
= arg_get_lit(ctx
, 5);
1259 uint8_t datain
[250] = {0};
1261 CLIGetHexWithReturn(ctx
, 6, datain
, &datainlen
);
1265 mfpSetVerboseMode(verbose
);
1268 memmove(key
, mfp_default_key
, 16);
1273 PrintAndLogEx(ERR
, "<key> must be 16 bytes. Got %d", keylen
);
1277 if (datainlen
!= 16) {
1278 PrintAndLogEx(ERR
, "<data> must be 16 bytes. Got %d", datainlen
);
1283 PrintAndLogEx(ERR
, "<config number> must be in range [0..3]. Got %d", blockNum
);
1286 mf4Session_t mf4session
;
1288 keyn
[1] = usecck
? 0x01 : 0x00;
1290 PrintAndLogEx(INFO
, "--key index:", sprint_hex(keyn
, 2));
1292 int res
= MifareAuth4(&mf4session
, keyn
, key
, true, true, true, verbose
, false);
1294 PrintAndLogEx(ERR
, "Authentication error: %d", res
);
1297 data_crypt(&mf4session
, &datain
[0], &datain
[0], false);
1298 uint8_t data
[250] = {0};
1300 uint8_t mac
[8] = {0};
1301 res
= MFPWriteBlock(&mf4session
, false, nomacres
, blockNum
& 0xff, 0xb0, datain
, false, false, data
, sizeof(data
), &datalen
, mac
);
1303 PrintAndLogEx(ERR
, "Write error: %d", res
);
1308 if (datalen
!= 3 && (datalen
!= 3 + (nomacres
? 0 : 8))) {
1309 PrintAndLogEx(ERR
, "Error return length:%d", datalen
);
1314 if (datalen
&& data
[0] != 0x90) {
1315 PrintAndLogEx(ERR
, "Card write error: %02x %s", data
[0], mfpGetErrorDescription(data
[0]));
1320 if (memcmp(&data
[1], mac
, 8) && !nomacres
) {
1321 PrintAndLogEx(WARNING
, "WARNING: mac not equal...");
1322 PrintAndLogEx(WARNING
, "MAC card: %s", sprint_hex(&data
[1], 8));
1323 PrintAndLogEx(WARNING
, "MAC reader: %s", sprint_hex(mac
, 8));
1324 } else if (!nomacres
) {
1326 PrintAndLogEx(INFO
, "MAC: %s", sprint_hex(&data
[1], 8));
1330 PrintAndLogEx(INFO
, "Write config ( " _GREEN_("ok") " )");
1334 static int plus_key_check(uint8_t startSector
, uint8_t endSector
, uint8_t startKeyAB
, uint8_t endKeyAB
,
1335 uint8_t keyList
[MAX_AES_KEYS_LIST_LEN
][AES_KEY_LEN
], size_t keyListLen
, uint8_t foundKeys
[2][64][AES_KEY_LEN
+ 1],
1338 bool selectCard
= true;
1339 uint8_t keyn
[2] = {0};
1341 // sector number from 0
1342 for (uint8_t sector
= startSector
; sector
<= endSector
; sector
++) {
1344 for (uint8_t keyAB
= startKeyAB
; keyAB
<= endKeyAB
; keyAB
++) {
1345 // main cycle with key check
1346 for (int i
= 0; i
< keyListLen
; i
++) {
1348 // allow client abort every iteration
1349 if (kbd_enter_pressed()) {
1350 PrintAndLogEx(WARNING
, "\naborted via keyboard!\n");
1352 return PM3_EOPABORTED
;
1356 if (verbose
== false) {
1357 PrintAndLogEx(NORMAL
, "." NOLF
);
1361 uint16_t uKeyNum
= 0x4000 + sector
* 2 + keyAB
;
1362 keyn
[0] = uKeyNum
>> 8;
1363 keyn
[1] = uKeyNum
& 0xff;
1365 for (int retry
= 0; retry
< 4; retry
++) {
1366 res
= MifareAuth4(NULL
, keyn
, keyList
[i
], selectCard
, true, false, false, true);
1367 if (res
== PM3_SUCCESS
|| res
== PM3_EWRONGANSWER
)
1371 PrintAndLogEx(WARNING
, "\nretried[%d]...", retry
);
1373 PrintAndLogEx(NORMAL
, "R" NOLF
);
1380 // key for [sector,keyAB] found
1381 if (res
== PM3_SUCCESS
) {
1383 PrintAndLogEx(INFO
, "\nFound key for sector %d key %s [%s]", sector
, keyAB
== 0 ? "A" : "B", sprint_hex_inrow(keyList
[i
], 16));
1385 PrintAndLogEx(NORMAL
, "+" NOLF
);
1387 foundKeys
[keyAB
][sector
][0] = 0x01;
1388 memcpy(&foundKeys
[keyAB
][sector
][1], keyList
[i
], AES_KEY_LEN
);
1393 // break out from keylist check loop,
1398 PrintAndLogEx(WARNING
, "\nsector %02d key %d [%s] res: %d", sector
, keyAB
, sprint_hex_inrow(keyList
[i
], 16), res
);
1402 // PM3_EWRONGANSWER -16
1403 if (res
== PM3_ERFTRANS
) {
1405 PrintAndLogEx(ERR
, "\nExchange error. Aborted.");
1407 PrintAndLogEx(NORMAL
, "E" NOLF
);
1410 return PM3_ECARDEXCHANGE
;
1422 static void Fill2bPattern(uint8_t keyList
[MAX_AES_KEYS_LIST_LEN
][AES_KEY_LEN
], uint32_t *keyListLen
, uint32_t *startPattern
) {
1423 for (uint32_t pt
= *startPattern
; pt
< 0x10000; pt
++) {
1424 keyList
[*keyListLen
][0] = (pt
>> 8) & 0xff;
1425 keyList
[*keyListLen
][1] = pt
& 0xff;
1426 memcpy(&keyList
[*keyListLen
][2], &keyList
[*keyListLen
][0], 2);
1427 memcpy(&keyList
[*keyListLen
][4], &keyList
[*keyListLen
][0], 4);
1428 memcpy(&keyList
[*keyListLen
][8], &keyList
[*keyListLen
][0], 8);
1431 if (*keyListLen
== MAX_AES_KEYS_LIST_LEN
)
1437 static int CmdHFMFPChk(const char *Cmd
) {
1439 CLIParserContext
*ctx
;
1440 CLIParserInit(&ctx
, "hf mfp chk",
1441 "Checks keys on MIFARE Plus card",
1442 "hf mfp chk -k 000102030405060708090a0b0c0d0e0f -> check key on sector 0 as key A and B\n"
1443 "hf mfp chk -s 2 -a -> check default key list on sector 2, only key A\n"
1444 "hf mfp chk -d mfp_default_keys -s0 -e6 -> check keys from dictionary against sectors 0-6\n"
1445 "hf mfp chk --pattern1b --dump -> check all 1-byte keys pattern and save found keys to file\n"
1446 "hf mfp chk --pattern2b --startp2b FA00 -> check all 2-byte keys pattern. Start from key FA00FA00...FA00");
1448 void *argtable
[] = {
1450 arg_lit0("a", "keya", "Check only key A (def: check all keys)"),
1451 arg_lit0("b", "keyb", "Check only key B (def: check all keys)"),
1452 arg_int0("s", "startsec", "<0..255>", "Start sector number"),
1453 arg_int0("e", "endsec", "<0..255>", "End sector number"),
1454 arg_str0("k", "key", "<hex>", "Key for checking (HEX 16 bytes)"),
1455 arg_str0("d", "dict", "<fn>", "Dictionary file with keys"),
1456 arg_lit0(NULL
, "pattern1b", "Check all 1-byte combinations of key (0000...0000, 0101...0101, 0202...0202, ...)"),
1457 arg_lit0(NULL
, "pattern2b", "Check all 2-byte combinations of key (0000...0000, 0001...0001, 0002...0002, ...)"),
1458 arg_str0(NULL
, "startp2b", "<pattern>", "Start key (2-byte HEX) for 2-byte search (use with `--pattern2b`)"),
1459 arg_lit0(NULL
, "dump", "Dump found keys to JSON file"),
1460 arg_lit0("v", "verbose", "Verbose output"),
1463 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1465 bool keyA
= arg_get_lit(ctx
, 1);
1466 bool keyB
= arg_get_lit(ctx
, 2);
1467 uint8_t startSector
= arg_get_int_def(ctx
, 3, 0);
1468 uint8_t endSector
= arg_get_int_def(ctx
, 4, 0);
1470 uint8_t keyList
[MAX_AES_KEYS_LIST_LEN
][AES_KEY_LEN
] = {{0}};
1471 uint32_t keyListLen
= 0;
1472 uint8_t foundKeys
[2][64][AES_KEY_LEN
+ 1] = {{{0}}};
1474 uint8_t vkey
[16] = {0};
1476 CLIGetHexWithReturn(ctx
, 5, vkey
, &vkeylen
);
1478 if (vkeylen
== 16) {
1479 memcpy(&keyList
[keyListLen
], vkey
, 16);
1482 PrintAndLogEx(ERR
, "Specified key must have 16 bytes. Got %d", vkeylen
);
1488 uint8_t dict_filename
[FILE_PATH_SIZE
+ 2] = {0};
1489 int dict_filenamelen
= 0;
1490 if (CLIParamStrToBuf(arg_get_str(ctx
, 6), dict_filename
, FILE_PATH_SIZE
, &dict_filenamelen
)) {
1491 PrintAndLogEx(FAILED
, "File name too long or invalid.");
1496 bool pattern1b
= arg_get_lit(ctx
, 7);
1497 bool pattern2b
= arg_get_lit(ctx
, 8);
1499 if (pattern1b
&& pattern2b
) {
1500 PrintAndLogEx(ERR
, "Pattern search mode must be 2-byte or 1-byte only.");
1505 if (dict_filenamelen
&& (pattern1b
|| pattern2b
)) {
1506 PrintAndLogEx(ERR
, "Pattern search mode and dictionary mode can't be used in one command.");
1511 uint32_t startPattern
= 0x0000;
1512 uint8_t vpattern
[2];
1513 int vpatternlen
= 0;
1514 CLIGetHexWithReturn(ctx
, 9, vpattern
, &vpatternlen
);
1515 if (vpatternlen
> 0) {
1516 if (vpatternlen
<= 2) {
1517 startPattern
= (vpattern
[0] << 8) + vpattern
[1];
1519 PrintAndLogEx(ERR
, "Pattern must be 2-bytes. Got %d", vpatternlen
);
1524 PrintAndLogEx(WARNING
, "Pattern entered, but search mode not is 2-byte search.");
1527 bool create_dumpfile
= arg_get_lit(ctx
, 10);
1528 bool verbose
= arg_get_lit(ctx
, 11);
1531 uint8_t startKeyAB
= 0;
1532 uint8_t endKeyAB
= 1;
1533 if (keyA
&& (keyB
== false))
1536 if ((keyA
== false) && keyB
)
1539 if (endSector
< startSector
)
1540 endSector
= startSector
;
1542 // 1-byte pattern search mode
1544 for (int i
= 0; i
< 0x100; i
++) {
1545 memset(keyList
[i
], i
, 16);
1551 // 2-byte pattern search mode
1553 Fill2bPattern(keyList
, &keyListLen
, &startPattern
);
1556 int res
= PM3_SUCCESS
;
1559 size_t endFilePosition
= 0;
1560 if (dict_filenamelen
) {
1561 uint32_t keycnt
= 0;
1562 res
= loadFileDICTIONARYEx((char *)dict_filename
, keyList
, sizeof(keyList
), NULL
, 16, &keycnt
, 0, &endFilePosition
, true);
1564 if (res
== PM3_SUCCESS
&& endFilePosition
) {
1565 keyListLen
= keycnt
;
1566 PrintAndLogEx(SUCCESS
, "First part of dictionary successfully loaded.");
1570 if (keyListLen
== 0) {
1571 for (int i
= 0; i
< g_mifare_plus_default_keys_len
; i
++) {
1572 if (hex_to_bytes(g_mifare_plus_default_keys
[i
], keyList
[keyListLen
], 16) != 16) {
1580 if (keyListLen
== 0) {
1581 PrintAndLogEx(ERR
, "Key list is empty. Nothing to check.");
1584 PrintAndLogEx(INFO
, "Loaded " _YELLOW_("%"PRIu32
) " keys", keyListLen
);
1587 if (verbose
== false) {
1588 PrintAndLogEx(INFO
, "Search keys");
1592 res
= plus_key_check(startSector
, endSector
, startKeyAB
, endKeyAB
, keyList
, keyListLen
, foundKeys
, verbose
);
1593 if (res
== PM3_EOPABORTED
) {
1597 if (pattern2b
&& startPattern
< 0x10000) {
1598 if (verbose
== false) {
1599 PrintAndLogEx(NORMAL
, "p" NOLF
);
1603 Fill2bPattern(keyList
, &keyListLen
, &startPattern
);
1607 if (dict_filenamelen
&& endFilePosition
) {
1608 if (verbose
== false)
1609 PrintAndLogEx(NORMAL
, "d" NOLF
);
1611 uint32_t keycnt
= 0;
1612 res
= loadFileDICTIONARYEx((char *)dict_filename
, keyList
, sizeof(keyList
), NULL
, 16, &keycnt
, endFilePosition
, &endFilePosition
, false);
1613 if (res
== PM3_SUCCESS
&& endFilePosition
) {
1614 keyListLen
= keycnt
;
1622 if (verbose
== false) {
1623 PrintAndLogEx(NORMAL
, "");
1627 char strA
[46 + 1] = {0};
1628 char strB
[46 + 1] = {0};
1630 uint8_t ndef_key
[] = {0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7};
1631 bool has_ndef_key
= false;
1632 bool printedHeader
= false;
1633 for (uint8_t s
= startSector
; s
<= endSector
; s
++) {
1635 if ((memcmp(&foundKeys
[0][s
][1], ndef_key
, AES_KEY_LEN
) == 0) ||
1636 (memcmp(&foundKeys
[1][s
][1], ndef_key
, AES_KEY_LEN
) == 0)) {
1637 has_ndef_key
= true;
1640 if (printedHeader
== false) {
1641 PrintAndLogEx(NORMAL
, "");
1642 PrintAndLogEx(INFO
, "-----+----------------------------------+----------------------------------");
1643 PrintAndLogEx(INFO
, " Sec | key A | key B");
1644 PrintAndLogEx(INFO
, "-----+----------------------------------+----------------------------------");
1645 printedHeader
= true;
1648 if (foundKeys
[0][s
][0]) {
1649 snprintf(strA
, sizeof(strA
), _GREEN_("%s"), sprint_hex_inrow(&foundKeys
[0][s
][1], AES_KEY_LEN
));
1651 snprintf(strA
, sizeof(strA
), _RED_("%s"), "--------------------------------");
1654 if (foundKeys
[1][s
][0]) {
1655 snprintf(strB
, sizeof(strB
), _GREEN_("%s"), sprint_hex_inrow(&foundKeys
[1][s
][1], AES_KEY_LEN
));
1657 snprintf(strB
, sizeof(strB
), _RED_("%s"), "--------------------------------");
1660 PrintAndLogEx(INFO
, " " _YELLOW_("%03d") " | %s | %s", s
, strA
, strB
);
1663 if (printedHeader
== false)
1664 PrintAndLogEx(INFO
, "No keys found(");
1666 PrintAndLogEx(INFO
, "-----+----------------------------------+----------------------------------\n");
1668 // save keys to json
1669 if (create_dumpfile
&& printedHeader
) {
1671 size_t keys_len
= (2 * 64 * (AES_KEY_LEN
+ 1));
1673 uint8_t data
[10 + 1 + 2 + 1 + 256 + keys_len
];
1674 memset(data
, 0, sizeof(data
));
1677 SendCommandMIX(CMD_HF_ISO14443A_READER
, ISO14A_CONNECT
, 0, 0, NULL
, 0);
1679 PacketResponseNG resp
;
1680 if (WaitForResponseTimeout(CMD_ACK
, &resp
, 2500) == false) {
1681 PrintAndLogEx(WARNING
, "timeout while waiting for reply.");
1682 return PM3_ETIMEOUT
;
1685 iso14a_card_select_t card
;
1686 memcpy(&card
, (iso14a_card_select_t
*)resp
.data
.asBytes
, sizeof(iso14a_card_select_t
));
1688 uint64_t select_status
= resp
.oldarg
[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision
1690 if (select_status
== 1 || select_status
== 2) {
1691 memcpy(data
, card
.uid
, card
.uidlen
);
1692 data
[10] = card
.sak
;
1693 data
[11] = card
.atqa
[1];
1694 data
[12] = card
.atqa
[0];
1695 atslen
= card
.ats_len
;
1697 memcpy(&data
[14], card
.ats
, atslen
);
1700 char *fptr
= calloc(sizeof(char) * (strlen("hf-mfp-") + strlen("-key")) + card
.uidlen
* 2 + 1, sizeof(uint8_t));
1701 strcpy(fptr
, "hf-mfp-");
1703 FillFileNameByUID(fptr
, card
.uid
, "-key", card
.uidlen
);
1705 // length: UID(10b)+SAK(1b)+ATQA(2b)+ATSlen(1b)+ATS(atslen)+foundKeys[2][64][AES_KEY_LEN + 1]
1706 memcpy(&data
[14 + atslen
], foundKeys
, keys_len
);
1707 // 64 here is for how many "rows" there is in the data array. A bit confusing
1708 saveFileJSON(fptr
, jsfMfPlusKeys
, data
, 64, NULL
);
1713 if ((memcmp(&foundKeys
[0][0][1], "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7", AES_KEY_LEN
) == 0)) {
1714 PrintAndLogEx(HINT
, "MAD key detected. Try " _YELLOW_("`hf mfp mad`") " for more details");
1719 PrintAndLogEx(HINT
, "NDEF key detected. Try " _YELLOW_("`hf mfp ndefread -h`") " for more details");
1721 PrintAndLogEx(NORMAL
, "");
1725 static int CmdHFMFPDump(const char *Cmd
) {
1726 CLIParserContext
*ctx
;
1727 CLIParserInit(&ctx
, "hf mfp dump",
1728 "Dump MIFARE Plus tag to file (bin/json)\n"
1729 "If no <name> given, UID will be used as filename",
1731 "hf mfp dump --keys hf-mf-066C8B78-key.bin --> MIFARE Plus with keys from specified file\n");
1733 void *argtable
[] = {
1735 arg_str0("f", "file", "<fn>", "Specify a filename for dump file"),
1736 arg_str0("k", "keys", "<fn>", "Specify a filename for keys file"),
1737 // arg_lit0(NULL, "ns", "no save to file"),
1738 // arg_lit0("v", "verbose", "Verbose output"),
1741 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1744 char data_fn
[FILE_PATH_SIZE
] = {0};
1745 CLIParamStrToBuf(arg_get_str(ctx
, 1), (uint8_t *)data_fn
, FILE_PATH_SIZE
, &datafnlen
);
1748 char key_fn
[FILE_PATH_SIZE
] = {0};
1749 CLIParamStrToBuf(arg_get_str(ctx
, 2), (uint8_t *)key_fn
, FILE_PATH_SIZE
, &keyfnlen
);
1751 // bool nosave = arg_get_lit(ctx, 3);
1752 // bool verbose = arg_get_lit(ctx, 4);
1755 PrintAndLogEx(INFO
, " To be implemented, feel free to contribute!");
1756 return PM3_ENOTIMPL
;
1759 mfpSetVerboseMode(verbose);
1762 uint8_t *mem = calloc(MIFARE_4K_MAXBLOCK * MFBLOCK_SIZE, sizeof(uint8_t));
1764 PrintAndLogEx(ERR, "failed to allocate memory");
1769 // iso14a_card_select_t card ;
1770 // int res = mfp_read_tag(&card, mem, key_fn);
1771 // if (res != PM3_SUCCESS) {
1777 // Skip saving card data to file
1779 PrintAndLogEx(INFO, "Called with no save option");
1785 // if (strlen(data_fn) < 1) {
1786 // char *fptr = calloc(sizeof(char) * (strlen("hf-mfp-") + strlen("-dump")) + card.uidlen * 2 + 1, sizeof(uint8_t));
1787 // strcpy(fptr, "hf-mfp-");
1788 // FillFileNameByUID(fptr, card.uid, "-dump", card.uidlen);
1789 // strcpy(data_fn, fptr);
1793 // pm3_save_mf_dump(filename, dump, MIFARE_4K_MAX_BYTES, jsfCardMemory);
1800 static int CmdHFMFPMAD(const char *Cmd
) {
1802 CLIParserContext
*ctx
;
1803 CLIParserInit(&ctx
, "hf mfp mad",
1804 "Checks and prints MIFARE Application Directory (MAD)",
1806 "hf mfp mad --aid e103 -k d3f7d3f7d3f7d3f7d3f7d3f7d3f7d3f7 -> read and print NDEF data from MAD aid");
1808 void *argtable
[] = {
1810 arg_lit0("v", "verbose", "Verbose output"),
1811 arg_str0(NULL
, "aid", "<hex>", "Print all sectors with aid"),
1812 arg_str0("k", "key", "<hex>", "Key for printing sectors"),
1813 arg_lit0("b", "keyb", "Use key B for access printing sectors (def: key A)"),
1814 arg_lit0(NULL
, "be", "(optional: BigEndian)"),
1815 arg_lit0(NULL
, "dch", "Decode Card Holder information"),
1818 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1820 bool verbose
= arg_get_lit(ctx
, 1);
1821 uint8_t aid
[2] = {0};
1823 CLIGetHexWithReturn(ctx
, 2, aid
, &aidlen
);
1824 uint8_t key
[16] = {0};
1826 CLIGetHexWithReturn(ctx
, 3, key
, &keylen
);
1827 bool keyB
= arg_get_lit(ctx
, 4);
1828 bool swapmad
= arg_get_lit(ctx
, 5);
1829 bool decodeholder
= arg_get_lit(ctx
, 6);
1833 if (aidlen
!= 2 && !decodeholder
&& keylen
> 0) {
1834 PrintAndLogEx(WARNING
, "Using default MAD keys instead");
1837 uint8_t sector0
[16 * 4] = {0};
1838 uint8_t sector10
[16 * 4] = {0};
1840 if (mfpReadSector(MF_MAD1_SECTOR
, MF_KEY_A
, (uint8_t *)g_mifarep_mad_key
, sector0
, verbose
)) {
1841 PrintAndLogEx(NORMAL
, "");
1842 PrintAndLogEx(ERR
, "error, read sector 0. card doesn't have MAD or doesn't have MAD on default keys");
1849 PrintAndLogEx(SUCCESS
, "Raw:");
1850 for (int i
= 0; i
< 4; i
++)
1851 PrintAndLogEx(INFO
, "[%d] %s", i
, sprint_hex(§or0
[i
* 16], 16));
1854 bool haveMAD2
= false;
1855 MAD1DecodeAndPrint(sector0
, swapmad
, verbose
, &haveMAD2
);
1858 if (mfpReadSector(MF_MAD2_SECTOR
, MF_KEY_A
, (uint8_t *)g_mifarep_mad_key
, sector10
, verbose
)) {
1859 PrintAndLogEx(NORMAL
, "");
1860 PrintAndLogEx(ERR
, "error, read sector " _YELLOW_("0x10") ". Card doesn't have MAD or doesn't have MAD on default keys");
1864 MAD2DecodeAndPrint(sector10
, swapmad
, verbose
);
1867 if (aidlen
== 2 || decodeholder
) {
1868 uint16_t mad
[7 + 8 + 8 + 8 + 8] = {0};
1870 if (MADDecode(sector0
, sector10
, mad
, &madlen
, swapmad
)) {
1871 PrintAndLogEx(ERR
, "can't decode MAD");
1872 return PM3_EWRONGANSWER
;
1875 // copy default NDEF key
1876 uint8_t akey
[16] = {0};
1877 memcpy(akey
, g_mifarep_ndef_key
, 16);
1879 // user specified key
1881 memcpy(akey
, key
, 16);
1884 uint16_t aaid
= 0x0004;
1886 aaid
= (aid
[0] << 8) + aid
[1];
1887 PrintAndLogEx(NORMAL
, "");
1888 PrintAndLogEx(INFO
, "-------------- " _CYAN_("AID 0x%04x") " ---------------", aaid
);
1890 for (int i
= 0; i
< madlen
; i
++) {
1891 if (aaid
== mad
[i
]) {
1892 uint8_t vsector
[16 * 4] = {0};
1893 if (mfpReadSector(i
+ 1, keyB
? MF_KEY_B
: MF_KEY_A
, akey
, vsector
, false)) {
1894 PrintAndLogEx(NORMAL
, "");
1895 PrintAndLogEx(ERR
, "error, read sector %d error", i
+ 1);
1899 for (int j
= 0; j
< (verbose
? 4 : 3); j
++)
1900 PrintAndLogEx(NORMAL
, " [%03d] %s", (i
+ 1) * 4 + j
, sprint_hex(&vsector
[j
* 16], 16));
1907 PrintAndLogEx(NORMAL
, "");
1908 PrintAndLogEx(INFO
, "-------- " _CYAN_("Card Holder Info 0x%04x") " --------", aaid
);
1910 uint8_t data
[4096] = {0};
1913 for (int i
= 0; i
< madlen
; i
++) {
1914 if (aaid
== mad
[i
]) {
1916 uint8_t vsector
[16 * 4] = {0};
1917 if (mfReadSector(i
+ 1, keyB
? MF_KEY_B
: MF_KEY_A
, akey
, vsector
)) {
1918 PrintAndLogEx(NORMAL
, "");
1919 PrintAndLogEx(ERR
, "error, read sector %d", i
+ 1);
1923 memcpy(&data
[datalen
], vsector
, 16 * 3);
1929 PrintAndLogEx(WARNING
, "no Card Holder Info data");
1932 MADCardHolderInfoDecode(data
, datalen
, verbose
);
1938 static int CmdHFMFPNDEFFormat(const char *Cmd
) {
1939 CLIParserContext
*ctx
;
1940 CLIParserInit(&ctx
, "hf mfp ndefformat",
1941 "format MIFARE Plus Tag as a NFC tag with Data Exchange Format (NDEF)\n"
1942 "If no <name> given, UID will be used as filename. \n"
1943 "It will try default keys and MAD keys to detect if tag is already formatted in order to write.\n"
1945 "If not, it will try finding a key file based on your UID. ie, if you ran autopwn before",
1946 "hf mfp ndefformat\n"
1947 "hf mfp ndefformat --keys hf-mf-01020304-key.bin --> with keys from specified file\n"
1950 void *argtable
[] = {
1952 arg_str0("k", "keys", "<fn>", "filename of keys"),
1955 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1958 char keyFilename
[FILE_PATH_SIZE
] = {0};
1959 CLIParamStrToBuf(arg_get_str(ctx
, 1), (uint8_t *)keyFilename
, FILE_PATH_SIZE
, &keyfnlen
);
1963 PrintAndLogEx(SUCCESS
, "Not implemented yet. Feel free to contribute!");
1964 PrintAndLogEx(NORMAL
, "");
1968 int CmdHFMFPNDEFRead(const char *Cmd
) {
1970 CLIParserContext
*ctx
;
1971 CLIParserInit(&ctx
, "hf mfp ndefread",
1972 "Prints NFC Data Exchange Format (NDEF)",
1973 "hf mfp ndefread \n"
1974 "hf mfp ndefread -vv -> shows NDEF parsed and raw data\n"
1975 "hf mfp ndefread --aid e103 -k d3f7d3f7d3f7d3f7d3f7d3f7d3f7d3f7 -> shows NDEF data with custom AID and key\n"
1976 "hf mfp ndefread -f myfilename -> save raw NDEF to file"
1979 void *argtable
[] = {
1981 arg_litn("v", "verbose", 0, 2, "verbose output"),
1982 arg_str0(NULL
, "aid", "<aid>", "replace default aid for NDEF"),
1983 arg_str0("k", "key", "<key>", "replace default key for NDEF"),
1984 arg_lit0("b", "keyb", "use key B for access sectors (by default: key A)"),
1985 arg_str0("f", "file", "<fn>", "save raw NDEF to file"),
1988 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1990 bool verbose
= arg_get_lit(ctx
, 1);
1991 bool verbose2
= arg_get_lit(ctx
, 1) > 1;
1992 uint8_t aid
[2] = {0};
1994 CLIGetHexWithReturn(ctx
, 2, aid
, &aidlen
);
1995 uint8_t key
[16] = {0};
1997 CLIGetHexWithReturn(ctx
, 3, key
, &keylen
);
1998 bool keyB
= arg_get_lit(ctx
, 4);
2001 char filename
[FILE_PATH_SIZE
] = {0};
2002 CLIParamStrToBuf(arg_get_str(ctx
, 5), (uint8_t *)filename
, FILE_PATH_SIZE
, &fnlen
);
2005 uint16_t ndefAID
= 0xe103;
2007 ndefAID
= (aid
[0] << 8) + aid
[1];
2009 uint8_t ndefkey
[16] = {0};
2010 memcpy(ndefkey
, g_mifarep_ndef_key
, 16);
2012 memcpy(ndefkey
, key
, 16);
2015 uint8_t sector0
[16 * 4] = {0};
2016 uint8_t sector10
[16 * 4] = {0};
2017 uint8_t data
[4096] = {0};
2021 PrintAndLogEx(INFO
, "reading MAD v1 sector");
2023 if (mfpReadSector(MF_MAD1_SECTOR
, MF_KEY_A
, (uint8_t *)g_mifarep_mad_key
, sector0
, verbose
)) {
2024 PrintAndLogEx(ERR
, "error, read sector 0. card doesn't have MAD or doesn't have MAD on default keys");
2025 PrintAndLogEx(HINT
, "Try " _YELLOW_("`hf mfp ndefread -k `") " with your custom key");
2029 bool haveMAD2
= false;
2030 int res
= MADCheck(sector0
, NULL
, verbose
, &haveMAD2
);
2031 if (res
!= PM3_SUCCESS
) {
2032 PrintAndLogEx(ERR
, "MAD error %d", res
);
2039 PrintAndLogEx(INFO
, "reading MAD v2 sector");
2041 if (mfpReadSector(MF_MAD2_SECTOR
, MF_KEY_A
, (uint8_t *)g_mifarep_mad_key
, sector10
, verbose
)) {
2042 PrintAndLogEx(ERR
, "error, read sector 0x10. card doesn't have MAD or doesn't have MAD on default keys");
2043 PrintAndLogEx(HINT
, "Try " _YELLOW_("`hf mfp ndefread -k `") " with your custom key");
2048 uint16_t mad
[7 + 8 + 8 + 8 + 8] = {0};
2050 res
= MADDecode(sector0
, (haveMAD2
? sector10
: NULL
), mad
, &madlen
, false);
2051 if (res
!= PM3_SUCCESS
) {
2052 PrintAndLogEx(ERR
, "can't decode MAD");
2056 PrintAndLogEx(INFO
, "reading data from tag");
2057 for (int i
= 0; i
< madlen
; i
++) {
2058 if (ndefAID
== mad
[i
]) {
2059 uint8_t vsector
[16 * 4] = {0};
2060 if (mfpReadSector(i
+ 1, keyB
? MF_KEY_B
: MF_KEY_A
, ndefkey
, vsector
, false)) {
2061 PrintAndLogEx(ERR
, "error, reading sector %d", i
+ 1);
2065 memcpy(&data
[datalen
], vsector
, 16 * 3);
2068 PrintAndLogEx(INPLACE
, "%d", i
);
2071 PrintAndLogEx(NORMAL
, "");
2074 PrintAndLogEx(ERR
, "no NDEF data");
2079 PrintAndLogEx(NORMAL
, "");
2080 PrintAndLogEx(INFO
, "--- " _CYAN_("MF Plus NDEF raw") " ----------------");
2081 print_buffer(data
, datalen
, 1);
2084 res
= NDEFDecodeAndPrint(data
, datalen
, verbose
);
2085 if (res
!= PM3_SUCCESS
) {
2086 PrintAndLogEx(INFO
, "Trying to parse NDEF records w/o NDEF header");
2087 res
= NDEFRecordsDecodeAndPrint(data
, datalen
, verbose
);
2090 // get total NDEF length before save. If fails, we save it all
2092 if (NDEFGetTotalLength(data
, datalen
, &n
) != PM3_SUCCESS
)
2095 pm3_save_dump(filename
, data
, n
, jsfNDEF
);
2097 if (verbose
== false) {
2098 PrintAndLogEx(HINT
, "Try " _YELLOW_("`hf mfp ndefread -v`") " for more details");
2100 if (verbose2
== false) {
2101 PrintAndLogEx(HINT
, "Try " _YELLOW_("`hf mfp ndefread -vv`") " for more details");
2107 static int CmdHFMFPNDEFWrite(const char *Cmd
) {
2108 CLIParserContext
*ctx
;
2109 CLIParserInit(&ctx
, "hf mfp ndefwrite",
2110 "Write raw NDEF hex bytes to tag. This commands assumes tag already been NFC/NDEF formatted.\n",
2111 "hf mfp ndefwrite -d 0300FE -> write empty record to tag\n"
2112 "hf mfp ndefwrite -f myfilename\n"
2113 "hf mfp ndefwrite -d 033fd1023a53709101195405656e2d55534963656d616e2054776974746572206c696e6b5101195502747769747465722e636f6d2f686572726d616e6e31303031\n"
2116 void *argtable
[] = {
2118 arg_str0("d", NULL
, "<hex>", "raw NDEF hex bytes"),
2119 arg_str0("f", "file", "<fn>", "write raw NDEF file to tag"),
2120 arg_lit0("p", NULL
, "fix NDEF record headers / terminator block if missing"),
2121 arg_lit0("v", "verbose", "verbose output"),
2124 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
2126 uint8_t raw
[4096] = {0};
2128 CLIGetHexWithReturn(ctx
, 1, raw
, &rawlen
);
2131 char filename
[FILE_PATH_SIZE
] = {0};
2132 CLIParamStrToBuf(arg_get_str(ctx
, 2), (uint8_t *)filename
, FILE_PATH_SIZE
, &fnlen
);
2134 bool fix_msg
= arg_get_lit(ctx
, 3);
2135 bool verbose
= arg_get_lit(ctx
, 4);
2139 PrintAndLogEx(NORMAL
, "called with fix NDEF message param");
2143 PrintAndLogEx(NORMAL
, "");
2145 PrintAndLogEx(SUCCESS
, "Not implemented yet. Feel free to contribute!");
2146 PrintAndLogEx(NORMAL
, "");
2150 static int CmdHFMFPList(const char *Cmd
) {
2151 return CmdTraceListAlias(Cmd
, "hf mfp", "mfp -c");
2154 static command_t CommandTable
[] = {
2155 {"help", CmdHelp
, AlwaysAvailable
, "This help"},
2156 {"list", CmdHFMFPList
, AlwaysAvailable
, "List MIFARE Plus history"},
2157 {"-----------", CmdHelp
, IfPm3Iso14443a
, "------------------- " _CYAN_("operations") " ---------------------"},
2158 {"auth", CmdHFMFPAuth
, IfPm3Iso14443a
, "Authentication"},
2159 {"chk", CmdHFMFPChk
, IfPm3Iso14443a
, "Check keys"},
2160 {"dump", CmdHFMFPDump
, IfPm3Iso14443a
, "Dump MIFARE Plus tag to binary file"},
2161 {"info", CmdHFMFPInfo
, IfPm3Iso14443a
, "Info about MIFARE Plus tag"},
2162 {"mad", CmdHFMFPMAD
, IfPm3Iso14443a
, "Check and print MAD"},
2163 {"rdbl", CmdHFMFPRdbl
, IfPm3Iso14443a
, "Read blocks from card"},
2164 {"rdsc", CmdHFMFPRdsc
, IfPm3Iso14443a
, "Read sectors from card"},
2165 {"wrbl", CmdHFMFPWrbl
, IfPm3Iso14443a
, "Write block to card"},
2166 {"chkey", CmdHFMFPChKey
, IfPm3Iso14443a
, "Change key on card"},
2167 {"chconf", CmdHFMFPChConf
, IfPm3Iso14443a
, "Change config on card"},
2168 {"-----------", CmdHelp
, IfPm3Iso14443a
, "---------------- " _CYAN_("personalization") " -------------------"},
2169 {"commitp", CmdHFMFPCommitPerso
, IfPm3Iso14443a
, "Configure security layer (SL1/SL3 mode)"},
2170 {"initp", CmdHFMFPInitPerso
, IfPm3Iso14443a
, "Fill all the card's keys in SL0 mode"},
2171 {"wrp", CmdHFMFPWritePerso
, IfPm3Iso14443a
, "Write Perso command"},
2172 {"-----------", CmdHelp
, IfPm3Iso14443a
, "---------------------- " _CYAN_("ndef") " ------------------------"},
2173 {"ndefformat", CmdHFMFPNDEFFormat
, IfPm3Iso14443a
, "Format MIFARE Plus Tag as NFC Tag"},
2174 {"ndefread", CmdHFMFPNDEFRead
, IfPm3Iso14443a
, "Read and print NDEF records from card"},
2175 {"ndefwrite", CmdHFMFPNDEFWrite
, IfPm3Iso14443a
, "Write NDEF records to card"},
2176 {NULL
, NULL
, 0, NULL
}
2179 static int CmdHelp(const char *Cmd
) {
2180 (void)Cmd
; // Cmd is not used so far
2181 CmdsHelp(CommandTable
);
2185 int CmdHFMFP(const char *Cmd
) {
2186 clearCommandBuffer();
2187 return CmdsParse(CommandTable
, Cmd
);