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 {0x09, "Invalid block number"},
39 {0x0b, "Command code error"},
40 {0x0c, "Length error"},
43 int PlusErrorsLen
= sizeof(PlusErrors
) / sizeof(PlusErrorsElm
);
45 const char * GetErrorDescription(uint8_t errorCode
) {
46 for(int i
= 0; i
< PlusErrorsLen
; i
++)
47 if (errorCode
== PlusErrors
[i
].Code
)
48 return PlusErrors
[i
].Description
;
50 return PlusErrors
[0].Description
;
53 static int CmdHelp(const char *Cmd
);
55 static bool VerboseMode
= false;
56 void SetVerboseMode(bool verbose
) {
57 VerboseMode
= verbose
;
60 int intExchangeRAW14aPlus(uint8_t *datain
, int datainlen
, bool activateField
, bool leaveSignalON
, uint8_t *dataout
, int maxdataoutlen
, int *dataoutlen
) {
62 PrintAndLog(">>> %s", sprint_hex(datain
, datainlen
));
64 int res
= ExchangeRAW14a(datain
, datainlen
, activateField
, leaveSignalON
, dataout
, maxdataoutlen
, dataoutlen
);
67 PrintAndLog("<<< %s", sprint_hex(dataout
, *dataoutlen
));
72 int MFPWritePerso(uint8_t *keyNum
, uint8_t *key
, bool activateField
, bool leaveSignalON
, uint8_t *dataout
, int maxdataoutlen
, int *dataoutlen
) {
73 uint8_t rcmd
[3 + 16] = {0xa8, keyNum
[1], keyNum
[0], 0x00};
74 memmove(&rcmd
[3], key
, 16);
76 return intExchangeRAW14aPlus(rcmd
, sizeof(rcmd
), activateField
, leaveSignalON
, dataout
, maxdataoutlen
, dataoutlen
);
79 int MFPCommitPerso(bool activateField
, bool leaveSignalON
, uint8_t *dataout
, int maxdataoutlen
, int *dataoutlen
) {
80 uint8_t rcmd
[1] = {0xaa};
82 return intExchangeRAW14aPlus(rcmd
, sizeof(rcmd
), activateField
, leaveSignalON
, dataout
, maxdataoutlen
, dataoutlen
);
85 int CmdHFMFPInfo(const char *cmd
) {
87 if (cmd
&& strlen(cmd
) > 0)
88 PrintAndLog("WARNING: command don't have any parameters.\n");
90 // info about 14a part
94 UsbCommand c
= {CMD_READER_ISO_14443a
, {ISO14A_CONNECT
| ISO14A_NO_DISCONNECT
, 0, 0}};
98 WaitForResponse(CMD_ACK
,&resp
);
100 iso14a_card_select_t card
;
101 memcpy(&card
, (iso14a_card_select_t
*)resp
.d
.asBytes
, sizeof(iso14a_card_select_t
));
103 uint64_t select_status
= resp
.arg
[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision
105 if (select_status
== 1 || select_status
== 2) {
106 PrintAndLog("----------------------------------------------");
107 PrintAndLog("Mifare Plus info:");
109 // MIFARE Type Identification Procedure
110 // https://www.nxp.com/docs/en/application-note/AN10833.pdf
111 uint16_t ATQA
= card
.atqa
[0] + (card
.atqa
[1] << 8);
112 if (ATQA
== 0x0004) PrintAndLog("ATQA: Mifare Plus 2k 4bUID");
113 if (ATQA
== 0x0002) PrintAndLog("ATQA: Mifare Plus 4k 4bUID");
114 if (ATQA
== 0x0044) PrintAndLog("ATQA: Mifare Plus 2k 7bUID");
115 if (ATQA
== 0x0042) PrintAndLog("ATQA: Mifare Plus 4k 7bUID");
117 uint8_t SLmode
= 0xff;
118 if (card
.sak
== 0x08) {
119 PrintAndLog("SAK: Mifare Plus 2k 7bUID");
120 if (select_status
== 2) SLmode
= 1;
122 if (card
.sak
== 0x18) {
123 PrintAndLog("SAK: Mifare Plus 4k 7bUID");
124 if (select_status
== 2) SLmode
= 1;
126 if (card
.sak
== 0x10) {
127 PrintAndLog("SAK: Mifare Plus 2k");
128 if (select_status
== 2) SLmode
= 2;
130 if (card
.sak
== 0x11) {
131 PrintAndLog("SAK: Mifare Plus 4k");
132 if (select_status
== 2) SLmode
= 2;
134 if (card
.sak
== 0x20) {
135 PrintAndLog("SAK: Mifare Plus SL0/SL3 or Mifare desfire");
136 if (card
.ats_len
> 0) {
140 uint8_t data
[250] = {0};
142 // https://github.com/Proxmark/proxmark3/blob/master/client/scripts/mifarePlus.lua#L161
143 uint8_t cmd
[3 + 16] = {0xa8, 0x90, 0x90, 0x00};
144 int res
= ExchangeRAW14a(cmd
, sizeof(cmd
), false, false, data
, sizeof(data
), &datalen
);
145 if (!res
&& datalen
> 1 && data
[0] == 0x09) {
152 PrintAndLog("Mifare Plus SL mode: SL%d", SLmode
);
154 PrintAndLog("Mifare Plus SL mode: unknown(");
156 PrintAndLog("Mifare Plus info not available.");
164 int CmdHFMFPWritePerso(const char *cmd
) {
165 uint8_t keyNum
[64] = {0};
167 uint8_t key
[64] = {0};
170 CLIParserInit("hf mfp wrp",
171 "Executes Write Perso command. Can be used in SL0 mode only.",
172 "Usage:\n\thf mfp wrp 4000 000102030405060708090a0b0c0d0e0f -> write key (00..0f) to key number 4000 \n"
173 "\thf mfp wrp 4000 -> write default key(0xff..0xff) to key number 4000");
177 arg_lit0("vV", "verbose", "show internal data."),
178 arg_str1(NULL
, NULL
, "<HEX key number (2b)>", NULL
),
179 arg_strx0(NULL
, NULL
, "<HEX key (16b)>", NULL
),
182 CLIExecWithReturn(cmd
, argtable
, true);
184 bool verbose
= arg_get_lit(1);
185 CLIGetHexWithReturn(2, keyNum
, &keyNumLen
);
186 CLIGetHexWithReturn(3, key
, &keyLen
);
189 SetVerboseMode(verbose
);
192 memmove(key
, DefaultKey
, 16);
196 if (keyNumLen
!= 2) {
197 PrintAndLog("Key number length must be 2 bytes instead of: %d", keyNumLen
);
201 PrintAndLog("Key length must be 16 bytes instead of: %d", keyLen
);
205 uint8_t data
[250] = {0};
208 int res
= MFPWritePerso(keyNum
, key
, true, false, data
, sizeof(data
), &datalen
);
210 PrintAndLog("Exchange error: %d", res
);
215 PrintAndLog("Command must return 3 bytes instead of: %d", datalen
);
219 if (data
[0] != 0x90) {
220 PrintAndLog("Command error: %02x %s", data
[0], GetErrorDescription(data
[0]));
223 PrintAndLog("Write OK.");
228 uint16_t CardAddresses
[] = {0x9000, 0x9001, 0x9002, 0x9003, 0x9004, 0xA000, 0xA001, 0xA080, 0xA081, 0xC000, 0xC001};
230 int CmdHFMFPInitPerso(const char *cmd
) {
232 uint8_t key
[256] = {0};
234 uint8_t keyNum
[2] = {0};
235 uint8_t data
[250] = {0};
238 CLIParserInit("hf mfp initp",
239 "Executes Write Perso command for all card's keys. Can be used in SL0 mode only.",
240 "Usage:\n\thf mfp initp 000102030405060708090a0b0c0d0e0f -> fill all the keys with key (00..0f)\n"
241 "\thf mfp initp -vv -> fill all the keys with default key(0xff..0xff) and show all the data exchange");
245 arg_litn("vV", "verbose", 0, 2, "show internal data."),
246 arg_strx0(NULL
, NULL
, "<HEX key (16b)>", NULL
),
249 CLIExecWithReturn(cmd
, argtable
, true);
251 bool verbose
= arg_get_lit(1);
252 bool verbose2
= arg_get_lit(1) > 1;
253 CLIGetHexWithReturn(2, key
, &keyLen
);
256 if (keyLen
&& keyLen
!= 16) {
257 PrintAndLog("Key length must be 16 bytes instead of: %d", keyLen
);
262 memmove(key
, DefaultKey
, 16);
264 SetVerboseMode(verbose2
);
265 for (uint16_t sn
= 0x4000; sn
< 0x4050; sn
++) {
267 keyNum
[1] = sn
& 0xff;
268 res
= MFPWritePerso(keyNum
, key
, (sn
== 0x4000), true, data
, sizeof(data
), &datalen
);
269 if (!res
&& (datalen
== 3) && data
[0] == 0x09) {
270 PrintAndLog("2k card detected.");
273 if (res
|| (datalen
!= 3) || data
[0] != 0x90) {
274 PrintAndLog("Write error on address %04x", sn
);
279 SetVerboseMode(verbose
);
280 for (int i
= 0; i
< sizeof(CardAddresses
) / 2; i
++) {
281 keyNum
[0] = CardAddresses
[i
] >> 8;
282 keyNum
[1] = CardAddresses
[i
] & 0xff;
283 res
= MFPWritePerso(keyNum
, key
, false, true, data
, sizeof(data
), &datalen
);
284 if (!res
&& (datalen
== 3) && data
[0] == 0x09) {
285 PrintAndLog("Skipped[%04x]...", CardAddresses
[i
]);
287 if (res
|| (datalen
!= 3) || data
[0] != 0x90) {
288 PrintAndLog("Write error on address %04x", CardAddresses
[i
]);
299 PrintAndLog("Done.");
304 int CmdHFMFPCommitPerso(const char *cmd
) {
305 CLIParserInit("hf mfp commitp",
306 "Executes Commit Perso command. Can be used in SL0 mode only.",
307 "Usage:\n\thf mfp commitp -> \n");
311 arg_lit0("vV", "verbose", "show internal data."),
312 arg_int0(NULL
, NULL
, "SL mode", NULL
),
315 CLIExecWithReturn(cmd
, argtable
, true);
317 bool verbose
= arg_get_lit(1);
320 SetVerboseMode(verbose
);
322 uint8_t data
[250] = {0};
325 int res
= MFPCommitPerso(true, false, data
, sizeof(data
), &datalen
);
327 PrintAndLog("Exchange error: %d", res
);
332 PrintAndLog("Command must return 3 bytes instead of: %d", datalen
);
336 if (data
[0] != 0x90) {
337 PrintAndLog("Command error: %02x %s", data
[0], GetErrorDescription(data
[0]));
340 PrintAndLog("Switch level OK.");
345 int CmdHFMFPAuth(const char *cmd
) {
346 uint8_t keyn
[250] = {0};
348 uint8_t key
[250] = {0};
351 CLIParserInit("hf mfp auth",
352 "Executes AES authentication command in ISO14443-4",
353 "Usage:\n\thf mfp auth 4000 000102030405060708090a0b0c0d0e0f -> executes authentication\n"
354 "\thf mfp auth 9003 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -v -> executes authentication and shows all the system data\n");
358 arg_lit0("vV", "verbose", "show internal data."),
359 arg_str1(NULL
, NULL
, "<Key Num (HEX 2 bytes)>", NULL
),
360 arg_str1(NULL
, NULL
, "<Key Value (HEX 16 bytes)>", NULL
),
363 CLIExecWithReturn(cmd
, argtable
, true);
365 bool verbose
= arg_get_lit(1);
366 CLIGetHexWithReturn(2, keyn
, &keynlen
);
367 CLIGetHexWithReturn(3, key
, &keylen
);
371 PrintAndLog("ERROR: <Key Num> must be 2 bytes long instead of: %d", keynlen
);
376 PrintAndLog("ERROR: <Key Value> must be 16 bytes long instead of: %d", keylen
);
380 return MifareAuth4(NULL
, keyn
, key
, true, false, verbose
);
383 int CmdHFMFPRdbl(const char *cmd
) {
385 //int res = MifareAuth4(&session, keyn, key, true, false, verbose);
391 int CmdHFMFPRdsc(const char *cmd
) {
396 int CmdHFMFPWrbl(const char *cmd
) {
401 static command_t CommandTable
[] =
403 {"help", CmdHelp
, 1, "This help"},
404 {"info", CmdHFMFPInfo
, 0, "Info about Mifare Plus tag"},
405 {"wrp", CmdHFMFPWritePerso
, 0, "Write Perso command"},
406 {"initp", CmdHFMFPInitPerso
, 0, "Fills all the card's keys"},
407 {"commitp", CmdHFMFPCommitPerso
, 0, "Move card to SL1 or SL3 mode"},
408 {"auth", CmdHFMFPAuth
, 0, "Authentication in iso1443-4"},
409 // {"rdbl", CmdHFMFPRdbl, 0, "Read blocks"},
410 // {"rdsc", CmdHFMFPRdsc, 0, "Read sectors"},
411 // {"wrbl", CmdHFMFPWrbl, 0, "Write blocks"},
412 {NULL
, NULL
, 0, NULL
}
415 int CmdHFMFP(const char *Cmd
) {
416 (void)WaitForResponseTimeout(CMD_ACK
,NULL
,100);
417 CmdsParse(CommandTable
, Cmd
);
421 int CmdHelp(const char *Cmd
) {
422 CmdsHelp(CommandTable
);