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 // MIFARE Application Directory (MAD) functions
17 //-----------------------------------------------------------------------------
21 #include "commonutil.h" // ARRAYLEN
25 #include "fileutils.h"
27 #include "mifaredefault.h"
29 // https://www.nxp.com/docs/en/application-note/AN10787.pdf
30 static json_t
*mad_known_aids
= NULL
;
32 static const char *holder_info_type
[] = {
39 static const char *aid_admin
[] = {
43 "additional directory info",
48 static int open_mad_file(json_t
**root
, bool verbose
) {
51 int res
= searchFile(&path
, RESOURCES_SUBDIR
, "mad", ".json", true);
52 if (res
!= PM3_SUCCESS
) {
56 int retval
= PM3_SUCCESS
;
59 *root
= json_load_file(path
, 0, &error
);
61 PrintAndLogEx(ERR
, "json (%s) error on line %d: %s", path
, error
.line
, error
.text
);
66 if (!json_is_array(*root
)) {
67 PrintAndLogEx(ERR
, "Invalid json (%s) format. root must be an array.", path
);
73 PrintAndLogEx(SUCCESS
, "Loaded file " _YELLOW_("`%s`") " (%s) %zu records.", path
, _GREEN_("ok"), json_array_size(*root
));
79 static int close_mad_file(json_t
*root
) {
84 static const char *mad_json_get_str(json_t
*data
, const char *name
) {
86 json_t
*jstr
= json_object_get(data
, name
);
90 if (!json_is_string(jstr
)) {
91 PrintAndLogEx(WARNING
, _YELLOW_("`%s`") " is not a string", name
);
95 const char *cstr
= json_string_value(jstr
);
96 if (strlen(cstr
) == 0)
102 static int print_aid_description(json_t
*root
, uint16_t aid
, char *fmt
, bool verbose
) {
104 snprintf(lmad
, sizeof(lmad
), "0x%04x", aid
); // must be lowercase
108 for (uint32_t idx
= 0; idx
< json_array_size(root
); idx
++) {
109 json_t
*data
= json_array_get(root
, idx
);
110 if (!json_is_object(data
)) {
111 PrintAndLogEx(ERR
, "data [%d] is not an object\n", idx
);
114 const char *fmad
= mad_json_get_str(data
, "mad");
115 char lfmad
[strlen(fmad
) + 1];
118 if (strcmp(lmad
, lfmad
) == 0) {
125 PrintAndLogEx(INFO
, fmt
, " (unknown)");
129 const char *vmad
= mad_json_get_str(elm
, "mad");
130 const char *application
= mad_json_get_str(elm
, "application");
131 const char *company
= mad_json_get_str(elm
, "company");
132 const char *provider
= mad_json_get_str(elm
, "service_provider");
133 const char *integrator
= mad_json_get_str(elm
, "system_integrator");
135 if (application
&& company
) {
136 size_t result_len
= 6 + strlen(application
) + strlen(company
);
137 char result
[result_len
];
138 snprintf(result
, result_len
, " %s [%s]", application
, company
);
139 PrintAndLogEx(INFO
, fmt
, result
);
143 PrintAndLogEx(SUCCESS
, " MAD: %s", vmad
);
145 PrintAndLogEx(SUCCESS
, " Application: %s", application
);
147 PrintAndLogEx(SUCCESS
, " Company: %s", company
);
149 PrintAndLogEx(SUCCESS
, " Service provider: %s", provider
);
151 PrintAndLogEx(SUCCESS
, " System integrator: %s", integrator
);
156 static int madCRCCheck(uint8_t *sector
, bool verbose
, int MADver
) {
158 uint8_t crc
= CRC8Mad(§or
[16 + 1], 15 + 16);
159 if (crc
!= sector
[16]) {
160 PrintAndLogEx(WARNING
, _RED_("Wrong MAD %d CRC") " calculated: 0x%02x != 0x%02x", MADver
, crc
, sector
[16]);
164 uint8_t crc
= CRC8Mad(§or
[1], 15 + 16 + 16);
165 if (crc
!= sector
[0]) {
166 PrintAndLogEx(WARNING
, _RED_("Wrong MAD %d CRC") " calculated: 0x%02x != 0x%02x", MADver
, crc
, sector
[0]);
173 static uint16_t madGetAID(const uint8_t *sector
, bool swapmad
, int MADver
, int sectorNo
) {
176 mad
= (sector
[16 + 2 + (sectorNo
- 1) * 2 + 1] << 8) + (sector
[16 + 2 + (sectorNo
- 1) * 2]);
178 mad
= (sector
[2 + (sectorNo
- 1) * 2 + 1] << 8) + (sector
[2 + (sectorNo
- 1) * 2]);
180 return BSWAP_16(mad
);
186 int MADCheck(uint8_t *sector0
, uint8_t *sector10
, bool verbose
, bool *haveMAD2
) {
191 uint8_t GPB
= sector0
[(3 * 16) + 9];
193 PrintAndLogEx(SUCCESS
, "GPB....... " _GREEN_("0x%02X"), GPB
);
195 // DA (MAD available)
197 PrintAndLogEx(ERR
, "DA = 0! MAD not available");
201 uint8_t mad_ver
= GPB
& 0x03;
203 PrintAndLogEx(SUCCESS
, "Version... " _GREEN_("%d"), mad_ver
);
206 if ((mad_ver
!= 0x01) && (mad_ver
!= 0x02)) {
207 PrintAndLogEx(ERR
, "Wrong MAD version " _RED_("0x%02X"), mad_ver
);
212 *haveMAD2
= (mad_ver
== 2);
215 int res
= madCRCCheck(sector0
, true, 1);
216 if (verbose
&& res
== PM3_SUCCESS
) {
217 PrintAndLogEx(SUCCESS
, "CRC8...... 0x%02X ( %s )", sector0
[16], _GREEN_("ok"));
220 if (mad_ver
== 2 && sector10
) {
221 int res2
= madCRCCheck(sector10
, true, 2);
222 if (res
== PM3_SUCCESS
)
225 if (verbose
&& !res2
)
226 PrintAndLogEx(SUCCESS
, "CRC8...... 0x%02X ( %s )", sector10
[0], _GREEN_("ok"));
229 // MA (multi-application card)
232 PrintAndLogEx(SUCCESS
, "Multi application card");
234 PrintAndLogEx(SUCCESS
, "Single application card");
239 int MADDecode(uint8_t *sector0
, uint8_t *sector10
, uint16_t *mad
, size_t *madlen
, bool swapmad
) {
241 bool haveMAD2
= false;
242 int res
= MADCheck(sector0
, sector10
, false, &haveMAD2
);
243 if (res
!= PM3_SUCCESS
) {
244 PrintAndLogEx(WARNING
, "Not a valid MAD");
248 for (int i
= 1; i
< 16; i
++) {
249 mad
[*madlen
] = madGetAID(sector0
, swapmad
, 1, i
);
254 // mad2 sector (0x10 == 16dec) here
255 mad
[*madlen
] = 0x0005;
258 for (int i
= 1; i
< 24; i
++) {
259 mad
[*madlen
] = madGetAID(sector10
, swapmad
, 2, i
);
266 int MADCardHolderInfoDecode(uint8_t *data
, size_t datalen
, bool verbose
) {
268 while (idx
< datalen
) {
269 uint8_t len
= data
[idx
] & 0x3f;
270 uint8_t type
= data
[idx
] >> 6;
273 PrintAndLogEx(INFO
, "%14s " _GREEN_("%.*s"), holder_info_type
[type
], len
, &data
[idx
]);
282 static int MADInfoByteDecode(const uint8_t *sector
, bool swapmad
, int mad_ver
, bool verbose
) {
285 info
= sector
[16 + 1] & 0x3f;
287 PrintAndLogEx(WARNING
, "Invalid Info byte (MAD1) value " _YELLOW_("0x%02x"), info
);
289 // I understand the spec in a way that MAD1 InfoByte should not point into MAD2 sectors, @lukaskuzmiak
290 PrintAndLogEx(WARNING
, "MAD1 Info byte points outside of MAD1 sector space (0x%02x), report a bug?", info
);
295 info
= sector
[1] & 0x3f;
296 if (info
== 0x10 || info
>= 0x28) {
297 PrintAndLogEx(WARNING
, "Invalid Info byte (MAD2) value " _YELLOW_("0x%02x"), info
);
305 void MADPrintHeader(void) {
306 PrintAndLogEx(NORMAL
, "");
307 PrintAndLogEx(INFO
, "--- " _CYAN_("MIFARE App Directory Information") " ----------------");
308 PrintAndLogEx(INFO
, "-----------------------------------------------------");
311 int MAD1DecodeAndPrint(uint8_t *sector
, bool swapmad
, bool verbose
, bool *haveMAD2
) {
312 open_mad_file(&mad_known_aids
, verbose
);
314 PrintAndLogEx(NORMAL
, "");
315 PrintAndLogEx(INFO
, "------------ " _CYAN_("MAD v1 details") " -------------");
318 MADCheck(sector
, NULL
, verbose
, haveMAD2
);
320 int ibs
= MADInfoByteDecode(sector
, swapmad
, 1, verbose
);
323 PrintAndLogEx(SUCCESS
, "Card publisher sector " _MAGENTA_("0x%02X"), ibs
);
325 PrintAndLogEx(WARNING
, "Card publisher " _RED_("not") " present " _YELLOW_("0x%02x"), ibs
);
328 PrintAndLogEx(NORMAL
, "");
329 PrintAndLogEx(INFO
, "---------------- " _CYAN_("Listing") " ----------------");
331 PrintAndLogEx(INFO
, " 00 MAD v1");
332 uint32_t prev_aid
= 0xFFFFFFFF;
333 for (int i
= 1; i
< 16; i
++) {
334 uint16_t aid
= madGetAID(sector
, swapmad
, 1, i
);
337 (ibs
== i
) ? _MAGENTA_(" %02d [%04X] %s") : " %02d [" _GREEN_("%04X") "] %s",
343 } else if (prev_aid
== aid
) {
345 (ibs
== i
) ? _MAGENTA_(" %02d [%04X] continuation") : " %02d [" _YELLOW_("%04X") "] continuation",
351 snprintf(fmt
, sizeof(fmt
), (ibs
== i
) ? _MAGENTA_(" %02d [%04X]%s") : " %02d [" _GREEN_("%04X") "]%s", i
, aid
, "%s");
352 print_aid_description(mad_known_aids
, aid
, fmt
, verbose
);
356 close_mad_file(mad_known_aids
);
360 int MAD2DecodeAndPrint(uint8_t *sector
, bool swapmad
, bool verbose
) {
361 open_mad_file(&mad_known_aids
, false);
363 PrintAndLogEx(NORMAL
, "");
364 PrintAndLogEx(INFO
, "------------ " _CYAN_("MAD v2 details") " -------------");
366 int res
= madCRCCheck(sector
, true, 2);
368 if (res
== PM3_SUCCESS
)
369 PrintAndLogEx(SUCCESS
, "CRC8...... 0x%02X ( " _GREEN_("%s") " )", sector
[0], "ok");
371 PrintAndLogEx(SUCCESS
, "CRC8...... 0x%02X ( " _RED_("%s") " )", sector
[0], "fail");
374 int ibs
= MADInfoByteDecode(sector
, swapmad
, 2, verbose
);
376 PrintAndLogEx(SUCCESS
, "Card publisher sector " _MAGENTA_("0x%02X"), ibs
);
378 PrintAndLogEx(WARNING
, "Card publisher " _RED_("not") " present " _YELLOW_("0x%02x"), ibs
);
381 PrintAndLogEx(NORMAL
, "");
382 PrintAndLogEx(INFO
, "---------------- " _CYAN_("Listing") " ----------------");
384 PrintAndLogEx(INFO
, " 16 MAD v2");
386 uint32_t prev_aid
= 0xFFFFFFFF;
387 for (int i
= 1; i
< 8 + 8 + 7 + 1; i
++) {
388 uint16_t aid
= madGetAID(sector
, swapmad
, 2, i
);
391 (ibs
== i
) ? _MAGENTA_(" %02d [%04X] %s") : " %02d [" _GREEN_("%04X") "] %s",
396 } else if (prev_aid
== aid
) {
398 (ibs
== i
) ? _MAGENTA_(" %02d [%04X] continuation") : " %02d [" _YELLOW_("%04X") "] continuation",
404 snprintf(fmt
, sizeof(fmt
), (ibs
== i
) ? _MAGENTA_(" %02d [%04X]%s") : " %02d [" _GREEN_("%04X") "]%s", i
+ 16, aid
, "%s");
405 print_aid_description(mad_known_aids
, aid
, fmt
, verbose
);
409 close_mad_file(mad_known_aids
);
414 int MADDFDecodeAndPrint(uint32_t short_aid
, bool verbose
) {
415 open_mad_file(&mad_known_aids
, false);
418 snprintf(fmt
, sizeof(fmt
), " MAD AID Function 0x%04X :" _YELLOW_("%s"), short_aid
, "%s");
419 print_aid_description(mad_known_aids
, short_aid
, fmt
, verbose
);
420 close_mad_file(mad_known_aids
);
424 bool HasMADKey(uint8_t *d
) {
428 return (memcmp(d
+ (3 * MFBLOCK_SIZE
), g_mifare_mad_key
, sizeof(g_mifare_mad_key
)) == 0);
431 int DetectHID(uint8_t *d
, uint16_t manufacture
) {
436 for (int i
= 1; i
< 16; i
++) {
437 uint16_t aid
= madGetAID(d
, false, 1, i
);
438 if (aid
== manufacture
) {
446 int convert_mad_to_arr(uint8_t *in
, uint16_t ilen
, uint8_t *out
, uint16_t *olen
) {
448 if (in
== NULL
|| out
== NULL
|| ilen
== 0) {
453 if (HasMADKey(in
) == false) {
454 PrintAndLogEx(FAILED
, "No MAD key was detected in the dump file");
458 uint8_t sector0
[MFBLOCK_SIZE
* 4] = {0};
459 uint8_t sector10
[MFBLOCK_SIZE
* 4] = {0};
461 memcpy(sector0
, in
, sizeof(sector0
));
462 if (ilen
== MIFARE_4K_MAX_BYTES
) {
463 memcpy(sector10
, in
+ (MF_MAD2_SECTOR
* 4 * MFBLOCK_SIZE
), sizeof(sector10
));
466 uint16_t mad
[7 + 8 + 8 + 8 + 8] = {0};
468 if (MADDecode(sector0
, sector10
, mad
, &madlen
, false)) {
469 PrintAndLogEx(ERR
, "can't decode MAD");
473 uint16_t ndef_aid
= 0xE103;
474 for (int i
= 0; i
< madlen
; i
++) {
475 if (ndef_aid
== mad
[i
]) {
476 uint8_t tmp
[MFBLOCK_SIZE
* 4] = {0};
477 memset(tmp
, 0x00, sizeof(tmp
));
479 // sector i dump (skip first sector +1)
480 memcpy(tmp
, in
+ (i
+ 1) * sizeof(tmp
), sizeof(tmp
));
483 // print_hex_noascii_break(tmp, sizeof(tmp) - MFBLOCK_SIZE, MFBLOCK_SIZE);
485 // copy to out (skip ST)
486 memcpy(out
, tmp
, sizeof(tmp
) - MFBLOCK_SIZE
);
487 out
+= sizeof(tmp
) - MFBLOCK_SIZE
;
488 *olen
+= sizeof(tmp
) - MFBLOCK_SIZE
;