X-Git-Url: http://git.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/blobdiff_plain/ae3340a0fb04bf8f8b8c874e0859d7d0886e2db1..ac4ecfe35327f827aeaf8426af2662c656affd2c:/client/mifare4.c diff --git a/client/mifare4.c b/client/mifare4.c index 3489c857..419e9b23 100644 --- a/client/mifare4.c +++ b/client/mifare4.c @@ -1,5 +1,6 @@ //----------------------------------------------------------------------------- // Copyright (C) 2018 Merlok +// Copyright (C) 2018 drHatson // // 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 @@ -14,14 +15,133 @@ #include "cmdhf14a.h" #include "util.h" #include "ui.h" -#include "polarssl/libpcrypto.h" +#include "crypto/libpcrypto.h" + +AccessConditions_t MFAccessConditions[] = { + {0x00, "read AB; write AB; increment AB; decrement transfer restore AB"}, + {0x01, "read AB; decrement transfer restore AB"}, + {0x02, "read AB"}, + {0x03, "read B; write B"}, + {0x04, "read AB; writeB"}, + {0x05, "read B"}, + {0x06, "read AB; write B; increment B; decrement transfer restore AB"}, + {0x07, "none"} +}; + +AccessConditions_t MFAccessConditionsTrailer[] = { + {0x00, "read A by A; read ACCESS by A; read B by A; write B by A"}, + {0x01, "write A by A; read ACCESS by A write ACCESS by A; read B by A; write B by A"}, + {0x02, "read ACCESS by A; read B by A"}, + {0x03, "write A by B; read ACCESS by AB; write ACCESS by B; write B by B"}, + {0x04, "write A by B; read ACCESS by AB; write B by B"}, + {0x05, "read ACCESS by AB; write ACCESS by B"}, + {0x06, "read ACCESS by AB"}, + {0x07, "read ACCESS by AB"} +}; + +char *mfGetAccessConditionsDesc(uint8_t blockn, uint8_t *data) { + static char StaticNone[] = "none"; + + uint8_t data1 = ((data[1] >> 4) & 0x0f) >> blockn; + uint8_t data2 = ((data[2]) & 0x0f) >> blockn; + uint8_t data3 = ((data[2] >> 4) & 0x0f) >> blockn; + + uint8_t cond = (data1 & 0x01) << 2 | (data2 & 0x01) << 1 | (data3 & 0x01); + + if (blockn == 3) { + for (int i = 0; i < ARRAYLEN(MFAccessConditionsTrailer); i++) + if (MFAccessConditionsTrailer[i].cond == cond) { + return MFAccessConditionsTrailer[i].description; + } + } else { + for (int i = 0; i < ARRAYLEN(MFAccessConditions); i++) + if (MFAccessConditions[i].cond == cond) { + return MFAccessConditions[i].description; + } + }; + + return StaticNone; +}; + +int CalculateEncIVCommand(mf4Session *session, uint8_t *iv, bool verbose) { + memcpy(&iv[0], session->TI, 4); + memcpy(&iv[4], &session->R_Ctr, 2); + memcpy(&iv[6], &session->W_Ctr, 2); + memcpy(&iv[8], &session->R_Ctr, 2); + memcpy(&iv[10], &session->W_Ctr, 2); + memcpy(&iv[12], &session->R_Ctr, 2); + memcpy(&iv[14], &session->W_Ctr, 2); + + return 0; +} + +int CalculateEncIVResponse(mf4Session *session, uint8_t *iv, bool verbose) { + memcpy(&iv[0], &session->R_Ctr, 2); + memcpy(&iv[2], &session->W_Ctr, 2); + memcpy(&iv[4], &session->R_Ctr, 2); + memcpy(&iv[6], &session->W_Ctr, 2); + memcpy(&iv[8], &session->R_Ctr, 2); + memcpy(&iv[10], &session->W_Ctr, 2); + memcpy(&iv[12], session->TI, 4); + + return 0; +} + + +int CalculateMAC(mf4Session *session, MACType_t mtype, uint8_t blockNum, uint8_t blockCount, uint8_t *data, int datalen, uint8_t *mac, bool verbose) { + if (!session || !session->Authenticated || !mac || !data || !datalen || datalen < 1) + return 1; + + memset(mac, 0x00, 8); + + uint16_t ctr = session->R_Ctr; + switch(mtype) { + case mtypWriteCmd: + case mtypWriteResp: + ctr = session->W_Ctr; + break; + case mtypReadCmd: + case mtypReadResp: + break; + } + + uint8_t macdata[2049] = {data[0], (ctr & 0xFF), (ctr >> 8), 0}; + int macdatalen = datalen; + memcpy(&macdata[3], session->TI, 4); + + switch(mtype) { + case mtypReadCmd: + memcpy(&macdata[7], &data[1], datalen - 1); + macdatalen = datalen + 6; + break; + case mtypReadResp: + macdata[7] = blockNum; + macdata[8] = 0; + macdata[9] = blockCount; + memcpy(&macdata[10], &data[1], datalen - 1); + macdatalen = datalen + 9; + break; + case mtypWriteCmd: + memcpy(&macdata[7], &data[1], datalen - 1); + macdatalen = datalen + 6; + break; + case mtypWriteResp: + macdatalen = 1 + 6; + break; + } + + if (verbose) + PrintAndLog("MAC data[%d]: %s", macdatalen, sprint_hex(macdata, macdatalen)); + + return aes_cmac8(NULL, session->Kmac, macdata, mac, macdatalen); +} int MifareAuth4(mf4Session *session, uint8_t *keyn, uint8_t *key, bool activateField, bool leaveSignalON, bool verbose) { uint8_t data[257] = {0}; int datalen = 0; - uint8_t Rnd1[17] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00}; - uint8_t Rnd2[17] = {0}; + uint8_t RndA[17] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00}; + uint8_t RndB[17] = {0}; if (session) session->Authenticated = false; @@ -55,23 +175,23 @@ int MifareAuth4(mf4Session *session, uint8_t *keyn, uint8_t *key, bool activateF return 3; } - aes_decode(NULL, key, &data[1], Rnd2, 16); - Rnd2[16] = Rnd2[0]; + aes_decode(NULL, key, &data[1], RndB, 16); + RndB[16] = RndB[0]; if (verbose) - PrintAndLog("Rnd2: %s", sprint_hex(Rnd2, 16)); + PrintAndLog("RndB: %s", sprint_hex(RndB, 16)); uint8_t cmd2[33] = {0}; cmd2[0] = 0x72; uint8_t raw[32] = {0}; - memmove(raw, Rnd1, 16); - memmove(&raw[16], &Rnd2[1], 16); + memmove(raw, RndA, 16); + memmove(&raw[16], &RndB[1], 16); aes_encode(NULL, key, raw, &cmd2[1], 32); if (verbose) PrintAndLog(">phase2: %s", sprint_hex(cmd2, 33)); - res = ExchangeRAW14a(cmd2, sizeof(cmd2), false, false, data, sizeof(data), &datalen); + res = ExchangeRAW14a(cmd2, sizeof(cmd2), false, true, data, sizeof(data), &datalen); if (res) { PrintAndLog("ERROR exchande raw error: %d", res); DropField(); @@ -85,19 +205,49 @@ int MifareAuth4(mf4Session *session, uint8_t *keyn, uint8_t *key, bool activateF if (verbose) { PrintAndLog("res: %s", sprint_hex(raw, 32)); - PrintAndLog("Rnd1`: %s", sprint_hex(&raw[4], 16)); + PrintAndLog("RndA`: %s", sprint_hex(&raw[4], 16)); } - if (memcmp(&raw[4], &Rnd1[1], 16)) { + if (memcmp(&raw[4], &RndA[1], 16)) { PrintAndLog("\nERROR: Authentication FAILED. rnd not equal"); if (verbose) { - PrintAndLog("rnd1 reader: %s", sprint_hex(&Rnd1[1], 16)); - PrintAndLog("rnd1 card: %s", sprint_hex(&raw[4], 16)); + PrintAndLog("RndA reader: %s", sprint_hex(&RndA[1], 16)); + PrintAndLog("RndA card: %s", sprint_hex(&raw[4], 16)); } DropField(); return 5; } + if (verbose) { + PrintAndLog(" TI: %s", sprint_hex(raw, 4)); + PrintAndLog("pic: %s", sprint_hex(&raw[20], 6)); + PrintAndLog("pcd: %s", sprint_hex(&raw[26], 6)); + } + + uint8_t kenc[16] = {0}; + memcpy(&kenc[0], &RndA[11], 5); + memcpy(&kenc[5], &RndB[11], 5); + for(int i = 0; i < 5; i++) + kenc[10 + i] = RndA[4 + i] ^ RndB[4 + i]; + kenc[15] = 0x11; + + aes_encode(NULL, key, kenc, kenc, 16); + if (verbose) { + PrintAndLog("kenc: %s", sprint_hex(kenc, 16)); + } + + uint8_t kmac[16] = {0}; + memcpy(&kmac[0], &RndA[7], 5); + memcpy(&kmac[5], &RndB[7], 5); + for(int i = 0; i < 5; i++) + kmac[10 + i] = RndA[0 + i] ^ RndB[0 + i]; + kmac[15] = 0x22; + + aes_encode(NULL, key, kmac, kmac, 16); + if (verbose) { + PrintAndLog("kmac: %s", sprint_hex(kmac, 16)); + } + if (!leaveSignalON) DropField(); @@ -106,9 +256,17 @@ int MifareAuth4(mf4Session *session, uint8_t *keyn, uint8_t *key, bool activateF if (session) { session->Authenticated = true; + session->R_Ctr = 0; + session->W_Ctr = 0; session->KeyNum = keyn[1] + (keyn[0] << 8); - memmove(session->Rnd1, Rnd1, 16); - memmove(session->Rnd2, Rnd2, 16); + memmove(session->RndA, RndA, 16); + memmove(session->RndB, RndB, 16); + memmove(session->Key, key, 16); + memmove(session->TI, raw, 4); + memmove(session->PICCap2, &raw[20], 6); + memmove(session->PCDCap2, &raw[26], 6); + memmove(session->Kenc, kenc, 16); + memmove(session->Kmac, kmac, 16); } PrintAndLog("Authentication OK"); @@ -116,3 +274,38 @@ int MifareAuth4(mf4Session *session, uint8_t *keyn, uint8_t *key, bool activateF 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) { + if (sectorNo < 32) + return 4; + else + return 16; +} + +uint8_t mfFirstBlockOfSector(uint8_t sectorNo) { + if (sectorNo < 32) + return sectorNo * 4; + else + return 32 * 4 + (sectorNo - 32) * 16; +} + +uint8_t mfSectorTrailer(uint8_t blockNo) { + if (blockNo < 32*4) { + return (blockNo | 0x03); + } else { + return (blockNo | 0x0f); + } +} + +bool mfIsSectorTrailer(uint8_t blockNo) { + return (blockNo == mfSectorTrailer(blockNo)); +} + +uint8_t mfSectorNum(uint8_t blockNo) { + if (blockNo < 32 * 4) + return blockNo / 4; + else + return 32 + (blockNo - 32 * 4) / 16; + +}