style
[RRG-proxmark3.git] / client / src / cmdhffudan.c
blobbeedac04a2e8b8f9e3a90759cabe9b8976296d4d
1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
3 //
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.
8 //
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 proximity cards from ISO14443A / Fudan commands
17 //-----------------------------------------------------------------------------
19 #include "cmdhffudan.h"
21 #include <stdio.h>
22 #include <string.h>
23 #include <ctype.h>
24 #include "cliparser.h"
25 #include "cmdparser.h" // command_t
26 #include "comms.h"
27 #include "cmdhf14a.h"
28 #include "cmddata.h"
29 #include "mifare.h" // xiso
30 #include "cmdhf.h" //
31 #include "fileutils.h" // saveFile
32 #include "ui.h"
33 #include "commonutil.h" // MemLeToUint2byte
34 #include "protocols.h" // ISO14 defines
35 #include "crc16.h" // compute_crc
36 #include "util_posix.h" // msclock
38 #define FUDAN_BLOCK_READ_RETRY 3
39 #define MAX_FUDAN_BLOCK_SIZE 4
40 #define MAX_FUDAN_05_BLOCKS 8 // 16
41 #define MAX_FUDAN_08_BLOCKS 64
43 #ifndef AddCrc14A
44 # define AddCrc14A(data, len) compute_crc(CRC_14443_A, (data), (len), (data)+(len), (data)+(len)+1)
45 #endif
48 // iceman: these types are quite unsure.
49 typedef enum {
50 FM11RF005M,
51 FM11RF008M,
52 FM11RF005SH,
53 FM11RF08SH,
54 FUDAN_NONE,
55 } fudan_type_t;
57 static void fudan_print_blocks(uint16_t n, uint8_t *d) {
58 PrintAndLogEx(NORMAL, "");
59 PrintAndLogEx(INFO, "----+-------------+-----------------");
60 PrintAndLogEx(INFO, "blk | data | ascii");
61 PrintAndLogEx(INFO, "----+-------------+-----------------");
62 for (uint16_t b = 0; b < n; b++) {
63 PrintAndLogEx(INFO, "%3d | %s ", b, sprint_hex_ascii(d + (b * MAX_FUDAN_BLOCK_SIZE), MAX_FUDAN_BLOCK_SIZE));
65 PrintAndLogEx(INFO, "----+-------------+-----------------");
66 PrintAndLogEx(NORMAL, "");
69 static char *GenerateFilename(iso14a_card_select_t *card, const char *prefix, const char *suffix) {
70 if (card == NULL) {
71 return NULL;
73 char *fptr = calloc(sizeof(char) * (strlen(prefix) + strlen(suffix)) + sizeof(card->uid) * 2 + 1, sizeof(uint8_t));
74 strcpy(fptr, prefix);
75 FillFileNameByUID(fptr, card->uid, suffix, card->uidlen);
76 return fptr;
79 static fudan_type_t fudan_detected(iso14a_card_select_t *card) {
81 if ((card->sak & 0x0A) == 0x0A) {
83 uint8_t atqa = MemLeToUint2byte(card->atqa);
84 if ((atqa & 0x0003) == 0x0003) {
85 // Uses Shanghai algo
86 // printTag("FM11RF005SH (FUDAN Shanghai Metro)");
87 return FM11RF005SH;
88 } else if ((atqa & 0x0005) == 0x0005) {
89 // printTag("FM11RF005M (FUDAN MIFARE Classic clone)");
90 return FM11RF005M;
91 } else if ((atqa & 0x0008) == 0x0008) {
92 // printTag("FM11RF008M (FUDAN MIFARE Classic clone)");
93 return FM11RF008M;
96 } else if ((card->sak & 0x53) == 0x53) {
97 // printTag("FM11RF08SH (FUDAN)");
98 return FM11RF08SH;
100 return FUDAN_NONE;
103 static int fudan_get_type(iso14a_card_select_t *card, bool verbose) {
105 if (card == NULL) {
106 return PM3_EINVARG;
109 clearCommandBuffer();
110 SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0);
111 PacketResponseNG resp;
112 if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
113 PrintAndLogEx(DEBUG, "iso14443a card select timeout");
114 return PM3_ESOFT;
117 memcpy(card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t));
120 0: couldn't read
121 1: OK, with ATS
122 2: OK, no ATS
123 3: proprietary Anticollision
125 uint64_t select_status = resp.oldarg[0];
127 if (select_status == 0) {
128 PrintAndLogEx(DEBUG, "iso14443a card select failed");
129 DropField();
130 return PM3_ESOFT;
133 if (select_status == 3) {
134 if (verbose) {
135 PrintAndLogEx(INFO, "Card doesn't support standard iso14443-3 anticollision");
136 PrintAndLogEx(SUCCESS, "ATQA: %02X %02X", card->atqa[1], card->atqa[0]);
138 DropField();
139 return PM3_ESOFT;
142 PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex(card->uid, card->uidlen));
143 if (verbose) {
144 PrintAndLogEx(SUCCESS, "ATQA: " _GREEN_("%02X %02X"), card->atqa[1], card->atqa[0]);
145 PrintAndLogEx(SUCCESS, " SAK: " _GREEN_("%02X [%" PRIu64 "]"), card->sak, select_status);
147 if (card->ats_len >= 3) { // a valid ATS consists of at least the length byte (TL) and 2 CRC bytes
148 if (card->ats_len == card->ats[0] + 2)
149 PrintAndLogEx(SUCCESS, " ATS: " _GREEN_("%s"), sprint_hex(card->ats, card->ats[0]));
150 else {
151 PrintAndLogEx(SUCCESS, " ATS: [%d] " _GREEN_("%s"), card->ats_len, sprint_hex(card->ats, card->ats_len));
155 return PM3_SUCCESS;
158 int read_fudan_uid(bool loop, bool verbose) {
160 do {
161 iso14a_card_select_t card;
163 int res = fudan_get_type(&card, verbose);
165 if (loop) {
166 if (res != PM3_SUCCESS) {
167 continue;
169 } else {
170 switch (res) {
171 case PM3_EFAILED:
172 case PM3_EINVARG:
173 return res;
174 case PM3_ETIMEOUT:
175 PrintAndLogEx(DEBUG, "command execution time out");
176 return res;
177 case PM3_ESOFT:
178 PrintAndLogEx(DEBUG, "fudan card select failed");
179 return PM3_ESOFT;
180 default:
181 break;
186 if (loop) {
187 res = handle_hf_plot(verbose);
188 if (res != PM3_SUCCESS) {
189 PrintAndLogEx(DEBUG, "plot failed");
193 // decoding code
194 if (loop == false) {
195 PrintAndLogEx(NORMAL, "");
198 } while (loop && kbd_enter_pressed() == false);
201 return PM3_SUCCESS;
204 static int CmdHFFudanReader(const char *Cmd) {
205 CLIParserContext *ctx;
206 CLIParserInit(&ctx, "hf fudan reader",
207 "Read a fudan tag",
208 "hf fudan reader\n"
209 "hf fudan reader -@ -> continuous reader mode"
212 void *argtable[] = {
213 arg_param_begin,
214 arg_lit0("v", "verbose", "verbose output"),
215 arg_lit0("@", NULL, "optional - continuous reader mode"),
216 arg_param_end
218 CLIExecWithReturn(ctx, Cmd, argtable, true);
220 bool verbose = arg_get_lit(ctx, 1);
221 bool cm = arg_get_lit(ctx, 2);
223 CLIParserFree(ctx);
225 if (cm) {
226 PrintAndLogEx(INFO, "Press " _GREEN_("<Enter>") " to exit");
229 read_fudan_uid(cm, verbose);
231 DropField();
232 return PM3_SUCCESS;
235 static int CmdHFFudanDump(const char *Cmd) {
236 CLIParserContext *ctx;
237 CLIParserInit(&ctx, "hf fudan dump",
238 "Dump FUDAN tag to file (bin/json)\n"
239 "If no <name> given, UID will be used as filename",
240 "hf fudan dump -f mydump --> dump using filename\n"
243 void *argtable[] = {
244 arg_param_begin,
245 arg_str0("f", "file", "<fn>", "Specify a filename for dump file"),
246 arg_lit0(NULL, "ns", "no save to file"),
247 arg_param_end
249 CLIExecWithReturn(ctx, Cmd, argtable, true);
251 int datafnlen = 0;
252 char dataFilename[FILE_PATH_SIZE] = {0};
253 CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)dataFilename, FILE_PATH_SIZE, &datafnlen);
254 bool nosave = arg_get_lit(ctx, 2);
255 CLIParserFree(ctx);
257 // Select card to get UID/UIDLEN/ATQA/SAK information
258 // leaves the field on
259 iso14a_card_select_t card;
260 int res = fudan_get_type(&card, false);
261 if (res != PM3_SUCCESS) {
262 PrintAndLogEx(FAILED, "failed to select a fudan card. Exiting...");
263 DropField();
264 return PM3_SUCCESS;
267 // validations
268 fudan_type_t t = fudan_detected(&card);
269 if (t == FUDAN_NONE) {
270 PrintAndLogEx(FAILED, "failed to detect a fudan card. Exiting...");
271 DropField();
272 return PM3_SUCCESS;
275 // detect card size
276 // 512b, 8kbits
277 uint8_t num_blocks = MAX_FUDAN_05_BLOCKS;
278 switch (t) {
279 case FM11RF008M:
280 num_blocks = MAX_FUDAN_08_BLOCKS;
281 break;
282 case FM11RF005SH:
283 case FM11RF005M:
284 case FM11RF08SH:
285 case FUDAN_NONE:
286 default:
287 break;
290 uint8_t carddata[num_blocks * MAX_FUDAN_BLOCK_SIZE];
293 uint16_t flags = (ISO14A_NO_SELECT | ISO14A_NO_DISCONNECT | ISO14A_NO_RATS | ISO14A_RAW);
294 uint32_t argtimeout = 0;
295 uint32_t numbits = 0;
297 PrintAndLogEx(SUCCESS, "." NOLF);
298 // dump memory
299 for (uint8_t b = 0; b < num_blocks; b++) {
301 // read block
302 uint8_t cmd[4] = {ISO14443A_CMD_READBLOCK, b, 0x00, 0x00};
303 AddCrc14A(cmd, 2);
305 for (uint8_t tries = 0; tries < FUDAN_BLOCK_READ_RETRY; tries++) {
307 clearCommandBuffer();
308 PacketResponseNG resp;
309 SendCommandMIX(CMD_HF_ISO14443A_READER, flags, sizeof(cmd) | ((uint32_t)(numbits << 16)), argtimeout, cmd, sizeof(cmd));
311 if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
312 if (resp.status == PM3_SUCCESS) {
313 uint8_t *data = resp.data.asBytes;
314 memcpy(carddata + (b * MAX_FUDAN_BLOCK_SIZE), data, MAX_FUDAN_BLOCK_SIZE);
315 PrintAndLogEx(NORMAL, "." NOLF);
316 break;
317 } else {
318 PrintAndLogEx(NORMAL, "");
319 PrintAndLogEx(FAILED, "could not read block %2d", b);
321 } else {
322 PrintAndLogEx(NORMAL, "");
323 PrintAndLogEx(WARNING, "command execution time out when trying to read block %2d", b);
328 DropField();
330 PrintAndLogEx(SUCCESS, "\nSucceeded in dumping all blocks");
332 fudan_print_blocks(num_blocks, carddata);
334 if (nosave) {
335 PrintAndLogEx(INFO, "Called with no save option");
336 PrintAndLogEx(NORMAL, "");
337 return PM3_SUCCESS;
340 // create filename if none was given
341 if (strlen(dataFilename) < 1) {
342 char *fptr = GenerateFilename(&card, "hf-fudan-", "-dump");
343 if (fptr == NULL)
344 return PM3_ESOFT;
346 strcpy(dataFilename, fptr);
347 free(fptr);
350 pm3_save_dump(dataFilename, (uint8_t *)carddata, sizeof(carddata), jsfFudan);
351 return PM3_SUCCESS;
354 static int CmdHFFudanWrBl(const char *Cmd) {
356 CLIParserContext *ctx;
357 CLIParserInit(&ctx, "hf fudan wrbl",
358 "Write fudan block with 4 hex bytes of data\n",
359 "hf fudan wrbl --blk 1 -k FFFFFFFFFFFF -d 01020304"
361 void *argtable[] = {
362 arg_param_begin,
363 arg_int1(NULL, "blk", "<dec>", "block number"),
364 arg_str0("k", "key", "<hex>", "key, 6 hex bytes"),
365 arg_str0("d", "data", "<hex>", "bytes to write, 4 hex bytes"),
366 arg_param_end
368 CLIExecWithReturn(ctx, Cmd, argtable, false);
370 int b = arg_get_int_def(ctx, 1, 1);
372 int keylen = 0;
373 uint8_t key[6] = {0};
374 CLIGetHexWithReturn(ctx, 2, key, &keylen);
376 uint8_t block[MAX_FUDAN_BLOCK_SIZE] = {0x00};
377 int blen = 0;
378 CLIGetHexWithReturn(ctx, 3, block, &blen);
379 CLIParserFree(ctx);
381 if (blen != MAX_FUDAN_BLOCK_SIZE) {
382 PrintAndLogEx(WARNING, "block data must include 4 HEX bytes. Got %i", blen);
383 return PM3_EINVARG;
386 if (b > 255) {
387 return PM3_EINVARG;
390 PrintAndLogEx(SUCCESS, "Not implemented yet. Feel free to contribute!");
394 uint8_t blockno = (uint8_t)b;
396 PrintAndLogEx(INFO, "Writing block no %d, key %s", blockno, sprint_hex_inrow(key, sizeof(key)));
397 PrintAndLogEx(INFO, "data: %s", sprint_hex(block, sizeof(block)));
399 uint8_t data[26];
400 memcpy(data, key, sizeof(key));
401 memcpy(data + 10, block, sizeof(block));
402 clearCommandBuffer();
403 SendCommandMIX(CMD_HF_MIFARE_WRITEBL, blockno, 0, 0, data, sizeof(data));
405 PacketResponseNG resp;
406 if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) {
407 PrintAndLogEx(FAILED, "command execution time out");
408 return PM3_ETIMEOUT;
411 uint8_t isok = resp.oldarg[0] & 0xff;
412 if (isok) {
413 PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )");
414 PrintAndLogEx(HINT, "try `" _YELLOW_("hf fudan rdbl") "` to verify");
415 } else {
416 PrintAndLogEx(FAILED, "Write ( " _RED_("fail") " )");
419 return PM3_SUCCESS;
422 static int CmdHFFudanRdBl(const char *Cmd) {
423 CLIParserContext *ctx;
424 CLIParserInit(&ctx, "hf fudan rdbl",
425 "Read fudan block",
426 "hf fudan rdbl --blk 0 -k FFFFFFFFFFFF\n"
427 "hf fudan rdbl --blk 3 -v\n"
429 void *argtable[] = {
430 arg_param_begin,
431 arg_int1(NULL, "blk", "<dec>", "block number"),
432 arg_str0("k", "key", "<hex>", "key, 6 hex bytes"),
433 arg_lit0("v", "verbose", "verbose output"),
434 arg_param_end
436 CLIExecWithReturn(ctx, Cmd, argtable, false);
437 int b = arg_get_int_def(ctx, 1, 0);
439 int keylen = 0;
440 uint8_t key[6] = {0};
441 CLIGetHexWithReturn(ctx, 2, key, &keylen);
442 // bool verbose = arg_get_lit(ctx, 3);
443 CLIParserFree(ctx);
445 if (b > 255) {
446 return PM3_EINVARG;
449 PrintAndLogEx(SUCCESS, "Not implemented yet. Feel free to contribute!");
450 PrintAndLogEx(NORMAL, "");
451 return PM3_SUCCESS;
455 static int CmdHFFudanView(const char *Cmd) {
457 CLIParserContext *ctx;
458 CLIParserInit(&ctx, "hf fudan view",
459 "Print a FUDAN dump file (bin/eml/json)",
460 "hf fudan view -f hf-fudan-01020304-dump.bin"
462 void *argtable[] = {
463 arg_param_begin,
464 arg_str1("f", "file", "<fn>", "Specify a filename for dump file"),
465 arg_param_end
467 CLIExecWithReturn(ctx, Cmd, argtable, false);
468 int fnlen = 0;
469 char filename[FILE_PATH_SIZE];
470 CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
471 CLIParserFree(ctx);
473 // read dump file
474 uint8_t *dump = NULL;
475 size_t bytes_read = 0;
476 int res = pm3_load_dump(filename, (void **)&dump, &bytes_read, (MAX_FUDAN_BLOCK_SIZE * MAX_FUDAN_08_BLOCKS));
477 if (res != PM3_SUCCESS) {
478 return res;
481 uint16_t block_cnt = MIN(MAX_FUDAN_05_BLOCKS, (bytes_read / MAX_FUDAN_BLOCK_SIZE));
483 fudan_print_blocks(block_cnt, dump);
485 free(dump);
486 return PM3_SUCCESS;
489 static int CmdHelp(const char *Cmd);
491 static command_t CommandTable[] = {
492 {"help", CmdHelp, AlwaysAvailable, "This help"},
493 {"reader", CmdHFFudanReader, IfPm3Iso14443a, "Act like a fudan reader"},
494 {"dump", CmdHFFudanDump, IfPm3Iso14443a, "Dump FUDAN tag to binary file"},
495 //{"sim", CmdHFFudanSim, IfPm3Iso14443a, "Simulate a fudan tag"},
496 {"rdbl", CmdHFFudanRdBl, IfPm3Iso14443a, "Read a fudan tag"},
497 {"view", CmdHFFudanView, AlwaysAvailable, "Display content from tag dump file"},
498 {"wrbl", CmdHFFudanWrBl, IfPm3Iso14443a, "Write a fudan tag"},
499 {NULL, NULL, 0, NULL}
502 static int CmdHelp(const char *Cmd) {
503 (void)Cmd; // Cmd is not used so far
504 CmdsHelp(CommandTable);
505 return PM3_SUCCESS;
508 int CmdHFFudan(const char *Cmd) {
509 clearCommandBuffer();
510 return CmdsParse(CommandTable, Cmd);