Mfp commands (#698)
[proxmark3-svn] / client / cmdhfmfp.c
CommitLineData
dc3e2acf
OM
1//-----------------------------------------------------------------------------
2// Copyright (C) 2018 Merlok
3//
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
6// the license.
7//-----------------------------------------------------------------------------
8// High frequency MIFARE Plus commands
9//-----------------------------------------------------------------------------
10
11#include "cmdhfmfp.h"
12
13#include <inttypes.h>
14#include <string.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <ctype.h>
18#include "comms.h"
19#include "cmdmain.h"
20#include "util.h"
21#include "ui.h"
22#include "cmdhf14a.h"
23#include "mifare.h"
ae3340a0
OM
24#include "mifare4.h"
25#include "cliparser/cliparser.h"
26#include "polarssl/libpcrypto.h"
27
28static const uint8_t DefaultKey[16] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
29
30typedef struct {
31 uint8_t Code;
32 const char *Description;
33} PlusErrorsElm;
34
35static const PlusErrorsElm PlusErrors[] = {
36 {0xFF, ""},
37 {0x00, "Unknown error"},
38 {0x09, "Invalid block number"},
39 {0x0b, "Command code error"},
40 {0x0c, "Length error"},
41 {0x90, "OK"},
42};
43int PlusErrorsLen = sizeof(PlusErrors) / sizeof(PlusErrorsElm);
44
45const char * GetErrorDescription(uint8_t errorCode) {
46 for(int i = 0; i < PlusErrorsLen; i++)
47 if (errorCode == PlusErrors[i].Code)
48 return PlusErrors[i].Description;
49
50 return PlusErrors[0].Description;
51}
dc3e2acf
OM
52
53static int CmdHelp(const char *Cmd);
54
ae3340a0
OM
55static bool VerboseMode = false;
56void SetVerboseMode(bool verbose) {
57 VerboseMode = verbose;
58}
59
60int intExchangeRAW14aPlus(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) {
61 if(VerboseMode)
62 PrintAndLog(">>> %s", sprint_hex(datain, datainlen));
63
64 int res = ExchangeRAW14a(datain, datainlen, activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen);
65
66 if(VerboseMode)
67 PrintAndLog("<<< %s", sprint_hex(dataout, *dataoutlen));
68
69 return res;
70}
71
72int 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);
75
76 return intExchangeRAW14aPlus(rcmd, sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen);
77}
78
79int MFPCommitPerso(bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) {
80 uint8_t rcmd[1] = {0xaa};
81
82 return intExchangeRAW14aPlus(rcmd, sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen);
83}
84
dc3e2acf
OM
85int CmdHFMFPInfo(const char *cmd) {
86
87 if (cmd && strlen(cmd) > 0)
88 PrintAndLog("WARNING: command don't have any parameters.\n");
89
90 // info about 14a part
91 CmdHF14AInfo("");
92
93 // Mifare Plus info
94 UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0}};
95 SendCommand(&c);
96
97 UsbCommand resp;
98 WaitForResponse(CMD_ACK,&resp);
99
100 iso14a_card_select_t card;
101 memcpy(&card, (iso14a_card_select_t *)resp.d.asBytes, sizeof(iso14a_card_select_t));
102
103 uint64_t select_status = resp.arg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision
104
105 if (select_status == 1 || select_status == 2) {
106 PrintAndLog("----------------------------------------------");
107 PrintAndLog("Mifare Plus info:");
108
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");
116
117 uint8_t SLmode = 0xff;
118 if (card.sak == 0x08) {
119 PrintAndLog("SAK: Mifare Plus 2k 7bUID");
120 if (select_status == 2) SLmode = 1;
121 }
122 if (card.sak == 0x18) {
123 PrintAndLog("SAK: Mifare Plus 4k 7bUID");
124 if (select_status == 2) SLmode = 1;
125 }
126 if (card.sak == 0x10) {
127 PrintAndLog("SAK: Mifare Plus 2k");
128 if (select_status == 2) SLmode = 2;
129 }
130 if (card.sak == 0x11) {
131 PrintAndLog("SAK: Mifare Plus 4k");
132 if (select_status == 2) SLmode = 2;
133 }
134 if (card.sak == 0x20) {
135 PrintAndLog("SAK: Mifare Plus SL0/SL3 or Mifare desfire");
136 if (card.ats_len > 0) {
137 SLmode = 3;
138
139 // check SL0
140 uint8_t data[250] = {0};
141 int datalen = 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) {
146 SLmode = 0;
147 }
148 }
149 }
150
151 if (SLmode != 0xff)
152 PrintAndLog("Mifare Plus SL mode: SL%d", SLmode);
153 else
154 PrintAndLog("Mifare Plus SL mode: unknown(");
155 } else {
156 PrintAndLog("Mifare Plus info not available.");
157 }
158
159 DropField();
160
161 return 0;
162}
163
ae3340a0
OM
164int CmdHFMFPWritePerso(const char *cmd) {
165 uint8_t keyNum[64] = {0};
166 int keyNumLen = 0;
167 uint8_t key[64] = {0};
168 int keyLen = 0;
169
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");
174
175 void* argtable[] = {
176 arg_param_begin,
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),
180 arg_param_end
181 };
182 CLIExecWithReturn(cmd, argtable, true);
183
184 bool verbose = arg_get_lit(1);
185 CLIGetHexWithReturn(2, keyNum, &keyNumLen);
186 CLIGetHexWithReturn(3, key, &keyLen);
187 CLIParserFree();
188
189 SetVerboseMode(verbose);
190
191 if (!keyLen) {
192 memmove(key, DefaultKey, 16);
193 keyLen = 16;
194 }
195
196 if (keyNumLen != 2) {
197 PrintAndLog("Key number length must be 2 bytes instead of: %d", keyNumLen);
198 return 1;
199 }
200 if (keyLen != 16) {
201 PrintAndLog("Key length must be 16 bytes instead of: %d", keyLen);
202 return 1;
203 }
204
205 uint8_t data[250] = {0};
206 int datalen = 0;
207
208 int res = MFPWritePerso(keyNum, key, true, false, data, sizeof(data), &datalen);
209 if (res) {
210 PrintAndLog("Exchange error: %d", res);
211 return res;
212 }
213
214 if (datalen != 3) {
215 PrintAndLog("Command must return 3 bytes instead of: %d", datalen);
216 return 1;
217 }
218
219 if (data[0] != 0x90) {
220 PrintAndLog("Command error: %02x %s", data[0], GetErrorDescription(data[0]));
221 return 1;
222 }
223 PrintAndLog("Write OK.");
224
225 return 0;
226}
227
228uint16_t CardAddresses[] = {0x9000, 0x9001, 0x9002, 0x9003, 0x9004, 0xA000, 0xA001, 0xA080, 0xA081, 0xC000, 0xC001};
229
230int CmdHFMFPInitPerso(const char *cmd) {
231 int res;
232 uint8_t key[256] = {0};
233 int keyLen = 0;
234 uint8_t keyNum[2] = {0};
235 uint8_t data[250] = {0};
236 int datalen = 0;
237
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");
242
243 void* argtable[] = {
244 arg_param_begin,
245 arg_litn("vV", "verbose", 0, 2, "show internal data."),
246 arg_strx0(NULL, NULL, "<HEX key (16b)>", NULL),
247 arg_param_end
248 };
249 CLIExecWithReturn(cmd, argtable, true);
250
251 bool verbose = arg_get_lit(1);
252 bool verbose2 = arg_get_lit(1) > 1;
253 CLIGetHexWithReturn(2, key, &keyLen);
254 CLIParserFree();
255
256 if (keyLen && keyLen != 16) {
257 PrintAndLog("Key length must be 16 bytes instead of: %d", keyLen);
258 return 1;
259 }
260
261 if (!keyLen)
262 memmove(key, DefaultKey, 16);
263
264 SetVerboseMode(verbose2);
265 for (uint16_t sn = 0x4000; sn < 0x4050; sn++) {
266 keyNum[0] = sn >> 8;
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.");
271 break;
272 }
273 if (res || (datalen != 3) || data[0] != 0x90) {
274 PrintAndLog("Write error on address %04x", sn);
275 break;
276 }
277 }
278
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]);
286 } else {
287 if (res || (datalen != 3) || data[0] != 0x90) {
288 PrintAndLog("Write error on address %04x", CardAddresses[i]);
289 break;
290 }
291 }
292 }
293
294 DropField();
295
296 if (res)
297 return res;
298
299 PrintAndLog("Done.");
300
301 return 0;
302}
303
304int 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");
308
309 void* argtable[] = {
310 arg_param_begin,
311 arg_lit0("vV", "verbose", "show internal data."),
312 arg_int0(NULL, NULL, "SL mode", NULL),
313 arg_param_end
314 };
315 CLIExecWithReturn(cmd, argtable, true);
316
317 bool verbose = arg_get_lit(1);
318 CLIParserFree();
319
320 SetVerboseMode(verbose);
321
322 uint8_t data[250] = {0};
323 int datalen = 0;
324
325 int res = MFPCommitPerso(true, false, data, sizeof(data), &datalen);
326 if (res) {
327 PrintAndLog("Exchange error: %d", res);
328 return res;
329 }
330
331 if (datalen != 3) {
332 PrintAndLog("Command must return 3 bytes instead of: %d", datalen);
333 return 1;
334 }
335
336 if (data[0] != 0x90) {
337 PrintAndLog("Command error: %02x %s", data[0], GetErrorDescription(data[0]));
338 return 1;
339 }
340 PrintAndLog("Switch level OK.");
341
342 return 0;
343}
344
345int CmdHFMFPAuth(const char *cmd) {
346 uint8_t keyn[250] = {0};
347 int keynlen = 0;
348 uint8_t key[250] = {0};
349 int keylen = 0;
350
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");
355
356 void* argtable[] = {
357 arg_param_begin,
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),
361 arg_param_end
362 };
363 CLIExecWithReturn(cmd, argtable, true);
364
365 bool verbose = arg_get_lit(1);
366 CLIGetHexWithReturn(2, keyn, &keynlen);
367 CLIGetHexWithReturn(3, key, &keylen);
368 CLIParserFree();
369
370 if (keynlen != 2) {
371 PrintAndLog("ERROR: <Key Num> must be 2 bytes long instead of: %d", keynlen);
372 return 1;
373 }
374
375 if (keylen != 16) {
376 PrintAndLog("ERROR: <Key Value> must be 16 bytes long instead of: %d", keylen);
377 return 1;
378 }
379
380 return MifareAuth4(NULL, keyn, key, true, false, verbose);
381}
382
383int CmdHFMFPRdbl(const char *cmd) {
384 //mf4Session session
385 //int res = MifareAuth4(&session, keyn, key, true, false, verbose);
386 //res = Read();
387
388 return 0;
389}
390
391int CmdHFMFPRdsc(const char *cmd) {
392
393 return 0;
394}
395
396int CmdHFMFPWrbl(const char *cmd) {
397
398 return 0;
399}
400
dc3e2acf
OM
401static command_t CommandTable[] =
402{
403 {"help", CmdHelp, 1, "This help"},
404 {"info", CmdHFMFPInfo, 0, "Info about Mifare Plus tag"},
ae3340a0
OM
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"},
dc3e2acf
OM
412 {NULL, NULL, 0, NULL}
413};
414
415int CmdHFMFP(const char *Cmd) {
416 (void)WaitForResponseTimeout(CMD_ACK,NULL,100);
417 CmdsParse(CommandTable, Cmd);
418 return 0;
419}
420
421int CmdHelp(const char *Cmd) {
422 CmdsHelp(CommandTable);
423 return 0;
424}
Impressum, Datenschutz