1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2018 Merlok
4 // This code is licensed to you under the terms of the GNU GPL, version 2 or,
5 // at your option, any later version. See the LICENSE.txt file for the text of
7 //-----------------------------------------------------------------------------
8 // High frequency MIFARE Plus commands
9 //-----------------------------------------------------------------------------
25 #include "cliparser/cliparser.h"
26 #include "polarssl/libpcrypto.h"
28 static const uint8_t DefaultKey
[16] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
32 const char *Description
;
35 static const PlusErrorsElm PlusErrors
[] = {
37 {0x00, "Unknown error"},
38 {0x06, "Block use error"},
39 {0x07, "Command use error"},
40 {0x08, "Invalid write command"},
41 {0x09, "Invalid block number"},
42 {0x0b, "Command code error"},
43 {0x0c, "Length error"},
46 int PlusErrorsLen
= sizeof(PlusErrors
) / sizeof(PlusErrorsElm
);
48 const char * GetErrorDescription(uint8_t errorCode
) {
49 for(int i
= 0; i
< PlusErrorsLen
; i
++)
50 if (errorCode
== PlusErrors
[i
].Code
)
51 return PlusErrors
[i
].Description
;
53 return PlusErrors
[0].Description
;
56 static int CmdHelp(const char *Cmd
);
58 static bool VerboseMode
= false;
59 void SetVerboseMode(bool verbose
) {
60 VerboseMode
= verbose
;
63 int intExchangeRAW14aPlus(uint8_t *datain
, int datainlen
, bool activateField
, bool leaveSignalON
, uint8_t *dataout
, int maxdataoutlen
, int *dataoutlen
) {
65 PrintAndLog(">>> %s", sprint_hex(datain
, datainlen
));
67 int res
= ExchangeRAW14a(datain
, datainlen
, activateField
, leaveSignalON
, dataout
, maxdataoutlen
, dataoutlen
);
70 PrintAndLog("<<< %s", sprint_hex(dataout
, *dataoutlen
));
75 int MFPWritePerso(uint8_t *keyNum
, uint8_t *key
, bool activateField
, bool leaveSignalON
, uint8_t *dataout
, int maxdataoutlen
, int *dataoutlen
) {
76 uint8_t rcmd
[3 + 16] = {0xa8, keyNum
[1], keyNum
[0], 0x00};
77 memmove(&rcmd
[3], key
, 16);
79 return intExchangeRAW14aPlus(rcmd
, sizeof(rcmd
), activateField
, leaveSignalON
, dataout
, maxdataoutlen
, dataoutlen
);
82 int MFPCommitPerso(bool activateField
, bool leaveSignalON
, uint8_t *dataout
, int maxdataoutlen
, int *dataoutlen
) {
83 uint8_t rcmd
[1] = {0xaa};
85 return intExchangeRAW14aPlus(rcmd
, sizeof(rcmd
), activateField
, leaveSignalON
, dataout
, maxdataoutlen
, dataoutlen
);
88 int MFPReadBlock(mf4Session
*session
, bool plain
, uint8_t blockNum
, uint8_t blockCount
, bool activateField
, bool leaveSignalON
, uint8_t *dataout
, int maxdataoutlen
, int *dataoutlen
, uint8_t *mac
) {
89 uint8_t rcmd
[4 + 8] = {(plain
?(0x37):(0x33)), blockNum
, 0x00, blockCount
};
90 if (!plain
&& session
)
91 CalculateMAC(session
, rcmd
, 4, &rcmd
[4], VerboseMode
);
93 int res
= intExchangeRAW14aPlus(rcmd
, plain
?4:sizeof(rcmd
), activateField
, leaveSignalON
, dataout
, maxdataoutlen
, dataoutlen
);
98 CalculateMAC(session
, dataout
, *dataoutlen
, mac
, VerboseMode
);
103 int MFPWriteBlock(mf4Session
*session
, uint8_t blockNum
, uint8_t *data
, bool activateField
, bool leaveSignalON
, uint8_t *dataout
, int maxdataoutlen
, int *dataoutlen
, uint8_t *mac
) {
104 uint8_t rcmd
[1 + 2 + 16 + 8] = {0xA3, blockNum
, 0x00};
105 memmove(&rcmd
[3], data
, 16);
107 CalculateMAC(session
, rcmd
, 19, &rcmd
[19], VerboseMode
);
109 int res
= intExchangeRAW14aPlus(rcmd
, sizeof(rcmd
), activateField
, leaveSignalON
, dataout
, maxdataoutlen
, dataoutlen
);
114 CalculateMAC(session
, dataout
, *dataoutlen
, mac
, VerboseMode
);
119 int CmdHFMFPInfo(const char *cmd
) {
121 if (cmd
&& strlen(cmd
) > 0)
122 PrintAndLog("WARNING: command don't have any parameters.\n");
124 // info about 14a part
128 UsbCommand c
= {CMD_READER_ISO_14443a
, {ISO14A_CONNECT
| ISO14A_NO_DISCONNECT
, 0, 0}};
132 WaitForResponse(CMD_ACK
,&resp
);
134 iso14a_card_select_t card
;
135 memcpy(&card
, (iso14a_card_select_t
*)resp
.d
.asBytes
, sizeof(iso14a_card_select_t
));
137 uint64_t select_status
= resp
.arg
[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision
139 if (select_status
== 1 || select_status
== 2) {
140 PrintAndLog("----------------------------------------------");
141 PrintAndLog("Mifare Plus info:");
143 // MIFARE Type Identification Procedure
144 // https://www.nxp.com/docs/en/application-note/AN10833.pdf
145 uint16_t ATQA
= card
.atqa
[0] + (card
.atqa
[1] << 8);
146 if (ATQA
== 0x0004) PrintAndLog("ATQA: Mifare Plus 2k 4bUID");
147 if (ATQA
== 0x0002) PrintAndLog("ATQA: Mifare Plus 4k 4bUID");
148 if (ATQA
== 0x0044) PrintAndLog("ATQA: Mifare Plus 2k 7bUID");
149 if (ATQA
== 0x0042) PrintAndLog("ATQA: Mifare Plus 4k 7bUID");
151 uint8_t SLmode
= 0xff;
152 if (card
.sak
== 0x08) {
153 PrintAndLog("SAK: Mifare Plus 2k 7bUID");
154 if (select_status
== 2) SLmode
= 1;
156 if (card
.sak
== 0x18) {
157 PrintAndLog("SAK: Mifare Plus 4k 7bUID");
158 if (select_status
== 2) SLmode
= 1;
160 if (card
.sak
== 0x10) {
161 PrintAndLog("SAK: Mifare Plus 2k");
162 if (select_status
== 2) SLmode
= 2;
164 if (card
.sak
== 0x11) {
165 PrintAndLog("SAK: Mifare Plus 4k");
166 if (select_status
== 2) SLmode
= 2;
168 if (card
.sak
== 0x20) {
169 PrintAndLog("SAK: Mifare Plus SL0/SL3 or Mifare desfire");
170 if (card
.ats_len
> 0) {
174 uint8_t data
[250] = {0};
176 // https://github.com/Proxmark/proxmark3/blob/master/client/scripts/mifarePlus.lua#L161
177 uint8_t cmd
[3 + 16] = {0xa8, 0x90, 0x90, 0x00};
178 int res
= ExchangeRAW14a(cmd
, sizeof(cmd
), false, false, data
, sizeof(data
), &datalen
);
179 if (!res
&& datalen
> 1 && data
[0] == 0x09) {
186 PrintAndLog("Mifare Plus SL mode: SL%d", SLmode
);
188 PrintAndLog("Mifare Plus SL mode: unknown(");
190 PrintAndLog("Mifare Plus info not available.");
198 int CmdHFMFPWritePerso(const char *cmd
) {
199 uint8_t keyNum
[64] = {0};
201 uint8_t key
[64] = {0};
204 CLIParserInit("hf mfp wrp",
205 "Executes Write Perso command. Can be used in SL0 mode only.",
206 "Usage:\n\thf mfp wrp 4000 000102030405060708090a0b0c0d0e0f -> write key (00..0f) to key number 4000 \n"
207 "\thf mfp wrp 4000 -> write default key(0xff..0xff) to key number 4000");
211 arg_lit0("vV", "verbose", "show internal data."),
212 arg_str1(NULL
, NULL
, "<HEX key number (2b)>", NULL
),
213 arg_strx0(NULL
, NULL
, "<HEX key (16b)>", NULL
),
216 CLIExecWithReturn(cmd
, argtable
, true);
218 bool verbose
= arg_get_lit(1);
219 CLIGetHexWithReturn(2, keyNum
, &keyNumLen
);
220 CLIGetHexWithReturn(3, key
, &keyLen
);
223 SetVerboseMode(verbose
);
226 memmove(key
, DefaultKey
, 16);
230 if (keyNumLen
!= 2) {
231 PrintAndLog("Key number length must be 2 bytes instead of: %d", keyNumLen
);
235 PrintAndLog("Key length must be 16 bytes instead of: %d", keyLen
);
239 uint8_t data
[250] = {0};
242 int res
= MFPWritePerso(keyNum
, key
, true, false, data
, sizeof(data
), &datalen
);
244 PrintAndLog("Exchange error: %d", res
);
249 PrintAndLog("Command must return 3 bytes instead of: %d", datalen
);
253 if (data
[0] != 0x90) {
254 PrintAndLog("Command error: %02x %s", data
[0], GetErrorDescription(data
[0]));
257 PrintAndLog("Write OK.");
262 uint16_t CardAddresses
[] = {0x9000, 0x9001, 0x9002, 0x9003, 0x9004, 0xA000, 0xA001, 0xA080, 0xA081, 0xC000, 0xC001};
264 int CmdHFMFPInitPerso(const char *cmd
) {
266 uint8_t key
[256] = {0};
268 uint8_t keyNum
[2] = {0};
269 uint8_t data
[250] = {0};
272 CLIParserInit("hf mfp initp",
273 "Executes Write Perso command for all card's keys. Can be used in SL0 mode only.",
274 "Usage:\n\thf mfp initp 000102030405060708090a0b0c0d0e0f -> fill all the keys with key (00..0f)\n"
275 "\thf mfp initp -vv -> fill all the keys with default key(0xff..0xff) and show all the data exchange");
279 arg_litn("vV", "verbose", 0, 2, "show internal data."),
280 arg_strx0(NULL
, NULL
, "<HEX key (16b)>", NULL
),
283 CLIExecWithReturn(cmd
, argtable
, true);
285 bool verbose
= arg_get_lit(1);
286 bool verbose2
= arg_get_lit(1) > 1;
287 CLIGetHexWithReturn(2, key
, &keyLen
);
290 if (keyLen
&& keyLen
!= 16) {
291 PrintAndLog("Key length must be 16 bytes instead of: %d", keyLen
);
296 memmove(key
, DefaultKey
, 16);
298 SetVerboseMode(verbose2
);
299 for (uint16_t sn
= 0x4000; sn
< 0x4050; sn
++) {
301 keyNum
[1] = sn
& 0xff;
302 res
= MFPWritePerso(keyNum
, key
, (sn
== 0x4000), true, data
, sizeof(data
), &datalen
);
303 if (!res
&& (datalen
== 3) && data
[0] == 0x09) {
304 PrintAndLog("2k card detected.");
307 if (res
|| (datalen
!= 3) || data
[0] != 0x90) {
308 PrintAndLog("Write error on address %04x", sn
);
313 SetVerboseMode(verbose
);
314 for (int i
= 0; i
< sizeof(CardAddresses
) / 2; i
++) {
315 keyNum
[0] = CardAddresses
[i
] >> 8;
316 keyNum
[1] = CardAddresses
[i
] & 0xff;
317 res
= MFPWritePerso(keyNum
, key
, false, true, data
, sizeof(data
), &datalen
);
318 if (!res
&& (datalen
== 3) && data
[0] == 0x09) {
319 PrintAndLog("Skipped[%04x]...", CardAddresses
[i
]);
321 if (res
|| (datalen
!= 3) || data
[0] != 0x90) {
322 PrintAndLog("Write error on address %04x", CardAddresses
[i
]);
333 PrintAndLog("Done.");
338 int CmdHFMFPCommitPerso(const char *cmd
) {
339 CLIParserInit("hf mfp commitp",
340 "Executes Commit Perso command. Can be used in SL0 mode only.",
341 "Usage:\n\thf mfp commitp -> \n");
345 arg_lit0("vV", "verbose", "show internal data."),
346 arg_int0(NULL
, NULL
, "SL mode", NULL
),
349 CLIExecWithReturn(cmd
, argtable
, true);
351 bool verbose
= arg_get_lit(1);
354 SetVerboseMode(verbose
);
356 uint8_t data
[250] = {0};
359 int res
= MFPCommitPerso(true, false, data
, sizeof(data
), &datalen
);
361 PrintAndLog("Exchange error: %d", res
);
366 PrintAndLog("Command must return 3 bytes instead of: %d", datalen
);
370 if (data
[0] != 0x90) {
371 PrintAndLog("Command error: %02x %s", data
[0], GetErrorDescription(data
[0]));
374 PrintAndLog("Switch level OK.");
379 int CmdHFMFPAuth(const char *cmd
) {
380 uint8_t keyn
[250] = {0};
382 uint8_t key
[250] = {0};
385 CLIParserInit("hf mfp auth",
386 "Executes AES authentication command for Mifare Plus card",
387 "Usage:\n\thf mfp auth 4000 000102030405060708090a0b0c0d0e0f -> executes authentication\n"
388 "\thf mfp auth 9003 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -v -> executes authentication and shows all the system data\n");
392 arg_lit0("vV", "verbose", "show internal data."),
393 arg_str1(NULL
, NULL
, "<Key Num (HEX 2 bytes)>", NULL
),
394 arg_str1(NULL
, NULL
, "<Key Value (HEX 16 bytes)>", NULL
),
397 CLIExecWithReturn(cmd
, argtable
, true);
399 bool verbose
= arg_get_lit(1);
400 CLIGetHexWithReturn(2, keyn
, &keynlen
);
401 CLIGetHexWithReturn(3, key
, &keylen
);
405 PrintAndLog("ERROR: <Key Num> must be 2 bytes long instead of: %d", keynlen
);
410 PrintAndLog("ERROR: <Key Value> must be 16 bytes long instead of: %d", keylen
);
414 return MifareAuth4(NULL
, keyn
, key
, true, false, verbose
);
417 int CmdHFMFPRdbl(const char *cmd
) {
418 uint8_t keyn
[2] = {0};
419 uint8_t key
[250] = {0};
422 CLIParserInit("hf mfp rdbl",
423 "Reads several blocks from Mifare Plus card in plain mode.",
424 "Usage:\n\thf mfp rdbl 0 000102030405060708090a0b0c0d0e0f -> executes authentication and read block 0 data\n"
425 "\thf mfp rdbl 1 -v -> executes authentication and shows sector 1 data with default key 0xFF..0xFF and some additional data\n");
429 arg_lit0("vV", "verbose", "show internal data."),
430 arg_int0("nN", "count", "blocks count (by default 1).", NULL
),
431 arg_lit0("bB", "keyb", "use key B (by default keyA)."),
432 arg_lit0("pP", "plain", "plain communication between reader and card."),
433 arg_int1(NULL
, NULL
, "<Block Num (0..255)>", NULL
),
434 arg_str0(NULL
, NULL
, "<Key Value (HEX 16 bytes)>", NULL
),
437 CLIExecWithReturn(cmd
, argtable
, false);
439 bool verbose
= arg_get_lit(1);
440 int blocksCount
= arg_get_int_def(2, 1);
441 bool keyB
= arg_get_lit(3);
442 int plain
= arg_get_lit(4) | true;
443 uint32_t blockn
= arg_get_int(5);
444 CLIGetHexWithReturn(6, key
, &keylen
);
447 SetVerboseMode(verbose
);
450 memmove(key
, DefaultKey
, 16);
455 PrintAndLog("ERROR: <Block Num> must be in range [0..255] instead of: %d", blockn
);
460 PrintAndLog("ERROR: <Key Value> must be 16 bytes long instead of: %d", keylen
);
464 // 3 blocks - wo iso14443-4 chaining
465 if (blocksCount
> 3) {
466 PrintAndLog("ERROR: blocks count must be less than 3 instead of: %d", blocksCount
);
470 uint8_t sectorNum
= mfSectorNum(blockn
& 0xff);
471 uint16_t uKeyNum
= 0x4000 + sectorNum
* 2 + (keyB
? 1 : 0);
472 keyn
[0] = uKeyNum
>> 8;
473 keyn
[1] = uKeyNum
& 0xff;
475 PrintAndLog("--block:%d sector[%d]:%02x key:%04x", blockn
, mfNumBlocksPerSector(sectorNum
), sectorNum
, uKeyNum
);
478 int res
= MifareAuth4(&session
, keyn
, key
, true, true, verbose
);
480 PrintAndLog("Authentication error: %d", res
);
484 uint8_t data
[250] = {0};
486 uint8_t mac
[8] = {0};
487 res
= MFPReadBlock(&session
, plain
, blockn
& 0xff, blocksCount
, false, false, data
, sizeof(data
), &datalen
, mac
);
489 PrintAndLog("Read error: %d", res
);
493 if (datalen
&& data
[0] != 0x90) {
494 PrintAndLog("Card read error: %02x %s", data
[0], GetErrorDescription(data
[0]));
498 if (datalen
!= 1 + blocksCount
* 16 + 8 + 2) {
499 PrintAndLog("Error return length:%d", datalen
);
504 for(int i
= 0; i
< blocksCount
; i
++) {
505 PrintAndLog("data[%03d]: %s", indx
, sprint_hex(&data
[1 + i
* 16], 16));
507 if (mfIsSectorTrailer(indx
)){
508 PrintAndLog("data[%03d]: ------------------- trailer -------------------", indx
);
513 if (!memcmp(&data
[blocksCount
* 16 + 1], mac
, 8)) {
514 PrintAndLog("WARNING: mac not equal...");
515 PrintAndLog("MAC card: %s", sprint_hex(&data
[blocksCount
* 16 + 1], 8));
516 PrintAndLog("MAC reader: %s", sprint_hex(mac
, 8));
519 PrintAndLog("MAC: %s", sprint_hex(&data
[blocksCount
* 16 + 1], 8));
525 int CmdHFMFPRdsc(const char *cmd
) {
526 uint8_t keyn
[2] = {0};
527 uint8_t key
[250] = {0};
530 CLIParserInit("hf mfp rdsc",
531 "Reads one sector from Mifare Plus card in plain mode.",
532 "Usage:\n\thf mfp rdsc 0 000102030405060708090a0b0c0d0e0f -> executes authentication and read sector 0 data\n"
533 "\thf mfp rdsc 1 -v -> executes authentication and shows sector 1 data with default key 0xFF..0xFF and some additional data\n");
537 arg_lit0("vV", "verbose", "show internal data."),
538 arg_lit0("bB", "keyb", "use key B (by default keyA)."),
539 arg_lit0("pP", "plain", "plain communication between reader and card."),
540 arg_int1(NULL
, NULL
, "<Sector Num (0..255)>", NULL
),
541 arg_str0(NULL
, NULL
, "<Key Value (HEX 16 bytes)>", NULL
),
544 CLIExecWithReturn(cmd
, argtable
, false);
546 bool verbose
= arg_get_lit(1);
547 bool keyB
= arg_get_lit(2);
548 bool plain
= arg_get_lit(3) | true;
549 uint32_t sectorNum
= arg_get_int(4);
550 CLIGetHexWithReturn(5, key
, &keylen
);
553 SetVerboseMode(verbose
);
556 memmove(key
, DefaultKey
, 16);
560 if (sectorNum
> 39) {
561 PrintAndLog("ERROR: <Sector Num> must be in range [0..39] instead of: %d", sectorNum
);
566 PrintAndLog("ERROR: <Key Value> must be 16 bytes long instead of: %d", keylen
);
570 uint16_t uKeyNum
= 0x4000 + sectorNum
* 2 + (keyB
? 1 : 0);
571 keyn
[0] = uKeyNum
>> 8;
572 keyn
[1] = uKeyNum
& 0xff;
574 PrintAndLog("--sector[%d]:%02x key:%04x", mfNumBlocksPerSector(sectorNum
), sectorNum
, uKeyNum
);
577 int res
= MifareAuth4(&session
, keyn
, key
, true, true, verbose
);
579 PrintAndLog("Authentication error: %d", res
);
583 uint8_t data
[250] = {0};
585 uint8_t mac
[8] = {0};
586 for(int n
= mfFirstBlockOfSector(sectorNum
); n
< mfFirstBlockOfSector(sectorNum
) + mfNumBlocksPerSector(sectorNum
); n
++) {
587 res
= MFPReadBlock(&session
, plain
, n
& 0xff, 1, false, true, data
, sizeof(data
), &datalen
, mac
);
589 PrintAndLog("Read error: %d", res
);
594 if (datalen
&& data
[0] != 0x90) {
595 PrintAndLog("Card read error: %02x %s", data
[0], GetErrorDescription(data
[0]));
599 if (datalen
!= 1 + 16 + 8 + 2) {
600 PrintAndLog("Error return length:%d", datalen
);
605 PrintAndLog("data[%03d]: %s", n
, sprint_hex(&data
[1], 16));
607 if (!memcmp(&data
[1 + 16], mac
, 8)) {
608 PrintAndLog("WARNING: mac on block %d not equal...", n
);
609 PrintAndLog("MAC card: %s", sprint_hex(&data
[1 + 16], 8));
610 PrintAndLog("MAC reader: %s", sprint_hex(mac
, 8));
613 PrintAndLog("MAC: %s", sprint_hex(&data
[1 + 16], 8));
621 int CmdHFMFPWrbl(const char *cmd
) {
622 uint8_t keyn
[2] = {0};
623 uint8_t key
[250] = {0};
625 uint8_t datain
[250] = {0};
628 CLIParserInit("hf mfp wrbl",
629 "Writes one block to Mifare Plus card.",
630 "Usage:\n\thf mfp wrbl 1 ff0000000000000000000000000000ff 000102030405060708090a0b0c0d0e0f -> writes block 1 data\n"
631 "\thf mfp wrbl 2 ff0000000000000000000000000000ff -v -> writes block 2 data with default key 0xFF..0xFF and some additional data\n");
635 arg_lit0("vV", "verbose", "show internal data."),
636 arg_lit0("bB", "keyb", "use key B (by default keyA)."),
637 arg_int1(NULL
, NULL
, "<Block Num (0..255)>", NULL
),
638 arg_str1(NULL
, NULL
, "<Data (HEX 16 bytes)>", NULL
),
639 arg_str0(NULL
, NULL
, "<Key (HEX 16 bytes)>", NULL
),
642 CLIExecWithReturn(cmd
, argtable
, false);
644 bool verbose
= arg_get_lit(1);
645 bool keyB
= arg_get_lit(2);
646 uint32_t blockNum
= arg_get_int(3);
647 CLIGetHexWithReturn(4, datain
, &datainlen
);
648 CLIGetHexWithReturn(5, key
, &keylen
);
651 SetVerboseMode(verbose
);
654 memmove(key
, DefaultKey
, 16);
659 PrintAndLog("ERROR: <Block Num> must be in range [0..255] instead of: %d", blockNum
);
664 PrintAndLog("ERROR: <Key> must be 16 bytes long instead of: %d", keylen
);
668 if (datainlen
!= 16) {
669 PrintAndLog("ERROR: <Data> must be 16 bytes long instead of: %d", datainlen
);
673 uint8_t sectorNum
= mfSectorNum(blockNum
& 0xff);
674 uint16_t uKeyNum
= 0x4000 + sectorNum
* 2 + (keyB
? 1 : 0);
675 keyn
[0] = uKeyNum
>> 8;
676 keyn
[1] = uKeyNum
& 0xff;
678 PrintAndLog("--block:%d sector[%d]:%02x key:%04x", blockNum
& 0xff, mfNumBlocksPerSector(sectorNum
), sectorNum
, uKeyNum
);
681 int res
= MifareAuth4(&session
, keyn
, key
, true, true, verbose
);
683 PrintAndLog("Authentication error: %d", res
);
687 uint8_t data
[250] = {0};
689 uint8_t mac
[8] = {0};
690 res
= MFPWriteBlock(&session
, blockNum
& 0xff, datain
, false, false, data
, sizeof(data
), &datalen
, mac
);
692 PrintAndLog("Write error: %d", res
);
697 if (datalen
!= 3 && (datalen
!= 3 + 8)) {
698 PrintAndLog("Error return length:%d", datalen
);
703 if (datalen
&& data
[0] != 0x90) {
704 PrintAndLog("Card write error: %02x %s", data
[0], GetErrorDescription(data
[0]));
709 if (!memcmp(&data
[1], mac
, 8)) {
710 PrintAndLog("WARNING: mac not equal...");
711 PrintAndLog("MAC card: %s", sprint_hex(&data
[1], 8));
712 PrintAndLog("MAC reader: %s", sprint_hex(mac
, 8));
715 PrintAndLog("MAC: %s", sprint_hex(&data
[1], 8));
722 static command_t CommandTable
[] =
724 {"help", CmdHelp
, 1, "This help"},
725 {"info", CmdHFMFPInfo
, 0, "Info about Mifare Plus tag"},
726 {"wrp", CmdHFMFPWritePerso
, 0, "Write Perso command"},
727 {"initp", CmdHFMFPInitPerso
, 0, "Fills all the card's keys"},
728 {"commitp", CmdHFMFPCommitPerso
, 0, "Move card to SL1 or SL3 mode"},
729 {"auth", CmdHFMFPAuth
, 0, "Authentication"},
730 {"rdbl", CmdHFMFPRdbl
, 0, "Read blocks"},
731 {"rdsc", CmdHFMFPRdsc
, 0, "Read sectors"},
732 // {"wrbl", CmdHFMFPWrbl, 0, "Write blocks"},
733 {NULL
, NULL
, 0, NULL
}
736 int CmdHFMFP(const char *Cmd
) {
737 (void)WaitForResponseTimeout(CMD_ACK
,NULL
,100);
738 CmdsParse(CommandTable
, Cmd
);
742 int CmdHelp(const char *Cmd
) {
743 CmdsHelp(CommandTable
);