--- /dev/null
+//-----------------------------------------------------------------------------
+// Copyright (C) 2019 Merlok
+//
+// This code is licensed to you under the terms of the GNU GPL, version 2 or,
+// at your option, any later version. See the LICENSE.txt file for the text of
+// the license.
+//-----------------------------------------------------------------------------
+// MIFARE Application Directory (MAD) functions
+//-----------------------------------------------------------------------------
+
+#include "mad.h"
+#include "ui.h"
+#include "crc.h"
+#include "util.h"
+
+// https://www.nxp.com/docs/en/application-note/AN10787.pdf
+static madAIDDescr madKnownAIDs[] = {
+ {0x0000, "free"},
+ {0x0001, "defect, e.g. access keys are destroyed or unknown"},
+ {0x0002, "reserved"},
+ {0x0003, "contains additional directory info"},
+ {0x0004, "contains card holder information in ASCII format."},
+ {0x0005, "not applicable (above memory size)"},
+
+ {0x03e1, "NDEF"},
+};
+
+static madAIDDescr madKnownClusterCodes[] = {
+ {0x00, "cluster: card administration"},
+ {0x01, "cluster: miscellaneous applications"},
+ {0x02, "cluster: miscellaneous applications"},
+ {0x03, "cluster: miscellaneous applications"},
+ {0x04, "cluster: miscellaneous applications"},
+ {0x05, "cluster: miscellaneous applications"},
+ {0x06, "cluster: miscellaneous applications"},
+ {0x07, "cluster: miscellaneous applications"},
+ {0x08, "cluster: airlines"},
+ {0x09, "cluster: ferry traffic"},
+ {0x10, "cluster: railway services"},
+ {0x11, "cluster: miscellaneous applications"},
+ {0x12, "cluster: transport"},
+ {0x14, "cluster: security solutions"},
+ {0x18, "cluster: city traffic"},
+ {0x19, "cluster: Czech Railways"},
+ {0x20, "cluster: bus services"},
+ {0x21, "cluster: multi modal transit"},
+ {0x28, "cluster: taxi"},
+ {0x30, "cluster: road toll"},
+ {0x31, "cluster: generic transport"},
+ {0x38, "cluster: company services"},
+ {0x40, "cluster: city card services"},
+ {0x47, "cluster: access control & security"},
+ {0x48, "cluster: access control & security"},
+ {0x49, "cluster: VIGIK"},
+ {0x4A, "cluster: Ministry of Defence, Netherlands"},
+ {0x4B, "cluster: Bosch Telecom, Germany"},
+ {0x4C, "cluster: European Union Institutions"},
+ {0x50, "cluster: ski ticketing"},
+ {0x51, "cluster: access control & security"},
+ {0x52, "cluster: access control & security"},
+ {0x53, "cluster: access control & security"},
+ {0x54, "cluster: access control & security"},
+ {0x55, "cluster: SOAA standard for offline access standard"},
+ {0x56, "cluster: access control & security"},
+ {0x58, "cluster: academic services"},
+ {0x60, "cluster: food"},
+ {0x68, "cluster: non-food trade"},
+ {0x70, "cluster: hotel"},
+ {0x71, "cluster: loyalty"},
+ {0x75, "cluster: airport services"},
+ {0x78, "cluster: car rental"},
+ {0x79, "cluster: Dutch government"},
+ {0x80, "cluster: administration services"},
+ {0x88, "cluster: electronic purse"},
+ {0x90, "cluster: television"},
+ {0x91, "cluster: cruise ship"},
+ {0x95, "cluster: IOPTA"},
+ {0x97, "cluster: metering"},
+ {0x98, "cluster: telephone"},
+ {0xA0, "cluster: health services"},
+ {0xA8, "cluster: warehouse"},
+ {0xB0, "cluster: electronic trade"},
+ {0xB8, "cluster: banking"},
+ {0xC0, "cluster: entertainment & sports"},
+ {0xC8, "cluster: car parking"},
+ {0xC9, "cluster: fleet management"},
+ {0xD0, "cluster: fuel, gasoline"},
+ {0xD8, "cluster: info services"},
+ {0xE0, "cluster: press"},
+ {0xE1, "cluster: NFC Forum"},
+ {0xE8, "cluster: computer"},
+ {0xF0, "cluster: mail"},
+ {0xF8, "cluster: miscellaneous applications"},
+};
+
+static const char unknownAID[] = "";
+
+static const char *GetAIDDescription(uint16_t AID) {
+ for (int i = 0; i < ARRAYLEN(madKnownAIDs); i++)
+ if (madKnownAIDs[i].AID == AID)
+ return madKnownAIDs[i].Description;
+
+ for (int i = 0; i < ARRAYLEN(madKnownClusterCodes); i++)
+ if (madKnownClusterCodes[i].AID == (AID >> 8)) // high byte - cluster code
+ return madKnownClusterCodes[i].Description;
+
+ return unknownAID;
+}
+
+int madCRCCheck(uint8_t *sector, bool verbose, int MADver) {
+ if (MADver == 1) {
+ uint8_t crc = CRC8Mad(§or[16 + 1], 15 + 16);
+ if (crc != sector[16]) {
+ PrintAndLogEx(WARNING, "Wrong MAD%d CRC. Calculated: 0x%02x, from card: 0x%02x", MADver, crc, sector[16]);
+ return 3;
+ };
+ } else {
+ uint8_t crc = CRC8Mad(§or[1], 15 + 16 + 16);
+ if (crc != sector[0]) {
+ PrintAndLogEx(WARNING, "Wrong MAD%d CRC. Calculated: 0x%02x, from card: 0x%02x", MADver, crc, sector[16]);
+ return 3;
+ };
+ }
+
+ return 0;
+}
+
+uint16_t madGetAID(uint8_t *sector, int MADver, int sectorNo) {
+ if (MADver == 1)
+ return (sector[16 + 2 + (sectorNo - 1) * 2] << 8) + (sector[16 + 2 + (sectorNo - 1) * 2 + 1]);
+ else
+ return (sector[2 + (sectorNo - 1) * 2] << 8) + (sector[2 + (sectorNo - 1) * 2 + 1]);
+}
+
+int MADCheck(uint8_t *sector0, uint8_t *sector10, bool verbose, bool *haveMAD2) {
+ int res = 0;
+
+ if (!sector0)
+ return 1;
+
+ uint8_t GPB = sector0[3 * 16 + 9];
+ if (verbose)
+ PrintAndLogEx(NORMAL, "GPB: 0x%02x", GPB);
+
+ // DA (MAD available)
+ if (!(GPB & 0x80)) {
+ PrintAndLogEx(ERR, "DA=0! MAD not available.");
+ return 1;
+ }
+
+ // MA (multi-application card)
+ if (verbose) {
+ if (GPB & 0x40)
+ PrintAndLogEx(NORMAL, "Multi application card.");
+ else
+ PrintAndLogEx(NORMAL, "Single application card.");
+ }
+
+ uint8_t MADVer = GPB & 0x03;
+ if (verbose)
+ PrintAndLogEx(NORMAL, "MAD version: %d", MADVer);
+
+ // MAD version
+ if ((MADVer != 0x01) && (MADVer != 0x02)) {
+ PrintAndLogEx(ERR, "Wrong MAD version: 0x%02x", MADVer);
+ return 2;
+ };
+
+ if (haveMAD2)
+ *haveMAD2 = (MADVer == 2);
+
+ res = madCRCCheck(sector0, true, 1);
+
+ if (verbose && !res)
+ PrintAndLogEx(NORMAL, "CRC8-MAD1 OK.");
+
+ if (MADVer == 2 && sector10) {
+ int res2 = madCRCCheck(sector10, true, 2);
+ if (!res)
+ res = res2;
+
+ if (verbose & !res2)
+ PrintAndLogEx(NORMAL, "CRC8-MAD2 OK.");
+ }
+
+ return res;
+}
+
+int MADDecode(uint8_t *sector0, uint8_t *sector10, uint16_t *mad, size_t *madlen) {
+ *madlen = 0;
+ bool haveMAD2 = false;
+ MADCheck(sector0, sector10, false, &haveMAD2);
+
+ for (int i = 1; i < 16; i++) {
+ mad[*madlen] = madGetAID(sector0, 1, i);
+ (*madlen)++;
+ }
+
+ if (haveMAD2) {
+ // mad2 sector (0x10 == 16dec) here
+ mad[*madlen] = 0x0005;
+ (*madlen)++;
+
+ for (int i = 1; i < 24; i++) {
+ mad[*madlen] = madGetAID(sector10, 2, i);
+ (*madlen)++;
+ }
+ }
+
+ return 0;
+}
+
+
+int MAD1DecodeAndPrint(uint8_t *sector, bool verbose, bool *haveMAD2) {
+
+ // check MAD1 only
+ MADCheck(sector, NULL, verbose, haveMAD2);
+
+ // info byte
+ uint8_t InfoByte = sector[16 + 1] & 0x3f;
+ if (InfoByte) {
+ PrintAndLogEx(NORMAL, "Card publisher sector: 0x%02x", InfoByte);
+ } else {
+ if (verbose)
+ PrintAndLogEx(NORMAL, "Card publisher sector not present.");
+ }
+ if (InfoByte == 0x10 || InfoByte >= 0x28)
+ PrintAndLogEx(WARNING, "Info byte error");
+
+ PrintAndLogEx(NORMAL, "00 MAD1");
+ for (int i = 1; i < 16; i++) {
+ uint16_t AID = madGetAID(sector, 1, i);
+ PrintAndLogEx(NORMAL, "%02d [%04X] %s", i, AID, GetAIDDescription(AID));
+ };
+
+ return 0;
+};
+
+int MAD2DecodeAndPrint(uint8_t *sector, bool verbose) {
+ PrintAndLogEx(NORMAL, "16 MAD2");
+
+ int res = madCRCCheck(sector, true, 2);
+
+ if (verbose && !res)
+ PrintAndLogEx(NORMAL, "CRC8-MAD2 OK.");
+
+ uint8_t InfoByte = sector[1] & 0x3f;
+ PrintAndLogEx(NORMAL, "MAD2 Card publisher sector: 0x%02x", InfoByte);
+
+ for (int i = 1; i < 8 + 8 + 7 + 1; i++) {
+ uint16_t AID = madGetAID(sector, 2, i);
+ PrintAndLogEx(NORMAL, "%02d [%04X] %s", i + 16, AID, GetAIDDescription(AID));
+ };
+
+ return 0;
+};