This commit is contained in:
Chris 2019-03-01 19:08:08 +01:00
commit 11f86c2d95
24 changed files with 1663 additions and 1223 deletions

View file

@ -102,6 +102,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
- Added `hf emv` `gpo`, `readrec`, `genac`, `challenge`, `intauth` - separate commands from `hf emc exec` (@merlokk)
- Added `hf fido` `assert` and `make` commands from fido2 protocol (authenticatorMakeCredential and authenticatorGetAssertion) (@merlokk)
- Added trailer block decoding to `hf mf rdbl` and `hf mf cgetbl` (@merlokk)
- Added `hf mf mad` and `hf mfp mad` MAD decode, check and print commands (@merlokk)
### Fixed
- Changed driver file proxmark3.inf to support both old and new Product/Vendor IDs (piwi)

View file

@ -108,7 +108,7 @@ CORESRCS = uart_posix.c \
CMDSRCS = crapto1/crapto1.c \
crapto1/crypto1.c \
mfkey.c \
mifare/mfkey.c \
tea.c \
fido/additional_ca.c \
fido/cose.c \
@ -126,7 +126,7 @@ CMDSRCS = crapto1/crapto1.c \
loclass/elite_crack.c \
loclass/fileutils.c \
whereami.c \
mifarehost.c \
mifare/mifarehost.c \
parity.c \
crc.c \
crc16.c \
@ -156,7 +156,8 @@ CMDSRCS = crapto1/crapto1.c \
emv/test/cda_test.c\
emv/cmdemv.c \
emv/emv_roca.c \
mifare4.c \
mifare/mifare4.c \
mifare/mad.c \
cmdanalyse.c \
cmdhf.c \
cmdhflist.c \

View file

@ -23,7 +23,7 @@
#include "tea.h"
#include "legic_prng.h"
#include "loclass/elite_crack.h"
#include "mfkey.h" //nonce2key
#include "mifare/mfkey.h" //nonce2key
#include "util_posix.h" // msclock

View file

@ -29,7 +29,7 @@
#include "cmdhfmf.h"
#include "cmdhfmfu.h"
#include "cmdhf.h" // list cmd
#include "mifarehost.h"
#include "mifare/mifarehost.h"
#include "emv/apduinfo.h"
#include "emv/emvcore.h"

View file

@ -31,8 +31,8 @@
#include "emv/cmdemv.h" // EMV
#include "protocols.h"
#include "crapto1/crapto1.h"
#include "mifarehost.h"
#include "mifaredefault.h"
#include "mifare/mifarehost.h"
#include "mifare/mifaredefault.h"
#include "parity.h" // oddparity
#include "iso15693tools.h" // ISO15693 crc

View file

@ -9,7 +9,8 @@
//-----------------------------------------------------------------------------
#include "cmdhfmf.h"
#include "mifare4.h"
#include "mifare/mifare4.h"
#include "mifare/mad.h"
#define MFBLOCK_SIZE 16
@ -3213,6 +3214,49 @@ int CmdHF14AMfAuth4(const char *Cmd) {
return MifareAuth4(NULL, keyn, key, true, false, true);
}
// https://www.nxp.com/docs/en/application-note/AN10787.pdf
int CmdHF14AMfMAD(const char *cmd) {
CLIParserInit("hf mf mad",
"Checks and prints Mifare Application Directory (MAD)",
"Usage:\n\thf mf mad -> shows MAD if exists\n");
void* argtable[] = {
arg_param_begin,
arg_lit0("vV", "verbose", "show technical data"),
arg_param_end
};
CLIExecWithReturn(cmd, argtable, true);
bool verbose = arg_get_lit(1);
CLIParserFree();
uint8_t sector[16 * 4] = {0};
if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector)) {
PrintAndLogEx(ERR, "read sector 0 error. card don't have MAD or don't have MAD on default keys.");
return 2;
}
if (verbose) {
for(int i = 0; i < 4; i ++)
PrintAndLogEx(NORMAL, "[%d] %s", i, sprint_hex(&sector[i * 16], 16));
}
bool haveMAD2 = false;
MAD1DecodeAndPrint(sector, verbose, &haveMAD2);
if (haveMAD2) {
if (mfReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector)) {
PrintAndLogEx(ERR, "read sector 0x10 error. card don't have MAD or don't have MAD on default keys.");
return 2;
}
MAD2DecodeAndPrint(sector, verbose);
}
return 0;
}
int CmdHF14AMfList(const char *Cmd) {
CmdTraceList("mf");
return 0;
@ -3255,6 +3299,9 @@ static command_t CommandTable[] = {
{"cgetsc", CmdHF14AMfCGetSc, 0, "Read sector - Magic Chinese card"},
{"cload", CmdHF14AMfCLoad, 0, "Load dump into magic Chinese card"},
{"csave", CmdHF14AMfCSave, 0, "Save dump from magic Chinese card into file or emulator"},
{"-----------", CmdHelp, 1, ""},
{"mad", CmdHF14AMfMAD, 0, "Checks and prints MAD"},
// {"ndef", CmdHF14AMfHDEF, 0, "Checks and prints NDEF records from card"},
{"ice", CmdHF14AMfice, 0, "collect Mifare Classic nonces to file"},
{NULL, NULL, 0, NULL}

View file

@ -24,11 +24,11 @@
#include "common.h"
#include "util.h"
#include "mifare.h" // nonces_t struct
#include "mfkey.h" // mfkey32_moebious
#include "mifare/mfkey.h" // mfkey32_moebious
#include "cmdhfmfhard.h"
#include "mifarehost.h" // icesector_t, sector_t
#include "mifare/mifarehost.h" // icesector_t, sector_t
#include "util_posix.h" // msclock
#include "mifaredefault.h" // mifare default key array
#include "mifare/mifaredefault.h" // mifare default key array
#include "cmdhf14a.h" // dropfield
#include "cliparser/cliparser.h" // argtable
#include "hardnested/hardnested_bf_core.h" // SetSIMDInstr

View file

@ -22,109 +22,15 @@
#include "ui.h"
#include "cmdhf14a.h"
#include "mifare.h"
#include "mifare4.h"
#include "mifare/mifare4.h"
#include "mifare/mad.h"
#include "cliparser/cliparser.h"
#include "crypto/libpcrypto.h"
static const uint8_t DefaultKey[16] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
typedef struct {
uint8_t Code;
const char *Description;
} PlusErrorsElm;
static const PlusErrorsElm PlusErrors[] = {
{0xFF, ""},
{0x00, "Transfer cannot be granted within the current authentication."},
{0x06, "Access Conditions not fulfilled. Block does not exist, block is not a value block."},
{0x07, "Too many read or write commands in the session or in the transaction."},
{0x08, "Invalid MAC in command or response"},
{0x09, "Block Number is not valid"},
{0x0a, "Invalid block number, not existing block number"},
{0x0b, "The current command code not available at the current card state."},
{0x0c, "Length error"},
{0x0f, "General Manipulation Error. Failure in the operation of the PICC (cannot write to the data block), etc."},
{0x90, "OK"},
};
int PlusErrorsLen = sizeof(PlusErrors) / sizeof(PlusErrorsElm);
const char * GetErrorDescription(uint8_t errorCode) {
for(int i = 0; i < PlusErrorsLen; i++)
if (errorCode == PlusErrors[i].Code)
return PlusErrors[i].Description;
return PlusErrors[0].Description;
}
static int CmdHelp(const char *Cmd);
static bool VerboseMode = false;
void SetVerboseMode(bool verbose) {
VerboseMode = verbose;
}
int intExchangeRAW14aPlus(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) {
if(VerboseMode)
PrintAndLogEx(INFO, ">>> %s", sprint_hex(datain, datainlen));
int res = ExchangeRAW14a(datain, datainlen, activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen);
if(VerboseMode)
PrintAndLogEx(INFO, "<<< %s", sprint_hex(dataout, *dataoutlen));
return res;
}
int MFPWritePerso(uint8_t *keyNum, uint8_t *key, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) {
uint8_t rcmd[3 + 16] = {0xa8, keyNum[1], keyNum[0], 0x00};
memmove(&rcmd[3], key, 16);
return intExchangeRAW14aPlus(rcmd, sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen);
}
int MFPCommitPerso(bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) {
uint8_t rcmd[1] = {0xaa};
return intExchangeRAW14aPlus(rcmd, sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen);
}
int MFPReadBlock(mf4Session *session, bool plain, uint8_t blockNum, uint8_t blockCount, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac) {
uint8_t rcmd[4 + 8] = {(plain?(0x37):(0x33)), blockNum, 0x00, blockCount};
if (!plain && session)
CalculateMAC(session, mtypReadCmd, blockNum, blockCount, rcmd, 4, &rcmd[4], VerboseMode);
int res = intExchangeRAW14aPlus(rcmd, plain?4:sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen);
if(res)
return res;
if (session)
session->R_Ctr++;
if(session && mac && *dataoutlen > 11)
CalculateMAC(session, mtypReadResp, blockNum, blockCount, dataout, *dataoutlen - 8 - 2, mac, VerboseMode);
return 0;
}
int MFPWriteBlock(mf4Session *session, uint8_t blockNum, uint8_t *data, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac) {
uint8_t rcmd[1 + 2 + 16 + 8] = {0xA3, blockNum, 0x00};
memmove(&rcmd[3], data, 16);
if (session)
CalculateMAC(session, mtypWriteCmd, blockNum, 1, rcmd, 19, &rcmd[19], VerboseMode);
int res = intExchangeRAW14aPlus(rcmd, sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen);
if(res)
return res;
if (session)
session->W_Ctr++;
if(session && mac && *dataoutlen > 3)
CalculateMAC(session, mtypWriteResp, blockNum, 1, dataout, *dataoutlen, mac, VerboseMode);
return 0;
}
int CmdHFMFPInfo(const char *cmd) {
if (cmd && strlen(cmd) > 0)
@ -229,7 +135,7 @@ int CmdHFMFPWritePerso(const char *cmd) {
CLIGetHexWithReturn(3, key, &keyLen);
CLIParserFree();
SetVerboseMode(verbose);
mfpSetVerboseMode(verbose);
if (!keyLen) {
memmove(key, DefaultKey, 16);
@ -260,7 +166,7 @@ int CmdHFMFPWritePerso(const char *cmd) {
}
if (data[0] != 0x90) {
PrintAndLogEx(ERR, "Command error: %02x %s", data[0], GetErrorDescription(data[0]));
PrintAndLogEx(ERR, "Command error: %02x %s", data[0], mfpGetErrorDescription(data[0]));
return 1;
}
PrintAndLogEx(INFO, "Write OK.");
@ -304,7 +210,7 @@ int CmdHFMFPInitPerso(const char *cmd) {
if (!keyLen)
memmove(key, DefaultKey, 16);
SetVerboseMode(verbose2);
mfpSetVerboseMode(verbose2);
for (uint16_t sn = 0x4000; sn < 0x4050; sn++) {
keyNum[0] = sn >> 8;
keyNum[1] = sn & 0xff;
@ -319,7 +225,7 @@ int CmdHFMFPInitPerso(const char *cmd) {
}
}
SetVerboseMode(verbose);
mfpSetVerboseMode(verbose);
for (int i = 0; i < sizeof(CardAddresses) / 2; i++) {
keyNum[0] = CardAddresses[i] >> 8;
keyNum[1] = CardAddresses[i] & 0xff;
@ -360,7 +266,7 @@ int CmdHFMFPCommitPerso(const char *cmd) {
bool verbose = arg_get_lit(1);
CLIParserFree();
SetVerboseMode(verbose);
mfpSetVerboseMode(verbose);
uint8_t data[250] = {0};
int datalen = 0;
@ -377,7 +283,7 @@ int CmdHFMFPCommitPerso(const char *cmd) {
}
if (data[0] != 0x90) {
PrintAndLogEx(ERR, "Command error: %02x %s", data[0], GetErrorDescription(data[0]));
PrintAndLogEx(ERR, "Command error: %02x %s", data[0], mfpGetErrorDescription(data[0]));
return 1;
}
PrintAndLogEx(INFO, "Switch level OK.");
@ -453,7 +359,7 @@ int CmdHFMFPRdbl(const char *cmd) {
CLIGetHexWithReturn(6, key, &keylen);
CLIParserFree();
SetVerboseMode(verbose);
mfpSetVerboseMode(verbose);
if (!keylen) {
memmove(key, DefaultKey, 16);
@ -504,7 +410,7 @@ int CmdHFMFPRdbl(const char *cmd) {
}
if (datalen && data[0] != 0x90) {
PrintAndLogEx(ERR, "Card read error: %02x %s", data[0], GetErrorDescription(data[0]));
PrintAndLogEx(ERR, "Card read error: %02x %s", data[0], mfpGetErrorDescription(data[0]));
return 6;
}
@ -563,7 +469,7 @@ int CmdHFMFPRdsc(const char *cmd) {
CLIGetHexWithReturn(5, key, &keylen);
CLIParserFree();
SetVerboseMode(verbose);
mfpSetVerboseMode(verbose);
if (!keylen) {
memmove(key, DefaultKey, 16);
@ -605,7 +511,7 @@ int CmdHFMFPRdsc(const char *cmd) {
}
if (datalen && data[0] != 0x90) {
PrintAndLogEx(ERR, "Card read error: %02x %s", data[0], GetErrorDescription(data[0]));
PrintAndLogEx(ERR, "Card read error: %02x %s", data[0], mfpGetErrorDescription(data[0]));
DropField();
return 6;
}
@ -661,7 +567,7 @@ int CmdHFMFPWrbl(const char *cmd) {
CLIGetHexWithReturn(5, key, &keylen);
CLIParserFree();
SetVerboseMode(verbose);
mfpSetVerboseMode(verbose);
if (!keylen) {
memmove(key, DefaultKey, 16);
@ -714,7 +620,7 @@ int CmdHFMFPWrbl(const char *cmd) {
}
if (datalen && data[0] != 0x90) {
PrintAndLogEx(ERR, "Card write error: %02x %s", data[0], GetErrorDescription(data[0]));
PrintAndLogEx(ERR, "Card write error: %02x %s", data[0], mfpGetErrorDescription(data[0]));
DropField();
return 6;
}
@ -733,6 +639,50 @@ int CmdHFMFPWrbl(const char *cmd) {
return 0;
}
int CmdHFMFPMAD(const char *cmd) {
CLIParserInit("hf mfp mad",
"Checks and prints Mifare Application Directory (MAD)",
"Usage:\n\thf mfp mad -> shows MAD if exists\n");
void* argtable[] = {
arg_param_begin,
arg_lit0("vV", "verbose", "show technical data"),
arg_param_end
};
CLIExecWithReturn(cmd, argtable, true);
bool verbose = arg_get_lit(1);
CLIParserFree();
uint8_t sector[16 * 4] = {0};
if (mfpReadSector(MF_MAD1_SECTOR, MF_KEY_A, (uint8_t *)g_mifarep_mad_key, sector, verbose)) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(ERR, "read sector 0 error. card don't have MAD or don't have MAD on default keys.");
return 2;
}
if (verbose) {
for(int i = 0; i < 4; i ++)
PrintAndLogEx(NORMAL, "[%d] %s", i, sprint_hex(&sector[i * 16], 16));
}
bool haveMAD2 = false;
MAD1DecodeAndPrint(sector, verbose, &haveMAD2);
if (haveMAD2) {
if (mfpReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifarep_mad_key, sector, verbose)) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(ERR, "read sector 0x10 error. card don't have MAD or don't have MAD on default keys.");
return 2;
}
MAD2DecodeAndPrint(sector, verbose);
}
return 0;
}
static command_t CommandTable[] =
{
{"help", CmdHelp, 1, "This help"},
@ -744,6 +694,7 @@ static command_t CommandTable[] =
{"rdbl", CmdHFMFPRdbl, 0, "Read blocks"},
{"rdsc", CmdHFMFPRdsc, 0, "Read sectors"},
{"wrbl", CmdHFMFPWrbl, 0, "Write blocks"},
{"mad", CmdHFMFPMAD, 0, "Checks and prints MAD"},
{NULL, NULL, 0, NULL}
};

View file

@ -10,7 +10,7 @@
#ifndef CMDHFMFP_H__
#define CMDHFMFP_H__
#include "mifaredefault.h"
#include "mifare/mifaredefault.h"
extern int CmdHFMFP(const char *Cmd);

View file

@ -98,6 +98,7 @@ fc00018778f7,--VästtrafikenKeyA, RKFÖstgötaTrafikenKeyA
314B49474956,--VIGIK1KeyA
564c505f4d41,--VIGIK1KeyB
ba5b895da162,--VIGIK1KeyB
4143414F5250,
#
# Data from: http://irq5.io/2013/04/13/decoding-bcard-conference-badges/
f4a9ef2afc6d,--BCARD KeyB

View file

@ -48,7 +48,7 @@
#include <stdarg.h>
#include "../ui.h"
#include "../emv/emvjson.h"
#include "mifare4.h"
#include "mifare/mifare4.h"
#include "cmdhfmfu.h"
typedef enum {

212
client/mifare/mad.c Normal file
View file

@ -0,0 +1,212 @@
//-----------------------------------------------------------------------------
// 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(&sector[16 + 1], 15 + 16);
if (crc != sector[16]) {
if (verbose)
PrintAndLogEx(ERR, "Wrong MAD%d CRC. Calculated: 0x%02x, from card: 0x%02x", MADver, crc, sector[16]);
return 3;
};
} else {
uint8_t crc = CRC8Mad(&sector[1], 15 + 16 + 16);
if (crc != sector[0]) {
if (verbose)
PrintAndLogEx(ERR, "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 MAD1DecodeAndPrint(uint8_t *sector, bool verbose, bool *haveMAD2) {
uint8_t GPB = sector[3 * 16 + 9];
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 (GPB & 0x40)
PrintAndLogEx(NORMAL, "Multi application card.");
else
PrintAndLogEx(NORMAL, "Single application card.");
uint8_t MADVer = GPB & 0x03;
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);
int res = madCRCCheck(sector, true, 1);
if (res)
return res;
if (verbose)
PrintAndLogEx(NORMAL, "CRC8-MAD OK.");
// 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 (res)
return res;
if (verbose)
PrintAndLogEx(NORMAL, "CRC8-MAD 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;
};

26
client/mifare/mad.h Normal file
View file

@ -0,0 +1,26 @@
//-----------------------------------------------------------------------------
// 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
//-----------------------------------------------------------------------------
#ifndef _MAD_H_
#define _MAD_H_
#include <stdint.h>
#include <stdbool.h>
typedef struct {
uint16_t AID;
const char *Description;
} madAIDDescr;
int MAD1DecodeAndPrint(uint8_t *sector, bool verbose, bool *haveMAD2);
int MAD2DecodeAndPrint(uint8_t *sector, bool verbose);
#endif // _MAD_H_

View file

@ -17,6 +17,39 @@
#include "ui.h"
#include "crypto/libpcrypto.h"
static bool VerboseMode = false;
void mfpSetVerboseMode(bool verbose) {
VerboseMode = verbose;
}
typedef struct {
uint8_t Code;
const char *Description;
} PlusErrorsElm;
static const PlusErrorsElm PlusErrors[] = {
{0xFF, ""},
{0x00, "Transfer cannot be granted within the current authentication."},
{0x06, "Access Conditions not fulfilled. Block does not exist, block is not a value block."},
{0x07, "Too many read or write commands in the session or in the transaction."},
{0x08, "Invalid MAC in command or response"},
{0x09, "Block Number is not valid"},
{0x0a, "Invalid block number, not existing block number"},
{0x0b, "The current command code not available at the current card state."},
{0x0c, "Length error"},
{0x0f, "General Manipulation Error. Failure in the operation of the PICC (cannot write to the data block), etc."},
{0x90, "OK"},
};
int PlusErrorsLen = sizeof(PlusErrors) / sizeof(PlusErrorsElm);
const char * mfpGetErrorDescription(uint8_t errorCode) {
for(int i = 0; i < PlusErrorsLen; i++)
if (errorCode == PlusErrors[i].Code)
return PlusErrors[i].Description;
return PlusErrors[0].Description;
}
AccessConditions_t MFAccessConditions[] = {
{0x00, "rdAB wrAB incAB dectrAB"},
{0x01, "rdAB dectrAB"},
@ -274,6 +307,130 @@ int MifareAuth4(mf4Session *session, uint8_t *keyn, uint8_t *key, bool activateF
return 0;
}
int intExchangeRAW14aPlus(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) {
if(VerboseMode)
PrintAndLogEx(INFO, ">>> %s", sprint_hex(datain, datainlen));
int res = ExchangeRAW14a(datain, datainlen, activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen);
if(VerboseMode)
PrintAndLogEx(INFO, "<<< %s", sprint_hex(dataout, *dataoutlen));
return res;
}
int MFPWritePerso(uint8_t *keyNum, uint8_t *key, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) {
uint8_t rcmd[3 + 16] = {0xa8, keyNum[1], keyNum[0], 0x00};
memmove(&rcmd[3], key, 16);
return intExchangeRAW14aPlus(rcmd, sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen);
}
int MFPCommitPerso(bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) {
uint8_t rcmd[1] = {0xaa};
return intExchangeRAW14aPlus(rcmd, sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen);
}
int MFPReadBlock(mf4Session *session, bool plain, uint8_t blockNum, uint8_t blockCount, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac) {
uint8_t rcmd[4 + 8] = {(plain?(0x37):(0x33)), blockNum, 0x00, blockCount};
if (!plain && session)
CalculateMAC(session, mtypReadCmd, blockNum, blockCount, rcmd, 4, &rcmd[4], VerboseMode);
int res = intExchangeRAW14aPlus(rcmd, plain?4:sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen);
if(res)
return res;
if (session)
session->R_Ctr++;
if(session && mac && *dataoutlen > 11)
CalculateMAC(session, mtypReadResp, blockNum, blockCount, dataout, *dataoutlen - 8 - 2, mac, VerboseMode);
return 0;
}
int MFPWriteBlock(mf4Session *session, uint8_t blockNum, uint8_t *data, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac) {
uint8_t rcmd[1 + 2 + 16 + 8] = {0xA3, blockNum, 0x00};
memmove(&rcmd[3], data, 16);
if (session)
CalculateMAC(session, mtypWriteCmd, blockNum, 1, rcmd, 19, &rcmd[19], VerboseMode);
int res = intExchangeRAW14aPlus(rcmd, sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen);
if(res)
return res;
if (session)
session->W_Ctr++;
if(session && mac && *dataoutlen > 3)
CalculateMAC(session, mtypWriteResp, blockNum, 1, dataout, *dataoutlen, mac, VerboseMode);
return 0;
}
int mfpReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *dataout, bool verbose){
uint8_t keyn[2] = {0};
bool plain = false;
uint16_t uKeyNum = 0x4000 + sectorNo * 2 + (keyType ? 1 : 0);
keyn[0] = uKeyNum >> 8;
keyn[1] = uKeyNum & 0xff;
if (verbose)
PrintAndLogEx(INFO, "--sector[%d]:%02x key:%04x", mfNumBlocksPerSector(sectorNo), sectorNo, uKeyNum);
mf4Session session;
int res = MifareAuth4(&session, keyn, key, true, true, verbose);
if (res) {
PrintAndLogEx(ERR, "Sector %d authentication error: %d", sectorNo, res);
return res;
}
uint8_t data[250] = {0};
int datalen = 0;
uint8_t mac[8] = {0};
uint8_t firstBlockNo = mfFirstBlockOfSector(sectorNo);
for(int n = firstBlockNo; n < firstBlockNo + mfNumBlocksPerSector(sectorNo); n++) {
res = MFPReadBlock(&session, plain, n & 0xff, 1, false, true, data, sizeof(data), &datalen, mac);
if (res) {
PrintAndLogEx(ERR, "Sector %d read error: %d", sectorNo, res);
DropField();
return res;
}
if (datalen && data[0] != 0x90) {
PrintAndLogEx(ERR, "Sector %d card read error: %02x %s", sectorNo, data[0], mfpGetErrorDescription(data[0]));
DropField();
return 5;
}
if (datalen != 1 + 16 + 8 + 2) {
PrintAndLogEx(ERR, "Sector %d error returned data length:%d", sectorNo, datalen);
DropField();
return 6;
}
memcpy(&dataout[(n - firstBlockNo) * 16], &data[1], 16);
if (verbose)
PrintAndLogEx(INFO, "data[%03d]: %s", n, sprint_hex(&data[1], 16));
if (memcmp(&data[1 + 16], mac, 8)) {
PrintAndLogEx(WARNING, "WARNING: mac on block %d not equal...", n);
PrintAndLogEx(WARNING, "MAC card: %s", sprint_hex(&data[1 + 16], 8));
PrintAndLogEx(WARNING, "MAC reader: %s", sprint_hex(mac, 8));
if (!verbose)
return 7;
} else {
if(verbose)
PrintAndLogEx(INFO, "MAC: %s", sprint_hex(&data[1 + 16], 8));
}
}
DropField();
return 0;
}
// Mifare Memory Structure: up to 32 Sectors with 4 blocks each (1k and 2k cards),
// plus evtl. 8 sectors with 16 blocks each (4k cards)
uint8_t mfNumBlocksPerSector(uint8_t sectorNo) {

View file

@ -43,9 +43,18 @@ typedef struct {
char *description;
} AccessConditions_t;
extern void mfpSetVerboseMode(bool verbose);
extern const char * mfpGetErrorDescription(uint8_t errorCode);
extern int CalculateMAC(mf4Session *session, MACType_t mtype, uint8_t blockNum, uint8_t blockCount, uint8_t *data, int datalen, uint8_t *mac, bool verbose);
extern int MifareAuth4(mf4Session *session, uint8_t *keyn, uint8_t *key, bool activateField, bool leaveSignalON, bool verbose);
extern int MFPWritePerso(uint8_t *keyNum, uint8_t *key, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen);
extern int MFPCommitPerso(bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen);
extern int MFPReadBlock(mf4Session *session, bool plain, uint8_t blockNum, uint8_t blockCount, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac);
extern int MFPWriteBlock(mf4Session *session, uint8_t blockNum, uint8_t *data, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac);
extern int mfpReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *dataout, bool verbose);
extern char *mfGetAccessConditionsDesc(uint8_t blockn, uint8_t *data);
extern uint8_t mfNumBlocksPerSector(uint8_t sectorNo);

View file

@ -31,7 +31,7 @@ static const uint64_t g_mifare_default_keys[] =
0xabcdef123456,
0x4d3a99c351dd,
0x1a982c7e459a,
0xd3f7d3f7d3f7,
0xd3f7d3f7d3f7, // NDEF public key
0x714c5c886e97,
0x587ee5f9350f,
0xa0478cc39091,
@ -42,4 +42,8 @@ static const uint64_t g_mifare_default_keys[] =
0x96a301bce267
};
static const uint8_t g_mifare_mad_key[] = {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5};
static const uint8_t g_mifare_ndef_key[] = {0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7};
static const uint8_t g_mifarep_mad_key[] = {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7};
#endif

View file

@ -1,4 +1,4 @@
// Merlok, 2011, 2012
// Merlok, 2011, 2012, 2019
// people from mifare@nethemba.com, 2010
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
@ -414,6 +414,32 @@ out:
return -4;
}
// MIFARE
int mfReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *data) {
UsbCommand c = {CMD_MIFARE_READSC, {sectorNo, keyType, 0}};
memcpy(c.d.asBytes, key, 6);
clearCommandBuffer();
SendCommand(&c);
UsbCommand resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
uint8_t isOK = resp.arg[0] & 0xff;
if (isOK) {
memcpy(data, resp.d.asBytes, mfNumBlocksPerSector(sectorNo) * 16);
return 0;
} else {
return 1;
}
} else {
PrintAndLogEx(ERR, "Command execute timeout");
return 2;
}
return 0;
}
// EMULATOR
int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount) {
UsbCommand c = {CMD_MIFARE_EML_MEMGET, {blockNum, blocksCount, 0}};

View file

@ -1,4 +1,4 @@
// Merlok, 2011
// Merlok, 2011, 2019
// people from mifare@nethemba.com, 2010
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
@ -76,7 +76,7 @@ extern int mfCheckKeys_fast( uint8_t sectorsCnt, uint8_t firstChunk, uint8_t las
uint8_t strategy, uint32_t size, uint8_t *keyBlock, sector_t *e_sector, bool use_flashmemory);
extern int mfKeyBrute(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint64_t *resultkey);
extern int mfReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *data);
extern int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount);
extern int mfEmlSetMem(uint8_t *data, int blockNum, int blocksCount);

0
client/obj/mifare/.dummy Normal file
View file

View file

@ -19,7 +19,7 @@
#include "cmdmain.h"
#include "comms.h"
#include "util.h"
#include "mifarehost.h"
#include "mifare/mifarehost.h"
#include "crc.h"
#include "crc16.h"
#include "crc64.h"

View file

@ -99,14 +99,13 @@ uint32_t CRC8Maxim(uint8_t *buff, size_t size) {
crc_update2(&crc, buff[i], 8);
return crc_finish(&crc);
}
// width=8 poly=0x1d, reversed poly=0x?? init=0xe3 refin=true refout=true xorout=0x0000 check=0xC6 name="CRC-8/MAD"
// the CRC needs to be reversed before returned.
// width=8 poly=0x1d, init=0xc7 (0xe3 - WRONG! but it mentioned in MAD datasheet) refin=false refout=false xorout=0x00 name="CRC-8/MIFARE-MAD"
uint32_t CRC8Mad(uint8_t *buff, size_t size) {
crc_t crc;
crc_init_ref(&crc, 8, 0x1d, 0xe3, 0, true, true);
crc_init_ref(&crc, 8, 0x1d, 0xc7, 0, false, false);
for ( int i = 0; i < size; ++i)
crc_update2(&crc, buff[i], 8);
return reflect8(crc_finish(&crc));
return crc_finish(&crc);
}
// width=4 poly=0xC, reversed poly=0x7 init=0x5 refin=true refout=true xorout=0x0000 check= name="CRC-4/LEGIC"
uint32_t CRC4Legic(uint8_t *cmd, size_t size) {

View file

@ -13,6 +13,12 @@
#include "common.h"
#define MF_KEY_A 0
#define MF_KEY_B 1
#define MF_MAD1_SECTOR 0x00
#define MF_MAD2_SECTOR 0x10
//-----------------------------------------------------------------------------
// ISO 14443A
//-----------------------------------------------------------------------------