From d47c8c701a3a3f69115ee06fb7969cf159f3b6e9 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Wed, 20 Sep 2017 07:32:15 +0200 Subject: [PATCH 001/310] Fix clock_gettime() for Apple OSX (also fixes hf mf hardnested) --- client/util_posix.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/client/util_posix.c b/client/util_posix.c index dd3d714c..8f3ed46b 100644 --- a/client/util_posix.c +++ b/client/util_posix.c @@ -35,7 +35,7 @@ void msleep(uint32_t n) { } #endif // _WIN32 -#ifdef __MACH__ +#ifdef __APPLE__ #define CLOCK_MONOTONIC (1) #define CLOCK_REALTIME (2) @@ -63,20 +63,18 @@ void msleep(uint32_t n) { } else if (clk_id == CLOCK_MONOTONIC) { static uint64_t clock_start_time = 0; - static mach_timebase_info_data_t timebase_ifo = {0, 0}; + static mach_timebase_info_data_t timebase_info = {0, 0}; uint64_t now = mach_absolute_time(); if (clock_start_time == 0) { - //kern_return_t mach_status = mach_timebase_info(&timebase_ifo); - // appease "unused variable" warning for release builds - //(void)mach_status; + mach_timebase_info(&timebase_info); clock_start_time = now; } now = (uint64_t)((double)(now - clock_start_time) - * (double)timebase_ifo.numer - / (double)timebase_ifo.denom); + * (double)timebase_info.numer + / (double)timebase_info.denom); t->tv_sec = now / 1000000000; t->tv_nsec = now % 1000000000; From 3a05a1e73940bfa13ce5f732fd5d14ee333c6977 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Fri, 22 Sep 2017 20:40:42 +0300 Subject: [PATCH 002/310] reworking magic cheneese card wipe (#365) Implement hf mf cwipe. Remove wipe parameter from hf mf csetuid. --- CHANGELOG.md | 2 + armsrc/appmain.c | 3 + armsrc/apps.h | 3 +- armsrc/mifarecmd.c | 146 ++++++++++++++++++++++++++++++++++++-- client/cmdhfmf.c | 169 ++++++++++++++++++++++++++++++++------------ client/cmdhfmf.h | 1 + client/mifarehost.c | 161 +++++++++++++++++++++++------------------ client/mifarehost.h | 3 +- client/util.c | 11 ++- client/util.h | 1 + include/usb_cmd.h | 1 + 11 files changed, 382 insertions(+), 119 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f34b78f0..da0e30ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac ### Changed - Improved backdoor detection missbehaving magic s50/1k tag (Fl0-0) +- Deleted wipe functionality from `hf mf csetuid` (Merlok) ### Fixed @@ -17,6 +18,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added backdoor detection for gen1b magic s70/4k tag (Fl0-0) - Added data fsktonrz, a fsk cleaning/demodulating routine for weak fsk signal. Note: follow this up with a `data rawdemod nr` to finish demoding your signal. (marshmellow) - Added lf em 410xbrute, LF EM410x reader bruteforce attack by simulating UIDs from a file (Fl0-0) +- Added `hf mf cwipe` command. It wipes "magic Chinese" card. For 1a generation it uses card's "wipe" command. For gen1a and gen1b it uses a write command. (Merlok) ## [3.0.1][2017-06-08] diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 25285396..b375c3ce 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1199,6 +1199,9 @@ void UsbPacketReceived(uint8_t *packet, int len) break; // Work with "magic Chinese" card + case CMD_MIFARE_CWIPE: + MifareCWipe(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); + break; case CMD_MIFARE_CSETBLOCK: MifareCSetBlock(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); break; diff --git a/armsrc/apps.h b/armsrc/apps.h index aa5b47fb..1ce3215f 100644 --- a/armsrc/apps.h +++ b/armsrc/apps.h @@ -134,7 +134,8 @@ void MifareEMemClr(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) void MifareEMemSet(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); void MifareEMemGet(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); void MifareECardLoad(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); -void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); // Work with "magic Chinese" card +void MifareCWipe(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); // Work with "magic Chinese" card +void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); void MifareCGetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); void MifareCIdent(); // is "magic chinese" card? void MifareUSetPwd(uint8_t arg0, uint8_t *datain); diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 0e9c3672..60a85c80 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -1170,6 +1170,143 @@ void MifareECardLoad(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datai // Work with "magic Chinese" card (email him: ouyangweidaxian@live.cn) // //----------------------------------------------------------------------------- + +static bool isBlockTrailer(int blockN) { + if (blockN >= 0 && blockN < 128) { + return ((blockN & 0x03) == 0x03); + } + if (blockN >= 128 && blockN <= 256) { + return ((blockN & 0x0F) == 0x0F); + } + return FALSE; +} + +void MifareCWipe(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain){ + // var + byte_t isOK = 0; + uint32_t numBlocks = arg0; + // cmdParams: + // bit 0 - wipe gen1a + // bit 1 - fill card with default data + // bit 2 - gen1a = 0, gen1b = 1 + uint8_t cmdParams = arg1; + bool needWipe = cmdParams & 0x01; + bool needFill = cmdParams & 0x02; + bool gen1b = cmdParams & 0x04; + + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE]; + uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE]; + + uint8_t block0[16] = {0x01, 0x02, 0x03, 0x04, 0x04, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xAF}; + uint8_t block1[16] = {0x00}; + uint8_t blockK[16] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x08, 0x77, 0x8F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + uint8_t d_block[18] = {0x00}; + + // card commands + uint8_t wupC1[] = { 0x40 }; + uint8_t wupC2[] = { 0x43 }; + uint8_t wipeC[] = { 0x41 }; + + // iso14443 setup + LED_A_ON(); + LED_B_OFF(); + LED_C_OFF(); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + // tracing + clear_trace(); + set_tracing(true); + + while (true){ + // wipe + if (needWipe){ + ReaderTransmitBitsPar(wupC1,7,0, NULL); + if(!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= 1) Dbprintf("wupC1 error"); + break; + }; + + ReaderTransmit(wipeC, sizeof(wipeC), NULL); + if(!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= 1) Dbprintf("wipeC error"); + break; + }; + + if(mifare_classic_halt(NULL, 0)) { + if (MF_DBGLEVEL > 2) Dbprintf("Halt error"); + }; + }; + + // put default data + if (needFill){ + // select commands + ReaderTransmitBitsPar(wupC1, 7, 0, NULL); + + // gen1b magic tag : do no issue wupC2 and don't expect 0x0a response after SELECT_UID (after getting UID from chip in 'hf mf csetuid' command) + if (!gen1b) { + + if(!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= 1) Dbprintf("wupC1 error"); + break; + }; + + ReaderTransmit(wupC2, sizeof(wupC2), NULL); + if(!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= 1) Dbprintf("wupC2 error"); + break; + }; + } + + // send blocks command + for (int blockNo = 0; blockNo < numBlocks; blockNo++) { + if ((mifare_sendcmd_short(NULL, 0, 0xA0, blockNo, receivedAnswer, receivedAnswerPar, NULL) != 1) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= 1) Dbprintf("write block send command error"); + break; + }; + + // check type of block and add crc + if (!isBlockTrailer(blockNo)){ + memcpy(d_block, block1, 16); + } else { + memcpy(d_block, blockK, 16); + } + if (blockNo == 0) { + memcpy(d_block, block0, 16); + } + AppendCrc14443a(d_block, 16); + + // send write command + ReaderTransmit(d_block, sizeof(d_block), NULL); + if ((ReaderReceive(receivedAnswer, receivedAnswerPar) != 1) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= 1) Dbprintf("write block send data error"); + break; + }; + } + + // halt + // do no issue halt command for gen1b + if (!gen1b) { + if (mifare_classic_halt(NULL, 0)) { + if (MF_DBGLEVEL > 2) Dbprintf("Halt error"); + break; + } + } + } + break; + } + + // send USB response + LED_B_ON(); + cmd_send(CMD_ACK,isOK,0,0,NULL,0); + LED_B_OFF(); + + // reset fpga + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + + return; +} + void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain){ // params @@ -1214,13 +1351,14 @@ void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datai if (workFlags & 0x01) { if(!iso14443a_select_card(uid, NULL, &cuid, true, 0)) { if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); - break; + // Continue, if we set wrong UID or wrong UID checksum or some ATQA or SAK we will can't select card. But we need to write block 0 to make card work. + //break; }; if(mifare_classic_halt(NULL, cuid)) { if (MF_DBGLEVEL > 2) Dbprintf("Halt error"); // Continue, some magic tags misbehavies and send an answer to it. - // break; + // break; }; }; @@ -1239,7 +1377,7 @@ void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datai break; }; - if(mifare_classic_halt(NULL, cuid)) { + if(mifare_classic_halt(NULL, 0)) { if (MF_DBGLEVEL > 2) Dbprintf("Halt error"); // Continue, some magic tags misbehavies and send an answer to it. // break; @@ -1283,7 +1421,7 @@ void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datai if (workFlags & 0x04) { // do no issue halt command for gen1b magic tag (#db# halt error. response len: 1) if (!(workFlags & 0x40)) { - if (mifare_classic_halt(NULL, cuid)) { + if (mifare_classic_halt(NULL, 0)) { if (MF_DBGLEVEL > 2) Dbprintf("Halt error"); // Continue, some magic tags misbehavies and send an answer to it. // break; diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 90ebc27b..5a9896de 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -1783,65 +1783,69 @@ int CmdHF14AMfEKeyPrn(const char *Cmd) int CmdHF14AMfCSetUID(const char *Cmd) { - uint8_t wipeCard = 0; uint8_t uid[8] = {0x00}; uint8_t oldUid[8] = {0x00}; uint8_t atqa[2] = {0x00}; uint8_t sak[1] = {0x00}; - uint8_t atqaPresent = 1; + uint8_t atqaPresent = 0; int res; - char ctmp; - int argi=0; - if (strlen(Cmd) < 1 || param_getchar(Cmd, argi) == 'h') { - PrintAndLog("Usage: hf mf csetuid [ATQA 4 hex symbols SAK 2 hex symbols] [w]"); - PrintAndLog("sample: hf mf csetuid 01020304"); - PrintAndLog("sample: hf mf csetuid 01020304 0004 08 w"); - PrintAndLog("Set UID, ATQA, and SAK for magic Chinese card (only works with such cards)"); - PrintAndLog("If you also want to wipe the card then add 'w' at the end of the command line."); - return 0; - } - - if (param_getchar(Cmd, argi) && param_gethex(Cmd, argi, uid, 8)) { + uint8_t needHelp = 0; + char cmdp = 1; + + if (param_getchar(Cmd, 0) && param_gethex(Cmd, 0, uid, 8)) { PrintAndLog("UID must include 8 HEX symbols"); return 1; } - argi++; - ctmp = param_getchar(Cmd, argi); - if (ctmp == 'w' || ctmp == 'W') { - wipeCard = 1; - atqaPresent = 0; - } - - if (atqaPresent) { - if (param_getchar(Cmd, argi)) { - if (param_gethex(Cmd, argi, atqa, 4)) { - PrintAndLog("ATQA must include 4 HEX symbols"); - return 1; - } - argi++; - if (!param_getchar(Cmd, argi) || param_gethex(Cmd, argi, sak, 2)) { - PrintAndLog("SAK must include 2 HEX symbols"); - return 1; - } - argi++; - } else - atqaPresent = 0; - } - - if(!wipeCard) { - ctmp = param_getchar(Cmd, argi); - if (ctmp == 'w' || ctmp == 'W') { - wipeCard = 1; + if (param_getlength(Cmd, 1) > 1 && param_getlength(Cmd, 2) > 1) { + atqaPresent = 1; + cmdp = 3; + + if (param_gethex(Cmd, 1, atqa, 4)) { + PrintAndLog("ATQA must include 4 HEX symbols"); + return 1; + } + + if (param_gethex(Cmd, 2, sak, 2)) { + PrintAndLog("SAK must include 2 HEX symbols"); + return 1; } } - PrintAndLog("--wipe card:%s uid:%s", (wipeCard)?"YES":"NO", sprint_hex(uid, 4)); + while(param_getchar(Cmd, cmdp) != 0x00) + { + switch(param_getchar(Cmd, cmdp)) + { + case 'h': + case 'H': + needHelp = 1; + break; + default: + PrintAndLog("ERROR: Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + needHelp = 1; + break; + } + cmdp++; + } - res = mfCSetUID(uid, (atqaPresent)?atqa:NULL, (atqaPresent)?sak:NULL, oldUid, wipeCard); + if (strlen(Cmd) < 1 || needHelp) { + PrintAndLog(""); + PrintAndLog("Usage: hf mf csetuid [ATQA 4 hex symbols SAK 2 hex symbols]"); + PrintAndLog("sample: hf mf csetuid 01020304"); + PrintAndLog("sample: hf mf csetuid 01020304 0004 08"); + PrintAndLog("Set UID, ATQA, and SAK for magic Chinese card (only works with such cards)"); + return 0; + } + + PrintAndLog("uid:%s", sprint_hex(uid, 4)); + if (atqaPresent) { + PrintAndLog("--atqa:%s sak:%02x", sprint_hex(atqa, 2), sak[0]); + } + + res = mfCSetUID(uid, (atqaPresent)?atqa:NULL, (atqaPresent)?sak:NULL, oldUid); if (res) { - PrintAndLog("Can't set UID. error=%d", res); + PrintAndLog("Can't set UID. Error=%d", res); return 1; } @@ -1850,6 +1854,80 @@ int CmdHF14AMfCSetUID(const char *Cmd) return 0; } +static int ParamGetCardSize(const char c) { + int numBlocks = 16 * 4; + switch (c) { + case '0' : numBlocks = 5 * 4; break; + case '2' : numBlocks = 32 * 4; break; + case '4' : numBlocks = 32 * 4 + 8 * 16; break; + default: numBlocks = 16 * 4; + } + return numBlocks; +} + +int CmdHF14AMfCWipe(const char *Cmd) +{ + int res, gen = 0; + int numBlocks = 16 * 4; + bool wipeCard = false; + bool fillCard = false; + + if (strlen(Cmd) < 1 || param_getchar(Cmd, 0) == 'h') { + PrintAndLog("Usage: hf mf cwipe [card size] [w] [p]"); + PrintAndLog("sample: hf mf cwipe 1 w s"); + PrintAndLog("[card size]: 0 = 320 bytes (Mifare Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); + PrintAndLog("w - Wipe magic Chinese card (only works with gen:1a cards)"); + PrintAndLog("f - Fill the card with default data and keys (works with gen:1a and gen:1b cards only)"); + return 0; + } + + gen = mfCIdentify(); + if ((gen != 1) && (gen != 2)) + return 1; + + numBlocks = ParamGetCardSize(param_getchar(Cmd, 0)); + + char cmdp = 0; + while(param_getchar(Cmd, cmdp) != 0x00){ + switch(param_getchar(Cmd, cmdp)) { + case 'w': + case 'W': + wipeCard = 1; + break; + case 'f': + case 'F': + fillCard = 1; + break; + default: + break; + } + cmdp++; + } + + if (!wipeCard && !fillCard) + wipeCard = TRUE; + + PrintAndLog("--blocks count:%2d wipe:%c fill:%c", numBlocks, (wipeCard)?'y':'n', (fillCard)?'y':'n'); + + if (gen == 2) { + /* generation 1b magic card */ + if (wipeCard) { + PrintAndLog("WARNING: can't wipe magic card 1b generation"); + } + res = mfCWipe(numBlocks, true, false, fillCard); + } else { + /* generation 1a magic card by default */ + res = mfCWipe(numBlocks, false, wipeCard, fillCard); + } + + if (res) { + PrintAndLog("Can't wipe. error=%d", res); + return 1; + } + PrintAndLog("OK"); + return 0; +} + int CmdHF14AMfCSetBlk(const char *Cmd) { uint8_t memBlock[16] = {0x00}; @@ -1866,6 +1944,8 @@ int CmdHF14AMfCSetBlk(const char *Cmd) } gen = mfCIdentify(); + if ((gen != 1) && (gen != 2)) + return 1; blockNo = param_get8(Cmd, 0); @@ -2398,6 +2478,7 @@ static command_t CommandTable[] = {"esave", CmdHF14AMfESave, 0, "Save to file emul dump"}, {"ecfill", CmdHF14AMfECFill, 0, "Fill simulator memory with help of keys from simulator"}, {"ekeyprn", CmdHF14AMfEKeyPrn, 0, "Print keys from simulator memory"}, + {"cwipe", CmdHF14AMfCWipe, 0, "Wipe magic Chinese card"}, {"csetuid", CmdHF14AMfCSetUID, 0, "Set UID for magic Chinese card"}, {"csetblk", CmdHF14AMfCSetBlk, 0, "Write block - Magic Chinese card"}, {"cgetblk", CmdHF14AMfCGetBlk, 0, "Read block - Magic Chinese card"}, diff --git a/client/cmdhfmf.h b/client/cmdhfmf.h index fc87b228..235fd86d 100644 --- a/client/cmdhfmf.h +++ b/client/cmdhfmf.h @@ -34,6 +34,7 @@ extern int CmdHF14AMfELoad(const char* cmd); extern int CmdHF14AMfESave(const char* cmd); extern int CmdHF14AMfECFill(const char* cmd); extern int CmdHF14AMfEKeyPrn(const char* cmd); +extern int CmdHF14AMfCWipe(const char* cmd); extern int CmdHF14AMfCSetUID(const char* cmd); extern int CmdHF14AMfCSetBlk(const char* cmd); extern int CmdHF14AMfCGetBlk(const char* cmd); diff --git a/client/mifarehost.c b/client/mifarehost.c index 92ffbd4e..8a840d47 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -396,7 +396,8 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount) { UsbCommand c = {CMD_MIFARE_EML_MEMGET, {blockNum, blocksCount, 0}}; SendCommand(&c); - UsbCommand resp; + + UsbCommand resp; if (!WaitForResponseTimeout(CMD_ACK,&resp,1500)) return 1; memcpy(data, resp.d.asBytes, blocksCount * 16); return 0; @@ -416,6 +417,7 @@ int mfCGetBlock(uint8_t blockNo, uint8_t *data, uint8_t params) { UsbCommand c = {CMD_MIFARE_CGETBLOCK, {params, 0, blockNo}}; SendCommand(&c); + UsbCommand resp; if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) { isOK = resp.arg[0] & 0xff; @@ -434,8 +436,9 @@ int mfCSetBlock(uint8_t blockNo, uint8_t *data, uint8_t *uid, bool wantWipe, uin UsbCommand c = {CMD_MIFARE_CSETBLOCK, {wantWipe, params & (0xFE | (uid == NULL ? 0:1)), blockNo}}; memcpy(c.d.asBytes, data, 16); SendCommand(&c); + UsbCommand resp; - if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) { + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { isOK = resp.arg[0] & 0xff; if (uid != NULL) memcpy(uid, resp.d.asBytes, 4); @@ -445,25 +448,40 @@ int mfCSetBlock(uint8_t blockNo, uint8_t *data, uint8_t *uid, bool wantWipe, uin PrintAndLog("Command execute timeout"); return 1; } + return 0; } -int mfCSetUID(uint8_t *uid, uint8_t *atqa, uint8_t *sak, uint8_t *oldUID, bool wantWipe) { +int mfCWipe(uint32_t numSectors, bool gen1b, bool wantWipe, bool wantFill) { + uint8_t isOK = 0; + uint8_t cmdParams = wantWipe + wantFill * 0x02 + gen1b * 0x04; + UsbCommand c = {CMD_MIFARE_CWIPE, {numSectors, cmdParams, 0}}; + SendCommand(&c); + + UsbCommand resp; + WaitForResponse(CMD_ACK,&resp); + isOK = resp.arg[0] & 0xff; + + return isOK; +} + +int mfCSetUID(uint8_t *uid, uint8_t *atqa, uint8_t *sak, uint8_t *oldUID) { uint8_t oldblock0[16] = {0x00}; uint8_t block0[16] = {0x00}; - int old, gen = 0; + int gen = 0, res; gen = mfCIdentify(); + /* generation 1a magic card by default */ + uint8_t cmdParams = CSETBLOCK_SINGLE_OPER; if (gen == 2) { /* generation 1b magic card */ - old = mfCGetBlock(0, oldblock0, CSETBLOCK_SINGLE_OPER | CSETBLOCK_MAGIC_1B); - } else { - /* generation 1a magic card by default */ - old = mfCGetBlock(0, oldblock0, CSETBLOCK_SINGLE_OPER); + cmdParams = CSETBLOCK_SINGLE_OPER | CSETBLOCK_MAGIC_1B; } + + res = mfCGetBlock(0, oldblock0, cmdParams); - if (old == 0) { + if (res == 0) { memcpy(block0, oldblock0, 16); PrintAndLog("old block 0: %s", sprint_hex(block0,16)); } else { @@ -474,25 +492,73 @@ int mfCSetUID(uint8_t *uid, uint8_t *atqa, uint8_t *sak, uint8_t *oldUID, bool w // UID memcpy(block0, uid, 4); // Mifare UID BCC - block0[4] = block0[0]^block0[1]^block0[2]^block0[3]; + block0[4] = block0[0] ^ block0[1] ^ block0[2] ^ block0[3]; // mifare classic SAK(byte 5) and ATQA(byte 6 and 7, reversed) - if (sak!=NULL) - block0[5]=sak[0]; - if (atqa!=NULL) { - block0[6]=atqa[1]; - block0[7]=atqa[0]; + if (sak != NULL) + block0[5] = sak[0]; + if (atqa != NULL) { + block0[6] = atqa[1]; + block0[7] = atqa[0]; } - PrintAndLog("new block 0: %s", sprint_hex(block0,16)); + PrintAndLog("new block 0: %s", sprint_hex(block0, 16)); - if (gen == 2) { - /* generation 1b magic card */ - return mfCSetBlock(0, block0, oldUID, wantWipe, CSETBLOCK_SINGLE_OPER | CSETBLOCK_MAGIC_1B); - } else { - /* generation 1a magic card by default */ - return mfCSetBlock(0, block0, oldUID, wantWipe, CSETBLOCK_SINGLE_OPER); + res = mfCSetBlock(0, block0, oldUID, false, cmdParams); + if (res) { + PrintAndLog("Can't set block 0. Error: %d", res); + return res; } + + return 0; } +int mfCIdentify() +{ + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0}}; + SendCommand(&c); + + UsbCommand resp; + WaitForResponse(CMD_ACK,&resp); + + iso14a_card_select_t card; + memcpy(&card, (iso14a_card_select_t *)resp.d.asBytes, sizeof(iso14a_card_select_t)); + + uint64_t select_status = resp.arg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision + + if(select_status != 0) { + uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0 + c.arg[0] = ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT; + c.arg[1] = 2; + c.arg[2] = 0; + memcpy(c.d.asBytes, rats, 2); + SendCommand(&c); + WaitForResponse(CMD_ACK,&resp); + } + + c.cmd = CMD_MIFARE_CIDENT; + c.arg[0] = 0; + c.arg[1] = 0; + c.arg[2] = 0; + SendCommand(&c); + WaitForResponse(CMD_ACK,&resp); + + uint8_t isGeneration = resp.arg[0] & 0xff; + switch( isGeneration ){ + case 1: PrintAndLog("Chinese magic backdoor commands (GEN 1a) detected"); break; + case 2: PrintAndLog("Chinese magic backdoor command (GEN 1b) detected"); break; + default: PrintAndLog("No chinese magic backdoor command detected"); break; + } + + // disconnect + c.cmd = CMD_READER_ISO_14443a; + c.arg[0] = 0; + c.arg[1] = 0; + c.arg[2] = 0; + SendCommand(&c); + + return (int) isGeneration; +} + + // SNIFFER // constants @@ -531,7 +597,7 @@ int isBlockEmpty(int blockN) { } int isBlockTrailer(int blockN) { - return ((blockN & 0x03) == 0x03); + return ((blockN & 0x03) == 0x03); } int saveTraceCard(void) { @@ -593,6 +659,7 @@ int loadTraceCard(uint8_t *tuid) { blockNum++; } fclose(f); + return 0; } @@ -613,6 +680,7 @@ int mfTraceInit(uint8_t *tuid, uint8_t *atqa, uint8_t sak, bool wantSaveToEmlFil uid = bytes_to_num(tuid + 3, 4); traceState = TRACE_IDLE; + return 0; } @@ -815,6 +883,8 @@ int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile) { return 0; } +// DECODING + int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len){ /* uint32_t nt; // tag challenge @@ -835,48 +905,3 @@ int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, return 0; } -int mfCIdentify() -{ - UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0}}; - SendCommand(&c); - UsbCommand resp; - WaitForResponse(CMD_ACK,&resp); - - iso14a_card_select_t card; - memcpy(&card, (iso14a_card_select_t *)resp.d.asBytes, sizeof(iso14a_card_select_t)); - - uint64_t select_status = resp.arg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision - - if(select_status != 0) { - uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0 - c.arg[0] = ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT; - c.arg[1] = 2; - c.arg[2] = 0; - memcpy(c.d.asBytes, rats, 2); - SendCommand(&c); - WaitForResponse(CMD_ACK,&resp); - } - - c.cmd = CMD_MIFARE_CIDENT; - c.arg[0] = 0; - c.arg[1] = 0; - c.arg[2] = 0; - SendCommand(&c); - WaitForResponse(CMD_ACK,&resp); - - uint8_t isGeneration = resp.arg[0] & 0xff; - switch( isGeneration ){ - case 1: PrintAndLog("Chinese magic backdoor commands (GEN 1a) detected"); break; - case 2: PrintAndLog("Chinese magic backdoor command (GEN 1b) detected"); break; - default: PrintAndLog("No chinese magic backdoor command detected"); break; - } - - // disconnect - c.cmd = CMD_READER_ISO_14443a; - c.arg[0] = 0; - c.arg[1] = 0; - c.arg[2] = 0; - SendCommand(&c); - - return (int) isGeneration; -} diff --git a/client/mifarehost.h b/client/mifarehost.h index 7f9a2b45..34793a29 100644 --- a/client/mifarehost.h +++ b/client/mifarehost.h @@ -33,7 +33,8 @@ extern int mfCheckKeys (uint8_t blockNo, uint8_t keyType, bool clear_trace, uint extern int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount); extern int mfEmlSetMem(uint8_t *data, int blockNum, int blocksCount); -extern int mfCSetUID(uint8_t *uid, uint8_t *atqa, uint8_t *sak, uint8_t *oldUID, bool wantWipe); +extern int mfCWipe(uint32_t numSectors, bool gen1b, bool wantWipe, bool wantFill); +extern int mfCSetUID(uint8_t *uid, uint8_t *atqa, uint8_t *sak, uint8_t *oldUID); extern int mfCSetBlock(uint8_t blockNo, uint8_t *data, uint8_t *uid, bool wantWipe, uint8_t params); extern int mfCGetBlock(uint8_t blockNo, uint8_t *data, uint8_t params); diff --git a/client/util.c b/client/util.c index 38dd3a12..86e8c502 100644 --- a/client/util.c +++ b/client/util.c @@ -322,7 +322,7 @@ char * printBits(size_t const size, void const * const ptr) // ------------------------------------------------------------------------- // line - param line -// bg, en - symbol numbers in param line of beginning an ending parameter +// bg, en - symbol numbers in param line of beginning and ending parameter // paramnum - param number (from 0) // ------------------------------------------------------------------------- int param_getptr(const char *line, int *bg, int *en, int paramnum) @@ -355,6 +355,15 @@ int param_getptr(const char *line, int *bg, int *en, int paramnum) } +int param_getlength(const char *line, int paramnum) +{ + int bg, en; + + if (param_getptr(line, &bg, &en, paramnum)) return 0; + + return en - bg + 1; +} + char param_getchar(const char *line, int paramnum) { int bg, en; diff --git a/client/util.h b/client/util.h index 640ef434..6177dd93 100644 --- a/client/util.h +++ b/client/util.h @@ -51,6 +51,7 @@ extern uint32_t SwapBits(uint32_t value, int nrbits); extern uint8_t *SwapEndian64(const uint8_t *src, const size_t len, const uint8_t blockSize); extern void SwapEndian64ex(const uint8_t *src, const size_t len, const uint8_t blockSize, uint8_t *dest); +extern int param_getlength(const char *line, int paramnum); extern char param_getchar(const char *line, int paramnum); extern int param_getptr(const char *line, int *bg, int *en, int paramnum); extern uint8_t param_get8(const char *line, int paramnum); diff --git a/include/usb_cmd.h b/include/usb_cmd.h index 0ab9730e..194a9d53 100644 --- a/include/usb_cmd.h +++ b/include/usb_cmd.h @@ -175,6 +175,7 @@ typedef struct{ #define CMD_MIFARE_CSETBLOCK 0x0605 #define CMD_MIFARE_CGETBLOCK 0x0606 #define CMD_MIFARE_CIDENT 0x0607 +#define CMD_MIFARE_CWIPE 0x0608 #define CMD_SIMULATE_MIFARE_CARD 0x0610 From 2ce43a28f7f7fb1c6f3c091f31d5cc4f68f36082 Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 26 Sep 2017 14:29:08 +0300 Subject: [PATCH 003/310] fixed #395 --- client/cmdhfmf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 5a9896de..5999d20e 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -1905,7 +1905,7 @@ int CmdHF14AMfCWipe(const char *Cmd) } if (!wipeCard && !fillCard) - wipeCard = TRUE; + wipeCard = true; PrintAndLog("--blocks count:%2d wipe:%c fill:%c", numBlocks, (wipeCard)?'y':'n', (fillCard)?'y':'n'); From a2d058f3aa4bde441b1a7912844d0eb6d13f455d Mon Sep 17 00:00:00 2001 From: Fl0-0 Date: Tue, 26 Sep 2017 16:36:05 +0200 Subject: [PATCH 004/310] Fix typo and gcc-7 warnings (#401) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix typo in hf mf csave help * Fix gcc 7 warning: '~' on an expression of type bool [-Wbool-operation], use logical ! not instead of ~ * Fix gcc 7 warning: ‘memset’ used with length equal to number of elements without multiplication by element size [-Wmemset-elt-size] * Fix gcc 7 warning: warning: duplicate ‘const’ declaration specifier [-Wduplicate-decl-specifier] --- client/cmdhfmf.c | 14 +++++++------- client/cmdlfem4x.c | 2 +- client/hardnested/hardnested_bf_core.c | 2 +- client/loclass/cipher.c | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 5999d20e..aafbce2a 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -2193,13 +2193,13 @@ int CmdHF14AMfCSave(const char *Cmd) { if (param_getchar(Cmd, 0) == 'h') { PrintAndLog("It saves `magic Chinese` card dump into the file `filename.eml` or `cardID.eml`"); PrintAndLog("or into emulator memory (option `e`). 4K card: (option `4`)"); - PrintAndLog("Usage: hf mf esave [file name w/o `.eml`][e][4]"); - PrintAndLog("Sample: hf mf esave "); - PrintAndLog(" hf mf esave filename"); - PrintAndLog(" hf mf esave e"); - PrintAndLog(" hf mf esave 4"); - PrintAndLog(" hf mf esave filename 4"); - PrintAndLog(" hf mf esave e 4"); + PrintAndLog("Usage: hf mf csave [file name w/o `.eml`][e][4]"); + PrintAndLog("Sample: hf mf csave "); + PrintAndLog(" hf mf csave filename"); + PrintAndLog(" hf mf csave e"); + PrintAndLog(" hf mf csave 4"); + PrintAndLog(" hf mf csave filename 4"); + PrintAndLog(" hf mf csave e 4"); return 0; } diff --git a/client/cmdlfem4x.c b/client/cmdlfem4x.c index 013c2f3f..f5dfee35 100644 --- a/client/cmdlfem4x.c +++ b/client/cmdlfem4x.c @@ -606,7 +606,7 @@ int EM4x50Read(const char *Cmd, bool verbose) char tmp2[20]; int phaseoff; high = low = 0; - memset(tmpbuff, 0, MAX_GRAPH_TRACE_LEN / 64); + memset(tmpbuff, 0, sizeof(tmpbuff)); // get user entry if any sscanf(Cmd, "%i %i", &clk, &invert); diff --git a/client/hardnested/hardnested_bf_core.c b/client/hardnested/hardnested_bf_core.c index 5e81c2ba..3c0c044f 100644 --- a/client/hardnested/hardnested_bf_core.c +++ b/client/hardnested/hardnested_bf_core.c @@ -342,7 +342,7 @@ const uint64_t CRACK_STATES_BITSLICED(uint32_t cuid, uint8_t *best_first_bytes, // } #endif // add the even state bits - const bitslice_t const *restrict bitsliced_even_state = bitsliced_even_states[block_idx]; + const bitslice_t *restrict bitsliced_even_state = bitsliced_even_states[block_idx]; for(uint32_t state_idx = 1; state_idx < STATE_SIZE; state_idx += 2) { state_p[state_idx] = bitsliced_even_state[state_idx/2]; } diff --git a/client/loclass/cipher.c b/client/loclass/cipher.c index a701da31..57bc831f 100644 --- a/client/loclass/cipher.c +++ b/client/loclass/cipher.c @@ -114,9 +114,9 @@ uint8_t _select(bool x, bool y, uint8_t r) bool r6 = r >> 1 & 0x1; bool r7 = r & 0x1; - bool z0 = (r0 & r2) ^ (r1 & ~r3) ^ (r2 | r4); + bool z0 = (r0 & r2) ^ (r1 & !r3) ^ (r2 | r4); bool z1 = (r0 | r2) ^ ( r5 | r7) ^ r1 ^ r6 ^ x ^ y; - bool z2 = (r3 & ~r5) ^ (r4 & r6 ) ^ r7 ^ x; + bool z2 = (r3 & !r5) ^ (r4 & r6 ) ^ r7 ^ x; // The three bitz z0.. z1 are packed into a uint8_t: // 00000ZZZ From f53eb07de0312bde80ba46f0b7dc79c51bcb85f2 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Mon, 25 Sep 2017 18:31:10 +0200 Subject: [PATCH 005/310] Don't ignore environment variables CFLAGS and LDFLAGS * they are be set by some build environments to find include and lib dirs --- client/Makefile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/client/Makefile b/client/Makefile index fd2ae290..3a96e9e9 100644 --- a/client/Makefile +++ b/client/Makefile @@ -12,14 +12,15 @@ TARFLAGS = -C .. --ignore-failed-read -rvf RM = rm -f MV = mv -#COMMON_FLAGS = -m32 +ENV_LDFLAGS := $(LDFLAGS) +ENV_CFLAGS := $(CFLAGS) VPATH = ../common ../zlib ../uart OBJDIR = obj LDLIBS = -L/opt/local/lib -L/usr/local/lib -lreadline -lpthread -lm LUALIB = ../liblua/liblua.a -LDFLAGS = $(COMMON_FLAGS) -CFLAGS = -std=c99 -D_ISOC99_SOURCE -I. -I../include -I../common -I../zlib -I../uart -I/opt/local/include -I../liblua -Wall $(COMMON_FLAGS) -g -O3 +LDFLAGS = $(ENV_LDFLAGS) +CFLAGS = $(ENV_CFLAGS) -std=c99 -D_ISOC99_SOURCE -I. -I../include -I../common -I../zlib -I../uart -I/opt/local/include -I../liblua -Wall -g -O3 CXXFLAGS = -I../include -Wall -O3 LUAPLATFORM = generic From 340f50a53c4cb4a5c217eadabe6347b8dae52c63 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Tue, 26 Sep 2017 18:15:19 +0200 Subject: [PATCH 006/310] Don't ignore environment variables CFLAGS and LDFLAGS for tools as well --- tools/mfkey/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/mfkey/Makefile b/tools/mfkey/Makefile index 92d3be72..9dab37f5 100755 --- a/tools/mfkey/Makefile +++ b/tools/mfkey/Makefile @@ -1,8 +1,8 @@ VPATH = ../../common ../../common/crapto1 ../../client CC = gcc LD = gcc -CFLAGS = -std=c99 -D_ISOC99_SOURCE -I../../include -I../../common -I../../client -Wall -O3 -LDFLAGS = +CFLAGS += -std=c99 -D_ISOC99_SOURCE -I../../include -I../../common -I../../client -Wall -O3 +LDFLAGS += OBJS = crypto1.o crapto1.o parity.o util_posix.o mfkey.o EXES = mfkey32 mfkey64 From ab92e6c301adf29e5091ed263e6ef589f15e33d4 Mon Sep 17 00:00:00 2001 From: Mikhail Yushkovskiy Date: Wed, 27 Sep 2017 23:37:46 +0300 Subject: [PATCH 007/310] Added bus blaster (http://dangerousprototypes.com/docs/Bus_Blaster) configuration for AT91SAM7S512 support in openocd. --- tools/at91sam7s512-busblaster.cfg | 54 +++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 tools/at91sam7s512-busblaster.cfg diff --git a/tools/at91sam7s512-busblaster.cfg b/tools/at91sam7s512-busblaster.cfg new file mode 100644 index 00000000..6555936b --- /dev/null +++ b/tools/at91sam7s512-busblaster.cfg @@ -0,0 +1,54 @@ +## General OpenOCD configuration +# Ports +telnet_port 4444 +gdb_port 3333 + +## Interface configuration section +# Interface + +# you can use +#source [find interface/ftdi/dp_busblaster.cfg] +# or + +interface ftdi +ftdi_device_desc "Dual RS232-HS" +ftdi_vid_pid 0x0403 0x6010 + +ftdi_layout_init 0x0c08 0x0f1b +ftdi_layout_signal nTRST -data 0x0100 -noe 0x0400 +ftdi_layout_signal nSRST -data 0x0200 -noe 0x0800 + +adapter_khz 1000 + +## Chipset configuration section +# use combined on interfaces or targets that can't set TRST/SRST separately +reset_config srst_only srst_pulls_trst + +jtag newtap sam7x cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id 0x3f0f0f0f + +target create sam7x.cpu arm7tdmi -endian little -chain-position sam7x.cpu + +sam7x.cpu configure -event reset-init { + soft_reset_halt + mww 0xfffffd00 0xa5000004 # RSTC_CR: Reset peripherals + mww 0xfffffd44 0x00008000 # WDT_MR: disable watchdog + mww 0xfffffd08 0xa5000001 # RSTC_MR enable user reset + mww 0xfffffc20 0x00005001 # CKGR_MOR : enable the main oscillator + sleep 10 + mww 0xfffffc2c 0x000b1c02 # CKGR_PLLR: 16MHz * 12/2 = 96MHz + sleep 10 + mww 0xfffffc30 0x00000007 # PMC_MCKR : MCK = PLL / 2 = 48 MHz + sleep 10 + mww 0xffffff60 0x00480100 # MC_FMR: flash mode (FWS=1,FMCN=72) + sleep 100 + +} + +# GDB can also flash my flash! +gdb_memory_map enable +gdb_breakpoint_override hard +#armv4_5 core_state arm + +sam7x.cpu configure -work-area-virt 0 -work-area-phys 0x00200000 -work-area-size 0x10000 -work-area-backup 0 +flash bank sam7x512.flash.0 at91sam7 0 0 0 0 sam7x.cpu 0 0 0 0 0 0 0 18432 +flash bank sam7x512.flash.1 at91sam7 0 0 0 0 sam7x.cpu 1 0 0 0 0 0 0 18432 From c04a4b60cefc545641582da165c42b3023f5f753 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Sun, 1 Oct 2017 17:52:10 +0200 Subject: [PATCH 008/310] Don't do iso14443-4 select for mifare emulations on processor cards (IDPrime, SmartMX, ...) --- armsrc/appmain.c | 2 +- armsrc/epa.c | 2 +- armsrc/iso14443a.c | 37 +++++++++++++++++++------------------ armsrc/iso14443a.h | 2 +- armsrc/mifarecmd.c | 34 +++++++++++++++++----------------- client/cmdhf14a.c | 11 ++++++++++- client/cmdhfmfu.c | 2 +- client/mifarehost.c | 24 ++++++++++++------------ include/mifare.h | 3 ++- 9 files changed, 64 insertions(+), 53 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index b375c3ce..e292483b 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -450,7 +450,7 @@ void StandAloneMode14a() SpinDelay(300); } } - if (!iso14443a_select_card(uid, &hi14a_card[selected], &cuid, true, 0)) + if (!iso14443a_select_card(uid, &hi14a_card[selected], &cuid, true, 0, true)) continue; else { diff --git a/armsrc/epa.c b/armsrc/epa.c index f434aa34..fd71430b 100644 --- a/armsrc/epa.c +++ b/armsrc/epa.c @@ -530,7 +530,7 @@ int EPA_Setup() // power up the field iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD); // select the card - return_code = iso14443a_select_card(uid, &card_select_info, NULL, true, 0); + return_code = iso14443a_select_card(uid, &card_select_info, NULL, true, 0, false); if (return_code == 1) { // send the PPS request ReaderTransmit((uint8_t *)pps, sizeof(pps), NULL); diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 29b23833..e9ad2535 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1733,7 +1733,8 @@ int ReaderReceive(uint8_t *receivedAnswer, uint8_t *parity) // fills the card info record unless NULL // if anticollision is false, then the UID must be provided in uid_ptr[] // and num_cascades must be set (1: 4 Byte UID, 2: 7 Byte UID, 3: 10 Byte UID) -int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades) { +// requests ATS unless no_rats is true +int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats) { uint8_t wupa[] = { 0x52 }; // 0x26 - REQA 0x52 - WAKE-UP uint8_t sel_all[] = { 0x93,0x20 }; uint8_t sel_uid[] = { 0x93,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; @@ -1868,24 +1869,24 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u // non iso14443a compliant tag if( (sak & 0x20) == 0) return 2; - // Request for answer to select - AppendCrc14443a(rats, 2); - ReaderTransmit(rats, sizeof(rats), NULL); + if (!no_rats) { + // Request for answer to select + AppendCrc14443a(rats, 2); + ReaderTransmit(rats, sizeof(rats), NULL); - if (!(len = ReaderReceive(resp, resp_par))) return 0; + if (!(len = ReaderReceive(resp, resp_par))) return 0; - - if(p_hi14a_card) { - memcpy(p_hi14a_card->ats, resp, sizeof(p_hi14a_card->ats)); - p_hi14a_card->ats_len = len; + if(p_hi14a_card) { + memcpy(p_hi14a_card->ats, resp, len); + p_hi14a_card->ats_len = len; + } + + // reset the PCB block number + iso14_pcb_blocknum = 0; + + // set default timeout based on ATS + iso14a_set_ATS_timeout(resp); } - - // reset the PCB block number - iso14_pcb_blocknum = 0; - - // set default timeout based on ATS - iso14a_set_ATS_timeout(resp); - return 1; } @@ -1971,7 +1972,7 @@ void ReaderIso14443a(UsbCommand *c) iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); if(!(param & ISO14A_NO_SELECT)) { iso14a_card_select_t *card = (iso14a_card_select_t*)buf; - arg0 = iso14443a_select_card(NULL, card, NULL, true, 0); + arg0 = iso14443a_select_card(NULL, card, NULL, true, 0, param & ISO14A_NO_RATS); cmd_send(CMD_ACK,arg0,card->uidlen,0,buf,sizeof(iso14a_card_select_t)); } } @@ -2168,7 +2169,7 @@ void ReaderMifare(bool first_try) SpinDelay(100); } - if(!iso14443a_select_card(uid, NULL, &cuid, true, 0)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { if (MF_DBGLEVEL >= 1) Dbprintf("Mifare: Can't select card"); continue; } diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index 658216e7..10e50e0f 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -27,6 +27,6 @@ extern int ReaderReceive(uint8_t *receivedAnswer, uint8_t *par); extern void iso14443a_setup(uint8_t fpga_minor_mode); extern int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data); -extern int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *resp_data, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades); +extern int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *resp_data, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats); extern void iso14a_set_trigger(bool enable); #endif /* __ISO14443A_H */ diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 60a85c80..a3f0d374 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -59,7 +59,7 @@ void MifareReadBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) LED_C_OFF(); while (true) { - if(!iso14443a_select_card(uid, NULL, &cuid, true, 0)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); break; }; @@ -106,7 +106,7 @@ void MifareUC_Auth(uint8_t arg0, uint8_t *keybytes){ clear_trace(); - if(!iso14443a_select_card(NULL, NULL, NULL, true, 0)) { + if(!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Can't select card"); OnError(0); return; @@ -141,7 +141,7 @@ void MifareUReadBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) clear_trace(); - int len = iso14443a_select_card(NULL, NULL, NULL, true, 0); + int len = iso14443a_select_card(NULL, NULL, NULL, true, 0, true); if(!len) { if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Can't select card (RC:%02X)",len); OnError(1); @@ -217,7 +217,7 @@ void MifareReadSector(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) LED_C_OFF(); isOK = 1; - if(!iso14443a_select_card(uid, NULL, &cuid, true, 0)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { isOK = 0; if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); } @@ -281,7 +281,7 @@ void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain) return; } - int len = iso14443a_select_card(NULL, NULL, NULL, true, 0); + int len = iso14443a_select_card(NULL, NULL, NULL, true, 0, true); if (!len) { if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Can't select card (RC:%d)",len); OnError(1); @@ -383,7 +383,7 @@ void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) LED_C_OFF(); while (true) { - if(!iso14443a_select_card(uid, NULL, &cuid, true, 0)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); break; }; @@ -483,7 +483,7 @@ void MifareUWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) clear_trace(); - if(!iso14443a_select_card(NULL, NULL, NULL, true, 0)) { + if(!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); OnError(0); return; @@ -542,7 +542,7 @@ void MifareUSetPwd(uint8_t arg0, uint8_t *datain){ clear_trace(); - if(!iso14443a_select_card(NULL, NULL, NULL, true, 0)) { + if(!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); OnError(0); return; @@ -662,7 +662,7 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, if (!have_uid) { // need a full select cycle to get the uid first iso14a_card_select_t card_info; - if(!iso14443a_select_card(uid, &card_info, &cuid, true, 0)) { + if(!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { if (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Can't select card (ALL)"); continue; } @@ -674,7 +674,7 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, } have_uid = true; } else { // no need for anticollision. We can directly select the card - if(!iso14443a_select_card(uid, NULL, NULL, false, cascade_levels)) { + if(!iso14443a_select_card(uid, NULL, NULL, false, cascade_levels, true)) { if (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Can't select card (UID)"); continue; } @@ -807,7 +807,7 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t calibrate, uint8_t *dat continue; } - if(!iso14443a_select_card(uid, NULL, &cuid, true, 0)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { if (MF_DBGLEVEL >= 1) Dbprintf("Nested: Can't select card"); rtr--; continue; @@ -881,7 +881,7 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t calibrate, uint8_t *dat continue; } - if(!iso14443a_select_card(uid, NULL, &cuid, true, 0)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { if (MF_DBGLEVEL >= 1) Dbprintf("Nested: Can't select card"); continue; }; @@ -1000,7 +1000,7 @@ void MifareChkKeys(uint16_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) // Iceman: use piwi's faster nonce collecting part in hardnested. if (!have_uid) { // need a full select cycle to get the uid first iso14a_card_select_t card_info; - if(!iso14443a_select_card(uid, &card_info, &cuid, true, 0)) { + if(!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { if (OLD_MF_DBGLEVEL >= 1) Dbprintf("ChkKeys: Can't select card"); --i; // try same key once again continue; @@ -1013,7 +1013,7 @@ void MifareChkKeys(uint16_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) } have_uid = true; } else { // no need for anticollision. We can directly select the card - if(!iso14443a_select_card(uid, NULL, NULL, false, cascade_levels)) { + if(!iso14443a_select_card(uid, NULL, NULL, false, cascade_levels, true)) { if (OLD_MF_DBGLEVEL >= 1) Dbprintf("ChkKeys: Can't select card (UID)"); --i; // try same key once again continue; @@ -1111,7 +1111,7 @@ void MifareECardLoad(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datai bool isOK = true; - if(!iso14443a_select_card(uid, NULL, &cuid, true, 0)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { isOK = false; if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); } @@ -1349,7 +1349,7 @@ void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datai // get UID from chip if (workFlags & 0x01) { - if(!iso14443a_select_card(uid, NULL, &cuid, true, 0)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); // Continue, if we set wrong UID or wrong UID checksum or some ATQA or SAK we will can't select card. But we need to write block 0 to make card work. //break; @@ -1573,7 +1573,7 @@ void Mifare_DES_Auth1(uint8_t arg0, uint8_t *datain){ iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); clear_trace(); - int len = iso14443a_select_card(uid, NULL, &cuid, true, 0); + int len = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); if(!len) { if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Can't select card"); OnError(1); diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index b75215a0..db9ce46e 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -442,7 +442,7 @@ int CmdHF14ACUIDs(const char *Cmd) // repeat n times for (int i = 0; i < n; i++) { // execute anticollision procedure - UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT, 0, 0}}; + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_RATS, 0, 0}}; SendCommand(&c); UsbCommand resp; @@ -581,6 +581,7 @@ int CmdHF14ACmdRaw(const char *cmd) { bool power = false; bool active = false; bool active_select = false; + bool no_rats = false; uint16_t numbits = 0; bool bTimeout = false; uint32_t timeout = 0; @@ -601,6 +602,7 @@ int CmdHF14ACmdRaw(const char *cmd) { PrintAndLog(" -b number of bits to send. Useful for send partial byte"); PrintAndLog(" -t timeout in ms"); PrintAndLog(" -T use Topaz protocol to send command"); + PrintAndLog(" -3 ISO14443-3 select only (skip RATS)"); return 0; } @@ -645,6 +647,9 @@ int CmdHF14ACmdRaw(const char *cmd) { case 'T': topazmode = true; break; + case '3': + no_rats = true; + break; default: PrintAndLog("Invalid option"); return 0; @@ -718,6 +723,10 @@ int CmdHF14ACmdRaw(const char *cmd) { c.arg[0] |= ISO14A_TOPAZMODE; } + if(no_rats) { + c.arg[0] |= ISO14A_NO_RATS; + } + // Max buffer is USB_CMD_DATA_SIZE (512) c.arg[1] = (datalen & 0xFFFF) | ((uint32_t)numbits << 16); memcpy(c.d.asBytes,data,datalen); diff --git a/client/cmdhfmfu.c b/client/cmdhfmfu.c index ca4544f3..3021631a 100644 --- a/client/cmdhfmfu.c +++ b/client/cmdhfmfu.c @@ -108,7 +108,7 @@ char *getUlev1CardSizeStr( uint8_t fsize ){ } static void ul_switch_on_field(void) { - UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0}}; + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_DISCONNECT | ISO14A_NO_RATS, 0, 0}}; clearCommandBuffer(); SendCommand(&c); } diff --git a/client/mifarehost.c b/client/mifarehost.c index 8a840d47..629c8feb 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -519,20 +519,20 @@ int mfCIdentify() UsbCommand resp; WaitForResponse(CMD_ACK,&resp); - iso14a_card_select_t card; - memcpy(&card, (iso14a_card_select_t *)resp.d.asBytes, sizeof(iso14a_card_select_t)); + // iso14a_card_select_t card; + // memcpy(&card, (iso14a_card_select_t *)resp.d.asBytes, sizeof(iso14a_card_select_t)); - uint64_t select_status = resp.arg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision + // uint64_t select_status = resp.arg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision - if(select_status != 0) { - uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0 - c.arg[0] = ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT; - c.arg[1] = 2; - c.arg[2] = 0; - memcpy(c.d.asBytes, rats, 2); - SendCommand(&c); - WaitForResponse(CMD_ACK,&resp); - } + // if(select_status != 0) { + // uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0 + // c.arg[0] = ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT; + // c.arg[1] = 2; + // c.arg[2] = 0; + // memcpy(c.d.asBytes, rats, 2); + // SendCommand(&c); + // WaitForResponse(CMD_ACK,&resp); + // } c.cmd = CMD_MIFARE_CIDENT; c.arg[0] = 0; diff --git a/include/mifare.h b/include/mifare.h index e2386cd5..bede67a9 100644 --- a/include/mifare.h +++ b/include/mifare.h @@ -34,7 +34,8 @@ typedef enum ISO14A_COMMAND { ISO14A_APPEND_CRC = (1 << 5), ISO14A_SET_TIMEOUT = (1 << 6), ISO14A_NO_SELECT = (1 << 7), - ISO14A_TOPAZMODE = (1 << 8) + ISO14A_TOPAZMODE = (1 << 8), + ISO14A_NO_RATS = (1 << 9) } iso14a_command_t; typedef struct { From 5d8f664d7abbd18bb60da2f11a2d5e8bcd29bdb6 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Wed, 4 Oct 2017 18:25:34 +0200 Subject: [PATCH 009/310] Adjust LUA scripts to new ISO14A_NO_RATS option --- client/lualibs/read14a.lua | 9 +++++++-- client/scripts/14araw.lua | 13 ++++++++++--- client/scripts/didump.lua | 2 +- client/scripts/formatMifare.lua | 2 +- client/scripts/mfkeys.lua | 2 +- client/scripts/mifare_autopwn.lua | 2 +- client/scripts/tnp3clone.lua | 2 +- client/scripts/tnp3dump.lua | 2 +- 8 files changed, 23 insertions(+), 11 deletions(-) diff --git a/client/lualibs/read14a.lua b/client/lualibs/read14a.lua index 943a1722..ec227b17 100644 --- a/client/lualibs/read14a.lua +++ b/client/lualibs/read14a.lua @@ -22,7 +22,8 @@ local ISO14A_COMMAND = { ISO14A_APPEND_CRC = 0x20, ISO14A_SET_TIMEOUT = 0x40, ISO14A_NO_SELECT = 0x80, - ISO14A_TOPAZMODE = 0x100 + ISO14A_TOPAZMODE = 0x100, + ISO14A_NO_RATS = 0x200 } local ISO14443a_TYPES = {} @@ -92,9 +93,10 @@ end -- This function does a connect and retrieves som einfo -- @param dont_disconnect - if true, does not disable the field +-- @param no_rats - if true, skips ISO14443-4 select (RATS) -- @return if successfull: an table containing card info -- @return if unsuccessfull : nil, error -local function read14443a(dont_disconnect) +local function read14443a(dont_disconnect, no_rats) local command, result, info, err, data command = Command:new{cmd = cmds.CMD_READER_ISO_14443a, @@ -102,6 +104,9 @@ local function read14443a(dont_disconnect) if dont_disconnect then command.arg1 = command.arg1 + ISO14A_COMMAND.ISO14A_NO_DISCONNECT end + if no_rats then + command.arg1 = command.arg1 + ISO14A_COMMAND.ISO14A_NO_RATS + end local result,err = sendToDevice(command) if result then local count,cmd,arg0,arg1,arg2 = bin.unpack('LLLL',result) diff --git a/client/scripts/14araw.lua b/client/scripts/14araw.lua index 0ce98656..82f30fa8 100644 --- a/client/scripts/14araw.lua +++ b/client/scripts/14araw.lua @@ -17,6 +17,8 @@ Arguments: -p stay connected - dont inactivate the field -x Data to send (NO SPACES!) -d Debug flag + -t Topaz mode + -3 Skip ISO14443-4 select Examples : @@ -70,6 +72,7 @@ function help() print(example) end + --- -- The main entry point function main(args) @@ -83,15 +86,19 @@ function main(args) local stayconnected = false local payload = nil local doconnect = true + local topaz_mode = false + local no_rats = false -- Read the parameters - for o, a in getopt.getopt(args, 'corcpx:') do + for o, a in getopt.getopt(args, 'corcpxt3:') do if o == "o" then doconnect = false end if o == "r" then ignore_response = true end if o == "c" then appendcrc = true end if o == "p" then stayconnected = true end if o == "x" then payload = a end if o == "d" then DEBUG = true end + if o == "t" then topaz_mode = true end + if o == "3" then no_rats = true end end -- First of all, connect @@ -99,7 +106,7 @@ function main(args) dbg("doconnect") -- We reuse the connect functionality from a -- common library - info, err = lib14a.read1443a(true) + info, err = lib14a.read1443a(true, no_rats) if err then return oops(err) end print(("Connected to card, uid = %s"):format(info.uid)) @@ -137,11 +144,11 @@ function showdata(usbpacket) end - function sendRaw(rawdata, options) print(">> ", rawdata) local flags = lib14a.ISO14A_COMMAND.ISO14A_NO_DISCONNECT + lib14a.ISO14A_COMMAND.ISO14A_RAW + if topaz_mode == true then flags = flags + lib14a.ISO14A_COMMAND.ISO14A_TOPAZMODE end local command = Command:new{cmd = cmds.CMD_READER_ISO_14443a, arg1 = flags, -- Send raw diff --git a/client/scripts/didump.lua b/client/scripts/didump.lua index 2386d42d..124c3dc3 100644 --- a/client/scripts/didump.lua +++ b/client/scripts/didump.lua @@ -406,7 +406,7 @@ function main(args) -- GET TAG UID - result, err = lib14a.read1443a(false) + result, err = lib14a.read1443a(false, true) if not result then return oops(err) end diff --git a/client/scripts/formatMifare.lua b/client/scripts/formatMifare.lua index a14c6b78..56397acd 100644 --- a/client/scripts/formatMifare.lua +++ b/client/scripts/formatMifare.lua @@ -71,7 +71,7 @@ end -- -- Read information from a card function GetCardInfo() - result, err = lib14a.read1443a(false) + result, err = lib14a.read1443a(false, true) if not result then print(err) return diff --git a/client/scripts/mfkeys.lua b/client/scripts/mfkeys.lua index c54f6e67..8e54f908 100644 --- a/client/scripts/mfkeys.lua +++ b/client/scripts/mfkeys.lua @@ -170,7 +170,7 @@ local function main( args) print(desc); - result, err = reader.read1443a() + result, err = reader.read1443a(false, true) if not result then print(err) return diff --git a/client/scripts/mifare_autopwn.lua b/client/scripts/mifare_autopwn.lua index e68f7a75..bede483b 100644 --- a/client/scripts/mifare_autopwn.lua +++ b/client/scripts/mifare_autopwn.lua @@ -56,7 +56,7 @@ end -- @return if unsuccessfull : nil, error function wait_for_mifare() while not core.ukbhit() do - res, err = reader.read1443a() + res, err = reader.read1443a(false, true) if res then return res end -- err means that there was no response from card end diff --git a/client/scripts/tnp3clone.lua b/client/scripts/tnp3clone.lua index e87c338e..2a8d2763 100644 --- a/client/scripts/tnp3clone.lua +++ b/client/scripts/tnp3clone.lua @@ -122,7 +122,7 @@ local function main(args) -- find tag - result, err = lib14a.read1443a(false) + result, err = lib14a.read1443a(false, true) if not result then return oops(err) end -- load keys diff --git a/client/scripts/tnp3dump.lua b/client/scripts/tnp3dump.lua index 211d146f..aca7d046 100644 --- a/client/scripts/tnp3dump.lua +++ b/client/scripts/tnp3dump.lua @@ -127,7 +127,7 @@ local function main(args) local cmdSetDbgOff = "hf mf dbg 0" core.console( cmdSetDbgOff) - result, err = lib14a.read1443a(false) + result, err = lib14a.read1443a(false, true) if not result then return oops(err) end From adf023ffe3f6d4490d534e48a66a6c6387a7c53e Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Thu, 5 Oct 2017 14:37:51 +0300 Subject: [PATCH 010/310] hf mf nested add some functionality (#403) * Added nested auto mode. it checks known keys and then launches nested * Check if we allready have all keys after nested --- CHANGELOG.md | 4 + client/cmdhfmf.c | 261 ++++++++++++++++++++++++++++++++++------------- 2 files changed, 192 insertions(+), 73 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da0e30ad..842c8bef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac ### Changed - Improved backdoor detection missbehaving magic s50/1k tag (Fl0-0) - Deleted wipe functionality from `hf mf csetuid` (Merlok) +- Changed `hf mf nested` logic (Merlok) +- Added `hf mf nested` mode: autosearch keys for attack (from well known keys) (Merlok) ### Fixed @@ -19,6 +21,8 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added data fsktonrz, a fsk cleaning/demodulating routine for weak fsk signal. Note: follow this up with a `data rawdemod nr` to finish demoding your signal. (marshmellow) - Added lf em 410xbrute, LF EM410x reader bruteforce attack by simulating UIDs from a file (Fl0-0) - Added `hf mf cwipe` command. It wipes "magic Chinese" card. For 1a generation it uses card's "wipe" command. For gen1a and gen1b it uses a write command. (Merlok) +- Added to `hf mf nested` source key check before attack (Merlok) +- Added to `hf mf nested` after attack it checks all found keys on non-open sectors (Merlok) ## [3.0.1][2017-06-08] diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index aafbce2a..295f22b7 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -222,6 +222,28 @@ uint8_t NumBlocksPerSector(uint8_t sectorNo) } } +static int ParamCardSizeSectors(const char c) { + int numBlocks = 16; + switch (c) { + case '0' : numBlocks = 5; break; + case '2' : numBlocks = 32; break; + case '4' : numBlocks = 40; break; + default: numBlocks = 16; + } + return numBlocks; +} + +static int ParamCardSizeBlocks(const char c) { + int numBlocks = 16 * 4; + switch (c) { + case '0' : numBlocks = 5 * 4; break; + case '2' : numBlocks = 32 * 4; break; + case '4' : numBlocks = 32 * 4 + 8 * 16; break; + default: numBlocks = 16 * 4; + } + return numBlocks; +} + int CmdHF14AMfDump(const char *Cmd) { uint8_t sectorNo, blockNo; @@ -238,14 +260,7 @@ int CmdHF14AMfDump(const char *Cmd) UsbCommand resp; char cmdp = param_getchar(Cmd, 0); - switch (cmdp) { - case '0' : numSectors = 5; break; - case '1' : - case '\0': numSectors = 16; break; - case '2' : numSectors = 32; break; - case '4' : numSectors = 40; break; - default: numSectors = 16; - } + numSectors = ParamCardSizeSectors(cmdp); if (strlen(Cmd) > 1 || cmdp == 'h' || cmdp == 'H') { PrintAndLog("Usage: hf mf dump [card memory]"); @@ -516,6 +531,7 @@ typedef struct { } sector_t; +# define NESTED_KEY_COUNT 15 int CmdHF14AMfNested(const char *Cmd) { int i, j, res, iterations; @@ -526,10 +542,12 @@ int CmdHF14AMfNested(const char *Cmd) uint8_t trgKeyType = 0; uint8_t SectorsCnt = 0; uint8_t key[6] = {0, 0, 0, 0, 0, 0}; - uint8_t keyBlock[14*6]; + uint8_t keyBlock[NESTED_KEY_COUNT * 6]; uint64_t key64 = 0; - bool transferToEml = false; + + bool autosearchKey = false; + bool transferToEml = false; bool createDumpFile = false; FILE *fkeys; uint8_t standart[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; @@ -540,66 +558,90 @@ int CmdHF14AMfNested(const char *Cmd) if (strlen(Cmd)<3) { PrintAndLog("Usage:"); PrintAndLog(" all sectors: hf mf nested [t,d]"); + PrintAndLog(" all sectors autosearch key: hf mf nested * [t,d]"); PrintAndLog(" one sector: hf mf nested o "); PrintAndLog(" [t]"); + PrintAndLog(" "); PrintAndLog("card memory - 0 - MINI(320 bytes), 1 - 1K, 2 - 2K, 4 - 4K, - 1K"); - PrintAndLog("t - transfer keys into emulator memory"); - PrintAndLog("d - write keys to binary file"); + PrintAndLog("t - transfer keys to emulator memory"); + PrintAndLog("d - write keys to binary file dumpkeys.bin"); PrintAndLog(" "); PrintAndLog(" sample1: hf mf nested 1 0 A FFFFFFFFFFFF "); PrintAndLog(" sample2: hf mf nested 1 0 A FFFFFFFFFFFF t "); PrintAndLog(" sample3: hf mf nested 1 0 A FFFFFFFFFFFF d "); PrintAndLog(" sample4: hf mf nested o 0 A FFFFFFFFFFFF 4 A"); + PrintAndLog(" sample5: hf mf nested 1 * t"); return 0; } + // cmdp = param_getchar(Cmd, 0); - blockNo = param_get8(Cmd, 1); - ctmp = param_getchar(Cmd, 2); - - if (ctmp != 'a' && ctmp != 'A' && ctmp != 'b' && ctmp != 'B') { - PrintAndLog("Key type must be A or B"); - return 1; - } - - if (ctmp != 'A' && ctmp != 'a') - keyType = 1; - - if (param_gethex(Cmd, 3, key, 12)) { - PrintAndLog("Key must include 12 HEX symbols"); - return 1; - } - if (cmdp == 'o' || cmdp == 'O') { cmdp = 'o'; - trgBlockNo = param_get8(Cmd, 4); - ctmp = param_getchar(Cmd, 5); + SectorsCnt = 1; + } else { + SectorsCnt = ParamCardSizeSectors(cmdp); + } + + // . number or autosearch key (*) + if (param_getchar(Cmd, 1) == '*') { + autosearchKey = true; + + ctmp = param_getchar(Cmd, 2); + transferToEml |= (ctmp == 't' || ctmp == 'T'); + createDumpFile |= (ctmp == 'd' || ctmp == 'D'); + + PrintAndLog("--nested. sectors:%2d, block no:*, eml:%c, dmp=%c ", SectorsCnt, transferToEml?'y':'n', createDumpFile?'y':'n'); + } else { + blockNo = param_get8(Cmd, 1); + + ctmp = param_getchar(Cmd, 2); if (ctmp != 'a' && ctmp != 'A' && ctmp != 'b' && ctmp != 'B') { - PrintAndLog("Target key type must be A or B"); + PrintAndLog("Key type must be A or B"); return 1; } - if (ctmp != 'A' && ctmp != 'a') - trgKeyType = 1; - } else { - switch (cmdp) { - case '0': SectorsCnt = 05; break; - case '1': SectorsCnt = 16; break; - case '2': SectorsCnt = 32; break; - case '4': SectorsCnt = 40; break; - default: SectorsCnt = 16; + if (ctmp != 'A' && ctmp != 'a') + keyType = 1; + + if (param_gethex(Cmd, 3, key, 12)) { + PrintAndLog("Key must include 12 HEX symbols"); + return 1; } + + // check if we can authenticate to sector + res = mfCheckKeys(blockNo, keyType, true, 1, key, &key64); + if (res) { + PrintAndLog("Can't authenticate to block:%3d key type:%c key:%s", blockNo, keyType?'B':'A', sprint_hex(key, 6)); + return 3; + } + + // one sector nested + if (cmdp == 'o') { + trgBlockNo = param_get8(Cmd, 4); + + ctmp = param_getchar(Cmd, 5); + if (ctmp != 'a' && ctmp != 'A' && ctmp != 'b' && ctmp != 'B') { + PrintAndLog("Target key type must be A or B"); + return 1; + } + if (ctmp != 'A' && ctmp != 'a') + trgKeyType = 1; + + ctmp = param_getchar(Cmd, 6); + transferToEml |= (ctmp == 't' || ctmp == 'T'); + createDumpFile |= (ctmp == 'd' || ctmp == 'D'); + } else { + ctmp = param_getchar(Cmd, 4); + transferToEml |= (ctmp == 't' || ctmp == 'T'); + createDumpFile |= (ctmp == 'd' || ctmp == 'D'); + } + + PrintAndLog("--nested. sectors:%2d, block no:%3d, key type:%c, eml:%c, dmp=%c ", SectorsCnt, blockNo, keyType?'B':'A', transferToEml?'y':'n', createDumpFile?'y':'n'); } - ctmp = param_getchar(Cmd, 4); - if (ctmp == 't' || ctmp == 'T') transferToEml = true; - else if (ctmp == 'd' || ctmp == 'D') createDumpFile = true; - - ctmp = param_getchar(Cmd, 6); - transferToEml |= (ctmp == 't' || ctmp == 'T'); - transferToEml |= (ctmp == 'd' || ctmp == 'D'); - - if (cmdp == 'o') { + // one-sector nested + if (cmdp == 'o') { // ------------------------------------ one sector working PrintAndLog("--target block no:%3d, target key type:%c ", trgBlockNo, trgKeyType?'B':'A'); int16_t isOK = mfnested(blockNo, keyType, key, trgBlockNo, trgKeyType, keyBlock, true); if (isOK) { @@ -630,6 +672,7 @@ int CmdHF14AMfNested(const char *Cmd) else num_to_bytes(key64, 6, &keyBlock[10]); mfEmlSetMem(keyBlock, sectortrailer, 1); + PrintAndLog("Key transferred to emulator memory."); } } else { PrintAndLog("No valid key found"); @@ -657,13 +700,14 @@ int CmdHF14AMfNested(const char *Cmd) num_to_bytes(0xa0478cc39091, 6, (uint8_t*)(keyBlock + 11 * 6)); num_to_bytes(0x533cb6c723f6, 6, (uint8_t*)(keyBlock + 12 * 6)); num_to_bytes(0x8fd0a4f256e9, 6, (uint8_t*)(keyBlock + 13 * 6)); + num_to_bytes(0x1a2b3c4d5e6f, 6, (uint8_t*)(keyBlock + 14 * 6)); PrintAndLog("Testing known keys. Sector count=%d", SectorsCnt); for (i = 0; i < SectorsCnt; i++) { for (j = 0; j < 2; j++) { if (e_sector[i].foundKey[j]) continue; - res = mfCheckKeys(FirstBlockOfSector(i), j, true, 6, keyBlock, &key64); + res = mfCheckKeys(FirstBlockOfSector(i), j, true, NESTED_KEY_COUNT, keyBlock, &key64); if (!res) { e_sector[i].Key[j] = key64; @@ -671,6 +715,32 @@ int CmdHF14AMfNested(const char *Cmd) } } } + + // get known key from array + bool keyFound = false; + if (autosearchKey) { + for (i = 0; i < SectorsCnt; i++) { + for (j = 0; j < 2; j++) { + if (e_sector[i].foundKey[j]) { + // get known key + blockNo = i * 4; + keyType = j; + num_to_bytes(e_sector[i].Key[j], 6, key); + + keyFound = true; + break; + } + } + if (keyFound) break; + } + + // Can't found a key.... + if (!keyFound) { + PrintAndLog("Can't found any of the known keys."); + return 4; + } + PrintAndLog("--auto key. block no:%3d, key type:%c key:%s", blockNo, keyType?'B':'A', sprint_hex(key, 6)); + } // nested sectors iterations = 0; @@ -707,10 +777,71 @@ int CmdHF14AMfNested(const char *Cmd) } } - printf("Time in nested: %1.3f (%1.3f sec per key)\n\n", ((float)(msclock() - msclock1))/1000.0, ((float)(msclock() - msclock1))/iterations/1000.0); + // print nested statistic + PrintAndLog("\n\n-----------------------------------------------\nNested statistic:\nIterations count: %d", iterations); + PrintAndLog("Time in nested: %1.3f (%1.3f sec per key)", ((float)(msclock() - msclock1))/1000.0, ((float)(msclock() - msclock1))/iterations/1000.0); + + // check if we have unrecognized keys + bool notFoundKeys = false; + for (i = 0; i < SectorsCnt; i++) { + for (j = 0; j < 2; j++) { + if (!e_sector[i].foundKey[j]) { + notFoundKeys = true; + break; + } + } + if (notFoundKeys) break; + } + + if (notFoundKeys) { + PrintAndLog("-----------------------------------------------\n"); + PrintAndLog("We have unrecognized keys. Trying to check if we have this keys on key buffer..."); - PrintAndLog("-----------------------------------------------\nIterations count: %d\n\n", iterations); - //print them + // fill keyBlock with known keys + int cnt = 0; + for (i = 0; i < SectorsCnt; i++) { + for (j = 0; j < 2; j++) { + if (e_sector[i].foundKey[j]) { + // try to insert key to keyBlock + if (cnt < NESTED_KEY_COUNT) { + + // search for dublicates + bool dubl = false; + for (int v = 0; v < NESTED_KEY_COUNT; v++) { + if (e_sector[i].Key[j] == bytes_to_num((uint8_t*)(keyBlock + v * 6), 6)) { + dubl = true; + break; + } + } + + // insert + if (!dubl) { + num_to_bytes(e_sector[i].Key[j], 6, (uint8_t*)(keyBlock + cnt * 6)); + cnt++; + } + } + } + } + } + + // try to auth with known keys to not recognized sectors keys + PrintAndLog("Testing keys. Sector count=%d known keys count:%d", SectorsCnt, cnt); + for (i = 0; i < SectorsCnt; i++) { + for (j = 0; j < 2; j++) { + if (e_sector[i].foundKey[j]) continue; + + res = mfCheckKeys(FirstBlockOfSector(i), j, true, cnt, keyBlock, &key64); + + if (!res) { + e_sector[i].Key[j] = key64; + e_sector[i].foundKey[j] = 1; + } + } + } + + } // if (notFoundKeys) + + // print result PrintAndLog("|---|----------------|---|----------------|---|"); PrintAndLog("|sec|key A |res|key B |res|"); PrintAndLog("|---|----------------|---|----------------|---|"); @@ -720,7 +851,7 @@ int CmdHF14AMfNested(const char *Cmd) } PrintAndLog("|---|----------------|---|----------------|---|"); - // transfer them to the emulator + // transfer keys to the emulator memory if (transferToEml) { for (i = 0; i < SectorsCnt; i++) { mfEmlGetMem(keyBlock, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1); @@ -730,6 +861,7 @@ int CmdHF14AMfNested(const char *Cmd) num_to_bytes(e_sector[i].Key[1], 6, &keyBlock[10]); mfEmlSetMem(keyBlock, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1); } + PrintAndLog("Keys transferred to emulator memory."); } // Create dump file @@ -946,13 +1078,7 @@ int CmdHF14AMfChk(const char *Cmd) if (param_getchar(Cmd, 0)=='*') { blockNo = 3; - switch(param_getchar(Cmd+1, 0)) { - case '0': SectorsCnt = 5; break; - case '1': SectorsCnt = 16; break; - case '2': SectorsCnt = 32; break; - case '4': SectorsCnt = 40; break; - default: SectorsCnt = 16; - } + SectorsCnt = ParamCardSizeSectors(param_getchar(Cmd + 1, 0)); } else blockNo = param_get8(Cmd, 0); @@ -1854,17 +1980,6 @@ int CmdHF14AMfCSetUID(const char *Cmd) return 0; } -static int ParamGetCardSize(const char c) { - int numBlocks = 16 * 4; - switch (c) { - case '0' : numBlocks = 5 * 4; break; - case '2' : numBlocks = 32 * 4; break; - case '4' : numBlocks = 32 * 4 + 8 * 16; break; - default: numBlocks = 16 * 4; - } - return numBlocks; -} - int CmdHF14AMfCWipe(const char *Cmd) { int res, gen = 0; @@ -1885,7 +2000,7 @@ int CmdHF14AMfCWipe(const char *Cmd) if ((gen != 1) && (gen != 2)) return 1; - numBlocks = ParamGetCardSize(param_getchar(Cmd, 0)); + numBlocks = ParamCardSizeBlocks(param_getchar(Cmd, 0)); char cmdp = 0; while(param_getchar(Cmd, cmdp) != 0x00){ From 7085af051de01a7b93cb8943822e65a8bd6f59b3 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Thu, 5 Oct 2017 18:07:26 +0200 Subject: [PATCH 011/310] fix LUA script errors --- client/scripts/14araw.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/scripts/14araw.lua b/client/scripts/14araw.lua index 82f30fa8..94b3020c 100644 --- a/client/scripts/14araw.lua +++ b/client/scripts/14araw.lua @@ -90,7 +90,7 @@ function main(args) local no_rats = false -- Read the parameters - for o, a in getopt.getopt(args, 'corcpxt3:') do + for o, a in getopt.getopt(args, 'orcpx:dt3') do if o == "o" then doconnect = false end if o == "r" then ignore_response = true end if o == "c" then appendcrc = true end @@ -114,7 +114,7 @@ function main(args) -- The actual raw payload, if any if payload then - res,err = sendRaw(payload,{ignore_response = ignore_response}) + res,err = sendRaw(payload,{ignore_response = ignore_response, topaz_mode = topaz_mode}) if err then return oops(err) end if not ignoreresponse then @@ -148,7 +148,7 @@ function sendRaw(rawdata, options) print(">> ", rawdata) local flags = lib14a.ISO14A_COMMAND.ISO14A_NO_DISCONNECT + lib14a.ISO14A_COMMAND.ISO14A_RAW - if topaz_mode == true then flags = flags + lib14a.ISO14A_COMMAND.ISO14A_TOPAZMODE end + if options.topaz_mode == true then flags = flags + lib14a.ISO14A_COMMAND.ISO14A_TOPAZMODE end local command = Command:new{cmd = cmds.CMD_READER_ISO_14443a, arg1 = flags, -- Send raw From 275d9e61c245e65fe09ccb5c49f09dfe505a642d Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Sun, 15 Oct 2017 22:19:34 +0300 Subject: [PATCH 012/310] Check keys in hf mf nested and hf mf chk (#414) Improve hf mf chk and hf mf nested * hf mf chk. added interrupt of procedure by usb * extract mifare default keys into separate module * arm side multisector `hf mf chk` * hf mf nested. change key search procedure * hf mf nested. added key check after we have found a key. * small fix hf list f * hf mf chk. add timeout (arm side) and some tweaks. --- CHANGELOG.md | 3 + armsrc/apps.h | 2 +- armsrc/iso14443a.h | 1 + armsrc/mifarecmd.c | 91 +++++--------- armsrc/mifareutil.c | 122 +++++++++++++++++++ armsrc/mifareutil.h | 9 ++ client/cmdhf.c | 2 +- client/cmdhfmf.c | 265 ++++++++++++++++++----------------------- client/cmdhfmf.h | 2 + client/cmdmain.c | 9 +- client/cmdmain.h | 1 + client/mifaredefault.h | 40 +++++++ client/mifarehost.c | 33 ++++- client/mifarehost.h | 13 +- 14 files changed, 375 insertions(+), 218 deletions(-) create mode 100644 client/mifaredefault.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 842c8bef..45fe1392 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Deleted wipe functionality from `hf mf csetuid` (Merlok) - Changed `hf mf nested` logic (Merlok) - Added `hf mf nested` mode: autosearch keys for attack (from well known keys) (Merlok) +- `hf mf nested` Check keys after they have found (Merlok) +- `hf mf chk` Move main cycle to arm (Merlok) ### Fixed @@ -23,6 +25,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added `hf mf cwipe` command. It wipes "magic Chinese" card. For 1a generation it uses card's "wipe" command. For gen1a and gen1b it uses a write command. (Merlok) - Added to `hf mf nested` source key check before attack (Merlok) - Added to `hf mf nested` after attack it checks all found keys on non-open sectors (Merlok) +- `hf mf chk` Added setings to set iso14443a operations timeout. default timeout set to 500us (Merlok) ## [3.0.1][2017-06-08] diff --git a/armsrc/apps.h b/armsrc/apps.h index 1ce3215f..b0070148 100644 --- a/armsrc/apps.h +++ b/armsrc/apps.h @@ -127,7 +127,7 @@ void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) void MifareUWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain); void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, uint8_t *datain); -void MifareChkKeys(uint16_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain); +void MifareChkKeys(uint16_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain); void Mifare1ksim(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain); void MifareSetDbgLvl(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); void MifareEMemClr(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index 10e50e0f..9977a658 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -29,4 +29,5 @@ extern void iso14443a_setup(uint8_t fpga_minor_mode); extern int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data); extern int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *resp_data, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats); extern void iso14a_set_trigger(bool enable); +extern void iso14a_set_timeout(uint32_t timeout); #endif /* __ISO14443A_H */ diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index a3f0d374..edafe0a3 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -20,10 +20,6 @@ #include "parity.h" #include "crc.h" -#define AUTHENTICATION_TIMEOUT 848 // card times out 1ms after wrong authentication (according to NXP documentation) -#define PRE_AUTHENTICATION_LEADTIME 400 // some (non standard) cards need a pause after select before they are ready for first authentication - - // the block number for the ISO14443-4 PCB static uint8_t pcb_blocknum = 0; // Deselect card by sending a s-block. the crc is precalced for speed @@ -961,24 +957,14 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t calibrate, uint8_t *dat // MIFARE check keys. key count up to 85. // //----------------------------------------------------------------------------- -void MifareChkKeys(uint16_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) +void MifareChkKeys(uint16_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain) { uint8_t blockNo = arg0 & 0xff; uint8_t keyType = (arg0 >> 8) & 0xff; - bool clearTrace = arg1; + bool clearTrace = arg1 & 0x01; + bool multisectorCheck = arg1 & 0x02; + uint8_t set14aTimeout = (arg1 >> 8) & 0xff; uint8_t keyCount = arg2; - uint64_t ui64Key = 0; - - bool have_uid = false; - uint8_t cascade_levels = 0; - uint32_t timeout = 0; - int i; - byte_t isOK = 0; - uint8_t uid[10]; - uint32_t cuid; - struct Crypto1State mpcs = {0, 0}; - struct Crypto1State *pcs; - pcs = &mpcs; // clear debug level int OLD_MF_DBGLEVEL = MF_DBGLEVEL; @@ -992,52 +978,33 @@ void MifareChkKeys(uint16_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) if (clearTrace) clear_trace(); set_tracing(true); - for (i = 0; i < keyCount; i++) { -// if(mifare_classic_halt(pcs, cuid)) { -// if (MF_DBGLEVEL >= 1) Dbprintf("ChkKeys: Halt error"); -// } - - // Iceman: use piwi's faster nonce collecting part in hardnested. - if (!have_uid) { // need a full select cycle to get the uid first - iso14a_card_select_t card_info; - if(!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { - if (OLD_MF_DBGLEVEL >= 1) Dbprintf("ChkKeys: Can't select card"); - --i; // try same key once again - continue; - } - switch (card_info.uidlen) { - case 4 : cascade_levels = 1; break; - case 7 : cascade_levels = 2; break; - case 10: cascade_levels = 3; break; - default: break; - } - have_uid = true; - } else { // no need for anticollision. We can directly select the card - if(!iso14443a_select_card(uid, NULL, NULL, false, cascade_levels, true)) { - if (OLD_MF_DBGLEVEL >= 1) Dbprintf("ChkKeys: Can't select card (UID)"); - --i; // try same key once again - continue; - } - } - - ui64Key = bytes_to_num(datain + i * 6, 6); - if(mifare_classic_auth(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST)) { - uint8_t dummy_answer = 0; - ReaderTransmit(&dummy_answer, 1, NULL); - timeout = GetCountSspClk() + AUTHENTICATION_TIMEOUT; - - // wait for the card to become ready again - while(GetCountSspClk() < timeout); - continue; - } - - isOK = 1; - break; + if (set14aTimeout){ + iso14a_set_timeout(set14aTimeout * 10); // timeout: ms = x/106 35-minimum, 50-OK 106-recommended 500-safe } + + if (multisectorCheck) { + TKeyIndex keyIndex = {{0}}; + uint8_t sectorCnt = blockNo; + int res = MifareMultisectorChk(datain, keyCount, sectorCnt, keyType, OLD_MF_DBGLEVEL, &keyIndex); - LED_B_ON(); - cmd_send(CMD_ACK,isOK,0,0,datain + i * 6,6); - LED_B_OFF(); + LED_B_ON(); + if (res >= 0) { + cmd_send(CMD_ACK, 1, 0, 0, keyIndex, 80); + } else { + cmd_send(CMD_ACK, 0, 0, 0, NULL, 0); + } + LED_B_OFF(); + } else { + int res = MifareChkBlockKeys(datain, keyCount, blockNo, keyType, OLD_MF_DBGLEVEL); + + LED_B_ON(); + if (res > 0) { + cmd_send(CMD_ACK, 1, 0, 0, datain + (res - 1) * 6, 6); + } else { + cmd_send(CMD_ACK, 0, 0, 0, NULL, 0); + } + LED_B_OFF(); + } FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c index fdead5ee..97f7b3d1 100644 --- a/armsrc/mifareutil.c +++ b/armsrc/mifareutil.c @@ -764,3 +764,125 @@ int mifare_desfire_des_auth2(uint32_t uid, uint8_t *key, uint8_t *blockData){ } return 1; } + +//----------------------------------------------------------------------------- +// MIFARE check keys +// +//----------------------------------------------------------------------------- +// one key check +int MifareChkBlockKey(uint8_t *uid, uint32_t *cuid, uint8_t *cascade_levels, uint64_t ui64Key, uint8_t blockNo, uint8_t keyType, uint8_t debugLevel) { + + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + + // Iceman: use piwi's faster nonce collecting part in hardnested. + if (*cascade_levels == 0) { // need a full select cycle to get the uid first + iso14a_card_select_t card_info; + if(!iso14443a_select_card(uid, &card_info, cuid, true, 0, true)) { + if (debugLevel >= 1) Dbprintf("ChkKeys: Can't select card"); + return 1; + } + switch (card_info.uidlen) { + case 4 : *cascade_levels = 1; break; + case 7 : *cascade_levels = 2; break; + case 10: *cascade_levels = 3; break; + default: break; + } + } else { // no need for anticollision. We can directly select the card + if(!iso14443a_select_card(uid, NULL, NULL, false, *cascade_levels, true)) { + if (debugLevel >= 1) Dbprintf("ChkKeys: Can't select card (UID) lvl=%d", *cascade_levels); + return 1; + } + } + + if(mifare_classic_auth(pcs, *cuid, blockNo, keyType, ui64Key, AUTH_FIRST)) { +// SpinDelayUs(AUTHENTICATION_TIMEOUT); // it not needs because mifare_classic_auth have timeout from iso14a_set_timeout() + return 2; + } else { +/* // let it be here. it like halt command, but maybe it will work in some strange cases + uint8_t dummy_answer = 0; + ReaderTransmit(&dummy_answer, 1, NULL); + int timeout = GetCountSspClk() + AUTHENTICATION_TIMEOUT; + // wait for the card to become ready again + while(GetCountSspClk() < timeout) {}; +*/ + // it needs after success authentication + mifare_classic_halt(pcs, *cuid); + } + + return 0; +} + +// multi key check +int MifareChkBlockKeys(uint8_t *keys, uint8_t keyCount, uint8_t blockNo, uint8_t keyType, uint8_t debugLevel) { + uint8_t uid[10]; + uint32_t cuid = 0; + uint8_t cascade_levels = 0; + uint64_t ui64Key = 0; + + int retryCount = 0; + for (uint8_t i = 0; i < keyCount; i++) { + + // Allow button press / usb cmd to interrupt device + if (BUTTON_PRESS() && !usb_poll_validate_length()) { + Dbprintf("ChkKeys: Cancel operation. Exit..."); + return -2; + } + + ui64Key = bytes_to_num(keys + i * 6, 6); + int res = MifareChkBlockKey(uid, &cuid, &cascade_levels, ui64Key, blockNo, keyType, debugLevel); + + // can't select + if (res == 1) { + retryCount++; + if (retryCount >= 5) { + Dbprintf("ChkKeys: block=%d key=%d. Can't select. Exit...", blockNo, keyType); + return -1; + } + --i; // try the same key once again + + SpinDelay(20); +// Dbprintf("ChkKeys: block=%d key=%d. Try the same key once again...", blockNo, keyType); + continue; + } + + // can't authenticate + if (res == 2) { + retryCount = 0; + continue; // can't auth. wrong key. + } + + return i + 1; + } + + return 0; +} + +// multisector multikey check +int MifareMultisectorChk(uint8_t *keys, uint8_t keyCount, uint8_t SectorCount, uint8_t keyType, uint8_t debugLevel, TKeyIndex *keyIndex) { + int res = 0; + +// int clk = GetCountSspClk(); + + for(int sc = 0; sc < SectorCount; sc++){ + WDT_HIT(); + + int keyAB = keyType; + do { + res = MifareChkBlockKeys(keys, keyCount, FirstBlockOfSector(sc), keyAB & 0x01, debugLevel); + if (res < 0){ + return res; + } + if (res > 0){ + (*keyIndex)[keyAB & 0x01][sc] = res; + } + } while(--keyAB > 0); + } + +// Dbprintf("%d %d", GetCountSspClk() - clk, (GetCountSspClk() - clk)/(SectorCount*keyCount*(keyType==2?2:1))); + + return 0; +} + + diff --git a/armsrc/mifareutil.h b/armsrc/mifareutil.h index 468c5cce..8ffd5e89 100644 --- a/armsrc/mifareutil.h +++ b/armsrc/mifareutil.h @@ -13,6 +13,7 @@ #define __MIFAREUTIL_H #include "crapto1/crapto1.h" +#include "usb_cdc.h" // mifare authentication #define CRYPT_NONE 0 @@ -20,6 +21,8 @@ #define CRYPT_REQUEST 2 #define AUTH_FIRST 0 #define AUTH_NESTED 2 +#define AUTHENTICATION_TIMEOUT 848 // card times out 1ms after wrong authentication (according to NXP documentation) +#define PRE_AUTHENTICATION_LEADTIME 400 // some (non standard) cards need a pause after select before they are ready for first authentication // mifare 4bit card answers #define CARD_ACK 0x0A // 1010 - ACK @@ -99,4 +102,10 @@ int emlGetValBl(uint32_t *blReg, uint8_t *blBlock, int blockNum); int emlSetValBl(uint32_t blReg, uint8_t blBlock, int blockNum); int emlCheckValBl(int blockNum); +// mifare check keys +typedef uint8_t TKeyIndex[2][40]; +int MifareChkBlockKey(uint8_t *uid, uint32_t *cuid, uint8_t *cascade_levels, uint64_t ui64Key, uint8_t blockNo, uint8_t keyType, uint8_t debugLevel); +int MifareChkBlockKeys(uint8_t *keys, uint8_t keyCount, uint8_t blockNo, uint8_t keyType, uint8_t debugLevel); +int MifareMultisectorChk(uint8_t *keys, uint8_t keyCount, uint8_t SectorCount, uint8_t keyType, uint8_t debugLevel, TKeyIndex *keyIndex); + #endif diff --git a/client/cmdhf.c b/client/cmdhf.c index eb2ba9b2..6aa5ae4e 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -546,7 +546,7 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui if (showWaitCycles && !isResponse && next_record_is_response(tracepos, trace)) { uint32_t next_timestamp = *((uint32_t *)(trace + tracepos)); - PrintAndLog(" %9d | %9d | %s | fdt (Frame Delay Time): %d", + PrintAndLog(" %10d | %10d | %s | fdt (Frame Delay Time): %d", (EndOfTransmissionTimestamp - first_timestamp), (next_timestamp - first_timestamp), " ", diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 295f22b7..12fb0b78 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -524,13 +524,6 @@ int CmdHF14AMfRestore(const char *Cmd) return 0; } - -typedef struct { - uint64_t Key[2]; - int foundKey[2]; -} sector_t; - - # define NESTED_KEY_COUNT 15 int CmdHF14AMfNested(const char *Cmd) { @@ -686,35 +679,12 @@ int CmdHF14AMfNested(const char *Cmd) if (e_sector == NULL) return 1; //test current key and additional standard keys first - memcpy(keyBlock, key, 6); - num_to_bytes(0xffffffffffff, 6, (uint8_t*)(keyBlock + 1 * 6)); - num_to_bytes(0x000000000000, 6, (uint8_t*)(keyBlock + 2 * 6)); - num_to_bytes(0xa0a1a2a3a4a5, 6, (uint8_t*)(keyBlock + 3 * 6)); - num_to_bytes(0xb0b1b2b3b4b5, 6, (uint8_t*)(keyBlock + 4 * 6)); - num_to_bytes(0xaabbccddeeff, 6, (uint8_t*)(keyBlock + 5 * 6)); - num_to_bytes(0x4d3a99c351dd, 6, (uint8_t*)(keyBlock + 6 * 6)); - num_to_bytes(0x1a982c7e459a, 6, (uint8_t*)(keyBlock + 7 * 6)); - num_to_bytes(0xd3f7d3f7d3f7, 6, (uint8_t*)(keyBlock + 8 * 6)); - num_to_bytes(0x714c5c886e97, 6, (uint8_t*)(keyBlock + 9 * 6)); - num_to_bytes(0x587ee5f9350f, 6, (uint8_t*)(keyBlock + 10 * 6)); - num_to_bytes(0xa0478cc39091, 6, (uint8_t*)(keyBlock + 11 * 6)); - num_to_bytes(0x533cb6c723f6, 6, (uint8_t*)(keyBlock + 12 * 6)); - num_to_bytes(0x8fd0a4f256e9, 6, (uint8_t*)(keyBlock + 13 * 6)); - num_to_bytes(0x1a2b3c4d5e6f, 6, (uint8_t*)(keyBlock + 14 * 6)); + for (int defaultKeyCounter = 0; defaultKeyCounter < MifareDefaultKeysSize; defaultKeyCounter++){ + num_to_bytes(MifareDefaultKeys[defaultKeyCounter], 6, (uint8_t*)(keyBlock + defaultKeyCounter * 6)); + } PrintAndLog("Testing known keys. Sector count=%d", SectorsCnt); - for (i = 0; i < SectorsCnt; i++) { - for (j = 0; j < 2; j++) { - if (e_sector[i].foundKey[j]) continue; - - res = mfCheckKeys(FirstBlockOfSector(i), j, true, NESTED_KEY_COUNT, keyBlock, &key64); - - if (!res) { - e_sector[i].Key[j] = key64; - e_sector[i].foundKey[j] = 1; - } - } - } + mfCheckKeysSec(SectorsCnt, 2, MF_CHKKEYS_DEFTIMEOUT, true, NESTED_KEY_COUNT, keyBlock, e_sector); // get known key from array bool keyFound = false; @@ -772,6 +742,9 @@ int CmdHF14AMfNested(const char *Cmd) PrintAndLog("Found valid key:%012" PRIx64, key64); e_sector[sectorNo].foundKey[trgKeyType] = 1; e_sector[sectorNo].Key[trgKeyType] = key64; + + // try to check this key as a key to the other sectors + mfCheckKeysSec(SectorsCnt, 2, MF_CHKKEYS_DEFTIMEOUT, true, 1, keyBlock, e_sector); } } } @@ -781,66 +754,6 @@ int CmdHF14AMfNested(const char *Cmd) PrintAndLog("\n\n-----------------------------------------------\nNested statistic:\nIterations count: %d", iterations); PrintAndLog("Time in nested: %1.3f (%1.3f sec per key)", ((float)(msclock() - msclock1))/1000.0, ((float)(msclock() - msclock1))/iterations/1000.0); - // check if we have unrecognized keys - bool notFoundKeys = false; - for (i = 0; i < SectorsCnt; i++) { - for (j = 0; j < 2; j++) { - if (!e_sector[i].foundKey[j]) { - notFoundKeys = true; - break; - } - } - if (notFoundKeys) break; - } - - if (notFoundKeys) { - PrintAndLog("-----------------------------------------------\n"); - PrintAndLog("We have unrecognized keys. Trying to check if we have this keys on key buffer..."); - - // fill keyBlock with known keys - int cnt = 0; - for (i = 0; i < SectorsCnt; i++) { - for (j = 0; j < 2; j++) { - if (e_sector[i].foundKey[j]) { - // try to insert key to keyBlock - if (cnt < NESTED_KEY_COUNT) { - - // search for dublicates - bool dubl = false; - for (int v = 0; v < NESTED_KEY_COUNT; v++) { - if (e_sector[i].Key[j] == bytes_to_num((uint8_t*)(keyBlock + v * 6), 6)) { - dubl = true; - break; - } - } - - // insert - if (!dubl) { - num_to_bytes(e_sector[i].Key[j], 6, (uint8_t*)(keyBlock + cnt * 6)); - cnt++; - } - } - } - } - } - - // try to auth with known keys to not recognized sectors keys - PrintAndLog("Testing keys. Sector count=%d known keys count:%d", SectorsCnt, cnt); - for (i = 0; i < SectorsCnt; i++) { - for (j = 0; j < 2; j++) { - if (e_sector[i].foundKey[j]) continue; - - res = mfCheckKeys(FirstBlockOfSector(i), j, true, cnt, keyBlock, &key64); - - if (!res) { - e_sector[i].Key[j] = key64; - e_sector[i].foundKey[j] = 1; - } - } - } - - } // if (notFoundKeys) - // print result PrintAndLog("|---|----------------|---|----------------|---|"); PrintAndLog("|sec|key A |res|key B |res|"); @@ -1022,14 +935,18 @@ int CmdHF14AMfNestedHard(const char *Cmd) int CmdHF14AMfChk(const char *Cmd) { if (strlen(Cmd)<3) { - PrintAndLog("Usage: hf mf chk |<*card memory> [t|d] [] []"); + PrintAndLog("Usage: hf mf chk |<*card memory> [t|d|s|ss] [] []"); PrintAndLog(" * - all sectors"); PrintAndLog("card memory - 0 - MINI(320 bytes), 1 - 1K, 2 - 2K, 4 - 4K, - 1K"); PrintAndLog("d - write keys to binary file\n"); PrintAndLog("t - write keys to emulator memory"); + PrintAndLog("s - slow execute. timeout 1ms"); + PrintAndLog("ss- very slow execute. timeout 5ms"); PrintAndLog(" sample: hf mf chk 0 A 1234567890ab keys.dic"); PrintAndLog(" hf mf chk *1 ? t"); PrintAndLog(" hf mf chk *1 ? d"); + PrintAndLog(" hf mf chk *1 ? s"); + PrintAndLog(" hf mf chk *1 ? dss"); return 0; } @@ -1042,42 +959,28 @@ int CmdHF14AMfChk(const char *Cmd) int i, res; int keycnt = 0; char ctmp = 0x00; + char ctmp3[3] = {0x00}; uint8_t blockNo = 0; - uint8_t SectorsCnt = 1; + uint8_t SectorsCnt = 0; uint8_t keyType = 0; uint64_t key64 = 0; + uint32_t timeout14a = 0; // timeout in us + bool param3InUse = false; int transferToEml = 0; int createDumpFile = 0; + + sector_t *e_sector = NULL; keyBlock = calloc(stKeyBlock, 6); if (keyBlock == NULL) return 1; - uint64_t defaultKeys[] = - { - 0xffffffffffff, // Default key (first key used by program if no user defined key) - 0x000000000000, // Blank key - 0xa0a1a2a3a4a5, // NFCForum MAD key - 0xb0b1b2b3b4b5, - 0xaabbccddeeff, - 0x4d3a99c351dd, - 0x1a982c7e459a, - 0xd3f7d3f7d3f7, - 0x714c5c886e97, - 0x587ee5f9350f, - 0xa0478cc39091, - 0x533cb6c723f6, - 0x8fd0a4f256e9 - }; - int defaultKeysSize = sizeof(defaultKeys) / sizeof(uint64_t); - - for (int defaultKeyCounter = 0; defaultKeyCounter < defaultKeysSize; defaultKeyCounter++) - { - num_to_bytes(defaultKeys[defaultKeyCounter], 6, (uint8_t*)(keyBlock + defaultKeyCounter * 6)); + int defaultKeysSize = MifareDefaultKeysSize; + for (int defaultKeyCounter = 0; defaultKeyCounter < defaultKeysSize; defaultKeyCounter++){ + num_to_bytes(MifareDefaultKeys[defaultKeyCounter], 6, (uint8_t*)(keyBlock + defaultKeyCounter * 6)); } if (param_getchar(Cmd, 0)=='*') { - blockNo = 3; SectorsCnt = ParamCardSizeSectors(param_getchar(Cmd + 1, 0)); } else @@ -1086,10 +989,10 @@ int CmdHF14AMfChk(const char *Cmd) ctmp = param_getchar(Cmd, 1); switch (ctmp) { case 'a': case 'A': - keyType = !0; + keyType = 0; break; case 'b': case 'B': - keyType = !1; + keyType = 1; break; case '?': keyType = 2; @@ -1100,11 +1003,33 @@ int CmdHF14AMfChk(const char *Cmd) return 1; }; + // transfer to emulator & create dump file ctmp = param_getchar(Cmd, 2); - if (ctmp == 't' || ctmp == 'T') transferToEml = 1; - else if (ctmp == 'd' || ctmp == 'D') createDumpFile = 1; + if (ctmp == 't' || ctmp == 'T') transferToEml = 1; + if (ctmp == 'd' || ctmp == 'D') createDumpFile = 1; + + param3InUse = transferToEml | createDumpFile; + + timeout14a = 500; // fast by default + // double parameters - ts, ds + int clen = param_getlength(Cmd, 2); + if (clen == 2 || clen == 3){ + param_getstr(Cmd, 2, ctmp3); + ctmp = ctmp3[1]; + } + //parse + if (ctmp == 's' || ctmp == 'S') { + timeout14a = 1000; // slow + if (!param3InUse && clen == 2 && (ctmp3[1] == 's' || ctmp3[1] == 'S')) { + timeout14a = 5000; // very slow + } + if (param3InUse && clen == 3 && (ctmp3[2] == 's' || ctmp3[2] == 'S')) { + timeout14a = 5000; // very slow + } + param3InUse = true; + } - for (i = transferToEml || createDumpFile; param_getchar(Cmd, 2 + i); i++) { + for (i = param3InUse; param_getchar(Cmd, 2 + i); i++) { if (!param_gethex(Cmd, 2 + i, keyBlock + 6 * keycnt, 12)) { if ( stKeyBlock - keycnt < 2) { p = realloc(keyBlock, 6*(stKeyBlock+=10)); @@ -1169,6 +1094,7 @@ int CmdHF14AMfChk(const char *Cmd) } } + // fill with default keys if (keycnt == 0) { PrintAndLog("No key specified, trying default keys"); for (;keycnt < defaultKeysSize; keycnt++) @@ -1178,47 +1104,84 @@ int CmdHF14AMfChk(const char *Cmd) } // initialize storage for found keys - bool validKey[2][40]; - uint8_t foundKey[2][40][6]; - for (uint16_t t = 0; t < 2; t++) { + e_sector = calloc(SectorsCnt, sizeof(sector_t)); + if (e_sector == NULL) return 1; + for (uint8_t keyAB = 0; keyAB < 2; keyAB++) { for (uint16_t sectorNo = 0; sectorNo < SectorsCnt; sectorNo++) { - validKey[t][sectorNo] = false; - for (uint16_t i = 0; i < 6; i++) { - foundKey[t][sectorNo][i] = 0xff; - } + e_sector[sectorNo].Key[keyAB] = 0xffffffffffff; + e_sector[sectorNo].foundKey[keyAB] = 0; } } + printf("\n"); - for ( int t = !keyType; t < 2; keyType==2?(t++):(t=2) ) { - int b=blockNo; - for (int i = 0; i < SectorsCnt; ++i) { - PrintAndLog("--sector:%2d, block:%3d, key type:%C, key count:%2d ", i, b, t?'B':'A', keycnt); - uint32_t max_keys = keycnt>USB_CMD_DATA_SIZE/6?USB_CMD_DATA_SIZE/6:keycnt; + bool foundAKey = false; + uint32_t max_keys = keycnt > USB_CMD_DATA_SIZE / 6 ? USB_CMD_DATA_SIZE / 6 : keycnt; + if (SectorsCnt) { + PrintAndLog("To cancel this operation press the button on the proxmark..."); + printf("--"); + for (uint32_t c = 0; c < keycnt; c += max_keys) { + + uint32_t size = keycnt-c > max_keys ? max_keys : keycnt-c; + res = mfCheckKeysSec(SectorsCnt, keyType, timeout14a * 1.06 / 100, true, size, &keyBlock[6 * c], e_sector); // timeout is (ms * 106)/10 or us*0.0106 + + if (res != 1) { + if (!res) { + printf("o"); + foundAKey = true; + } else { + printf("."); + } + } else { + printf("\n"); + PrintAndLog("Command execute timeout"); + } + } + } else { + int keyAB = keyType; + do { for (uint32_t c = 0; c < keycnt; c+=max_keys) { - uint32_t size = keycnt-c>max_keys?max_keys:keycnt-c; - res = mfCheckKeys(b, t, true, size, &keyBlock[6*c], &key64); + + uint32_t size = keycnt-c > max_keys ? max_keys : keycnt-c; + res = mfCheckKeys(blockNo, keyAB & 0x01, true, size, &keyBlock[6 * c], &key64); + if (res != 1) { if (!res) { - PrintAndLog("Found valid key:[%012" PRIx64 "]",key64); - num_to_bytes(key64, 6, foundKey[t][i]); - validKey[t][i] = true; + PrintAndLog("Found valid key:[%d:%c]%012" PRIx64, blockNo, (keyAB & 0x01)?'B':'A', key64); + foundAKey = true; } } else { PrintAndLog("Command execute timeout"); } } - b<127?(b+=4):(b+=16); - } + } while(--keyAB > 0); } - + + // print result + if (foundAKey) { + if (SectorsCnt) { + PrintAndLog(""); + PrintAndLog("|---|----------------|---|----------------|---|"); + PrintAndLog("|sec|key A |res|key B |res|"); + PrintAndLog("|---|----------------|---|----------------|---|"); + for (i = 0; i < SectorsCnt; i++) { + PrintAndLog("|%03d| %012" PRIx64 " | %d | %012" PRIx64 " | %d |", i, + e_sector[i].Key[0], e_sector[i].foundKey[0], e_sector[i].Key[1], e_sector[i].foundKey[1]); + } + PrintAndLog("|---|----------------|---|----------------|---|"); + } + } else { + PrintAndLog(""); + PrintAndLog("No valid keys found."); + } + if (transferToEml) { uint8_t block[16]; for (uint16_t sectorNo = 0; sectorNo < SectorsCnt; sectorNo++) { - if (validKey[0][sectorNo] || validKey[1][sectorNo]) { + if (e_sector[sectorNo].foundKey[0] || e_sector[sectorNo].foundKey[1]) { mfEmlGetMem(block, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1); for (uint16_t t = 0; t < 2; t++) { - if (validKey[t][sectorNo]) { - memcpy(block + t*10, foundKey[t][sectorNo], 6); + if (e_sector[sectorNo].foundKey[t]) { + num_to_bytes(e_sector[sectorNo].Key[t], 6, block + t * 10); } } mfEmlSetMem(block, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1); @@ -1231,16 +1194,22 @@ int CmdHF14AMfChk(const char *Cmd) FILE *fkeys = fopen("dumpkeys.bin","wb"); if (fkeys == NULL) { PrintAndLog("Could not create file dumpkeys.bin"); + free(e_sector); free(keyBlock); return 1; } - for (uint16_t t = 0; t < 2; t++) { - fwrite(foundKey[t], 1, 6*SectorsCnt, fkeys); + uint8_t mkey[6]; + for (uint8_t t = 0; t < 2; t++) { + for (uint8_t sectorNo = 0; sectorNo < SectorsCnt; sectorNo++) { + num_to_bytes(e_sector[sectorNo].Key[t], 6, mkey); + fwrite(mkey, 1, 6, fkeys); + } } fclose(fkeys); PrintAndLog("Found keys have been dumped to file dumpkeys.bin. 0xffffffffffff has been inserted for unknown keys."); } + free(e_sector); free(keyBlock); PrintAndLog(""); return 0; diff --git a/client/cmdhfmf.h b/client/cmdhfmf.h index 235fd86d..3bd3e95a 100644 --- a/client/cmdhfmf.h +++ b/client/cmdhfmf.h @@ -11,6 +11,8 @@ #ifndef CMDHFMF_H__ #define CMDHFMF_H__ +#include "mifaredefault.h" + extern int CmdHFMF(const char *Cmd); extern int CmdHF14AMfDbg(const char* cmd); diff --git a/client/cmdmain.c b/client/cmdmain.c index f407cee4..db88b3ac 100644 --- a/client/cmdmain.c +++ b/client/cmdmain.c @@ -139,7 +139,7 @@ int getCommand(UsbCommand* response) * @param ms_timeout * @return true if command was returned, otherwise false */ -bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout) { +bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning) { UsbCommand resp; @@ -155,7 +155,7 @@ bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeou } } msleep(10); // XXX ugh - if (dm_seconds == 200) { // Two seconds elapsed + if (dm_seconds == 200 && show_warning) { // Two seconds elapsed PrintAndLog("Waiting for a response from the proxmark..."); PrintAndLog("Don't forget to cancel its operation first by pressing on the button"); } @@ -163,9 +163,12 @@ bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeou return false; } +bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout) { + return WaitForResponseTimeoutW(cmd, response, ms_timeout, true); +} bool WaitForResponse(uint32_t cmd, UsbCommand* response) { - return WaitForResponseTimeout(cmd,response,-1); + return WaitForResponseTimeoutW(cmd, response, -1, true); } diff --git a/client/cmdmain.h b/client/cmdmain.h index 0de3f392..d39bc114 100644 --- a/client/cmdmain.h +++ b/client/cmdmain.h @@ -18,6 +18,7 @@ extern void UsbCommandReceived(UsbCommand *UC); extern int CommandReceived(char *Cmd); +extern bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning); extern bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout); extern bool WaitForResponse(uint32_t cmd, UsbCommand* response); extern void clearCommandBuffer(); diff --git a/client/mifaredefault.h b/client/mifaredefault.h new file mode 100644 index 00000000..78231c86 --- /dev/null +++ b/client/mifaredefault.h @@ -0,0 +1,40 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 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 default constants +//----------------------------------------------------------------------------- + +#ifndef MIFAREDEFAULT_H__ +#define MIFAREDEFAULT_H__ + +#include + +#define MifareDefaultKeysSize sizeof(MifareDefaultKeys) / sizeof(uint64_t) + +static const uint64_t MifareDefaultKeys[] = +{ + 0xffffffffffff, // Default key (first key used by program if no user defined key) + 0x000000000000, // Blank key + 0xa0a1a2a3a4a5, // NFCForum MAD key + 0xb0b1b2b3b4b5, + 0xaabbccddeeff, + 0x1a2b3c4d5e6f, + 0x123456789abc, + 0x010203040506, + 0x123456abcdef, + 0xabcdef123456, + 0x4d3a99c351dd, + 0x1a982c7e459a, + 0xd3f7d3f7d3f7, + 0x714c5c886e97, + 0x587ee5f9350f, + 0xa0478cc39091, + 0x533cb6c723f6, + 0x8fd0a4f256e9 +}; + +#endif diff --git a/client/mifarehost.c b/client/mifarehost.c index 629c8feb..a02019a3 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -228,17 +228,46 @@ int mfCheckKeys (uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t key *key = -1; - UsbCommand c = {CMD_MIFARE_CHKKEYS, {((blockNo & 0xff) | ((keyType&0xff)<<8)), clear_trace, keycnt}}; + UsbCommand c = {CMD_MIFARE_CHKKEYS, {((blockNo & 0xff) | ((keyType & 0xff) << 8)), clear_trace, keycnt}}; memcpy(c.d.asBytes, keyBlock, 6 * keycnt); SendCommand(&c); UsbCommand resp; - if (!WaitForResponseTimeout(CMD_ACK,&resp,3000)) return 1; + if (!WaitForResponseTimeout(CMD_ACK,&resp,3000)) return 1; if ((resp.arg[0] & 0xff) != 0x01) return 2; *key = bytes_to_num(resp.d.asBytes, 6); return 0; } +int mfCheckKeysSec(uint8_t sectorCnt, uint8_t keyType, uint8_t timeout14a, bool clear_trace, uint8_t keycnt, uint8_t * keyBlock, sector_t * e_sector){ + + uint8_t keyPtr = 0; + + if (e_sector == NULL) + return -1; + + UsbCommand c = {CMD_MIFARE_CHKKEYS, {((sectorCnt & 0xff) | ((keyType & 0xff) << 8)), (clear_trace | 0x02)|((timeout14a & 0xff) << 8), keycnt}}; + memcpy(c.d.asBytes, keyBlock, 6 * keycnt); + SendCommand(&c); + + UsbCommand resp; + if (!WaitForResponseTimeoutW(CMD_ACK, &resp, MAX(3000, 1000 + 13 * sectorCnt * keycnt * (keyType == 2 ? 2 : 1)), false)) return 1; // timeout: 13 ms / fail auth + if ((resp.arg[0] & 0xff) != 0x01) return 2; + + bool foundAKey = false; + for(int sec = 0; sec < sectorCnt; sec++){ + for(int keyAB = 0; keyAB < 2; keyAB++){ + keyPtr = *(resp.d.asBytes + keyAB * 40 + sec); + if (keyPtr){ + e_sector[sec].foundKey[keyAB] = true; + e_sector[sec].Key[keyAB] = bytes_to_num(keyBlock + (keyPtr - 1) * 6, 6); + foundAKey = true; + } + } + } + return foundAKey ? 0 : 3; +} + // Compare 16 Bits out of cryptostate int Compare16Bits(const void * a, const void * b) { if ((*(uint64_t*)b & 0x00ff000000ff0000) == (*(uint64_t*)a & 0x00ff000000ff0000)) return 0; diff --git a/client/mifarehost.h b/client/mifarehost.h index 34793a29..118d55cc 100644 --- a/client/mifarehost.h +++ b/client/mifarehost.h @@ -1,4 +1,4 @@ -// Merlok, 2011 +// Merlok, 2011, 2017 // people from mifare@nethemba.com, 2010 // // This code is licensed to you under the terms of the GNU GPL, version 2 or, @@ -15,6 +15,11 @@ #include #include "data.h" +// defaults +// timeout in units. (ms * 106)/10 or us*0.0106 +// 5 == 500us +#define MF_CHKKEYS_DEFTIMEOUT 5 + // mfCSetBlock work flags #define CSETBLOCK_UID 0x01 #define CSETBLOCK_WUPC 0x02 @@ -24,11 +29,17 @@ #define CSETBLOCK_SINGLE_OPER 0x1F #define CSETBLOCK_MAGIC_1B 0x40 +typedef struct { + uint64_t Key[2]; + int foundKey[2]; +} sector_t; + extern char logHexFileName[FILE_PATH_SIZE]; extern int mfDarkside(uint64_t *key); extern int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *ResultKeys, bool calibrate); extern int mfCheckKeys (uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keycnt, uint8_t *keyBlock, uint64_t *key); +extern int mfCheckKeysSec(uint8_t sectorCnt, uint8_t keyType, uint8_t timeout14a, bool clear_trace, uint8_t keycnt, uint8_t * keyBlock, sector_t * e_sector); extern int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount); extern int mfEmlSetMem(uint8_t *data, int blockNum, int blocksCount); From 73d7bf75c8b1d9afde18964ba597a84966b4f272 Mon Sep 17 00:00:00 2001 From: merlokk Date: Mon, 16 Oct 2017 11:33:17 +0300 Subject: [PATCH 013/310] small fix #416 --- armsrc/iso14443b.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/armsrc/iso14443b.h b/armsrc/iso14443b.h index f90c54f3..de6faa92 100644 --- a/armsrc/iso14443b.h +++ b/armsrc/iso14443b.h @@ -7,7 +7,7 @@ // at your option, any later version. See the LICENSE.txt file for the text of // the license. //----------------------------------------------------------------------------- -// Routines to support ISO 14443 type A. +// Routines to support ISO 14443 type B. //----------------------------------------------------------------------------- #ifndef __ISO14443B_H From 9d0a333449d52dc0d9cfca217cf9f0cc0f792744 Mon Sep 17 00:00:00 2001 From: Iceman Date: Mon, 16 Oct 2017 18:16:39 +0200 Subject: [PATCH 014/310] wrong define used (#421) The set define under the case "STD_SET_CONFIGURATION" is not the same as here. The endpoint is configured as INTERUPT not ISOCHRONOUS --- common/usb_cdc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/usb_cdc.c b/common/usb_cdc.c index 84aa2c06..e4c5fb57 100644 --- a/common/usb_cdc.c +++ b/common/usb_cdc.c @@ -611,7 +611,7 @@ void AT91F_CDC_Enumerate() { else if (wIndex == 2) pUdp->UDP_CSR[2] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_IN); else if (wIndex == 3) - pUdp->UDP_CSR[3] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_ISO_IN); + pUdp->UDP_CSR[3] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_INT_IN); AT91F_USB_SendZlp(pUdp); } else From f194e4290ce72c4a6b1eccc007393f5ef6634003 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Tue, 17 Oct 2017 19:25:40 +0200 Subject: [PATCH 015/310] Device side USB implementation: * add some comments and #defines in usb_cdc.c * use full FIFO size of 64 Bytes for device -> host transfers --- common/usb_cdc.c | 230 +++++++++++++++++++++++++---------------------- 1 file changed, 121 insertions(+), 109 deletions(-) diff --git a/common/usb_cdc.c b/common/usb_cdc.c index e4c5fb57..56690ad8 100644 --- a/common/usb_cdc.c +++ b/common/usb_cdc.c @@ -38,19 +38,20 @@ #define AT91C_EP_CONTROL 0 -#define AT91C_EP_IN_SIZE 0x40 #define AT91C_EP_OUT 1 -#define AT91C_EP_OUT_SIZE 0x40 #define AT91C_EP_IN 2 +#define AT91C_EP_NOTIFY 3 +#define AT91C_EP_OUT_SIZE 0x40 +#define AT91C_EP_IN_SIZE 0x40 static const char devDescriptor[] = { /* Device descriptor */ 0x12, // bLength 0x01, // bDescriptorType 0x00,0x02, // Complies with USB Spec. Release (0200h = release 2.0) - 0x02, // bDeviceClass: CDC class code - 0x00, // bDeviceSubclass: CDC class sub code - 0x00, // bDeviceProtocol: CDC Device protocol + 0x02, // bDeviceClass: (Communication Device Class) + 0x00, // bDeviceSubclass: (unused at this time) + 0x00, // bDeviceProtocol: (unused at this time) 0x08, // bMaxPacketSize0 0xc4,0x9a, // Vendor ID (0x9ac4 = J. Westhues) 0x8f,0x4b, // Product ID (0x4b8f = Proxmark-3 RFID Instrument) @@ -74,79 +75,78 @@ static const char cfgDescriptor[] = { 0xC0, // CbmAttributes 0xA0 0xFA, // CMaxPower - /* Communication Class Interface Descriptor Requirement */ + /* Interface 0 Descriptor: Communication Class Interface */ 0x09, // bLength 0x04, // bDescriptorType 0x00, // bInterfaceNumber 0x00, // bAlternateSetting 0x01, // bNumEndpoints - 0x02, // bInterfaceClass - 0x02, // bInterfaceSubclass - 0x01, // bInterfaceProtocol + 0x02, // bInterfaceClass: Communication Interface Class + 0x02, // bInterfaceSubclass: Abstract Control Model + 0x01, // bInterfaceProtocol: Common AT Commands, V.25ter 0x00, // iInterface /* Header Functional Descriptor */ 0x05, // bFunction Length - 0x24, // bDescriptor type: CS_INTERFACE - 0x00, // bDescriptor subtype: Header Func Desc + 0x24, // bDescriptor type: CS_INTERFACE + 0x00, // bDescriptor subtype: Header Functional Descriptor 0x10, // bcdCDC:1.1 0x01, /* ACM Functional Descriptor */ 0x04, // bFunctionLength - 0x24, // bDescriptor Type: CS_INTERFACE - 0x02, // bDescriptor Subtype: ACM Func Desc - 0x02, // bmCapabilities + 0x24, // bDescriptor Type: CS_INTERFACE + 0x02, // bDescriptor Subtype: Abstract Control Management Functional Descriptor + 0x02, // bmCapabilities: D1: Device supports the request combination of Set_Line_Coding, Set_Control_Line_State, Get_Line_Coding, and the notification Serial_State /* Union Functional Descriptor */ 0x05, // bFunctionLength - 0x24, // bDescriptorType: CS_INTERFACE - 0x06, // bDescriptor Subtype: Union Func Desc - 0x00, // bMasterInterface: Communication Class Interface - 0x01, // bSlaveInterface0: Data Class Interface + 0x24, // bDescriptorType: CS_INTERFACE + 0x06, // bDescriptor Subtype: Union Functional Descriptor + 0x00, // bMasterInterface: Communication Class Interface + 0x01, // bSlaveInterface0: Data Class Interface /* Call Management Functional Descriptor */ 0x05, // bFunctionLength - 0x24, // bDescriptor Type: CS_INTERFACE - 0x01, // bDescriptor Subtype: Call Management Func Desc - 0x00, // bmCapabilities: D1 + D0 - 0x01, // bDataInterface: Data Class Interface 1 + 0x24, // bDescriptor Type: CS_INTERFACE + 0x01, // bDescriptor Subtype: Call Management Functional Descriptor + 0x00, // bmCapabilities: Device sends/receives call management information only over the Communication Class interface. Device does not handle call management itself + 0x01, // bDataInterface: Data Class Interface 1 /* Endpoint 1 descriptor */ 0x07, // bLength 0x05, // bDescriptorType - 0x83, // bEndpointAddress, Endpoint 03 - IN - 0x03, // bmAttributes INT - 0x08, // wMaxPacketSize + 0x83, // bEndpointAddress: Endpoint 03 - IN + 0x03, // bmAttributes: INT + 0x08, // wMaxPacketSize: 8 0x00, 0xFF, // bInterval - /* Data Class Interface Descriptor Requirement */ + /* Interface 1 Descriptor: Data Class Interface */ 0x09, // bLength 0x04, // bDescriptorType 0x01, // bInterfaceNumber 0x00, // bAlternateSetting 0x02, // bNumEndpoints - 0x0A, // bInterfaceClass - 0x00, // bInterfaceSubclass - 0x00, // bInterfaceProtocol + 0x0A, // bInterfaceClass: Data Interface Class + 0x00, // bInterfaceSubclass: not used + 0x00, // bInterfaceProtocol: No class specific protocol required) 0x00, // iInterface - /* First alternate setting */ /* Endpoint 1 descriptor */ 0x07, // bLength 0x05, // bDescriptorType - 0x01, // bEndpointAddress, Endpoint 01 - OUT - 0x02, // bmAttributes BULK - AT91C_EP_OUT_SIZE, // wMaxPacketSize + 0x01, // bEndpointAddress: Endpoint 01 - OUT + 0x02, // bmAttributes: BULK + AT91C_EP_OUT_SIZE, // wMaxPacketSize 0x00, 0x00, // bInterval /* Endpoint 2 descriptor */ 0x07, // bLength 0x05, // bDescriptorType - 0x82, // bEndpointAddress, Endpoint 02 - IN - 0x02, // bmAttributes BULK + 0x82, // bEndpointAddress: Endpoint 02 - IN + 0x02, // bmAttributes: BULK AT91C_EP_IN_SIZE, // wMaxPacketSize 0x00, 0x00 // bInterval @@ -262,6 +262,7 @@ AT91S_CDC_LINE_CODING line = { 0, // None Parity 8}; // 8 Data bits + void AT91F_CDC_Enumerate(); AT91PS_UDP pUdp = AT91C_BASE_UDP; @@ -269,52 +270,55 @@ byte_t btConfiguration = 0; byte_t btConnection = 0; byte_t btReceiveBank = AT91C_UDP_RX_DATA_BK0; + //*---------------------------------------------------------------------------- //* \fn usb_disable //* \brief This function deactivates the USB device //*---------------------------------------------------------------------------- void usb_disable() { - // Disconnect the USB device - AT91C_BASE_PIOA->PIO_ODR = GPIO_USB_PU; - - // Clear all lingering interrupts - if(pUdp->UDP_ISR & AT91C_UDP_ENDBUSRES) { - pUdp->UDP_ICR = AT91C_UDP_ENDBUSRES; - } + // Disconnect the USB device + AT91C_BASE_PIOA->PIO_ODR = GPIO_USB_PU; + + // Clear all lingering interrupts + if(pUdp->UDP_ISR & AT91C_UDP_ENDBUSRES) { + pUdp->UDP_ICR = AT91C_UDP_ENDBUSRES; + } } + //*---------------------------------------------------------------------------- //* \fn usb_enable //* \brief This function Activates the USB device //*---------------------------------------------------------------------------- void usb_enable() { - // Set the PLL USB Divider - AT91C_BASE_CKGR->CKGR_PLLR |= AT91C_CKGR_USBDIV_1 ; - - // Specific Chip USB Initialisation - // Enables the 48MHz USB clock UDPCK and System Peripheral USB Clock - AT91C_BASE_PMC->PMC_SCER = AT91C_PMC_UDP; - AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_UDP); - - // Enable UDP PullUp (USB_DP_PUP) : enable & Clear of the corresponding PIO - // Set in PIO mode and Configure in Output - AT91C_BASE_PIOA->PIO_PER = GPIO_USB_PU; // Set in PIO mode - AT91C_BASE_PIOA->PIO_OER = GPIO_USB_PU; // Configure as Output - - // Clear for set the Pullup resistor - AT91C_BASE_PIOA->PIO_CODR = GPIO_USB_PU; - - // Disconnect and reconnect USB controller for 100ms - usb_disable(); - - // Wait for a short while - for (volatile size_t i=0; i<0x100000; i++); + // Set the PLL USB Divider + AT91C_BASE_CKGR->CKGR_PLLR |= AT91C_CKGR_USBDIV_1 ; - // Reconnect USB reconnect - AT91C_BASE_PIOA->PIO_SODR = GPIO_USB_PU; - AT91C_BASE_PIOA->PIO_OER = GPIO_USB_PU; + // Specific Chip USB Initialisation + // Enables the 48MHz USB clock UDPCK and System Peripheral USB Clock + AT91C_BASE_PMC->PMC_SCER = AT91C_PMC_UDP; + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_UDP); + + // Enable UDP PullUp (USB_DP_PUP) : enable & Clear of the corresponding PIO + // Set in PIO mode and Configure in Output + AT91C_BASE_PIOA->PIO_PER = GPIO_USB_PU; // Set in PIO mode + AT91C_BASE_PIOA->PIO_OER = GPIO_USB_PU; // Configure as Output + + // Clear for set the Pullup resistor + AT91C_BASE_PIOA->PIO_CODR = GPIO_USB_PU; + + // Disconnect and reconnect USB controller for 100ms + usb_disable(); + + // Wait for a short while + for (volatile size_t i=0; i<0x100000; i++); + + // Reconnect USB reconnect + AT91C_BASE_PIOA->PIO_SODR = GPIO_USB_PU; + AT91C_BASE_PIOA->PIO_OER = GPIO_USB_PU; } + //*---------------------------------------------------------------------------- //* \fn usb_check //* \brief Test if the device is configured and handle enumeration @@ -331,8 +335,7 @@ bool usb_check() { pUdp->UDP_FADDR = AT91C_UDP_FEN; // Configure endpoint 0 pUdp->UDP_CSR[AT91C_EP_CONTROL] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_CTRL); - } - else if (isr & AT91C_UDP_EPINT0) { + } else if (isr & AT91C_UDP_EPINT0) { pUdp->UDP_ICR = AT91C_UDP_EPINT0; AT91F_CDC_Enumerate(); } @@ -342,10 +345,11 @@ bool usb_check() { bool usb_poll() { - if (!usb_check()) return false; - return (pUdp->UDP_CSR[AT91C_EP_OUT] & btReceiveBank); + if (!usb_check()) return false; + return (pUdp->UDP_CSR[AT91C_EP_OUT] & btReceiveBank); } + /** In github PR #129, some users appears to get a false positive from usb_poll, which returns true, but the usb_read operation @@ -356,7 +360,6 @@ bool usb_poll() **/ bool usb_poll_validate_length() { - if (!usb_check()) return false; if (!(pUdp->UDP_CSR[AT91C_EP_OUT] & btReceiveBank)) return false; return (pUdp->UDP_CSR[AT91C_EP_OUT] >> 16) > 0; @@ -393,48 +396,54 @@ uint32_t usb_read(byte_t* data, size_t len) { return nbBytesRcv; } + //*---------------------------------------------------------------------------- //* \fn usb_write //* \brief Send through endpoint 2 //*---------------------------------------------------------------------------- uint32_t usb_write(const byte_t* data, const size_t len) { - size_t length = len; + size_t length = len; uint32_t cpt = 0; - if (!length) return 0; - if (!usb_check()) return 0; - + if (!length) return 0; + if (!usb_check()) return 0; + // Send the first packet - cpt = MIN(length, AT91C_EP_IN_SIZE-1); + cpt = MIN(length, AT91C_EP_IN_SIZE); length -= cpt; - while (cpt--) pUdp->UDP_FDR[AT91C_EP_IN] = *data++; + while (cpt--) { + pUdp->UDP_FDR[AT91C_EP_IN] = *data++; + } UDP_SET_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXPKTRDY); while (length) { - // Fill the second bank - cpt = MIN(length, AT91C_EP_IN_SIZE-1); + // Fill the next bank + cpt = MIN(length, AT91C_EP_IN_SIZE); length -= cpt; - while (cpt--) pUdp->UDP_FDR[AT91C_EP_IN] = *data++; - // Wait for the first bank to be sent + while (cpt--) { + pUdp->UDP_FDR[AT91C_EP_IN] = *data++; + } + // Wait for the previous bank to be sent while (!(pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP)) { if (!usb_check()) return length; - } + } UDP_CLEAR_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXCOMP); while (pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP); UDP_SET_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXPKTRDY); } - + // Wait for the end of transfer while (!(pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP)) { if (!usb_check()) return length; - } - + } + UDP_CLEAR_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXCOMP); while (pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP); return length; } + //*---------------------------------------------------------------------------- //* \fn AT91F_USB_SendData //* \brief Send Data through the control endpoint @@ -477,6 +486,7 @@ static void AT91F_USB_SendData(AT91PS_UDP pUdp, const char *pData, uint32_t leng } } + //*---------------------------------------------------------------------------- //* \fn AT91F_USB_SendZlp //* \brief Send zero length packet through the control endpoint @@ -488,6 +498,7 @@ void AT91F_USB_SendZlp(AT91PS_UDP pUdp) { while (pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_TXCOMP); } + //*---------------------------------------------------------------------------- //* \fn AT91F_USB_SendStall //* \brief Stall the control endpoint @@ -499,6 +510,7 @@ void AT91F_USB_SendStall(AT91PS_UDP pUdp) { while (pUdp->UDP_CSR[AT91C_EP_CONTROL] & (AT91C_UDP_FORCESTALL | AT91C_UDP_ISOERROR)); } + //*---------------------------------------------------------------------------- //* \fn AT91F_CDC_Enumerate //* \brief This function is a callback invoked when a SETUP packet is received @@ -510,16 +522,16 @@ void AT91F_CDC_Enumerate() { if ( !(pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_RXSETUP) ) return; - bmRequestType = pUdp->UDP_FDR[0]; - bRequest = pUdp->UDP_FDR[0]; - wValue = (pUdp->UDP_FDR[0] & 0xFF); - wValue |= (pUdp->UDP_FDR[0] << 8); - wIndex = (pUdp->UDP_FDR[0] & 0xFF); - wIndex |= (pUdp->UDP_FDR[0] << 8); - wLength = (pUdp->UDP_FDR[0] & 0xFF); - wLength |= (pUdp->UDP_FDR[0] << 8); + bmRequestType = pUdp->UDP_FDR[AT91C_EP_CONTROL]; + bRequest = pUdp->UDP_FDR[AT91C_EP_CONTROL]; + wValue = (pUdp->UDP_FDR[AT91C_EP_CONTROL] & 0xFF); + wValue |= (pUdp->UDP_FDR[AT91C_EP_CONTROL] << 8); + wIndex = (pUdp->UDP_FDR[AT91C_EP_CONTROL] & 0xFF); + wIndex |= (pUdp->UDP_FDR[AT91C_EP_CONTROL] << 8); + wLength = (pUdp->UDP_FDR[AT91C_EP_CONTROL] & 0xFF); + wLength |= (pUdp->UDP_FDR[AT91C_EP_CONTROL] << 8); - if (bmRequestType & 0x80) { + if (bmRequestType & 0x80) { // Data Phase Transfer Direction Device to Host UDP_SET_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_DIR); while ( !(pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_DIR) ); } @@ -553,29 +565,29 @@ void AT91F_CDC_Enumerate() { btConfiguration = wValue; AT91F_USB_SendZlp(pUdp); pUdp->UDP_GLBSTATE = (wValue) ? AT91C_UDP_CONFG : AT91C_UDP_FADDEN; - pUdp->UDP_CSR[1] = (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_OUT) : 0; - pUdp->UDP_CSR[2] = (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_IN) : 0; - pUdp->UDP_CSR[3] = (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_INT_IN) : 0; + pUdp->UDP_CSR[AT91C_EP_OUT] = (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_OUT) : 0; + pUdp->UDP_CSR[AT91C_EP_IN] = (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_IN) : 0; + pUdp->UDP_CSR[AT91C_EP_NOTIFY] = (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_INT_IN) : 0; break; case STD_GET_CONFIGURATION: AT91F_USB_SendData(pUdp, (char *) &(btConfiguration), sizeof(btConfiguration)); break; case STD_GET_STATUS_ZERO: - wStatus = 0; + wStatus = 0; // Device is Bus powered, remote wakeup disabled AT91F_USB_SendData(pUdp, (char *) &wStatus, sizeof(wStatus)); break; case STD_GET_STATUS_INTERFACE: - wStatus = 0; + wStatus = 0; // reserved for future use AT91F_USB_SendData(pUdp, (char *) &wStatus, sizeof(wStatus)); break; case STD_GET_STATUS_ENDPOINT: wStatus = 0; wIndex &= 0x0F; - if ((pUdp->UDP_GLBSTATE & AT91C_UDP_CONFG) && (wIndex <= 3)) { + if ((pUdp->UDP_GLBSTATE & AT91C_UDP_CONFG) && (wIndex <= AT91C_EP_NOTIFY)) { wStatus = (pUdp->UDP_CSR[wIndex] & AT91C_UDP_EPEDS) ? 0 : 1; AT91F_USB_SendData(pUdp, (char *) &wStatus, sizeof(wStatus)); } - else if ((pUdp->UDP_GLBSTATE & AT91C_UDP_FADDEN) && (wIndex == 0)) { + else if ((pUdp->UDP_GLBSTATE & AT91C_UDP_FADDEN) && (wIndex == AT91C_EP_CONTROL)) { wStatus = (pUdp->UDP_CSR[wIndex] & AT91C_UDP_EPEDS) ? 0 : 1; AT91F_USB_SendData(pUdp, (char *) &wStatus, sizeof(wStatus)); } @@ -590,7 +602,7 @@ void AT91F_CDC_Enumerate() { break; case STD_SET_FEATURE_ENDPOINT: wIndex &= 0x0F; - if ((wValue == 0) && wIndex && (wIndex <= 3)) { + if ((wValue == 0) && (wIndex >= AT91C_EP_OUT) && (wIndex <= AT91C_EP_NOTIFY)) { pUdp->UDP_CSR[wIndex] = 0; AT91F_USB_SendZlp(pUdp); } @@ -605,13 +617,13 @@ void AT91F_CDC_Enumerate() { break; case STD_CLEAR_FEATURE_ENDPOINT: wIndex &= 0x0F; - if ((wValue == 0) && wIndex && (wIndex <= 3)) { - if (wIndex == 1) - pUdp->UDP_CSR[1] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_OUT); - else if (wIndex == 2) - pUdp->UDP_CSR[2] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_IN); - else if (wIndex == 3) - pUdp->UDP_CSR[3] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_INT_IN); + if ((wValue == 0) && (wIndex >= AT91C_EP_OUT) && (wIndex <= AT91C_EP_NOTIFY)) { + if (wIndex == AT91C_EP_OUT) + pUdp->UDP_CSR[AT91C_EP_OUT] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_OUT); + else if (wIndex == AT91C_EP_IN) + pUdp->UDP_CSR[AT91C_EP_IN] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_IN); + else if (wIndex == AT91C_EP_NOTIFY) + pUdp->UDP_CSR[AT91C_EP_NOTIFY] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_INT_IN); AT91F_USB_SendZlp(pUdp); } else From 0c86cb0127054e8109d0f0ca4d7e2a890d1962e8 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Wed, 18 Oct 2017 22:44:59 +0300 Subject: [PATCH 016/310] Check keys in hf mf nested (issue #426) * hf mf nested added 14a timeout for check keys * hf mf nested added options s and ss --- CHANGELOG.md | 1 + client/cmdhfmf.c | 59 ++++++++++++++++++++++++++++++++++++------------ 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 45fe1392..c7ec6507 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added to `hf mf nested` source key check before attack (Merlok) - Added to `hf mf nested` after attack it checks all found keys on non-open sectors (Merlok) - `hf mf chk` Added setings to set iso14443a operations timeout. default timeout set to 500us (Merlok) +- Added to `hf mf nested` parameters `s` and `ss` for checking slow cards (Merlok) ## [3.0.1][2017-06-08] diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 12fb0b78..553803c1 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -524,7 +524,35 @@ int CmdHF14AMfRestore(const char *Cmd) return 0; } +//---------------------------------------------- +// Nested +//---------------------------------------------- # define NESTED_KEY_COUNT 15 + +static void parseParamTDS(const char *Cmd, const uint8_t indx, bool *paramT, bool *paramD, uint8_t *timeout) { + char ctmp3[3] = {0}; + int len = param_getlength(Cmd, indx); + if (len > 0 && len < 4){ + param_getstr(Cmd, indx, ctmp3); + + *paramT |= (ctmp3[0] == 't' || ctmp3[0] == 'T'); + *paramD |= (ctmp3[0] == 'd' || ctmp3[0] == 'D'); + bool paramS1 = *paramT || *paramD; + + // slow and very slow + if (ctmp3[0] == 's' || ctmp3[0] == 'S' || ctmp3[1] == 's' || ctmp3[1] == 'S') { + *timeout = 11; // slow + + if (!paramS1 && (ctmp3[1] == 's' || ctmp3[1] == 'S')) { + *timeout = 53; // very slow + } + if (paramS1 && (ctmp3[2] == 's' || ctmp3[2] == 'S')) { + *timeout = 53; // very slow + } + } + } +} + int CmdHF14AMfNested(const char *Cmd) { int i, j, res, iterations; @@ -537,6 +565,8 @@ int CmdHF14AMfNested(const char *Cmd) uint8_t key[6] = {0, 0, 0, 0, 0, 0}; uint8_t keyBlock[NESTED_KEY_COUNT * 6]; uint64_t key64 = 0; + // timeout in units. (ms * 106)/10 or us*0.0106 + uint8_t btimeout14a = MF_CHKKEYS_DEFTIMEOUT; // fast by default bool autosearchKey = false; @@ -550,20 +580,23 @@ int CmdHF14AMfNested(const char *Cmd) if (strlen(Cmd)<3) { PrintAndLog("Usage:"); - PrintAndLog(" all sectors: hf mf nested [t,d]"); - PrintAndLog(" all sectors autosearch key: hf mf nested * [t,d]"); + PrintAndLog(" all sectors: hf mf nested [t|d|s|ss]"); + PrintAndLog(" all sectors autosearch key: hf mf nested * [t|d|s|ss]"); PrintAndLog(" one sector: hf mf nested o "); PrintAndLog(" [t]"); PrintAndLog(" "); PrintAndLog("card memory - 0 - MINI(320 bytes), 1 - 1K, 2 - 2K, 4 - 4K, - 1K"); PrintAndLog("t - transfer keys to emulator memory"); PrintAndLog("d - write keys to binary file dumpkeys.bin"); + PrintAndLog("s - Slow (1ms) check keys (required by some non standard cards)"); + PrintAndLog("ss - Very slow (5ms) check keys"); PrintAndLog(" "); PrintAndLog(" sample1: hf mf nested 1 0 A FFFFFFFFFFFF "); PrintAndLog(" sample2: hf mf nested 1 0 A FFFFFFFFFFFF t "); PrintAndLog(" sample3: hf mf nested 1 0 A FFFFFFFFFFFF d "); PrintAndLog(" sample4: hf mf nested o 0 A FFFFFFFFFFFF 4 A"); PrintAndLog(" sample5: hf mf nested 1 * t"); + PrintAndLog(" sample6: hf mf nested 1 * ss"); return 0; } @@ -580,11 +613,10 @@ int CmdHF14AMfNested(const char *Cmd) if (param_getchar(Cmd, 1) == '*') { autosearchKey = true; - ctmp = param_getchar(Cmd, 2); - transferToEml |= (ctmp == 't' || ctmp == 'T'); - createDumpFile |= (ctmp == 'd' || ctmp == 'D'); + parseParamTDS(Cmd, 2, &transferToEml, &createDumpFile, &btimeout14a); - PrintAndLog("--nested. sectors:%2d, block no:*, eml:%c, dmp=%c ", SectorsCnt, transferToEml?'y':'n', createDumpFile?'y':'n'); + PrintAndLog("--nested. sectors:%2d, block no:*, eml:%c, dmp=%c checktimeout=%d us", + SectorsCnt, transferToEml?'y':'n', createDumpFile?'y':'n', ((int)btimeout14a * 10000) / 106); } else { blockNo = param_get8(Cmd, 1); @@ -621,16 +653,13 @@ int CmdHF14AMfNested(const char *Cmd) if (ctmp != 'A' && ctmp != 'a') trgKeyType = 1; - ctmp = param_getchar(Cmd, 6); - transferToEml |= (ctmp == 't' || ctmp == 'T'); - createDumpFile |= (ctmp == 'd' || ctmp == 'D'); + parseParamTDS(Cmd, 6, &transferToEml, &createDumpFile, &btimeout14a); } else { - ctmp = param_getchar(Cmd, 4); - transferToEml |= (ctmp == 't' || ctmp == 'T'); - createDumpFile |= (ctmp == 'd' || ctmp == 'D'); + parseParamTDS(Cmd, 4, &transferToEml, &createDumpFile, &btimeout14a); } - PrintAndLog("--nested. sectors:%2d, block no:%3d, key type:%c, eml:%c, dmp=%c ", SectorsCnt, blockNo, keyType?'B':'A', transferToEml?'y':'n', createDumpFile?'y':'n'); + PrintAndLog("--nested. sectors:%2d, block no:%3d, key type:%c, eml:%c, dmp=%c checktimeout=%d us", + SectorsCnt, blockNo, keyType?'B':'A', transferToEml?'y':'n', createDumpFile?'y':'n', ((int)btimeout14a * 10000) / 106); } // one-sector nested @@ -684,7 +713,7 @@ int CmdHF14AMfNested(const char *Cmd) } PrintAndLog("Testing known keys. Sector count=%d", SectorsCnt); - mfCheckKeysSec(SectorsCnt, 2, MF_CHKKEYS_DEFTIMEOUT, true, NESTED_KEY_COUNT, keyBlock, e_sector); + mfCheckKeysSec(SectorsCnt, 2, btimeout14a, true, NESTED_KEY_COUNT, keyBlock, e_sector); // get known key from array bool keyFound = false; @@ -744,7 +773,7 @@ int CmdHF14AMfNested(const char *Cmd) e_sector[sectorNo].Key[trgKeyType] = key64; // try to check this key as a key to the other sectors - mfCheckKeysSec(SectorsCnt, 2, MF_CHKKEYS_DEFTIMEOUT, true, 1, keyBlock, e_sector); + mfCheckKeysSec(SectorsCnt, 2, btimeout14a, true, 1, keyBlock, e_sector); } } } From 36b1cdd1b4064e1a506b5bab0d00e61b91cb10b2 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Thu, 19 Oct 2017 13:23:09 +0300 Subject: [PATCH 017/310] fix fpga_comress sending no-error messages to stderr (#430) --- client/fpga_compress.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/client/fpga_compress.c b/client/fpga_compress.c index 2ef883db..a672ab58 100644 --- a/client/fpga_compress.c +++ b/client/fpga_compress.c @@ -43,12 +43,12 @@ static void usage(void) { - fprintf(stderr, "Usage: fpga_compress ... \n"); - fprintf(stderr, " Combine n FPGA bitstream files and compress them into one.\n\n"); - fprintf(stderr, " fpga_compress -d "); - fprintf(stderr, " Decompress . Write result to "); - fprintf(stderr, " fpga_compress -t "); - fprintf(stderr, " Compress hardnested table . Write result to "); + fprintf(stdout, "Usage: fpga_compress ... \n"); + fprintf(stdout, " Combine n FPGA bitstream files and compress them into one.\n\n"); + fprintf(stdout, " fpga_compress -d "); + fprintf(stdout, " Decompress . Write result to "); + fprintf(stdout, " fpga_compress -t "); + fprintf(stdout, " Compress hardnested table . Write result to "); } @@ -150,7 +150,7 @@ int zlib_compress(FILE *infile[], uint8_t num_infiles, FILE *outfile, bool hardn ret = deflate(&compressed_fpga_stream, Z_FINISH); } - fprintf(stderr, "compressed %u input bytes to %lu output bytes\n", i, compressed_fpga_stream.total_out); + fprintf(stdout, "compressed %u input bytes to %lu output bytes\n", i, compressed_fpga_stream.total_out); if (ret != Z_STREAM_END) { fprintf(stderr, "Error in deflate(): %i %s\n", ret, compressed_fpga_stream.msg); From aa757f71d997130800466f7fd958f1beea5ce081 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Fri, 20 Oct 2017 07:49:53 +0300 Subject: [PATCH 018/310] proxmark3 refactoring command line parameters (#417) * add -c (execute command from command line) * fix: sometimes proxmark executes command twice... * fix: start proxmark from QT was in a strange way (if we issue command very fast - it hangs) * added -l (execute lua script) * rework help * small memory management bugfix * small fix in executing command files * enable piping from STDIN --- CHANGELOG.md | 5 + client/proxgui.cpp | 22 ++--- client/proxgui.h | 2 +- client/proxguiqt.cpp | 18 +++- client/proxguiqt.h | 28 +++--- client/proxmark3.c | 231 +++++++++++++++++++++++++++++++------------ client/proxmark3.h | 2 +- client/util.c | 21 ++++ client/util.h | 3 + 9 files changed, 241 insertions(+), 91 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7ec6507..044538c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,10 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added `hf mf nested` mode: autosearch keys for attack (from well known keys) (Merlok) - `hf mf nested` Check keys after they have found (Merlok) - `hf mf chk` Move main cycle to arm (Merlok) +- Changed proxmark command line parameter `flush` to `-f` or `-flush` (Merlok) ### Fixed +- Changed start sequence in Qt mode (fix: short commands hangs main Qt thread) (Merlok) ### Added - Added PAC/Stanley detection to lf search (marshmellow) @@ -27,6 +29,9 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added to `hf mf nested` after attack it checks all found keys on non-open sectors (Merlok) - `hf mf chk` Added setings to set iso14443a operations timeout. default timeout set to 500us (Merlok) - Added to `hf mf nested` parameters `s` and `ss` for checking slow cards (Merlok) +- Added to proxmark command line parameters `w` - wait 20s for serial port (Merlok) +- Added to proxmark command line parameters `c` and `l` - execute command and lua script from command line (Merlok) +- Added to proxmark ability to execute commands from stdin (pipe) (Merlok) ## [3.0.1][2017-06-08] diff --git a/client/proxgui.cpp b/client/proxgui.cpp index e7fdae6c..e899174c 100644 --- a/client/proxgui.cpp +++ b/client/proxgui.cpp @@ -15,7 +15,7 @@ static ProxGuiQT *gui = NULL; static WorkerThread *main_loop_thread = NULL; -WorkerThread::WorkerThread(char *script_cmds_file, bool usb_present) : script_cmds_file(script_cmds_file), usb_present(usb_present) +WorkerThread::WorkerThread(char *script_cmds_file, char *script_cmd, bool usb_present) : script_cmds_file(script_cmds_file), script_cmd(script_cmd), usb_present(usb_present) { } @@ -24,7 +24,7 @@ WorkerThread::~WorkerThread() } void WorkerThread::run() { - main_loop(script_cmds_file, usb_present); + main_loop(script_cmds_file, script_cmd, usb_present); } extern "C" void ShowGraphWindow(void) @@ -56,11 +56,10 @@ extern "C" void MainGraphics(void) if (!gui) return; - main_loop_thread->start(); gui->MainLoop(); } -extern "C" void InitGraphics(int argc, char **argv, char *script_cmds_file, bool usb_present) +extern "C" void InitGraphics(int argc, char **argv, char *script_cmds_file, char *script_cmd, bool usb_present) { #ifdef Q_WS_X11 bool useGUI = getenv("DISPLAY") != 0; @@ -70,18 +69,15 @@ extern "C" void InitGraphics(int argc, char **argv, char *script_cmds_file, bool if (!useGUI) return; - gui = new ProxGuiQT(argc, argv); - main_loop_thread = new WorkerThread(script_cmds_file, usb_present); - QObject::connect(main_loop_thread, SIGNAL(finished()), main_loop_thread, SLOT(deleteLater())); - QObject::connect(main_loop_thread, SIGNAL(finished()), gui, SLOT(_Exit())); + main_loop_thread = new WorkerThread(script_cmds_file, script_cmd, usb_present); + gui = new ProxGuiQT(argc, argv, main_loop_thread); } - extern "C" void ExitGraphics(void) { - if (!gui) - return; + if (!gui) + return; - gui->Exit(); - gui = NULL; + gui->Exit(); + gui = NULL; } diff --git a/client/proxgui.h b/client/proxgui.h index 6e261cb9..77bcbf01 100644 --- a/client/proxgui.h +++ b/client/proxgui.h @@ -19,7 +19,7 @@ void ShowGraphWindow(void); void HideGraphWindow(void); void RepaintGraphWindow(void); void MainGraphics(void); -void InitGraphics(int argc, char **argv, char *script_cmds_file, bool usb_present); +void InitGraphics(int argc, char **argv, char *script_cmds_file, char *script_cmd, bool usb_present); void ExitGraphics(void); #define MAX_GRAPH_TRACE_LEN (40000*8) diff --git a/client/proxguiqt.cpp b/client/proxguiqt.cpp index 506b8138..dc8279b5 100644 --- a/client/proxguiqt.cpp +++ b/client/proxguiqt.cpp @@ -85,6 +85,17 @@ void ProxGuiQT::_Exit(void) { delete this; } +void ProxGuiQT::_StartProxmarkThread(void) { + if (!proxmarkThread) + return; + + // if thread finished delete self and delete application + QObject::connect(proxmarkThread, SIGNAL(finished()), proxmarkThread, SLOT(deleteLater())); + QObject::connect(proxmarkThread, SIGNAL(finished()), this, SLOT(_Exit())); + // start proxmark thread + proxmarkThread->start(); +} + void ProxGuiQT::MainLoop() { plotapp = new QApplication(argc, argv); @@ -94,11 +105,14 @@ void ProxGuiQT::MainLoop() connect(this, SIGNAL(HideGraphWindowSignal()), this, SLOT(_HideGraphWindow())); connect(this, SIGNAL(ExitSignal()), this, SLOT(_Exit())); + //start proxmark thread after starting event loop + QTimer::singleShot(200, this, SLOT(_StartProxmarkThread())); + plotapp->exec(); } -ProxGuiQT::ProxGuiQT(int argc, char **argv) : plotapp(NULL), plotwidget(NULL), - argc(argc), argv(argv) +ProxGuiQT::ProxGuiQT(int argc, char **argv, WorkerThread *wthread) : plotapp(NULL), plotwidget(NULL), + argc(argc), argv(argv), proxmarkThread(wthread) { } diff --git a/client/proxguiqt.h b/client/proxguiqt.h index 8a3b8cfc..45a65b04 100644 --- a/client/proxguiqt.h +++ b/client/proxguiqt.h @@ -88,6 +88,18 @@ class ProxWidget : public QWidget void vchange_dthr_down(int v); }; +class WorkerThread : public QThread { + Q_OBJECT; + public: + WorkerThread(char*, char*, bool); + ~WorkerThread(); + void run(); + private: + char *script_cmds_file = NULL; + char *script_cmd = NULL; + bool usb_present; +}; + class ProxGuiQT : public QObject { Q_OBJECT; @@ -98,9 +110,10 @@ class ProxGuiQT : public QObject int argc; char **argv; void (*main_func)(void); + WorkerThread *proxmarkThread; public: - ProxGuiQT(int argc, char **argv); + ProxGuiQT(int argc, char **argv, WorkerThread *wthread); ~ProxGuiQT(void); void ShowGraphWindow(void); void RepaintGraphWindow(void); @@ -112,6 +125,7 @@ class ProxGuiQT : public QObject void _RepaintGraphWindow(void); void _HideGraphWindow(void); void _Exit(void); + void _StartProxmarkThread(void); signals: void ShowGraphWindowSignal(void); void RepaintGraphWindowSignal(void); @@ -119,16 +133,4 @@ class ProxGuiQT : public QObject void ExitSignal(void); }; - -class WorkerThread : public QThread { - Q_OBJECT; -public: - WorkerThread(char*, bool); - ~WorkerThread(); - void run(); -private: - char *script_cmds_file = NULL; - bool usb_present; -}; - #endif // PROXGUI_QT diff --git a/client/proxmark3.c b/client/proxmark3.c index 956eb6a8..d0e68b61 100644 --- a/client/proxmark3.c +++ b/client/proxmark3.c @@ -18,14 +18,21 @@ #include #include "proxmark3.h" +#include "util_posix.h" #include "proxgui.h" #include "cmdmain.h" #include "uart.h" #include "ui.h" +#include "util.h" #include "cmdparser.h" #include "cmdhw.h" #include "whereami.h" +#ifdef _WIN32 +#define SERIAL_PORT_H "com3" +#else +#define SERIAL_PORT_H "/dev/ttyACM0" +#endif // a global mutex to prevent interlaced printing from different threads pthread_mutex_t print_lock; @@ -89,11 +96,13 @@ static void *uart_receiver(void *targ) { } -void main_loop(char *script_cmds_file, bool usb_present) { +void main_loop(char *script_cmds_file, char *script_cmd, bool usb_present) { struct receiver_arg rarg; char *cmd = NULL; pthread_t reader_thread; - + bool execCommand = (script_cmd != NULL); + bool stdinOnPipe = !isatty(STDIN_FILENO); + if (usb_present) { rarg.run = 1; pthread_create(&reader_thread, NULL, &uart_receiver, &rarg); @@ -101,46 +110,68 @@ void main_loop(char *script_cmds_file, bool usb_present) { CmdVersion(NULL); } + // file with script FILE *script_file = NULL; - char script_cmd_buf[256]; // iceman, needs lua script the same file_path_buffer as the rest + char script_cmd_buf[256] = {0}; // iceman, needs lua script the same file_path_buffer as the rest if (script_cmds_file) { script_file = fopen(script_cmds_file, "r"); if (script_file) { - printf("using 'scripting' commands file %s\n", script_cmds_file); + printf("executing commands from file: %s\n", script_cmds_file); } } - + read_history(".history"); while(1) { - // If there is a script file if (script_file) { + memset(script_cmd_buf, 0, sizeof(script_cmd_buf)); if (!fgets(script_cmd_buf, sizeof(script_cmd_buf), script_file)) { fclose(script_file); script_file = NULL; } else { - char *nl; - nl = strrchr(script_cmd_buf, '\r'); - if (nl) *nl = '\0'; - - nl = strrchr(script_cmd_buf, '\n'); - if (nl) *nl = '\0'; + strcleanrn(script_cmd_buf, sizeof(script_cmd_buf)); - if ((cmd = (char*) malloc(strlen(script_cmd_buf) + 1)) != NULL) { - memset(cmd, 0, strlen(script_cmd_buf)); - strcpy(cmd, script_cmd_buf); - printf("%s\n", cmd); + if ((cmd = strmcopy(script_cmd_buf)) != NULL) { + printf(PROXPROMPT"%s\n", cmd); + } + } + } else { + // If there is a script command + if (execCommand){ + if ((cmd = strmcopy(script_cmd)) != NULL) { + printf(PROXPROMPT"%s\n", cmd); + } + + execCommand = false; + } else { + // exit after exec command + if (script_cmd) + break; + + // if there is a pipe from stdin + if (stdinOnPipe) { + memset(script_cmd_buf, 0, sizeof(script_cmd_buf)); + if (!fgets(script_cmd_buf, sizeof(script_cmd_buf), stdin)) { + printf("\nStdin end. Exit...\n"); + break; + } + strcleanrn(script_cmd_buf, sizeof(script_cmd_buf)); + + if ((cmd = strmcopy(script_cmd_buf)) != NULL) { + printf(PROXPROMPT"%s\n", cmd); + } + + } else { + // read command from command prompt + cmd = readline(PROXPROMPT); } } } - if (!script_file) { - cmd = readline(PROXPROMPT); - } - + // execute command if (cmd) { while(cmd[strlen(cmd) - 1] == ' ') @@ -154,12 +185,13 @@ void main_loop(char *script_cmds_file, bool usb_present) { } } free(cmd); + cmd = NULL; } else { printf("\n"); break; } } - + write_history(".history"); if (usb_present) { @@ -171,7 +203,6 @@ void main_loop(char *script_cmds_file, bool usb_present) { fclose(script_file); script_file = NULL; } - } static void dumpAllHelp(int markdown) @@ -212,36 +243,129 @@ static void set_my_executable_path(void) } } +static void show_help(bool showFullHelp, char *command_line){ + printf("syntax: %s [-h|-help|-m|-f|-flush|-w|-wait|-c|-command|-l|-lua] [cmd_script_file_name] [command][lua_script_name]\n", command_line); + printf("\tLinux example:'%s /dev/ttyACM0'\n", command_line); + printf("\tWindows example:'%s com3'\n\n", command_line); + + if (showFullHelp){ + printf("help: <-h|-help> Dump all interactive command's help at once.\n"); + printf("\t%s -h\n\n", command_line); + printf("markdown: <-m> Dump all interactive help at once in markdown syntax\n"); + printf("\t%s -m\n\n", command_line); + printf("flush: <-f|-flush> Output will be flushed after every print.\n"); + printf("\t%s -f\n\n", command_line); + printf("wait: <-w|-wait> 20sec waiting the serial port to appear in the OS\n"); + printf("\t%s "SERIAL_PORT_H" -w\n\n", command_line); + printf("script: A script file with one proxmark3 command per line.\n\n"); + printf("command: <-c|-command> Execute one proxmark3 command.\n"); + printf("\t%s "SERIAL_PORT_H" -c \"hf mf chk 1* ?\"\n", command_line); + printf("\t%s "SERIAL_PORT_H" -command \"hf mf nested 1 *\"\n\n", command_line); + printf("lua: <-l|-lua> Execute lua script.\n"); + printf("\t%s "SERIAL_PORT_H" -l hf_read\n\n", command_line); + } +} int main(int argc, char* argv[]) { srand(time(0)); + bool usb_present = false; + bool waitCOMPort = false; + bool executeCommand = false; + bool addLuaExec = false; + char *script_cmds_file = NULL; + char *script_cmd = NULL; + if (argc < 2) { - printf("syntax: %s \n\n",argv[0]); - printf("\tLinux example:'%s /dev/ttyACM0'\n\n", argv[0]); - printf("help: %s -h\n\n", argv[0]); - printf("\tDump all interactive help at once\n"); - printf("markdown: %s -m\n\n", argv[0]); - printf("\tDump all interactive help at once in markdown syntax\n"); + show_help(true, argv[0]); return 1; } - if (strcmp(argv[1], "-h") == 0) { - printf("syntax: %s \n\n",argv[0]); - printf("\tLinux example:'%s /dev/ttyACM0'\n\n", argv[0]); - dumpAllHelp(0); - return 0; - } - if (strcmp(argv[1], "-m") == 0) { - dumpAllHelp(1); - return 0; + + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i],"-help") == 0) { + show_help(false, argv[0]); + dumpAllHelp(0); + return 0; + } + + if (strcmp(argv[i], "-m") == 0) { + dumpAllHelp(1); + return 0; + } + + if(strcmp(argv[i],"-f") == 0 || strcmp(argv[i],"-flush") == 0){ + printf("Output will be flushed after every print.\n"); + flushAfterWrite = 1; + } + + if(strcmp(argv[i],"-w") == 0 || strcmp(argv[i],"-wait") == 0){ + waitCOMPort = true; + } + + if(strcmp(argv[i],"-c") == 0 || strcmp(argv[i],"-command") == 0){ + executeCommand = true; + } + + if(strcmp(argv[i],"-l") == 0 || strcmp(argv[i],"-lua") == 0){ + executeCommand = true; + addLuaExec = true; + } } + // If the user passed the filename of the 'script' to execute, get it from last parameter + if (argc > 2 && argv[argc - 1] && argv[argc - 1][0] != '-') { + if (executeCommand){ + script_cmd = argv[argc - 1]; + + while(script_cmd[strlen(script_cmd) - 1] == ' ') + script_cmd[strlen(script_cmd) - 1] = 0x00; + + if (strlen(script_cmd) == 0) { + script_cmd = NULL; + } else { + if (addLuaExec){ + // add "script run " to command + char *ctmp = NULL; + int len = strlen(script_cmd) + 11 + 1; + if ((ctmp = (char*) malloc(len)) != NULL) { + memset(ctmp, 0, len); + strcpy(ctmp, "script run "); + strcpy(&ctmp[11], script_cmd); + script_cmd = ctmp; + } + } + + printf("Execute command from commandline: %s\n", script_cmd); + } + } else { + script_cmds_file = argv[argc - 1]; + } + } + + // check command + if (executeCommand && (!script_cmd || strlen(script_cmd) == 0)){ + printf("ERROR: execute command: command not found.\n"); + return 2; + } + + // set global variables set_my_executable_path(); - bool usb_present = false; - char *script_cmds_file = NULL; - - sp = uart_open(argv[1]); + // open uart + if (!waitCOMPort) { + sp = uart_open(argv[1]); + } else { + printf("Waiting for Proxmark to appear on %s ", argv[1]); + int openCount = 0; + do { + sp = uart_open(argv[1]); + msleep(1000); + printf("."); + } while(++openCount < 20 && (sp == INVALID_SERIAL_PORT || sp == CLAIMED_SERIAL_PORT)); + printf("\n"); + } + + // check result of uart opening if (sp == INVALID_SERIAL_PORT) { printf("ERROR: invalid serial port\n"); usb_present = false; @@ -254,44 +378,29 @@ int main(int argc, char* argv[]) { usb_present = true; offline = 0; } - - // If the user passed the filename of the 'script' to execute, get it - if (argc > 2 && argv[2]) { - if (argv[2][0] == 'f' && //buzzy, if a word 'flush' passed, flush the output after every log entry. - argv[2][1] == 'l' && - argv[2][2] == 'u' && - argv[2][3] == 's' && - argv[2][4] == 'h') - { - printf("Output will be flushed after every print.\n"); - flushAfterWrite = 1; - } - else - script_cmds_file = argv[2]; - } - + // create a mutex to avoid interlacing print commands from our different threads pthread_mutex_init(&print_lock, NULL); #ifdef HAVE_GUI #ifdef _WIN32 - InitGraphics(argc, argv, script_cmds_file, usb_present); + InitGraphics(argc, argv, script_cmds_file, script_cmd, usb_present); MainGraphics(); #else char* display = getenv("DISPLAY"); if (display && strlen(display) > 1) { - InitGraphics(argc, argv, script_cmds_file, usb_present); + InitGraphics(argc, argv, script_cmds_file, script_cmd, usb_present); MainGraphics(); } else { - main_loop(script_cmds_file, usb_present); + main_loop(script_cmds_file, script_cmd, usb_present); } #endif #else - main_loop(script_cmds_file, usb_present); + main_loop(script_cmds_file, script_cmd, usb_present); #endif // Clean up the port diff --git a/client/proxmark3.h b/client/proxmark3.h index 7ff7f676..c6185c43 100644 --- a/client/proxmark3.h +++ b/client/proxmark3.h @@ -23,7 +23,7 @@ extern "C" { void SendCommand(UsbCommand *c); const char *get_my_executable_path(void); const char *get_my_executable_directory(void); -void main_loop(char *script_cmds_file, bool usb_present); +void main_loop(char *script_cmds_file, char *script_cmd, bool usb_present); #ifdef __cplusplus } diff --git a/client/util.c b/client/util.c index 86e8c502..8357f601 100644 --- a/client/util.c +++ b/client/util.c @@ -623,7 +623,28 @@ void clean_ascii(unsigned char *buf, size_t len) { } } +// replace \r \n to \0 +void strcleanrn(char *buf, size_t len) { + strcreplace(buf, len, '\n', '\0'); + strcreplace(buf, len, '\r', '\0'); +} +// replace char in buffer +void strcreplace(char *buf, size_t len, char from, char to) { + for (size_t i = 0; i < len; i++) { + if (buf[i] == from) + buf[i] = to; + } +} + +char *strmcopy(char *buf) { + char * str = NULL; + if ((str = (char*) malloc(strlen(buf) + 1)) != NULL) { + memset(str, 0, strlen(buf) + 1); + strcpy(str, buf); + } + return str; +} // determine number of logical CPU cores (use for multithreaded functions) diff --git a/client/util.h b/client/util.h index 6177dd93..d6ed7d17 100644 --- a/client/util.h +++ b/client/util.h @@ -77,6 +77,9 @@ extern uint32_t le32toh (uint8_t *data); extern void rol(uint8_t *data, const size_t len); extern void clean_ascii(unsigned char *buf, size_t len); +void strcleanrn(char *buf, size_t len); +void strcreplace(char *buf, size_t len, char from, char to); +char *strmcopy(char *buf); extern int num_CPUs(void); // number of logical CPUs From 1df4df6d8a7742fd0aeae347d4683cea495325e1 Mon Sep 17 00:00:00 2001 From: tisf Date: Fri, 20 Oct 2017 21:55:13 +0700 Subject: [PATCH 019/310] Fixing liblua issues On MacOS might have issues with compilation. This should fix it in a case where liblua used is not the built in one. --- client/pm3_binlib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/pm3_binlib.c b/client/pm3_binlib.c index 5c1e3cfc..ed46c8e8 100644 --- a/client/pm3_binlib.c +++ b/client/pm3_binlib.c @@ -107,7 +107,8 @@ static int l_unpack(lua_State *L) /** unpack(f,s, [init]) */ size_t len; const char *s=luaL_checklstring(L,2,&len); /* switched s and f */ const char *f=luaL_checkstring(L,1); - int i_read = luaL_optint(L,3,1)-1; + int i_read = luaL_optinteger(L,3,1)-1; + // int i_read = luaL_optint(L,3,1)-1; unsigned int i; if (i_read >= 0) { i = i_read; @@ -347,4 +348,3 @@ int set_bin_library (lua_State *L) { lua_pop(L, 1); return 1; } - From 6e49717b5e025b6ac7139b00f2f39d3b22b424a1 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Fri, 20 Oct 2017 17:55:13 +0200 Subject: [PATCH 020/310] fix hf mf sim (issue #412) (#419) * move to separate files mifaresim.[ch] * check CRC of commands * don't execute commands without successfull authentication * ensure correct timing of REQA, WUPA, ANTICOL and SELECT responses * trace reader commands immediately, only fix start time after tag response. Decreases time to be ready for next reader command. * remove iso14443-4 remnants * trace raw reader commands instead of decrypted ones * some refactoring * fix hf mf sim * timing: decrease time to get ready for new reader commands --- armsrc/BigBuf.c | 4 + armsrc/BigBuf.h | 1 + armsrc/Makefile | 2 +- armsrc/apps.h | 7 - armsrc/iclass.c | 1 + armsrc/iso14443a.c | 933 +++++--------------------------------------- armsrc/iso14443a.h | 23 ++ armsrc/mifaresim.c | 620 +++++++++++++++++++++++++++++ armsrc/mifaresim.h | 20 + armsrc/mifareutil.c | 12 +- armsrc/mifareutil.h | 18 +- 11 files changed, 777 insertions(+), 864 deletions(-) create mode 100644 armsrc/mifaresim.c create mode 100644 armsrc/mifaresim.h diff --git a/armsrc/BigBuf.c b/armsrc/BigBuf.c index 8870f426..4fe97b46 100644 --- a/armsrc/BigBuf.c +++ b/armsrc/BigBuf.c @@ -136,6 +136,10 @@ void set_tracing(bool enable) { tracing = enable; } +bool get_tracing(void) { + return tracing; +} + /** * Get the number of bytes traced * @return diff --git a/armsrc/BigBuf.h b/armsrc/BigBuf.h index 6a052dca..05538044 100644 --- a/armsrc/BigBuf.h +++ b/armsrc/BigBuf.h @@ -37,6 +37,7 @@ extern void BigBuf_print_status(void); extern uint16_t BigBuf_get_traceLen(void); extern void clear_trace(void); extern void set_tracing(bool enable); +extern bool get_tracing(void); extern bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_start, uint32_t timestamp_end, uint8_t *parity, bool readerToTag); extern int LogTraceHitag(const uint8_t * btBytes, int iBits, int iSamples, uint32_t dwParity, int bReader); extern uint8_t emlSet(uint8_t *data, uint32_t offset, uint32_t length); diff --git a/armsrc/Makefile b/armsrc/Makefile index 861d98c6..dea5d06c 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -17,7 +17,7 @@ APP_CFLAGS = -DWITH_ISO14443a_StandAlone -DWITH_LF -DWITH_ISO15693 -DWITH_ISO144 #SRC_LCD = fonts.c LCD.c SRC_LF = lfops.c hitag2.c hitagS.c lfsampling.c pcf7931.c lfdemod.c protocols.c SRC_ISO15693 = iso15693.c iso15693tools.c -SRC_ISO14443a = epa.c iso14443a.c mifareutil.c mifarecmd.c mifaresniff.c +SRC_ISO14443a = epa.c iso14443a.c mifareutil.c mifarecmd.c mifaresniff.c mifaresim.c SRC_ISO14443b = iso14443b.c SRC_CRAPTO1 = crypto1.c des.c SRC_CRC = iso14443crc.c crc.c crc16.c crc32.c parity.c diff --git a/armsrc/apps.h b/armsrc/apps.h index b0070148..542e6b90 100644 --- a/armsrc/apps.h +++ b/armsrc/apps.h @@ -99,14 +99,9 @@ void ReadSTMemoryIso14443b(uint32_t); void RAMFUNC SnoopIso14443b(void); void SendRawCommand14443B(uint32_t, uint32_t, uint8_t, uint8_t[]); -/// iso14443a.h -void RAMFUNC SnoopIso14443a(uint8_t param); -void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd, byte_t* data); -void ReaderIso14443a(UsbCommand * c); // Also used in iclass.c bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t len, uint32_t timestamp_start, uint32_t timestamp_end, uint8_t *parity, bool readerToTag); void GetParity(const uint8_t *pbtCmd, uint16_t len, uint8_t *parity); -void iso14a_set_trigger(bool enable); void RAMFUNC SniffMifare(uint8_t param); @@ -115,8 +110,6 @@ void EPA_PACE_Collect_Nonce(UsbCommand * c); void EPA_PACE_Replay(UsbCommand *c); // mifarecmd.h -void ReaderMifare(bool first_try); -int32_t dist_nt(uint32_t nt1, uint32_t nt2); void MifareReadBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *data); void MifareUReadBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain); void MifareUC_Auth(uint8_t arg0, uint8_t *datain); diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 32f9594b..f9aedc95 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -42,6 +42,7 @@ #include "string.h" #include "common.h" #include "cmd.h" +#include "iso14443a.h" // Needed for CRC in emulation mode; // same construction as in ISO 14443; // different initial value (CRC_ICLASS) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index e9ad2535..a3367036 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -132,13 +132,13 @@ uint16_t FpgaSendQueueDelay; #define DELAY_FPGA_QUEUE (FpgaSendQueueDelay<<1) // When the PM acts as tag and is sending, it takes -// 4*16 ticks until we can write data to the sending hold register +// 4*16 + 8 ticks until we can write data to the sending hold register // 8*16 ticks until the SHR is transferred to the Sending Shift Register -// 8 ticks until the first transfer starts -// 8 ticks later the FPGA samples the data -// + a varying number of ticks in the FPGA Delay Queue (mod_sig_buf) +// 8 ticks later the FPGA samples the first data +// + 16 ticks until assigned to mod_sig // + 1 tick to assign mod_sig_coil -#define DELAY_ARM2AIR_AS_TAG (4*16 + 8*16 + 8 + 8 + DELAY_FPGA_QUEUE + 1) +// + a varying number of ticks in the FPGA Delay Queue (mod_sig_buf) +#define DELAY_ARM2AIR_AS_TAG (4*16 + 8 + 8*16 + 8 + 16 + 1 + DELAY_FPGA_QUEUE) // When the PM acts as sniffer and is receiving tag data, it takes // 3 ticks A/D conversion @@ -185,13 +185,13 @@ void iso14a_set_trigger(bool enable) { } -void iso14a_set_timeout(uint32_t timeout) { +static void iso14a_set_timeout(uint32_t timeout) { iso14a_timeout = timeout; if(MF_DBGLEVEL >= 3) Dbprintf("ISO14443A Timeout set to %ld (%dms)", iso14a_timeout, iso14a_timeout / 106); } -void iso14a_set_ATS_timeout(uint8_t *ats) { +static void iso14a_set_ATS_timeout(uint8_t *ats) { uint8_t tb1; uint8_t fwi; @@ -246,7 +246,7 @@ void AppendCrc14443a(uint8_t* data, int len) ComputeCrc14443(CRC_14443_A,data,len,data+len,data+len+1); } -void AppendCrc14443b(uint8_t* data, int len) +static void AppendCrc14443b(uint8_t* data, int len) { ComputeCrc14443(CRC_14443_B,data,len,data+len,data+len+1); } @@ -283,7 +283,7 @@ const bool Mod_Miller_LUT[] = { #define IsMillerModulationNibble1(b) (Mod_Miller_LUT[(b & 0x000000F0) >> 4]) #define IsMillerModulationNibble2(b) (Mod_Miller_LUT[(b & 0x0000000F)]) -void UartReset() +static void UartReset() { Uart.state = STATE_UNSYNCD; Uart.bitCount = 0; @@ -295,7 +295,7 @@ void UartReset() Uart.endTime = 0; } -void UartInit(uint8_t *data, uint8_t *parity) +static void UartInit(uint8_t *data, uint8_t *parity) { Uart.output = data; Uart.parity = parity; @@ -455,7 +455,7 @@ const bool Mod_Manchester_LUT[] = { #define IsManchesterModulationNibble2(b) (Mod_Manchester_LUT[(b & 0x000F)]) -void DemodReset() +static void DemodReset() { Demod.state = DEMOD_UNSYNCD; Demod.len = 0; // number of decoded data bytes @@ -469,7 +469,7 @@ void DemodReset() Demod.endTime = 0; } -void DemodInit(uint8_t *data, uint8_t *parity) +static void DemodInit(uint8_t *data, uint8_t *parity) { Demod.output = data; Demod.parity = parity; @@ -793,14 +793,6 @@ static void CodeIso14443aAsTagPar(const uint8_t *cmd, uint16_t len, uint8_t *par ToSendMax++; } -static void CodeIso14443aAsTag(const uint8_t *cmd, uint16_t len) -{ - uint8_t par[MAX_PARITY_SIZE]; - - GetParity(cmd, len, par); - CodeIso14443aAsTagPar(cmd, len, par); -} - static void Code4bitAnswerAsTag(uint8_t cmd) { @@ -840,6 +832,38 @@ static void Code4bitAnswerAsTag(uint8_t cmd) ToSendMax++; } + +static uint8_t *LastReaderTraceTime = NULL; + +static void EmLogTraceReader(void) { + // remember last reader trace start to fix timing info later + LastReaderTraceTime = BigBuf_get_addr() + BigBuf_get_traceLen(); + LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); +} + + +static void FixLastReaderTraceTime(uint32_t tag_StartTime) { + uint32_t reader_EndTime = Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG; + uint32_t reader_StartTime = Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG; + uint16_t reader_modlen = reader_EndTime - reader_StartTime; + uint16_t approx_fdt = tag_StartTime - reader_EndTime; + uint16_t exact_fdt = (approx_fdt - 20 + 32)/64 * 64 + 20; + reader_StartTime = tag_StartTime - exact_fdt - reader_modlen; + LastReaderTraceTime[0] = (reader_StartTime >> 0) & 0xff; + LastReaderTraceTime[1] = (reader_StartTime >> 8) & 0xff; + LastReaderTraceTime[2] = (reader_StartTime >> 16) & 0xff; + LastReaderTraceTime[3] = (reader_StartTime >> 24) & 0xff; +} + + +static void EmLogTraceTag(uint8_t *tag_data, uint16_t tag_len, uint8_t *tag_Parity, uint32_t ProxToAirDuration) { + uint32_t tag_StartTime = LastTimeProxToAirStart*16 + DELAY_ARM2AIR_AS_TAG; + uint32_t tag_EndTime = (LastTimeProxToAirStart + ProxToAirDuration)*16 + DELAY_ARM2AIR_AS_TAG; + LogTrace(tag_data, tag_len, tag_StartTime, tag_EndTime, tag_Parity, false); + FixLastReaderTraceTime(tag_StartTime); +} + + //----------------------------------------------------------------------------- // Wait for commands from reader // Stop when button is pressed @@ -868,33 +892,22 @@ static int GetIso14443aCommandFromReader(uint8_t *received, uint8_t *parity, int b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; if(MillerDecoding(b, 0)) { *len = Uart.len; + EmLogTraceReader(); return true; } } } } -static int EmSendCmd14443aRaw(uint8_t *resp, uint16_t respLen, bool correctionNeeded); -int EmSend4bitEx(uint8_t resp, bool correctionNeeded); + +static int EmSend4bitEx(uint8_t resp, bool correctionNeeded); int EmSend4bit(uint8_t resp); -int EmSendCmdExPar(uint8_t *resp, uint16_t respLen, bool correctionNeeded, uint8_t *par); +static int EmSendCmdExPar(uint8_t *resp, uint16_t respLen, bool correctionNeeded, uint8_t *par); int EmSendCmdEx(uint8_t *resp, uint16_t respLen, bool correctionNeeded); -int EmSendCmd(uint8_t *resp, uint16_t respLen); -int EmSendCmdPar(uint8_t *resp, uint16_t respLen, uint8_t *par); -bool EmLogTrace(uint8_t *reader_data, uint16_t reader_len, uint32_t reader_StartTime, uint32_t reader_EndTime, uint8_t *reader_Parity, - uint8_t *tag_data, uint16_t tag_len, uint32_t tag_StartTime, uint32_t tag_EndTime, uint8_t *tag_Parity); +int EmSendPrecompiledCmd(tag_response_info_t *response_info, bool correctionNeeded); -static uint8_t* free_buffer_pointer; -typedef struct { - uint8_t* response; - size_t response_n; - uint8_t* modulation; - size_t modulation_n; - uint32_t ProxToAirDuration; -} tag_response_info_t; - -bool prepare_tag_modulation(tag_response_info_t* response_info, size_t max_buffer_size) { +static bool prepare_tag_modulation(tag_response_info_t* response_info, size_t max_buffer_size) { // Example response, answer to MIFARE Classic read block will be 16 bytes + 2 CRC = 18 bytes // This will need the following byte array for a modulation sequence // 144 data bits (18 * 8) @@ -908,17 +921,18 @@ bool prepare_tag_modulation(tag_response_info_t* response_info, size_t max_buffe // Prepare the tag modulation bits from the message - CodeIso14443aAsTag(response_info->response,response_info->response_n); + GetParity(response_info->response, response_info->response_n, &(response_info->par)); + CodeIso14443aAsTagPar(response_info->response,response_info->response_n, &(response_info->par)); // Make sure we do not exceed the free buffer space if (ToSendMax > max_buffer_size) { Dbprintf("Out of memory, when modulating bits for tag answer:"); - Dbhexdump(response_info->response_n,response_info->response,false); + Dbhexdump(response_info->response_n, response_info->response, false); return false; } // Copy the byte array, used for this modulation to the buffer position - memcpy(response_info->modulation,ToSend,ToSendMax); + memcpy(response_info->modulation, ToSend, ToSendMax); // Store the number of bytes that were used for encoding/modulation and the time needed to transfer them response_info->modulation_n = ToSendMax; @@ -930,21 +944,20 @@ bool prepare_tag_modulation(tag_response_info_t* response_info, size_t max_buffe // "precompile" responses. There are 7 predefined responses with a total of 28 bytes data to transmit. // Coded responses need one byte per bit to transfer (data, parity, start, stop, correction) -// 28 * 8 data bits, 28 * 1 parity bits, 7 start bits, 7 stop bits, 7 correction bits +// 28 * 8 data bits, 28 * 1 parity bits, 7 start bits, 7 stop bits, 7 correction bits for the modulation // -> need 273 bytes buffer #define ALLOCATED_TAG_MODULATION_BUFFER_SIZE 273 -bool prepare_allocated_tag_modulation(tag_response_info_t* response_info) { +bool prepare_allocated_tag_modulation(tag_response_info_t* response_info, uint8_t **buffer, size_t *max_buffer_size) { + // Retrieve and store the current buffer index - response_info->modulation = free_buffer_pointer; - - // Determine the maximum size we can use from our buffer - size_t max_buffer_size = ALLOCATED_TAG_MODULATION_BUFFER_SIZE; + response_info->modulation = *buffer; // Forward the prepare tag modulation function to the inner function - if (prepare_tag_modulation(response_info, max_buffer_size)) { - // Update the free buffer offset - free_buffer_pointer += ToSendMax; + if (prepare_tag_modulation(response_info, *max_buffer_size)) { + // Update the free buffer offset and the remaining buffer size + *buffer += ToSendMax; + *max_buffer_size -= ToSendMax; return true; } else { return false; @@ -1074,8 +1087,8 @@ void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd, byte_t* data) // allocate buffers: uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); uint8_t *receivedCmdPar = BigBuf_malloc(MAX_PARITY_SIZE); - free_buffer_pointer = BigBuf_malloc(ALLOCATED_TAG_MODULATION_BUFFER_SIZE); - + uint8_t *free_buffer_pointer = BigBuf_malloc(ALLOCATED_TAG_MODULATION_BUFFER_SIZE); + size_t free_buffer_size = ALLOCATED_TAG_MODULATION_BUFFER_SIZE; // clear trace clear_trace(); set_tracing(true); @@ -1083,7 +1096,7 @@ void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd, byte_t* data) // Prepare the responses of the anticollision phase // there will be not enough time to do this at the moment the reader sends it REQA for (size_t i=0; imodulation, p_response->modulation_n, receivedCmd[0] == 0x52); - // do the tracing for the previous reader request and this tag answer: - uint8_t par[MAX_PARITY_SIZE]; - GetParity(p_response->response, p_response->response_n, par); - - EmLogTrace(Uart.output, - Uart.len, - Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, - Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, - Uart.parity, - p_response->response, - p_response->response_n, - LastTimeProxToAirStart*16 + DELAY_ARM2AIR_AS_TAG, - (LastTimeProxToAirStart + p_response->ProxToAirDuration)*16 + DELAY_ARM2AIR_AS_TAG, - par); + EmSendPrecompiledCmd(p_response, receivedCmd[0] == 0x52); } if (!tracing) { @@ -1262,7 +1248,7 @@ void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd, byte_t* data) // prepare a delayed transfer. This simply shifts ToSend[] by a number // of bits specified in the delay parameter. -void PrepareDelayedTransfer(uint16_t delay) +static void PrepareDelayedTransfer(uint16_t delay) { uint8_t bitmask = 0; uint8_t bits_to_shift = 0; @@ -1335,7 +1321,7 @@ static void TransmitFor14443a(const uint8_t *cmd, uint16_t len, uint32_t *timing //----------------------------------------------------------------------------- // Prepare reader command (in bits, support short frames) to send to FPGA //----------------------------------------------------------------------------- -void CodeIso14443aBitsAsReaderPar(const uint8_t *cmd, uint16_t bits, const uint8_t *parity) +static void CodeIso14443aBitsAsReaderPar(const uint8_t *cmd, uint16_t bits, const uint8_t *parity) { int i, j; int last; @@ -1413,21 +1399,13 @@ void CodeIso14443aBitsAsReaderPar(const uint8_t *cmd, uint16_t bits, const uint8 ToSendMax++; } -//----------------------------------------------------------------------------- -// Prepare reader command to send to FPGA -//----------------------------------------------------------------------------- -void CodeIso14443aAsReaderPar(const uint8_t *cmd, uint16_t len, const uint8_t *parity) -{ - CodeIso14443aBitsAsReaderPar(cmd, len*8, parity); -} - //----------------------------------------------------------------------------- // Wait for commands from reader // Stop when button is pressed (return 1) or field was gone (return 2) // Or return 0 when command is captured //----------------------------------------------------------------------------- -static int EmGetCmd(uint8_t *received, uint16_t *len, uint8_t *parity) +int EmGetCmd(uint8_t *received, uint16_t *len, uint8_t *parity) { *len = 0; @@ -1485,6 +1463,7 @@ static int EmGetCmd(uint8_t *received, uint16_t *len, uint8_t *parity) b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; if(MillerDecoding(b, 0)) { *len = Uart.len; + EmLogTraceReader(); return 0; } } @@ -1497,7 +1476,6 @@ static int EmSendCmd14443aRaw(uint8_t *resp, uint16_t respLen, bool correctionNe { uint8_t b; uint16_t i = 0; - uint32_t ThisTransferTime; // Modulate Manchester FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_MOD); @@ -1525,10 +1503,7 @@ static int EmSendCmd14443aRaw(uint8_t *resp, uint16_t respLen, bool correctionNe if (AT91C_BASE_SSC->SSC_RHR) break; } - while ((ThisTransferTime = GetCountSspClk()) & 0x00000007); - - // Clear TXRDY: - AT91C_BASE_SSC->SSC_THR = SEC_F; + LastTimeProxToAirStart = (GetCountSspClk() & 0xfffffff8) + (correctionNeeded?8:0); // send cycle for(; i < respLen; ) { @@ -1544,7 +1519,7 @@ static int EmSendCmd14443aRaw(uint8_t *resp, uint16_t respLen, bool correctionNe // Ensure that the FPGA Delay Queue is empty before we switch to TAGSIM_LISTEN again: uint8_t fpga_queued_bits = FpgaSendQueueDelay >> 3; - for (i = 0; i <= fpga_queued_bits/8 + 1; ) { + for (i = 0; i < fpga_queued_bits/8; ) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { AT91C_BASE_SSC->SSC_THR = SEC_F; FpgaSendQueueDelay = (uint8_t)AT91C_BASE_SSC->SSC_RHR; @@ -1552,87 +1527,60 @@ static int EmSendCmd14443aRaw(uint8_t *resp, uint16_t respLen, bool correctionNe } } - LastTimeProxToAirStart = ThisTransferTime + (correctionNeeded?8:0); - return 0; } -int EmSend4bitEx(uint8_t resp, bool correctionNeeded){ + +static int EmSend4bitEx(uint8_t resp, bool correctionNeeded){ Code4bitAnswerAsTag(resp); int res = EmSendCmd14443aRaw(ToSend, ToSendMax, correctionNeeded); // do the tracing for the previous reader request and this tag answer: - uint8_t par[1]; - GetParity(&resp, 1, par); - EmLogTrace(Uart.output, - Uart.len, - Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, - Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, - Uart.parity, - &resp, - 1, - LastTimeProxToAirStart*16 + DELAY_ARM2AIR_AS_TAG, - (LastTimeProxToAirStart + LastProxToAirDuration)*16 + DELAY_ARM2AIR_AS_TAG, - par); + EmLogTraceTag(&resp, 1, NULL, LastProxToAirDuration); return res; } + int EmSend4bit(uint8_t resp){ return EmSend4bitEx(resp, false); } -int EmSendCmdExPar(uint8_t *resp, uint16_t respLen, bool correctionNeeded, uint8_t *par){ + +static int EmSendCmdExPar(uint8_t *resp, uint16_t respLen, bool correctionNeeded, uint8_t *par){ CodeIso14443aAsTagPar(resp, respLen, par); int res = EmSendCmd14443aRaw(ToSend, ToSendMax, correctionNeeded); // do the tracing for the previous reader request and this tag answer: - EmLogTrace(Uart.output, - Uart.len, - Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, - Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, - Uart.parity, - resp, - respLen, - LastTimeProxToAirStart*16 + DELAY_ARM2AIR_AS_TAG, - (LastTimeProxToAirStart + LastProxToAirDuration)*16 + DELAY_ARM2AIR_AS_TAG, - par); + EmLogTraceTag(resp, respLen, par, LastProxToAirDuration); return res; } + int EmSendCmdEx(uint8_t *resp, uint16_t respLen, bool correctionNeeded){ uint8_t par[MAX_PARITY_SIZE]; GetParity(resp, respLen, par); return EmSendCmdExPar(resp, respLen, correctionNeeded, par); } + int EmSendCmd(uint8_t *resp, uint16_t respLen){ uint8_t par[MAX_PARITY_SIZE]; GetParity(resp, respLen, par); return EmSendCmdExPar(resp, respLen, false, par); } + int EmSendCmdPar(uint8_t *resp, uint16_t respLen, uint8_t *par){ return EmSendCmdExPar(resp, respLen, false, par); } -bool EmLogTrace(uint8_t *reader_data, uint16_t reader_len, uint32_t reader_StartTime, uint32_t reader_EndTime, uint8_t *reader_Parity, - uint8_t *tag_data, uint16_t tag_len, uint32_t tag_StartTime, uint32_t tag_EndTime, uint8_t *tag_Parity) -{ - if (tracing) { - // we cannot exactly measure the end and start of a received command from reader. However we know that the delay from - // end of the received command to start of the tag's (simulated by us) answer is n*128+20 or n*128+84 resp. - // with n >= 9. The start of the tags answer can be measured and therefore the end of the received command be calculated: - uint16_t reader_modlen = reader_EndTime - reader_StartTime; - uint16_t approx_fdt = tag_StartTime - reader_EndTime; - uint16_t exact_fdt = (approx_fdt - 20 + 32)/64 * 64 + 20; - reader_EndTime = tag_StartTime - exact_fdt; - reader_StartTime = reader_EndTime - reader_modlen; - if (!LogTrace(reader_data, reader_len, reader_StartTime, reader_EndTime, reader_Parity, true)) { - return false; - } else return(!LogTrace(tag_data, tag_len, tag_StartTime, tag_EndTime, tag_Parity, false)); - } else { - return true; - } + +int EmSendPrecompiledCmd(tag_response_info_t *response_info, bool correctionNeeded) { + int ret = EmSendCmd14443aRaw(response_info->modulation, response_info->modulation_n, correctionNeeded); + // do the tracing for the previous reader request and this tag answer: + EmLogTraceTag(response_info->response, response_info->response_n, &(response_info->par), response_info->ProxToAirDuration); + return ret; } + //----------------------------------------------------------------------------- // Wait a certain time for tag response // If a response is captured return true @@ -1693,7 +1641,7 @@ void ReaderTransmitPar(uint8_t* frame, uint16_t len, uint8_t *par, uint32_t *tim } -void ReaderTransmitBits(uint8_t* frame, uint16_t len, uint32_t *timing) +static void ReaderTransmitBits(uint8_t* frame, uint16_t len, uint32_t *timing) { // Generate parity and redirect uint8_t par[MAX_PARITY_SIZE]; @@ -1710,7 +1658,8 @@ void ReaderTransmit(uint8_t* frame, uint16_t len, uint32_t *timing) ReaderTransmitBitsPar(frame, len*8, par, timing); } -int ReaderReceiveOffset(uint8_t* receivedAnswer, uint16_t offset, uint8_t *parity) + +static int ReaderReceiveOffset(uint8_t* receivedAnswer, uint16_t offset, uint8_t *parity) { if (!GetIso14443aAnswerFromTag(receivedAnswer, parity, offset)) return false; if (tracing) { @@ -1719,6 +1668,7 @@ int ReaderReceiveOffset(uint8_t* receivedAnswer, uint16_t offset, uint8_t *parit return Demod.len; } + int ReaderReceive(uint8_t *receivedAnswer, uint8_t *parity) { if (!GetIso14443aAnswerFromTag(receivedAnswer, parity, 0)) return false; @@ -1890,6 +1840,7 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u return 1; } + void iso14443a_setup(uint8_t fpga_minor_mode) { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Set up the synchronous serial port @@ -1912,9 +1863,10 @@ void iso14443a_setup(uint8_t fpga_minor_mode) { DemodReset(); UartReset(); NextTransferTime = 2*DELAY_ARM2AIR_AS_READER; - iso14a_set_timeout(1050); // 10ms default + iso14a_set_timeout(1060); // 10ms default } + int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data) { uint8_t parity[MAX_PARITY_SIZE]; uint8_t real_cmd[cmd_len+4]; @@ -1943,6 +1895,7 @@ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data) { return len; } + //----------------------------------------------------------------------------- // Read an ISO 14443a tag. Send out commands and store answers. // @@ -2041,7 +1994,7 @@ void ReaderIso14443a(UsbCommand *c) // Determine the distance between two nonces. // Assume that the difference is small, but we don't know which is first. // Therefore try in alternating directions. -int32_t dist_nt(uint32_t nt1, uint32_t nt2) { +static int32_t dist_nt(uint32_t nt1, uint32_t nt2) { uint16_t i; uint32_t nttmp1, nttmp2; @@ -2353,696 +2306,6 @@ void ReaderMifare(bool first_try) set_tracing(false); } -/** - *MIFARE 1K simulate. - * - *@param flags : - * FLAG_INTERACTIVE - In interactive mode, we are expected to finish the operation with an ACK - * FLAG_4B_UID_IN_DATA - means that there is a 4-byte UID in the data-section, we're expected to use that - * FLAG_7B_UID_IN_DATA - means that there is a 7-byte UID in the data-section, we're expected to use that - * FLAG_10B_UID_IN_DATA - use 10-byte UID in the data-section not finished - * FLAG_NR_AR_ATTACK - means we should collect NR_AR responses for bruteforcing later - * FLAG_RANDOM_NONCE - means we should generate some pseudo-random nonce data (only allows moebius attack) - *@param exitAfterNReads, exit simulation after n blocks have been read, 0 is infinite ... - * (unless reader attack mode enabled then it runs util it gets enough nonces to recover all keys attmpted) - */ -void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t *datain) -{ - int cardSTATE = MFEMUL_NOFIELD; - int _UID_LEN = 0; // 4, 7, 10 - int vHf = 0; // in mV - int res; - uint32_t selTimer = 0; - uint32_t authTimer = 0; - uint16_t len = 0; - uint8_t cardWRBL = 0; - uint8_t cardAUTHSC = 0; - uint8_t cardAUTHKEY = 0xff; // no authentication - uint32_t cardRr = 0; - uint32_t cuid = 0; - //uint32_t rn_enc = 0; - uint32_t ans = 0; - uint32_t cardINTREG = 0; - uint8_t cardINTBLOCK = 0; - struct Crypto1State mpcs = {0, 0}; - struct Crypto1State *pcs; - pcs = &mpcs; - uint32_t numReads = 0;//Counts numer of times reader read a block - uint8_t receivedCmd[MAX_MIFARE_FRAME_SIZE]; - uint8_t receivedCmd_par[MAX_MIFARE_PARITY_SIZE]; - uint8_t response[MAX_MIFARE_FRAME_SIZE]; - uint8_t response_par[MAX_MIFARE_PARITY_SIZE]; - - uint8_t rATQA[] = {0x04, 0x00}; // Mifare classic 1k 4BUID - uint8_t rUIDBCC1[] = {0xde, 0xad, 0xbe, 0xaf, 0x62}; - uint8_t rUIDBCC2[] = {0xde, 0xad, 0xbe, 0xaf, 0x62}; // !!! - uint8_t rUIDBCC3[] = {0xde, 0xad, 0xbe, 0xaf, 0x62}; - - uint8_t rSAKfinal[]= {0x08, 0xb6, 0xdd}; // mifare 1k indicated - uint8_t rSAK1[] = {0x04, 0xda, 0x17}; // indicate UID not finished - - uint8_t rAUTH_NT[] = {0x01, 0x02, 0x03, 0x04}; - uint8_t rAUTH_AT[] = {0x00, 0x00, 0x00, 0x00}; - - //Here, we collect UID,sector,keytype,NT,AR,NR,NT2,AR2,NR2 - // This will be used in the reader-only attack. - - //allow collecting up to 7 sets of nonces to allow recovery of up to 7 keys - #define ATTACK_KEY_COUNT 7 // keep same as define in cmdhfmf.c -> readerAttack() (Cannot be more than 7) - nonces_t ar_nr_resp[ATTACK_KEY_COUNT*2]; //*2 for 2 separate attack types (nml, moebius) - memset(ar_nr_resp, 0x00, sizeof(ar_nr_resp)); - - uint8_t ar_nr_collected[ATTACK_KEY_COUNT*2]; //*2 for 2nd attack type (moebius) - memset(ar_nr_collected, 0x00, sizeof(ar_nr_collected)); - uint8_t nonce1_count = 0; - uint8_t nonce2_count = 0; - uint8_t moebius_n_count = 0; - bool gettingMoebius = false; - uint8_t mM = 0; //moebius_modifier for collection storage - - // Authenticate response - nonce - uint32_t nonce; - if (flags & FLAG_RANDOM_NONCE) { - nonce = prand(); - } else { - nonce = bytes_to_num(rAUTH_NT, 4); - } - - //-- Determine the UID - // Can be set from emulator memory, incoming data - // and can be 7 or 4 bytes long - if (flags & FLAG_4B_UID_IN_DATA) - { - // 4B uid comes from data-portion of packet - memcpy(rUIDBCC1,datain,4); - rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3]; - _UID_LEN = 4; - } else if (flags & FLAG_7B_UID_IN_DATA) { - // 7B uid comes from data-portion of packet - memcpy(&rUIDBCC1[1],datain,3); - memcpy(rUIDBCC2, datain+3, 4); - _UID_LEN = 7; - } else if (flags & FLAG_10B_UID_IN_DATA) { - memcpy(&rUIDBCC1[1], datain, 3); - memcpy(&rUIDBCC2[1], datain+3, 3); - memcpy( rUIDBCC3, datain+6, 4); - _UID_LEN = 10; - } else { - // get UID from emul memory - guess at length - emlGetMemBt(receivedCmd, 7, 1); - if (receivedCmd[0] == 0x00) { // ---------- 4BUID - emlGetMemBt(rUIDBCC1, 0, 4); - _UID_LEN = 4; - } else { // ---------- 7BUID - emlGetMemBt(&rUIDBCC1[1], 0, 3); - emlGetMemBt(rUIDBCC2, 3, 4); - _UID_LEN = 7; - } - } - - switch (_UID_LEN) { - case 4: - // save CUID - cuid = bytes_to_num(rUIDBCC1, 4); - // BCC - rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3]; - if (MF_DBGLEVEL >= 2) { - Dbprintf("4B UID: %02x%02x%02x%02x", - rUIDBCC1[0], - rUIDBCC1[1], - rUIDBCC1[2], - rUIDBCC1[3] - ); - } - break; - case 7: - rATQA[0] |= 0x40; - // save CUID - cuid = bytes_to_num(rUIDBCC2, 4); - // CascadeTag, CT - rUIDBCC1[0] = 0x88; - // BCC - rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3]; - rUIDBCC2[4] = rUIDBCC2[0] ^ rUIDBCC2[1] ^ rUIDBCC2[2] ^ rUIDBCC2[3]; - if (MF_DBGLEVEL >= 2) { - Dbprintf("7B UID: %02x %02x %02x %02x %02x %02x %02x", - rUIDBCC1[1], - rUIDBCC1[2], - rUIDBCC1[3], - rUIDBCC2[0], - rUIDBCC2[1], - rUIDBCC2[2], - rUIDBCC2[3] - ); - } - break; - case 10: - rATQA[0] |= 0x80; - //sak_10[0] &= 0xFB; - // save CUID - cuid = bytes_to_num(rUIDBCC3, 4); - // CascadeTag, CT - rUIDBCC1[0] = 0x88; - rUIDBCC2[0] = 0x88; - // BCC - rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3]; - rUIDBCC2[4] = rUIDBCC2[0] ^ rUIDBCC2[1] ^ rUIDBCC2[2] ^ rUIDBCC2[3]; - rUIDBCC3[4] = rUIDBCC3[0] ^ rUIDBCC3[1] ^ rUIDBCC3[2] ^ rUIDBCC3[3]; - - if (MF_DBGLEVEL >= 2) { - Dbprintf("10B UID: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", - rUIDBCC1[1], - rUIDBCC1[2], - rUIDBCC1[3], - rUIDBCC2[1], - rUIDBCC2[2], - rUIDBCC2[3], - rUIDBCC3[0], - rUIDBCC3[1], - rUIDBCC3[2], - rUIDBCC3[3] - ); - } - break; - default: - break; - } - - // We need to listen to the high-frequency, peak-detected path. - iso14443a_setup(FPGA_HF_ISO14443A_TAGSIM_LISTEN); - - // free eventually allocated BigBuf memory but keep Emulator Memory - BigBuf_free_keep_EM(); - - // clear trace - clear_trace(); - set_tracing(true); - - bool finished = false; - bool button_pushed = BUTTON_PRESS(); - while (!button_pushed && !finished && !usb_poll_validate_length()) { - WDT_HIT(); - - // find reader field - if (cardSTATE == MFEMUL_NOFIELD) { - vHf = (MAX_ADC_HF_VOLTAGE * AvgAdc(ADC_CHAN_HF)) >> 10; - if (vHf > MF_MINFIELDV) { - cardSTATE_TO_IDLE(); - LED_A_ON(); - } - } - if (cardSTATE == MFEMUL_NOFIELD) { - button_pushed = BUTTON_PRESS(); - continue; - } - - //Now, get data - res = EmGetCmd(receivedCmd, &len, receivedCmd_par); - if (res == 2) { //Field is off! - cardSTATE = MFEMUL_NOFIELD; - LEDsoff(); - continue; - } else if (res == 1) { - break; //return value 1 means button press - } - - // REQ or WUP request in ANY state and WUP in HALTED state - if (len == 1 && ((receivedCmd[0] == ISO14443A_CMD_REQA && cardSTATE != MFEMUL_HALTED) || receivedCmd[0] == ISO14443A_CMD_WUPA)) { - selTimer = GetTickCount(); - EmSendCmdEx(rATQA, sizeof(rATQA), (receivedCmd[0] == ISO14443A_CMD_WUPA)); - cardSTATE = MFEMUL_SELECT1; - - // init crypto block - LED_B_OFF(); - LED_C_OFF(); - crypto1_destroy(pcs); - cardAUTHKEY = 0xff; - if (flags & FLAG_RANDOM_NONCE) { - nonce = prand(); - } - continue; - } - - switch (cardSTATE) { - case MFEMUL_NOFIELD: - case MFEMUL_HALTED: - case MFEMUL_IDLE:{ - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); - break; - } - case MFEMUL_SELECT1:{ - // select all - 0x93 0x20 - if (len == 2 && (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && receivedCmd[1] == 0x20)) { - if (MF_DBGLEVEL >= 4) Dbprintf("SELECT ALL received"); - EmSendCmd(rUIDBCC1, sizeof(rUIDBCC1)); - break; - } - - // select card - 0x93 0x70 ... - if (len == 9 && - (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && receivedCmd[1] == 0x70 && memcmp(&receivedCmd[2], rUIDBCC1, 4) == 0)) { - if (MF_DBGLEVEL >= 4) - Dbprintf("SELECT %02x%02x%02x%02x received",receivedCmd[2],receivedCmd[3],receivedCmd[4],receivedCmd[5]); - - switch(_UID_LEN) { - case 4: - cardSTATE = MFEMUL_WORK; - LED_B_ON(); - if (MF_DBGLEVEL >= 4) Dbprintf("--> WORK. anticol1 time: %d", GetTickCount() - selTimer); - EmSendCmd(rSAKfinal, sizeof(rSAKfinal)); - break; - case 7: - cardSTATE = MFEMUL_SELECT2; - EmSendCmd(rSAK1, sizeof(rSAK1)); - break; - case 10: - cardSTATE = MFEMUL_SELECT2; - EmSendCmd(rSAK1, sizeof(rSAK1)); - break; - default:break; - } - } else { - cardSTATE_TO_IDLE(); - } - break; - } - case MFEMUL_SELECT3:{ - if (!len) { - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); - break; - } - // select all cl3 - 0x97 0x20 - if (len == 2 && (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_3 && receivedCmd[1] == 0x20)) { - EmSendCmd(rUIDBCC3, sizeof(rUIDBCC3)); - break; - } - // select card cl3 - 0x97 0x70 - if (len == 9 && - (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_3 && - receivedCmd[1] == 0x70 && - memcmp(&receivedCmd[2], rUIDBCC3, 4) == 0) ) { - - EmSendCmd(rSAKfinal, sizeof(rSAKfinal)); - cardSTATE = MFEMUL_WORK; - LED_B_ON(); - if (MF_DBGLEVEL >= 4) Dbprintf("--> WORK. anticol3 time: %d", GetTickCount() - selTimer); - break; - } - cardSTATE_TO_IDLE(); - break; - } - case MFEMUL_AUTH1:{ - if( len != 8) { - cardSTATE_TO_IDLE(); - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); - break; - } - - uint32_t nr = bytes_to_num(receivedCmd, 4); - uint32_t ar = bytes_to_num(&receivedCmd[4], 4); - - // Collect AR/NR per keytype & sector - if(flags & FLAG_NR_AR_ATTACK) { - for (uint8_t i = 0; i < ATTACK_KEY_COUNT; i++) { - if ( ar_nr_collected[i+mM]==0 || ((cardAUTHSC == ar_nr_resp[i+mM].sector) && (cardAUTHKEY == ar_nr_resp[i+mM].keytype) && (ar_nr_collected[i+mM] > 0)) ) { - // if first auth for sector, or matches sector and keytype of previous auth - if (ar_nr_collected[i+mM] < 2) { - // if we haven't already collected 2 nonces for this sector - if (ar_nr_resp[ar_nr_collected[i+mM]].ar != ar) { - // Avoid duplicates... probably not necessary, ar should vary. - if (ar_nr_collected[i+mM]==0) { - // first nonce collect - ar_nr_resp[i+mM].cuid = cuid; - ar_nr_resp[i+mM].sector = cardAUTHSC; - ar_nr_resp[i+mM].keytype = cardAUTHKEY; - ar_nr_resp[i+mM].nonce = nonce; - ar_nr_resp[i+mM].nr = nr; - ar_nr_resp[i+mM].ar = ar; - nonce1_count++; - // add this nonce to first moebius nonce - ar_nr_resp[i+ATTACK_KEY_COUNT].cuid = cuid; - ar_nr_resp[i+ATTACK_KEY_COUNT].sector = cardAUTHSC; - ar_nr_resp[i+ATTACK_KEY_COUNT].keytype = cardAUTHKEY; - ar_nr_resp[i+ATTACK_KEY_COUNT].nonce = nonce; - ar_nr_resp[i+ATTACK_KEY_COUNT].nr = nr; - ar_nr_resp[i+ATTACK_KEY_COUNT].ar = ar; - ar_nr_collected[i+ATTACK_KEY_COUNT]++; - } else { // second nonce collect (std and moebius) - ar_nr_resp[i+mM].nonce2 = nonce; - ar_nr_resp[i+mM].nr2 = nr; - ar_nr_resp[i+mM].ar2 = ar; - if (!gettingMoebius) { - nonce2_count++; - // check if this was the last second nonce we need for std attack - if ( nonce2_count == nonce1_count ) { - // done collecting std test switch to moebius - // first finish incrementing last sample - ar_nr_collected[i+mM]++; - // switch to moebius collection - gettingMoebius = true; - mM = ATTACK_KEY_COUNT; - if (flags & FLAG_RANDOM_NONCE) { - nonce = prand(); - } else { - nonce = nonce*7; - } - break; - } - } else { - moebius_n_count++; - // if we've collected all the nonces we need - finish. - if (nonce1_count == moebius_n_count) finished = true; - } - } - ar_nr_collected[i+mM]++; - } - } - // we found right spot for this nonce stop looking - break; - } - } - } - - // --- crypto - crypto1_word(pcs, nr , 1); - cardRr = ar ^ crypto1_word(pcs, 0, 0); - - // test if auth OK - if (cardRr != prng_successor(nonce, 64)){ - if (MF_DBGLEVEL >= 2) Dbprintf("AUTH FAILED for sector %d with key %c. cardRr=%08x, succ=%08x", - cardAUTHSC, cardAUTHKEY == 0 ? 'A' : 'B', - cardRr, prng_successor(nonce, 64)); - // Shouldn't we respond anything here? - // Right now, we don't nack or anything, which causes the - // reader to do a WUPA after a while. /Martin - // -- which is the correct response. /piwi - cardSTATE_TO_IDLE(); - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); - break; - } - - //auth successful - ans = prng_successor(nonce, 96) ^ crypto1_word(pcs, 0, 0); - - num_to_bytes(ans, 4, rAUTH_AT); - // --- crypto - EmSendCmd(rAUTH_AT, sizeof(rAUTH_AT)); - LED_C_ON(); - cardSTATE = MFEMUL_WORK; - if (MF_DBGLEVEL >= 4) Dbprintf("AUTH COMPLETED for sector %d with key %c. time=%d", - cardAUTHSC, cardAUTHKEY == 0 ? 'A' : 'B', - GetTickCount() - authTimer); - break; - } - case MFEMUL_SELECT2:{ - if (!len) { - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); - break; - } - // select all cl2 - 0x95 0x20 - if (len == 2 && (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2 && receivedCmd[1] == 0x20)) { - EmSendCmd(rUIDBCC2, sizeof(rUIDBCC2)); - break; - } - - // select cl2 card - 0x95 0x70 xxxxxxxxxxxx - if (len == 9 && - (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2 && receivedCmd[1] == 0x70 && memcmp(&receivedCmd[2], rUIDBCC2, 4) == 0)) { - switch(_UID_LEN) { - case 7: - EmSendCmd(rSAKfinal, sizeof(rSAKfinal)); - cardSTATE = MFEMUL_WORK; - LED_B_ON(); - if (MF_DBGLEVEL >= 4) Dbprintf("--> WORK. anticol2 time: %d", GetTickCount() - selTimer); - break; - case 10: - EmSendCmd(rSAK1, sizeof(rSAK1)); - cardSTATE = MFEMUL_SELECT3; - break; - default:break; - } - break; - } - - // i guess there is a command). go into the work state. - if (len != 4) { - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); - break; - } - cardSTATE = MFEMUL_WORK; - //goto lbWORK; - //intentional fall-through to the next case-stmt - } - - case MFEMUL_WORK:{ - if (len == 0) { - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); - break; - } - - bool encrypted_data = (cardAUTHKEY != 0xFF) ; - - if(encrypted_data) { - // decrypt seqence - mf_crypto1_decrypt(pcs, receivedCmd, len); - } - - if (len == 4 && (receivedCmd[0] == 0x60 || receivedCmd[0] == 0x61)) { - - // if authenticating to a block that shouldn't exist - as long as we are not doing the reader attack - if (receivedCmd[1] >= 16 * 4 && !(flags & FLAG_NR_AR_ATTACK)) { - //is this the correct response to an auth on a out of range block? marshmellow - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); - if (MF_DBGLEVEL >= 2) Dbprintf("Reader tried to operate (0x%02x) on out of range block: %d (0x%02x), nacking",receivedCmd[0],receivedCmd[1],receivedCmd[1]); - break; - } - - authTimer = GetTickCount(); - cardAUTHSC = receivedCmd[1] / 4; // received block num - cardAUTHKEY = receivedCmd[0] - 0x60; - crypto1_destroy(pcs);//Added by martin - crypto1_create(pcs, emlGetKey(cardAUTHSC, cardAUTHKEY)); - //uint64_t key=emlGetKey(cardAUTHSC, cardAUTHKEY); - //Dbprintf("key: %04x%08x",(uint32_t)(key>>32)&0xFFFF,(uint32_t)(key&0xFFFFFFFF)); - - if (!encrypted_data) { // first authentication - if (MF_DBGLEVEL >= 4) Dbprintf("Reader authenticating for block %d (0x%02x) with key %d",receivedCmd[1] ,receivedCmd[1],cardAUTHKEY ); - - crypto1_word(pcs, cuid ^ nonce, 0);//Update crypto state - num_to_bytes(nonce, 4, rAUTH_AT); // Send nonce - } else { // nested authentication - if (MF_DBGLEVEL >= 4) Dbprintf("Reader doing nested authentication for block %d (0x%02x) with key %d",receivedCmd[1] ,receivedCmd[1],cardAUTHKEY ); - ans = nonce ^ crypto1_word(pcs, cuid ^ nonce, 0); - num_to_bytes(ans, 4, rAUTH_AT); - } - - EmSendCmd(rAUTH_AT, sizeof(rAUTH_AT)); - //Dbprintf("Sending rAUTH %02x%02x%02x%02x", rAUTH_AT[0],rAUTH_AT[1],rAUTH_AT[2],rAUTH_AT[3]); - cardSTATE = MFEMUL_AUTH1; - break; - } - - // rule 13 of 7.5.3. in ISO 14443-4. chaining shall be continued - // BUT... ACK --> NACK - if (len == 1 && receivedCmd[0] == CARD_ACK) { - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); - break; - } - - // rule 12 of 7.5.3. in ISO 14443-4. R(NAK) --> R(ACK) - if (len == 1 && receivedCmd[0] == CARD_NACK_NA) { - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); - break; - } - - if(len != 4) { - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); - break; - } - - if(receivedCmd[0] == 0x30 // read block - || receivedCmd[0] == 0xA0 // write block - || receivedCmd[0] == 0xC0 // inc - || receivedCmd[0] == 0xC1 // dec - || receivedCmd[0] == 0xC2 // restore - || receivedCmd[0] == 0xB0) { // transfer - if (receivedCmd[1] >= 16 * 4) { - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); - if (MF_DBGLEVEL >= 2) Dbprintf("Reader tried to operate (0x%02x) on out of range block: %d (0x%02x), nacking",receivedCmd[0],receivedCmd[1],receivedCmd[1]); - break; - } - - if (receivedCmd[1] / 4 != cardAUTHSC) { - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); - if (MF_DBGLEVEL >= 2) Dbprintf("Reader tried to operate (0x%02x) on block (0x%02x) not authenticated for (0x%02x), nacking",receivedCmd[0],receivedCmd[1],cardAUTHSC); - break; - } - } - // read block - if (receivedCmd[0] == 0x30) { - if (MF_DBGLEVEL >= 4) { - Dbprintf("Reader reading block %d (0x%02x)",receivedCmd[1],receivedCmd[1]); - } - emlGetMem(response, receivedCmd[1], 1); - AppendCrc14443a(response, 16); - mf_crypto1_encrypt(pcs, response, 18, response_par); - EmSendCmdPar(response, 18, response_par); - numReads++; - if(exitAfterNReads > 0 && numReads == exitAfterNReads) { - Dbprintf("%d reads done, exiting", numReads); - finished = true; - } - break; - } - // write block - if (receivedCmd[0] == 0xA0) { - if (MF_DBGLEVEL >= 4) Dbprintf("RECV 0xA0 write block %d (%02x)",receivedCmd[1],receivedCmd[1]); - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); - cardSTATE = MFEMUL_WRITEBL2; - cardWRBL = receivedCmd[1]; - break; - } - // increment, decrement, restore - if (receivedCmd[0] == 0xC0 || receivedCmd[0] == 0xC1 || receivedCmd[0] == 0xC2) { - if (MF_DBGLEVEL >= 4) Dbprintf("RECV 0x%02x inc(0xC1)/dec(0xC0)/restore(0xC2) block %d (%02x)",receivedCmd[0],receivedCmd[1],receivedCmd[1]); - if (emlCheckValBl(receivedCmd[1])) { - if (MF_DBGLEVEL >= 2) Dbprintf("Reader tried to operate on block, but emlCheckValBl failed, nacking"); - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); - break; - } - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); - if (receivedCmd[0] == 0xC1) - cardSTATE = MFEMUL_INTREG_INC; - if (receivedCmd[0] == 0xC0) - cardSTATE = MFEMUL_INTREG_DEC; - if (receivedCmd[0] == 0xC2) - cardSTATE = MFEMUL_INTREG_REST; - cardWRBL = receivedCmd[1]; - break; - } - // transfer - if (receivedCmd[0] == 0xB0) { - if (MF_DBGLEVEL >= 4) Dbprintf("RECV 0x%02x transfer block %d (%02x)",receivedCmd[0],receivedCmd[1],receivedCmd[1]); - if (emlSetValBl(cardINTREG, cardINTBLOCK, receivedCmd[1])) - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); - else - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); - break; - } - // halt - if (receivedCmd[0] == 0x50 && receivedCmd[1] == 0x00) { - LED_B_OFF(); - LED_C_OFF(); - cardSTATE = MFEMUL_HALTED; - if (MF_DBGLEVEL >= 4) Dbprintf("--> HALTED. Selected time: %d ms", GetTickCount() - selTimer); - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); - break; - } - // RATS - if (receivedCmd[0] == 0xe0) {//RATS - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); - break; - } - // command not allowed - if (MF_DBGLEVEL >= 4) Dbprintf("Received command not allowed, nacking"); - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); - break; - } - case MFEMUL_WRITEBL2:{ - if (len == 18){ - mf_crypto1_decrypt(pcs, receivedCmd, len); - emlSetMem(receivedCmd, cardWRBL, 1); - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); - cardSTATE = MFEMUL_WORK; - } else { - cardSTATE_TO_IDLE(); - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); - } - break; - } - - case MFEMUL_INTREG_INC:{ - mf_crypto1_decrypt(pcs, receivedCmd, len); - memcpy(&ans, receivedCmd, 4); - if (emlGetValBl(&cardINTREG, &cardINTBLOCK, cardWRBL)) { - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); - cardSTATE_TO_IDLE(); - break; - } - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); - cardINTREG = cardINTREG + ans; - cardSTATE = MFEMUL_WORK; - break; - } - case MFEMUL_INTREG_DEC:{ - mf_crypto1_decrypt(pcs, receivedCmd, len); - memcpy(&ans, receivedCmd, 4); - if (emlGetValBl(&cardINTREG, &cardINTBLOCK, cardWRBL)) { - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); - cardSTATE_TO_IDLE(); - break; - } - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); - cardINTREG = cardINTREG - ans; - cardSTATE = MFEMUL_WORK; - break; - } - case MFEMUL_INTREG_REST:{ - mf_crypto1_decrypt(pcs, receivedCmd, len); - memcpy(&ans, receivedCmd, 4); - if (emlGetValBl(&cardINTREG, &cardINTBLOCK, cardWRBL)) { - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); - cardSTATE_TO_IDLE(); - break; - } - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); - cardSTATE = MFEMUL_WORK; - break; - } - } - button_pushed = BUTTON_PRESS(); - } - - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); - - if(flags & FLAG_NR_AR_ATTACK && MF_DBGLEVEL >= 1) { - for ( uint8_t i = 0; i < ATTACK_KEY_COUNT; i++) { - if (ar_nr_collected[i] == 2) { - Dbprintf("Collected two pairs of AR/NR which can be used to extract %s from reader for sector %d:", (i= 1) Dbprintf("Emulator stopped. Tracing: %d trace length: %d ", tracing, BigBuf_get_traceLen()); - - if(flags & FLAG_INTERACTIVE) { // Interactive mode flag, means we need to send ACK - //Send the collected ar_nr in the response - cmd_send(CMD_ACK,CMD_SIMULATE_MIFARE_CARD,button_pushed,0,&ar_nr_resp,sizeof(ar_nr_resp)); - } -} - //----------------------------------------------------------------------------- // MIFARE sniffer. diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index 9977a658..8bd80510 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -15,15 +15,38 @@ #include #include +#include "usb_cmd.h" #include "mifare.h" +typedef struct { + uint8_t* response; + uint8_t* modulation; + uint16_t response_n; + uint16_t modulation_n; + uint32_t ProxToAirDuration; + uint8_t par; // enough for precalculated parity of 8 Byte responses +} tag_response_info_t; + extern void GetParity(const uint8_t *pbtCmd, uint16_t len, uint8_t *par); extern void AppendCrc14443a(uint8_t *data, int len); +extern void RAMFUNC SnoopIso14443a(uint8_t param); +extern void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd, byte_t *data); +extern void ReaderIso14443a(UsbCommand *c); extern void ReaderTransmit(uint8_t *frame, uint16_t len, uint32_t *timing); extern void ReaderTransmitBitsPar(uint8_t *frame, uint16_t bits, uint8_t *par, uint32_t *timing); extern void ReaderTransmitPar(uint8_t *frame, uint16_t len, uint8_t *par, uint32_t *timing); extern int ReaderReceive(uint8_t *receivedAnswer, uint8_t *par); +extern void ReaderMifare(bool first_try); + +extern int EmGetCmd(uint8_t *received, uint16_t *len, uint8_t *parity); +extern int EmSendCmd(uint8_t *resp, uint16_t respLen); +extern int EmSendCmdEx(uint8_t *resp, uint16_t respLen, bool correctionNeeded); +extern int EmSend4bit(uint8_t resp); +extern int EmSendCmdPar(uint8_t *resp, uint16_t respLen, uint8_t *par); +extern int EmSendPrecompiledCmd(tag_response_info_t *response_info, bool correctionNeeded); + +extern bool prepare_allocated_tag_modulation(tag_response_info_t *response_info, uint8_t **buffer, size_t *buffer_size); extern void iso14443a_setup(uint8_t fpga_minor_mode); extern int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data); diff --git a/armsrc/mifaresim.c b/armsrc/mifaresim.c new file mode 100644 index 00000000..91f45efd --- /dev/null +++ b/armsrc/mifaresim.c @@ -0,0 +1,620 @@ +//----------------------------------------------------------------------------- +// Merlok - June 2011, 2012 +// Gerhard de Koning Gans - May 2008 +// Hagen Fritsch - June 2010 +// +// 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 Classic Card Simulation +//----------------------------------------------------------------------------- + +#include "mifaresim.h" +#include "iso14443a.h" +#include "iso14443crc.h" +#include "crapto1/crapto1.h" +#include "BigBuf.h" +#include "string.h" +#include "mifareutil.h" +#include "fpgaloader.h" +#include "proxmark3.h" +#include "usb_cdc.h" +#include "cmd.h" +#include "protocols.h" +#include "apps.h" + +//mifare emulator states +#define MFEMUL_NOFIELD 0 +#define MFEMUL_IDLE 1 +#define MFEMUL_SELECT1 2 +#define MFEMUL_SELECT2 3 +#define MFEMUL_SELECT3 4 +#define MFEMUL_AUTH1 5 +#define MFEMUL_AUTH2 6 +#define MFEMUL_WORK 7 +#define MFEMUL_WRITEBL2 8 +#define MFEMUL_INTREG_INC 9 +#define MFEMUL_INTREG_DEC 10 +#define MFEMUL_INTREG_REST 11 +#define MFEMUL_HALTED 12 + +#define cardSTATE_TO_IDLE() { cardSTATE = MFEMUL_IDLE; LED_B_OFF(); LED_C_OFF(); } + + + +static void MifareSimInit(uint8_t flags, uint8_t *datain, tag_response_info_t **responses, uint32_t *cuid, uint8_t *uid_len) { + + #define TAG_RESPONSE_COUNT 5 // number of precompiled responses + static uint8_t rATQA[] = {0x04, 0x00}; // indicate Mifare classic 1k 4Byte UID + static uint8_t rUIDBCC1[] = {0x00, 0x00, 0x00, 0x00, 0x00}; // UID 1st cascade level + static uint8_t rUIDBCC2[] = {0x00, 0x00, 0x00, 0x00, 0x00}; // UID 2nd cascade level + static uint8_t rSAKfinal[]= {0x08, 0xb6, 0xdd}; // mifare 1k indicated + static uint8_t rSAK1[] = {0x04, 0xda, 0x17}; // indicate UID not finished + + *uid_len = 4; + // UID can be set from emulator memory or incoming data and can be 4 or 7 bytes long + if (flags & FLAG_4B_UID_IN_DATA) { // get UID from datain + memcpy(rUIDBCC1, datain, 4); + } else if (flags & FLAG_7B_UID_IN_DATA) { + rUIDBCC1[0] = 0x88; + memcpy(rUIDBCC1+1, datain, 3); + memcpy(rUIDBCC2, datain+3, 4); + *uid_len = 7; + } else { + uint8_t probable_atqa; + emlGetMemBt(&probable_atqa, 7, 1); // get UID from emul memory - weak guess at length + if (probable_atqa == 0x00) { // ---------- 4BUID + emlGetMemBt(rUIDBCC1, 0, 4); + } else { // ---------- 7BUID + rUIDBCC1[0] = 0x88; + emlGetMemBt(rUIDBCC1+1, 0, 3); + emlGetMemBt(rUIDBCC2, 3, 4); + *uid_len = 7; + } + } + + switch (*uid_len) { + case 4: + *cuid = bytes_to_num(rUIDBCC1, 4); + rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3]; + if (MF_DBGLEVEL >= 2) { + Dbprintf("4B UID: %02x%02x%02x%02x", + rUIDBCC1[0], rUIDBCC1[1], rUIDBCC1[2], rUIDBCC1[3] ); + } + break; + case 7: + rATQA[0] |= 0x40; + *cuid = bytes_to_num(rUIDBCC2, 4); + rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3]; + rUIDBCC2[4] = rUIDBCC2[0] ^ rUIDBCC2[1] ^ rUIDBCC2[2] ^ rUIDBCC2[3]; + if (MF_DBGLEVEL >= 2) { + Dbprintf("7B UID: %02x %02x %02x %02x %02x %02x %02x", + rUIDBCC1[1], rUIDBCC1[2], rUIDBCC1[3], rUIDBCC2[0], rUIDBCC2[1], rUIDBCC2[2], rUIDBCC2[3] ); + } + break; + default: + break; + } + + static tag_response_info_t responses_init[TAG_RESPONSE_COUNT] = { + { .response = rATQA, .response_n = sizeof(rATQA) }, // Answer to request - respond with card type + { .response = rUIDBCC1, .response_n = sizeof(rUIDBCC1) }, // Anticollision cascade1 - respond with first part of uid + { .response = rUIDBCC2, .response_n = sizeof(rUIDBCC2) }, // Anticollision cascade2 - respond with 2nd part of uid + { .response = rSAKfinal, .response_n = sizeof(rSAKfinal) }, // Acknowledge select - last cascade + { .response = rSAK1, .response_n = sizeof(rSAK1) } // Acknowledge select - previous cascades + }; + + // Prepare ("precompile") the responses of the anticollision phase. There will be not enough time to do this at the moment the reader sends its REQA or SELECT + // There are 7 predefined responses with a total of 18 bytes data to transmit. Coded responses need one byte per bit to transfer (data, parity, start, stop, correction) + // 18 * 8 data bits, 18 * 1 parity bits, 5 start bits, 5 stop bits, 5 correction bits -> need 177 bytes buffer + #define ALLOCATED_TAG_MODULATION_BUFFER_SIZE 177 // number of bytes required for precompiled responses + + uint8_t *free_buffer_pointer = BigBuf_malloc(ALLOCATED_TAG_MODULATION_BUFFER_SIZE); + size_t free_buffer_size = ALLOCATED_TAG_MODULATION_BUFFER_SIZE; + for (size_t i = 0; i < TAG_RESPONSE_COUNT; i++) { + prepare_allocated_tag_modulation(&responses_init[i], &free_buffer_pointer, &free_buffer_size); + } + + *responses = responses_init; + + // indices into responses array: + #define ATQA 0 + #define UIDBCC1 1 + #define UIDBCC2 2 + #define SAKfinal 3 + #define SAK1 4 + +} + + +static bool HasValidCRC(uint8_t *receivedCmd, uint16_t receivedCmd_len) { + uint8_t CRC_byte_1, CRC_byte_2; + ComputeCrc14443(CRC_14443_A, receivedCmd, receivedCmd_len-2, &CRC_byte_1, &CRC_byte_2); + return (receivedCmd[receivedCmd_len-2] == CRC_byte_1 && receivedCmd[receivedCmd_len-1] == CRC_byte_2); +} + + +/** + *MIFARE 1K simulate. + * + *@param flags : + * FLAG_INTERACTIVE - In interactive mode, we are expected to finish the operation with an ACK + * FLAG_4B_UID_IN_DATA - means that there is a 4-byte UID in the data-section, we're expected to use that + * FLAG_7B_UID_IN_DATA - means that there is a 7-byte UID in the data-section, we're expected to use that + * FLAG_10B_UID_IN_DATA - use 10-byte UID in the data-section not finished + * FLAG_NR_AR_ATTACK - means we should collect NR_AR responses for bruteforcing later + * FLAG_RANDOM_NONCE - means we should generate some pseudo-random nonce data (only allows moebius attack) + *@param exitAfterNReads, exit simulation after n blocks have been read, 0 is infinite ... + * (unless reader attack mode enabled then it runs util it gets enough nonces to recover all keys attmpted) + */ +void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t *datain) +{ + tag_response_info_t *responses; + uint8_t uid_len = 4; + uint32_t cuid = 0; + uint8_t cardWRBL = 0; + uint8_t cardAUTHSC = 0; + uint8_t cardAUTHKEY = 0xff; // no authentication + uint32_t cardRr = 0; + //uint32_t rn_enc = 0; + uint32_t ans = 0; + uint32_t cardINTREG = 0; + uint8_t cardINTBLOCK = 0; + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + uint32_t numReads = 0;//Counts numer of times reader reads a block + uint8_t receivedCmd[MAX_MIFARE_FRAME_SIZE]; + uint8_t receivedCmd_dec[MAX_MIFARE_FRAME_SIZE]; + uint8_t receivedCmd_par[MAX_MIFARE_PARITY_SIZE]; + uint16_t receivedCmd_len; + uint8_t response[MAX_MIFARE_FRAME_SIZE]; + uint8_t response_par[MAX_MIFARE_PARITY_SIZE]; + + uint8_t rAUTH_NT[] = {0x01, 0x02, 0x03, 0x04}; + uint8_t rAUTH_AT[] = {0x00, 0x00, 0x00, 0x00}; + + //Here, we collect UID,sector,keytype,NT,AR,NR,NT2,AR2,NR2 + // This will be used in the reader-only attack. + + //allow collecting up to 7 sets of nonces to allow recovery of up to 7 keys + #define ATTACK_KEY_COUNT 7 // keep same as define in cmdhfmf.c -> readerAttack() (Cannot be more than 7) + nonces_t ar_nr_resp[ATTACK_KEY_COUNT*2]; //*2 for 2 separate attack types (nml, moebius) 36 * 7 * 2 bytes = 504 bytes + memset(ar_nr_resp, 0x00, sizeof(ar_nr_resp)); + + uint8_t ar_nr_collected[ATTACK_KEY_COUNT*2]; //*2 for 2nd attack type (moebius) + memset(ar_nr_collected, 0x00, sizeof(ar_nr_collected)); + uint8_t nonce1_count = 0; + uint8_t nonce2_count = 0; + uint8_t moebius_n_count = 0; + bool gettingMoebius = false; + uint8_t mM = 0; //moebius_modifier for collection storage + + // Authenticate response - nonce + uint32_t nonce; + if (flags & FLAG_RANDOM_NONCE) { + nonce = prand(); + } else { + nonce = bytes_to_num(rAUTH_NT, 4); + } + + // free eventually allocated BigBuf memory but keep Emulator Memory + BigBuf_free_keep_EM(); + + MifareSimInit(flags, datain, &responses, &cuid, &uid_len); + + // We need to listen to the high-frequency, peak-detected path. + iso14443a_setup(FPGA_HF_ISO14443A_TAGSIM_LISTEN); + + // clear trace + clear_trace(); + set_tracing(true); + ResetSspClk(); + + bool finished = false; + bool button_pushed = BUTTON_PRESS(); + int cardSTATE = MFEMUL_NOFIELD; + + while (!button_pushed && !finished && !usb_poll_validate_length()) { + WDT_HIT(); + + // find reader field + if (cardSTATE == MFEMUL_NOFIELD) { + int vHf = (MAX_ADC_HF_VOLTAGE * AvgAdc(ADC_CHAN_HF)) >> 10; + if (vHf > MF_MINFIELDV) { + LED_A_ON(); + cardSTATE_TO_IDLE(); + } + button_pushed = BUTTON_PRESS(); + continue; + } + + //Now, get data + int res = EmGetCmd(receivedCmd, &receivedCmd_len, receivedCmd_par); + + if (res == 2) { //Field is off! + LEDsoff(); + cardSTATE = MFEMUL_NOFIELD; + continue; + } else if (res == 1) { // button pressed + button_pushed = true; + break; + } + + // WUPA in HALTED state or REQA or WUPA in any other state + if (receivedCmd_len == 1 && ((receivedCmd[0] == ISO14443A_CMD_REQA && cardSTATE != MFEMUL_HALTED) || receivedCmd[0] == ISO14443A_CMD_WUPA)) { + EmSendPrecompiledCmd(&responses[ATQA], (receivedCmd[0] == ISO14443A_CMD_WUPA)); + + // init crypto block + crypto1_destroy(pcs); + cardAUTHKEY = 0xff; + if (flags & FLAG_RANDOM_NONCE) { + nonce = prand(); + } + LED_B_OFF(); + LED_C_OFF(); + cardSTATE = MFEMUL_SELECT1; + continue; + } + + switch (cardSTATE) { + case MFEMUL_NOFIELD: + case MFEMUL_HALTED: + case MFEMUL_IDLE:{ + break; + } + case MFEMUL_SELECT1:{ + // select all - 0x93 0x20 + if (receivedCmd_len == 2 && (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && receivedCmd[1] == 0x20)) { + if (MF_DBGLEVEL >= 4) Dbprintf("SELECT ALL CL1 received"); + EmSendPrecompiledCmd(&responses[UIDBCC1], false); + break; + } + // select card - 0x93 0x70 ... + if (receivedCmd_len == 9 && + (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && receivedCmd[1] == 0x70 && memcmp(&receivedCmd[2], responses[UIDBCC1].response, 4) == 0)) { + if (MF_DBGLEVEL >= 4) Dbprintf("SELECT CL1 %02x%02x%02x%02x received",receivedCmd[2],receivedCmd[3],receivedCmd[4],receivedCmd[5]); + if (uid_len == 4) { + EmSendPrecompiledCmd(&responses[SAKfinal], false); + LED_B_ON(); + cardSTATE = MFEMUL_WORK; + break; + } else if (uid_len == 7) { + EmSendPrecompiledCmd(&responses[SAK1], false); + cardSTATE = MFEMUL_SELECT2; + break; + } + } + cardSTATE_TO_IDLE(); + break; + } + case MFEMUL_SELECT2:{ + // select all cl2 - 0x95 0x20 + if (receivedCmd_len == 2 && (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2 && receivedCmd[1] == 0x20)) { + if (MF_DBGLEVEL >= 4) Dbprintf("SELECT ALL CL2 received"); + EmSendPrecompiledCmd(&responses[UIDBCC2], false); + break; + } + // select cl2 card - 0x95 0x70 xxxxxxxxxxxx + if (receivedCmd_len == 9 && + (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2 && receivedCmd[1] == 0x70 && memcmp(&receivedCmd[2], responses[UIDBCC2].response, 4) == 0)) { + if (uid_len == 7) { + if (MF_DBGLEVEL >= 4) Dbprintf("SELECT CL2 %02x%02x%02x%02x received",receivedCmd[2],receivedCmd[3],receivedCmd[4],receivedCmd[5]); + EmSendPrecompiledCmd(&responses[SAKfinal], false); + LED_B_ON(); + cardSTATE = MFEMUL_WORK; + break; + } + } + cardSTATE_TO_IDLE(); + break; + } + case MFEMUL_WORK:{ + if (receivedCmd_len != 4) { // all commands must have exactly 4 bytes + break; + } + bool encrypted_data = (cardAUTHKEY != 0xFF) ; + if (encrypted_data) { + // decrypt seqence + mf_crypto1_decryptEx(pcs, receivedCmd, receivedCmd_len, receivedCmd_dec); + } else { + memcpy(receivedCmd_dec, receivedCmd, receivedCmd_len); + } + if (!HasValidCRC(receivedCmd_dec, receivedCmd_len)) { // all commands must have a valid CRC + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + break; + } + if (receivedCmd_dec[0] == MIFARE_AUTH_KEYA || receivedCmd_dec[0] == MIFARE_AUTH_KEYB) { + // if authenticating to a block that shouldn't exist - as long as we are not doing the reader attack + if (receivedCmd_dec[1] >= 16 * 4 && !(flags & FLAG_NR_AR_ATTACK)) { + //is this the correct response to an auth on a out of range block? marshmellow + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + if (MF_DBGLEVEL >= 2) Dbprintf("Reader tried to operate (0x%02x) on out of range block: %d (0x%02x), nacking",receivedCmd_dec[0],receivedCmd_dec[1],receivedCmd_dec[1]); + break; + } + cardAUTHSC = receivedCmd_dec[1] / 4; // received block num + cardAUTHKEY = receivedCmd_dec[0] & 0x01; + crypto1_destroy(pcs);//Added by martin + crypto1_create(pcs, emlGetKey(cardAUTHSC, cardAUTHKEY)); + if (!encrypted_data) { // first authentication + if (MF_DBGLEVEL >= 4) Dbprintf("Reader authenticating for block %d (0x%02x) with key %d",receivedCmd_dec[1], receivedCmd_dec[1], cardAUTHKEY); + crypto1_word(pcs, cuid ^ nonce, 0);//Update crypto state + num_to_bytes(nonce, 4, rAUTH_AT); // Send nonce + } else { // nested authentication + if (MF_DBGLEVEL >= 4) Dbprintf("Reader doing nested authentication for block %d (0x%02x) with key %d", receivedCmd_dec[1], receivedCmd_dec[1], cardAUTHKEY); + ans = nonce ^ crypto1_word(pcs, cuid ^ nonce, 0); + num_to_bytes(ans, 4, rAUTH_AT); + } + EmSendCmd(rAUTH_AT, sizeof(rAUTH_AT)); + cardSTATE = MFEMUL_AUTH1; + break; + } + if (!encrypted_data) { // all other commands must be encrypted (authenticated) + break; + } + if(receivedCmd_dec[0] == ISO14443A_CMD_READBLOCK + || receivedCmd_dec[0] == ISO14443A_CMD_WRITEBLOCK + || receivedCmd_dec[0] == MIFARE_CMD_INC + || receivedCmd_dec[0] == MIFARE_CMD_DEC + || receivedCmd_dec[0] == MIFARE_CMD_RESTORE + || receivedCmd_dec[0] == MIFARE_CMD_TRANSFER) { + if (receivedCmd_dec[1] >= 16 * 4) { + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + if (MF_DBGLEVEL >= 2) Dbprintf("Reader tried to operate (0x%02x) on out of range block: %d (0x%02x), nacking",receivedCmd_dec[0],receivedCmd_dec[1],receivedCmd_dec[1]); + break; + } + if (receivedCmd_dec[1] / 4 != cardAUTHSC) { + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + if (MF_DBGLEVEL >= 2) Dbprintf("Reader tried to operate (0x%02x) on block (0x%02x) not authenticated for (0x%02x), nacking",receivedCmd_dec[0],receivedCmd_dec[1],cardAUTHSC); + break; + } + } + if (receivedCmd_dec[0] == ISO14443A_CMD_READBLOCK) { + if (MF_DBGLEVEL >= 4) { + Dbprintf("Reader reading block %d (0x%02x)",receivedCmd_dec[1],receivedCmd_dec[1]); + } + emlGetMem(response, receivedCmd_dec[1], 1); + AppendCrc14443a(response, 16); + mf_crypto1_encrypt(pcs, response, 18, response_par); + EmSendCmdPar(response, 18, response_par); + numReads++; + if(exitAfterNReads > 0 && numReads == exitAfterNReads) { + Dbprintf("%d reads done, exiting", numReads); + finished = true; + } + break; + } + if (receivedCmd_dec[0] == ISO14443A_CMD_WRITEBLOCK) { + if (MF_DBGLEVEL >= 4) Dbprintf("RECV 0xA0 write block %d (%02x)",receivedCmd_dec[1],receivedCmd_dec[1]); + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); + cardWRBL = receivedCmd_dec[1]; + cardSTATE = MFEMUL_WRITEBL2; + break; + } + if (receivedCmd_dec[0] == MIFARE_CMD_INC || receivedCmd_dec[0] == MIFARE_CMD_DEC || receivedCmd_dec[0] == MIFARE_CMD_RESTORE) { + if (MF_DBGLEVEL >= 4) Dbprintf("RECV 0x%02x inc(0xC1)/dec(0xC0)/restore(0xC2) block %d (%02x)",receivedCmd_dec[0],receivedCmd_dec[1],receivedCmd_dec[1]); + if (emlCheckValBl(receivedCmd_dec[1])) { + if (MF_DBGLEVEL >= 2) Dbprintf("Reader tried to operate on block, but emlCheckValBl failed, nacking"); + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + break; + } + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); + cardWRBL = receivedCmd_dec[1]; + if (receivedCmd_dec[0] == MIFARE_CMD_INC) + cardSTATE = MFEMUL_INTREG_INC; + if (receivedCmd_dec[0] == MIFARE_CMD_DEC) + cardSTATE = MFEMUL_INTREG_DEC; + if (receivedCmd_dec[0] == MIFARE_CMD_RESTORE) + cardSTATE = MFEMUL_INTREG_REST; + break; + } + if (receivedCmd_dec[0] == MIFARE_CMD_TRANSFER) { + if (MF_DBGLEVEL >= 4) Dbprintf("RECV 0x%02x transfer block %d (%02x)",receivedCmd_dec[0],receivedCmd_dec[1],receivedCmd_dec[1]); + if (emlSetValBl(cardINTREG, cardINTBLOCK, receivedCmd_dec[1])) + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + else + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); + break; + } + // halt + if (receivedCmd_dec[0] == ISO14443A_CMD_HALT && receivedCmd_dec[1] == 0x00) { + if (MF_DBGLEVEL >= 4) Dbprintf("--> HALTED."); + LED_B_OFF(); + LED_C_OFF(); + cardSTATE = MFEMUL_HALTED; + break; + } + // command not allowed + if (MF_DBGLEVEL >= 4) Dbprintf("Received command not allowed, nacking"); + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + break; + } + case MFEMUL_AUTH1:{ + if (receivedCmd_len != 8) { + cardSTATE_TO_IDLE(); + break; + } + + uint32_t nr = bytes_to_num(receivedCmd, 4); + uint32_t ar = bytes_to_num(&receivedCmd[4], 4); + + // Collect AR/NR per keytype & sector + if(flags & FLAG_NR_AR_ATTACK) { + for (uint8_t i = 0; i < ATTACK_KEY_COUNT; i++) { + if ( ar_nr_collected[i+mM]==0 || ((cardAUTHSC == ar_nr_resp[i+mM].sector) && (cardAUTHKEY == ar_nr_resp[i+mM].keytype) && (ar_nr_collected[i+mM] > 0)) ) { + // if first auth for sector, or matches sector and keytype of previous auth + if (ar_nr_collected[i+mM] < 2) { + // if we haven't already collected 2 nonces for this sector + if (ar_nr_resp[ar_nr_collected[i+mM]].ar != ar) { + // Avoid duplicates... probably not necessary, ar should vary. + if (ar_nr_collected[i+mM]==0) { + // first nonce collect + ar_nr_resp[i+mM].cuid = cuid; + ar_nr_resp[i+mM].sector = cardAUTHSC; + ar_nr_resp[i+mM].keytype = cardAUTHKEY; + ar_nr_resp[i+mM].nonce = nonce; + ar_nr_resp[i+mM].nr = nr; + ar_nr_resp[i+mM].ar = ar; + nonce1_count++; + // add this nonce to first moebius nonce + ar_nr_resp[i+ATTACK_KEY_COUNT].cuid = cuid; + ar_nr_resp[i+ATTACK_KEY_COUNT].sector = cardAUTHSC; + ar_nr_resp[i+ATTACK_KEY_COUNT].keytype = cardAUTHKEY; + ar_nr_resp[i+ATTACK_KEY_COUNT].nonce = nonce; + ar_nr_resp[i+ATTACK_KEY_COUNT].nr = nr; + ar_nr_resp[i+ATTACK_KEY_COUNT].ar = ar; + ar_nr_collected[i+ATTACK_KEY_COUNT]++; + } else { // second nonce collect (std and moebius) + ar_nr_resp[i+mM].nonce2 = nonce; + ar_nr_resp[i+mM].nr2 = nr; + ar_nr_resp[i+mM].ar2 = ar; + if (!gettingMoebius) { + nonce2_count++; + // check if this was the last second nonce we need for std attack + if ( nonce2_count == nonce1_count ) { + // done collecting std test switch to moebius + // first finish incrementing last sample + ar_nr_collected[i+mM]++; + // switch to moebius collection + gettingMoebius = true; + mM = ATTACK_KEY_COUNT; + if (flags & FLAG_RANDOM_NONCE) { + nonce = prand(); + } else { + nonce = nonce*7; + } + break; + } + } else { + moebius_n_count++; + // if we've collected all the nonces we need - finish. + if (nonce1_count == moebius_n_count) finished = true; + } + } + ar_nr_collected[i+mM]++; + } + } + // we found right spot for this nonce stop looking + break; + } + } + } + + // --- crypto + crypto1_word(pcs, nr , 1); + cardRr = ar ^ crypto1_word(pcs, 0, 0); + + // test if auth OK + if (cardRr != prng_successor(nonce, 64)){ + if (MF_DBGLEVEL >= 2) Dbprintf("AUTH FAILED for sector %d with key %c. cardRr=%08x, succ=%08x", + cardAUTHSC, cardAUTHKEY == 0 ? 'A' : 'B', + cardRr, prng_successor(nonce, 64)); + // Shouldn't we respond anything here? + // Right now, we don't nack or anything, which causes the + // reader to do a WUPA after a while. /Martin + // -- which is the correct response. /piwi + cardAUTHKEY = 0xff; // not authenticated + cardSTATE_TO_IDLE(); + break; + } + ans = prng_successor(nonce, 96) ^ crypto1_word(pcs, 0, 0); + num_to_bytes(ans, 4, rAUTH_AT); + EmSendCmd(rAUTH_AT, sizeof(rAUTH_AT)); + if (MF_DBGLEVEL >= 4) Dbprintf("AUTH COMPLETED for sector %d with key %c.", cardAUTHSC, cardAUTHKEY == 0 ? 'A' : 'B'); + LED_C_ON(); + cardSTATE = MFEMUL_WORK; + break; + } + case MFEMUL_WRITEBL2:{ + if (receivedCmd_len == 18) { + mf_crypto1_decryptEx(pcs, receivedCmd, receivedCmd_len, receivedCmd_dec); + if (HasValidCRC(receivedCmd_dec, receivedCmd_len)) { + emlSetMem(receivedCmd_dec, cardWRBL, 1); + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); + cardSTATE = MFEMUL_WORK; + break; + } + } + cardSTATE_TO_IDLE(); + break; + } + case MFEMUL_INTREG_INC:{ + if (receivedCmd_len == 6) { + mf_crypto1_decryptEx(pcs, receivedCmd, receivedCmd_len, (uint8_t*)&ans); + if (emlGetValBl(&cardINTREG, &cardINTBLOCK, cardWRBL)) { + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + cardSTATE_TO_IDLE(); + break; + } + cardINTREG = cardINTREG + ans; + } + cardSTATE = MFEMUL_WORK; + break; + } + case MFEMUL_INTREG_DEC:{ + if (receivedCmd_len == 6) { + mf_crypto1_decryptEx(pcs, receivedCmd, receivedCmd_len, (uint8_t*)&ans); + if (emlGetValBl(&cardINTREG, &cardINTBLOCK, cardWRBL)) { + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + cardSTATE_TO_IDLE(); + break; + } + } + cardINTREG = cardINTREG - ans; + cardSTATE = MFEMUL_WORK; + break; + } + case MFEMUL_INTREG_REST:{ + mf_crypto1_decryptEx(pcs, receivedCmd, receivedCmd_len, (uint8_t*)&ans); + if (emlGetValBl(&cardINTREG, &cardINTBLOCK, cardWRBL)) { + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + cardSTATE_TO_IDLE(); + break; + } + cardSTATE = MFEMUL_WORK; + break; + } + } + button_pushed = BUTTON_PRESS(); + } + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + + if(flags & FLAG_NR_AR_ATTACK && MF_DBGLEVEL >= 1) { + for ( uint8_t i = 0; i < ATTACK_KEY_COUNT; i++) { + if (ar_nr_collected[i] == 2) { + Dbprintf("Collected two pairs of AR/NR which can be used to extract %s from reader for sector %d:", (i= 1) Dbprintf("Emulator stopped. Tracing: %d trace length: %d ", get_tracing(), BigBuf_get_traceLen()); + + if(flags & FLAG_INTERACTIVE) { // Interactive mode flag, means we need to send ACK + //Send the collected ar_nr in the response + cmd_send(CMD_ACK,CMD_SIMULATE_MIFARE_CARD,button_pushed,0,&ar_nr_resp,sizeof(ar_nr_resp)); + } +} diff --git a/armsrc/mifaresim.h b/armsrc/mifaresim.h new file mode 100644 index 00000000..1e17a882 --- /dev/null +++ b/armsrc/mifaresim.h @@ -0,0 +1,20 @@ +//----------------------------------------------------------------------------- +// Merlok - June 2011, 2012 +// Gerhard de Koning Gans - May 2008 +// Hagen Fritsch - June 2010 +// +// 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 Classic Card Simulation +//----------------------------------------------------------------------------- + +#ifndef __MIFARESIM_H +#define __MIFARESIM_H + +#include + +extern void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t *datain); + +#endif diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c index 97f7b3d1..e5ef6c19 100644 --- a/armsrc/mifareutil.c +++ b/armsrc/mifareutil.c @@ -24,23 +24,27 @@ int MF_DBGLEVEL = MF_DBG_ALL; // crypto1 helpers -void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len){ +void mf_crypto1_decryptEx(struct Crypto1State *pcs, uint8_t *data_in, int len, uint8_t *data_out){ uint8_t bt = 0; int i; if (len != 1) { for (i = 0; i < len; i++) - data[i] = crypto1_byte(pcs, 0x00, 0) ^ data[i]; + data_out[i] = crypto1_byte(pcs, 0x00, 0) ^ data_in[i]; } else { bt = 0; for (i = 0; i < 4; i++) - bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data[0], i)) << i; + bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data_in[0], i)) << i; - data[0] = bt; + data_out[0] = bt; } return; } +void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len){ + mf_crypto1_decryptEx(pcs, data, len, data); +} + void mf_crypto1_encrypt(struct Crypto1State *pcs, uint8_t *data, uint16_t len, uint8_t *par) { uint8_t bt = 0; int i; diff --git a/armsrc/mifareutil.h b/armsrc/mifareutil.h index 8ffd5e89..645d0e7d 100644 --- a/armsrc/mifareutil.h +++ b/armsrc/mifareutil.h @@ -41,23 +41,6 @@ extern int MF_DBGLEVEL; -//mifare emulator states -#define MFEMUL_NOFIELD 0 -#define MFEMUL_IDLE 1 -#define MFEMUL_SELECT1 2 -#define MFEMUL_SELECT2 3 -#define MFEMUL_SELECT3 4 -#define MFEMUL_AUTH1 5 -#define MFEMUL_AUTH2 6 -#define MFEMUL_WORK 7 -#define MFEMUL_WRITEBL2 8 -#define MFEMUL_INTREG_INC 9 -#define MFEMUL_INTREG_DEC 10 -#define MFEMUL_INTREG_REST 11 -#define MFEMUL_HALTED 12 - -#define cardSTATE_TO_IDLE() cardSTATE = MFEMUL_IDLE; LED_B_OFF(); LED_C_OFF(); - //functions int mifare_sendcmd(uint8_t cmd, uint8_t *data, uint8_t data_size, uint8_t* answer, uint8_t *answer_parity, uint32_t *timing); int mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t data, uint8_t* answer, uint8_t *answer_parity, uint32_t *timing); @@ -85,6 +68,7 @@ int mifare_desfire_des_auth2(uint32_t uid, uint8_t *key, uint8_t *blockData); // crypto functions void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *receivedCmd, int len); +void mf_crypto1_decryptEx(struct Crypto1State *pcs, uint8_t *data_in, int len, uint8_t *data_out); void mf_crypto1_encrypt(struct Crypto1State *pcs, uint8_t *data, uint16_t len, uint8_t *par); uint8_t mf_crypto1_encrypt4bit(struct Crypto1State *pcs, uint8_t data); From ef9746cb2f2c637d902573f0df6228ef7edc1f32 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Fri, 20 Oct 2017 20:11:02 +0300 Subject: [PATCH 021/310] added CI appveyor.com (#436) --- appveyor.yml | 142 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 appveyor.yml diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..a4a70f7a --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,142 @@ +version: 3.0.1.{build} +image: Visual Studio 2017 +clone_folder: C:\ProxSpace\pm3 +init: +- ps: "$psversiontable\n#Get-ChildItem Env:\n\n$releasename=\"\"\n$env:APPVEYOR_REPO_COMMIT_SHORT = $env:APPVEYOR_REPO_COMMIT.Substring(0, 8)\nif ($env:appveyor_repo_tag -match \"true\"){\n $releasename=$env:APPVEYOR_REPO_TAG_NAME + \"/\"\n}\n$releasename+=$env:APPVEYOR_BUILD_VERSION + \" [\" + $env:APPVEYOR_REPO_COMMIT_SHORT + \"]\" \n\nWrite-Host \"repository: $env:appveyor_repo_name branch:$env:APPVEYOR_REPO_BRANCH release: $releasename\" -ForegroundColor Yellow\nAdd-AppveyorMessage -Message \"[$env:APPVEYOR_REPO_COMMIT_SHORT]$env:appveyor_repo_name($env:APPVEYOR_REPO_BRANCH)\" -Category Information -Details \"repository: $env:appveyor_repo_name branch: $env:APPVEYOR_REPO_BRANCH release: $releasename\"\n\niex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))" +clone_script: +- ps: >- + Write-Host "Removing ProxSpace..." -NoNewLine + + cd \ + + Remove-Item -Recurse -Force -Path c:\ProxSpace\* + + Write-Host "[ OK ]" -ForegroundColor Green + + + Write-Host "Git clone ProxSpace..." -NoNewLine + + git clone -q https://github.com/Gator96100/ProxSpace c:\ProxSpace + + Write-Host "[ OK ]" -ForegroundColor Green + + + Write-Host "Removing pm3 dir..." -NoNewLine + + Remove-Item -Recurse -Force -Path c:\ProxSpace\pm3\* + + Write-Host "[ OK ]" -ForegroundColor Green + + + Write-Host "Cloning repository <$env:appveyor_repo_name> to $env:appveyor_build_folder ..." -NoNewLine + + if(-not $env:appveyor_pull_request_number) { + git clone -q --branch=$env:appveyor_repo_branch https://github.com/$env:appveyor_repo_name.git $env:appveyor_build_folder + cd $env:appveyor_build_folder + git checkout -qf $env:appveyor_repo_commit + } else { + git clone -q https://github.com/$env:appveyor_repo_name.git $env:appveyor_build_folder + cd $env:appveyor_build_folder + git fetch -q origin +refs/pull/$env:appveyor_pull_request_number/merge: + git checkout -qf FETCH_HEAD + } + + Write-Host "[ OK ]" -ForegroundColor Green + + + Write-Host "Fill msys\etc\fstab file..." -NoNewLine + + New-Item c:\ProxSpace\msys\etc\fstab -type file -force -value "#Win32_Path Mount_Point`nc:\ProxSpace\devkitARM /devkitARM`nc:\ProxSpace\Qt\5.6 /qt `nc:\ProxSpace\pm3 /pm3`n" + + Write-Host "[ OK ]" -ForegroundColor Green +install: +- ps: >- + function Exec-External { + param( + [Parameter(Position=0,Mandatory=1)][scriptblock] $command + ) + & $command + if ($LASTEXITCODE -ne 0) { + throw ("Command returned non-zero error-code ${LASTEXITCODE}: $command") + } + } +build_script: +- ps: "$env:Path = \"C:\\ProxSpace\\msys\\bin;$env:Path\"\n\n#make\nbash -lc -i \"pwd;make all\"\n\n#some checks\nif(!(Test-Path C:\\ProxSpace\\pm3\\client\\proxmark3.exe)){\nthrow \"Main file proxmark3.exe not exists.\"\n}\nif(!(Test-Path C:\\ProxSpace\\pm3\\armsrc\\obj\\fullimage.elf)){\nthrow \"ARM file fullimage.elf not exists.\"\n}\nif(!(Test-Path C:\\ProxSpace\\pm3\\client\\hardnested\\tables\\*.bin.z)){\nthrow \"Files in hardnested\\tables not exists.\"\n}\n\n#copy\nWrite-Host \"Copy release files...\" -NoNewLine -ForegroundColor Yellow\nNew-Item -ItemType Directory -Force -Path C:\\ProxSpace\\Release\nCopy-Item C:\\ProxSpace\\pm3\\client\\*.exe C:\\ProxSpace\\Release\nNew-Item -ItemType Directory -Force -Path C:\\ProxSpace\\Release\\arm\nCopy-Item C:\\ProxSpace\\pm3\\armsrc\\obj\\*.elf C:\\ProxSpace\\Release\\arm\nCopy-Item C:\\ProxSpace\\pm3\\bootrom\\obj\\*.elf C:\\ProxSpace\\Release\\arm\nNew-Item -ItemType Directory -Force -Path C:\\ProxSpace\\Release\\scripts\nCopy-Item C:\\ProxSpace\\pm3\\client\\scripts\\*.lua C:\\ProxSpace\\Release\\scripts\nNew-Item -ItemType Directory -Force -Path C:\\ProxSpace\\Release\\hardnested\\tables\nCopy-Item C:\\ProxSpace\\pm3\\client\\hardnested\\*.bin C:\\ProxSpace\\Release\\hardnested\nCopy-Item C:\\ProxSpace\\pm3\\client\\hardnested\\tables\\*.bin.z C:\\ProxSpace\\Release\\hardnested\\tables\nWrite-Host \"[ OK ]\" -ForegroundColor Green\n\n#archive and push\n$releasename=\"\"\nif ($env:appveyor_repo_tag -match \"true\"){\n$releasename=$env:APPVEYOR_REPO_TAG_NAME + \"/\"\n}\n$releasename+=$env:APPVEYOR_BUILD_VERSION + \" [\" + $env:APPVEYOR_REPO_COMMIT.Substring(0, 7) + \"]\" \n\nWrite-Host \"Archive and publish release files ($releasename)...\" -NoNewLine -ForegroundColor Yellow\ncd C:\\ProxSpace\n7z a release.zip C:\\ProxSpace\\Release\nPush-AppveyorArtifact release.zip -DeploymentName \"$releasename\"\nWrite-Host \"[ OK ]\" -ForegroundColor Green\n\nWrite-Host \"Builded...\" -ForegroundColor Yellow" +test_script: +- ps: >- + $env:Path = "C:\ProxSpace\msys\bin;$env:Path" + + cd c:\ProxSpace\pm3 + + + $global:TestsPassed=$true + + $global:TestTime=[System.Environment]::TickCount + + + Function ExecTest($Name, $File, $Cond) { + [bool]$res=$false; + if ($Cond -eq $null){ + } Else { + If (!($Cond -is [bool] -or $Cond -is [byte] -or $Cond -is [int16] -or $Cond -is [int32] -or $Cond -is [int64] -or $Cond -is [float])){ + if ($Cond -is "String" -and $Cond -like "*passed*"){ + $res= $true + } + if ($Cond -is "String" -and $Cond -like "*true*"){ + $res= $true + } + } Else { + $res=$Cond; + } + } + + If ($res) { + Add-AppveyorTest -Name "$Name" -Framework NUnit -Filename "$File" -Outcome Passed -Duration "$([System.Environment]::TickCount-$global:TestTime)" + }Else { + Add-AppveyorTest -Name "$Name" -Framework NUnit -Filename "$File" -Outcome Failed -Duration "$([System.Environment]::TickCount-$global:TestTime)" + $global:TestsPassed=$false + } + $global:TestTime=[System.Environment]::TickCount + } + + + Write-Host "Running tests..." -ForegroundColor Yellow + + + #file test + + ExecTest "proxmark3 exists" "proxmark3.exe" $(Test-Path C:\ProxSpace\Release\proxmark3.exe) + + ExecTest "arm image exists" "\arm\fullimage1.elf" $(Test-Path C:\ProxSpace\Release\arm\fullimage.elf) + + ExecTest "bootrom exists" "bootrom.elf" $(Test-Path C:\ProxSpace\Release\arm\bootrom.elf) + + ExecTest "hardnested tables exists" "hardnested" $(Test-Path C:\ProxSpace\Release\hardnested\tables\*.z) + + ExecTest "release exists" "release.zip" $(Test-Path C:\ProxSpace\release.zip) + + + #proxmark logic tests + + ExecTest "proxmark help" "proxmark3 -h" $(bash -lc 'cd ~/client;proxmark3 -h | grep -q Execute && echo Passed || echo Failed') + + ExecTest "proxmark help hardnested" "proxmark3 -h" $(bash -lc 'cd ~/client;proxmark3 -h | grep -q hardnested && echo Passed || echo Failed') + + + ExecTest "hf mf offline text" "hf mf" $(bash -lc "cd ~/client;proxmark3 comx -c 'hf mf' | grep -q at_enc && echo Passed || echo Failed") + + ExecTest "hf mf hardnested" "hf mf hardnested" $(bash -lc "cd ~/client;proxmark3 comx -c 'hf mf hardnested t 1 000000000000' | grep -q 'found:' && echo Passed || echo Failed") + + + if ($global:TestsPassed) { + Write-Host "Tests [ OK ]" -ForegroundColor Green + } else { + Write-Host "Tests [ ERROR ]" -ForegroundColor Red + throw "Tests error." + } +on_success: +- ps: Write-Host "Build success..." -ForegroundColor Green +on_failure: +- ps: Write-Host "Build error." -ForegroundColor Red +on_finish: +- ps: $blockRdp = $false; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) \ No newline at end of file From bb04ef216debb8e5996dfe7acddf6af4413cb69a Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Fri, 20 Oct 2017 20:20:07 +0200 Subject: [PATCH 022/310] small fix: make iso14a_set_timeout() external --- armsrc/iso14443a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index a3367036..026b177a 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -185,7 +185,7 @@ void iso14a_set_trigger(bool enable) { } -static void iso14a_set_timeout(uint32_t timeout) { +void iso14a_set_timeout(uint32_t timeout) { iso14a_timeout = timeout; if(MF_DBGLEVEL >= 3) Dbprintf("ISO14443A Timeout set to %ld (%dms)", iso14a_timeout, iso14a_timeout / 106); } From d372569b45b7ce862d2ed2db192eead861806251 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Thu, 7 Sep 2017 09:33:32 +0200 Subject: [PATCH 023/310] FPGA changes ISO14443B: * slightly increase reader field strength * increase sensitivity when reading, allowing increased reading distance --- fpga/fpga_hf.bit | Bin 42175 -> 42175 bytes fpga/hi_read_rx_xcorr.v | 31 +++++++++++++++++-------------- fpga/hi_read_tx.v | 16 +++++++++------- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/fpga/fpga_hf.bit b/fpga/fpga_hf.bit index b9f2e6292e42801a73b57aa3e43dff00191ec8b9..bd8cf8e7dcf7ae80756e29f2417a94da7e9dd290 100644 GIT binary patch literal 42175 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�{n79a~&>|uqF{t z&~Dh5uveMXg!{y}x2AFAzkV^SX_rofr4T;q5W0}#F5@{*zpSgZT<{ogWmBoe(GA7| zAHTSM;}29<4mj8;VGq}%0sP`8Gsa|re|h*7?)ApQ+XncB5ticD1Awh-Cc}9#&%#IY zs@{RXhxGYZYVH_ZVwlRm^K7Ps&7ANQty3KH)?%c77*G$C$qquzsYAluJclZD1|)1u;P zAm<#kw13!)Y#flWtbXGnb*RYG#+&kd?|vt8*htD(mB!`L`V9`@4jU%{TU};!IDC>` z_tv5Wzkue$9Nz$Jtzgx2FhBl#9M&B#ekd~GdxSGIY&t#RIxNAUqsQa(FP;AqPBz?8 z9f5JksBpYGQSHajO5%t6)q}frE@^YRF%#av`UU(dj~}A`P(_pAl~3787yA$m;8!{S z0xm05Yn)$m(f+WD#s#jcklc0k_>Umgjr6ZF;%e7#rzlbhd`;)NJ-oiTD*#0Mq`WZ*>I9Phg^Ti z5k;#PX~9ivbpGQ1WTq)K7H}7@{t)rQ2=6h}Z#aoz<1IMFT%TIRuT$bI^RJ!8&jkXB zy|?T%et|fE9&>?zRRG#Dc=9ddVLF;a8s(UA9f+9j6T}ZsW)8|$3Xh3KIqb%E_IyV) zxqcyHvHp<0Zv$a(6GPN5J^hLVWA5WIBYue7pGInit(YW`P;<+08GaS(H}(ijcy9tk zjE)ZI@sz|5HMH5r7$|Uu+yVR&XJOEwL?!V~ zThnO7luj9M&9s*BueIP-&NOEJMc!k%YW8~vmEL1P{BS<9$&E(<6aPY+oyN)PKJ@YN zE8t&U^iRyT))_B~r~0$G*~^V91>@I;i~Q^Ay+WrF((LNjYU6pXVCPHt7l#df*!r6( zn89=8XxDBfg=vdx9>Soria1=e#=v@4{t$FYvF^bWU;=qos-7+(2qj8Pk>+N z>1}G)DRl+d8i#FJPA`p=*Kd549+ldvzkzf)!fRRNzYLC0mGLk1ITC(QoQ6GYJ&cdy z6!!ZR^eX}$4h9{EToCYZI9ZM2N%;J*8v*~?2)hAmj9O5bWj)k58rnXW@Gq3H5#mu? z-msW`znMaut$<&lb9BPp-@N!N^DkP$#Q6eEP&*fnCJX#)+)-vMgs{ecwI6$BQO=7W zV$5ar8|r>1-h*8K1Z7chj(s>rikjWh`i<>CYS;}W_R{i(YPUK&@EZ1N^BDRR@?W*n zz~wfznB{sG@v+&Qubfa1yOB(>e&ZmZ_DL0e#W-)HF6F5z50|fZ7njv<{0jLmjnq)w{cBo&S|XID9aV#*De|YY*|dZ z9+{)dqE0}Ynp_n67V(R1LhhqIfZB&$)E|CXyiNVqgUlHN{F(s#Wya!JFAvN3h1%w! z*!t>{`i)^a9%{=h-sAJHnvC(TdQk#cOY1jmgbgV=HkhHjgZRdfk&9iL|2jplhDPP= zZ-J-0n`ukUM$H@WtBrxI7(ZMC{?!NGw1*DmoIWcX)A=ucQp7Lu^(~EeR8Lki2U)bP z`d;xgbSK7aE69IU&?edVhwR#LB9^xNSX{KX?6;3&y0GMW@p$? z1JM$t_!U8TEd|yOkdp9?(*Q(xAgpKsex2!lRiexfGzY4;QnqMZR&5ebDn0$;`uPFn zWRQY%KwS}UQDR(h93bvi7wb3FrcjfNzdYb`Mw`J`>e>tu^6~3Ch#NOaYm)gFAiFv} zFb3{fR=-h;S#{fbBX+nMoRP(?e>>=Z)0`U!sx<*jHQAJyogR+hAG(F)iRE@`LEFCYDm#{-wApcSYY)b@ib^HbdeE@d+*`;jc59oWD*;; z&8Od}m8scZHjdM?YKa3Pwn(s=qJDn6z)dHZ!jy)F#x+wBxtk=I_y-NAl8$8rp_`K-i_#uKA#(Eq1<&ylDSAPi4VX25%h&3tL zI*xB(A0|uVhli}zF^B_xDnL}T;y;jIzY*lWv`^7Vt?IQk3#9_DPm7PjNb|Te9I6h24waDn0*oXkF`c_^jxlZH=wR!dVdK zeJRa_OZk^dj6;e@YEUNZ@Y1>#+GlAWDd1lprtkN+1W$t2NFT=jFu^8jQ>EK)6xLzgUVVAs2it+K~m1L)?bt z3h?UzoKIQ7xG`szj`}IqnRee4?|tU(4FP<4D&Ciw0`3}hRXa4ThzlZF(dhj5#+yM zSA};R$}MYi?p&hxmqt`Y0ZAkpJ34Kah*p-8@;n6qArRVx_B_oo(u}`VEvF z!dx`-gr^d=VWLiER07D#`Bw^I!!fokJ+l~dwZSjpUs*aY;TyY6eD0>xo~;JCenaO6 z1OBzpdUUvz(Te%kV$^R`Ur#@_HGT#8ua7Ie$4DH3ySve?UM{-TfYf#);9r0V?B|>N zn15j(TIi z!cR*0*Ndt>gKCX)G@-g+2E%+1OxwdC|Fw%Yg&Jj;`8x+T!>P)o+U(r!Vs(5@MD#fA zuu#8|5)*Vcwe*IiXs~v0RaHs;Yn+tpq_Bc6V5=3*J(BGGmXUn4X=skI;nzT7zi z{YnTVh_${4{7b+T-0i_uH^SF@jMo(eU}$c)jbiM>iU zDI}URZaM$*vt8QYtB6|NERLGx{OcHaN*CSY#Rjq2_#oJCJbPH+UlniBOR;u0F^+WS zd#Wp?`IqL)`6B;%gMQNAltb7Mh4j>#0sqn!^||nw0mri~!xg7!(9Vn@emDVJ2aF41 zxyyq3!`lC9ecOz=!^4{9X#8Tn1H4EE{Hy*HqO$F7WIL-&y7QGp7QTcg#aZws?Pumf zkJ}fmF8427?NRU2lJV_3@#{xzeMB*M&!p+FjT*10fnGG_;W?Z( z|41cxQT*}bMSx$Q0Hb&ERV-KkF!}^?CA$OsnlJaO%&DqB6BleQn-6>ZiyOj^&!c|c zLHuy6rNWp%*iggP`_ymP7aO|9W*=Uy!@QHFa+ToM^G@6GjaE}L>)<%Hs4v(4lKhvw z*=`(%e>hhwK2+v}*GO&+@?THTplY15#s~g{@#`dp%A8S;IZ1kcdgi`xANZGR(Euia z=O>UoE9$&;pRPZI_y*Y4bodL-IR)6t7}IPexUs_1FV24f0@z$2m(;5p97rt3ubnYE#MTSzS;9tmpnK2vQDe_kqey-RQ&o3um|WM0v6o>*t>ajJC+q>{L3~U4mtKXZ(}%S$_n=LUl#e-m6TUYQVIY3S7x@~$VJZ=`Bw%@`GnLe zql@1H-FbL0z^~(gza<4T2q5zgf-#U8RLFm&>F!46gBZ^bYOhrQR$ssFEb_0c+=+0o zKU4(saS$w|V~oin|8ku)IjiEH$j#ZJej$kJ>DNeoKTV>#II1IKZ#B=tQVJ1+?m5{-tK;jUbm)$bZp)Yyd0i4QIi>e&Ax6 z0()4(zns)A6NgG^hFU!!ZXX_Y?r^GiiHtYT;}sL2W|_1F{)I~$;IuCkZVFN;%N8-E?V)%6E2coZ zaQ;i;J^Q*{Yq%k%s&|U4hfRd@UvFX0NAgXOOQI%O8NVDDKOB>Z2Mh}EtNDV2h0M-5 z&!+P8uytRB60MYrKUB)U&QVfDl1&Nu?OtpEWh|t;9CahgJ8S4(dPN;(-ok=NoT01W1z;#vN$A60JP&E=IO@|^P&0lurv(8-8=i?S-xh`htE?W{^T)pgj>_~G+ks0bREjrT>-eq&aO`*8m2O~{pa6!UXPEps9VjYSG^W0}Yp)gJDQqkaST18ldh zt(#|}Y(qAHJUU2($(oe#>m?NZ9sNKx@ogL6Ut6xpVDOy(!rm`ya+48sD=Y=dWua!?OFdAP0ke$jL8tIT2{K-nKJG&~T5H245LIhY) zb+!`9m<8CKr6kK$z%OF7ijmbXa`b?Pty?qp!Aisr1N`bi9SB!w`}R=77UNe&#%)pO z#}8}4zo<#}Oo}tLKZpa?cg6NZ4v82e+5*p)(C5EIo7~Rxe5fCgyz zuk4V2kzPGTuWr&|L$0Xr7>}mI$Gtrkw@2X{b8Kf$zTrHmJM`epP#%I#WB(dJ<){ zH~BD;v-&E396}=ONR=acygnmzC7n^%I?*2Nr~_&->JR6fv!BW#A+o`!7Fq|b`n@=6 z)XG-JM?ZPbxaInl0r0c|5TT_=Wp78XIr3 zdh6=twbVl4I}OIKYu)PG#rM2I{^HeCd|DohtaUd6YbGTvdxQ+1j zD(pj(_9+xsdj>!1*+Z_MhZ#&m zieJ(>T~4|B=a38hD~8>h;`b;-@ZY2Bc)8Eee+H?EhIF$zaEA-p8HJ^hFVS-!Hxy>8;BobM>8a$0sfW8-8D}N#MyF%Sb*b$cZa~dHIPT%r11Ot+FS9lM(HC$IG4IVY z#}6@7ob$b7I}L^ssyZnqB=GA9R_85GzkpxXW9p0IDogC`M(!^S5XVIv6vzB@7vhJt zrxjvNGMYgx%}b%qdv%TLxB~#{A1u}%I$&GtABh&r1hhMph8!2lg25u(MLXL}u zld;oW_vHe}iu^0|$Z$(8ysrCi2^1!yxy&~d4yvrXV0pU+zIn!4XKiqFM zj=|P_md7&|hT;!wwpw0}e@VRM>6c62avoNV3|pFZWtlUEHY*sAufU4x{oFHmyb(w} zg}Z&gzeFN~%TD6O`aT%I>#M@)a{8Ojj)zPX!DLsEiz~v-+{%g zOzBP)=jes|y7D;xHJ?BYbK6l5;OK*3XN&lCFKuoBBz;oYsei`(05#Ar!Sy8Z-Xi}x z;B2mISre9(OKCGXZgn5z+Kkw{mFqS={c=!$IOxFDH!Dquj01@*J6CkTwp{7$`H}kF z^t=l--!o?cK^@+jods`tUZIlk$#AQu%Y0kQuX+2a@J~(DD2~@;+31P#_~BdfrQPkL zJMvGRf$X{v(c@nm^SD?>V`aa&ivDqE(R4UFa7uAl3~J@YqpW=czJ95SIBk+)9 zl0FHyN7dy#TgUkIkQooLPr^2n%>@VLv|L9*dYbwC7sR0`8B-SSD{vDmmmV05gVD>K zGFP9#32Tl~-^%xI`1uswkj47>85-lV!y`sgO*rkj2xu1q+U+@wdRhmS{VUq$#?Dy# zaFxL}8-DmsfeXzz|20yqpC6Z;Wx&pi%VDgSRSTq53b9uPqUGZk)$@3kD4iyFmH=A^jTrQO)P(^F@?Qt(Vg`8!RBM2z0FGyjHwc-d!yx+s zehuTdR*V7Gk^kcN>@$d-XXoVJB7QBDTl-_pJqo9=hppI2>j$Dm4oruL2kGXm_A&{5@=3Wun#Eo^8 zLHrQ$dCIUAbtq#ka>L6jqp3kkx#5o$kGPUhae8*TQpBr!MkB+QfF1UUJCz=EP z!3dv!2QQX>!4XNXYZV|uE z(AT*V!xqil%4>7~^@M;M2xKnr-n2rz+3V6C5_2igU0r`pv;0ewa1K1?+}ua%e)dTSX_?E#jBO z`kuDHT4{im;b5%zB|U1ks+;$S0Ka;KI++=UiyjdOR_9^EUx2d|c?+5J0KYnDGv=p( z@4MJe*Vthxen%;S1lx^fR5j&#*Wb2COm$$=0cBokgjMqKP z)dhQIXXuvyQot`+ahzVluOp2zAB}bgw&g9tMI27#btA}sp|(&y=yH9O+-^gyq&Ptb zLM^VZfRw^6Sg7%8M}@LCBP1S3i!np_2`59H|jv8U6@LZUm@@>AHNjhhcI6< z!tqQ$Z3@gaJ8_^jxIYT#mr;E!qj^A^k6%6!a|8SeU7+&{X|Fu)tNe|+o>1T0hR#^H zH;tsvPZ2-FQMDsh{=P+YXkgh1jtSQ)E^>#2zPEOk_~^<{Pp<9sP&-Xgm7*ups?LvH94X#>m;eMN^ARyRr zvJgMy`+K=sqv|W&e@x#{f4axIYxi**^@n*Ba#-G)a{LfypkyLxd>r|&r7F^HjM>PD znW!)+i65Tt#(9UgLc2`94hu_wg{uWFOHUT?**RLWQ-5d8`M5D+nK`%`>={; zW(D}g(ZWtL;91d(>@=$oYeK|+1-!3WCHRFim>R#tQG0=`Ik2i3F{>D?=p5mF7uU}p z>S>i*(=iYzcUiZ^@m`!w9uKE)3gU-MyENkfY^`aP3)AAi;K>&_kQn5@5U!y_@0D}$ z1QdpoWp~p)xdHjF()tY^GoN2J$LR^D2JFG+Ld0k>|MelT66#99@}JGz;|Ng^m@Dl(zv4e0JU=vIjhai3!+#kiU zCQ!vsR@;K(hcFH{athkd^y=pkHni)w93>{Nyic5R_L1m@4_WE?EjoUf?o1&P_z^l9 zTGsou^sRii;e9fio>k0$VT3wtIF&x@boMRO{7V48O6oTnj}BEyU3eoBE%1==}1psB~!CTwXkpCRm2#ZWL2M#Fdz8g`=hwd+)7ryh&A1? zy~>8W3)>Q}%GO!lo*${en5}kqZ5}TA^Rg{fgL-U*8#&rq^0WN!;`kxd%8h12*SJ_O zceakjAvp7i>?|L@Lhy_)APEr3&N@#&p=)I#ZydmTKx(qZ^Isb=p51PBRvc0NZqu7J zd{E^v>bwsiRT4jZ6cr}GxHstA!wbf&`)<@6&EY-;{E84#EpB!4Mh+1;>O(dHiQr#! zRkF_a51D^G%Hsh<;+W?$70DtweldhSmM`YNc+4wZ+y|@6Hq>H1l6+BojH3CEOjqhX z?;_p{DN4cox|`YJ}-H7pkKxKAw8m+ zQr1lOin*Km8*^9n#Qvn`@$@x$U_rpYxNM#iNdko|;N9m2+NL51LzzdF;7KTrAFgdY41GUF-!~CR>=mz|vKsZq zrRTrskxXL-43YB%&UE!%sIT3j8gtKYh~+)~()Z7klf!JV`-Ba7t$0xl$}hM`ZkE&^ zzC~?Z7R6;7qpEeeNI_K#!?w<#KJxNhlu=~_W6+?q~;vgzg#=kh*1-sge^};rO zz*rz*MI{6*#V?rU@Vx^jC>0E~zDP>6t^+G6!>@zZB2&tqyB`J{WYQ`@eKeR=6fM0Y-fKI}76#cl5y86DlvCtNfG2BK{ieH%Lgg4Kl zvK28;=V&eF`2k~t=dXJFt5fJbHsf^2nsn3DR((guw~T;)0l%=62{q`@xHU>R9~U!G zQ4b^16vPktB>Q5rT%gQ$H>RxVtvJyfYT60>3iuc67vhI2p&EmeY|O=M_(;&NEcyia zh02E)tzV0IcLNJt1id?&|6Vl?U_S@^OCMX#Mef=LJ2sunMWz8j|E%_<%IgmiEj$KI zISttAbi)}0^M7Rng=Y%olC$9|{a`}A6gd5X08J*om9 z5k5+CA8u?HN5HmFNMD9ud@FmL%^img;{=fhO(b8k1^x2xKXd_G-u+S9|G_~pi7}Jm ztvlZz<(FZb?+BPdPK3eo6UJPEUl*J%6x$B!_@Rs(McKx`Cfmnw-k}7)9(7xjkzo zuW({~uB|5{O7QD~?6MO-3@_~->)m%-#6qwNM8i$&3YFj&pKHSrp;JtyQ?T>DtkdUw z`67-K&okfOdk$wJYHWn*##3##p+0k=QQqFc%9! zoWc6PQ;uH=S=D-FcaAV#d$c3-77r;#}-i681? zU*k26g9s#2Qbh2SJRYhVPl*QlXQ%OH>$cMKU;2a^*PP%WSO!PcstIGB zzU1$xu%vts=lzsXJ9lIPNhk=D!ZnNBEr7(rx#wY63^YMTmtQP{BWB{?#ZOdc|D$^VcwJ&7%I*AYxpL z&RfH~f1b-@IDR*MohS>(^*^0e=+|;-r+BHhB=Vf`G?VCP45s6G9zKMx`cn7 z&0N1BdR!kP!Zo>*;bn^J=hv3R4}o93vAJPY-B>p)ciY2TyJJdc#4^SBA+xPGGerL5 zFHY~3$$<(7&aKpA?&C3Y{UPS3=jN~sL>KV_jYW_tl;3v#61;=Sy zG0$i~cfVd5KZNIyirU+z=uNZZ$RF%7#tQkb68z#d#kIybR$!uRW9a_IZVm)R`it>H z#;?|lm4nOtwZ7OcHC1=LJgxv_m*UrYZrs;k>9WyC#+OCes!d*czCw?GnKugX2#%%VtX#Ju9*K~pBCr8*v=nEW43BSA$`vi z$AQ#A{)?+M*q;wyDP9`t0uJ7~f*8owUV>jW64yq}qs>S;^t>W=sefT$y#&8-G`

N^drwI3p?g=l3-PrH` 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 We*cOjW%&2Fq5%IME~cgYm;B#7qqOM& diff --git a/fpga/hi_read_rx_xcorr.v b/fpga/hi_read_rx_xcorr.v index afa46c44..433d6736 100644 --- a/fpga/hi_read_rx_xcorr.v +++ b/fpga/hi_read_rx_xcorr.v @@ -34,13 +34,13 @@ always @(negedge ck_1356megb) (* clock_signal = "yes" *) reg adc_clk; // sample frequency, always 16 * fc always @(ck_1356megb, xcorr_is_848, xcorr_quarter_freq, fc_div) - if (xcorr_is_848 & ~xcorr_quarter_freq) // fc = 847.5 kHz + if (xcorr_is_848 & ~xcorr_quarter_freq) // fc = 847.5 kHz, standard ISO14443B adc_clk <= ck_1356megb; - else if (~xcorr_is_848 & ~xcorr_quarter_freq) // fc = 424.25 kHz + else if (~xcorr_is_848 & ~xcorr_quarter_freq) // fc = 423.75 kHz adc_clk <= fc_div[0]; - else if (xcorr_is_848 & xcorr_quarter_freq) // fc = 212.125 kHz + else if (xcorr_is_848 & xcorr_quarter_freq) // fc = 211.875 kHz adc_clk <= fc_div[1]; - else // fc = 106.0625 kHz + else // fc = 105.9375 kHz adc_clk <= fc_div[2]; // When we're a reader, we just need to do the BPSK demod; but when we're an @@ -69,13 +69,16 @@ begin end end -// Let us report a correlation every 4 subcarrier cycles, or 4*16 samples, +// Let us report a correlation every 4 subcarrier cycles, or 4*16=64 samples, // so we need a 6-bit counter. reg [5:0] corr_i_cnt; // And a couple of registers in which to accumulate the correlations. -// we would add/sub at most 32 times adc_d, the signed result can be held in 14 bits. -reg signed [13:0] corr_i_accum; -reg signed [13:0] corr_q_accum; +// We would add at most 32 times the difference between unmodulated and modulated signal. It should +// be safe to assume that a tag will not be able to modulate the carrier signal by more than 25%. +// 32 * 255 * 0,25 = 2040, which can be held in 11 bits. Add 1 bit for sign. +reg signed [11:0] corr_i_accum; +reg signed [11:0] corr_q_accum; +// we will report maximum 8 significant bits reg signed [7:0] corr_i_out; reg signed [7:0] corr_q_out; // clock and frame signal for communication to ARM @@ -99,16 +102,16 @@ begin begin if(snoop) begin - // Send only 7 most significant bits of tag signal (signed), LSB is reader signal: - corr_i_out <= {corr_i_accum[13:7], after_hysteresis_prev_prev}; - corr_q_out <= {corr_q_accum[13:7], after_hysteresis_prev}; + // Send 7 most significant bits of tag signal (signed), plus 1 bit reader signal + corr_i_out <= {corr_i_accum[11:5], after_hysteresis_prev_prev}; + corr_q_out <= {corr_q_accum[11:5], after_hysteresis_prev}; after_hysteresis_prev_prev <= after_hysteresis; end else begin - // 8 most significant bits of tag signal - corr_i_out <= corr_i_accum[13:6]; - corr_q_out <= corr_q_accum[13:6]; + // 8 bits of tag signal + corr_i_out <= corr_i_accum[11:4]; + corr_q_out <= corr_q_accum[11:4]; end corr_i_accum <= adc_d; diff --git a/fpga/hi_read_tx.v b/fpga/hi_read_tx.v index f12e64eb..fc309cde 100644 --- a/fpga/hi_read_tx.v +++ b/fpga/hi_read_tx.v @@ -24,33 +24,36 @@ module hi_read_tx( output dbg; input shallow_modulation; +// low frequency outputs, not relevant +assign pwr_lo = 1'b0; +assign pwr_oe2 = 1'b0; + // The high-frequency stuff. For now, for testing, just bring out the carrier, // and allow the ARM to modulate it over the SSP. reg pwr_hi; reg pwr_oe1; -reg pwr_oe2; reg pwr_oe3; reg pwr_oe4; + always @(ck_1356megb or ssp_dout or shallow_modulation) begin if(shallow_modulation) begin pwr_hi <= ck_1356megb; - pwr_oe1 <= ~ssp_dout; - pwr_oe2 <= ~ssp_dout; - pwr_oe3 <= ~ssp_dout; - pwr_oe4 <= 1'b0; + pwr_oe1 <= 1'b0; + pwr_oe3 <= 1'b0; + pwr_oe4 <= ~ssp_dout; end else begin pwr_hi <= ck_1356megb & ssp_dout; pwr_oe1 <= 1'b0; - pwr_oe2 <= 1'b0; pwr_oe3 <= 1'b0; pwr_oe4 <= 1'b0; end end + // Then just divide the 13.56 MHz clock down to produce appropriate clocks // for the synchronous serial port. @@ -83,7 +86,6 @@ end assign ssp_din = after_hysteresis; -assign pwr_lo = 1'b0; assign dbg = ssp_din; endmodule From da36bed90d815171a54456c693f2fea8c12e66b3 Mon Sep 17 00:00:00 2001 From: merlokk Date: Sun, 22 Oct 2017 21:47:19 +0300 Subject: [PATCH 024/310] added testing timeout 40 sec to avoid hang VM --- appveyor.yml | 74 ++-------------------------------------------------- 1 file changed, 2 insertions(+), 72 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index a4a70f7a..daf883f2 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -63,80 +63,10 @@ install: build_script: - ps: "$env:Path = \"C:\\ProxSpace\\msys\\bin;$env:Path\"\n\n#make\nbash -lc -i \"pwd;make all\"\n\n#some checks\nif(!(Test-Path C:\\ProxSpace\\pm3\\client\\proxmark3.exe)){\nthrow \"Main file proxmark3.exe not exists.\"\n}\nif(!(Test-Path C:\\ProxSpace\\pm3\\armsrc\\obj\\fullimage.elf)){\nthrow \"ARM file fullimage.elf not exists.\"\n}\nif(!(Test-Path C:\\ProxSpace\\pm3\\client\\hardnested\\tables\\*.bin.z)){\nthrow \"Files in hardnested\\tables not exists.\"\n}\n\n#copy\nWrite-Host \"Copy release files...\" -NoNewLine -ForegroundColor Yellow\nNew-Item -ItemType Directory -Force -Path C:\\ProxSpace\\Release\nCopy-Item C:\\ProxSpace\\pm3\\client\\*.exe C:\\ProxSpace\\Release\nNew-Item -ItemType Directory -Force -Path C:\\ProxSpace\\Release\\arm\nCopy-Item C:\\ProxSpace\\pm3\\armsrc\\obj\\*.elf C:\\ProxSpace\\Release\\arm\nCopy-Item C:\\ProxSpace\\pm3\\bootrom\\obj\\*.elf C:\\ProxSpace\\Release\\arm\nNew-Item -ItemType Directory -Force -Path C:\\ProxSpace\\Release\\scripts\nCopy-Item C:\\ProxSpace\\pm3\\client\\scripts\\*.lua C:\\ProxSpace\\Release\\scripts\nNew-Item -ItemType Directory -Force -Path C:\\ProxSpace\\Release\\hardnested\\tables\nCopy-Item C:\\ProxSpace\\pm3\\client\\hardnested\\*.bin C:\\ProxSpace\\Release\\hardnested\nCopy-Item C:\\ProxSpace\\pm3\\client\\hardnested\\tables\\*.bin.z C:\\ProxSpace\\Release\\hardnested\\tables\nWrite-Host \"[ OK ]\" -ForegroundColor Green\n\n#archive and push\n$releasename=\"\"\nif ($env:appveyor_repo_tag -match \"true\"){\n$releasename=$env:APPVEYOR_REPO_TAG_NAME + \"/\"\n}\n$releasename+=$env:APPVEYOR_BUILD_VERSION + \" [\" + $env:APPVEYOR_REPO_COMMIT.Substring(0, 7) + \"]\" \n\nWrite-Host \"Archive and publish release files ($releasename)...\" -NoNewLine -ForegroundColor Yellow\ncd C:\\ProxSpace\n7z a release.zip C:\\ProxSpace\\Release\nPush-AppveyorArtifact release.zip -DeploymentName \"$releasename\"\nWrite-Host \"[ OK ]\" -ForegroundColor Green\n\nWrite-Host \"Builded...\" -ForegroundColor Yellow" test_script: -- ps: >- - $env:Path = "C:\ProxSpace\msys\bin;$env:Path" - - cd c:\ProxSpace\pm3 - - - $global:TestsPassed=$true - - $global:TestTime=[System.Environment]::TickCount - - - Function ExecTest($Name, $File, $Cond) { - [bool]$res=$false; - if ($Cond -eq $null){ - } Else { - If (!($Cond -is [bool] -or $Cond -is [byte] -or $Cond -is [int16] -or $Cond -is [int32] -or $Cond -is [int64] -or $Cond -is [float])){ - if ($Cond -is "String" -and $Cond -like "*passed*"){ - $res= $true - } - if ($Cond -is "String" -and $Cond -like "*true*"){ - $res= $true - } - } Else { - $res=$Cond; - } - } - - If ($res) { - Add-AppveyorTest -Name "$Name" -Framework NUnit -Filename "$File" -Outcome Passed -Duration "$([System.Environment]::TickCount-$global:TestTime)" - }Else { - Add-AppveyorTest -Name "$Name" -Framework NUnit -Filename "$File" -Outcome Failed -Duration "$([System.Environment]::TickCount-$global:TestTime)" - $global:TestsPassed=$false - } - $global:TestTime=[System.Environment]::TickCount - } - - - Write-Host "Running tests..." -ForegroundColor Yellow - - - #file test - - ExecTest "proxmark3 exists" "proxmark3.exe" $(Test-Path C:\ProxSpace\Release\proxmark3.exe) - - ExecTest "arm image exists" "\arm\fullimage1.elf" $(Test-Path C:\ProxSpace\Release\arm\fullimage.elf) - - ExecTest "bootrom exists" "bootrom.elf" $(Test-Path C:\ProxSpace\Release\arm\bootrom.elf) - - ExecTest "hardnested tables exists" "hardnested" $(Test-Path C:\ProxSpace\Release\hardnested\tables\*.z) - - ExecTest "release exists" "release.zip" $(Test-Path C:\ProxSpace\release.zip) - - - #proxmark logic tests - - ExecTest "proxmark help" "proxmark3 -h" $(bash -lc 'cd ~/client;proxmark3 -h | grep -q Execute && echo Passed || echo Failed') - - ExecTest "proxmark help hardnested" "proxmark3 -h" $(bash -lc 'cd ~/client;proxmark3 -h | grep -q hardnested && echo Passed || echo Failed') - - - ExecTest "hf mf offline text" "hf mf" $(bash -lc "cd ~/client;proxmark3 comx -c 'hf mf' | grep -q at_enc && echo Passed || echo Failed") - - ExecTest "hf mf hardnested" "hf mf hardnested" $(bash -lc "cd ~/client;proxmark3 comx -c 'hf mf hardnested t 1 000000000000' | grep -q 'found:' && echo Passed || echo Failed") - - - if ($global:TestsPassed) { - Write-Host "Tests [ OK ]" -ForegroundColor Green - } else { - Write-Host "Tests [ ERROR ]" -ForegroundColor Red - throw "Tests error." - } +- ps: "$env:Path = \"C:\\ProxSpace\\msys\\bin;$env:Path\"\ncd c:\\ProxSpace\\pm3\n\n$global:TestsPassed=$true\n\nFunction ExecTest($Name, $File, $Cmd) {\n#--- begin Job \n$Job = Start-Job -ScriptBlock { \n [bool]$res=$false;\n $TestTime=[System.Environment]::TickCount\n $env:Path = \"C:\\ProxSpace\\msys\\bin;$env:Path\"\n Set-Location $using:PWD\n\n $sb=[scriptblock]::Create(\"$using:Cmd\")\n #execute scriptblock\n $Cond=&$sb\n\n if ($Cond -eq $null){\n } Else {\n If (!($Cond -is [bool] -or $Cond -is [byte] -or $Cond -is [int16] -or $Cond -is [int32] -or $Cond -is [int64] -or $Cond -is [float])){\n if ($Cond -is \"String\" -and $Cond -like \"*passed*\"){\n $res= $true\n }\n if ($Cond -is \"String\" -and $Cond -like \"*true*\"){\n $res= $true\n }\n } Else {\n $res=$Cond;\n }\n }\n\n If ($res) {\n Add-AppveyorTest -Name \"$using:Name\" -Framework NUnit -Filename \"$using:File\" -Outcome Passed -Duration \"$([System.Environment]::TickCount-$TestTime)\"\n }Else {\n Add-AppveyorTest -Name \"$using:Name\" -Framework NUnit -Filename \"$using:File\" -Outcome Failed -Duration \"$([System.Environment]::TickCount-$TestTime)\" -ErrorMessage \"command:$using:Cmd`nresult:$Cond\"\n }\n return $res\n}\n#--- end Job\n\n [bool]$res=$false\n # Wait 40 sec timeout for Job\n if(Wait-Job $Job -Timeout 40){\n $Results = $Job | Receive-Job \n if($Results -like \"true\"){\n $res=$true\n }\n } else {\n Add-AppveyorTest -Name \"$Name\" -Framework NUnit -Filename \"$File\" -Outcome Failed -Duration 40000 -ErrorMessage \"timeout\"\n }\n Remove-Job -Force $Job\n\n if(!$res){\n $global:TestsPassed=$false\n }\n}\n\nWrite-Host \"Running tests...\" -ForegroundColor Yellow\n\n#file test\nExecTest \"proxmark3 exists\" \"proxmark3.exe\" {Test-Path C:\\ProxSpace\\Release\\proxmark3.exe}\nExecTest \"arm image exists\" \"\\arm\\fullimage1.elf\" {Test-Path C:\\ProxSpace\\Release\\arm\\fullimage.elf}\nExecTest \"bootrom exists\" \"bootrom.elf\" {Test-Path C:\\ProxSpace\\Release\\arm\\bootrom.elf}\nExecTest \"hardnested tables exists\" \"hardnested\" {Test-Path C:\\ProxSpace\\Release\\hardnested\\tables\\*.z}\nExecTest \"release exists\" \"release.zip\" {Test-Path C:\\ProxSpace\\release.zip}\n\n#proxmark logic tests\nExecTest \"proxmark help\" \"proxmark3 -h\" {bash -lc 'cd ~/client;proxmark3 -h | grep -q Execute && echo Passed || echo Failed'}\nExecTest \"proxmark help hardnested\" \"proxmark3 -h\" {bash -lc 'cd ~/client;proxmark3 -h | grep -q hardnested && echo Passed || echo Failed'}\n\nExecTest \"hf mf offline text\" \"hf mf\" {bash -lc \"cd ~/client;proxmark3 comx -c 'hf mf' | grep -q at_enc && echo Passed || echo Failed\"}\nExecTest \"hf mf hardnested\" \"hf mf hardnested\" {bash -lc \"cd ~/client;proxmark3 comx -c 'hf mf hardnested t 1 000000000000' | grep -q 'found:' && echo Passed || echo Failed\"}\n\nif ($global:TestsPassed) {\n Write-Host \"Tests [ OK ]\" -ForegroundColor Green\n} else {\n Write-Host \"Tests [ ERROR ]\" -ForegroundColor Red\n throw \"Tests error.\"\n}" on_success: - ps: Write-Host "Build success..." -ForegroundColor Green on_failure: - ps: Write-Host "Build error." -ForegroundColor Red on_finish: -- ps: $blockRdp = $false; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) \ No newline at end of file +- ps: $blockRdp = $false; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) From 9764d3ea0ec65f520efd4528301da00eedbfbad4 Mon Sep 17 00:00:00 2001 From: merlokk Date: Sun, 22 Oct 2017 22:20:44 +0300 Subject: [PATCH 025/310] normally show powershell scripts it needs to have no spaces at the end of lines and no `;` --- appveyor.yml | 207 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 204 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index daf883f2..58bb1362 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,29 @@ version: 3.0.1.{build} image: Visual Studio 2017 clone_folder: C:\ProxSpace\pm3 init: -- ps: "$psversiontable\n#Get-ChildItem Env:\n\n$releasename=\"\"\n$env:APPVEYOR_REPO_COMMIT_SHORT = $env:APPVEYOR_REPO_COMMIT.Substring(0, 8)\nif ($env:appveyor_repo_tag -match \"true\"){\n $releasename=$env:APPVEYOR_REPO_TAG_NAME + \"/\"\n}\n$releasename+=$env:APPVEYOR_BUILD_VERSION + \" [\" + $env:APPVEYOR_REPO_COMMIT_SHORT + \"]\" \n\nWrite-Host \"repository: $env:appveyor_repo_name branch:$env:APPVEYOR_REPO_BRANCH release: $releasename\" -ForegroundColor Yellow\nAdd-AppveyorMessage -Message \"[$env:APPVEYOR_REPO_COMMIT_SHORT]$env:appveyor_repo_name($env:APPVEYOR_REPO_BRANCH)\" -Category Information -Details \"repository: $env:appveyor_repo_name branch: $env:APPVEYOR_REPO_BRANCH release: $releasename\"\n\niex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))" +- ps: >- + $psversiontable + + #Get-ChildItem Env: + + + $releasename="" + + $env:APPVEYOR_REPO_COMMIT_SHORT = $env:APPVEYOR_REPO_COMMIT.Substring(0, 8) + + if ($env:appveyor_repo_tag -match "true"){ + $releasename=$env:APPVEYOR_REPO_TAG_NAME + "/" + } + + $releasename+=$env:APPVEYOR_BUILD_VERSION + " [" + $env:APPVEYOR_REPO_COMMIT_SHORT + "]" + + + Write-Host "repository: $env:appveyor_repo_name branch:$env:APPVEYOR_REPO_BRANCH release: $releasename" -ForegroundColor Yellow + + Add-AppveyorMessage -Message "[$env:APPVEYOR_REPO_COMMIT_SHORT]$env:appveyor_repo_name($env:APPVEYOR_REPO_BRANCH)" -Category Information -Details "repository: $env:appveyor_repo_name branch: $env:APPVEYOR_REPO_BRANCH release: $releasename" + + + iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) clone_script: - ps: >- Write-Host "Removing ProxSpace..." -NoNewLine @@ -61,9 +83,188 @@ install: } } build_script: -- ps: "$env:Path = \"C:\\ProxSpace\\msys\\bin;$env:Path\"\n\n#make\nbash -lc -i \"pwd;make all\"\n\n#some checks\nif(!(Test-Path C:\\ProxSpace\\pm3\\client\\proxmark3.exe)){\nthrow \"Main file proxmark3.exe not exists.\"\n}\nif(!(Test-Path C:\\ProxSpace\\pm3\\armsrc\\obj\\fullimage.elf)){\nthrow \"ARM file fullimage.elf not exists.\"\n}\nif(!(Test-Path C:\\ProxSpace\\pm3\\client\\hardnested\\tables\\*.bin.z)){\nthrow \"Files in hardnested\\tables not exists.\"\n}\n\n#copy\nWrite-Host \"Copy release files...\" -NoNewLine -ForegroundColor Yellow\nNew-Item -ItemType Directory -Force -Path C:\\ProxSpace\\Release\nCopy-Item C:\\ProxSpace\\pm3\\client\\*.exe C:\\ProxSpace\\Release\nNew-Item -ItemType Directory -Force -Path C:\\ProxSpace\\Release\\arm\nCopy-Item C:\\ProxSpace\\pm3\\armsrc\\obj\\*.elf C:\\ProxSpace\\Release\\arm\nCopy-Item C:\\ProxSpace\\pm3\\bootrom\\obj\\*.elf C:\\ProxSpace\\Release\\arm\nNew-Item -ItemType Directory -Force -Path C:\\ProxSpace\\Release\\scripts\nCopy-Item C:\\ProxSpace\\pm3\\client\\scripts\\*.lua C:\\ProxSpace\\Release\\scripts\nNew-Item -ItemType Directory -Force -Path C:\\ProxSpace\\Release\\hardnested\\tables\nCopy-Item C:\\ProxSpace\\pm3\\client\\hardnested\\*.bin C:\\ProxSpace\\Release\\hardnested\nCopy-Item C:\\ProxSpace\\pm3\\client\\hardnested\\tables\\*.bin.z C:\\ProxSpace\\Release\\hardnested\\tables\nWrite-Host \"[ OK ]\" -ForegroundColor Green\n\n#archive and push\n$releasename=\"\"\nif ($env:appveyor_repo_tag -match \"true\"){\n$releasename=$env:APPVEYOR_REPO_TAG_NAME + \"/\"\n}\n$releasename+=$env:APPVEYOR_BUILD_VERSION + \" [\" + $env:APPVEYOR_REPO_COMMIT.Substring(0, 7) + \"]\" \n\nWrite-Host \"Archive and publish release files ($releasename)...\" -NoNewLine -ForegroundColor Yellow\ncd C:\\ProxSpace\n7z a release.zip C:\\ProxSpace\\Release\nPush-AppveyorArtifact release.zip -DeploymentName \"$releasename\"\nWrite-Host \"[ OK ]\" -ForegroundColor Green\n\nWrite-Host \"Builded...\" -ForegroundColor Yellow" +- ps: >- + $env:Path = "C:\ProxSpace\msys\bin;$env:Path" + + + #make + + bash -lc -i "pwd;make all" + + + #some checks + + if(!(Test-Path C:\ProxSpace\pm3\client\proxmark3.exe)){ + + throw "Main file proxmark3.exe not exists." + + } + + if(!(Test-Path C:\ProxSpace\pm3\armsrc\obj\fullimage.elf)){ + + throw "ARM file fullimage.elf not exists." + + } + + if(!(Test-Path C:\ProxSpace\pm3\client\hardnested\tables\*.bin.z)){ + + throw "Files in hardnested\tables not exists." + + } + + + #copy + + Write-Host "Copy release files..." -NoNewLine -ForegroundColor Yellow + + New-Item -ItemType Directory -Force -Path C:\ProxSpace\Release + + Copy-Item C:\ProxSpace\pm3\client\*.exe C:\ProxSpace\Release + + New-Item -ItemType Directory -Force -Path C:\ProxSpace\Release\arm + + Copy-Item C:\ProxSpace\pm3\armsrc\obj\*.elf C:\ProxSpace\Release\arm + + Copy-Item C:\ProxSpace\pm3\bootrom\obj\*.elf C:\ProxSpace\Release\arm + + New-Item -ItemType Directory -Force -Path C:\ProxSpace\Release\scripts + + Copy-Item C:\ProxSpace\pm3\client\scripts\*.lua C:\ProxSpace\Release\scripts + + New-Item -ItemType Directory -Force -Path C:\ProxSpace\Release\hardnested\tables + + Copy-Item C:\ProxSpace\pm3\client\hardnested\*.bin C:\ProxSpace\Release\hardnested + + Copy-Item C:\ProxSpace\pm3\client\hardnested\tables\*.bin.z C:\ProxSpace\Release\hardnested\tables + + Write-Host "[ OK ]" -ForegroundColor Green + + + #archive and push + + $releasename="" + + if ($env:appveyor_repo_tag -match "true"){ + + $releasename=$env:APPVEYOR_REPO_TAG_NAME + "/" + + } + + $releasename+=$env:APPVEYOR_BUILD_VERSION + " [" + $env:APPVEYOR_REPO_COMMIT.Substring(0, 7) + "]" + + + Write-Host "Archive and publish release files ($releasename)..." -NoNewLine -ForegroundColor Yellow + + cd C:\ProxSpace + + 7z a release.zip C:\ProxSpace\Release + + Push-AppveyorArtifact release.zip -DeploymentName "$releasename" + + Write-Host "[ OK ]" -ForegroundColor Green + + + Write-Host "Builded..." -ForegroundColor Yellow test_script: -- ps: "$env:Path = \"C:\\ProxSpace\\msys\\bin;$env:Path\"\ncd c:\\ProxSpace\\pm3\n\n$global:TestsPassed=$true\n\nFunction ExecTest($Name, $File, $Cmd) {\n#--- begin Job \n$Job = Start-Job -ScriptBlock { \n [bool]$res=$false;\n $TestTime=[System.Environment]::TickCount\n $env:Path = \"C:\\ProxSpace\\msys\\bin;$env:Path\"\n Set-Location $using:PWD\n\n $sb=[scriptblock]::Create(\"$using:Cmd\")\n #execute scriptblock\n $Cond=&$sb\n\n if ($Cond -eq $null){\n } Else {\n If (!($Cond -is [bool] -or $Cond -is [byte] -or $Cond -is [int16] -or $Cond -is [int32] -or $Cond -is [int64] -or $Cond -is [float])){\n if ($Cond -is \"String\" -and $Cond -like \"*passed*\"){\n $res= $true\n }\n if ($Cond -is \"String\" -and $Cond -like \"*true*\"){\n $res= $true\n }\n } Else {\n $res=$Cond;\n }\n }\n\n If ($res) {\n Add-AppveyorTest -Name \"$using:Name\" -Framework NUnit -Filename \"$using:File\" -Outcome Passed -Duration \"$([System.Environment]::TickCount-$TestTime)\"\n }Else {\n Add-AppveyorTest -Name \"$using:Name\" -Framework NUnit -Filename \"$using:File\" -Outcome Failed -Duration \"$([System.Environment]::TickCount-$TestTime)\" -ErrorMessage \"command:$using:Cmd`nresult:$Cond\"\n }\n return $res\n}\n#--- end Job\n\n [bool]$res=$false\n # Wait 40 sec timeout for Job\n if(Wait-Job $Job -Timeout 40){\n $Results = $Job | Receive-Job \n if($Results -like \"true\"){\n $res=$true\n }\n } else {\n Add-AppveyorTest -Name \"$Name\" -Framework NUnit -Filename \"$File\" -Outcome Failed -Duration 40000 -ErrorMessage \"timeout\"\n }\n Remove-Job -Force $Job\n\n if(!$res){\n $global:TestsPassed=$false\n }\n}\n\nWrite-Host \"Running tests...\" -ForegroundColor Yellow\n\n#file test\nExecTest \"proxmark3 exists\" \"proxmark3.exe\" {Test-Path C:\\ProxSpace\\Release\\proxmark3.exe}\nExecTest \"arm image exists\" \"\\arm\\fullimage1.elf\" {Test-Path C:\\ProxSpace\\Release\\arm\\fullimage.elf}\nExecTest \"bootrom exists\" \"bootrom.elf\" {Test-Path C:\\ProxSpace\\Release\\arm\\bootrom.elf}\nExecTest \"hardnested tables exists\" \"hardnested\" {Test-Path C:\\ProxSpace\\Release\\hardnested\\tables\\*.z}\nExecTest \"release exists\" \"release.zip\" {Test-Path C:\\ProxSpace\\release.zip}\n\n#proxmark logic tests\nExecTest \"proxmark help\" \"proxmark3 -h\" {bash -lc 'cd ~/client;proxmark3 -h | grep -q Execute && echo Passed || echo Failed'}\nExecTest \"proxmark help hardnested\" \"proxmark3 -h\" {bash -lc 'cd ~/client;proxmark3 -h | grep -q hardnested && echo Passed || echo Failed'}\n\nExecTest \"hf mf offline text\" \"hf mf\" {bash -lc \"cd ~/client;proxmark3 comx -c 'hf mf' | grep -q at_enc && echo Passed || echo Failed\"}\nExecTest \"hf mf hardnested\" \"hf mf hardnested\" {bash -lc \"cd ~/client;proxmark3 comx -c 'hf mf hardnested t 1 000000000000' | grep -q 'found:' && echo Passed || echo Failed\"}\n\nif ($global:TestsPassed) {\n Write-Host \"Tests [ OK ]\" -ForegroundColor Green\n} else {\n Write-Host \"Tests [ ERROR ]\" -ForegroundColor Red\n throw \"Tests error.\"\n}" +- ps: >- + $env:Path = "C:\ProxSpace\msys\bin;$env:Path" + + cd c:\ProxSpace\pm3 + + + $global:TestsPassed=$true + + + Function ExecTest($Name, $File, $Cmd) { + + #--- begin Job + + $Job = Start-Job -ScriptBlock { + [bool]$res=$false + $TestTime=[System.Environment]::TickCount + $env:Path = "C:\ProxSpace\msys\bin;$env:Path" + Set-Location $using:PWD + + $sb=[scriptblock]::Create("$using:Cmd") + #execute scriptblock + $Cond=&$sb + + if ($Cond -eq $null){ + } Else { + If (!($Cond -is [bool] -or $Cond -is [byte] -or $Cond -is [int16] -or $Cond -is [int32] -or $Cond -is [int64] -or $Cond -is [float])){ + if ($Cond -is "String" -and $Cond -like "*passed*"){ + $res= $true + } + if ($Cond -is "String" -and $Cond -like "*true*"){ + $res= $true + } + } Else { + $res=$Cond + } + } + + If ($res) { + Add-AppveyorTest -Name "$using:Name" -Framework NUnit -Filename "$using:File" -Outcome Passed -Duration "$([System.Environment]::TickCount-$TestTime)" + }Else { + Add-AppveyorTest -Name "$using:Name" -Framework NUnit -Filename "$using:File" -Outcome Failed -Duration "$([System.Environment]::TickCount-$TestTime)" -ErrorMessage "command:$using:Cmd`nresult:$Cond" + } + return $res + } + + #--- end Job + + [bool]$res=$false + # Wait 40 sec timeout for Job + if(Wait-Job $Job -Timeout 40){ + $Results = $Job | Receive-Job + if($Results -like "true"){ + $res=$true + } + } else { + Add-AppveyorTest -Name "$Name" -Framework NUnit -Filename "$File" -Outcome Failed -Duration 40000 -ErrorMessage "timeout" + } + Remove-Job -Force $Job + + if(!$res){ + $global:TestsPassed=$false + } + } + + + Write-Host "Running tests..." -ForegroundColor Yellow + + + #file test + + ExecTest "proxmark3 exists" "proxmark3.exe" {Test-Path C:\ProxSpace\Release\proxmark3.exe} + + ExecTest "arm image exists" "\arm\fullimage1.elf" {Test-Path C:\ProxSpace\Release\arm\fullimage.elf} + + ExecTest "bootrom exists" "bootrom.elf" {Test-Path C:\ProxSpace\Release\arm\bootrom.elf} + + ExecTest "hardnested tables exists" "hardnested" {Test-Path C:\ProxSpace\Release\hardnested\tables\*.z} + + ExecTest "release exists" "release.zip" {Test-Path C:\ProxSpace\release.zip} + + + #proxmark logic tests + + ExecTest "proxmark help" "proxmark3 -h" {bash -lc 'cd ~/client;proxmark3 -h | grep -q Execute && echo Passed || echo Failed'} + + ExecTest "proxmark help hardnested" "proxmark3 -h" {bash -lc 'cd ~/client;proxmark3 -h | grep -q hardnested && echo Passed || echo Failed'} + + + ExecTest "hf mf offline text" "hf mf" {bash -lc "cd ~/client;proxmark3 comx -c 'hf mf' | grep -q at_enc && echo Passed || echo Failed"} + + ExecTest "hf mf hardnested" "hf mf hardnested" {bash -lc "cd ~/client;proxmark3 comx -c 'hf mf hardnested t 1 000000000000' | grep -q 'found:' && echo Passed || echo Failed"} + + + if ($global:TestsPassed) { + Write-Host "Tests [ OK ]" -ForegroundColor Green + } else { + Write-Host "Tests [ ERROR ]" -ForegroundColor Red + throw "Tests error." + } on_success: - ps: Write-Host "Build success..." -ForegroundColor Green on_failure: From 05b6b117850e7d60a0fb0609550f3c029d98201c Mon Sep 17 00:00:00 2001 From: Michael Farrell Date: Mon, 23 Oct 2017 18:04:06 +1100 Subject: [PATCH 026/310] Multiple USB-CDC changes (attempt 6) (#439) - Fix reporting of string descriptors. - Add note about ModemManager matching on the manufacturer string. --- common/usb_cdc.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/common/usb_cdc.c b/common/usb_cdc.c index 56690ad8..91a63cf3 100644 --- a/common/usb_cdc.c +++ b/common/usb_cdc.c @@ -44,6 +44,11 @@ #define AT91C_EP_OUT_SIZE 0x40 #define AT91C_EP_IN_SIZE 0x40 +// Language must always be 0. +#define STR_LANGUAGE_CODES 0x00 +#define STR_MANUFACTURER 0x01 +#define STR_PRODUCT 0x02 + static const char devDescriptor[] = { /* Device descriptor */ 0x12, // bLength @@ -56,8 +61,8 @@ static const char devDescriptor[] = { 0xc4,0x9a, // Vendor ID (0x9ac4 = J. Westhues) 0x8f,0x4b, // Product ID (0x4b8f = Proxmark-3 RFID Instrument) 0x01,0x00, // Device release number (0001) - 0x01, // iManufacturer - 0x02, // iProduct + STR_MANUFACTURER, // iManufacturer + STR_PRODUCT, // iProduct 0x00, // iSerialNumber 0x01 // bNumConfigs }; @@ -157,7 +162,9 @@ static const char StrDescLanguageCodes[] = { 0x03, // Type is string 0x09, 0x04 // supported language Code 0 = 0x0409 (English) }; - + +// Note: ModemManager (Linux) ignores Proxmark3 devices by matching the +// manufacturer string "proxmark.org". Don't change this. static const char StrDescManufacturer[] = { 26, // Length 0x03, // Type is string @@ -182,20 +189,18 @@ static const char StrDescProduct[] = { 'M', 0x00, '3', 0x00 }; - -static const char* const pStrings[] = -{ - StrDescLanguageCodes, - StrDescManufacturer, - StrDescProduct -}; const char* getStringDescriptor(uint8_t idx) { - if(idx >= (sizeof(pStrings) / sizeof(pStrings[0]))) { - return(NULL); - } else { - return(pStrings[idx]); + switch (idx) { + case STR_LANGUAGE_CODES: + return StrDescLanguageCodes; + case STR_MANUFACTURER: + return StrDescManufacturer; + case STR_PRODUCT: + return StrDescProduct; + default: + return NULL; } } From b5381d70dcbe6599dcb472fb65a4fe0bc8de98f9 Mon Sep 17 00:00:00 2001 From: merlokk Date: Mon, 23 Oct 2017 13:43:46 +0300 Subject: [PATCH 027/310] added write-host for tests --- appveyor.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 58bb1362..b072335a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -187,7 +187,9 @@ test_script: $sb=[scriptblock]::Create("$using:Cmd") #execute scriptblock + Write-host "Test [$using:Name] job: $using:Cmd" $Cond=&$sb + Write-host "Result[$using:Name]: $Cond" if ($Cond -eq $null){ } Else { @@ -221,6 +223,7 @@ test_script: $res=$true } } else { + Write-host "Test [$Name] timeout" -ForegroundColor Red Add-AppveyorTest -Name "$Name" -Framework NUnit -Filename "$File" -Outcome Failed -Duration 40000 -ErrorMessage "timeout" } Remove-Job -Force $Job From 23af9327a53b865383271e5844f75997671b32aa Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Tue, 24 Oct 2017 07:48:38 +0200 Subject: [PATCH 028/310] fix hw status (and USB comm in general) (#434) * don't ignore ReadLine() errors (Windows) * lock CmdBuffer with Mutex * refactor WaitForResponseTimeoutW --- client/cmdmain.c | 26 +++++++++++++++++++++----- client/proxmark3.c | 3 +-- uart/uart_win32.c | 8 +++----- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/client/cmdmain.c b/client/cmdmain.c index db88b3ac..739d68e1 100644 --- a/client/cmdmain.c +++ b/client/cmdmain.c @@ -8,6 +8,9 @@ // Main command parser entry point //----------------------------------------------------------------------------- +#include "cmdmain.h" + +#include #include #include #include @@ -21,7 +24,6 @@ #include "cmddata.h" #include "cmdhw.h" #include "cmdlf.h" -#include "cmdmain.h" #include "util.h" #include "util_posix.h" #include "cmdscript.h" @@ -41,6 +43,8 @@ static UsbCommand cmdBuffer[CMD_BUFFER_SIZE]; static int cmd_head;//Starts as 0 //Points to the position of the last unread command static int cmd_tail;//Starts as 0 +// to lock cmdBuffer operations from different threads +static pthread_mutex_t cmdBufferMutex = PTHREAD_MUTEX_INITIALIZER; static command_t CommandTable[] = { @@ -86,7 +90,9 @@ int CmdRev(const char *Cmd) void clearCommandBuffer() { //This is a very simple operation + pthread_mutex_lock(&cmdBufferMutex); cmd_tail = cmd_head; + pthread_mutex_unlock(&cmdBufferMutex); } /** @@ -95,6 +101,7 @@ void clearCommandBuffer() */ void storeCommand(UsbCommand *command) { + pthread_mutex_lock(&cmdBufferMutex); if( ( cmd_head+1) % CMD_BUFFER_SIZE == cmd_tail) { //If these two are equal, we're about to overwrite in the @@ -106,6 +113,7 @@ void storeCommand(UsbCommand *command) memcpy(destination, command, sizeof(UsbCommand)); cmd_head = (cmd_head +1) % CMD_BUFFER_SIZE; //increment head and wrap + pthread_mutex_unlock(&cmdBufferMutex); } @@ -116,8 +124,10 @@ void storeCommand(UsbCommand *command) */ int getCommand(UsbCommand* response) { + pthread_mutex_lock(&cmdBufferMutex); //If head == tail, there's nothing to read, or if we just got initialized if(cmd_head == cmd_tail){ + pthread_mutex_unlock(&cmdBufferMutex); return 0; } //Pick out the next unread command @@ -125,7 +135,7 @@ int getCommand(UsbCommand* response) memcpy(response, last_unread, sizeof(UsbCommand)); //Increment tail - this is a circular buffer, so modulo buffer size cmd_tail = (cmd_tail +1 ) % CMD_BUFFER_SIZE; - + pthread_mutex_unlock(&cmdBufferMutex); return 1; } @@ -147,22 +157,28 @@ bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeo response = &resp; } + uint64_t start_time = msclock(); + // Wait until the command is received - for(size_t dm_seconds=0; dm_seconds < ms_timeout/10; dm_seconds++) { + while (true) { while(getCommand(response)) { if(response->cmd == cmd){ return true; } } - msleep(10); // XXX ugh - if (dm_seconds == 200 && show_warning) { // Two seconds elapsed + if (msclock() - start_time > ms_timeout) { + break; + } + if (msclock() - start_time > 2000 && show_warning) { PrintAndLog("Waiting for a response from the proxmark..."); PrintAndLog("Don't forget to cancel its operation first by pressing on the button"); + break; } } return false; } + bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout) { return WaitForResponseTimeoutW(cmd, response, ms_timeout, true); } diff --git a/client/proxmark3.c b/client/proxmark3.c index d0e68b61..f7a87f6d 100644 --- a/client/proxmark3.c +++ b/client/proxmark3.c @@ -73,12 +73,11 @@ static void *uart_receiver(void *targ) { while (arg->run) { rxlen = 0; - if (uart_receive(sp, prx, sizeof(UsbCommand) - (prx-rx), &rxlen)) { + if (uart_receive(sp, prx, sizeof(UsbCommand) - (prx-rx), &rxlen) && rxlen) { prx += rxlen; if (prx-rx < sizeof(UsbCommand)) { continue; } - UsbCommandReceived((UsbCommand*)rx); } prx = rx; diff --git a/uart/uart_win32.c b/uart/uart_win32.c index af521ead..121b2b51 100644 --- a/uart/uart_win32.c +++ b/uart/uart_win32.c @@ -107,15 +107,13 @@ void uart_close(const serial_port sp) { free(sp); } -bool uart_receive(const serial_port sp, byte_t* pbtRx, size_t pszMaxRxLen, size_t* pszRxLen) { - ReadFile(((serial_port_windows*)sp)->hPort,pbtRx,pszMaxRxLen,(LPDWORD)pszRxLen,NULL); - return (*pszRxLen != 0); +bool uart_receive(const serial_port sp, byte_t *pbtRx, size_t pszMaxRxLen, size_t *pszRxLen) { + return ReadFile(((serial_port_windows*)sp)->hPort, pbtRx, pszMaxRxLen, (LPDWORD)pszRxLen, NULL); } bool uart_send(const serial_port sp, const byte_t* pbtTx, const size_t szTxLen) { DWORD dwTxLen = 0; - return WriteFile(((serial_port_windows*)sp)->hPort,pbtTx,szTxLen,&dwTxLen,NULL); - return (dwTxLen != 0); + return WriteFile(((serial_port_windows*)sp)->hPort, pbtTx, szTxLen, &dwTxLen, NULL); } bool uart_set_speed(serial_port sp, const uint32_t uiPortSpeed) { From 8a50d606171df609f7a201f29b0a88e092fa6f40 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Tue, 24 Oct 2017 14:10:52 +0200 Subject: [PATCH 029/310] Flush stdout when waiting for /dev/ttyACM0 --- client/proxmark3.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/proxmark3.c b/client/proxmark3.c index f7a87f6d..99ba9fba 100644 --- a/client/proxmark3.c +++ b/client/proxmark3.c @@ -355,11 +355,13 @@ int main(int argc, char* argv[]) { sp = uart_open(argv[1]); } else { printf("Waiting for Proxmark to appear on %s ", argv[1]); + fflush(stdout); int openCount = 0; do { sp = uart_open(argv[1]); msleep(1000); printf("."); + fflush(stdout); } while(++openCount < 20 && (sp == INVALID_SERIAL_PORT || sp == CLAIMED_SERIAL_PORT)); printf("\n"); } From 8bdb6043b09a32998a2ea16e8aa14bdc815ca7ca Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Wed, 25 Oct 2017 08:56:03 +0300 Subject: [PATCH 030/310] identification fix for magic chinese cards (#444) * fixed #411 --- armsrc/mifarecmd.c | 17 ++++++++++++++++- client/mifarehost.c | 19 ++++++++++--------- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index edafe0a3..39029d4e 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -1510,6 +1510,16 @@ void MifareCIdent(){ uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE]; uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE]; + + LED_A_ON(); + LED_B_OFF(); + LED_C_OFF(); +// FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); +// SpinDelay(100); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + clear_trace(); + set_tracing(true); ReaderTransmitBitsPar(wupC1,7,0, NULL); if(ReaderReceive(receivedAnswer, receivedAnswerPar) && (receivedAnswer[0] == 0x0a)) { @@ -1523,8 +1533,13 @@ void MifareCIdent(){ // From iceman1001: removed the if, since some magic tags misbehavies and send an answer to it. mifare_classic_halt(NULL, 0); - + + LED_B_ON(); cmd_send(CMD_ACK,isOK,0,0,0,0); + LED_B_OFF(); + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); } // diff --git a/client/mifarehost.c b/client/mifarehost.c index a02019a3..ca5d97e1 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -542,11 +542,12 @@ int mfCSetUID(uint8_t *uid, uint8_t *atqa, uint8_t *sak, uint8_t *oldUID) { int mfCIdentify() { - UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0}}; - SendCommand(&c); + UsbCommand c; +// UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0}}; +// SendCommand(&c); - UsbCommand resp; - WaitForResponse(CMD_ACK,&resp); + UsbCommand resp; +// WaitForResponse(CMD_ACK,&resp); // iso14a_card_select_t card; // memcpy(&card, (iso14a_card_select_t *)resp.d.asBytes, sizeof(iso14a_card_select_t)); @@ -578,11 +579,11 @@ int mfCIdentify() } // disconnect - c.cmd = CMD_READER_ISO_14443a; - c.arg[0] = 0; - c.arg[1] = 0; - c.arg[2] = 0; - SendCommand(&c); +// c.cmd = CMD_READER_ISO_14443a; +// c.arg[0] = 0; +// c.arg[1] = 0; +// c.arg[2] = 0; +// SendCommand(&c); return (int) isGeneration; } From 5d7d0c3af9a97817db86407498f12aae02a085da Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Thu, 26 Oct 2017 19:32:18 +0300 Subject: [PATCH 031/310] small improvement in tests (test result in green or red) (#448) --- appveyor.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index b072335a..c8e58b59 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -189,7 +189,6 @@ test_script: #execute scriptblock Write-host "Test [$using:Name] job: $using:Cmd" $Cond=&$sb - Write-host "Result[$using:Name]: $Cond" if ($Cond -eq $null){ } Else { @@ -206,8 +205,10 @@ test_script: } If ($res) { + Write-host "Result[$using:Name]: $Cond" -ForegroundColor Green Add-AppveyorTest -Name "$using:Name" -Framework NUnit -Filename "$using:File" -Outcome Passed -Duration "$([System.Environment]::TickCount-$TestTime)" }Else { + Write-host "Result[$using:Name]: $Cond" -ForegroundColor Red Add-AppveyorTest -Name "$using:Name" -Framework NUnit -Filename "$using:File" -Outcome Failed -Duration "$([System.Environment]::TickCount-$TestTime)" -ErrorMessage "command:$using:Cmd`nresult:$Cond" } return $res From e17660d5f7dfa44447068e7897ee6454157d2ae1 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Thu, 26 Oct 2017 20:09:53 +0300 Subject: [PATCH 032/310] code cleaning (#445) --- armsrc/mifarecmd.c | 2 -- client/cmdhf14a.c | 21 +-------------------- client/mifarehost.c | 38 +++----------------------------------- 3 files changed, 4 insertions(+), 57 deletions(-) diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 39029d4e..00fd638c 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -1514,8 +1514,6 @@ void MifareCIdent(){ LED_A_ON(); LED_B_OFF(); LED_C_OFF(); -// FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); -// SpinDelay(100); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); clear_trace(); diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index db9ce46e..e3f1a5f1 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -405,27 +405,8 @@ int CmdHF14AReader(const char *Cmd) // try to see if card responses to "chinese magic backdoor" commands. - c.cmd = CMD_MIFARE_CIDENT; - c.arg[0] = 0; - c.arg[1] = 0; - c.arg[2] = 0; - SendCommand(&c); - WaitForResponse(CMD_ACK,&resp); + mfCIdentify(); - uint8_t isGeneration = resp.arg[0] & 0xff; - switch( isGeneration ){ - case 1: PrintAndLog("Answers to chinese magic backdoor commands (GEN 1a): YES"); break; - case 2: PrintAndLog("Answers to chinese magic backdoor commands (GEN 1b): YES"); break; - default: PrintAndLog("Answers to chinese magic backdoor commands: NO"); break; - } - - // disconnect - c.cmd = CMD_READER_ISO_14443a; - c.arg[0] = 0; - c.arg[1] = 0; - c.arg[2] = 0; - SendCommand(&c); - return select_status; } diff --git a/client/mifarehost.c b/client/mifarehost.c index ca5d97e1..fe1a8edb 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -540,35 +540,10 @@ int mfCSetUID(uint8_t *uid, uint8_t *atqa, uint8_t *sak, uint8_t *oldUID) { return 0; } -int mfCIdentify() -{ - UsbCommand c; -// UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0}}; -// SendCommand(&c); - - UsbCommand resp; -// WaitForResponse(CMD_ACK,&resp); - - // iso14a_card_select_t card; - // memcpy(&card, (iso14a_card_select_t *)resp.d.asBytes, sizeof(iso14a_card_select_t)); - - // uint64_t select_status = resp.arg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision - - // if(select_status != 0) { - // uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0 - // c.arg[0] = ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT; - // c.arg[1] = 2; - // c.arg[2] = 0; - // memcpy(c.d.asBytes, rats, 2); - // SendCommand(&c); - // WaitForResponse(CMD_ACK,&resp); - // } - - c.cmd = CMD_MIFARE_CIDENT; - c.arg[0] = 0; - c.arg[1] = 0; - c.arg[2] = 0; +int mfCIdentify() { + UsbCommand c = {CMD_MIFARE_CIDENT, {0, 0, 0}}; SendCommand(&c); + UsbCommand resp; WaitForResponse(CMD_ACK,&resp); uint8_t isGeneration = resp.arg[0] & 0xff; @@ -578,13 +553,6 @@ int mfCIdentify() default: PrintAndLog("No chinese magic backdoor command detected"); break; } - // disconnect -// c.cmd = CMD_READER_ISO_14443a; -// c.arg[0] = 0; -// c.arg[1] = 0; -// c.arg[2] = 0; -// SendCommand(&c); - return (int) isGeneration; } From afdcb8c159a73aba95a017f1cfec98e8fa2b93c1 Mon Sep 17 00:00:00 2001 From: Michael Farrell Date: Fri, 27 Oct 2017 06:54:27 +1100 Subject: [PATCH 033/310] Comms refactor (prerequisite of libproxmark work) (#371) * Refactor the comms code only from PR#346, without comms_globals.h. * OSX: Add note for example serial port --- client/Makefile | 10 +- client/cmdlf.c | 8 +- client/cmdmain.c | 152 +------------------ client/cmdmain.h | 7 +- client/cmdparser.c | 3 +- client/comms.c | 325 +++++++++++++++++++++++++++++++++++++++++ client/comms.h | 45 ++++++ client/flash.c | 77 +++++++--- client/flash.h | 5 +- client/flasher.c | 85 ++++------- client/proxgui.cpp | 14 +- client/proxgui.h | 4 +- client/proxguiqt.h | 4 +- client/proxmark3.c | 107 ++++---------- client/proxmark3.h | 3 +- client/ui.c | 12 +- client/ui.h | 8 +- common/iso15693tools.c | 3 +- include/usb_cmd.h | 2 +- 19 files changed, 540 insertions(+), 334 deletions(-) create mode 100644 client/comms.c create mode 100644 client/comms.h diff --git a/client/Makefile b/client/Makefile index 3a96e9e9..e14939e3 100644 --- a/client/Makefile +++ b/client/Makefile @@ -21,7 +21,7 @@ LDLIBS = -L/opt/local/lib -L/usr/local/lib -lreadline -lpthread -lm LUALIB = ../liblua/liblua.a LDFLAGS = $(ENV_LDFLAGS) CFLAGS = $(ENV_CFLAGS) -std=c99 -D_ISOC99_SOURCE -I. -I../include -I../common -I../zlib -I../uart -I/opt/local/include -I../liblua -Wall -g -O3 -CXXFLAGS = -I../include -Wall -O3 +CXXFLAGS = -I../include -I../uart -Wall -O3 LUAPLATFORM = generic platform = $(shell uname) @@ -82,7 +82,10 @@ POSTCOMPILE = $(MV) -f $(OBJDIR)/$*.Td $(OBJDIR)/$*.d CORESRCS = uart_posix.c \ uart_win32.c \ util.c \ - util_posix.c + util_posix.c \ + comms.c \ + data.c \ + ui.c CMDSRCS = crapto1/crapto1.c\ crapto1/crypto1.c\ @@ -102,9 +105,7 @@ CMDSRCS = crapto1/crapto1.c\ crc64.c \ iso14443crc.c \ iso15693tools.c \ - data.c \ graph.c \ - ui.c \ cmddata.c \ lfdemod.c \ cmdhf.c \ @@ -175,6 +176,7 @@ ZLIBFLAGS = -DZ_SOLO -DZ_PREFIX -DNO_GZIP -DZLIB_PM3_TUNED #-DDEBUG -Dverbose=1 QTGUISRCS = proxgui.cpp proxguiqt.cpp proxguiqt.moc.cpp guidummy.cpp +NOGUISRCS = guidummy.cpp COREOBJS = $(CORESRCS:%.c=$(OBJDIR)/%.o) CMDOBJS = $(CMDSRCS:%.c=$(OBJDIR)/%.o) diff --git a/client/cmdlf.c b/client/cmdlf.c index eb664a11..501bfaed 100644 --- a/client/cmdlf.c +++ b/client/cmdlf.c @@ -335,7 +335,7 @@ int CmdLFSetConfig(const char *Cmd) } bool lf_read(bool silent, uint32_t samples) { - if (offline) return false; + if (IsOffline()) return false; UsbCommand c = {CMD_ACQUIRE_RAW_ADC_SAMPLES_125K, {silent,samples,0}}; clearCommandBuffer(); //And ship it to device @@ -878,7 +878,7 @@ int CmdVchDemod(const char *Cmd) int CheckChipType(char cmdp) { uint32_t wordData = 0; - if (offline || cmdp == '1') return 0; + if (IsOffline() || cmdp == '1') return 0; save_restoreGB(GRAPH_SAVE); save_restoreDB(GRAPH_SAVE); @@ -923,7 +923,7 @@ int CmdLFfind(const char *Cmd) return 0; } - if (!offline && (cmdp != '1')) { + if (!IsOffline() && (cmdp != '1')) { lf_read(true, 30000); } else if (GraphTraceLen < minLength) { PrintAndLog("Data in Graphbuffer was too small."); @@ -939,7 +939,7 @@ int CmdLFfind(const char *Cmd) // only run if graphbuffer is just noise as it should be for hitag/cotag if (graphJustNoise(GraphBuffer, testLen)) { // only run these tests if we are in online mode - if (!offline && (cmdp != '1')) { + if (!IsOffline() && (cmdp != '1')) { // test for em4x05 in reader talk first mode. if (EM4x05Block0Test(&wordData)) { PrintAndLog("\nValid EM4x05/EM4x69 Chip Found\nUse lf em 4x05readword/dump commands to read\n"); diff --git a/client/cmdmain.c b/client/cmdmain.c index 739d68e1..e199e127 100644 --- a/client/cmdmain.c +++ b/client/cmdmain.c @@ -36,15 +36,6 @@ static int CmdHelp(const char *Cmd); static int CmdQuit(const char *Cmd); static int CmdRev(const char *Cmd); -//For storing command that are received from the device -#define CMD_BUFFER_SIZE 50 -static UsbCommand cmdBuffer[CMD_BUFFER_SIZE]; -//Points to the next empty position to write to -static int cmd_head;//Starts as 0 -//Points to the position of the last unread command -static int cmd_tail;//Starts as 0 -// to lock cmdBuffer operations from different threads -static pthread_mutex_t cmdBufferMutex = PTHREAD_MUTEX_INITIALIZER; static command_t CommandTable[] = { @@ -64,6 +55,7 @@ command_t* getTopLevelCommandTable() { return CommandTable; } + int CmdHelp(const char *Cmd) { CmdsHelp(CommandTable); @@ -81,113 +73,6 @@ int CmdRev(const char *Cmd) return 0; } -/** - * @brief This method should be called when sending a new command to the pm3. In case any old - * responses from previous commands are stored in the buffer, a call to this method should clear them. - * A better method could have been to have explicit command-ACKS, so we can know which ACK goes to which - * operation. Right now we'll just have to live with this. - */ -void clearCommandBuffer() -{ - //This is a very simple operation - pthread_mutex_lock(&cmdBufferMutex); - cmd_tail = cmd_head; - pthread_mutex_unlock(&cmdBufferMutex); -} - -/** - * @brief storeCommand stores a USB command in a circular buffer - * @param UC - */ -void storeCommand(UsbCommand *command) -{ - pthread_mutex_lock(&cmdBufferMutex); - if( ( cmd_head+1) % CMD_BUFFER_SIZE == cmd_tail) - { - //If these two are equal, we're about to overwrite in the - // circular buffer. - PrintAndLog("WARNING: Command buffer about to overwrite command! This needs to be fixed!"); - } - //Store the command at the 'head' location - UsbCommand* destination = &cmdBuffer[cmd_head]; - memcpy(destination, command, sizeof(UsbCommand)); - - cmd_head = (cmd_head +1) % CMD_BUFFER_SIZE; //increment head and wrap - pthread_mutex_unlock(&cmdBufferMutex); -} - - -/** - * @brief getCommand gets a command from an internal circular buffer. - * @param response location to write command - * @return 1 if response was returned, 0 if nothing has been received - */ -int getCommand(UsbCommand* response) -{ - pthread_mutex_lock(&cmdBufferMutex); - //If head == tail, there's nothing to read, or if we just got initialized - if(cmd_head == cmd_tail){ - pthread_mutex_unlock(&cmdBufferMutex); - return 0; - } - //Pick out the next unread command - UsbCommand* last_unread = &cmdBuffer[cmd_tail]; - memcpy(response, last_unread, sizeof(UsbCommand)); - //Increment tail - this is a circular buffer, so modulo buffer size - cmd_tail = (cmd_tail +1 ) % CMD_BUFFER_SIZE; - pthread_mutex_unlock(&cmdBufferMutex); - return 1; -} - - -/** - * Waits for a certain response type. This method waits for a maximum of - * ms_timeout milliseconds for a specified response command. - *@brief WaitForResponseTimeout - * @param cmd command to wait for - * @param response struct to copy received command into. - * @param ms_timeout - * @return true if command was returned, otherwise false - */ -bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning) { - - UsbCommand resp; - - if (response == NULL) { - response = &resp; - } - - uint64_t start_time = msclock(); - - // Wait until the command is received - while (true) { - while(getCommand(response)) { - if(response->cmd == cmd){ - return true; - } - } - if (msclock() - start_time > ms_timeout) { - break; - } - if (msclock() - start_time > 2000 && show_warning) { - PrintAndLog("Waiting for a response from the proxmark..."); - PrintAndLog("Don't forget to cancel its operation first by pressing on the button"); - break; - } - } - return false; -} - - -bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout) { - return WaitForResponseTimeoutW(cmd, response, ms_timeout, true); -} - -bool WaitForResponse(uint32_t cmd, UsbCommand* response) { - return WaitForResponseTimeoutW(cmd, response, -1, true); -} - - //----------------------------------------------------------------------------- // Entry point into our code: called whenever the user types a command and // then presses Enter, which the full command line that they typed. @@ -196,38 +81,3 @@ int CommandReceived(char *Cmd) { return CmdsParse(CommandTable, Cmd); } - -//----------------------------------------------------------------------------- -// Entry point into our code: called whenever we received a packet over USB -// that we weren't necessarily expecting, for example a debug print. -//----------------------------------------------------------------------------- -void UsbCommandReceived(UsbCommand *UC) -{ - switch(UC->cmd) { - // First check if we are handling a debug message - case CMD_DEBUG_PRINT_STRING: { - char s[USB_CMD_DATA_SIZE+1]; - memset(s, 0x00, sizeof(s)); - size_t len = MIN(UC->arg[0],USB_CMD_DATA_SIZE); - memcpy(s,UC->d.asBytes,len); - PrintAndLog("#db# %s", s); - return; - } break; - - case CMD_DEBUG_PRINT_INTEGERS: { - PrintAndLog("#db# %08x, %08x, %08x \r\n", UC->arg[0], UC->arg[1], UC->arg[2]); - return; - } break; - - case CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K: { - memcpy(sample_buf+(UC->arg[0]),UC->d.asBytes,UC->arg[1]); - return; - } break; - - default: - storeCommand(UC); - break; - } - -} - diff --git a/client/cmdmain.h b/client/cmdmain.h index d39bc114..a833b41e 100644 --- a/client/cmdmain.h +++ b/client/cmdmain.h @@ -15,13 +15,10 @@ #include #include "usb_cmd.h" #include "cmdparser.h" +#include "comms.h" + -extern void UsbCommandReceived(UsbCommand *UC); extern int CommandReceived(char *Cmd); -extern bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning); -extern bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout); -extern bool WaitForResponse(uint32_t cmd, UsbCommand* response); -extern void clearCommandBuffer(); extern command_t* getTopLevelCommandTable(); #endif diff --git a/client/cmdparser.c b/client/cmdparser.c index 32508997..28ca2196 100644 --- a/client/cmdparser.c +++ b/client/cmdparser.c @@ -14,6 +14,7 @@ #include "ui.h" #include "cmdparser.h" #include "proxmark3.h" +#include "comms.h" void CmdsHelp(const command_t Commands[]) @@ -23,7 +24,7 @@ void CmdsHelp(const command_t Commands[]) int i = 0; while (Commands[i].Name) { - if (!offline || Commands[i].Offline) + if (!IsOffline() || Commands[i].Offline) PrintAndLog("%-16s %s", Commands[i].Name, Commands[i].Help); ++i; } diff --git a/client/comms.c b/client/comms.c new file mode 100644 index 00000000..abbae386 --- /dev/null +++ b/client/comms.c @@ -0,0 +1,325 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2009 Michael Gernoth +// Copyright (C) 2010 iZsh +// +// 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. +//----------------------------------------------------------------------------- +// Code for communicating with the proxmark3 hardware. +//----------------------------------------------------------------------------- + +#include + +#include "comms.h" +#include "uart.h" +#include "ui.h" +#include "common.h" +#include "data.h" +#include "util_posix.h" + +// Declare globals. + +// Serial port that we are communicating with the PM3 on. +static serial_port* port; + +// If TRUE, then there is no active connection to the PM3, and we will drop commands sent. +static bool offline; + +// Transmit buffer. +// TODO: Use locks and execute this on the main thread, rather than the receiver +// thread. Running on the main thread means we need to be careful in the +// flasher, as it means SendCommand is no longer async, and can't be used as a +// buffer for a pending command when the connection is re-established. +static UsbCommand txcmd; +static bool txcmd_pending; + +// Used by UsbReceiveCommand as a ring buffer for messages that are yet to be +// processed by a command handler (WaitForResponse{,Timeout}) +static UsbCommand cmdBuffer[CMD_BUFFER_SIZE]; + +// Points to the next empty position to write to +static int cmd_head = 0; + +// Points to the position of the last unread command +static int cmd_tail = 0; + +// to lock cmdBuffer operations from different threads +static pthread_mutex_t cmdBufferMutex = PTHREAD_MUTEX_INITIALIZER; + +// These wrappers are required because it is not possible to access a static +// global variable outside of the context of a single file. + +void SetSerialPort(serial_port* new_port) { + port = new_port; +} + +serial_port* GetSerialPort() { + return port; +} + +void SetOffline(bool new_offline) { + offline = new_offline; +} + +bool IsOffline() { + return offline; +} + +void SendCommand(UsbCommand *c) { + #ifdef COMMS_DEBUG + printf("Sending %04x cmd\n", c->cmd); + #endif + + if (offline) { + PrintAndLog("Sending bytes to proxmark failed - offline"); + return; + } + /** + The while-loop below causes hangups at times, when the pm3 unit is unresponsive + or disconnected. The main console thread is alive, but comm thread just spins here. + Not good.../holiman + **/ + while(txcmd_pending); + txcmd = *c; + txcmd_pending = true; +} + +/** + * @brief This method should be called when sending a new command to the pm3. In case any old + * responses from previous commands are stored in the buffer, a call to this method should clear them. + * A better method could have been to have explicit command-ACKS, so we can know which ACK goes to which + * operation. Right now we'll just have to live with this. + */ +void clearCommandBuffer() +{ + //This is a very simple operation + pthread_mutex_lock(&cmdBufferMutex); + cmd_tail = cmd_head; + pthread_mutex_unlock(&cmdBufferMutex); +} + +/** + * @brief storeCommand stores a USB command in a circular buffer + * @param UC + */ +void storeCommand(UsbCommand *command) +{ + pthread_mutex_lock(&cmdBufferMutex); + if( (cmd_head+1) % CMD_BUFFER_SIZE == cmd_tail) + { + // If these two are equal, we're about to overwrite in the + // circular buffer. + PrintAndLog("WARNING: Command buffer about to overwrite command! This needs to be fixed!"); + } + + // Store the command at the 'head' location + UsbCommand* destination = &cmdBuffer[cmd_head]; + memcpy(destination, command, sizeof(UsbCommand)); + + cmd_head = (cmd_head +1) % CMD_BUFFER_SIZE; //increment head and wrap + pthread_mutex_unlock(&cmdBufferMutex); +} + + +/** + * @brief getCommand gets a command from an internal circular buffer. + * @param response location to write command + * @return 1 if response was returned, 0 if nothing has been received + */ +int getCommand(UsbCommand* response) +{ + pthread_mutex_lock(&cmdBufferMutex); + //If head == tail, there's nothing to read, or if we just got initialized + if (cmd_head == cmd_tail){ + pthread_mutex_unlock(&cmdBufferMutex); + return 0; + } + + //Pick out the next unread command + UsbCommand* last_unread = &cmdBuffer[cmd_tail]; + memcpy(response, last_unread, sizeof(UsbCommand)); + //Increment tail - this is a circular buffer, so modulo buffer size + cmd_tail = (cmd_tail + 1) % CMD_BUFFER_SIZE; + + pthread_mutex_unlock(&cmdBufferMutex); + return 1; +} + +//----------------------------------------------------------------------------- +// Entry point into our code: called whenever we received a packet over USB +// that we weren't necessarily expecting, for example a debug print. +//----------------------------------------------------------------------------- +void UsbCommandReceived(UsbCommand *UC) +{ + switch(UC->cmd) { + + // First check if we are handling a debug message + case CMD_DEBUG_PRINT_STRING: { + char s[USB_CMD_DATA_SIZE+1]; + memset(s, 0x00, sizeof(s)); + size_t len = MIN(UC->arg[0],USB_CMD_DATA_SIZE); + memcpy(s,UC->d.asBytes,len); + PrintAndLog("#db# %s", s); + return; + } + + case CMD_DEBUG_PRINT_INTEGERS: { + PrintAndLog("#db# %08x, %08x, %08x \r\n", UC->arg[0], UC->arg[1], UC->arg[2]); + return; + } + + case CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K: { + // FIXME: This does unsanitised copies into memory when we don't know + // the size of the buffer. + if (sample_buf) { + memcpy(sample_buf+(UC->arg[0]),UC->d.asBytes,UC->arg[1]); + } + return; + } + + default: { + storeCommand(UC); + return; + } + } +} + +// Gets a single command from a proxmark3 device. This should never be used +// with the full client. +// +// @param conn A receiver_arg structure. +// @param command A buffer to store the received command. +bool ReceiveCommand(receiver_arg* conn, UsbCommand* command) { + // Local recieve buffer + size_t rxlen; + byte_t rx[sizeof(UsbCommand)]; + byte_t* prx = rx; + + while (conn->run) { + rxlen = 0; + if (uart_receive(port, prx, sizeof(UsbCommand) - (prx-rx), &rxlen) && rxlen) { + prx += rxlen; + if (prx-rx < sizeof(UsbCommand)) { + // Keep reading until we have a completed response. + continue; + } + + // We have a completed response. + memcpy(command, rx, sizeof(UsbCommand)); + return true; + } + + if (prx == rx) { + // We got no complete command while waiting, give up control + return false; + } + } + + // did not get a complete command before being cancelled. + return false; +} + +// Worker thread for processing incoming events from the PM3 +void *uart_receiver(void *targ) { + receiver_arg *conn = (receiver_arg*)targ; + UsbCommand rx; + + while (conn->run) { + #ifdef COMMS_DEBUG + printf("uart_receiver: get lock\n"); + #endif + // Lock up receives, in case they try to take it away from us. + pthread_mutex_lock(&conn->recv_lock); + #ifdef COMMS_DEBUG + printf("uart_receiver: lock acquired\n"); + #endif + + if (port == NULL) { + #ifdef COMMS_DEBUG + printf("uart_receiver: port disappeared\n"); + #endif + // Our port disappeared, stall. This code path matters for the flasher, + // where it is fiddling with the serial port under us. + pthread_mutex_unlock(&conn->recv_lock); + msleep(10); + continue; + } + + bool got_command = ReceiveCommand(conn, &rx); + #ifdef COMMS_DEBUG + printf("uart_receiver: got command\n"); + #endif + pthread_mutex_unlock(&conn->recv_lock); + + if (got_command) { + UsbCommandReceived(&rx); + } + + // We aren't normally trying to transmit in the flasher when the port would + // be reset, so we can just keep going at this point. + if (txcmd_pending) { + if (!uart_send(port, (byte_t*) &txcmd, sizeof(UsbCommand))) { + PrintAndLog("Sending bytes to proxmark failed"); + } + txcmd_pending = false; + } + } + + pthread_exit(NULL); + return NULL; +} + +/** + * Waits for a certain response type. This method waits for a maximum of + * ms_timeout milliseconds for a specified response command. + *@brief WaitForResponseTimeout + * @param cmd command to wait for, or CMD_ANY to take any command. + * @param response struct to copy received command into. + * @param ms_timeout + * @param show_warning + * @return true if command was returned, otherwise false + */ +bool WaitForResponseTimeoutW(uint64_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning) { + UsbCommand resp; + + #ifdef COMMS_DEBUG + printf("Waiting for %04x cmd\n", cmd); + #endif + + if (response == NULL) { + response = &resp; + } + + uint64_t start_time = msclock(); + + // Wait until the command is received + for (;;) { + while(getCommand(response)) { + if (cmd == CMD_ANY || response->cmd == cmd) { + return true; + } + } + + if (msclock() - start_time > ms_timeout) { + // We timed out. + break; + } + + if (msclock() - start_time > 2000 && show_warning) { + // 2 seconds elapsed (but this doesn't mean the timeout was exceeded) + PrintAndLog("Waiting for a response from the proxmark..."); + PrintAndLog("Don't forget to cancel its operation first by pressing on the button"); + break; + } + } + return false; +} + +bool WaitForResponseTimeout(uint64_t cmd, UsbCommand* response, size_t ms_timeout) { + return WaitForResponseTimeoutW(cmd, response, ms_timeout, true); +} + +bool WaitForResponse(uint64_t cmd, UsbCommand* response) { + return WaitForResponseTimeout(cmd, response, -1); +} diff --git a/client/comms.h b/client/comms.h new file mode 100644 index 00000000..75adeea1 --- /dev/null +++ b/client/comms.h @@ -0,0 +1,45 @@ +#ifndef COMMS_H_ +#define COMMS_H_ + +#include +#include + +#include "usb_cmd.h" +#include "uart.h" + +#ifndef CMD_BUFFER_SIZE +#define CMD_BUFFER_SIZE 50 +#endif + +#ifndef MAX_DEMOD_BUF_LEN +#define MAX_DEMOD_BUF_LEN (1024*128) +#endif + +#ifndef BIGBUF_SIZE +#define BIGBUF_SIZE 40000 +#endif + +typedef struct { + // If TRUE, continue running the uart_receiver thread. + bool run; + + // Lock around serial port receives + pthread_mutex_t recv_lock; +} receiver_arg; + + +// Wrappers required as static variables can only be used in one file. +void SetSerialPort(serial_port* new_port); +serial_port* GetSerialPort(); +void SetOffline(bool new_offline); +bool IsOffline(); + +void SendCommand(UsbCommand *c); +void *uart_receiver(void *targ); +void UsbCommandReceived(UsbCommand *UC); +void clearCommandBuffer(); +bool WaitForResponseTimeoutW(uint64_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning); +bool WaitForResponseTimeout(uint64_t cmd, UsbCommand* response, size_t ms_timeout); +bool WaitForResponse(uint64_t cmd, UsbCommand* response); + +#endif // COMMS_H_ diff --git a/client/flash.c b/client/flash.c index 7622e8a5..bf01b911 100644 --- a/client/flash.c +++ b/client/flash.c @@ -13,6 +13,7 @@ #include #include #include + #include "proxmark3.h" #include "util.h" #include "util_posix.h" @@ -20,11 +21,7 @@ #include "elf.h" #include "proxendian.h" #include "usb_cmd.h" - -void SendCommand(UsbCommand* txcmd); -void ReceiveCommand(UsbCommand* rxcmd); -void CloseProxmark(); -int OpenProxmark(size_t i); +#include "comms.h" // FIXME: what the fuckity fuck unsigned int current_command = CMD_UNKNOWN; @@ -44,6 +41,32 @@ static const uint8_t elf_ident[] = { EV_CURRENT }; +void CloseProxmark(receiver_arg* conn, char* serial_port_name) { + pthread_mutex_lock(&conn->recv_lock); + + // Block the port from being used by anything + serial_port* my_port = GetSerialPort(); + SetSerialPort(NULL); + + // Then close the port. + uart_close(my_port); + pthread_mutex_unlock(&conn->recv_lock); + + // Fix for linux, it seems that it is extremely slow to release the serial port file descriptor /dev/* + unlink(serial_port_name); +} + +bool OpenProxmark(char* serial_port_name) { + serial_port *new_port = uart_open(serial_port_name); + if (new_port == INVALID_SERIAL_PORT || new_port == CLAIMED_SERIAL_PORT) { + //poll once a second + return false; + } + + SetSerialPort(new_port); + return true; +} + // Turn PHDRs into flasher segments, checking for PHDR sanity and merging adjacent // unaligned segments if needed static int build_segs_from_phdrs(flash_file_t *ctx, FILE *fd, Elf32_Phdr *phdrs, int num_phdrs) @@ -278,9 +301,12 @@ static int get_proxmark_state(uint32_t *state) { UsbCommand c; c.cmd = CMD_DEVICE_INFO; - SendCommand(&c); + SendCommand(&c); UsbCommand resp; - ReceiveCommand(&resp); + while (!WaitForResponse(CMD_ANY, &resp)) { + // Keep waiting for a response + msleep(100); + } // Three outcomes: // 1. The old bootrom code will ignore CMD_DEVICE_INFO, but respond with an ACK @@ -307,7 +333,7 @@ static int get_proxmark_state(uint32_t *state) } // Enter the bootloader to be able to start flashing -static int enter_bootloader(char *serial_port_name) +static int enter_bootloader(receiver_arg* conn, char *serial_port_name) { uint32_t state; @@ -338,16 +364,17 @@ static int enter_bootloader(char *serial_port_name) SendCommand(&c); fprintf(stderr,"Press and hold down button NOW if your bootloader requires it.\n"); } - msleep(100); - CloseProxmark(); + + msleep(100); + CloseProxmark(conn, serial_port_name); fprintf(stderr,"Waiting for Proxmark to reappear on %s",serial_port_name); - do { + do { sleep(1); fprintf(stderr, "."); - } while (!OpenProxmark(0)); + } while (!OpenProxmark(serial_port_name)); + fprintf(stderr," Found.\n"); - return 0; } @@ -355,23 +382,25 @@ static int enter_bootloader(char *serial_port_name) return -1; } -static int wait_for_ack(void) +static int wait_for_ack() { - UsbCommand ack; - ReceiveCommand(&ack); - if (ack.cmd != CMD_ACK) { - printf("Error: Unexpected reply 0x%04" PRIx64 " (expected ACK)\n", ack.cmd); + UsbCommand resp; + while (!WaitForResponse(CMD_ANY, &resp)) { + msleep(100); + } + if (resp.cmd != CMD_ACK) { + printf("Error: Unexpected reply 0x%04" PRIx64 " (expected ACK)\n", resp.cmd); return -1; } return 0; } // Go into flashing mode -int flash_start_flashing(int enable_bl_writes,char *serial_port_name) +int flash_start_flashing(receiver_arg* conn, int enable_bl_writes,char *serial_port_name) { uint32_t state; - if (enter_bootloader(serial_port_name) < 0) + if (enter_bootloader(conn, serial_port_name) < 0) return -1; if (get_proxmark_state(&state) < 0) @@ -470,9 +499,9 @@ void flash_free(flash_file_t *ctx) } // just reset the unit -int flash_stop_flashing(void) { +int flash_stop_flashing() { UsbCommand c = {CMD_HARDWARE_RESET}; - SendCommand(&c); - msleep(100); - return 0; + SendCommand(&c); + msleep(100); + return 0; } diff --git a/client/flash.h b/client/flash.h index 3e9f77a7..279062af 100644 --- a/client/flash.h +++ b/client/flash.h @@ -11,6 +11,7 @@ #include #include "elf.h" +#include "comms.h" typedef struct { void *data; @@ -26,10 +27,12 @@ typedef struct { } flash_file_t; int flash_load(flash_file_t *ctx, const char *name, int can_write_bl); -int flash_start_flashing(int enable_bl_writes,char *serial_port_name); +int flash_start_flashing(receiver_arg* conn, int enable_bl_writes, char *serial_port_name); int flash_write(flash_file_t *ctx); void flash_free(flash_file_t *ctx); int flash_stop_flashing(void); +void CloseProxmark(receiver_arg* conn, char* serial_port_name); +bool OpenProxmark(char* serial_port_name); #endif diff --git a/client/flasher.c b/client/flasher.c index f257d994..40e40524 100644 --- a/client/flasher.c +++ b/client/flasher.c @@ -10,12 +10,15 @@ #include #include #include +#include + #include "proxmark3.h" #include "util.h" #include "util_posix.h" #include "flash.h" #include "uart.h" #include "usb_cmd.h" +#include "comms.h" #ifdef _WIN32 # define unlink(x) @@ -23,9 +26,6 @@ # include #endif -static serial_port sp; -static char* serial_port_name; - void cmd_debug(UsbCommand* UC) { // Debug printf("UsbCommand length[len=%zd]\n",sizeof(UsbCommand)); @@ -40,45 +40,6 @@ void cmd_debug(UsbCommand* UC) { printf("...\n"); } -void SendCommand(UsbCommand* txcmd) { -// printf("send: "); -// cmd_debug(txcmd); - if (!uart_send(sp,(byte_t*)txcmd,sizeof(UsbCommand))) { - printf("Sending bytes to proxmark failed\n"); - exit(1); - } -} - -void ReceiveCommand(UsbCommand* rxcmd) { - byte_t* prxcmd = (byte_t*)rxcmd; - byte_t* prx = prxcmd; - size_t rxlen; - while (true) { - if (uart_receive(sp, prx, sizeof(UsbCommand) - (prx-prxcmd), &rxlen)) { - prx += rxlen; - if ((prx-prxcmd) >= sizeof(UsbCommand)) { - return; - } - } - } -} - -void CloseProxmark() { - // Clean up the port - uart_close(sp); - // Fix for linux, it seems that it is extremely slow to release the serial port file descriptor /dev/* - unlink(serial_port_name); -} - -int OpenProxmark(size_t i) { - sp = uart_open(serial_port_name); - if (sp == INVALID_SERIAL_PORT || sp == CLAIMED_SERIAL_PORT) { - //poll once a second - return 0; - } - return 1; -} - static void usage(char *argv0) { fprintf(stderr, "Usage: %s [-b] image.elf [image.elf...]\n\n", argv0); @@ -86,9 +47,10 @@ static void usage(char *argv0) //Is the example below really true? /Martin fprintf(stderr, "Example:\n\n\t %s path/to/osimage.elf path/to/fpgaimage.elf\n", argv0); fprintf(stderr, "\nExample (Linux):\n\n\t %s /dev/ttyACM0 armsrc/obj/fullimage.elf\n", argv0); - fprintf(stderr, "\nNote (Linux): if the flasher gets stuck in 'Waiting for Proxmark to reappear on ',\n"); - fprintf(stderr, " you need to blacklist proxmark for modem-manager - see wiki for more details:\n"); - fprintf(stderr, " http://code.google.com/p/proxmark3/wiki/Linux\n\n"); + fprintf(stderr, "\nNote (Linux): if the flasher gets stuck at 'Waiting for Proxmark to reappear',\n"); + fprintf(stderr, " you may need to blacklist proxmark for modem-manager. v1.4.14 and later\n"); + fprintf(stderr, " include this configuration patch already. The change can be found at:\n"); + fprintf(stderr, " https://cgit.freedesktop.org/ModemManager/ModemManager/commit/?id=6e7ff47\n\n"); } #define MAX_FILES 4 @@ -99,7 +61,10 @@ int main(int argc, char **argv) int num_files = 0; int res; flash_file_t files[MAX_FILES]; + receiver_arg conn; + pthread_t reader_thread; + memset(&conn, 0, sizeof(receiver_arg)); memset(files, 0, sizeof(files)); if (argc < 3) { @@ -126,16 +91,22 @@ int main(int argc, char **argv) } } - serial_port_name = argv[1]; - - fprintf(stderr,"Waiting for Proxmark to appear on %s",serial_port_name); - do { - msleep(1000); - fprintf(stderr, "."); - } while (!OpenProxmark(0)); - fprintf(stderr," Found.\n"); + pthread_mutex_init(&conn.recv_lock, NULL); - res = flash_start_flashing(can_write_bl,serial_port_name); + char* serial_port_name = argv[1]; + + fprintf(stderr,"Waiting for Proxmark to appear on %s", serial_port_name); + do { + sleep(1); + fprintf(stderr, "."); + } while (!OpenProxmark(serial_port_name)); + fprintf(stderr," Found.\n"); + + // Lets start up the communications thread + conn.run = true; + pthread_create(&reader_thread, NULL, &uart_receiver, &conn); + + res = flash_start_flashing(&conn, can_write_bl, serial_port_name); if (res < 0) return -1; @@ -155,7 +126,11 @@ int main(int argc, char **argv) if (res < 0) return -1; - CloseProxmark(); + // Stop the command thread. + conn.run = false; + pthread_join(reader_thread, NULL); + CloseProxmark(&conn, serial_port_name); + pthread_mutex_destroy(&conn.recv_lock); fprintf(stderr, "All done.\n\n"); fprintf(stderr, "Have a nice day!\n"); diff --git a/client/proxgui.cpp b/client/proxgui.cpp index e899174c..6e4ec98e 100644 --- a/client/proxgui.cpp +++ b/client/proxgui.cpp @@ -11,11 +11,15 @@ #include "proxgui.h" #include "proxguiqt.h" #include "proxmark3.h" +#include "uart.h" static ProxGuiQT *gui = NULL; static WorkerThread *main_loop_thread = NULL; -WorkerThread::WorkerThread(char *script_cmds_file, char *script_cmd, bool usb_present) : script_cmds_file(script_cmds_file), script_cmd(script_cmd), usb_present(usb_present) +WorkerThread::WorkerThread(char *script_cmds_file, char *script_cmd, + bool usb_present, serial_port* sp) + : script_cmds_file(script_cmds_file), script_cmd(script_cmd), + usb_present(usb_present), sp(sp) { } @@ -24,7 +28,7 @@ WorkerThread::~WorkerThread() } void WorkerThread::run() { - main_loop(script_cmds_file, script_cmd, usb_present); + main_loop(script_cmds_file, script_cmd, usb_present, sp); } extern "C" void ShowGraphWindow(void) @@ -59,7 +63,9 @@ extern "C" void MainGraphics(void) gui->MainLoop(); } -extern "C" void InitGraphics(int argc, char **argv, char *script_cmds_file, char *script_cmd, bool usb_present) +extern "C" void InitGraphics(int argc, char **argv, char *script_cmds_file, + char *script_cmd, bool usb_present, + serial_port* sp) { #ifdef Q_WS_X11 bool useGUI = getenv("DISPLAY") != 0; @@ -69,7 +75,7 @@ extern "C" void InitGraphics(int argc, char **argv, char *script_cmds_file, char if (!useGUI) return; - main_loop_thread = new WorkerThread(script_cmds_file, script_cmd, usb_present); + main_loop_thread = new WorkerThread(script_cmds_file, script_cmd, usb_present, sp); gui = new ProxGuiQT(argc, argv, main_loop_thread); } diff --git a/client/proxgui.h b/client/proxgui.h index 77bcbf01..ea3abf32 100644 --- a/client/proxgui.h +++ b/client/proxgui.h @@ -14,12 +14,13 @@ extern "C" { #include #include +#include "uart.h" void ShowGraphWindow(void); void HideGraphWindow(void); void RepaintGraphWindow(void); void MainGraphics(void); -void InitGraphics(int argc, char **argv, char *script_cmds_file, char *script_cmd, bool usb_present); +void InitGraphics(int argc, char **argv, char *script_cmds_file, char *script_cmd, bool usb_present, serial_port* sp); void ExitGraphics(void); #define MAX_GRAPH_TRACE_LEN (40000*8) @@ -30,7 +31,6 @@ extern int s_Buff[MAX_GRAPH_TRACE_LEN]; extern double CursorScaleFactor; extern int PlotGridX, PlotGridY, PlotGridXdefault, PlotGridYdefault, CursorCPos, CursorDPos, GridOffset; extern int CommandFinished; -extern int offline; extern bool GridLocked; //Operations defined in data_operations diff --git a/client/proxguiqt.h b/client/proxguiqt.h index 45a65b04..4b2b2a27 100644 --- a/client/proxguiqt.h +++ b/client/proxguiqt.h @@ -21,6 +21,7 @@ #include #include +#include "uart.h" #include "ui/ui_overlays.h" /** * @brief The actual plot, black area were we paint the graph @@ -91,13 +92,14 @@ class ProxWidget : public QWidget class WorkerThread : public QThread { Q_OBJECT; public: - WorkerThread(char*, char*, bool); + WorkerThread(char*, char*, bool, serial_port*); ~WorkerThread(); void run(); private: char *script_cmds_file = NULL; char *script_cmd = NULL; bool usb_present; + serial_port *sp = NULL; }; class ProxGuiQT : public QObject diff --git a/client/proxmark3.c b/client/proxmark3.c index 99ba9fba..0acf34f6 100644 --- a/client/proxmark3.c +++ b/client/proxmark3.c @@ -27,86 +27,42 @@ #include "cmdparser.h" #include "cmdhw.h" #include "whereami.h" +#include "comms.h" #ifdef _WIN32 #define SERIAL_PORT_H "com3" +#elif __APPLE__ +#define SERIAL_PORT_H "/dev/tty.usbmodem*" #else #define SERIAL_PORT_H "/dev/ttyACM0" #endif -// a global mutex to prevent interlaced printing from different threads -pthread_mutex_t print_lock; - -static serial_port sp; -static UsbCommand txcmd; -volatile static bool txcmd_pending = false; - -void SendCommand(UsbCommand *c) { - #if 0 - printf("Sending %d bytes\n", sizeof(UsbCommand)); - #endif - - if (offline) { - PrintAndLog("Sending bytes to proxmark failed - offline"); - return; - } - /** - The while-loop below causes hangups at times, when the pm3 unit is unresponsive - or disconnected. The main console thread is alive, but comm thread just spins here. - Not good.../holiman - **/ - while(txcmd_pending); - txcmd = *c; - txcmd_pending = true; -} - -struct receiver_arg { - int run; -}; - -byte_t rx[sizeof(UsbCommand)]; -byte_t* prx = rx; - -static void *uart_receiver(void *targ) { - struct receiver_arg *arg = (struct receiver_arg*)targ; - size_t rxlen; - - while (arg->run) { - rxlen = 0; - if (uart_receive(sp, prx, sizeof(UsbCommand) - (prx-rx), &rxlen) && rxlen) { - prx += rxlen; - if (prx-rx < sizeof(UsbCommand)) { - continue; - } - UsbCommandReceived((UsbCommand*)rx); - } - prx = rx; - - if(txcmd_pending) { - if (!uart_send(sp, (byte_t*) &txcmd, sizeof(UsbCommand))) { - PrintAndLog("Sending bytes to proxmark failed"); - } - txcmd_pending = false; - } - } - - pthread_exit(NULL); - return NULL; -} - - -void main_loop(char *script_cmds_file, char *script_cmd, bool usb_present) { - struct receiver_arg rarg; +void main_loop(char *script_cmds_file, char* script_cmd, bool usb_present, serial_port* sp) { + receiver_arg conn; char *cmd = NULL; pthread_t reader_thread; bool execCommand = (script_cmd != NULL); bool stdinOnPipe = !isatty(STDIN_FILENO); - + + memset(&conn, 0, sizeof(receiver_arg)); + pthread_mutex_init(&conn.recv_lock, NULL); + + + // TODO: Move this into comms.c + PlotGridXdefault = 64; + PlotGridYdefault = 64; + showDemod = true; + CursorScaleFactor = 1; + if (usb_present) { - rarg.run = 1; - pthread_create(&reader_thread, NULL, &uart_receiver, &rarg); + conn.run = true; + SetSerialPort(sp); + SetOffline(false); + pthread_create(&reader_thread, NULL, &uart_receiver, &conn); // cache Version information now: CmdVersion(NULL); + } else { + SetOffline(true); } // file with script @@ -122,7 +78,7 @@ void main_loop(char *script_cmds_file, char *script_cmd, bool usb_present) { read_history(".history"); - while(1) { + while (1) { // If there is a script file if (script_file) { @@ -194,7 +150,7 @@ void main_loop(char *script_cmds_file, char *script_cmd, bool usb_present) { write_history(".history"); if (usb_present) { - rarg.run = 0; + conn.run = false; pthread_join(reader_thread, NULL); } @@ -202,6 +158,8 @@ void main_loop(char *script_cmds_file, char *script_cmd, bool usb_present) { fclose(script_file); script_file = NULL; } + + pthread_mutex_destroy(&conn.recv_lock); } static void dumpAllHelp(int markdown) @@ -274,6 +232,8 @@ int main(int argc, char* argv[]) { bool addLuaExec = false; char *script_cmds_file = NULL; char *script_cmd = NULL; + serial_port *sp = NULL; + g_debugMode = 0; if (argc < 2) { show_help(true, argv[0]); @@ -370,14 +330,11 @@ int main(int argc, char* argv[]) { if (sp == INVALID_SERIAL_PORT) { printf("ERROR: invalid serial port\n"); usb_present = false; - offline = 1; } else if (sp == CLAIMED_SERIAL_PORT) { printf("ERROR: serial port is claimed by another process\n"); usb_present = false; - offline = 1; } else { usb_present = true; - offline = 0; } // create a mutex to avoid interlacing print commands from our different threads @@ -385,23 +342,23 @@ int main(int argc, char* argv[]) { #ifdef HAVE_GUI #ifdef _WIN32 - InitGraphics(argc, argv, script_cmds_file, script_cmd, usb_present); + InitGraphics(argc, argv, script_cmds_file, script_cmd, usb_present, sp); MainGraphics(); #else char* display = getenv("DISPLAY"); if (display && strlen(display) > 1) { - InitGraphics(argc, argv, script_cmds_file, script_cmd, usb_present); + InitGraphics(argc, argv, script_cmds_file, script_cmd, usb_present, sp); MainGraphics(); } else { - main_loop(script_cmds_file, script_cmd, usb_present); + main_loop(script_cmds_file, script_cmd, usb_present, sp); } #endif #else - main_loop(script_cmds_file, script_cmd, usb_present); + main_loop(script_cmds_file, script_cmd, usb_present, sp); #endif // Clean up the port diff --git a/client/proxmark3.h b/client/proxmark3.h index c6185c43..11b8aabd 100644 --- a/client/proxmark3.h +++ b/client/proxmark3.h @@ -13,6 +13,7 @@ #define PROXMARK3_H__ #include "usb_cmd.h" +#include "uart.h" #define PROXPROMPT "proxmark3> " @@ -23,7 +24,7 @@ extern "C" { void SendCommand(UsbCommand *c); const char *get_my_executable_path(void); const char *get_my_executable_directory(void); -void main_loop(char *script_cmds_file, char *script_cmd, bool usb_present); +void main_loop(char *script_cmds_file, char *script_cmd, bool usb_present, serial_port* sp); #ifdef __cplusplus } diff --git a/client/ui.c b/client/ui.c index df2c3ce3..48869fd1 100644 --- a/client/ui.c +++ b/client/ui.c @@ -21,7 +21,7 @@ double CursorScaleFactor = 1; int PlotGridX=0, PlotGridY=0, PlotGridXdefault= 64, PlotGridYdefault= 64, CursorCPos= 0, CursorDPos= 0; int offline; -int flushAfterWrite = 0; //buzzy +bool flushAfterWrite = false; //buzzy int GridOffset = 0; bool GridLocked = false; bool showDemod = true; @@ -62,7 +62,6 @@ void PrintAndLog(char *fmt, ...) } #else // We are using libedit (OSX), which doesn't support this flag. - int need_hack = 0; #endif va_start(argptr, fmt); @@ -72,6 +71,9 @@ void PrintAndLog(char *fmt, ...) va_end(argptr); printf("\n"); + // This needs to be wrapped in ifdefs, as this if optimisation is disabled, + // this block won't be removed, and it'll fail at the linker. +#ifdef RL_STATE_READCMD if (need_hack) { rl_restore_prompt(); rl_replace_line(saved_line, 0); @@ -79,6 +81,7 @@ void PrintAndLog(char *fmt, ...) rl_redisplay(); free(saved_line); } +#endif if (logging && logfile) { vfprintf(logfile, fmt, argptr2); @@ -100,3 +103,8 @@ void SetLogFilename(char *fn) { logfilename = fn; } + +void SetFlushAfterWrite(bool flush_after_write) { + flushAfterWrite = flush_after_write; +} + diff --git a/client/ui.h b/client/ui.h index 4049033d..2986efaf 100644 --- a/client/ui.h +++ b/client/ui.h @@ -13,6 +13,11 @@ #include #include +#include + +// a global mutex to prevent interlaced printing from different threads +pthread_mutex_t print_lock; +extern uint8_t g_debugMode; void ShowGui(void); void HideGraphWindow(void); @@ -23,8 +28,7 @@ void SetLogFilename(char *fn); extern double CursorScaleFactor; extern int PlotGridX, PlotGridY, PlotGridXdefault, PlotGridYdefault, CursorCPos, CursorDPos, GridOffset; -extern int offline; -extern int flushAfterWrite; //buzzy +extern bool flushAfterWrite; //buzzy extern bool GridLocked; extern bool showDemod; diff --git a/common/iso15693tools.c b/common/iso15693tools.c index 26e636ca..cf7d5341 100644 --- a/common/iso15693tools.c +++ b/common/iso15693tools.c @@ -50,8 +50,9 @@ int Iso15693AddCrc(uint8_t *req, int n) { return n+2; } - +#ifdef ON_DEVICE int sprintf(char *str, const char *format, ...); +#endif // returns a string representation of the UID // UID is transmitted and stored LSB first, displayed MSB first diff --git a/include/usb_cmd.h b/include/usb_cmd.h index 194a9d53..df2923a0 100644 --- a/include/usb_cmd.h +++ b/include/usb_cmd.h @@ -212,7 +212,7 @@ typedef struct{ #define CMD_HF_SNIFFER 0x0800 #define CMD_UNKNOWN 0xFFFF - +#define CMD_ANY 0xFFFFFFFFFFFFFFFF //Mifare simulation flags #define FLAG_INTERACTIVE 0x01 From 3851172d8168f32f1dc28b48b7a4df88e30bdeca Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Fri, 27 Oct 2017 09:56:46 +0200 Subject: [PATCH 034/310] Revert "Comms refactor (prerequisite of libproxmark work) (#371)" (#450) This reverts commit afdcb8c159a73aba95a017f1cfec98e8fa2b93c1. --- client/Makefile | 10 +- client/cmdlf.c | 8 +- client/cmdmain.c | 152 ++++++++++++++++++- client/cmdmain.h | 7 +- client/cmdparser.c | 3 +- client/comms.c | 325 ----------------------------------------- client/comms.h | 45 ------ client/flash.c | 77 +++------- client/flash.h | 5 +- client/flasher.c | 85 +++++++---- client/proxgui.cpp | 14 +- client/proxgui.h | 4 +- client/proxguiqt.h | 4 +- client/proxmark3.c | 107 ++++++++++---- client/proxmark3.h | 3 +- client/ui.c | 12 +- client/ui.h | 8 +- common/iso15693tools.c | 3 +- include/usb_cmd.h | 2 +- 19 files changed, 334 insertions(+), 540 deletions(-) delete mode 100644 client/comms.c delete mode 100644 client/comms.h diff --git a/client/Makefile b/client/Makefile index e14939e3..3a96e9e9 100644 --- a/client/Makefile +++ b/client/Makefile @@ -21,7 +21,7 @@ LDLIBS = -L/opt/local/lib -L/usr/local/lib -lreadline -lpthread -lm LUALIB = ../liblua/liblua.a LDFLAGS = $(ENV_LDFLAGS) CFLAGS = $(ENV_CFLAGS) -std=c99 -D_ISOC99_SOURCE -I. -I../include -I../common -I../zlib -I../uart -I/opt/local/include -I../liblua -Wall -g -O3 -CXXFLAGS = -I../include -I../uart -Wall -O3 +CXXFLAGS = -I../include -Wall -O3 LUAPLATFORM = generic platform = $(shell uname) @@ -82,10 +82,7 @@ POSTCOMPILE = $(MV) -f $(OBJDIR)/$*.Td $(OBJDIR)/$*.d CORESRCS = uart_posix.c \ uart_win32.c \ util.c \ - util_posix.c \ - comms.c \ - data.c \ - ui.c + util_posix.c CMDSRCS = crapto1/crapto1.c\ crapto1/crypto1.c\ @@ -105,7 +102,9 @@ CMDSRCS = crapto1/crapto1.c\ crc64.c \ iso14443crc.c \ iso15693tools.c \ + data.c \ graph.c \ + ui.c \ cmddata.c \ lfdemod.c \ cmdhf.c \ @@ -176,7 +175,6 @@ ZLIBFLAGS = -DZ_SOLO -DZ_PREFIX -DNO_GZIP -DZLIB_PM3_TUNED #-DDEBUG -Dverbose=1 QTGUISRCS = proxgui.cpp proxguiqt.cpp proxguiqt.moc.cpp guidummy.cpp -NOGUISRCS = guidummy.cpp COREOBJS = $(CORESRCS:%.c=$(OBJDIR)/%.o) CMDOBJS = $(CMDSRCS:%.c=$(OBJDIR)/%.o) diff --git a/client/cmdlf.c b/client/cmdlf.c index 501bfaed..eb664a11 100644 --- a/client/cmdlf.c +++ b/client/cmdlf.c @@ -335,7 +335,7 @@ int CmdLFSetConfig(const char *Cmd) } bool lf_read(bool silent, uint32_t samples) { - if (IsOffline()) return false; + if (offline) return false; UsbCommand c = {CMD_ACQUIRE_RAW_ADC_SAMPLES_125K, {silent,samples,0}}; clearCommandBuffer(); //And ship it to device @@ -878,7 +878,7 @@ int CmdVchDemod(const char *Cmd) int CheckChipType(char cmdp) { uint32_t wordData = 0; - if (IsOffline() || cmdp == '1') return 0; + if (offline || cmdp == '1') return 0; save_restoreGB(GRAPH_SAVE); save_restoreDB(GRAPH_SAVE); @@ -923,7 +923,7 @@ int CmdLFfind(const char *Cmd) return 0; } - if (!IsOffline() && (cmdp != '1')) { + if (!offline && (cmdp != '1')) { lf_read(true, 30000); } else if (GraphTraceLen < minLength) { PrintAndLog("Data in Graphbuffer was too small."); @@ -939,7 +939,7 @@ int CmdLFfind(const char *Cmd) // only run if graphbuffer is just noise as it should be for hitag/cotag if (graphJustNoise(GraphBuffer, testLen)) { // only run these tests if we are in online mode - if (!IsOffline() && (cmdp != '1')) { + if (!offline && (cmdp != '1')) { // test for em4x05 in reader talk first mode. if (EM4x05Block0Test(&wordData)) { PrintAndLog("\nValid EM4x05/EM4x69 Chip Found\nUse lf em 4x05readword/dump commands to read\n"); diff --git a/client/cmdmain.c b/client/cmdmain.c index e199e127..739d68e1 100644 --- a/client/cmdmain.c +++ b/client/cmdmain.c @@ -36,6 +36,15 @@ static int CmdHelp(const char *Cmd); static int CmdQuit(const char *Cmd); static int CmdRev(const char *Cmd); +//For storing command that are received from the device +#define CMD_BUFFER_SIZE 50 +static UsbCommand cmdBuffer[CMD_BUFFER_SIZE]; +//Points to the next empty position to write to +static int cmd_head;//Starts as 0 +//Points to the position of the last unread command +static int cmd_tail;//Starts as 0 +// to lock cmdBuffer operations from different threads +static pthread_mutex_t cmdBufferMutex = PTHREAD_MUTEX_INITIALIZER; static command_t CommandTable[] = { @@ -55,7 +64,6 @@ command_t* getTopLevelCommandTable() { return CommandTable; } - int CmdHelp(const char *Cmd) { CmdsHelp(CommandTable); @@ -73,6 +81,113 @@ int CmdRev(const char *Cmd) return 0; } +/** + * @brief This method should be called when sending a new command to the pm3. In case any old + * responses from previous commands are stored in the buffer, a call to this method should clear them. + * A better method could have been to have explicit command-ACKS, so we can know which ACK goes to which + * operation. Right now we'll just have to live with this. + */ +void clearCommandBuffer() +{ + //This is a very simple operation + pthread_mutex_lock(&cmdBufferMutex); + cmd_tail = cmd_head; + pthread_mutex_unlock(&cmdBufferMutex); +} + +/** + * @brief storeCommand stores a USB command in a circular buffer + * @param UC + */ +void storeCommand(UsbCommand *command) +{ + pthread_mutex_lock(&cmdBufferMutex); + if( ( cmd_head+1) % CMD_BUFFER_SIZE == cmd_tail) + { + //If these two are equal, we're about to overwrite in the + // circular buffer. + PrintAndLog("WARNING: Command buffer about to overwrite command! This needs to be fixed!"); + } + //Store the command at the 'head' location + UsbCommand* destination = &cmdBuffer[cmd_head]; + memcpy(destination, command, sizeof(UsbCommand)); + + cmd_head = (cmd_head +1) % CMD_BUFFER_SIZE; //increment head and wrap + pthread_mutex_unlock(&cmdBufferMutex); +} + + +/** + * @brief getCommand gets a command from an internal circular buffer. + * @param response location to write command + * @return 1 if response was returned, 0 if nothing has been received + */ +int getCommand(UsbCommand* response) +{ + pthread_mutex_lock(&cmdBufferMutex); + //If head == tail, there's nothing to read, or if we just got initialized + if(cmd_head == cmd_tail){ + pthread_mutex_unlock(&cmdBufferMutex); + return 0; + } + //Pick out the next unread command + UsbCommand* last_unread = &cmdBuffer[cmd_tail]; + memcpy(response, last_unread, sizeof(UsbCommand)); + //Increment tail - this is a circular buffer, so modulo buffer size + cmd_tail = (cmd_tail +1 ) % CMD_BUFFER_SIZE; + pthread_mutex_unlock(&cmdBufferMutex); + return 1; +} + + +/** + * Waits for a certain response type. This method waits for a maximum of + * ms_timeout milliseconds for a specified response command. + *@brief WaitForResponseTimeout + * @param cmd command to wait for + * @param response struct to copy received command into. + * @param ms_timeout + * @return true if command was returned, otherwise false + */ +bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning) { + + UsbCommand resp; + + if (response == NULL) { + response = &resp; + } + + uint64_t start_time = msclock(); + + // Wait until the command is received + while (true) { + while(getCommand(response)) { + if(response->cmd == cmd){ + return true; + } + } + if (msclock() - start_time > ms_timeout) { + break; + } + if (msclock() - start_time > 2000 && show_warning) { + PrintAndLog("Waiting for a response from the proxmark..."); + PrintAndLog("Don't forget to cancel its operation first by pressing on the button"); + break; + } + } + return false; +} + + +bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout) { + return WaitForResponseTimeoutW(cmd, response, ms_timeout, true); +} + +bool WaitForResponse(uint32_t cmd, UsbCommand* response) { + return WaitForResponseTimeoutW(cmd, response, -1, true); +} + + //----------------------------------------------------------------------------- // Entry point into our code: called whenever the user types a command and // then presses Enter, which the full command line that they typed. @@ -81,3 +196,38 @@ int CommandReceived(char *Cmd) { return CmdsParse(CommandTable, Cmd); } + +//----------------------------------------------------------------------------- +// Entry point into our code: called whenever we received a packet over USB +// that we weren't necessarily expecting, for example a debug print. +//----------------------------------------------------------------------------- +void UsbCommandReceived(UsbCommand *UC) +{ + switch(UC->cmd) { + // First check if we are handling a debug message + case CMD_DEBUG_PRINT_STRING: { + char s[USB_CMD_DATA_SIZE+1]; + memset(s, 0x00, sizeof(s)); + size_t len = MIN(UC->arg[0],USB_CMD_DATA_SIZE); + memcpy(s,UC->d.asBytes,len); + PrintAndLog("#db# %s", s); + return; + } break; + + case CMD_DEBUG_PRINT_INTEGERS: { + PrintAndLog("#db# %08x, %08x, %08x \r\n", UC->arg[0], UC->arg[1], UC->arg[2]); + return; + } break; + + case CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K: { + memcpy(sample_buf+(UC->arg[0]),UC->d.asBytes,UC->arg[1]); + return; + } break; + + default: + storeCommand(UC); + break; + } + +} + diff --git a/client/cmdmain.h b/client/cmdmain.h index a833b41e..d39bc114 100644 --- a/client/cmdmain.h +++ b/client/cmdmain.h @@ -15,10 +15,13 @@ #include #include "usb_cmd.h" #include "cmdparser.h" -#include "comms.h" - +extern void UsbCommandReceived(UsbCommand *UC); extern int CommandReceived(char *Cmd); +extern bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning); +extern bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout); +extern bool WaitForResponse(uint32_t cmd, UsbCommand* response); +extern void clearCommandBuffer(); extern command_t* getTopLevelCommandTable(); #endif diff --git a/client/cmdparser.c b/client/cmdparser.c index 28ca2196..32508997 100644 --- a/client/cmdparser.c +++ b/client/cmdparser.c @@ -14,7 +14,6 @@ #include "ui.h" #include "cmdparser.h" #include "proxmark3.h" -#include "comms.h" void CmdsHelp(const command_t Commands[]) @@ -24,7 +23,7 @@ void CmdsHelp(const command_t Commands[]) int i = 0; while (Commands[i].Name) { - if (!IsOffline() || Commands[i].Offline) + if (!offline || Commands[i].Offline) PrintAndLog("%-16s %s", Commands[i].Name, Commands[i].Help); ++i; } diff --git a/client/comms.c b/client/comms.c deleted file mode 100644 index abbae386..00000000 --- a/client/comms.c +++ /dev/null @@ -1,325 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (C) 2009 Michael Gernoth -// Copyright (C) 2010 iZsh -// -// 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. -//----------------------------------------------------------------------------- -// Code for communicating with the proxmark3 hardware. -//----------------------------------------------------------------------------- - -#include - -#include "comms.h" -#include "uart.h" -#include "ui.h" -#include "common.h" -#include "data.h" -#include "util_posix.h" - -// Declare globals. - -// Serial port that we are communicating with the PM3 on. -static serial_port* port; - -// If TRUE, then there is no active connection to the PM3, and we will drop commands sent. -static bool offline; - -// Transmit buffer. -// TODO: Use locks and execute this on the main thread, rather than the receiver -// thread. Running on the main thread means we need to be careful in the -// flasher, as it means SendCommand is no longer async, and can't be used as a -// buffer for a pending command when the connection is re-established. -static UsbCommand txcmd; -static bool txcmd_pending; - -// Used by UsbReceiveCommand as a ring buffer for messages that are yet to be -// processed by a command handler (WaitForResponse{,Timeout}) -static UsbCommand cmdBuffer[CMD_BUFFER_SIZE]; - -// Points to the next empty position to write to -static int cmd_head = 0; - -// Points to the position of the last unread command -static int cmd_tail = 0; - -// to lock cmdBuffer operations from different threads -static pthread_mutex_t cmdBufferMutex = PTHREAD_MUTEX_INITIALIZER; - -// These wrappers are required because it is not possible to access a static -// global variable outside of the context of a single file. - -void SetSerialPort(serial_port* new_port) { - port = new_port; -} - -serial_port* GetSerialPort() { - return port; -} - -void SetOffline(bool new_offline) { - offline = new_offline; -} - -bool IsOffline() { - return offline; -} - -void SendCommand(UsbCommand *c) { - #ifdef COMMS_DEBUG - printf("Sending %04x cmd\n", c->cmd); - #endif - - if (offline) { - PrintAndLog("Sending bytes to proxmark failed - offline"); - return; - } - /** - The while-loop below causes hangups at times, when the pm3 unit is unresponsive - or disconnected. The main console thread is alive, but comm thread just spins here. - Not good.../holiman - **/ - while(txcmd_pending); - txcmd = *c; - txcmd_pending = true; -} - -/** - * @brief This method should be called when sending a new command to the pm3. In case any old - * responses from previous commands are stored in the buffer, a call to this method should clear them. - * A better method could have been to have explicit command-ACKS, so we can know which ACK goes to which - * operation. Right now we'll just have to live with this. - */ -void clearCommandBuffer() -{ - //This is a very simple operation - pthread_mutex_lock(&cmdBufferMutex); - cmd_tail = cmd_head; - pthread_mutex_unlock(&cmdBufferMutex); -} - -/** - * @brief storeCommand stores a USB command in a circular buffer - * @param UC - */ -void storeCommand(UsbCommand *command) -{ - pthread_mutex_lock(&cmdBufferMutex); - if( (cmd_head+1) % CMD_BUFFER_SIZE == cmd_tail) - { - // If these two are equal, we're about to overwrite in the - // circular buffer. - PrintAndLog("WARNING: Command buffer about to overwrite command! This needs to be fixed!"); - } - - // Store the command at the 'head' location - UsbCommand* destination = &cmdBuffer[cmd_head]; - memcpy(destination, command, sizeof(UsbCommand)); - - cmd_head = (cmd_head +1) % CMD_BUFFER_SIZE; //increment head and wrap - pthread_mutex_unlock(&cmdBufferMutex); -} - - -/** - * @brief getCommand gets a command from an internal circular buffer. - * @param response location to write command - * @return 1 if response was returned, 0 if nothing has been received - */ -int getCommand(UsbCommand* response) -{ - pthread_mutex_lock(&cmdBufferMutex); - //If head == tail, there's nothing to read, or if we just got initialized - if (cmd_head == cmd_tail){ - pthread_mutex_unlock(&cmdBufferMutex); - return 0; - } - - //Pick out the next unread command - UsbCommand* last_unread = &cmdBuffer[cmd_tail]; - memcpy(response, last_unread, sizeof(UsbCommand)); - //Increment tail - this is a circular buffer, so modulo buffer size - cmd_tail = (cmd_tail + 1) % CMD_BUFFER_SIZE; - - pthread_mutex_unlock(&cmdBufferMutex); - return 1; -} - -//----------------------------------------------------------------------------- -// Entry point into our code: called whenever we received a packet over USB -// that we weren't necessarily expecting, for example a debug print. -//----------------------------------------------------------------------------- -void UsbCommandReceived(UsbCommand *UC) -{ - switch(UC->cmd) { - - // First check if we are handling a debug message - case CMD_DEBUG_PRINT_STRING: { - char s[USB_CMD_DATA_SIZE+1]; - memset(s, 0x00, sizeof(s)); - size_t len = MIN(UC->arg[0],USB_CMD_DATA_SIZE); - memcpy(s,UC->d.asBytes,len); - PrintAndLog("#db# %s", s); - return; - } - - case CMD_DEBUG_PRINT_INTEGERS: { - PrintAndLog("#db# %08x, %08x, %08x \r\n", UC->arg[0], UC->arg[1], UC->arg[2]); - return; - } - - case CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K: { - // FIXME: This does unsanitised copies into memory when we don't know - // the size of the buffer. - if (sample_buf) { - memcpy(sample_buf+(UC->arg[0]),UC->d.asBytes,UC->arg[1]); - } - return; - } - - default: { - storeCommand(UC); - return; - } - } -} - -// Gets a single command from a proxmark3 device. This should never be used -// with the full client. -// -// @param conn A receiver_arg structure. -// @param command A buffer to store the received command. -bool ReceiveCommand(receiver_arg* conn, UsbCommand* command) { - // Local recieve buffer - size_t rxlen; - byte_t rx[sizeof(UsbCommand)]; - byte_t* prx = rx; - - while (conn->run) { - rxlen = 0; - if (uart_receive(port, prx, sizeof(UsbCommand) - (prx-rx), &rxlen) && rxlen) { - prx += rxlen; - if (prx-rx < sizeof(UsbCommand)) { - // Keep reading until we have a completed response. - continue; - } - - // We have a completed response. - memcpy(command, rx, sizeof(UsbCommand)); - return true; - } - - if (prx == rx) { - // We got no complete command while waiting, give up control - return false; - } - } - - // did not get a complete command before being cancelled. - return false; -} - -// Worker thread for processing incoming events from the PM3 -void *uart_receiver(void *targ) { - receiver_arg *conn = (receiver_arg*)targ; - UsbCommand rx; - - while (conn->run) { - #ifdef COMMS_DEBUG - printf("uart_receiver: get lock\n"); - #endif - // Lock up receives, in case they try to take it away from us. - pthread_mutex_lock(&conn->recv_lock); - #ifdef COMMS_DEBUG - printf("uart_receiver: lock acquired\n"); - #endif - - if (port == NULL) { - #ifdef COMMS_DEBUG - printf("uart_receiver: port disappeared\n"); - #endif - // Our port disappeared, stall. This code path matters for the flasher, - // where it is fiddling with the serial port under us. - pthread_mutex_unlock(&conn->recv_lock); - msleep(10); - continue; - } - - bool got_command = ReceiveCommand(conn, &rx); - #ifdef COMMS_DEBUG - printf("uart_receiver: got command\n"); - #endif - pthread_mutex_unlock(&conn->recv_lock); - - if (got_command) { - UsbCommandReceived(&rx); - } - - // We aren't normally trying to transmit in the flasher when the port would - // be reset, so we can just keep going at this point. - if (txcmd_pending) { - if (!uart_send(port, (byte_t*) &txcmd, sizeof(UsbCommand))) { - PrintAndLog("Sending bytes to proxmark failed"); - } - txcmd_pending = false; - } - } - - pthread_exit(NULL); - return NULL; -} - -/** - * Waits for a certain response type. This method waits for a maximum of - * ms_timeout milliseconds for a specified response command. - *@brief WaitForResponseTimeout - * @param cmd command to wait for, or CMD_ANY to take any command. - * @param response struct to copy received command into. - * @param ms_timeout - * @param show_warning - * @return true if command was returned, otherwise false - */ -bool WaitForResponseTimeoutW(uint64_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning) { - UsbCommand resp; - - #ifdef COMMS_DEBUG - printf("Waiting for %04x cmd\n", cmd); - #endif - - if (response == NULL) { - response = &resp; - } - - uint64_t start_time = msclock(); - - // Wait until the command is received - for (;;) { - while(getCommand(response)) { - if (cmd == CMD_ANY || response->cmd == cmd) { - return true; - } - } - - if (msclock() - start_time > ms_timeout) { - // We timed out. - break; - } - - if (msclock() - start_time > 2000 && show_warning) { - // 2 seconds elapsed (but this doesn't mean the timeout was exceeded) - PrintAndLog("Waiting for a response from the proxmark..."); - PrintAndLog("Don't forget to cancel its operation first by pressing on the button"); - break; - } - } - return false; -} - -bool WaitForResponseTimeout(uint64_t cmd, UsbCommand* response, size_t ms_timeout) { - return WaitForResponseTimeoutW(cmd, response, ms_timeout, true); -} - -bool WaitForResponse(uint64_t cmd, UsbCommand* response) { - return WaitForResponseTimeout(cmd, response, -1); -} diff --git a/client/comms.h b/client/comms.h deleted file mode 100644 index 75adeea1..00000000 --- a/client/comms.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef COMMS_H_ -#define COMMS_H_ - -#include -#include - -#include "usb_cmd.h" -#include "uart.h" - -#ifndef CMD_BUFFER_SIZE -#define CMD_BUFFER_SIZE 50 -#endif - -#ifndef MAX_DEMOD_BUF_LEN -#define MAX_DEMOD_BUF_LEN (1024*128) -#endif - -#ifndef BIGBUF_SIZE -#define BIGBUF_SIZE 40000 -#endif - -typedef struct { - // If TRUE, continue running the uart_receiver thread. - bool run; - - // Lock around serial port receives - pthread_mutex_t recv_lock; -} receiver_arg; - - -// Wrappers required as static variables can only be used in one file. -void SetSerialPort(serial_port* new_port); -serial_port* GetSerialPort(); -void SetOffline(bool new_offline); -bool IsOffline(); - -void SendCommand(UsbCommand *c); -void *uart_receiver(void *targ); -void UsbCommandReceived(UsbCommand *UC); -void clearCommandBuffer(); -bool WaitForResponseTimeoutW(uint64_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning); -bool WaitForResponseTimeout(uint64_t cmd, UsbCommand* response, size_t ms_timeout); -bool WaitForResponse(uint64_t cmd, UsbCommand* response); - -#endif // COMMS_H_ diff --git a/client/flash.c b/client/flash.c index bf01b911..7622e8a5 100644 --- a/client/flash.c +++ b/client/flash.c @@ -13,7 +13,6 @@ #include #include #include - #include "proxmark3.h" #include "util.h" #include "util_posix.h" @@ -21,7 +20,11 @@ #include "elf.h" #include "proxendian.h" #include "usb_cmd.h" -#include "comms.h" + +void SendCommand(UsbCommand* txcmd); +void ReceiveCommand(UsbCommand* rxcmd); +void CloseProxmark(); +int OpenProxmark(size_t i); // FIXME: what the fuckity fuck unsigned int current_command = CMD_UNKNOWN; @@ -41,32 +44,6 @@ static const uint8_t elf_ident[] = { EV_CURRENT }; -void CloseProxmark(receiver_arg* conn, char* serial_port_name) { - pthread_mutex_lock(&conn->recv_lock); - - // Block the port from being used by anything - serial_port* my_port = GetSerialPort(); - SetSerialPort(NULL); - - // Then close the port. - uart_close(my_port); - pthread_mutex_unlock(&conn->recv_lock); - - // Fix for linux, it seems that it is extremely slow to release the serial port file descriptor /dev/* - unlink(serial_port_name); -} - -bool OpenProxmark(char* serial_port_name) { - serial_port *new_port = uart_open(serial_port_name); - if (new_port == INVALID_SERIAL_PORT || new_port == CLAIMED_SERIAL_PORT) { - //poll once a second - return false; - } - - SetSerialPort(new_port); - return true; -} - // Turn PHDRs into flasher segments, checking for PHDR sanity and merging adjacent // unaligned segments if needed static int build_segs_from_phdrs(flash_file_t *ctx, FILE *fd, Elf32_Phdr *phdrs, int num_phdrs) @@ -301,12 +278,9 @@ static int get_proxmark_state(uint32_t *state) { UsbCommand c; c.cmd = CMD_DEVICE_INFO; - SendCommand(&c); + SendCommand(&c); UsbCommand resp; - while (!WaitForResponse(CMD_ANY, &resp)) { - // Keep waiting for a response - msleep(100); - } + ReceiveCommand(&resp); // Three outcomes: // 1. The old bootrom code will ignore CMD_DEVICE_INFO, but respond with an ACK @@ -333,7 +307,7 @@ static int get_proxmark_state(uint32_t *state) } // Enter the bootloader to be able to start flashing -static int enter_bootloader(receiver_arg* conn, char *serial_port_name) +static int enter_bootloader(char *serial_port_name) { uint32_t state; @@ -364,17 +338,16 @@ static int enter_bootloader(receiver_arg* conn, char *serial_port_name) SendCommand(&c); fprintf(stderr,"Press and hold down button NOW if your bootloader requires it.\n"); } - - msleep(100); - CloseProxmark(conn, serial_port_name); + msleep(100); + CloseProxmark(); fprintf(stderr,"Waiting for Proxmark to reappear on %s",serial_port_name); - do { + do { sleep(1); fprintf(stderr, "."); - } while (!OpenProxmark(serial_port_name)); - + } while (!OpenProxmark(0)); fprintf(stderr," Found.\n"); + return 0; } @@ -382,25 +355,23 @@ static int enter_bootloader(receiver_arg* conn, char *serial_port_name) return -1; } -static int wait_for_ack() +static int wait_for_ack(void) { - UsbCommand resp; - while (!WaitForResponse(CMD_ANY, &resp)) { - msleep(100); - } - if (resp.cmd != CMD_ACK) { - printf("Error: Unexpected reply 0x%04" PRIx64 " (expected ACK)\n", resp.cmd); + UsbCommand ack; + ReceiveCommand(&ack); + if (ack.cmd != CMD_ACK) { + printf("Error: Unexpected reply 0x%04" PRIx64 " (expected ACK)\n", ack.cmd); return -1; } return 0; } // Go into flashing mode -int flash_start_flashing(receiver_arg* conn, int enable_bl_writes,char *serial_port_name) +int flash_start_flashing(int enable_bl_writes,char *serial_port_name) { uint32_t state; - if (enter_bootloader(conn, serial_port_name) < 0) + if (enter_bootloader(serial_port_name) < 0) return -1; if (get_proxmark_state(&state) < 0) @@ -499,9 +470,9 @@ void flash_free(flash_file_t *ctx) } // just reset the unit -int flash_stop_flashing() { +int flash_stop_flashing(void) { UsbCommand c = {CMD_HARDWARE_RESET}; - SendCommand(&c); - msleep(100); - return 0; + SendCommand(&c); + msleep(100); + return 0; } diff --git a/client/flash.h b/client/flash.h index 279062af..3e9f77a7 100644 --- a/client/flash.h +++ b/client/flash.h @@ -11,7 +11,6 @@ #include #include "elf.h" -#include "comms.h" typedef struct { void *data; @@ -27,12 +26,10 @@ typedef struct { } flash_file_t; int flash_load(flash_file_t *ctx, const char *name, int can_write_bl); -int flash_start_flashing(receiver_arg* conn, int enable_bl_writes, char *serial_port_name); +int flash_start_flashing(int enable_bl_writes,char *serial_port_name); int flash_write(flash_file_t *ctx); void flash_free(flash_file_t *ctx); int flash_stop_flashing(void); -void CloseProxmark(receiver_arg* conn, char* serial_port_name); -bool OpenProxmark(char* serial_port_name); #endif diff --git a/client/flasher.c b/client/flasher.c index 40e40524..f257d994 100644 --- a/client/flasher.c +++ b/client/flasher.c @@ -10,15 +10,12 @@ #include #include #include -#include - #include "proxmark3.h" #include "util.h" #include "util_posix.h" #include "flash.h" #include "uart.h" #include "usb_cmd.h" -#include "comms.h" #ifdef _WIN32 # define unlink(x) @@ -26,6 +23,9 @@ # include #endif +static serial_port sp; +static char* serial_port_name; + void cmd_debug(UsbCommand* UC) { // Debug printf("UsbCommand length[len=%zd]\n",sizeof(UsbCommand)); @@ -40,6 +40,45 @@ void cmd_debug(UsbCommand* UC) { printf("...\n"); } +void SendCommand(UsbCommand* txcmd) { +// printf("send: "); +// cmd_debug(txcmd); + if (!uart_send(sp,(byte_t*)txcmd,sizeof(UsbCommand))) { + printf("Sending bytes to proxmark failed\n"); + exit(1); + } +} + +void ReceiveCommand(UsbCommand* rxcmd) { + byte_t* prxcmd = (byte_t*)rxcmd; + byte_t* prx = prxcmd; + size_t rxlen; + while (true) { + if (uart_receive(sp, prx, sizeof(UsbCommand) - (prx-prxcmd), &rxlen)) { + prx += rxlen; + if ((prx-prxcmd) >= sizeof(UsbCommand)) { + return; + } + } + } +} + +void CloseProxmark() { + // Clean up the port + uart_close(sp); + // Fix for linux, it seems that it is extremely slow to release the serial port file descriptor /dev/* + unlink(serial_port_name); +} + +int OpenProxmark(size_t i) { + sp = uart_open(serial_port_name); + if (sp == INVALID_SERIAL_PORT || sp == CLAIMED_SERIAL_PORT) { + //poll once a second + return 0; + } + return 1; +} + static void usage(char *argv0) { fprintf(stderr, "Usage: %s [-b] image.elf [image.elf...]\n\n", argv0); @@ -47,10 +86,9 @@ static void usage(char *argv0) //Is the example below really true? /Martin fprintf(stderr, "Example:\n\n\t %s path/to/osimage.elf path/to/fpgaimage.elf\n", argv0); fprintf(stderr, "\nExample (Linux):\n\n\t %s /dev/ttyACM0 armsrc/obj/fullimage.elf\n", argv0); - fprintf(stderr, "\nNote (Linux): if the flasher gets stuck at 'Waiting for Proxmark to reappear',\n"); - fprintf(stderr, " you may need to blacklist proxmark for modem-manager. v1.4.14 and later\n"); - fprintf(stderr, " include this configuration patch already. The change can be found at:\n"); - fprintf(stderr, " https://cgit.freedesktop.org/ModemManager/ModemManager/commit/?id=6e7ff47\n\n"); + fprintf(stderr, "\nNote (Linux): if the flasher gets stuck in 'Waiting for Proxmark to reappear on ',\n"); + fprintf(stderr, " you need to blacklist proxmark for modem-manager - see wiki for more details:\n"); + fprintf(stderr, " http://code.google.com/p/proxmark3/wiki/Linux\n\n"); } #define MAX_FILES 4 @@ -61,10 +99,7 @@ int main(int argc, char **argv) int num_files = 0; int res; flash_file_t files[MAX_FILES]; - receiver_arg conn; - pthread_t reader_thread; - memset(&conn, 0, sizeof(receiver_arg)); memset(files, 0, sizeof(files)); if (argc < 3) { @@ -91,22 +126,16 @@ int main(int argc, char **argv) } } - pthread_mutex_init(&conn.recv_lock, NULL); + serial_port_name = argv[1]; + + fprintf(stderr,"Waiting for Proxmark to appear on %s",serial_port_name); + do { + msleep(1000); + fprintf(stderr, "."); + } while (!OpenProxmark(0)); + fprintf(stderr," Found.\n"); - char* serial_port_name = argv[1]; - - fprintf(stderr,"Waiting for Proxmark to appear on %s", serial_port_name); - do { - sleep(1); - fprintf(stderr, "."); - } while (!OpenProxmark(serial_port_name)); - fprintf(stderr," Found.\n"); - - // Lets start up the communications thread - conn.run = true; - pthread_create(&reader_thread, NULL, &uart_receiver, &conn); - - res = flash_start_flashing(&conn, can_write_bl, serial_port_name); + res = flash_start_flashing(can_write_bl,serial_port_name); if (res < 0) return -1; @@ -126,11 +155,7 @@ int main(int argc, char **argv) if (res < 0) return -1; - // Stop the command thread. - conn.run = false; - pthread_join(reader_thread, NULL); - CloseProxmark(&conn, serial_port_name); - pthread_mutex_destroy(&conn.recv_lock); + CloseProxmark(); fprintf(stderr, "All done.\n\n"); fprintf(stderr, "Have a nice day!\n"); diff --git a/client/proxgui.cpp b/client/proxgui.cpp index 6e4ec98e..e899174c 100644 --- a/client/proxgui.cpp +++ b/client/proxgui.cpp @@ -11,15 +11,11 @@ #include "proxgui.h" #include "proxguiqt.h" #include "proxmark3.h" -#include "uart.h" static ProxGuiQT *gui = NULL; static WorkerThread *main_loop_thread = NULL; -WorkerThread::WorkerThread(char *script_cmds_file, char *script_cmd, - bool usb_present, serial_port* sp) - : script_cmds_file(script_cmds_file), script_cmd(script_cmd), - usb_present(usb_present), sp(sp) +WorkerThread::WorkerThread(char *script_cmds_file, char *script_cmd, bool usb_present) : script_cmds_file(script_cmds_file), script_cmd(script_cmd), usb_present(usb_present) { } @@ -28,7 +24,7 @@ WorkerThread::~WorkerThread() } void WorkerThread::run() { - main_loop(script_cmds_file, script_cmd, usb_present, sp); + main_loop(script_cmds_file, script_cmd, usb_present); } extern "C" void ShowGraphWindow(void) @@ -63,9 +59,7 @@ extern "C" void MainGraphics(void) gui->MainLoop(); } -extern "C" void InitGraphics(int argc, char **argv, char *script_cmds_file, - char *script_cmd, bool usb_present, - serial_port* sp) +extern "C" void InitGraphics(int argc, char **argv, char *script_cmds_file, char *script_cmd, bool usb_present) { #ifdef Q_WS_X11 bool useGUI = getenv("DISPLAY") != 0; @@ -75,7 +69,7 @@ extern "C" void InitGraphics(int argc, char **argv, char *script_cmds_file, if (!useGUI) return; - main_loop_thread = new WorkerThread(script_cmds_file, script_cmd, usb_present, sp); + main_loop_thread = new WorkerThread(script_cmds_file, script_cmd, usb_present); gui = new ProxGuiQT(argc, argv, main_loop_thread); } diff --git a/client/proxgui.h b/client/proxgui.h index ea3abf32..77bcbf01 100644 --- a/client/proxgui.h +++ b/client/proxgui.h @@ -14,13 +14,12 @@ extern "C" { #include #include -#include "uart.h" void ShowGraphWindow(void); void HideGraphWindow(void); void RepaintGraphWindow(void); void MainGraphics(void); -void InitGraphics(int argc, char **argv, char *script_cmds_file, char *script_cmd, bool usb_present, serial_port* sp); +void InitGraphics(int argc, char **argv, char *script_cmds_file, char *script_cmd, bool usb_present); void ExitGraphics(void); #define MAX_GRAPH_TRACE_LEN (40000*8) @@ -31,6 +30,7 @@ extern int s_Buff[MAX_GRAPH_TRACE_LEN]; extern double CursorScaleFactor; extern int PlotGridX, PlotGridY, PlotGridXdefault, PlotGridYdefault, CursorCPos, CursorDPos, GridOffset; extern int CommandFinished; +extern int offline; extern bool GridLocked; //Operations defined in data_operations diff --git a/client/proxguiqt.h b/client/proxguiqt.h index 4b2b2a27..45a65b04 100644 --- a/client/proxguiqt.h +++ b/client/proxguiqt.h @@ -21,7 +21,6 @@ #include #include -#include "uart.h" #include "ui/ui_overlays.h" /** * @brief The actual plot, black area were we paint the graph @@ -92,14 +91,13 @@ class ProxWidget : public QWidget class WorkerThread : public QThread { Q_OBJECT; public: - WorkerThread(char*, char*, bool, serial_port*); + WorkerThread(char*, char*, bool); ~WorkerThread(); void run(); private: char *script_cmds_file = NULL; char *script_cmd = NULL; bool usb_present; - serial_port *sp = NULL; }; class ProxGuiQT : public QObject diff --git a/client/proxmark3.c b/client/proxmark3.c index 0acf34f6..99ba9fba 100644 --- a/client/proxmark3.c +++ b/client/proxmark3.c @@ -27,42 +27,86 @@ #include "cmdparser.h" #include "cmdhw.h" #include "whereami.h" -#include "comms.h" #ifdef _WIN32 #define SERIAL_PORT_H "com3" -#elif __APPLE__ -#define SERIAL_PORT_H "/dev/tty.usbmodem*" #else #define SERIAL_PORT_H "/dev/ttyACM0" #endif -void main_loop(char *script_cmds_file, char* script_cmd, bool usb_present, serial_port* sp) { - receiver_arg conn; +// a global mutex to prevent interlaced printing from different threads +pthread_mutex_t print_lock; + +static serial_port sp; +static UsbCommand txcmd; +volatile static bool txcmd_pending = false; + +void SendCommand(UsbCommand *c) { + #if 0 + printf("Sending %d bytes\n", sizeof(UsbCommand)); + #endif + + if (offline) { + PrintAndLog("Sending bytes to proxmark failed - offline"); + return; + } + /** + The while-loop below causes hangups at times, when the pm3 unit is unresponsive + or disconnected. The main console thread is alive, but comm thread just spins here. + Not good.../holiman + **/ + while(txcmd_pending); + txcmd = *c; + txcmd_pending = true; +} + +struct receiver_arg { + int run; +}; + +byte_t rx[sizeof(UsbCommand)]; +byte_t* prx = rx; + +static void *uart_receiver(void *targ) { + struct receiver_arg *arg = (struct receiver_arg*)targ; + size_t rxlen; + + while (arg->run) { + rxlen = 0; + if (uart_receive(sp, prx, sizeof(UsbCommand) - (prx-rx), &rxlen) && rxlen) { + prx += rxlen; + if (prx-rx < sizeof(UsbCommand)) { + continue; + } + UsbCommandReceived((UsbCommand*)rx); + } + prx = rx; + + if(txcmd_pending) { + if (!uart_send(sp, (byte_t*) &txcmd, sizeof(UsbCommand))) { + PrintAndLog("Sending bytes to proxmark failed"); + } + txcmd_pending = false; + } + } + + pthread_exit(NULL); + return NULL; +} + + +void main_loop(char *script_cmds_file, char *script_cmd, bool usb_present) { + struct receiver_arg rarg; char *cmd = NULL; pthread_t reader_thread; bool execCommand = (script_cmd != NULL); bool stdinOnPipe = !isatty(STDIN_FILENO); - - memset(&conn, 0, sizeof(receiver_arg)); - pthread_mutex_init(&conn.recv_lock, NULL); - - - // TODO: Move this into comms.c - PlotGridXdefault = 64; - PlotGridYdefault = 64; - showDemod = true; - CursorScaleFactor = 1; - + if (usb_present) { - conn.run = true; - SetSerialPort(sp); - SetOffline(false); - pthread_create(&reader_thread, NULL, &uart_receiver, &conn); + rarg.run = 1; + pthread_create(&reader_thread, NULL, &uart_receiver, &rarg); // cache Version information now: CmdVersion(NULL); - } else { - SetOffline(true); } // file with script @@ -78,7 +122,7 @@ void main_loop(char *script_cmds_file, char* script_cmd, bool usb_present, seria read_history(".history"); - while (1) { + while(1) { // If there is a script file if (script_file) { @@ -150,7 +194,7 @@ void main_loop(char *script_cmds_file, char* script_cmd, bool usb_present, seria write_history(".history"); if (usb_present) { - conn.run = false; + rarg.run = 0; pthread_join(reader_thread, NULL); } @@ -158,8 +202,6 @@ void main_loop(char *script_cmds_file, char* script_cmd, bool usb_present, seria fclose(script_file); script_file = NULL; } - - pthread_mutex_destroy(&conn.recv_lock); } static void dumpAllHelp(int markdown) @@ -232,8 +274,6 @@ int main(int argc, char* argv[]) { bool addLuaExec = false; char *script_cmds_file = NULL; char *script_cmd = NULL; - serial_port *sp = NULL; - g_debugMode = 0; if (argc < 2) { show_help(true, argv[0]); @@ -330,11 +370,14 @@ int main(int argc, char* argv[]) { if (sp == INVALID_SERIAL_PORT) { printf("ERROR: invalid serial port\n"); usb_present = false; + offline = 1; } else if (sp == CLAIMED_SERIAL_PORT) { printf("ERROR: serial port is claimed by another process\n"); usb_present = false; + offline = 1; } else { usb_present = true; + offline = 0; } // create a mutex to avoid interlacing print commands from our different threads @@ -342,23 +385,23 @@ int main(int argc, char* argv[]) { #ifdef HAVE_GUI #ifdef _WIN32 - InitGraphics(argc, argv, script_cmds_file, script_cmd, usb_present, sp); + InitGraphics(argc, argv, script_cmds_file, script_cmd, usb_present); MainGraphics(); #else char* display = getenv("DISPLAY"); if (display && strlen(display) > 1) { - InitGraphics(argc, argv, script_cmds_file, script_cmd, usb_present, sp); + InitGraphics(argc, argv, script_cmds_file, script_cmd, usb_present); MainGraphics(); } else { - main_loop(script_cmds_file, script_cmd, usb_present, sp); + main_loop(script_cmds_file, script_cmd, usb_present); } #endif #else - main_loop(script_cmds_file, script_cmd, usb_present, sp); + main_loop(script_cmds_file, script_cmd, usb_present); #endif // Clean up the port diff --git a/client/proxmark3.h b/client/proxmark3.h index 11b8aabd..c6185c43 100644 --- a/client/proxmark3.h +++ b/client/proxmark3.h @@ -13,7 +13,6 @@ #define PROXMARK3_H__ #include "usb_cmd.h" -#include "uart.h" #define PROXPROMPT "proxmark3> " @@ -24,7 +23,7 @@ extern "C" { void SendCommand(UsbCommand *c); const char *get_my_executable_path(void); const char *get_my_executable_directory(void); -void main_loop(char *script_cmds_file, char *script_cmd, bool usb_present, serial_port* sp); +void main_loop(char *script_cmds_file, char *script_cmd, bool usb_present); #ifdef __cplusplus } diff --git a/client/ui.c b/client/ui.c index 48869fd1..df2c3ce3 100644 --- a/client/ui.c +++ b/client/ui.c @@ -21,7 +21,7 @@ double CursorScaleFactor = 1; int PlotGridX=0, PlotGridY=0, PlotGridXdefault= 64, PlotGridYdefault= 64, CursorCPos= 0, CursorDPos= 0; int offline; -bool flushAfterWrite = false; //buzzy +int flushAfterWrite = 0; //buzzy int GridOffset = 0; bool GridLocked = false; bool showDemod = true; @@ -62,6 +62,7 @@ void PrintAndLog(char *fmt, ...) } #else // We are using libedit (OSX), which doesn't support this flag. + int need_hack = 0; #endif va_start(argptr, fmt); @@ -71,9 +72,6 @@ void PrintAndLog(char *fmt, ...) va_end(argptr); printf("\n"); - // This needs to be wrapped in ifdefs, as this if optimisation is disabled, - // this block won't be removed, and it'll fail at the linker. -#ifdef RL_STATE_READCMD if (need_hack) { rl_restore_prompt(); rl_replace_line(saved_line, 0); @@ -81,7 +79,6 @@ void PrintAndLog(char *fmt, ...) rl_redisplay(); free(saved_line); } -#endif if (logging && logfile) { vfprintf(logfile, fmt, argptr2); @@ -103,8 +100,3 @@ void SetLogFilename(char *fn) { logfilename = fn; } - -void SetFlushAfterWrite(bool flush_after_write) { - flushAfterWrite = flush_after_write; -} - diff --git a/client/ui.h b/client/ui.h index 2986efaf..4049033d 100644 --- a/client/ui.h +++ b/client/ui.h @@ -13,11 +13,6 @@ #include #include -#include - -// a global mutex to prevent interlaced printing from different threads -pthread_mutex_t print_lock; -extern uint8_t g_debugMode; void ShowGui(void); void HideGraphWindow(void); @@ -28,7 +23,8 @@ void SetLogFilename(char *fn); extern double CursorScaleFactor; extern int PlotGridX, PlotGridY, PlotGridXdefault, PlotGridYdefault, CursorCPos, CursorDPos, GridOffset; -extern bool flushAfterWrite; //buzzy +extern int offline; +extern int flushAfterWrite; //buzzy extern bool GridLocked; extern bool showDemod; diff --git a/common/iso15693tools.c b/common/iso15693tools.c index cf7d5341..26e636ca 100644 --- a/common/iso15693tools.c +++ b/common/iso15693tools.c @@ -50,9 +50,8 @@ int Iso15693AddCrc(uint8_t *req, int n) { return n+2; } -#ifdef ON_DEVICE + int sprintf(char *str, const char *format, ...); -#endif // returns a string representation of the UID // UID is transmitted and stored LSB first, displayed MSB first diff --git a/include/usb_cmd.h b/include/usb_cmd.h index df2923a0..194a9d53 100644 --- a/include/usb_cmd.h +++ b/include/usb_cmd.h @@ -212,7 +212,7 @@ typedef struct{ #define CMD_HF_SNIFFER 0x0800 #define CMD_UNKNOWN 0xFFFF -#define CMD_ANY 0xFFFFFFFFFFFFFFFF + //Mifare simulation flags #define FLAG_INTERACTIVE 0x01 From fefb7f5376bb62901aac7494d42fb78e5ea5983a Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Fri, 27 Oct 2017 17:58:24 +0300 Subject: [PATCH 035/310] appveyor.yml. display return values (#451) * display return values from commands and compare them with value. as promised in #440 --- appveyor.yml | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index c8e58b59..eb22114e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -175,7 +175,7 @@ test_script: $global:TestsPassed=$true - Function ExecTest($Name, $File, $Cmd) { + Function ExecTest($Name, $File, $Cmd, $CheckResult) { #--- begin Job @@ -191,6 +191,21 @@ test_script: $Cond=&$sb if ($Cond -eq $null){ + } ElseIf($using:CheckResult -ne $null) { + [String]$searchstr="" + if ($Cond -is [Object]){ + ForEach($line in $Cond){ + Write-host $line -ForegroundColor Gray + $searchstr += $line + } + }else{ + Write-host "$Cond" -ForegroundColor Gray + $searchstr = $Cond + } + If($searchstr -like "*$using:CheckResult*") { + $res=$true + } + $Cond="*$using:CheckResult*" } Else { If (!($Cond -is [bool] -or $Cond -is [byte] -or $Cond -is [int16] -or $Cond -is [int32] -or $Cond -is [int64] -or $Cond -is [float])){ if ($Cond -is "String" -and $Cond -like "*passed*"){ @@ -258,9 +273,9 @@ test_script: ExecTest "proxmark help hardnested" "proxmark3 -h" {bash -lc 'cd ~/client;proxmark3 -h | grep -q hardnested && echo Passed || echo Failed'} - ExecTest "hf mf offline text" "hf mf" {bash -lc "cd ~/client;proxmark3 comx -c 'hf mf' | grep -q at_enc && echo Passed || echo Failed"} + ExecTest "hf mf offline text" "hf mf" {bash -lc "cd ~/client;proxmark3 comx -c 'hf mf'"} "at_enc" - ExecTest "hf mf hardnested" "hf mf hardnested" {bash -lc "cd ~/client;proxmark3 comx -c 'hf mf hardnested t 1 000000000000' | grep -q 'found:' && echo Passed || echo Failed"} + ExecTest "hf mf hardnested" "hf mf hardnested" {bash -lc "cd ~/client;proxmark3 comx -c 'hf mf hardnested t 1 000000000000'"} "found:" if ($global:TestsPassed) { From 7710983be338398f3278666c2c67bb9b3517dac0 Mon Sep 17 00:00:00 2001 From: merlokk Date: Fri, 27 Oct 2017 18:45:42 +0300 Subject: [PATCH 036/310] added `hf 14a apdu` command. --- client/cmdhf14a.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index e3f1a5f1..91fcdeb1 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -554,6 +554,19 @@ int CmdHF14ASnoop(const char *Cmd) { return 0; } +int CmdHF14AAPDU(const char *cmd) { + if (strlen(cmd)<2) { + PrintAndLog("Usage: hf 14a apdu [-s] [-k] [-t] "); + PrintAndLog(" -s activate field and select card"); + PrintAndLog(" -k leave the signal field ON after receive response"); + PrintAndLog(" -t executes TLV decoder if it possible"); + return 0; + } + + + + return 0; +} int CmdHF14ACmdRaw(const char *cmd) { UsbCommand c = {CMD_READER_ISO_14443a, {0, 0, 0}}; @@ -759,6 +772,7 @@ static command_t CommandTable[] = {"cuids", CmdHF14ACUIDs, 0, " Collect n>0 ISO14443 Type A UIDs in one go"}, {"sim", CmdHF14ASim, 0, " -- Simulate ISO 14443a tag"}, {"snoop", CmdHF14ASnoop, 0, "Eavesdrop ISO 14443 Type A"}, + {"apdu", CmdHF14AAPDU, 0, "Send ISO 1443-4 APDU to tag"}, {"raw", CmdHF14ACmdRaw, 0, "Send raw hex data to tag"}, {NULL, NULL, 0, NULL} }; From 1208cdcb45e8a71ec47dfbd579c57ecff1252c5d Mon Sep 17 00:00:00 2001 From: merlokk Date: Fri, 27 Oct 2017 19:02:45 +0300 Subject: [PATCH 037/310] added sketch for command `hf 14a apdu` --- client/cmdhf14a.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 91fcdeb1..d84a8f35 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -555,6 +555,10 @@ int CmdHF14ASnoop(const char *Cmd) { } int CmdHF14AAPDU(const char *cmd) { + uint8_t data[USB_CMD_DATA_SIZE]; + uint16_t datalen = 0; + uint8_t cmdc = 0; + if (strlen(cmd)<2) { PrintAndLog("Usage: hf 14a apdu [-s] [-k] [-t] "); PrintAndLog(" -s activate field and select card"); @@ -563,7 +567,42 @@ int CmdHF14AAPDU(const char *cmd) { return 0; } + cmdc |= ISO14A_CONNECT; + cmdc |= ISO14A_NO_DISCONNECT; + UsbCommand c = {CMD_READER_ISO_14443a, {cmdc | ISO14A_APDU | ISO14A_SET_TIMEOUT, 0, 100}}; // 100-timeout in iso14a_set_timeout() + // Max buffer is USB_CMD_DATA_SIZE (512) + c.arg[1] = (datalen & 0xFFFF) | ((uint32_t)numbits << 16); + + uint8_t first, second; + ComputeCrc14443(CRC_14443_A, data, datalen, &first, &second); + data[datalen++] = first; + data[datalen++] = second; + + memcpy(c.d.asBytes,data,datalen); + + SendCommand(&c); + + if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) { + recv = resp.d.asBytes; + uint8_t iLen = resp.arg[0]; + if(!iLen) + return; + hexout = (char *)malloc(iLen * 3 + 1); + if (hexout != NULL) { + for (int i = 0; i < iLen; i++) { // data in hex + sprintf(&hexout[i * 3], "%02X ", recv[i]); + } + PrintAndLog("%s", hexout); + free(hexout); + } else { + PrintAndLog("malloc failed..."); + return 1; + } + } else { + PrintAndLog("timeout while waiting for reply."); + return 2; + } return 0; } From 618c220c383db2b9cde32261e90a008b0fd2b736 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Sun, 29 Oct 2017 17:34:51 +0200 Subject: [PATCH 038/310] small fixes in ReaderIso14443a() (#446) --- armsrc/iso14443a.c | 12 ++++++++---- client/cmdhf14a.c | 13 +++++++++++-- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 026b177a..06a83feb 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1698,6 +1698,13 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u int cascade_level = 0; int len; + // init card struct + if(p_hi14a_card) { + p_hi14a_card->uidlen = 0; + memset(p_hi14a_card->uid, 0, 10); + p_hi14a_card->ats_len = 0; + } + // Broadcast for a card, WUPA (0x52) will force response from all cards in the field ReaderTransmitBitsPar(wupa, 7, NULL, NULL); @@ -1706,8 +1713,6 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u if(p_hi14a_card) { memcpy(p_hi14a_card->atqa, resp, 2); - p_hi14a_card->uidlen = 0; - memset(p_hi14a_card->uid,0,10); } if (anticollision) { @@ -1813,7 +1818,6 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u if(p_hi14a_card) { p_hi14a_card->sak = sak; - p_hi14a_card->ats_len = 0; } // non iso14443a compliant tag @@ -1908,7 +1912,7 @@ void ReaderIso14443a(UsbCommand *c) size_t lenbits = c->arg[1] >> 16; uint32_t timeout = c->arg[2]; uint32_t arg0 = 0; - byte_t buf[USB_CMD_DATA_SIZE]; + byte_t buf[USB_CMD_DATA_SIZE] = {0}; uint8_t par[MAX_PARITY_SIZE]; if(param & ISO14A_CONNECT) { diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index e3f1a5f1..09dcd34e 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -732,8 +732,17 @@ static void waitCmd(uint8_t iSelect) if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) { recv = resp.d.asBytes; - uint8_t iLen = iSelect ? resp.arg[1] : resp.arg[0]; - PrintAndLog("received %i octets", iLen); + uint8_t iLen = resp.arg[0]; + if (iSelect){ + iLen = resp.arg[1]; + if (iLen){ + PrintAndLog("Card selected. UID[%i]:", iLen); + } else { + PrintAndLog("Can't select card."); + } + } else { + PrintAndLog("received %i bytes:", iLen); + } if(!iLen) return; hexout = (char *)malloc(iLen * 3 + 1); From 980417eacd9a054b999ffdd3f83008f543b80c41 Mon Sep 17 00:00:00 2001 From: merlokk Date: Mon, 30 Oct 2017 17:29:03 +0200 Subject: [PATCH 039/310] iso14_apdu works via logical channel. not as it needs. it needs to change to raw( --- client/cmdhf14a.c | 114 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 96 insertions(+), 18 deletions(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index d84a8f35..472f2fe4 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -558,6 +558,12 @@ int CmdHF14AAPDU(const char *cmd) { uint8_t data[USB_CMD_DATA_SIZE]; uint16_t datalen = 0; uint8_t cmdc = 0; + char buf[5] = {0}; + int i = 0; + uint32_t temp; + bool activateField = false; + bool leaveSignalON = false; + bool decodeTLV = false; if (strlen(cmd)<2) { PrintAndLog("Usage: hf 14a apdu [-s] [-k] [-t] "); @@ -566,42 +572,114 @@ int CmdHF14AAPDU(const char *cmd) { PrintAndLog(" -t executes TLV decoder if it possible"); return 0; } - - cmdc |= ISO14A_CONNECT; - cmdc |= ISO14A_NO_DISCONNECT; - - UsbCommand c = {CMD_READER_ISO_14443a, {cmdc | ISO14A_APDU | ISO14A_SET_TIMEOUT, 0, 100}}; // 100-timeout in iso14a_set_timeout() - // Max buffer is USB_CMD_DATA_SIZE (512) - c.arg[1] = (datalen & 0xFFFF) | ((uint32_t)numbits << 16); - uint8_t first, second; - ComputeCrc14443(CRC_14443_A, data, datalen, &first, &second); - data[datalen++] = first; - data[datalen++] = second; + // strip + while (*cmd==' ' || *cmd=='\t') cmd++; - memcpy(c.d.asBytes,data,datalen); + while (cmd[i]!='\0') { + if (cmd[i]==' ' || cmd[i]=='\t') { i++; continue; } + if (cmd[i]=='-') { + switch (cmd[i + 1]) { + case 's': + case 'S': + activateField = true; + break; + case 'k': + case 'K': + leaveSignalON = true; + break; + case 't': + case 'T': + decodeTLV = true; + break; + default: + PrintAndLog("Invalid option"); + return 1; + } + i += 2; + continue; + } + if ((cmd[i] >= '0' && cmd[i] <= '9') || + (cmd[i] >= 'a' && cmd[i] <= 'f') || + (cmd[i] >= 'A' && cmd[i] <= 'F') ) { + buf[strlen(buf) + 1] = 0x00; + buf[strlen(buf)] = cmd[i]; + i++; + + if (strlen(buf) >= 2) { + sscanf(buf, "%x", &temp); + data[datalen] = (uint8_t)(temp & 0xff); + *buf = 0; + if (datalen > sizeof(data) - 2) { + PrintAndLog("Buffer is full..."); + break; + } else { + datalen++; + } + } + continue; + } + PrintAndLog("Invalid char on input"); + return 1; + } + if (*buf) { + PrintAndLog("Hex must have even number of digits. Detected %d symbols.", datalen * 2 + strlen(buf)); + return 1; + } + + PrintAndLog("--%s %s %s >>>> %s", activateField ? "sel": "", leaveSignalON ? "keep": "", decodeTLV ? "TLV": "", sprint_hex(data, datalen)); + + if (activateField) + cmdc |= ISO14A_CONNECT; + if (leaveSignalON) + cmdc |= ISO14A_NO_DISCONNECT; + + // "Command APDU" length should be 5+255+1, but javacard's APDU buffer might be smaller - 133 bytes + // https://stackoverflow.com/questions/32994936/safe-max-java-card-apdu-data-command-and-respond-size + // here length USB_CMD_DATA_SIZE=512 + // timeout timeout14a * 1.06 / 100, true, size, &keyBlock[6 * c], e_sector); // timeout is (ms * 106)/10 or us*0.0106 + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_APDU | ISO14A_SET_TIMEOUT | cmdc, (datalen & 0xFFFF), 1000 * 1000 * 1.06 / 100}}; + +// uint8_t first, second; +// ComputeCrc14443(CRC_14443_A, data, datalen, &first, &second); +// data[datalen++] = first; +// data[datalen++] = second; + + memcpy(c.d.asBytes, data, datalen); SendCommand(&c); - if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) { + uint8_t *recv; + char *hexout; + UsbCommand resp; + + if (activateField) { + if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) + return 2; + } + + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { recv = resp.d.asBytes; uint8_t iLen = resp.arg[0]; if(!iLen) - return; + return 2; hexout = (char *)malloc(iLen * 3 + 1); if (hexout != NULL) { for (int i = 0; i < iLen; i++) { // data in hex sprintf(&hexout[i * 3], "%02X ", recv[i]); } - PrintAndLog("%s", hexout); + PrintAndLog("<<<< %s", hexout); + + // here TLV decoder... + free(hexout); } else { PrintAndLog("malloc failed..."); - return 1; + return 2; } } else { - PrintAndLog("timeout while waiting for reply."); - return 2; + PrintAndLog("Reply timeout."); + return 3; } return 0; From f1a983a330c983e2858c5b97012c949b243a4db1 Mon Sep 17 00:00:00 2001 From: merlokk Date: Mon, 30 Oct 2017 17:58:43 +0200 Subject: [PATCH 040/310] fixed bug in CmdHF14ACmdRaw: if we cant select we send command anyway... --- armsrc/iso14443a.c | 18 ++++++++++++++++-- client/cmdhf14a.c | 19 ++++++++++++------- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 026b177a..2ff722b0 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1910,6 +1910,7 @@ void ReaderIso14443a(UsbCommand *c) uint32_t arg0 = 0; byte_t buf[USB_CMD_DATA_SIZE]; uint8_t par[MAX_PARITY_SIZE]; + bool cantSELECT = false; if(param & ISO14A_CONNECT) { clear_trace(); @@ -1922,11 +1923,19 @@ void ReaderIso14443a(UsbCommand *c) } if(param & ISO14A_CONNECT) { + LED_A_ON(); + clear_trace(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); if(!(param & ISO14A_NO_SELECT)) { iso14a_card_select_t *card = (iso14a_card_select_t*)buf; arg0 = iso14443a_select_card(NULL, card, NULL, true, 0, param & ISO14A_NO_RATS); + + // if we cant select then we cant send data + cantSELECT = (arg0 != 1); + + LED_B_ON(); cmd_send(CMD_ACK,arg0,card->uidlen,0,buf,sizeof(iso14a_card_select_t)); + LED_B_OFF(); } } @@ -1934,12 +1943,14 @@ void ReaderIso14443a(UsbCommand *c) iso14a_set_timeout(timeout); } - if(param & ISO14A_APDU) { + if(param & ISO14A_APDU && !cantSELECT) { arg0 = iso14_apdu(cmd, len, buf); + LED_B_ON(); cmd_send(CMD_ACK,arg0,0,0,buf,sizeof(buf)); + LED_B_OFF(); } - if(param & ISO14A_RAW) { + if(param & ISO14A_RAW && !cantSELECT) { if(param & ISO14A_APPEND_CRC) { if(param & ISO14A_TOPAZMODE) { AppendCrc14443b(cmd,len); @@ -1975,7 +1986,10 @@ void ReaderIso14443a(UsbCommand *c) } } arg0 = ReaderReceive(buf, par); + + LED_B_ON(); cmd_send(CMD_ACK,arg0,0,0,buf,sizeof(buf)); + LED_B_OFF(); } if(param & ISO14A_REQUEST_TRIGGER) { diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 472f2fe4..94c3ad2c 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -29,7 +29,7 @@ #include "mifarehost.h" static int CmdHelp(const char *Cmd); -static void waitCmd(uint8_t iLen); +static int waitCmd(uint8_t iLen); // structure and database for uid -> tagtype lookups typedef struct { @@ -656,6 +656,8 @@ int CmdHF14AAPDU(const char *cmd) { if (activateField) { if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) return 2; + if (resp.arg[0] != 1) + return 2; } if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { @@ -845,17 +847,17 @@ int CmdHF14ACmdRaw(const char *cmd) { SendCommand(&c); if (reply) { - if(active_select) - waitCmd(1); - if(datalen>0) + int res = 0; + if (active_select) + res = waitCmd(1); + if (!res && datalen > 0) waitCmd(0); } // if reply return 0; } -static void waitCmd(uint8_t iSelect) -{ +static int waitCmd(uint8_t iSelect) { uint8_t *recv; UsbCommand resp; char *hexout; @@ -865,7 +867,7 @@ static void waitCmd(uint8_t iSelect) uint8_t iLen = iSelect ? resp.arg[1] : resp.arg[0]; PrintAndLog("received %i octets", iLen); if(!iLen) - return; + return 1; hexout = (char *)malloc(iLen * 3 + 1); if (hexout != NULL) { for (int i = 0; i < iLen; i++) { // data in hex @@ -875,10 +877,13 @@ static void waitCmd(uint8_t iSelect) free(hexout); } else { PrintAndLog("malloc failed your client has low memory?"); + return 2; } } else { PrintAndLog("timeout while waiting for reply."); + return 3; } + return 0; } static command_t CommandTable[] = From f2b0169cc69c9bb963c58d419bb8e313dcbe5b24 Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 31 Oct 2017 13:28:16 +0200 Subject: [PATCH 041/310] apdu works. --- client/cmdhf14a.c | 68 +++++++++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 94c3ad2c..c16a64bd 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -561,6 +561,7 @@ int CmdHF14AAPDU(const char *cmd) { char buf[5] = {0}; int i = 0; uint32_t temp; + uint8_t first, second; bool activateField = false; bool leaveSignalON = false; bool decodeTLV = false; @@ -627,30 +628,31 @@ int CmdHF14AAPDU(const char *cmd) { return 1; } - PrintAndLog("--%s %s %s >>>> %s", activateField ? "sel": "", leaveSignalON ? "keep": "", decodeTLV ? "TLV": "", sprint_hex(data, datalen)); - if (activateField) cmdc |= ISO14A_CONNECT; if (leaveSignalON) cmdc |= ISO14A_NO_DISCONNECT; + + // ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02 + memmove(data + 1, data, datalen); + data[0] = 0x02; // bnr,nad,cid,chn=0; i-block(0x00) + datalen++; + + ComputeCrc14443(CRC_14443_A, data, datalen, &first, &second); + data[datalen++] = first; + data[datalen++] = second; + + PrintAndLog("--%s %s %s >>>> %s", activateField ? "sel": "", leaveSignalON ? "keep": "", decodeTLV ? "TLV": "", sprint_hex(data, datalen)); // "Command APDU" length should be 5+255+1, but javacard's APDU buffer might be smaller - 133 bytes // https://stackoverflow.com/questions/32994936/safe-max-java-card-apdu-data-command-and-respond-size // here length USB_CMD_DATA_SIZE=512 // timeout timeout14a * 1.06 / 100, true, size, &keyBlock[6 * c], e_sector); // timeout is (ms * 106)/10 or us*0.0106 - UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_APDU | ISO14A_SET_TIMEOUT | cmdc, (datalen & 0xFFFF), 1000 * 1000 * 1.06 / 100}}; - -// uint8_t first, second; -// ComputeCrc14443(CRC_14443_A, data, datalen, &first, &second); -// data[datalen++] = first; -// data[datalen++] = second; - + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_RAW | ISO14A_SET_TIMEOUT | cmdc, (datalen & 0xFFFF), 1000 * 1000 * 1.06 / 100}}; memcpy(c.d.asBytes, data, datalen); - SendCommand(&c); uint8_t *recv; - char *hexout; UsbCommand resp; if (activateField) { @@ -665,22 +667,36 @@ int CmdHF14AAPDU(const char *cmd) { uint8_t iLen = resp.arg[0]; if(!iLen) return 2; - hexout = (char *)malloc(iLen * 3 + 1); - if (hexout != NULL) { - for (int i = 0; i < iLen; i++) { // data in hex - sprintf(&hexout[i * 3], "%02X ", recv[i]); - } - PrintAndLog("<<<< %s", hexout); - - // here TLV decoder... - - free(hexout); - } else { - PrintAndLog("malloc failed..."); - return 2; - } + + PrintAndLog("<<<< %s", sprint_hex(recv, iLen)); + + // check apdu length + if (iLen < 5) { + PrintAndLog("ERROR: Small APDU response."); + return 3; + } + + // check block + if (data[0] != recv[0]) { + PrintAndLog("ERROR: Block type mismatch: %02x-%02x", data[0], recv[0]); + return 3; + } + + // CRC Check + ComputeCrc14443(CRC_14443_A, recv, iLen, &first, &second); + if (first || second) { + PrintAndLog("ERROR: ISO 14443A CRC error."); + return 3; + } + + PrintAndLog("APDU response: %02x %02x", recv[iLen - 4], recv[iLen - 3]); // TODO add APDU descriptions + + // here TLV decoder... + if (decodeTLV) { + } + } else { - PrintAndLog("Reply timeout."); + PrintAndLog("ERROR: Reply timeout."); return 3; } From 8019540b19fe60be30a4a003727260892934a242 Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 31 Oct 2017 15:15:57 +0200 Subject: [PATCH 042/310] param parsing convert to procedures --- client/cmdhf14a.c | 65 ++++++++++++++++++----------------------------- client/util.c | 57 ++++++++++++++++++++++++++++++++++++++--- client/util.h | 2 ++ 3 files changed, 81 insertions(+), 43 deletions(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index c16a64bd..336cb0d2 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -556,17 +556,14 @@ int CmdHF14ASnoop(const char *Cmd) { int CmdHF14AAPDU(const char *cmd) { uint8_t data[USB_CMD_DATA_SIZE]; - uint16_t datalen = 0; + int datalen = 0; uint8_t cmdc = 0; - char buf[5] = {0}; - int i = 0; - uint32_t temp; uint8_t first, second; bool activateField = false; bool leaveSignalON = false; bool decodeTLV = false; - if (strlen(cmd)<2) { + if (strlen(cmd) < 2) { PrintAndLog("Usage: hf 14a apdu [-s] [-k] [-t] "); PrintAndLog(" -s activate field and select card"); PrintAndLog(" -k leave the signal field ON after receive response"); @@ -574,13 +571,11 @@ int CmdHF14AAPDU(const char *cmd) { return 0; } - // strip - while (*cmd==' ' || *cmd=='\t') cmd++; - - while (cmd[i]!='\0') { - if (cmd[i]==' ' || cmd[i]=='\t') { i++; continue; } - if (cmd[i]=='-') { - switch (cmd[i + 1]) { + int cmdp = 0; + while(param_getchar(cmd, cmdp) != 0x00) { + char c = param_getchar(cmd, cmdp); + if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) + switch (param_getchar_indx(cmd, 1, cmdp)) { case 's': case 'S': activateField = true; @@ -594,39 +589,29 @@ int CmdHF14AAPDU(const char *cmd) { decodeTLV = true; break; default: - PrintAndLog("Invalid option"); + PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); return 1; } - i += 2; - continue; - } - if ((cmd[i] >= '0' && cmd[i] <= '9') || - (cmd[i] >= 'a' && cmd[i] <= 'f') || - (cmd[i] >= 'A' && cmd[i] <= 'F') ) { - buf[strlen(buf) + 1] = 0x00; - buf[strlen(buf)] = cmd[i]; - i++; - - if (strlen(buf) >= 2) { - sscanf(buf, "%x", &temp); - data[datalen] = (uint8_t)(temp & 0xff); - *buf = 0; - if (datalen > sizeof(data) - 2) { - PrintAndLog("Buffer is full..."); - break; - } else { - datalen++; - } + + if (isxdigit(c)) { + switch(param_gethex_to_eol(cmd, cmdp, data, sizeof(data), &datalen)) { + case 1: + PrintAndLog("Invalid HEX value."); + return 1; + case 2: + PrintAndLog("APDU too large."); + return 1; + case 3: + PrintAndLog("Hex must have even number of digits."); + return 1; } - continue; + + // we get all the hex to end of line with spaces + break; } - PrintAndLog("Invalid char on input"); - return 1; + + cmdp++; } - if (*buf) { - PrintAndLog("Hex must have even number of digits. Detected %d symbols.", datalen * 2 + strlen(buf)); - return 1; - } if (activateField) cmdc |= ISO14A_CONNECT; diff --git a/client/util.c b/client/util.c index 8357f601..9dab4655 100644 --- a/client/util.c +++ b/client/util.c @@ -364,13 +364,19 @@ int param_getlength(const char *line, int paramnum) return en - bg + 1; } -char param_getchar(const char *line, int paramnum) -{ +char param_getchar(const char *line, int paramnum) { + return param_getchar_indx(line, 0, paramnum); +} + +char param_getchar_indx(const char *line, int indx, int paramnum) { int bg, en; if (param_getptr(line, &bg, &en, paramnum)) return 0x00; - return line[bg]; + if (bg + indx > en) + return '\0'; + + return line[bg + indx]; } uint8_t param_get8(const char *line, int paramnum) @@ -480,6 +486,51 @@ int param_gethex_ex(const char *line, int paramnum, uint8_t * data, int *hexcnt) return 0; } + +int param_gethex_to_eol(const char *line, int paramnum, uint8_t * data, int maxdatalen, int *datalen) { + int bg, en; + uint32_t temp; + char buf[5] = {0}; + + if (param_getptr(line, &bg, &en, paramnum)) return 1; + + *datalen = 0; + + int indx = bg; + while (line[indx]) { + if (line[indx] == '\t' || line[indx] == ' ') + continue; + + if (isxdigit(line[indx])) { + buf[strlen(buf) + 1] = 0x00; + buf[strlen(buf)] = line[indx]; + } else { + // if we have symbols other than spaces and hex + return 1; + } + + if (*datalen >= maxdatalen) { + // if we dont have space in buffer and have symbols to translate + return 2; + } + + if (strlen(buf) >= 2) { + sscanf(buf, "%x", &temp); + data[*datalen] = (uint8_t)(temp & 0xff); + *buf = 0; + (*datalen)++; + } + + indx++; + } + + if (strlen(buf) > 0) + //error when not completed hex bytes + return 3; + + return 0; +} + int param_getstr(const char *line, int paramnum, char * str) { int bg, en; diff --git a/client/util.h b/client/util.h index d6ed7d17..f42625b1 100644 --- a/client/util.h +++ b/client/util.h @@ -53,6 +53,7 @@ extern void SwapEndian64ex(const uint8_t *src, const size_t len, const uint8_t b extern int param_getlength(const char *line, int paramnum); extern char param_getchar(const char *line, int paramnum); +extern char param_getchar_indx(const char *line, int indx, int paramnum); extern int param_getptr(const char *line, int *bg, int *en, int paramnum); extern uint8_t param_get8(const char *line, int paramnum); extern uint8_t param_get8ex(const char *line, int paramnum, int deflt, int base); @@ -62,6 +63,7 @@ extern uint8_t param_getdec(const char *line, int paramnum, uint8_t *destination extern uint8_t param_isdec(const char *line, int paramnum); extern int param_gethex(const char *line, int paramnum, uint8_t * data, int hexcnt); extern int param_gethex_ex(const char *line, int paramnum, uint8_t * data, int *hexcnt); +extern int param_gethex_to_eol(const char *line, int paramnum, uint8_t * data, int maxdatalen, int *datalen); extern int param_getstr(const char *line, int paramnum, char * str); extern int hextobinarray( char *target, char *source); From d1300b47ae47db3fef504d414cd0e86729234f85 Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 31 Oct 2017 16:08:58 +0200 Subject: [PATCH 043/310] apdu sending refactoring --- client/cmdhf14a.c | 167 +++++++++++++++++++++++++++------------------- 1 file changed, 99 insertions(+), 68 deletions(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 336cb0d2..97a57c5f 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -554,11 +554,86 @@ int CmdHF14ASnoop(const char *Cmd) { return 0; } +int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int *dataoutlen) { + uint8_t data[USB_CMD_DATA_SIZE]; + int datalen; + uint8_t cmdc = 0; + uint8_t first, second; + + if (activateField) + cmdc |= ISO14A_CONNECT; + if (leaveSignalON) + cmdc |= ISO14A_NO_DISCONNECT; + + // ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02 + memcpy(data + 1, datain, datainlen); + data[0] = 0x02; // bnr,nad,cid,chn=0; i-block(0x00) + datalen = datainlen + 1; + + ComputeCrc14443(CRC_14443_A, data, datalen, &first, &second); + data[datalen++] = first; + data[datalen++] = second; + + // "Command APDU" length should be 5+255+1, but javacard's APDU buffer might be smaller - 133 bytes + // https://stackoverflow.com/questions/32994936/safe-max-java-card-apdu-data-command-and-respond-size + // here length USB_CMD_DATA_SIZE=512 + // timeout timeout14a * 1.06 / 100, true, size, &keyBlock[6 * c], e_sector); // timeout is (ms * 106)/10 or us*0.0106 + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_RAW | ISO14A_SET_TIMEOUT | cmdc, (datalen & 0xFFFF), 1000 * 1000 * 1.06 / 100}}; + memcpy(c.d.asBytes, data, datalen); + SendCommand(&c); + + uint8_t *recv; + UsbCommand resp; + + if (activateField) { + if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) + return 1; + if (resp.arg[0] != 1) + return 1; + } + + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + recv = resp.d.asBytes; + uint8_t iLen = resp.arg[0]; + + *dataoutlen = iLen - 1 - 2; + if (*dataoutlen < 0) + *dataoutlen = 0; + memcpy(dataout, recv + 1, *dataoutlen); + + if(!iLen) + return 1; + + // check apdu length + if (iLen < 5) { + PrintAndLog("APDU ERROR: Small APDU response."); + return 2; + } + + // check block + if (data[0] != recv[0]) { + PrintAndLog("APDU ERROR: Block type mismatch: %02x-%02x", data[0], recv[0]); + return 2; + } + + // CRC Check + ComputeCrc14443(CRC_14443_A, recv, iLen, &first, &second); + if (first || second) { + PrintAndLog("APDU ERROR: ISO 14443A CRC error."); + return 3; + } + + } else { + PrintAndLog("APDU ERROR: Reply timeout."); + return 4; + } + + return 0; +} + int CmdHF14AAPDU(const char *cmd) { uint8_t data[USB_CMD_DATA_SIZE]; int datalen = 0; - uint8_t cmdc = 0; - uint8_t first, second; bool activateField = false; bool leaveSignalON = false; bool decodeTLV = false; @@ -594,7 +669,8 @@ int CmdHF14AAPDU(const char *cmd) { } if (isxdigit(c)) { - switch(param_gethex_to_eol(cmd, cmdp, data, sizeof(data), &datalen)) { + // len = data + PCB(1b) + CRC(2b) + switch(param_gethex_to_eol(cmd, cmdp, data, sizeof(data) - 1 - 2, &datalen)) { case 1: PrintAndLog("Invalid HEX value."); return 1; @@ -612,78 +688,33 @@ int CmdHF14AAPDU(const char *cmd) { cmdp++; } - - if (activateField) - cmdc |= ISO14A_CONNECT; - if (leaveSignalON) - cmdc |= ISO14A_NO_DISCONNECT; - - // ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02 - memmove(data + 1, data, datalen); - data[0] = 0x02; // bnr,nad,cid,chn=0; i-block(0x00) - datalen++; - - ComputeCrc14443(CRC_14443_A, data, datalen, &first, &second); - data[datalen++] = first; - data[datalen++] = second; PrintAndLog("--%s %s %s >>>> %s", activateField ? "sel": "", leaveSignalON ? "keep": "", decodeTLV ? "TLV": "", sprint_hex(data, datalen)); - // "Command APDU" length should be 5+255+1, but javacard's APDU buffer might be smaller - 133 bytes - // https://stackoverflow.com/questions/32994936/safe-max-java-card-apdu-data-command-and-respond-size - // here length USB_CMD_DATA_SIZE=512 - // timeout timeout14a * 1.06 / 100, true, size, &keyBlock[6 * c], e_sector); // timeout is (ms * 106)/10 or us*0.0106 - UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_RAW | ISO14A_SET_TIMEOUT | cmdc, (datalen & 0xFFFF), 1000 * 1000 * 1.06 / 100}}; - memcpy(c.d.asBytes, data, datalen); - SendCommand(&c); - - uint8_t *recv; - UsbCommand resp; - - if (activateField) { - if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) - return 2; - if (resp.arg[0] != 1) + switch(ExchangeAPDU14a(data, datalen, activateField, leaveSignalON, data, &datalen)) { + case 0: + break; + case 1: + PrintAndLog("APDU ERROR: Send APDU error."); + return 1; + case 2: return 2; + case 3: + return 3; + case 4: + return 4; + default: + return 5; } - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - recv = resp.d.asBytes; - uint8_t iLen = resp.arg[0]; - if(!iLen) - return 2; + PrintAndLog("<<<< %s", sprint_hex(data, datalen)); + + PrintAndLog("APDU response: %02x %02x", data[datalen - 2], data[datalen - 1]); // TODO add APDU descriptions - PrintAndLog("<<<< %s", sprint_hex(recv, iLen)); - - // check apdu length - if (iLen < 5) { - PrintAndLog("ERROR: Small APDU response."); - return 3; - } - - // check block - if (data[0] != recv[0]) { - PrintAndLog("ERROR: Block type mismatch: %02x-%02x", data[0], recv[0]); - return 3; - } - - // CRC Check - ComputeCrc14443(CRC_14443_A, recv, iLen, &first, &second); - if (first || second) { - PrintAndLog("ERROR: ISO 14443A CRC error."); - return 3; - } - - PrintAndLog("APDU response: %02x %02x", recv[iLen - 4], recv[iLen - 3]); // TODO add APDU descriptions - - // here TLV decoder... - if (decodeTLV) { - } - - } else { - PrintAndLog("ERROR: Reply timeout."); - return 3; - } + // here TLV decoder... + if (decodeTLV) { + PrintAndLog("--- TLV decoded:"); + } return 0; } From 6fc6cd0f57c9f036e3d1e976607df87dbb0214b2 Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 31 Oct 2017 16:20:23 +0200 Subject: [PATCH 044/310] export apdu function --- client/cmdhf14a.c | 17 ----------------- client/cmdhf14a.h | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 97a57c5f..cdaa18cd 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -9,24 +9,7 @@ // High frequency ISO14443A commands //----------------------------------------------------------------------------- -#include -#include -#include -#include -#include -#include "util.h" -#include "util_posix.h" -#include "iso14443crc.h" -#include "data.h" -#include "proxmark3.h" -#include "ui.h" -#include "cmdparser.h" #include "cmdhf14a.h" -#include "common.h" -#include "cmdmain.h" -#include "mifare.h" -#include "cmdhfmfu.h" -#include "mifarehost.h" static int CmdHelp(const char *Cmd); static int waitCmd(uint8_t iLen); diff --git a/client/cmdhf14a.h b/client/cmdhf14a.h index dfdf1f4a..0b58a55a 100644 --- a/client/cmdhf14a.h +++ b/client/cmdhf14a.h @@ -12,7 +12,24 @@ #ifndef CMDHF14A_H__ #define CMDHF14A_H__ +#include #include +#include +#include +#include +#include +#include "util.h" +#include "util_posix.h" +#include "iso14443crc.h" +#include "data.h" +#include "proxmark3.h" +#include "ui.h" +#include "cmdparser.h" +#include "common.h" +#include "cmdmain.h" +#include "mifare.h" +#include "cmdhfmfu.h" +#include "mifarehost.h" int CmdHF14A(const char *Cmd); int CmdHF14AList(const char *Cmd); @@ -22,4 +39,6 @@ int CmdHF14ASim(const char *Cmd); int CmdHF14ASnoop(const char *Cmd); char* getTagInfo(uint8_t uid); +extern int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int *dataoutlen); + #endif From a23f000182dfde6f2b429c15ff8a7cff87c8a40f Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 31 Oct 2017 16:25:42 +0200 Subject: [PATCH 045/310] move strucrture to .h file --- client/cmdhf14a.c | 7 ------- client/cmdhf14a.h | 6 ++++++ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index cdaa18cd..f6ed899a 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -14,12 +14,6 @@ static int CmdHelp(const char *Cmd); static int waitCmd(uint8_t iLen); -// structure and database for uid -> tagtype lookups -typedef struct { - uint8_t uid; - char* desc; -} manufactureName; - const manufactureName manufactureMapping[] = { // ID, "Vendor Country" { 0x01, "Motorola UK" }, @@ -93,7 +87,6 @@ const manufactureName manufactureMapping[] = { { 0x00, "no tag-info available" } // must be the last entry }; - // get a product description based on the UID // uid[8] tag uid // returns description of the best match diff --git a/client/cmdhf14a.h b/client/cmdhf14a.h index 0b58a55a..407cf07d 100644 --- a/client/cmdhf14a.h +++ b/client/cmdhf14a.h @@ -31,6 +31,12 @@ #include "cmdhfmfu.h" #include "mifarehost.h" +// structure and database for uid -> tagtype lookups +typedef struct { + uint8_t uid; + char* desc; +} manufactureName; + int CmdHF14A(const char *Cmd); int CmdHF14AList(const char *Cmd); int CmdHF14AMifare(const char *Cmd); From fe346768b35409677b6c7f40a6f2a6768cf54ac3 Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 31 Oct 2017 18:14:32 +0200 Subject: [PATCH 046/310] added showing apdu response codes --- client/Makefile | 1 + client/cmdhf14a.c | 4 +- client/cmdhf14a.h | 1 + client/emv/apduinfo.c | 304 ++++++++++++++++++++++++++++++++++++++++++ client/emv/apduinfo.h | 34 +++++ client/obj/emv/.dummy | 0 6 files changed, 342 insertions(+), 2 deletions(-) create mode 100644 client/emv/apduinfo.c create mode 100644 client/emv/apduinfo.h create mode 100644 client/obj/emv/.dummy diff --git a/client/Makefile b/client/Makefile index 3a96e9e9..8e1574d4 100644 --- a/client/Makefile +++ b/client/Makefile @@ -107,6 +107,7 @@ CMDSRCS = crapto1/crapto1.c\ ui.c \ cmddata.c \ lfdemod.c \ + emv/apduinfo.c\ cmdhf.c \ cmdhf14a.c \ cmdhf14b.c \ diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index f6ed899a..6d6ea854 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// 2011, Merlok +// 2011, 2017 Merlok // Copyright (C) 2010 iZsh , Hagen Fritsch // // This code is licensed to you under the terms of the GNU GPL, version 2 or, @@ -685,7 +685,7 @@ int CmdHF14AAPDU(const char *cmd) { PrintAndLog("<<<< %s", sprint_hex(data, datalen)); - PrintAndLog("APDU response: %02x %02x", data[datalen - 2], data[datalen - 1]); // TODO add APDU descriptions + PrintAndLog("APDU response: %02x %02x - %s", data[datalen - 2], data[datalen - 1], GetAPDUCodeDescription(data[datalen - 2], data[datalen - 1])); // here TLV decoder... if (decodeTLV) { diff --git a/client/cmdhf14a.h b/client/cmdhf14a.h index 407cf07d..919ece5a 100644 --- a/client/cmdhf14a.h +++ b/client/cmdhf14a.h @@ -30,6 +30,7 @@ #include "mifare.h" #include "cmdhfmfu.h" #include "mifarehost.h" +#include "emv/apduinfo.h" // structure and database for uid -> tagtype lookups typedef struct { diff --git a/client/emv/apduinfo.c b/client/emv/apduinfo.c new file mode 100644 index 00000000..e2e51d9d --- /dev/null +++ b/client/emv/apduinfo.c @@ -0,0 +1,304 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 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. +//----------------------------------------------------------------------------- +// APDU status bytes information +//----------------------------------------------------------------------------- + +#include "apduinfo.h" + +const APDUCode APDUCodeTable[] = { + // ID Type Description + {"XXXX", APDUCODE_TYPE_NONE, ""}, // blank string + {"6---", APDUCODE_TYPE_ERROR, "Class not supported."}, + {"61--", APDUCODE_TYPE_INFO, "Response bytes still available"}, + {"61XX", APDUCODE_TYPE_INFO, "Command successfully executed; 'XX' bytes of data are available and can be requested using GET RESPONSE."}, + {"62--", APDUCODE_TYPE_WARNING, "State of non-volatile memory unchanged"}, + {"6200", APDUCODE_TYPE_WARNING, "No information given (NV-Ram not changed)"}, + {"6201", APDUCODE_TYPE_WARNING, "NV-Ram not changed 1."}, + {"6281", APDUCODE_TYPE_WARNING, "Part of returned data may be corrupted"}, + {"6282", APDUCODE_TYPE_WARNING, "End of file/record reached before reading Le bytes"}, + {"6283", APDUCODE_TYPE_WARNING, "Selected file invalidated"}, + {"6284", APDUCODE_TYPE_WARNING, "Selected file is not valid. FCI not formated according to ISO"}, + {"6285", APDUCODE_TYPE_WARNING, "No input data available from a sensor on the card. No Purse Engine enslaved for R3bc"}, + {"62A2", APDUCODE_TYPE_WARNING, "Wrong R-MAC"}, + {"62A4", APDUCODE_TYPE_WARNING, "Card locked (during reset( ))"}, + {"62CX", APDUCODE_TYPE_WARNING, "Counter with value x (command dependent)"}, + {"62F1", APDUCODE_TYPE_WARNING, "Wrong C-MAC"}, + {"62F3", APDUCODE_TYPE_WARNING, "Internal reset"}, + {"62F5", APDUCODE_TYPE_WARNING, "Default agent locked"}, + {"62F7", APDUCODE_TYPE_WARNING, "Cardholder locked"}, + {"62F8", APDUCODE_TYPE_WARNING, "Basement is current agent"}, + {"62F9", APDUCODE_TYPE_WARNING, "CALC Key Set not unblocked"}, + {"62FX", APDUCODE_TYPE_WARNING, "-"}, + {"62XX", APDUCODE_TYPE_WARNING, "RFU"}, + {"63--", APDUCODE_TYPE_WARNING, "State of non-volatile memory changed"}, + {"6300", APDUCODE_TYPE_WARNING, "No information given (NV-Ram changed)"}, + {"6381", APDUCODE_TYPE_WARNING, "File filled up by the last write. Loading/updating is not allowed."}, + {"6382", APDUCODE_TYPE_WARNING, "Card key not supported."}, + {"6383", APDUCODE_TYPE_WARNING, "Reader key not supported."}, + {"6384", APDUCODE_TYPE_WARNING, "Plaintext transmission not supported."}, + {"6385", APDUCODE_TYPE_WARNING, "Secured transmission not supported."}, + {"6386", APDUCODE_TYPE_WARNING, "Volatile memory is not available."}, + {"6387", APDUCODE_TYPE_WARNING, "Non-volatile memory is not available."}, + {"6388", APDUCODE_TYPE_WARNING, "Key number not valid."}, + {"6389", APDUCODE_TYPE_WARNING, "Key length is not correct."}, + {"63C0", APDUCODE_TYPE_WARNING, "Verify fail, no try left."}, + {"63C1", APDUCODE_TYPE_WARNING, "Verify fail, 1 try left."}, + {"63C2", APDUCODE_TYPE_WARNING, "Verify fail, 2 tries left."}, + {"63C3", APDUCODE_TYPE_WARNING, "Verify fail, 3 tries left."}, + {"63CX", APDUCODE_TYPE_WARNING, "The counter has reached the value 'x' (0 = x = 15) (command dependent)."}, + {"63F1", APDUCODE_TYPE_WARNING, "More data expected."}, + {"63F2", APDUCODE_TYPE_WARNING, "More data expected and proactive command pending."}, + {"63FX", APDUCODE_TYPE_WARNING, "-"}, + {"63XX", APDUCODE_TYPE_WARNING, "RFU"}, + {"64--", APDUCODE_TYPE_ERROR, "State of non-volatile memory unchanged"}, + {"6400", APDUCODE_TYPE_ERROR, "No information given (NV-Ram not changed)"}, + {"6401", APDUCODE_TYPE_ERROR, "Command timeout. Immediate response required by the card."}, + {"64XX", APDUCODE_TYPE_ERROR, "RFU"}, + {"65--", APDUCODE_TYPE_ERROR, "State of non-volatile memory changed"}, + {"6500", APDUCODE_TYPE_ERROR, "No information given"}, + {"6501", APDUCODE_TYPE_ERROR, "Write error. Memory failure. There have been problems in writing or reading the EEPROM. Other hardware problems may also bring this error."}, + {"6581", APDUCODE_TYPE_ERROR, "Memory failure"}, + {"65FX", APDUCODE_TYPE_ERROR, "-"}, + {"65XX", APDUCODE_TYPE_ERROR, "RFU"}, + {"66--", APDUCODE_TYPE_SECURITY, " "}, + {"6600", APDUCODE_TYPE_SECURITY, "Error while receiving (timeout)"}, + {"6601", APDUCODE_TYPE_SECURITY, "Error while receiving (character parity error)"}, + {"6602", APDUCODE_TYPE_SECURITY, "Wrong checksum"}, + {"6603", APDUCODE_TYPE_SECURITY, "The current DF file without FCI"}, + {"6604", APDUCODE_TYPE_SECURITY, "No SF or KF under the current DF"}, + {"6669", APDUCODE_TYPE_SECURITY, "Incorrect Encryption/Decryption Padding"}, + {"66XX", APDUCODE_TYPE_SECURITY, "-"}, + {"67--", APDUCODE_TYPE_ERROR, " "}, + {"6700", APDUCODE_TYPE_ERROR, "Wrong length"}, + {"67XX", APDUCODE_TYPE_ERROR, "length incorrect (procedure)(ISO 7816-3)"}, + {"68--", APDUCODE_TYPE_ERROR, "Functions in CLA not supported"}, + {"6800", APDUCODE_TYPE_ERROR, "No information given (The request function is not supported by the card)"}, + {"6881", APDUCODE_TYPE_ERROR, "Logical channel not supported"}, + {"6882", APDUCODE_TYPE_ERROR, "Secure messaging not supported"}, + {"6883", APDUCODE_TYPE_ERROR, "Last command of the chain expected"}, + {"6884", APDUCODE_TYPE_ERROR, "Command chaining not supported"}, + {"68FX", APDUCODE_TYPE_ERROR, "-"}, + {"68XX", APDUCODE_TYPE_ERROR, "RFU"}, + {"69--", APDUCODE_TYPE_ERROR, "Command not allowed"}, + {"6900", APDUCODE_TYPE_ERROR, "No information given (Command not allowed)"}, + {"6901", APDUCODE_TYPE_ERROR, "Command not accepted (inactive state)"}, + {"6981", APDUCODE_TYPE_ERROR, "Command incompatible with file structure"}, + {"6982", APDUCODE_TYPE_ERROR, "Security condition not satisfied."}, + {"6983", APDUCODE_TYPE_ERROR, "Authentication method blocked"}, + {"6984", APDUCODE_TYPE_ERROR, "Referenced data reversibly blocked (invalidated)"}, + {"6985", APDUCODE_TYPE_ERROR, "Conditions of use not satisfied."}, + {"6986", APDUCODE_TYPE_ERROR, "Command not allowed (no current EF)"}, + {"6987", APDUCODE_TYPE_ERROR, "Expected secure messaging (SM) object missing"}, + {"6988", APDUCODE_TYPE_ERROR, "Incorrect secure messaging (SM) data object"}, + {"698D", APDUCODE_TYPE_NONE, "Reserved"}, + {"6996", APDUCODE_TYPE_ERROR, "Data must be updated again"}, + {"69E1", APDUCODE_TYPE_ERROR, "POL1 of the currently Enabled Profile prevents this action."}, + {"69F0", APDUCODE_TYPE_ERROR, "Permission Denied"}, + {"69F1", APDUCODE_TYPE_ERROR, "Permission Denied - Missing Privilege"}, + {"69FX", APDUCODE_TYPE_ERROR, "-"}, + {"69XX", APDUCODE_TYPE_ERROR, "RFU"}, + {"6A--", APDUCODE_TYPE_ERROR, "Wrong parameter(s) P1-P2"}, + {"6A00", APDUCODE_TYPE_ERROR, "No information given (Bytes P1 and/or P2 are incorrect)"}, + {"6A80", APDUCODE_TYPE_ERROR, "The parameters in the data field are incorrect."}, + {"6A81", APDUCODE_TYPE_ERROR, "Function not supported"}, + {"6A82", APDUCODE_TYPE_ERROR, "File not found"}, + {"6A83", APDUCODE_TYPE_ERROR, "Record not found"}, + {"6A84", APDUCODE_TYPE_ERROR, "There is insufficient memory space in record or file"}, + {"6A85", APDUCODE_TYPE_ERROR, "Lc inconsistent with TLV structure"}, + {"6A86", APDUCODE_TYPE_ERROR, "Incorrect P1 or P2 parameter."}, + {"6A87", APDUCODE_TYPE_ERROR, "Lc inconsistent with P1-P2"}, + {"6A88", APDUCODE_TYPE_ERROR, "Referenced data not found"}, + {"6A89", APDUCODE_TYPE_ERROR, "File already exists"}, + {"6A8A", APDUCODE_TYPE_ERROR, "DF name already exists."}, + {"6AF0", APDUCODE_TYPE_ERROR, "Wrong parameter value"}, + {"6AFX", APDUCODE_TYPE_ERROR, "-"}, + {"6AXX", APDUCODE_TYPE_ERROR, "RFU"}, + {"6B--", APDUCODE_TYPE_ERROR, " "}, + {"6B00", APDUCODE_TYPE_ERROR, "Wrong parameter(s) P1-P2"}, + {"6BXX", APDUCODE_TYPE_ERROR, "Reference incorrect (procedure byte), (ISO 7816-3)"}, + {"6C--", APDUCODE_TYPE_ERROR, "Wrong length Le"}, + {"6C00", APDUCODE_TYPE_ERROR, "Incorrect P3 length."}, + {"6CXX", APDUCODE_TYPE_ERROR, "Bad length value in Le; 'xx' is the correct exact Le"}, + {"6D--", APDUCODE_TYPE_ERROR, " "}, + {"6D00", APDUCODE_TYPE_ERROR, "Instruction code not supported or invalid"}, + {"6DXX", APDUCODE_TYPE_ERROR, "Instruction code not programmed or invalid (procedure byte), (ISO 7816-3)"}, + {"6E--", APDUCODE_TYPE_ERROR, " "}, + {"6E00", APDUCODE_TYPE_ERROR, "Class not supported"}, + {"6EXX", APDUCODE_TYPE_ERROR, "Instruction class not supported (procedure byte), (ISO 7816-3)"}, + {"6F--", APDUCODE_TYPE_ERROR, "Internal exception"}, + {"6F00", APDUCODE_TYPE_ERROR, "Command aborted - more exact diagnosis not possible (e.g., operating system error)."}, + {"6FFF", APDUCODE_TYPE_ERROR, "Card dead (overuse, …)"}, + {"6FXX", APDUCODE_TYPE_ERROR, "No precise diagnosis (procedure byte), (ISO 7816-3)"}, + {"9---", APDUCODE_TYPE_NONE, ""}, + {"9000", APDUCODE_TYPE_INFO, "Command successfully executed (OK)."}, + {"9004", APDUCODE_TYPE_WARNING, "PIN not succesfully verified, 3 or more PIN tries left"}, + {"9008", APDUCODE_TYPE_NONE, "Key/file not found"}, + {"9080", APDUCODE_TYPE_WARNING, "Unblock Try Counter has reached zero"}, + {"9100", APDUCODE_TYPE_NONE, "OK"}, + {"9101", APDUCODE_TYPE_NONE, "States.activity, States.lock Status or States.lockable has wrong value"}, + {"9102", APDUCODE_TYPE_NONE, "Transaction number reached its limit"}, + {"910C", APDUCODE_TYPE_NONE, "No changes"}, + {"910E", APDUCODE_TYPE_NONE, "Insufficient NV-Memory to complete command"}, + {"911C", APDUCODE_TYPE_NONE, "Command code not supported"}, + {"911E", APDUCODE_TYPE_NONE, "CRC or MAC does not match data"}, + {"9140", APDUCODE_TYPE_NONE, "Invalid key number specified"}, + {"917E", APDUCODE_TYPE_NONE, "Length of command string invalid"}, + {"919D", APDUCODE_TYPE_NONE, "Not allow the requested command"}, + {"919E", APDUCODE_TYPE_NONE, "Value of the parameter invalid"}, + {"91A0", APDUCODE_TYPE_NONE, "Requested AID not present on PICC"}, + {"91A1", APDUCODE_TYPE_NONE, "Unrecoverable error within application"}, + {"91AE", APDUCODE_TYPE_NONE, "Authentication status does not allow the requested command"}, + {"91AF", APDUCODE_TYPE_NONE, "Additional data frame is expected to be sent"}, + {"91BE", APDUCODE_TYPE_NONE, "Out of boundary"}, + {"91C1", APDUCODE_TYPE_NONE, "Unrecoverable error within PICC"}, + {"91CA", APDUCODE_TYPE_NONE, "Previous Command was not fully completed"}, + {"91CD", APDUCODE_TYPE_NONE, "PICC was disabled by an unrecoverable error"}, + {"91CE", APDUCODE_TYPE_NONE, "Number of Applications limited to 28"}, + {"91DE", APDUCODE_TYPE_NONE, "File or application already exists"}, + {"91EE", APDUCODE_TYPE_NONE, "Could not complete NV-write operation due to loss of power"}, + {"91F0", APDUCODE_TYPE_NONE, "Specified file number does not exist"}, + {"91F1", APDUCODE_TYPE_NONE, "Unrecoverable error within file"}, + {"920x", APDUCODE_TYPE_INFO, "Writing to EEPROM successful after 'x' attempts."}, + {"9210", APDUCODE_TYPE_ERROR, "Insufficient memory. No more storage available."}, + {"9240", APDUCODE_TYPE_ERROR, "Writing to EEPROM not successful."}, + {"9301", APDUCODE_TYPE_NONE, "Integrity error"}, + {"9302", APDUCODE_TYPE_NONE, "Candidate S2 invalid"}, + {"9303", APDUCODE_TYPE_ERROR, "Application is permanently locked"}, + {"9400", APDUCODE_TYPE_ERROR, "No EF selected."}, + {"9401", APDUCODE_TYPE_NONE, "Candidate currency code does not match purse currency"}, + {"9402", APDUCODE_TYPE_NONE, "Candidate amount too high"}, + {"9402", APDUCODE_TYPE_ERROR, "Address range exceeded."}, + {"9403", APDUCODE_TYPE_NONE, "Candidate amount too low"}, + {"9404", APDUCODE_TYPE_ERROR, "FID not found, record not found or comparison pattern not found."}, + {"9405", APDUCODE_TYPE_NONE, "Problems in the data field"}, + {"9406", APDUCODE_TYPE_ERROR, "Required MAC unavailable"}, + {"9407", APDUCODE_TYPE_NONE, "Bad currency : purse engine has no slot with R3bc currency"}, + {"9408", APDUCODE_TYPE_NONE, "R3bc currency not supported in purse engine"}, + {"9408", APDUCODE_TYPE_ERROR, "Selected file type does not match command."}, + {"9580", APDUCODE_TYPE_NONE, "Bad sequence"}, + {"9681", APDUCODE_TYPE_NONE, "Slave not found"}, + {"9700", APDUCODE_TYPE_NONE, "PIN blocked and Unblock Try Counter is 1 or 2"}, + {"9702", APDUCODE_TYPE_NONE, "Main keys are blocked"}, + {"9704", APDUCODE_TYPE_NONE, "PIN not succesfully verified, 3 or more PIN tries left"}, + {"9784", APDUCODE_TYPE_NONE, "Base key"}, + {"9785", APDUCODE_TYPE_NONE, "Limit exceeded - C-MAC key"}, + {"9786", APDUCODE_TYPE_NONE, "SM error - Limit exceeded - R-MAC key"}, + {"9787", APDUCODE_TYPE_NONE, "Limit exceeded - sequence counter"}, + {"9788", APDUCODE_TYPE_NONE, "Limit exceeded - R-MAC length"}, + {"9789", APDUCODE_TYPE_NONE, "Service not available"}, + {"9802", APDUCODE_TYPE_ERROR, "No PIN defined."}, + {"9804", APDUCODE_TYPE_ERROR, "Access conditions not satisfied, authentication failed."}, + {"9835", APDUCODE_TYPE_ERROR, "ASK RANDOM or GIVE RANDOM not executed."}, + {"9840", APDUCODE_TYPE_ERROR, "PIN verification not successful."}, + {"9850", APDUCODE_TYPE_ERROR, "INCREASE or DECREASE could not be executed because a limit has been reached."}, + {"9862", APDUCODE_TYPE_ERROR, "Authentication Error, application specific (incorrect MAC)"}, + {"9900", APDUCODE_TYPE_NONE, "1 PIN try left"}, + {"9904", APDUCODE_TYPE_NONE, "PIN not succesfully verified, 1 PIN try left"}, + {"9985", APDUCODE_TYPE_NONE, "Wrong status - Cardholder lock"}, + {"9986", APDUCODE_TYPE_ERROR, "Missing privilege"}, + {"9987", APDUCODE_TYPE_NONE, "PIN is not installed"}, + {"9988", APDUCODE_TYPE_NONE, "Wrong status - R-MAC state"}, + {"9A00", APDUCODE_TYPE_NONE, "2 PIN try left"}, + {"9A04", APDUCODE_TYPE_NONE, "PIN not succesfully verified, 2 PIN try left"}, + {"9A71", APDUCODE_TYPE_NONE, "Wrong parameter value - Double agent AID"}, + {"9A72", APDUCODE_TYPE_NONE, "Wrong parameter value - Double agent Type"}, + {"9D05", APDUCODE_TYPE_ERROR, "Incorrect certificate type"}, + {"9D07", APDUCODE_TYPE_ERROR, "Incorrect session data size"}, + {"9D08", APDUCODE_TYPE_ERROR, "Incorrect DIR file record size"}, + {"9D09", APDUCODE_TYPE_ERROR, "Incorrect FCI record size"}, + {"9D0A", APDUCODE_TYPE_ERROR, "Incorrect code size"}, + {"9D10", APDUCODE_TYPE_ERROR, "Insufficient memory to load application"}, + {"9D11", APDUCODE_TYPE_ERROR, "Invalid AID"}, + {"9D12", APDUCODE_TYPE_ERROR, "Duplicate AID"}, + {"9D13", APDUCODE_TYPE_ERROR, "Application previously loaded"}, + {"9D14", APDUCODE_TYPE_ERROR, "Application history list full"}, + {"9D15", APDUCODE_TYPE_ERROR, "Application not open"}, + {"9D17", APDUCODE_TYPE_ERROR, "Invalid offset"}, + {"9D18", APDUCODE_TYPE_ERROR, "Application already loaded"}, + {"9D19", APDUCODE_TYPE_ERROR, "Invalid certificate"}, + {"9D1A", APDUCODE_TYPE_ERROR, "Invalid signature"}, + {"9D1B", APDUCODE_TYPE_ERROR, "Invalid KTU"}, + {"9D1D", APDUCODE_TYPE_ERROR, "MSM controls not set"}, + {"9D1E", APDUCODE_TYPE_ERROR, "Application signature does not exist"}, + {"9D1F", APDUCODE_TYPE_ERROR, "KTU does not exist"}, + {"9D20", APDUCODE_TYPE_ERROR, "Application not loaded"}, + {"9D21", APDUCODE_TYPE_ERROR, "Invalid Open command data length"}, + {"9D30", APDUCODE_TYPE_ERROR, "Check data parameter is incorrect (invalid start address)"}, + {"9D31", APDUCODE_TYPE_ERROR, "Check data parameter is incorrect (invalid length)"}, + {"9D32", APDUCODE_TYPE_ERROR, "Check data parameter is incorrect (illegal memory check area)"}, + {"9D40", APDUCODE_TYPE_ERROR, "Invalid MSM Controls ciphertext"}, + {"9D41", APDUCODE_TYPE_ERROR, "MSM controls already set"}, + {"9D42", APDUCODE_TYPE_ERROR, "Set MSM Controls data length less than 2 bytes"}, + {"9D43", APDUCODE_TYPE_ERROR, "Invalid MSM Controls data length"}, + {"9D44", APDUCODE_TYPE_ERROR, "Excess MSM Controls ciphertext"}, + {"9D45", APDUCODE_TYPE_ERROR, "Verification of MSM Controls data failed"}, + {"9D50", APDUCODE_TYPE_ERROR, "Invalid MCD Issuer production ID"}, + {"9D51", APDUCODE_TYPE_ERROR, "Invalid MCD Issuer ID"}, + {"9D52", APDUCODE_TYPE_ERROR, "Invalid set MSM controls data date"}, + {"9D53", APDUCODE_TYPE_ERROR, "Invalid MCD number"}, + {"9D54", APDUCODE_TYPE_ERROR, "Reserved field error"}, + {"9D55", APDUCODE_TYPE_ERROR, "Reserved field error"}, + {"9D56", APDUCODE_TYPE_ERROR, "Reserved field error"}, + {"9D57", APDUCODE_TYPE_ERROR, "Reserved field error"}, + {"9D60", APDUCODE_TYPE_ERROR, "MAC verification failed"}, + {"9D61", APDUCODE_TYPE_ERROR, "Maximum number of unblocks reached"}, + {"9D62", APDUCODE_TYPE_ERROR, "Card was not blocked"}, + {"9D63", APDUCODE_TYPE_ERROR, "Crypto functions not available"}, + {"9D64", APDUCODE_TYPE_ERROR, "No application loaded"}, + {"9E00", APDUCODE_TYPE_NONE, "PIN not installed"}, + {"9E04", APDUCODE_TYPE_NONE, "PIN not succesfully verified, PIN not installed"}, + {"9F00", APDUCODE_TYPE_NONE, "PIN blocked and Unblock Try Counter is 3"}, + {"9F04", APDUCODE_TYPE_NONE, "PIN not succesfully verified, PIN blocked and Unblock Try Counter is 3"}, + {"9FXX", APDUCODE_TYPE_NONE, "Command successfully executed; 'xx' bytes of data are available and can be requested using GET RESPONSE."}, + {"9xXX", APDUCODE_TYPE_NONE, "Application related status, (ISO 7816-3)"} +}; + +int CodeCmp(const char *code1, const char *code2) { + int xsymb = 0; + int cmp = 0; + for (int i = 0; i < 4; i++) { + if (code1[i] == code2[i]) + cmp++; + if (code1[i] == 'X' || code2[i] == 'X') + xsymb++; + } + if (cmp == 4) + return 0; + + if (cmp + xsymb == 4) + return xsymb; + + return -1; +} + +APDUCode *GetAPDUCode(uint8_t sw1, uint8_t sw2) { + char buf[4] = {0}; + + sprintf(&buf[0], "%02X ", sw1); + sprintf(&buf[2], "%02X ", sw2); + + + int tableLen = sizeof(APDUCodeTable)/sizeof(APDUCode); + for (int i = 0; i < tableLen; i++) { + if (CodeCmp(APDUCodeTable[i].ID, buf) == 0) { // TODO make not so equal comparation... XXXX - not works... + return &APDUCodeTable[i]; + } + } + + return NULL; +} + +const char *GetAPDUCodeDescription(uint8_t sw1, uint8_t sw2) { + APDUCode *cd = GetAPDUCode(sw1, sw2); + if (cd) + return cd->Description; + else + return APDUCodeTable[0].Description; //empty string +} + + diff --git a/client/emv/apduinfo.h b/client/emv/apduinfo.h new file mode 100644 index 00000000..e388ae68 --- /dev/null +++ b/client/emv/apduinfo.h @@ -0,0 +1,34 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 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. +//----------------------------------------------------------------------------- +// APDU status bytes information +//----------------------------------------------------------------------------- + +#ifndef APDUINFO_H__ +#define APDUINFO_H__ + +#include +#include +#include +#include + +#define APDUCODE_TYPE_NONE 0 +#define APDUCODE_TYPE_INFO 1 +#define APDUCODE_TYPE_WARNING 2 +#define APDUCODE_TYPE_ERROR 3 +#define APDUCODE_TYPE_SECURITY 4 + +typedef struct { + const char *ID; + const uint8_t Type; + const char *Description; +} APDUCode; + +extern APDUCode* GetAPDUCode(uint8_t sw1, uint8_t sw2); +extern const char* GetAPDUCodeDescription(uint8_t sw1, uint8_t sw2); + +#endif diff --git a/client/obj/emv/.dummy b/client/obj/emv/.dummy new file mode 100644 index 00000000..e69de29b From 5bcb3496fa0409fb7a1dd22e9357a812c3f3f4fe Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 31 Oct 2017 19:11:23 +0200 Subject: [PATCH 047/310] small refactoring --- client/cmdhf14a.c | 2 +- client/emv/apduinfo.c | 13 +++++-------- client/emv/apduinfo.h | 2 +- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 6d6ea854..aebe2463 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -618,7 +618,7 @@ int CmdHF14AAPDU(const char *cmd) { PrintAndLog("Usage: hf 14a apdu [-s] [-k] [-t] "); PrintAndLog(" -s activate field and select card"); PrintAndLog(" -k leave the signal field ON after receive response"); - PrintAndLog(" -t executes TLV decoder if it possible"); + PrintAndLog(" -t executes TLV decoder if it possible. TODO!!!!"); return 0; } diff --git a/client/emv/apduinfo.c b/client/emv/apduinfo.c index e2e51d9d..d73c24fc 100644 --- a/client/emv/apduinfo.c +++ b/client/emv/apduinfo.c @@ -257,6 +257,7 @@ const APDUCode APDUCodeTable[] = { {"9FXX", APDUCODE_TYPE_NONE, "Command successfully executed; 'xx' bytes of data are available and can be requested using GET RESPONSE."}, {"9xXX", APDUCODE_TYPE_NONE, "Application related status, (ISO 7816-3)"} }; +const size_t APDUCodeTableLen = sizeof(APDUCodeTable)/sizeof(APDUCode); int CodeCmp(const char *code1, const char *code2) { int xsymb = 0; @@ -276,15 +277,13 @@ int CodeCmp(const char *code1, const char *code2) { return -1; } -APDUCode *GetAPDUCode(uint8_t sw1, uint8_t sw2) { +const APDUCode* const GetAPDUCode(uint8_t sw1, uint8_t sw2) { char buf[4] = {0}; sprintf(&buf[0], "%02X ", sw1); sprintf(&buf[2], "%02X ", sw2); - - int tableLen = sizeof(APDUCodeTable)/sizeof(APDUCode); - for (int i = 0; i < tableLen; i++) { + for (int i = 0; i < APDUCodeTableLen; i++) { if (CodeCmp(APDUCodeTable[i].ID, buf) == 0) { // TODO make not so equal comparation... XXXX - not works... return &APDUCodeTable[i]; } @@ -293,12 +292,10 @@ APDUCode *GetAPDUCode(uint8_t sw1, uint8_t sw2) { return NULL; } -const char *GetAPDUCodeDescription(uint8_t sw1, uint8_t sw2) { - APDUCode *cd = GetAPDUCode(sw1, sw2); +const char* GetAPDUCodeDescription(uint8_t sw1, uint8_t sw2) { + const APDUCode *cd = GetAPDUCode(sw1, sw2); if (cd) return cd->Description; else return APDUCodeTable[0].Description; //empty string } - - diff --git a/client/emv/apduinfo.h b/client/emv/apduinfo.h index e388ae68..a3fa2049 100644 --- a/client/emv/apduinfo.h +++ b/client/emv/apduinfo.h @@ -28,7 +28,7 @@ typedef struct { const char *Description; } APDUCode; -extern APDUCode* GetAPDUCode(uint8_t sw1, uint8_t sw2); +extern const APDUCode* const GetAPDUCode(uint8_t sw1, uint8_t sw2); extern const char* GetAPDUCodeDescription(uint8_t sw1, uint8_t sw2); #endif From 78a94ff9022f84d186d01e1baa0eafdf78a7652d Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 31 Oct 2017 19:19:49 +0200 Subject: [PATCH 048/310] improved GetAPDUCode() --- client/emv/apduinfo.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/client/emv/apduinfo.c b/client/emv/apduinfo.c index d73c24fc..fbdd714a 100644 --- a/client/emv/apduinfo.c +++ b/client/emv/apduinfo.c @@ -279,16 +279,33 @@ int CodeCmp(const char *code1, const char *code2) { const APDUCode* const GetAPDUCode(uint8_t sw1, uint8_t sw2) { char buf[4] = {0}; + int res; + int mineq = 100; + int mineqindx = 0; sprintf(&buf[0], "%02X ", sw1); sprintf(&buf[2], "%02X ", sw2); for (int i = 0; i < APDUCodeTableLen; i++) { - if (CodeCmp(APDUCodeTable[i].ID, buf) == 0) { // TODO make not so equal comparation... XXXX - not works... + res = CodeCmp(APDUCodeTable[i].ID, buf); + + // equal + if (res == 0) { return &APDUCodeTable[i]; } + + // with some 'X' + if (res > 0 && mineq > res) { + mineq = res; + mineqindx = i; + } } + // if we have not equal, but with some 'X' + if (mineqindx < 100) { + return &APDUCodeTable[mineqindx]; + } + return NULL; } From 7dac1034e5db678e0b8a291b58d6f2eb2fdf07df Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Wed, 1 Nov 2017 09:51:05 +0200 Subject: [PATCH 049/310] Add hf mf info, change hf mf reader (#452) * copy functionality from `hf 14a reader` to `hf 14a info` * added command `hf 14a reader` with simple anticollision-select procedure. * add parameters to `hf 14a reader`. may start and end acting as reader --- client/cmdhf14a.c | 78 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 09dcd34e..3f103f5f 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -133,7 +133,80 @@ int CmdHF14AList(const char *Cmd) return 0; } -int CmdHF14AReader(const char *Cmd) +int CmdHF14AReader(const char *Cmd) { + uint32_t cm = ISO14A_CONNECT; + bool disconnectAfter = false; + + int cmdp = 0; + while(param_getchar(Cmd, cmdp) != 0x00) { + switch(param_getchar(Cmd, cmdp)) { + case 'h': + case 'H': + PrintAndLog("Usage: hf 14a reader [d] [3]"); + PrintAndLog(" d drop the signal field after command executed"); + PrintAndLog(" x just drop the signal field"); + PrintAndLog(" 3 ISO14443-3 select only (skip RATS)"); + return 0; + case '3': + cm |= ISO14A_NO_RATS; + break; + case 'd': + case 'D': + disconnectAfter = true; + break; + case 'x': + case 'X': + disconnectAfter = true; + cm = cm - ISO14A_CONNECT; + break; + default: + PrintAndLog("Unknown command."); + return 1; + } + + cmdp++; + } + + if (!disconnectAfter) + cm |= ISO14A_NO_DISCONNECT; + + UsbCommand c = {CMD_READER_ISO_14443a, {cm, 0, 0}}; + SendCommand(&c); + + if (ISO14A_CONNECT & cm) { + UsbCommand resp; + WaitForResponse(CMD_ACK,&resp); + + iso14a_card_select_t card; + memcpy(&card, (iso14a_card_select_t *)resp.d.asBytes, sizeof(iso14a_card_select_t)); + + uint64_t select_status = resp.arg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision + + if(select_status == 0) { + PrintAndLog("iso14443a card select failed"); + return 1; + } + + if(select_status == 3) { + PrintAndLog("Card doesn't support standard iso14443-3 anticollision"); + PrintAndLog("ATQA : %02x %02x", card.atqa[1], card.atqa[0]); + return 1; + } + + PrintAndLog(" UID : %s", sprint_hex(card.uid, card.uidlen)); + PrintAndLog("ATQA : %02x %02x", card.atqa[1], card.atqa[0]); + PrintAndLog(" SAK : %02x [%d]", card.sak, resp.arg[0]); + if(card.ats_len >= 3) { // a valid ATS consists of at least the length byte (TL) and 2 CRC bytes + PrintAndLog(" ATS : %s", sprint_hex(card.ats, card.ats_len)); + } + PrintAndLog("Card is selected. You can now start sending commands"); + } else { + PrintAndLog("Field dropped."); + } + return 0; +} + +int CmdHF14AInfo(const char *Cmd) { UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0}}; SendCommand(&c); @@ -764,7 +837,8 @@ static command_t CommandTable[] = { {"help", CmdHelp, 1, "This help"}, {"list", CmdHF14AList, 0, "[Deprecated] List ISO 14443a history"}, - {"reader", CmdHF14AReader, 0, "Act like an ISO14443 Type A reader"}, + {"reader", CmdHF14AReader, 0, "Start acting like an ISO14443 Type A reader"}, + {"info", CmdHF14AInfo, 0, "Reads card and shows information about it"}, {"cuids", CmdHF14ACUIDs, 0, " Collect n>0 ISO14443 Type A UIDs in one go"}, {"sim", CmdHF14ASim, 0, " -- Simulate ISO 14443a tag"}, {"snoop", CmdHF14ASnoop, 0, "Eavesdrop ISO 14443 Type A"}, From a2bb2735d5095aabeba4a891fe4ba867a9afc2b3 Mon Sep 17 00:00:00 2001 From: merlokk Date: Wed, 1 Nov 2017 13:38:29 +0200 Subject: [PATCH 050/310] TLV decoding works --- client/Makefile | 4 + client/cmdhf14a.c | 4 +- client/cmdhf14a.h | 1 + client/emv/dump.c | 57 +++++ client/emv/dump.h | 24 +++ client/emv/emv_tags.c | 480 ++++++++++++++++++++++++++++++++++++++++++ client/emv/emv_tags.h | 24 +++ client/emv/emvcore.c | 31 +++ client/emv/emvcore.h | 31 +++ client/emv/tlv.c | 415 ++++++++++++++++++++++++++++++++++++ client/emv/tlv.h | 52 +++++ 11 files changed, 1121 insertions(+), 2 deletions(-) create mode 100644 client/emv/dump.c create mode 100644 client/emv/dump.h create mode 100644 client/emv/emv_tags.c create mode 100644 client/emv/emv_tags.h create mode 100644 client/emv/emvcore.c create mode 100644 client/emv/emvcore.h create mode 100644 client/emv/tlv.c create mode 100644 client/emv/tlv.h diff --git a/client/Makefile b/client/Makefile index 8e1574d4..30e91efb 100644 --- a/client/Makefile +++ b/client/Makefile @@ -108,6 +108,10 @@ CMDSRCS = crapto1/crapto1.c\ cmddata.c \ lfdemod.c \ emv/apduinfo.c\ + emv/dump.c\ + emv/tlv.c\ + emv/emv_tags.c\ + emv/emvcore.c\ cmdhf.c \ cmdhf14a.c \ cmdhf14b.c \ diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index aebe2463..1a7f6970 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -688,8 +688,8 @@ int CmdHF14AAPDU(const char *cmd) { PrintAndLog("APDU response: %02x %02x - %s", data[datalen - 2], data[datalen - 1], GetAPDUCodeDescription(data[datalen - 2], data[datalen - 1])); // here TLV decoder... - if (decodeTLV) { - PrintAndLog("--- TLV decoded:"); + if (decodeTLV && datalen > 4) { + TLVPrintFromBuffer(data, datalen - 2); } return 0; diff --git a/client/cmdhf14a.h b/client/cmdhf14a.h index 919ece5a..d11d17eb 100644 --- a/client/cmdhf14a.h +++ b/client/cmdhf14a.h @@ -31,6 +31,7 @@ #include "cmdhfmfu.h" #include "mifarehost.h" #include "emv/apduinfo.h" +#include "emv/emvcore.h" // structure and database for uid -> tagtype lookups typedef struct { diff --git a/client/emv/dump.c b/client/emv/dump.c new file mode 100644 index 00000000..9915ad73 --- /dev/null +++ b/client/emv/dump.c @@ -0,0 +1,57 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "dump.h" + +#include + +void dump_buffer_simple(const unsigned char *ptr, size_t len, FILE *f) +{ + int i; + + if (!f) + f = stdout; + + for (i = 0; i < len; i ++) + fprintf(f, "%s%02hhX", i ? " " : "", ptr[i]); +} + +void dump_buffer(const unsigned char *ptr, size_t len, FILE *f) +{ + int i, j; + + if (!f) + f = stdout; + + for (i = 0; i < len; i += 16) { + fprintf(f, "\t%02x:", i); + for (j = 0; j < 16; j++) { + if (i + j < len) + fprintf(f, " %02hhx", ptr[i + j]); + else + fprintf(f, " "); + } + fprintf(f, " |"); + for (j = 0; j < 16 && i + j < len; j++) { + fprintf(f, "%c", (ptr[i+j] >= 0x20 && ptr[i+j] < 0x7f) ? ptr[i+j] : '.' ); + } + fprintf(f, "\n"); + } +} + diff --git a/client/emv/dump.h b/client/emv/dump.h new file mode 100644 index 00000000..5976da44 --- /dev/null +++ b/client/emv/dump.h @@ -0,0 +1,24 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef DUMP_H +#define DUMP_H + +#include + +void dump_buffer_simple(const unsigned char *ptr, size_t len, FILE *f); +void dump_buffer(const unsigned char *ptr, size_t len, FILE *f); + +#endif diff --git a/client/emv/emv_tags.c b/client/emv/emv_tags.c new file mode 100644 index 00000000..9bcf2004 --- /dev/null +++ b/client/emv/emv_tags.c @@ -0,0 +1,480 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "tlv.h" +#include "emv_tags.h" + +#include + +enum emv_tag_t { + EMV_TAG_GENERIC, + EMV_TAG_BITMASK, + EMV_TAG_DOL, + EMV_TAG_CVM_LIST, + EMV_TAG_STRING, + EMV_TAG_NUMERIC, + EMV_TAG_YYMMDD, + EMV_TAG_FCI, +}; + +struct emv_tag { + tlv_tag_t tag; + char *name; + enum emv_tag_t type; + const void *data; +}; + +struct emv_tag_bit { + unsigned bit; + const char *name; +}; + +#define EMV_BIT(byte, bit) ((byte - 1) * 8 + (8 - bit)) +#define EMV_BIT_FINISH { (~0), NULL } + +static const struct emv_tag_bit EMV_AIP[] = { + { EMV_BIT(1, 7), "SDA supported" }, + { EMV_BIT(1, 6), "DDA supported" }, + { EMV_BIT(1, 5), "Cardholder verification is supported" }, + { EMV_BIT(1, 4), "Terminal risk management is to be performed" }, + { EMV_BIT(1, 3), "Issuer authentication is supported" }, + { EMV_BIT(1, 2), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(1, 1), "CDA supported" }, + { EMV_BIT(2, 8), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(2, 7), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(2, 6), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(2, 1), "Reserved for use by the EMV Contactless Specifications" }, + EMV_BIT_FINISH, +}; + +static const struct emv_tag_bit EMV_AUC[] = { + { EMV_BIT(1, 8), "Valid for domestic cash transactions" }, + { EMV_BIT(1, 7), "Valid for international cash transactions" }, + { EMV_BIT(1, 6), "Valid for domestic goods" }, + { EMV_BIT(1, 5), "Valid for international goods" }, + { EMV_BIT(1, 4), "Valid for domestic services" }, + { EMV_BIT(1, 3), "Valid for international services" }, + { EMV_BIT(1, 2), "Valid for ATMs" }, + { EMV_BIT(1, 1), "Valid at terminals other than ATMs" }, + { EMV_BIT(2, 8), "Domestic cashback allowed" }, + { EMV_BIT(2, 7), "International cashback allowed" }, + EMV_BIT_FINISH, +}; + +static const struct emv_tag_bit EMV_TVR[] = { + { EMV_BIT(1, 8), "Offline data authentication was not performed" }, + { EMV_BIT(1, 7), "SDA failed" }, + { EMV_BIT(1, 6), "ICC data missing" }, + { EMV_BIT(1, 5), "Card appears on terminal exception file" }, + { EMV_BIT(1, 4), "DDA failed" }, + { EMV_BIT(1, 3), "CDA failed" }, + { EMV_BIT(1, 2), "SDA selected" }, + { EMV_BIT(2, 8), "ICC and terminal have different application versions" }, + { EMV_BIT(2, 7), "Expired application" }, + { EMV_BIT(2, 6), "Application not yet effective" }, + { EMV_BIT(2, 5), "Requested service not allowed for card product" }, + { EMV_BIT(2, 4), "New card" }, + { EMV_BIT(3, 8), "Cardholder verification was not successful" }, + { EMV_BIT(3, 7), "Unrecognised CVM" }, + { EMV_BIT(3, 6), "PIN Try Limit exceeded" }, + { EMV_BIT(3, 5), "PIN entry required and PIN pad not present or not working" }, + { EMV_BIT(3, 4), "PIN entry required, PIN pad present, but PIN was not entered" }, + { EMV_BIT(3, 3), "Online PIN entered" }, + { EMV_BIT(4, 8), "Transaction exceeds floor limit" }, + { EMV_BIT(4, 7), "Lower consecutive offline limit exceeded" }, + { EMV_BIT(4, 6), "Upper consecutive offline limit exceeded" }, + { EMV_BIT(4, 5), "Transaction selected randomly for online processing" }, + { EMV_BIT(4, 4), "Merchant forced transaction online" }, + { EMV_BIT(5, 8), "Default TDOL used" }, + { EMV_BIT(5, 7), "Issuer authentication failed" }, + { EMV_BIT(5, 6), "Script processing failed before final GENERATE AC" }, + { EMV_BIT(5, 5), "Script processing failed after final GENERATE AC" }, + { EMV_BIT(5, 4), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(5, 3), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(5, 2), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(5, 1), "Reserved for use by the EMV Contactless Specifications" }, + EMV_BIT_FINISH, +}; + +static const struct emv_tag emv_tags[] = { + { 0x00 , "Unknown ???" }, + { 0x4f , "Application Dedicated File (ADF) Name" }, + { 0x50 , "Application Label", EMV_TAG_STRING }, + { 0x56 , "Track 1 Data" }, + { 0x57 , "Track 2 Equivalent Data" }, + { 0x5a , "Application Primary Account Number (PAN)" }, + { 0x5f20, "Cardholder Name", EMV_TAG_STRING }, + { 0x5f24, "Application Expiration Date", EMV_TAG_YYMMDD }, + { 0x5f25, "Application Effective Date", EMV_TAG_YYMMDD }, + { 0x5f28, "Issuer Country Code", EMV_TAG_NUMERIC }, + { 0x5f2a, "Transaction Currency Code", EMV_TAG_NUMERIC }, + { 0x5f2d, "Language Preference", EMV_TAG_STRING }, + { 0x5f30, "Service Code", EMV_TAG_NUMERIC }, + { 0x5f34, "Application Primary Account Number (PAN) Sequence Number", EMV_TAG_NUMERIC }, + { 0x61 , "Application Template" }, + { 0x6f , "File Control Information (FCI) Template", EMV_TAG_FCI }, + { 0x70 , "READ RECORD Response Message Template" }, + { 0x77 , "Response Message Template Format 2" }, + { 0x80 , "Response Message Template Format 1" }, + { 0x82 , "Application Interchange Profile", EMV_TAG_BITMASK, &EMV_AIP }, + { 0x83 , "Command Template" }, + { 0x84 , "Dedicated File (DF) Name" }, + { 0x87 , "Application Priority Indicator" }, + { 0x88 , "Short File Identifier (SFI)" }, + { 0x8a , "Authorisation Response Code" }, + { 0x8c , "Card Risk Management Data Object List 1 (CDOL1)", EMV_TAG_DOL }, + { 0x8d , "Card Risk Management Data Object List 2 (CDOL2)", EMV_TAG_DOL }, + { 0x8e , "Cardholder Verification Method (CVM) List", EMV_TAG_CVM_LIST }, + { 0x8f , "Certification Authority Public Key Index" }, + { 0x90 , "Issuer Public Key Certificate" }, + { 0x91 , "Issuer Authentication Data" }, + { 0x92 , "Issuer Public Key Remainder" }, + { 0x93 , "Signed Static Application Data" }, + { 0x94 , "Application File Locator (AFL)" }, + { 0x95 , "Terminal Verification Results" }, + { 0x9a , "Transaction Date", EMV_TAG_YYMMDD }, + { 0x9c , "Transaction Type" }, + { 0x9f02, "Amount, Authorised (Numeric)", EMV_TAG_NUMERIC }, + { 0x9f03, "Amount, Other (Numeric)", EMV_TAG_NUMERIC, }, + { 0x9f07, "Application Usage Control", EMV_TAG_BITMASK, &EMV_AUC }, + { 0x9f08, "Application Version Number" }, + { 0x9f0d, "Issuer Action Code - Default", EMV_TAG_BITMASK, &EMV_TVR }, + { 0x9f0e, "Issuer Action Code - Denial", EMV_TAG_BITMASK, &EMV_TVR }, + { 0x9f0f, "Issuer Action Code - Online", EMV_TAG_BITMASK, &EMV_TVR }, + { 0x9f10, "Issuer Application Data" }, + { 0x9f11, "Issuer Code Table Index", EMV_TAG_NUMERIC }, + { 0x9f12, "Application Preferred Name", EMV_TAG_STRING }, + { 0x9f13, "Last Online Application Transaction Counter (ATC) Register" }, + { 0x9f17, "Personal Identification Number (PIN) Try Counter" }, + { 0x9f1a, "Terminal Country Code" }, + { 0x9f1f, "Track 1 Discretionary Data", EMV_TAG_STRING }, + { 0x9f21, "Transaction Time" }, + { 0x9f26, "Application Cryptogram" }, + { 0x9f27, "Cryptogram Information Data" }, + { 0x9f2d, "ICC PIN Encipherment Public Key Certificate" }, + { 0x9f2e, "ICC PIN Encipherment Public Key Exponent" }, + { 0x9f2f, "ICC PIN Encipherment Public Key Remainder" }, + { 0x9f32, "Issuer Public Key Exponent" }, + { 0x9f34, "Cardholder Verification Method (CVM) Results" }, + { 0x9f35, "Terminal Type" }, + { 0x9f36, "Application Transaction Counter (ATC)" }, + { 0x9f37, "Unpredictable Number" }, + { 0x9f38, "Processing Options Data Object List (PDOL)", EMV_TAG_DOL }, + { 0x9f42, "Application Currency Code", EMV_TAG_NUMERIC }, + { 0x9f44, "Application Currency Exponent", EMV_TAG_NUMERIC }, + { 0x9f45, "Data Authentication Code" }, + { 0x9f46, "ICC Public Key Certificate" }, + { 0x9f47, "ICC Public Key Exponent" }, + { 0x9f48, "ICC Public Key Remainder" }, + { 0x9f49, "Dynamic Data Authentication Data Object List (DDOL)", EMV_TAG_DOL }, + { 0x9f4a, "Static Data Authentication Tag List" }, + { 0x9f4b, "Signed Dynamic Application Data" }, + { 0x9f4c, "ICC Dynamic Number" }, + { 0x9f4d, "Log Entry" }, + { 0x9f4f, "Log Format", EMV_TAG_DOL }, + { 0x9f62, "PCVC3(Track1)" }, + { 0x9f63, "PUNATC(Track1)" }, + { 0x9f64, "NATC(Track1)" }, + { 0x9f65, "PCVC3(Track2)" }, + { 0x9f66, "PUNATC(Track2)" }, + { 0x9f67, "NATC(Track2)" }, + { 0x9f6b, "Track 2 Data" }, + { 0xa5 , "File Control Information (FCI) Proprietary Template" }, + { 0xbf0c, "File Control Information (FCI) Issuer Discretionary Data" }, +}; + +static int emv_sort_tag(tlv_tag_t tag) +{ + return (int)(tag >= 0x100 ? tag : tag << 8); +} + +static int emv_tlv_compare(const void *a, const void *b) +{ + const struct tlv *tlv = a; + const struct emv_tag *tag = b; + + return emv_sort_tag(tlv->tag) - (emv_sort_tag(tag->tag)); +} + +static const struct emv_tag *emv_get_tag(const struct tlv *tlv) +{ + struct emv_tag *tag = bsearch(tlv, emv_tags, sizeof(emv_tags)/sizeof(emv_tags[0]), + sizeof(emv_tags[0]), emv_tlv_compare); + + return tag ? tag : &emv_tags[0]; +} + +static const char *bitstrings[] = { + ".......1", + "......1.", + ".....1..", + "....1...", + "...1....", + "..1.....", + ".1......", + "1.......", +}; + +static void emv_tag_dump_bitmask(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) +{ + const struct emv_tag_bit *bits = tag->data; + unsigned bit, byte; + + for (byte = 1; byte <= tlv->len; byte ++) { + unsigned char val = tlv->value[byte - 1]; + fprintf(f, "\tByte %u (%02x)\n", byte, val); + for (bit = 8; bit > 0; bit--, val <<= 1) { + if (val & 0x80) + fprintf(f, "\t\t%s - '%s'\n", bitstrings[bit - 1], + bits->bit == EMV_BIT(byte, bit) ? bits->name : "Unknown"); + if (bits->bit == EMV_BIT(byte, bit)) + bits ++; + } + } +} + +static void emv_tag_dump_dol(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) +{ + const unsigned char *buf = tlv->value; + size_t left = tlv->len; + + while (left) { + struct tlv doltlv; + const struct emv_tag *doltag; + + if (!tlv_parse_tl(&buf, &left, &doltlv)) { + fprintf(f, "Invalid Tag-Len\n"); + continue; + } + + doltag = emv_get_tag(&doltlv); + + fprintf(f, "\tTag %4hx len %02zx ('%s')\n", doltlv.tag, doltlv.len, doltag->name); + } +} + +static void emv_tag_dump_fci(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) { + const unsigned char *buf = tlv->value; + size_t left = tlv->len; + + while (left) { + struct tlv doltlv; + const struct emv_tag *doltag; + + if (!tlv_parse_tl(&buf, &left, &doltlv)) { + fprintf(f, "Invalid Tag-Len\n"); + continue; + } + + doltag = emv_get_tag(&doltlv); + + fprintf(f, "\t--%2hx[%02zx]'%s'\n", doltlv.tag, doltlv.len, doltag->name); + } +} + +static void emv_tag_dump_string(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) +{ + fprintf(f, "\tString value '"); + fwrite(tlv->value, 1, tlv->len, f); + fprintf(f, "'\n"); +} + +static unsigned long emv_value_numeric(const struct tlv *tlv, unsigned start, unsigned end) +{ + unsigned long ret = 0; + int i; + + if (end > tlv->len * 2) + return ret; + if (start >= end) + return ret; + + if (start & 1) { + ret += tlv->value[start/2] & 0xf; + i = start + 1; + } else + i = start; + + for (; i < end - 1; i += 2) { + ret *= 10; + ret += tlv->value[i/2] >> 4; + ret *= 10; + ret += tlv->value[i/2] & 0xf; + } + + if (end & 1) { + ret *= 10; + ret += tlv->value[end/2] >> 4; + } + + return ret; +} + +static void emv_tag_dump_numeric(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) +{ + fprintf(f, "\tNumeric value %lu\n", emv_value_numeric(tlv, 0, tlv->len * 2)); +} + +static void emv_tag_dump_yymmdd(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) +{ + fprintf(f, "\tDate: 20%02ld.%ld.%ld\n", + emv_value_numeric(tlv, 0, 2), + emv_value_numeric(tlv, 2, 4), + emv_value_numeric(tlv, 4, 6)); +} + +static uint32_t emv_get_binary(const unsigned char *S) +{ + return (S[0] << 24) | (S[1] << 16) | (S[2] << 8) | (S[3] << 0); +} + +static void emv_tag_dump_cvm_list(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) +{ + uint32_t X, Y; + int i; + + if (tlv->len < 10 || tlv->len % 2) { + fprintf(f, "\tINVALID!\n"); + return; + } + + X = emv_get_binary(tlv->value); + Y = emv_get_binary(tlv->value + 4); + + fprintf(f, "\tX: %d\n", X); + fprintf(f, "\tY: %d\n", Y); + + for (i = 8; i < tlv->len; i+= 2) { + const char *method; + const char *condition; + + switch (tlv->value[i] & 0x3f) { + case 0x0: + method = "Fail CVM processing"; + break; + case 0x1: + method = "Plaintext PIN verification performed by ICC"; + break; + case 0x2: + method = "Enciphered PIN verified online"; + break; + case 0x3: + method = "Plaintext PIN verification performed by ICC and signature (paper)"; + break; + case 0x4: + method = "Enciphered PIN verification performed by ICC"; + break; + case 0x5: + method = "Enciphered PIN verification performed by ICC and signature (paper)"; + break; + case 0x1e: + method = "Signature (paper)"; + break; + case 0x1f: + method = "No CVM required"; + break; + case 0x3f: + method = "NOT AVAILABLE!"; + break; + default: + method = "Unknown"; + break; + } + + switch (tlv->value[i+1]) { + case 0x00: + condition = "Always"; + break; + case 0x01: + condition = "If unattended cash"; + break; + case 0x02: + condition = "If not unattended cash and not manual cash and not purchase with cashback"; + break; + case 0x03: + condition = "If terminal supports the CVM"; + break; + case 0x04: + condition = "If manual cash"; + break; + case 0x05: + condition = "If purchase with cashback"; + break; + case 0x06: + condition = "If transaction is in the application currency and is under X value"; + break; + case 0x07: + condition = "If transaction is in the application currency and is over X value"; + break; + case 0x08: + condition = "If transaction is in the application currency and is under Y value"; + break; + case 0x09: + condition = "If transaction is in the application currency and is over Y value"; + break; + default: + condition = "Unknown"; + break; + } + + fprintf(f, "\t%02x %02x: '%s' '%s' and '%s' if this CVM is unsuccessful\n", + tlv->value[i], tlv->value[i+1], + method, condition, (tlv->value[i] & 0x40) ? "continue" : "fail"); + } +} + +bool emv_tag_dump(const struct tlv *tlv, FILE *f) +{ + if (!tlv) { + fprintf(f, "NULL\n"); + return false; + } + + const struct emv_tag *tag = emv_get_tag(tlv); + + fprintf(f, "--%2hx[%02zx] '%s':\n", tlv->tag, tlv->len, tag->name); + + switch (tag->type) { + case EMV_TAG_GENERIC: + break; + case EMV_TAG_BITMASK: + emv_tag_dump_bitmask(tlv, tag, f); + break; + case EMV_TAG_DOL: + emv_tag_dump_dol(tlv, tag, f); + break; + case EMV_TAG_CVM_LIST: + emv_tag_dump_cvm_list(tlv, tag, f); + break; + case EMV_TAG_STRING: + emv_tag_dump_string(tlv, tag, f); + break; + case EMV_TAG_NUMERIC: + emv_tag_dump_numeric(tlv, tag, f); + break; + case EMV_TAG_YYMMDD: + emv_tag_dump_yymmdd(tlv, tag, f); + break; + case EMV_TAG_FCI: + emv_tag_dump_fci(tlv, tag, f); + break; + }; + + return true; +} diff --git a/client/emv/emv_tags.h b/client/emv/emv_tags.h new file mode 100644 index 00000000..de6d9d1e --- /dev/null +++ b/client/emv/emv_tags.h @@ -0,0 +1,24 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef TAGS_H +#define TAGS_H + +#include "tlv.h" +#include + +bool emv_tag_dump(const struct tlv *tlv, FILE *f); + +#endif diff --git a/client/emv/emvcore.c b/client/emv/emvcore.c new file mode 100644 index 00000000..6f8892fe --- /dev/null +++ b/client/emv/emvcore.c @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 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. +//----------------------------------------------------------------------------- +// EMV core functions +//----------------------------------------------------------------------------- + +#include "emvcore.h" + +static bool print_cb(void *data, const struct tlv *tlv) { + emv_tag_dump(tlv, stdout); + dump_buffer(tlv->value, tlv->len, stdout); + + return true; +} + +void TLVPrintFromBuffer(uint8_t *data, int datalen) { + struct tlvdb *t = NULL; + t = tlvdb_parse(data, datalen); + if (t) { + PrintAndLog("TLV decoded:"); + + tlvdb_visit(t, print_cb, NULL); + tlvdb_free(t); + } else { + PrintAndLog("TLV ERROR: Can't parse response as TLV tree."); + } +} diff --git a/client/emv/emvcore.h b/client/emv/emvcore.h new file mode 100644 index 00000000..523b92ac --- /dev/null +++ b/client/emv/emvcore.h @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 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. +//----------------------------------------------------------------------------- +// EMV core functions +//----------------------------------------------------------------------------- + +#ifndef EMVCORE_H__ +#define EMVCORE_H__ + +#include +#include +#include +#include +#include "util.h" +#include "common.h" +#include "ui.h" +#include "emv/tlv.h" +#include "emv/dump.h" +#include "emv/emv_tags.h" + +extern void TLVPrintFromBuffer(uint8_t *data, int datalen); + +#endif + + + + diff --git a/client/emv/tlv.c b/client/emv/tlv.c new file mode 100644 index 00000000..d78f049e --- /dev/null +++ b/client/emv/tlv.c @@ -0,0 +1,415 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * https://github.com/lumag/emv-tools/blob/master/lib/tlv.c + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "tlv.h" + +#include +#include +#include +#include + +#define TLV_TAG_CLASS_MASK 0xc0 +#define TLV_TAG_COMPLEX 0x20 +#define TLV_TAG_VALUE_MASK 0x1f +#define TLV_TAG_VALUE_CONT 0x1f +#define TLV_TAG_INVALID 0 + +#define TLV_LEN_LONG 0x80 +#define TLV_LEN_MASK 0x7f +#define TLV_LEN_INVALID (~0) + +// http://radek.io/2012/11/10/magical-container_of-macro/ +//#define container_of(ptr, type, member) ({ +// const typeof( ((type *)0)->member ) *__mptr = (ptr); +// (type *)( (char *)__mptr - offsetof(type,member) );}) + +struct tlvdb { + struct tlv tag; + struct tlvdb *next; + struct tlvdb *parent; + struct tlvdb *children; +}; + +struct tlvdb_root { + struct tlvdb db; + size_t len; + unsigned char buf[0]; +}; + +static tlv_tag_t tlv_parse_tag(const unsigned char **buf, size_t *len) +{ + tlv_tag_t tag; + + if (*len == 0) + return TLV_TAG_INVALID; + tag = **buf; + --*len; + ++*buf; + if ((tag & TLV_TAG_VALUE_MASK) != TLV_TAG_VALUE_CONT) + return tag; + + if (*len == 0) + return TLV_TAG_INVALID; + + tag <<= 8; + tag |= **buf; + --*len; + ++*buf; + + return tag; +} + +static size_t tlv_parse_len(const unsigned char **buf, size_t *len) +{ + size_t l; + + if (*len == 0) + return TLV_LEN_INVALID; + + l = **buf; + --*len; + ++*buf; + + if (!(l & TLV_LEN_LONG)) + return l; + + size_t ll = l &~ TLV_LEN_LONG; + if (*len < ll) + return TLV_LEN_INVALID; + + /* FIXME */ + if (ll != 1) + return TLV_LEN_INVALID; + + l = **buf; + --*len; + ++*buf; + + return l; +} + +bool tlv_parse_tl(const unsigned char **buf, size_t *len, struct tlv *tlv) +{ + tlv->value = 0; + + tlv->tag = tlv_parse_tag(buf, len); + if (tlv->tag == TLV_TAG_INVALID) + return false; + + tlv->len = tlv_parse_len(buf, len); + if (tlv->len == TLV_LEN_INVALID) + return false; + + return true; +} + +static struct tlvdb *tlvdb_parse_children(struct tlvdb *parent); + +static bool tlvdb_parse_one(struct tlvdb *tlvdb, + struct tlvdb *parent, + const unsigned char **tmp, + size_t *left) +{ + tlvdb->next = tlvdb->children = NULL; + tlvdb->parent = parent; + + tlvdb->tag.tag = tlv_parse_tag(tmp, left); + if (tlvdb->tag.tag == TLV_TAG_INVALID) + goto err; + + tlvdb->tag.len = tlv_parse_len(tmp, left); + if (tlvdb->tag.len == TLV_LEN_INVALID) + goto err; + + if (tlvdb->tag.len > *left) + goto err; + + tlvdb->tag.value = *tmp; + + *tmp += tlvdb->tag.len; + *left -= tlvdb->tag.len; + + if (tlv_is_constructed(&tlvdb->tag) && (tlvdb->tag.len != 0)) { + tlvdb->children = tlvdb_parse_children(tlvdb); + if (!tlvdb->children) + goto err; + } else { + tlvdb->children = NULL; + } + + return true; + +err: + return false; +} + +static struct tlvdb *tlvdb_parse_children(struct tlvdb *parent) +{ + const unsigned char *tmp = parent->tag.value; + size_t left = parent->tag.len; + struct tlvdb *tlvdb, *first = NULL, *prev = NULL; + + while (left != 0) { + tlvdb = malloc(sizeof(*tlvdb)); + if (prev) + prev->next = tlvdb; + else + first = tlvdb; + prev = tlvdb; + + if (!tlvdb_parse_one(tlvdb, parent, &tmp, &left)) + goto err; + + tlvdb->parent = parent; + } + + return first; + +err: + tlvdb_free(first); + + return NULL; +} + +struct tlvdb *tlvdb_parse(const unsigned char *buf, size_t len) +{ + struct tlvdb_root *root; + const unsigned char *tmp; + size_t left; + + if (!len || !buf) + return NULL; + + root = malloc(sizeof(*root) + len); + root->len = len; + memcpy(root->buf, buf, len); + + tmp = root->buf; + left = len; + + if (!tlvdb_parse_one(&root->db, NULL, &tmp, &left)) + goto err; + + if (left) + goto err; + + return &root->db; + +err: + tlvdb_free(&root->db); + + return NULL; +} + +struct tlvdb *tlvdb_parse_multi(const unsigned char *buf, size_t len) +{ + struct tlvdb_root *root; + const unsigned char *tmp; + size_t left; + + if (!len || !buf) + return NULL; + + root = malloc(sizeof(*root) + len); + root->len = len; + memcpy(root->buf, buf, len); + + tmp = root->buf; + left = len; + + if (!tlvdb_parse_one(&root->db, NULL, &tmp, &left)) + goto err; + + while (left != 0) { + struct tlvdb *db = malloc(sizeof(*db)); + if (!tlvdb_parse_one(db, NULL, &tmp, &left)) { + free (db); + goto err; + } + + tlvdb_add(&root->db, db); + } + + return &root->db; + +err: + tlvdb_free(&root->db); + + return NULL; +} + +struct tlvdb *tlvdb_fixed(tlv_tag_t tag, size_t len, const unsigned char *value) +{ + struct tlvdb_root *root = malloc(sizeof(*root) + len); + + root->len = len; + memcpy(root->buf, value, len); + + root->db.parent = root->db.next = root->db.children = NULL; + root->db.tag.tag = tag; + root->db.tag.len = len; + root->db.tag.value = root->buf; + + return &root->db; +} + +struct tlvdb *tlvdb_external(tlv_tag_t tag, size_t len, const unsigned char *value) +{ + struct tlvdb_root *root = malloc(sizeof(*root)); + + root->len = 0; + + root->db.parent = root->db.next = root->db.children = NULL; + root->db.tag.tag = tag; + root->db.tag.len = len; + root->db.tag.value = value; + + return &root->db; +} + +void tlvdb_free(struct tlvdb *tlvdb) +{ + struct tlvdb *next = NULL; + + if (!tlvdb) + return; + + for (; tlvdb; tlvdb = next) { + next = tlvdb->next; + tlvdb_free(tlvdb->children); + free(tlvdb); + } +} + +void tlvdb_add(struct tlvdb *tlvdb, struct tlvdb *other) +{ + while (tlvdb->next) { + tlvdb = tlvdb->next; + } + + tlvdb->next = other; +} + +void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data) +{ + struct tlvdb *next = NULL; + + if (!tlvdb) + return; + + for (; tlvdb; tlvdb = next) { + next = tlvdb->next; + cb(data, &tlvdb->tag); + tlvdb_visit(tlvdb->children, cb, data); + } +} + +static const struct tlvdb *tlvdb_next(const struct tlvdb *tlvdb) +{ + if (tlvdb->children) + return tlvdb->children; + + while (tlvdb) { + if (tlvdb->next) + return tlvdb->next; + + tlvdb = tlvdb->parent; + } + + return NULL; +} + +const struct tlv *tlvdb_get(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev) +{ + if (prev) { +// tlvdb = tlvdb_next(container_of(prev, struct tlvdb, tag)); + tlvdb = tlvdb_next((struct tlvdb *)prev); + } + + + while (tlvdb) { + if (tlvdb->tag.tag == tag) + return &tlvdb->tag; + + tlvdb = tlvdb_next(tlvdb); + } + + return NULL; +} + +unsigned char *tlv_encode(const struct tlv *tlv, size_t *len) +{ + size_t size = tlv->len; + unsigned char *data; + size_t pos; + + if (tlv->tag > 0x100) + size += 2; + else + size += 1; + + if (tlv->len > 0x7f) + size += 2; + else + size += 1; + + data = malloc(size); + if (!data) { + *len = 0; + return NULL; + } + + pos = 0; + + if (tlv->tag > 0x100) { + data[pos++] = tlv->tag >> 8; + data[pos++] = tlv->tag & 0xff; + } else + data[pos++] = tlv->tag; + + if (tlv->len > 0x7f) { + data[pos++] = 0x81; + data[pos++] = tlv->len; + } else + data[pos++] = tlv->len; + + memcpy(data + pos, tlv->value, tlv->len); + pos += tlv->len; + + *len = pos; + return data; +} + +bool tlv_is_constructed(const struct tlv *tlv) +{ + return (tlv->tag < 0x100 ? tlv->tag : tlv->tag >> 8) & TLV_TAG_COMPLEX; +} + +bool tlv_equal(const struct tlv *a, const struct tlv *b) +{ + if (!a && !b) + return true; + + if (!a || !b) + return false; + + return a->tag == b->tag && a->len == b->len && !memcmp(a->value, b->value, a->len); +} diff --git a/client/emv/tlv.h b/client/emv/tlv.h new file mode 100644 index 00000000..3fd3f347 --- /dev/null +++ b/client/emv/tlv.h @@ -0,0 +1,52 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * https://github.com/lumag/emv-tools/blob/master/lib/include/openemv/tlv.h + */ + +#ifndef TLV_H +#define TLV_H + +#include +#include +#include + +typedef uint16_t tlv_tag_t; + +struct tlv { + tlv_tag_t tag; + size_t len; + const unsigned char *value; +}; + +struct tlvdb; +typedef bool (*tlv_cb)(void *data, const struct tlv *tlv); + +struct tlvdb *tlvdb_fixed(tlv_tag_t tag, size_t len, const unsigned char *value); +struct tlvdb *tlvdb_external(tlv_tag_t tag, size_t len, const unsigned char *value); +struct tlvdb *tlvdb_parse(const unsigned char *buf, size_t len); +struct tlvdb *tlvdb_parse_multi(const unsigned char *buf, size_t len); +void tlvdb_free(struct tlvdb *tlvdb); + +void tlvdb_add(struct tlvdb *tlvdb, struct tlvdb *other); + +void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data); +const struct tlv *tlvdb_get(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev); + +bool tlv_parse_tl(const unsigned char **buf, size_t *len, struct tlv *tlv); +unsigned char *tlv_encode(const struct tlv *tlv, size_t *len); +bool tlv_is_constructed(const struct tlv *tlv); +bool tlv_equal(const struct tlv *a, const struct tlv *b); + +#endif From fe842bed9ce5d92a59605c239bec71f19c94d31a Mon Sep 17 00:00:00 2001 From: merlokk Date: Wed, 1 Nov 2017 13:54:03 +0200 Subject: [PATCH 051/310] fixed bug with `hf search 14a` --- client/cmdhf.c | 2 +- client/cmdhf14a.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/client/cmdhf.c b/client/cmdhf.c index 6aa5ae4e..168296ba 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -663,7 +663,7 @@ int CmdHFList(const char *Cmd) int CmdHFSearch(const char *Cmd){ int ans = 0; PrintAndLog(""); - ans = CmdHF14AReader("s"); + ans = CmdHF14AInfo("s"); if (ans > 0) { PrintAndLog("\nValid ISO14443A Tag Found - Quiting Search\n"); return ans; diff --git a/client/cmdhf14a.h b/client/cmdhf14a.h index dfdf1f4a..2556678d 100644 --- a/client/cmdhf14a.h +++ b/client/cmdhf14a.h @@ -18,6 +18,7 @@ int CmdHF14A(const char *Cmd); int CmdHF14AList(const char *Cmd); int CmdHF14AMifare(const char *Cmd); int CmdHF14AReader(const char *Cmd); +extern int CmdHF14AInfo(const char *Cmd); int CmdHF14ASim(const char *Cmd); int CmdHF14ASnoop(const char *Cmd); char* getTagInfo(uint8_t uid); From c5e9a0212e68f864371439cb5be8fd227002e9c8 Mon Sep 17 00:00:00 2001 From: merlokk Date: Wed, 1 Nov 2017 16:55:12 +0200 Subject: [PATCH 052/310] deleted condition in iso14a select (sak & 0x20 ==0) - error. Usually SAK = 88 --- armsrc/iso14443a.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 2ff722b0..01026aa5 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1817,7 +1817,8 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u } // non iso14443a compliant tag - if( (sak & 0x20) == 0) return 2; + // now not work + //if( (sak & 0x20) == 0) return 2; if (!no_rats) { // Request for answer to select From 89ec86abd293e6294223e971278caf4aad2be190 Mon Sep 17 00:00:00 2001 From: merlokk Date: Wed, 1 Nov 2017 17:02:35 +0200 Subject: [PATCH 053/310] now SAK check is OK --- armsrc/iso14443a.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 01026aa5..860af238 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1817,8 +1817,8 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u } // non iso14443a compliant tag - // now not work - //if( (sak & 0x20) == 0) return 2; + // https://www.nxp.com/docs/en/application-note/AN10834.pdf page 7 + if( (sak & 0x20) != 0) return 2; if (!no_rats) { // Request for answer to select From 7376da5c44ac5d33c3515730f7213a64d51d696a Mon Sep 17 00:00:00 2001 From: merlokk Date: Wed, 1 Nov 2017 18:22:03 +0200 Subject: [PATCH 054/310] revert SAK check --- armsrc/iso14443a.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 7d484015..33db9f22 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1820,9 +1820,8 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u p_hi14a_card->sak = sak; } - // non iso14443a compliant tag - // https://www.nxp.com/docs/en/application-note/AN10834.pdf page 7 - if( (sak & 0x20) != 0) return 2; + // PICC compilant with iso14443a-4 ---> (SAK & 0x20 != 0) + if( (sak & 0x20) == 0) return 2; if (!no_rats) { // Request for answer to select From 499df9088d5845d954918da51dc339becd4a7da4 Mon Sep 17 00:00:00 2001 From: merlokk Date: Wed, 1 Nov 2017 18:34:15 +0200 Subject: [PATCH 055/310] fix behavier reading iso14443-3 and iso14443-4 cards --- armsrc/iso14443a.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 33db9f22..c39b8a20 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1935,7 +1935,10 @@ void ReaderIso14443a(UsbCommand *c) arg0 = iso14443a_select_card(NULL, card, NULL, true, 0, param & ISO14A_NO_RATS); // if we cant select then we cant send data - cantSELECT = (arg0 != 1); + if (arg0 != 1 && arg0 != 2) { + // 1 - all is OK with ATS, 2 - without ATS + cantSELECT = true; + } LED_B_ON(); cmd_send(CMD_ACK,arg0,card->uidlen,0,buf,sizeof(iso14a_card_select_t)); From 83df98d691c002d99d5a8f37d17f9205ba649cc2 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Wed, 1 Nov 2017 18:44:43 +0200 Subject: [PATCH 056/310] change comment in iso14443a.c (#459) --- armsrc/iso14443a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 06a83feb..987d29ad 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1820,7 +1820,7 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u p_hi14a_card->sak = sak; } - // non iso14443a compliant tag + // PICC compilant with iso14443a-4 ---> (SAK & 0x20 != 0) if( (sak & 0x20) == 0) return 2; if (!no_rats) { From fe6bf3c58c939bfa076f06034e7963ec27080ef0 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Wed, 1 Nov 2017 18:48:59 +0200 Subject: [PATCH 057/310] improve `hf 14a info` (#457) * added validate_prng_nonce from iceman1001 fork * added DetectClassicPrng from iceman1001 fork --- client/cmdhf14a.c | 15 +++++++++ client/mifarehost.c | 69 ++++++++++++++++++++++++++++++++++++++++ client/mifarehost.h | 1 + common/crapto1/crapto1.c | 1 - 4 files changed, 85 insertions(+), 1 deletion(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 3f103f5f..928864a1 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -244,8 +244,10 @@ int CmdHF14AInfo(const char *Cmd) PrintAndLog("ATQA : %02x %02x", card.atqa[1], card.atqa[0]); PrintAndLog(" SAK : %02x [%d]", card.sak, resp.arg[0]); + bool isMifareClassic = true; switch (card.sak) { case 0x00: + isMifareClassic = false; //***************************************test**************** // disconnect @@ -480,6 +482,19 @@ int CmdHF14AInfo(const char *Cmd) // try to see if card responses to "chinese magic backdoor" commands. mfCIdentify(); + if (isMifareClassic) { + switch(DetectClassicPrng()) { + case 0: + PrintAndLog("Prng detection: HARDEND (hardnested)"); + break; + case 1: + PrintAndLog("Prng detection: WEAK"); + break; + default: + PrintAndLog("Prng detection error."); + } + } + return select_status; } diff --git a/client/mifarehost.c b/client/mifarehost.c index fe1a8edb..67277b59 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -903,3 +903,72 @@ int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, return 0; } +/** validate_prng_nonce + * Determine if nonce is deterministic. ie: Suspectable to Darkside attack. + * returns + * true = weak prng + * false = hardend prng + */ +bool validate_prng_nonce(uint32_t nonce) { + uint16_t *dist = 0; + uint16_t x, i; + + dist = malloc(2 << 16); + if(!dist) + return -1; + + // init prng table: + for (x = i = 1; i; ++i) { + dist[(x & 0xff) << 8 | x >> 8] = i; + x = x >> 1 | (x ^ x >> 2 ^ x >> 3 ^ x >> 5) << 15; + } + + uint32_t res = (65535 - dist[nonce >> 16] + dist[nonce & 0xffff]) % 65535; + + free(dist); + return (res == 16); +} + +/* Detect Tag Prng, +* function performs a partial AUTH, where it tries to authenticate against block0, key A, but only collects tag nonce. +* the tag nonce is check to see if it has a predictable PRNG. +* @returns +* TRUE if tag uses WEAK prng (ie Now the NACK bug also needs to be present for Darkside attack) +* FALSE is tag uses HARDEND prng (ie hardnested attack possible, with known key) +*/ +int DetectClassicPrng(void){ + + UsbCommand resp, respA; + uint8_t cmd[] = {0x60, 0x00}; // MIFARE_AUTH_KEYA + uint32_t flags = ISO14A_CONNECT | ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_RATS; + + UsbCommand c = {CMD_READER_ISO_14443a, {flags, sizeof(cmd), 0}}; + memcpy(c.d.asBytes, cmd, sizeof(cmd)); + + clearCommandBuffer(); + SendCommand(&c); + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + PrintAndLog("PRNG UID: Reply timeout."); + return -1; + } + + // if select tag failed. + if (resp.arg[0] == 0) { + PrintAndLog("PRNG error: selecting tag failed, can't detect prng."); + return -1; + } + + if (!WaitForResponseTimeout(CMD_ACK, &respA, 5000)) { + PrintAndLog("PRNG data: Reply timeout."); + return -1; + } + + // check respA + if (respA.arg[0] != 4) { + PrintAndLog("PRNG data error: Wrong length: %d", respA.arg[0]); + return -1; + } + + uint32_t nonce = bytes_to_num(respA.d.asBytes, respA.arg[0]); + return validate_prng_nonce(nonce); +} diff --git a/client/mifarehost.h b/client/mifarehost.h index 118d55cc..031dac1b 100644 --- a/client/mifarehost.h +++ b/client/mifarehost.h @@ -60,5 +60,6 @@ extern int saveTraceCard(void); extern int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len); extern int mfCIdentify(); +extern int DetectClassicPrng(void); #endif diff --git a/common/crapto1/crapto1.c b/common/crapto1/crapto1.c index 9398a1f3..1edfca1b 100644 --- a/common/crapto1/crapto1.c +++ b/common/crapto1/crapto1.c @@ -426,7 +426,6 @@ int nonce_distance(uint32_t from, uint32_t to) return (65535 + dist[to >> 16] - dist[from >> 16]) % 65535; } - static uint32_t fastfwd[2][8] = { { 0, 0x4BC53, 0xECB1, 0x450E2, 0x25E29, 0x6E27A, 0x2B298, 0x60ECB}, { 0, 0x1D962, 0x4BC53, 0x56531, 0xECB1, 0x135D3, 0x450E2, 0x58980}}; From 75e42ef5dfc6868e696cbc13ef373d97a3040ada Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Wed, 1 Nov 2017 21:37:18 +0200 Subject: [PATCH 058/310] added changelog (#460) --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 044538c4..60caf413 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - `hf mf nested` Check keys after they have found (Merlok) - `hf mf chk` Move main cycle to arm (Merlok) - Changed proxmark command line parameter `flush` to `-f` or `-flush` (Merlok) +- Changed `hf 14a reader` to just reqest-anticilission-select sequence (Merlok) ### Fixed - Changed start sequence in Qt mode (fix: short commands hangs main Qt thread) (Merlok) @@ -32,6 +33,8 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added to proxmark command line parameters `w` - wait 20s for serial port (Merlok) - Added to proxmark command line parameters `c` and `l` - execute command and lua script from command line (Merlok) - Added to proxmark ability to execute commands from stdin (pipe) (Merlok) +- Added `hf 14a info` and moved there functionality from `hf 14a reader` (Merlok) +- Added to `hf 14a info` detection of weak prng from Iceman1001 fork (Merlok) ## [3.0.1][2017-06-08] From 23207d748f65f97365a608f2a9abfc3ac5997413 Mon Sep 17 00:00:00 2001 From: merlokk Date: Thu, 2 Nov 2017 14:11:42 +0200 Subject: [PATCH 059/310] tlv decoder works. --- client/cmdhf14a.c | 2 +- client/emv/emv_tags.c | 25 +------------------------ client/emv/emvcore.c | 2 +- 3 files changed, 3 insertions(+), 26 deletions(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index f3189c10..86cfea2f 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -760,7 +760,7 @@ int CmdHF14AAPDU(const char *cmd) { PrintAndLog("APDU response: %02x %02x - %s", data[datalen - 2], data[datalen - 1], GetAPDUCodeDescription(data[datalen - 2], data[datalen - 1])); - // here TLV decoder... + // TLV decoder if (decodeTLV && datalen > 4) { TLVPrintFromBuffer(data, datalen - 2); } diff --git a/client/emv/emv_tags.c b/client/emv/emv_tags.c index 9bcf2004..1aae847a 100644 --- a/client/emv/emv_tags.c +++ b/client/emv/emv_tags.c @@ -30,7 +30,6 @@ enum emv_tag_t { EMV_TAG_STRING, EMV_TAG_NUMERIC, EMV_TAG_YYMMDD, - EMV_TAG_FCI, }; struct emv_tag { @@ -128,7 +127,7 @@ static const struct emv_tag emv_tags[] = { { 0x5f30, "Service Code", EMV_TAG_NUMERIC }, { 0x5f34, "Application Primary Account Number (PAN) Sequence Number", EMV_TAG_NUMERIC }, { 0x61 , "Application Template" }, - { 0x6f , "File Control Information (FCI) Template", EMV_TAG_FCI }, + { 0x6f , "File Control Information (FCI) Template" }, { 0x70 , "READ RECORD Response Message Template" }, { 0x77 , "Response Message Template Format 2" }, { 0x80 , "Response Message Template Format 1" }, @@ -269,25 +268,6 @@ static void emv_tag_dump_dol(const struct tlv *tlv, const struct emv_tag *tag, F } } -static void emv_tag_dump_fci(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) { - const unsigned char *buf = tlv->value; - size_t left = tlv->len; - - while (left) { - struct tlv doltlv; - const struct emv_tag *doltag; - - if (!tlv_parse_tl(&buf, &left, &doltlv)) { - fprintf(f, "Invalid Tag-Len\n"); - continue; - } - - doltag = emv_get_tag(&doltlv); - - fprintf(f, "\t--%2hx[%02zx]'%s'\n", doltlv.tag, doltlv.len, doltag->name); - } -} - static void emv_tag_dump_string(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) { fprintf(f, "\tString value '"); @@ -471,9 +451,6 @@ bool emv_tag_dump(const struct tlv *tlv, FILE *f) case EMV_TAG_YYMMDD: emv_tag_dump_yymmdd(tlv, tag, f); break; - case EMV_TAG_FCI: - emv_tag_dump_fci(tlv, tag, f); - break; }; return true; diff --git a/client/emv/emvcore.c b/client/emv/emvcore.c index 6f8892fe..9264b110 100644 --- a/client/emv/emvcore.c +++ b/client/emv/emvcore.c @@ -19,7 +19,7 @@ static bool print_cb(void *data, const struct tlv *tlv) { void TLVPrintFromBuffer(uint8_t *data, int datalen) { struct tlvdb *t = NULL; - t = tlvdb_parse(data, datalen); + t = tlvdb_parse_multi(data, datalen); if (t) { PrintAndLog("TLV decoded:"); From 1d6f7bc8ac10ed1761f9181eb74245e53ef6d978 Mon Sep 17 00:00:00 2001 From: merlokk Date: Thu, 2 Nov 2017 14:28:05 +0200 Subject: [PATCH 060/310] added changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60caf413..10e6b827 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - `hf mf chk` Move main cycle to arm (Merlok) - Changed proxmark command line parameter `flush` to `-f` or `-flush` (Merlok) - Changed `hf 14a reader` to just reqest-anticilission-select sequence (Merlok) +- Changed `hf 14a raw` - works with LED's and some exchange logic (Merlok) ### Fixed - Changed start sequence in Qt mode (fix: short commands hangs main Qt thread) (Merlok) @@ -35,6 +36,8 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added to proxmark ability to execute commands from stdin (pipe) (Merlok) - Added `hf 14a info` and moved there functionality from `hf 14a reader` (Merlok) - Added to `hf 14a info` detection of weak prng from Iceman1001 fork (Merlok) +- Added to `hf 14a apdu` - exchange apdu via iso1443-4 (Merlok) +- Added to `hf 14a apdu` - apdu and tlv results parser (Merlok) ## [3.0.1][2017-06-08] From b35e04a7c6a8b6c99fcdc8f45e387437cbae4f08 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Tue, 24 Oct 2017 07:49:14 +0200 Subject: [PATCH 061/310] fix hf mf sim: * more timing fixes * correctly determine correction bit (taken from iceman's fork) * add checking of Access Conditions for Read command * never allow reading KeyA --- armsrc/iso14443a.c | 88 ++++++++++--------- armsrc/iso14443a.h | 4 +- armsrc/mifaresim.c | 201 +++++++++++++++++++++++++++++++++++++++----- armsrc/mifareutil.c | 19 ++++- armsrc/mifareutil.h | 5 ++ fpga/fpga_hf.bit | Bin 42175 -> 42175 bytes fpga/hi_iso14443a.v | 10 ++- 7 files changed, 257 insertions(+), 70 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 026b177a..af810b74 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -900,11 +900,11 @@ static int GetIso14443aCommandFromReader(uint8_t *received, uint8_t *parity, int } -static int EmSend4bitEx(uint8_t resp, bool correctionNeeded); +static int EmSend4bitEx(uint8_t resp); int EmSend4bit(uint8_t resp); -static int EmSendCmdExPar(uint8_t *resp, uint16_t respLen, bool correctionNeeded, uint8_t *par); -int EmSendCmdEx(uint8_t *resp, uint16_t respLen, bool correctionNeeded); -int EmSendPrecompiledCmd(tag_response_info_t *response_info, bool correctionNeeded); +static int EmSendCmdExPar(uint8_t *resp, uint16_t respLen, uint8_t *par); +int EmSendCmdEx(uint8_t *resp, uint16_t respLen); +int EmSendPrecompiledCmd(tag_response_info_t *response_info); static bool prepare_tag_modulation(tag_response_info_t* response_info, size_t max_buffer_size) { @@ -1138,7 +1138,7 @@ void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd, byte_t* data) } else if(receivedCmd[1] == 0x70 && receivedCmd[0] == 0x95) { // Received a SELECT (cascade 2) p_response = &responses[4]; order = 30; } else if(receivedCmd[0] == 0x30) { // Received a (plain) READ - EmSendCmdEx(data+(4*receivedCmd[1]),16,false); + EmSendCmdEx(data+(4*receivedCmd[1]),16); // Dbprintf("Read request from reader: %x %x",receivedCmd[0],receivedCmd[1]); // We already responded, do not send anything with the EmSendCmd14443aRaw() that is called below p_response = NULL; @@ -1231,7 +1231,7 @@ void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd, byte_t* data) cmdsRecvd++; if (p_response != NULL) { - EmSendPrecompiledCmd(p_response, receivedCmd[0] == 0x52); + EmSendPrecompiledCmd(p_response); } if (!tracing) { @@ -1413,12 +1413,6 @@ int EmGetCmd(uint8_t *received, uint16_t *len, uint8_t *parity) int analogCnt = 0; int analogAVG = 0; - // Set FPGA mode to "simulated ISO 14443 tag", no modulation (listen - // only, since we are receiving, not transmitting). - // Signal field is off with the appropriate LED - LED_D_OFF(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_LISTEN); - // Set ADC to read field strength AT91C_BASE_ADC->ADC_CR = AT91C_ADC_SWRST; AT91C_BASE_ADC->ADC_MR = @@ -1429,12 +1423,23 @@ int EmGetCmd(uint8_t *received, uint16_t *len, uint8_t *parity) // start ADC AT91C_BASE_ADC->ADC_CR = AT91C_ADC_START; - // Now run a 'software UART' on the stream of incoming samples. + // Run a 'software UART' on the stream of incoming samples. UartInit(received, parity); - // Clear RXRDY: - uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - + // Ensure that the FPGA Delay Queue is empty before we switch to TAGSIM_LISTEN + do { + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + AT91C_BASE_SSC->SSC_THR = SEC_F; + uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; (void) b; + } + } while (GetCountSspClk() < LastTimeProxToAirStart + LastProxToAirDuration + (FpgaSendQueueDelay>>3)); + + // Set FPGA mode to "simulated ISO 14443 tag", no modulation (listen + // only, since we are receiving, not transmitting). + // Signal field is off with the appropriate LED + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_LISTEN); + for(;;) { WDT_HIT(); @@ -1460,7 +1465,7 @@ int EmGetCmd(uint8_t *received, uint16_t *len, uint8_t *parity) // receive and test the miller decoding if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; if(MillerDecoding(b, 0)) { *len = Uart.len; EmLogTraceReader(); @@ -1472,18 +1477,27 @@ int EmGetCmd(uint8_t *received, uint16_t *len, uint8_t *parity) } -static int EmSendCmd14443aRaw(uint8_t *resp, uint16_t respLen, bool correctionNeeded) +static int EmSendCmd14443aRaw(uint8_t *resp, uint16_t respLen) { uint8_t b; uint16_t i = 0; - + bool correctionNeeded; + // Modulate Manchester FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_MOD); // include correction bit if necessary - if (Uart.parityBits & 0x01) { - correctionNeeded = true; + if (Uart.bitCount == 7) + { + // Short tags (7 bits) don't have parity, determine the correct value from MSB + correctionNeeded = Uart.output[0] & 0x40; } + else + { + // Look at the last parity bit + correctionNeeded = Uart.parity[(Uart.len-1)/8] & (0x80 >> ((Uart.len-1) & 7)); + } + if(correctionNeeded) { // 1236, so correction bit needed i = 0; @@ -1517,23 +1531,13 @@ static int EmSendCmd14443aRaw(uint8_t *resp, uint16_t respLen, bool correctionNe } } - // Ensure that the FPGA Delay Queue is empty before we switch to TAGSIM_LISTEN again: - uint8_t fpga_queued_bits = FpgaSendQueueDelay >> 3; - for (i = 0; i < fpga_queued_bits/8; ) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = SEC_F; - FpgaSendQueueDelay = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - i++; - } - } - return 0; } -static int EmSend4bitEx(uint8_t resp, bool correctionNeeded){ +static int EmSend4bitEx(uint8_t resp){ Code4bitAnswerAsTag(resp); - int res = EmSendCmd14443aRaw(ToSend, ToSendMax, correctionNeeded); + int res = EmSendCmd14443aRaw(ToSend, ToSendMax); // do the tracing for the previous reader request and this tag answer: EmLogTraceTag(&resp, 1, NULL, LastProxToAirDuration); return res; @@ -1541,40 +1545,40 @@ static int EmSend4bitEx(uint8_t resp, bool correctionNeeded){ int EmSend4bit(uint8_t resp){ - return EmSend4bitEx(resp, false); + return EmSend4bitEx(resp); } -static int EmSendCmdExPar(uint8_t *resp, uint16_t respLen, bool correctionNeeded, uint8_t *par){ +static int EmSendCmdExPar(uint8_t *resp, uint16_t respLen, uint8_t *par){ CodeIso14443aAsTagPar(resp, respLen, par); - int res = EmSendCmd14443aRaw(ToSend, ToSendMax, correctionNeeded); + int res = EmSendCmd14443aRaw(ToSend, ToSendMax); // do the tracing for the previous reader request and this tag answer: EmLogTraceTag(resp, respLen, par, LastProxToAirDuration); return res; } -int EmSendCmdEx(uint8_t *resp, uint16_t respLen, bool correctionNeeded){ +int EmSendCmdEx(uint8_t *resp, uint16_t respLen){ uint8_t par[MAX_PARITY_SIZE]; GetParity(resp, respLen, par); - return EmSendCmdExPar(resp, respLen, correctionNeeded, par); + return EmSendCmdExPar(resp, respLen, par); } int EmSendCmd(uint8_t *resp, uint16_t respLen){ uint8_t par[MAX_PARITY_SIZE]; GetParity(resp, respLen, par); - return EmSendCmdExPar(resp, respLen, false, par); + return EmSendCmdExPar(resp, respLen, par); } int EmSendCmdPar(uint8_t *resp, uint16_t respLen, uint8_t *par){ - return EmSendCmdExPar(resp, respLen, false, par); + return EmSendCmdExPar(resp, respLen, par); } -int EmSendPrecompiledCmd(tag_response_info_t *response_info, bool correctionNeeded) { - int ret = EmSendCmd14443aRaw(response_info->modulation, response_info->modulation_n, correctionNeeded); +int EmSendPrecompiledCmd(tag_response_info_t *response_info) { + int ret = EmSendCmd14443aRaw(response_info->modulation, response_info->modulation_n); // do the tracing for the previous reader request and this tag answer: EmLogTraceTag(response_info->response, response_info->response_n, &(response_info->par), response_info->ProxToAirDuration); return ret; diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index 8bd80510..8796edf5 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -41,10 +41,10 @@ extern void ReaderMifare(bool first_try); extern int EmGetCmd(uint8_t *received, uint16_t *len, uint8_t *parity); extern int EmSendCmd(uint8_t *resp, uint16_t respLen); -extern int EmSendCmdEx(uint8_t *resp, uint16_t respLen, bool correctionNeeded); +extern int EmSendCmdEx(uint8_t *resp, uint16_t respLen); extern int EmSend4bit(uint8_t resp); extern int EmSendCmdPar(uint8_t *resp, uint16_t respLen, uint8_t *par); -extern int EmSendPrecompiledCmd(tag_response_info_t *response_info, bool correctionNeeded); +extern int EmSendPrecompiledCmd(tag_response_info_t *response_info); extern bool prepare_allocated_tag_modulation(tag_response_info_t *response_info, uint8_t **buffer, size_t *buffer_size); diff --git a/armsrc/mifaresim.c b/armsrc/mifaresim.c index 91f45efd..1fdf99d6 100644 --- a/armsrc/mifaresim.c +++ b/armsrc/mifaresim.c @@ -41,6 +41,132 @@ #define cardSTATE_TO_IDLE() { cardSTATE = MFEMUL_IDLE; LED_B_OFF(); LED_C_OFF(); } +#define AC_DATA_READ 0 +#define AC_DATA_WRITE 1 +#define AC_DATA_INC 2 +#define AC_DATA_DEC_TRANS_REST 3 +#define AC_KEYA_READ 0 +#define AC_KEYA_WRITE 1 +#define AC_KEYB_READ 2 +#define AC_KEYB_WRITE 3 +#define AC_AC_READ 4 +#define AC_AC_WRITE 5 + +#define AUTHKEYA 0 +#define AUTHKEYB 1 +#define AUTHKEYNONE 0xff + + +static bool IsTrailerAccessAllowed(uint8_t blockNo, uint8_t keytype, uint8_t action) { + uint8_t sector_trailer[16]; + emlGetMem(sector_trailer, blockNo, 1); + uint8_t AC = ((sector_trailer[7] >> 5) & 0x04) + | ((sector_trailer[8] >> 2) & 0x02) + | ((sector_trailer[8] >> 7) & 0x01); + switch (action) { + case AC_KEYA_READ: { + return false; + break; + } + case AC_KEYA_WRITE: { + return ((keytype == AUTHKEYA && (AC == 0x00 || AC == 0x01)) + || (keytype == AUTHKEYB && (AC == 0x04 || AC == 0x03))); + break; + } + case AC_KEYB_READ: { + return (keytype == AUTHKEYA && (AC == 0x00 || AC == 0x02 || AC == 0x01)); + break; + } + case AC_KEYB_WRITE: { + return ((keytype == AUTHKEYA && (AC == 0x00 || AC == 0x04)) + || (keytype == AUTHKEYB && (AC == 0x04 || AC == 0x03))); + break; + } + case AC_AC_READ: { + return ((keytype == AUTHKEYA) + || (keytype == AUTHKEYB && !(AC == 0x00 || AC == 0x02 || AC == 0x01))); + break; + } + case AC_AC_WRITE: { + return ((keytype == AUTHKEYA && (AC == 0x01)) + || (keytype == AUTHKEYB && (AC == 0x03 || AC == 0x05))); + break; + } + default: return false; + } +} + + +static bool IsDataAccessAllowed(uint8_t blockNo, uint8_t keytype, uint8_t action) +{ + uint8_t sector_trailer[16]; + emlGetMem(sector_trailer, SectorTrailer(blockNo), 1); + + uint8_t sector_block; + if (blockNo < 32*4) { + sector_block = blockNo & 0x03; + } else { + sector_block = (blockNo & 0x0f) / 5; + } + + uint8_t AC; + switch (sector_block) { + case 0x00: { + AC = ((sector_trailer[7] >> 2) & 0x04) + | ((sector_trailer[8] << 1) & 0x02) + | ((sector_trailer[8] >> 4) & 0x01); + break; + } + case 0x01: { + AC = ((sector_trailer[7] >> 3) & 0x04) + | ((sector_trailer[8] >> 0) & 0x02) + | ((sector_trailer[8] >> 5) & 0x01); + break; + } + case 0x02: { + AC = ((sector_trailer[7] >> 4) & 0x04) + | ((sector_trailer[8] >> 1) & 0x02) + | ((sector_trailer[8] >> 6) & 0x01); + break; + } + default: + return false; + } + + switch (action) { + case AC_DATA_READ: { + return ((keytype == AUTHKEYA && !(AC == 0x03 || AC == 0x05 || AC == 0x07)) + || (keytype == AUTHKEYB && !(AC == 0x07))); + break; + } + case AC_DATA_WRITE: { + return ((keytype == AUTHKEYA && (AC == 0x00)) + || (keytype == AUTHKEYB && (AC == 0x00 || AC == 0x04 || AC == 0x06 || AC == 0x03))); + break; + } + case AC_DATA_INC: { + return ((keytype == AUTHKEYA && (AC == 0x00)) + || (keytype == AUTHKEYB && (AC == 0x00 || AC == 0x06))); + break; + } + case AC_DATA_DEC_TRANS_REST: { + return ((keytype == AUTHKEYA && (AC == 0x00 || AC == 0x06 || AC == 0x01)) + || (keytype == AUTHKEYB && (AC == 0x00 || AC == 0x06 || AC == 0x01))); + break; + } + } + + return false; +} + + +static bool IsAccessAllowed(uint8_t blockNo, uint8_t keytype, uint8_t action) { + if (IsSectorTrailer(blockNo)) { + return IsTrailerAccessAllowed(blockNo, keytype, action); + } else { + return IsDataAccessAllowed(blockNo, keytype, action); + } +} static void MifareSimInit(uint8_t flags, uint8_t *datain, tag_response_info_t **responses, uint32_t *cuid, uint8_t *uid_len) { @@ -155,7 +281,7 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t * uint32_t cuid = 0; uint8_t cardWRBL = 0; uint8_t cardAUTHSC = 0; - uint8_t cardAUTHKEY = 0xff; // no authentication + uint8_t cardAUTHKEY = AUTHKEYNONE; // no authentication uint32_t cardRr = 0; //uint32_t rn_enc = 0; uint32_t ans = 0; @@ -244,11 +370,11 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t * // WUPA in HALTED state or REQA or WUPA in any other state if (receivedCmd_len == 1 && ((receivedCmd[0] == ISO14443A_CMD_REQA && cardSTATE != MFEMUL_HALTED) || receivedCmd[0] == ISO14443A_CMD_WUPA)) { - EmSendPrecompiledCmd(&responses[ATQA], (receivedCmd[0] == ISO14443A_CMD_WUPA)); + EmSendPrecompiledCmd(&responses[ATQA]); // init crypto block crypto1_destroy(pcs); - cardAUTHKEY = 0xff; + cardAUTHKEY = AUTHKEYNONE; if (flags & FLAG_RANDOM_NONCE) { nonce = prand(); } @@ -268,7 +394,7 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t * // select all - 0x93 0x20 if (receivedCmd_len == 2 && (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && receivedCmd[1] == 0x20)) { if (MF_DBGLEVEL >= 4) Dbprintf("SELECT ALL CL1 received"); - EmSendPrecompiledCmd(&responses[UIDBCC1], false); + EmSendPrecompiledCmd(&responses[UIDBCC1]); break; } // select card - 0x93 0x70 ... @@ -276,12 +402,12 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t * (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && receivedCmd[1] == 0x70 && memcmp(&receivedCmd[2], responses[UIDBCC1].response, 4) == 0)) { if (MF_DBGLEVEL >= 4) Dbprintf("SELECT CL1 %02x%02x%02x%02x received",receivedCmd[2],receivedCmd[3],receivedCmd[4],receivedCmd[5]); if (uid_len == 4) { - EmSendPrecompiledCmd(&responses[SAKfinal], false); + EmSendPrecompiledCmd(&responses[SAKfinal]); LED_B_ON(); cardSTATE = MFEMUL_WORK; break; } else if (uid_len == 7) { - EmSendPrecompiledCmd(&responses[SAK1], false); + EmSendPrecompiledCmd(&responses[SAK1]); cardSTATE = MFEMUL_SELECT2; break; } @@ -293,7 +419,7 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t * // select all cl2 - 0x95 0x20 if (receivedCmd_len == 2 && (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2 && receivedCmd[1] == 0x20)) { if (MF_DBGLEVEL >= 4) Dbprintf("SELECT ALL CL2 received"); - EmSendPrecompiledCmd(&responses[UIDBCC2], false); + EmSendPrecompiledCmd(&responses[UIDBCC2]); break; } // select cl2 card - 0x95 0x70 xxxxxxxxxxxx @@ -301,7 +427,7 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t * (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2 && receivedCmd[1] == 0x70 && memcmp(&receivedCmd[2], responses[UIDBCC2].response, 4) == 0)) { if (uid_len == 7) { if (MF_DBGLEVEL >= 4) Dbprintf("SELECT CL2 %02x%02x%02x%02x received",receivedCmd[2],receivedCmd[3],receivedCmd[4],receivedCmd[5]); - EmSendPrecompiledCmd(&responses[SAKfinal], false); + EmSendPrecompiledCmd(&responses[SAKfinal]); LED_B_ON(); cardSTATE = MFEMUL_WORK; break; @@ -314,7 +440,7 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t * if (receivedCmd_len != 4) { // all commands must have exactly 4 bytes break; } - bool encrypted_data = (cardAUTHKEY != 0xFF) ; + bool encrypted_data = (cardAUTHKEY != AUTHKEYNONE) ; if (encrypted_data) { // decrypt seqence mf_crypto1_decryptEx(pcs, receivedCmd, receivedCmd_len, receivedCmd_dec); @@ -371,10 +497,24 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t * } } if (receivedCmd_dec[0] == ISO14443A_CMD_READBLOCK) { + uint8_t blockNo = receivedCmd_dec[1]; if (MF_DBGLEVEL >= 4) { - Dbprintf("Reader reading block %d (0x%02x)",receivedCmd_dec[1],receivedCmd_dec[1]); + Dbprintf("Reader reading block %d (0x%02x)", blockNo, blockNo); + } + emlGetMem(response, blockNo, 1); + if (IsSectorTrailer(blockNo)) { + memset(response, 0x00, 6); // keyA can never be read + if (!IsAccessAllowed(blockNo, cardAUTHKEY, AC_KEYB_READ)) { + memset(response+10, 0x00, 6); // keyB cannot be read + } + if (!IsAccessAllowed(blockNo, cardAUTHKEY, AC_AC_READ)) { + memset(response+6, 0x00, 4); // AC bits cannot be read + } + } else { + if (!IsAccessAllowed(blockNo, cardAUTHKEY, AC_DATA_READ)) { + memset(response, 0x00, 16); // datablock cannot be read + } } - emlGetMem(response, receivedCmd_dec[1], 1); AppendCrc14443a(response, 16); mf_crypto1_encrypt(pcs, response, 18, response_par); EmSendCmdPar(response, 18, response_par); @@ -386,21 +526,23 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t * break; } if (receivedCmd_dec[0] == ISO14443A_CMD_WRITEBLOCK) { - if (MF_DBGLEVEL >= 4) Dbprintf("RECV 0xA0 write block %d (%02x)",receivedCmd_dec[1],receivedCmd_dec[1]); + uint8_t blockNo = receivedCmd_dec[1]; + if (MF_DBGLEVEL >= 4) Dbprintf("RECV 0xA0 write block %d (%02x)", blockNo, blockNo); EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); - cardWRBL = receivedCmd_dec[1]; + cardWRBL = blockNo; cardSTATE = MFEMUL_WRITEBL2; break; } if (receivedCmd_dec[0] == MIFARE_CMD_INC || receivedCmd_dec[0] == MIFARE_CMD_DEC || receivedCmd_dec[0] == MIFARE_CMD_RESTORE) { - if (MF_DBGLEVEL >= 4) Dbprintf("RECV 0x%02x inc(0xC1)/dec(0xC0)/restore(0xC2) block %d (%02x)",receivedCmd_dec[0],receivedCmd_dec[1],receivedCmd_dec[1]); - if (emlCheckValBl(receivedCmd_dec[1])) { + uint8_t blockNo = receivedCmd_dec[1]; + if (MF_DBGLEVEL >= 4) Dbprintf("RECV 0x%02x inc(0xC1)/dec(0xC0)/restore(0xC2) block %d (%02x)",receivedCmd_dec[0], blockNo, blockNo); + if (emlCheckValBl(blockNo)) { if (MF_DBGLEVEL >= 2) Dbprintf("Reader tried to operate on block, but emlCheckValBl failed, nacking"); EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); break; } EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); - cardWRBL = receivedCmd_dec[1]; + cardWRBL = blockNo; if (receivedCmd_dec[0] == MIFARE_CMD_INC) cardSTATE = MFEMUL_INTREG_INC; if (receivedCmd_dec[0] == MIFARE_CMD_DEC) @@ -410,7 +552,8 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t * break; } if (receivedCmd_dec[0] == MIFARE_CMD_TRANSFER) { - if (MF_DBGLEVEL >= 4) Dbprintf("RECV 0x%02x transfer block %d (%02x)",receivedCmd_dec[0],receivedCmd_dec[1],receivedCmd_dec[1]); + uint8_t blockNo = receivedCmd_dec[1]; + if (MF_DBGLEVEL >= 4) Dbprintf("RECV 0x%02x transfer block %d (%02x)",receivedCmd_dec[0], blockNo, blockNo); if (emlSetValBl(cardINTREG, cardINTBLOCK, receivedCmd_dec[1])) EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); else @@ -508,20 +651,20 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t * // test if auth OK if (cardRr != prng_successor(nonce, 64)){ if (MF_DBGLEVEL >= 2) Dbprintf("AUTH FAILED for sector %d with key %c. cardRr=%08x, succ=%08x", - cardAUTHSC, cardAUTHKEY == 0 ? 'A' : 'B', + cardAUTHSC, cardAUTHKEY == AUTHKEYA ? 'A' : 'B', cardRr, prng_successor(nonce, 64)); // Shouldn't we respond anything here? // Right now, we don't nack or anything, which causes the // reader to do a WUPA after a while. /Martin // -- which is the correct response. /piwi - cardAUTHKEY = 0xff; // not authenticated + cardAUTHKEY = AUTHKEYNONE; // not authenticated cardSTATE_TO_IDLE(); break; } ans = prng_successor(nonce, 96) ^ crypto1_word(pcs, 0, 0); num_to_bytes(ans, 4, rAUTH_AT); EmSendCmd(rAUTH_AT, sizeof(rAUTH_AT)); - if (MF_DBGLEVEL >= 4) Dbprintf("AUTH COMPLETED for sector %d with key %c.", cardAUTHSC, cardAUTHKEY == 0 ? 'A' : 'B'); + if (MF_DBGLEVEL >= 4) Dbprintf("AUTH COMPLETED for sector %d with key %c.", cardAUTHSC, cardAUTHKEY == AUTHKEYA ? 'A' : 'B'); LED_C_ON(); cardSTATE = MFEMUL_WORK; break; @@ -530,8 +673,24 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t * if (receivedCmd_len == 18) { mf_crypto1_decryptEx(pcs, receivedCmd, receivedCmd_len, receivedCmd_dec); if (HasValidCRC(receivedCmd_dec, receivedCmd_len)) { + if (IsSectorTrailer(cardWRBL)) { + emlGetMem(response, cardWRBL, 1); + if (!IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_KEYA_WRITE)) { + memcpy(receivedCmd_dec, response, 6); // don't change KeyA + } + if (!IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_KEYB_WRITE)) { + memcpy(receivedCmd_dec+10, response+10, 6); // don't change KeyA + } + if (!IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_AC_WRITE)) { + memcpy(receivedCmd_dec+6, response+6, 4); // don't change AC bits + } + } else { + if (!IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_DATA_WRITE)) { + memcpy(receivedCmd_dec, response, 16); // don't change anything + } + } emlSetMem(receivedCmd_dec, cardWRBL, 1); - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); // always ACK? cardSTATE = MFEMUL_WORK; break; } diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c index e5ef6c19..38ca934a 100644 --- a/armsrc/mifareutil.c +++ b/armsrc/mifareutil.c @@ -9,13 +9,15 @@ // Work with mifare cards. //----------------------------------------------------------------------------- -#include #include "mifareutil.h" + +#include +#include + #include "proxmark3.h" #include "apps.h" #include "util.h" #include "parity.h" - #include "iso14443crc.h" #include "iso14443a.h" #include "crapto1/crapto1.h" @@ -585,6 +587,19 @@ uint8_t FirstBlockOfSector(uint8_t sectorNo) } +uint8_t SectorTrailer(uint8_t blockNo) +{ + if (blockNo < 32*4) { + return (blockNo | 0x03); + } else { + return (blockNo | 0x0f); + } +} + +bool IsSectorTrailer(uint8_t blockNo) +{ + return (blockNo == SectorTrailer(blockNo)); +} // work with emulator memory void emlSetMem(uint8_t *data, int blockNum, int blocksCount) { diff --git a/armsrc/mifareutil.h b/armsrc/mifareutil.h index 645d0e7d..c34dc8f4 100644 --- a/armsrc/mifareutil.h +++ b/armsrc/mifareutil.h @@ -12,6 +12,9 @@ #ifndef __MIFAREUTIL_H #define __MIFAREUTIL_H +#include +#include + #include "crapto1/crapto1.h" #include "usb_cdc.h" @@ -75,6 +78,8 @@ uint8_t mf_crypto1_encrypt4bit(struct Crypto1State *pcs, uint8_t data); // Mifare memory structure uint8_t NumBlocksPerSector(uint8_t sectorNo); uint8_t FirstBlockOfSector(uint8_t sectorNo); +bool IsSectorTrailer(uint8_t blockNo); +uint8_t SectorTrailer(uint8_t blockNo); // emulator functions void emlClearMem(void); diff --git a/fpga/fpga_hf.bit b/fpga/fpga_hf.bit index b9f2e6292e42801a73b57aa3e43dff00191ec8b9..229151dfbf50cb8de730a85446ccbc18407e8474 100644 GIT binary patch delta 64 zcmV-G0Kfmg$pXL00+2WnF)%MOHvnV+2{1T1GcY Date: Fri, 3 Nov 2017 13:42:38 +0200 Subject: [PATCH 062/310] moved includes, added clear_trace flag (it was there...) and fixed multiapdu iso14443 stream --- armsrc/iso14443a.c | 7 +++---- client/cmdhf14a.c | 37 +++++++++++++++++++++++++++++++++---- client/cmdhf14a.h | 15 +-------------- include/mifare.h | 3 ++- 4 files changed, 39 insertions(+), 23 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 5a27e06b..90e8538e 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1916,19 +1916,18 @@ void ReaderIso14443a(UsbCommand *c) uint8_t par[MAX_PARITY_SIZE]; bool cantSELECT = false; - if(param & ISO14A_CONNECT) { + set_tracing(true); + + if(param & ISO14A_CLEAR_TRACE) { clear_trace(); } - set_tracing(true); - if(param & ISO14A_REQUEST_TRIGGER) { iso14a_set_trigger(true); } if(param & ISO14A_CONNECT) { LED_A_ON(); - clear_trace(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); if(!(param & ISO14A_NO_SELECT)) { iso14a_card_select_t *card = (iso14a_card_select_t*)buf; diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 1ae63ada..c2d12fd9 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -11,9 +11,25 @@ #include "cmdhf14a.h" +#include "util.h" +#include "util_posix.h" +#include "iso14443crc.h" +#include "data.h" +#include "proxmark3.h" +#include "ui.h" +#include "cmdparser.h" +#include "common.h" +#include "cmdmain.h" +#include "mifare.h" +#include "cmdhfmfu.h" +#include "mifarehost.h" +#include "emv/apduinfo.h" +#include "emv/emvcore.h" + static int CmdHelp(const char *Cmd); static int waitCmd(uint8_t iLen); + const manufactureName manufactureMapping[] = { // ID, "Vendor Country" { 0x01, "Motorola UK" }, @@ -621,17 +637,22 @@ int CmdHF14ASnoop(const char *Cmd) { int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int *dataoutlen) { uint8_t data[USB_CMD_DATA_SIZE]; int datalen; - uint8_t cmdc = 0; + uint16_t cmdc = 0; uint8_t first, second; + static uint8_t iso14_pcb_blocknum; - if (activateField) - cmdc |= ISO14A_CONNECT; + if (activateField) { + cmdc |= ISO14A_CONNECT | ISO14A_CLEAR_TRACE; + iso14_pcb_blocknum = 0; + } if (leaveSignalON) cmdc |= ISO14A_NO_DISCONNECT; // ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02 memcpy(data + 1, datain, datainlen); data[0] = 0x02; // bnr,nad,cid,chn=0; i-block(0x00) + data[0] += iso14_pcb_blocknum; // add block number (bnr) + datalen = datainlen + 1; ComputeCrc14443(CRC_14443_A, data, datalen, &first, &second); @@ -673,6 +694,14 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea PrintAndLog("APDU ERROR: Small APDU response."); return 2; } + + // invert block number field in PCB byte + if ( ((recv[0] & 0xC0) == 0 // I-Block + || (recv[0] & 0xD0) == 0x80) // R-Block with ACK bit set to 0 + && (recv[0] & 0x01) == iso14_pcb_blocknum) // equal block numbers + { + iso14_pcb_blocknum ^= 1; + } // check block if (data[0] != recv[0]) { @@ -905,7 +934,7 @@ int CmdHF14ACmdRaw(const char *cmd) { if(active || active_select) { - c.arg[0] |= ISO14A_CONNECT; + c.arg[0] |= ISO14A_CONNECT | ISO14A_CLEAR_TRACE; if(active) c.arg[0] |= ISO14A_NO_SELECT; } diff --git a/client/cmdhf14a.h b/client/cmdhf14a.h index 16e03574..e7784de2 100644 --- a/client/cmdhf14a.h +++ b/client/cmdhf14a.h @@ -18,20 +18,7 @@ #include #include #include -#include "util.h" -#include "util_posix.h" -#include "iso14443crc.h" -#include "data.h" -#include "proxmark3.h" -#include "ui.h" -#include "cmdparser.h" -#include "common.h" -#include "cmdmain.h" -#include "mifare.h" -#include "cmdhfmfu.h" -#include "mifarehost.h" -#include "emv/apduinfo.h" -#include "emv/emvcore.h" +#include // structure and database for uid -> tagtype lookups typedef struct { diff --git a/include/mifare.h b/include/mifare.h index bede67a9..b821f32b 100644 --- a/include/mifare.h +++ b/include/mifare.h @@ -35,7 +35,8 @@ typedef enum ISO14A_COMMAND { ISO14A_SET_TIMEOUT = (1 << 6), ISO14A_NO_SELECT = (1 << 7), ISO14A_TOPAZMODE = (1 << 8), - ISO14A_NO_RATS = (1 << 9) + ISO14A_NO_RATS = (1 << 9), + ISO14A_CLEAR_TRACE = (1 << 10) } iso14a_command_t; typedef struct { From b7d3e89923d418c8a7b6acf1ca74f45b436be26d Mon Sep 17 00:00:00 2001 From: merlokk Date: Thu, 9 Nov 2017 16:39:40 +0200 Subject: [PATCH 063/310] changed iso14aapdu arm side and client --- armsrc/iso14443a.c | 45 ++++++++++++++++------- client/cmdhf14a.c | 91 +++++++++++++++++----------------------------- client/cmdhf14a.h | 1 + 3 files changed, 66 insertions(+), 71 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 90e8538e..45425e07 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -12,10 +12,11 @@ #include "iso14443a.h" +#include +#include #include "proxmark3.h" #include "apps.h" #include "util.h" -#include "string.h" #include "cmd.h" #include "iso14443crc.h" #include "crapto1/crapto1.h" @@ -1873,29 +1874,47 @@ void iso14443a_setup(uint8_t fpga_minor_mode) { int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data) { uint8_t parity[MAX_PARITY_SIZE]; - uint8_t real_cmd[cmd_len+4]; - real_cmd[0] = 0x0a; //I-Block + uint8_t real_cmd[cmd_len + 4]; + + // ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02 + real_cmd[0] = 0x02; // bnr,nad,cid,chn=0; i-block(0x00) // put block number into the PCB real_cmd[0] |= iso14_pcb_blocknum; - real_cmd[1] = 0x00; //CID: 0 //FIXME: allow multiple selected cards - memcpy(real_cmd+2, cmd, cmd_len); - AppendCrc14443a(real_cmd,cmd_len+2); + memcpy(real_cmd + 1, cmd, cmd_len); + AppendCrc14443a(real_cmd, cmd_len + 1); - ReaderTransmit(real_cmd, cmd_len+4, NULL); + ReaderTransmit(real_cmd, cmd_len + 3, NULL); + size_t len = ReaderReceive(data, parity); uint8_t *data_bytes = (uint8_t *) data; - if (!len) + + if (!len) { return 0; //DATA LINK ERROR + // if we received an I- or R(ACK)-Block with a block number equal to the // current block number, toggle the current block number - else if (len >= 4 // PCB+CID+CRC = 4 bytes + } else{ + if (len >= 3 // PCB+CRC = 3 bytes && ((data_bytes[0] & 0xC0) == 0 // I-Block || (data_bytes[0] & 0xD0) == 0x80) // R-Block with ACK bit set to 0 && (data_bytes[0] & 0x01) == iso14_pcb_blocknum) // equal block numbers - { - iso14_pcb_blocknum ^= 1; - } + { + iso14_pcb_blocknum ^= 1; + } + // crc check + if (len >=3 && !CheckCrc14443(CRC_14443_A, data_bytes, len)) { + return -1; + } + + } + + // cut frame byte + len -= 1; + // memmove(data_bytes, data_bytes + 1, len); + for (int i = 0; i < len; i++) + data_bytes[i] = data_bytes[i + 1]; + return len; } @@ -1952,7 +1971,7 @@ void ReaderIso14443a(UsbCommand *c) if(param & ISO14A_APDU && !cantSELECT) { arg0 = iso14_apdu(cmd, len, buf); LED_B_ON(); - cmd_send(CMD_ACK,arg0,0,0,buf,sizeof(buf)); + cmd_send(CMD_ACK, arg0, 0, 0, buf, sizeof(buf)); LED_B_OFF(); } diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index c2d12fd9..18e13794 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -634,87 +634,73 @@ int CmdHF14ASnoop(const char *Cmd) { return 0; } +void DropField() { + UsbCommand c = {CMD_READER_ISO_14443a, {0, 0, 0}}; + SendCommand(&c); +} + int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int *dataoutlen) { - uint8_t data[USB_CMD_DATA_SIZE]; - int datalen; uint16_t cmdc = 0; - uint8_t first, second; - static uint8_t iso14_pcb_blocknum; if (activateField) { cmdc |= ISO14A_CONNECT | ISO14A_CLEAR_TRACE; - iso14_pcb_blocknum = 0; } if (leaveSignalON) cmdc |= ISO14A_NO_DISCONNECT; - // ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02 - memcpy(data + 1, datain, datainlen); - data[0] = 0x02; // bnr,nad,cid,chn=0; i-block(0x00) - data[0] += iso14_pcb_blocknum; // add block number (bnr) - - datalen = datainlen + 1; - - ComputeCrc14443(CRC_14443_A, data, datalen, &first, &second); - data[datalen++] = first; - data[datalen++] = second; - // "Command APDU" length should be 5+255+1, but javacard's APDU buffer might be smaller - 133 bytes // https://stackoverflow.com/questions/32994936/safe-max-java-card-apdu-data-command-and-respond-size // here length USB_CMD_DATA_SIZE=512 // timeout timeout14a * 1.06 / 100, true, size, &keyBlock[6 * c], e_sector); // timeout is (ms * 106)/10 or us*0.0106 - UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_RAW | ISO14A_SET_TIMEOUT | cmdc, (datalen & 0xFFFF), 1000 * 1000 * 1.06 / 100}}; - memcpy(c.d.asBytes, data, datalen); + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_APDU | ISO14A_SET_TIMEOUT | cmdc, (datainlen & 0xFFFF), 1000 * 1000 * 1.06 / 100}}; + memcpy(c.d.asBytes, datain, datainlen); SendCommand(&c); uint8_t *recv; UsbCommand resp; if (activateField) { - if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) + if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + PrintAndLog("APDU ERROR: Proxmark connection timeout."); return 1; - if (resp.arg[0] != 1) + } + if (resp.arg[0] != 1) { + PrintAndLog("APDU ERROR: Proxmark error %d.", resp.arg[0]); return 1; + } } if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { recv = resp.d.asBytes; - uint8_t iLen = resp.arg[0]; + int iLen = resp.arg[0]; - *dataoutlen = iLen - 1 - 2; + *dataoutlen = iLen - 2; if (*dataoutlen < 0) *dataoutlen = 0; - memcpy(dataout, recv + 1, *dataoutlen); + memcpy(dataout, recv, *dataoutlen); - if(!iLen) + if(!iLen) { + PrintAndLog("APDU ERROR: No APDU response."); return 1; - - // check apdu length - if (iLen < 5) { - PrintAndLog("APDU ERROR: Small APDU response."); - return 2; } - // invert block number field in PCB byte - if ( ((recv[0] & 0xC0) == 0 // I-Block - || (recv[0] & 0xD0) == 0x80) // R-Block with ACK bit set to 0 - && (recv[0] & 0x01) == iso14_pcb_blocknum) // equal block numbers - { - iso14_pcb_blocknum ^= 1; - } - - // check block - if (data[0] != recv[0]) { - PrintAndLog("APDU ERROR: Block type mismatch: %02x-%02x", data[0], recv[0]); + // check block TODO + if (iLen == -2) { + PrintAndLog("APDU ERROR: Block type mismatch."); return 2; } // CRC Check - ComputeCrc14443(CRC_14443_A, recv, iLen, &first, &second); - if (first || second) { + if (iLen == -1) { PrintAndLog("APDU ERROR: ISO 14443A CRC error."); return 3; } + + // check apdu length + if (iLen < 4) { + PrintAndLog("APDU ERROR: Small APDU response. Len=%d", iLen); + return 2; + } } else { PrintAndLog("APDU ERROR: Reply timeout."); @@ -782,23 +768,12 @@ int CmdHF14AAPDU(const char *cmd) { cmdp++; } - PrintAndLog("--%s %s %s >>>> %s", activateField ? "sel": "", leaveSignalON ? "keep": "", decodeTLV ? "TLV": "", sprint_hex(data, datalen)); + PrintAndLog(">>>>[%s%s%s] %s", activateField ? "sel ": "", leaveSignalON ? "keep ": "", decodeTLV ? "TLV": "", sprint_hex(data, datalen)); - switch(ExchangeAPDU14a(data, datalen, activateField, leaveSignalON, data, &datalen)) { - case 0: - break; - case 1: - PrintAndLog("APDU ERROR: Send APDU error."); - return 1; - case 2: - return 2; - case 3: - return 3; - case 4: - return 4; - default: - return 5; - } + int res = ExchangeAPDU14a(data, datalen, activateField, leaveSignalON, data, &datalen); + + if (res) + return res; PrintAndLog("<<<< %s", sprint_hex(data, datalen)); diff --git a/client/cmdhf14a.h b/client/cmdhf14a.h index e7784de2..5276da36 100644 --- a/client/cmdhf14a.h +++ b/client/cmdhf14a.h @@ -35,6 +35,7 @@ int CmdHF14ASim(const char *Cmd); int CmdHF14ASnoop(const char *Cmd); char* getTagInfo(uint8_t uid); +extern void DropField(); extern int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int *dataoutlen); #endif From 86a1c1dc252076f6c5d678f172df9ff116bea50d Mon Sep 17 00:00:00 2001 From: merlokk Date: Thu, 9 Nov 2017 22:01:00 +0200 Subject: [PATCH 064/310] eliminate double DropField command --- client/cmdhfmfu.c | 6 ------ client/cmdhfmfu.h | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/client/cmdhfmfu.c b/client/cmdhfmfu.c index 3021631a..c95fa20b 100644 --- a/client/cmdhfmfu.c +++ b/client/cmdhfmfu.c @@ -113,12 +113,6 @@ static void ul_switch_on_field(void) { SendCommand(&c); } -void ul_switch_off_field(void) { - UsbCommand c = {CMD_READER_ISO_14443a, {0, 0, 0}}; - clearCommandBuffer(); - SendCommand(&c); -} - static int ul_send_cmd_raw( uint8_t *cmd, uint8_t cmdlen, uint8_t *response, uint16_t responseLength ) { UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_RAW | ISO14A_NO_DISCONNECT | ISO14A_APPEND_CRC, cmdlen, 0}}; memcpy(c.d.asBytes, cmd, cmdlen); diff --git a/client/cmdhfmfu.h b/client/cmdhfmfu.h index 6c9e3ea1..a5e9f95c 100644 --- a/client/cmdhfmfu.h +++ b/client/cmdhfmfu.h @@ -16,7 +16,7 @@ int CmdHF14AMfUInfo(const char *Cmd); uint32_t GetHF14AMfU_Type(void); int ul_print_type(uint32_t tagtype, uint8_t spacer); -void ul_switch_off_field(void); +#define ul_switch_off_field DropField int usage_hf_mfu_dump(void); int usage_hf_mfu_info(void); From 9658b9e137dbeb2f81ec3d72d3045c1ee22650dd Mon Sep 17 00:00:00 2001 From: merlokk Date: Fri, 10 Nov 2017 09:15:56 +0200 Subject: [PATCH 065/310] move include modules from h to c file --- client/cmdhf14a.c | 5 +++++ client/cmdhf14a.h | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 18e13794..24a66b88 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -11,6 +11,11 @@ #include "cmdhf14a.h" +#include +#include +#include +#include +#include #include "util.h" #include "util_posix.h" #include "iso14443crc.h" diff --git a/client/cmdhf14a.h b/client/cmdhf14a.h index 5276da36..6f6df907 100644 --- a/client/cmdhf14a.h +++ b/client/cmdhf14a.h @@ -12,12 +12,7 @@ #ifndef CMDHF14A_H__ #define CMDHF14A_H__ -#include #include -#include -#include -#include -#include #include // structure and database for uid -> tagtype lookups From f5adb06faec7497926f6c42d5d045aac21e7ce22 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Fri, 10 Nov 2017 20:11:51 +0100 Subject: [PATCH 066/310] revert field on/off in hf 14a reader fix typo --- client/cmdhf14a.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 24a66b88..4b3975b3 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -132,28 +132,27 @@ int CmdHF14AList(const char *Cmd) int CmdHF14AReader(const char *Cmd) { uint32_t cm = ISO14A_CONNECT; - bool disconnectAfter = false; + bool disconnectAfter = true; int cmdp = 0; while(param_getchar(Cmd, cmdp) != 0x00) { switch(param_getchar(Cmd, cmdp)) { case 'h': case 'H': - PrintAndLog("Usage: hf 14a reader [d] [3]"); - PrintAndLog(" d drop the signal field after command executed"); + PrintAndLog("Usage: hf 14a reader [k|x] [3]"); + PrintAndLog(" k keep the field active after command executed"); PrintAndLog(" x just drop the signal field"); PrintAndLog(" 3 ISO14443-3 select only (skip RATS)"); return 0; case '3': cm |= ISO14A_NO_RATS; break; - case 'd': - case 'D': - disconnectAfter = true; + case 'k': + case 'K': + disconnectAfter = false; break; case 'x': case 'X': - disconnectAfter = true; cm = cm - ISO14A_CONNECT; break; default: @@ -196,10 +195,15 @@ int CmdHF14AReader(const char *Cmd) { if(card.ats_len >= 3) { // a valid ATS consists of at least the length byte (TL) and 2 CRC bytes PrintAndLog(" ATS : %s", sprint_hex(card.ats, card.ats_len)); } - PrintAndLog("Card is selected. You can now start sending commands"); - } else { + if (!disconnectAfter) { + PrintAndLog("Card is selected. You can now start sending commands"); + } + } + + if (disconnectAfter) { PrintAndLog("Field dropped."); } + return 0; } @@ -1009,7 +1013,7 @@ static command_t CommandTable[] = {"cuids", CmdHF14ACUIDs, 0, " Collect n>0 ISO14443 Type A UIDs in one go"}, {"sim", CmdHF14ASim, 0, " -- Simulate ISO 14443a tag"}, {"snoop", CmdHF14ASnoop, 0, "Eavesdrop ISO 14443 Type A"}, - {"apdu", CmdHF14AAPDU, 0, "Send ISO 1443-4 APDU to tag"}, + {"apdu", CmdHF14AAPDU, 0, "Send ISO 14443-4 APDU to tag"}, {"raw", CmdHF14ACmdRaw, 0, "Send raw hex data to tag"}, {NULL, NULL, 0, NULL} }; From 43912d6349ce08bb5d299b1602fba30e0354eaaa Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Fri, 10 Nov 2017 21:39:10 +0100 Subject: [PATCH 067/310] add: proper indentation when printing TLV objects --- client/emv/dump.c | 5 ++++- client/emv/dump.h | 2 +- client/emv/emv_tags.c | 41 ++++++++++++++++++++++++++++------------- client/emv/emv_tags.h | 2 +- client/emv/emvcore.c | 8 ++++---- client/emv/tlv.c | 6 +++--- client/emv/tlv.h | 4 ++-- 7 files changed, 43 insertions(+), 25 deletions(-) diff --git a/client/emv/dump.c b/client/emv/dump.c index 9915ad73..41d7c9fd 100644 --- a/client/emv/dump.c +++ b/client/emv/dump.c @@ -21,6 +21,8 @@ #include +#define PRINT_INDENT(level) {for (int i = 0; i < (level); i++) fprintf(f, "\t");} + void dump_buffer_simple(const unsigned char *ptr, size_t len, FILE *f) { int i; @@ -32,7 +34,7 @@ void dump_buffer_simple(const unsigned char *ptr, size_t len, FILE *f) fprintf(f, "%s%02hhX", i ? " " : "", ptr[i]); } -void dump_buffer(const unsigned char *ptr, size_t len, FILE *f) +void dump_buffer(const unsigned char *ptr, size_t len, FILE *f, int level) { int i, j; @@ -40,6 +42,7 @@ void dump_buffer(const unsigned char *ptr, size_t len, FILE *f) f = stdout; for (i = 0; i < len; i += 16) { + PRINT_INDENT(level); fprintf(f, "\t%02x:", i); for (j = 0; j < 16; j++) { if (i + j < len) diff --git a/client/emv/dump.h b/client/emv/dump.h index 5976da44..ad69ea83 100644 --- a/client/emv/dump.h +++ b/client/emv/dump.h @@ -19,6 +19,6 @@ #include void dump_buffer_simple(const unsigned char *ptr, size_t len, FILE *f); -void dump_buffer(const unsigned char *ptr, size_t len, FILE *f); +void dump_buffer(const unsigned char *ptr, size_t len, FILE *f, int level); #endif diff --git a/client/emv/emv_tags.c b/client/emv/emv_tags.c index 1aae847a..d91685c4 100644 --- a/client/emv/emv_tags.c +++ b/client/emv/emv_tags.c @@ -22,6 +22,8 @@ #include +#define PRINT_INDENT(level) {for (int i = 0; i < (level); i++) fprintf(f, "\t");} + enum emv_tag_t { EMV_TAG_GENERIC, EMV_TAG_BITMASK, @@ -230,16 +232,18 @@ static const char *bitstrings[] = { "1.......", }; -static void emv_tag_dump_bitmask(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) +static void emv_tag_dump_bitmask(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) { const struct emv_tag_bit *bits = tag->data; unsigned bit, byte; for (byte = 1; byte <= tlv->len; byte ++) { unsigned char val = tlv->value[byte - 1]; + PRINT_INDENT(level); fprintf(f, "\tByte %u (%02x)\n", byte, val); for (bit = 8; bit > 0; bit--, val <<= 1) { if (val & 0x80) + PRINT_INDENT(level); fprintf(f, "\t\t%s - '%s'\n", bitstrings[bit - 1], bits->bit == EMV_BIT(byte, bit) ? bits->name : "Unknown"); if (bits->bit == EMV_BIT(byte, bit)) @@ -248,7 +252,7 @@ static void emv_tag_dump_bitmask(const struct tlv *tlv, const struct emv_tag *ta } } -static void emv_tag_dump_dol(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) +static void emv_tag_dump_dol(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) { const unsigned char *buf = tlv->value; size_t left = tlv->len; @@ -258,20 +262,24 @@ static void emv_tag_dump_dol(const struct tlv *tlv, const struct emv_tag *tag, F const struct emv_tag *doltag; if (!tlv_parse_tl(&buf, &left, &doltlv)) { + PRINT_INDENT(level); fprintf(f, "Invalid Tag-Len\n"); continue; } doltag = emv_get_tag(&doltlv); + PRINT_INDENT(level); fprintf(f, "\tTag %4hx len %02zx ('%s')\n", doltlv.tag, doltlv.len, doltag->name); } } -static void emv_tag_dump_string(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) +static void emv_tag_dump_string(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) { + PRINT_INDENT(level); fprintf(f, "\tString value '"); fwrite(tlv->value, 1, tlv->len, f); + PRINT_INDENT(level); fprintf(f, "'\n"); } @@ -306,13 +314,15 @@ static unsigned long emv_value_numeric(const struct tlv *tlv, unsigned start, un return ret; } -static void emv_tag_dump_numeric(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) +static void emv_tag_dump_numeric(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) { + PRINT_INDENT(level); fprintf(f, "\tNumeric value %lu\n", emv_value_numeric(tlv, 0, tlv->len * 2)); } -static void emv_tag_dump_yymmdd(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) +static void emv_tag_dump_yymmdd(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) { + PRINT_INDENT(level); fprintf(f, "\tDate: 20%02ld.%ld.%ld\n", emv_value_numeric(tlv, 0, 2), emv_value_numeric(tlv, 2, 4), @@ -324,12 +334,13 @@ static uint32_t emv_get_binary(const unsigned char *S) return (S[0] << 24) | (S[1] << 16) | (S[2] << 8) | (S[3] << 0); } -static void emv_tag_dump_cvm_list(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) +static void emv_tag_dump_cvm_list(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) { uint32_t X, Y; int i; if (tlv->len < 10 || tlv->len % 2) { + PRINT_INDENT(level); fprintf(f, "\tINVALID!\n"); return; } @@ -337,7 +348,9 @@ static void emv_tag_dump_cvm_list(const struct tlv *tlv, const struct emv_tag *t X = emv_get_binary(tlv->value); Y = emv_get_binary(tlv->value + 4); + PRINT_INDENT(level); fprintf(f, "\tX: %d\n", X); + PRINT_INDENT(level); fprintf(f, "\tY: %d\n", Y); for (i = 8; i < tlv->len; i+= 2) { @@ -413,13 +426,14 @@ static void emv_tag_dump_cvm_list(const struct tlv *tlv, const struct emv_tag *t break; } + PRINT_INDENT(level); fprintf(f, "\t%02x %02x: '%s' '%s' and '%s' if this CVM is unsuccessful\n", tlv->value[i], tlv->value[i+1], method, condition, (tlv->value[i] & 0x40) ? "continue" : "fail"); } } -bool emv_tag_dump(const struct tlv *tlv, FILE *f) +bool emv_tag_dump(const struct tlv *tlv, FILE *f, int level) { if (!tlv) { fprintf(f, "NULL\n"); @@ -428,28 +442,29 @@ bool emv_tag_dump(const struct tlv *tlv, FILE *f) const struct emv_tag *tag = emv_get_tag(tlv); + PRINT_INDENT(level); fprintf(f, "--%2hx[%02zx] '%s':\n", tlv->tag, tlv->len, tag->name); switch (tag->type) { case EMV_TAG_GENERIC: break; case EMV_TAG_BITMASK: - emv_tag_dump_bitmask(tlv, tag, f); + emv_tag_dump_bitmask(tlv, tag, f, level); break; case EMV_TAG_DOL: - emv_tag_dump_dol(tlv, tag, f); + emv_tag_dump_dol(tlv, tag, f, level); break; case EMV_TAG_CVM_LIST: - emv_tag_dump_cvm_list(tlv, tag, f); + emv_tag_dump_cvm_list(tlv, tag, f, level); break; case EMV_TAG_STRING: - emv_tag_dump_string(tlv, tag, f); + emv_tag_dump_string(tlv, tag, f, level); break; case EMV_TAG_NUMERIC: - emv_tag_dump_numeric(tlv, tag, f); + emv_tag_dump_numeric(tlv, tag, f, level); break; case EMV_TAG_YYMMDD: - emv_tag_dump_yymmdd(tlv, tag, f); + emv_tag_dump_yymmdd(tlv, tag, f, level); break; }; diff --git a/client/emv/emv_tags.h b/client/emv/emv_tags.h index de6d9d1e..8dbd9e00 100644 --- a/client/emv/emv_tags.h +++ b/client/emv/emv_tags.h @@ -19,6 +19,6 @@ #include "tlv.h" #include -bool emv_tag_dump(const struct tlv *tlv, FILE *f); +bool emv_tag_dump(const struct tlv *tlv, FILE *f, int level); #endif diff --git a/client/emv/emvcore.c b/client/emv/emvcore.c index 9264b110..8dc93259 100644 --- a/client/emv/emvcore.c +++ b/client/emv/emvcore.c @@ -10,9 +10,9 @@ #include "emvcore.h" -static bool print_cb(void *data, const struct tlv *tlv) { - emv_tag_dump(tlv, stdout); - dump_buffer(tlv->value, tlv->len, stdout); +static bool print_cb(void *data, const struct tlv *tlv, int level) { + emv_tag_dump(tlv, stdout, level); + dump_buffer(tlv->value, tlv->len, stdout, level); return true; } @@ -23,7 +23,7 @@ void TLVPrintFromBuffer(uint8_t *data, int datalen) { if (t) { PrintAndLog("TLV decoded:"); - tlvdb_visit(t, print_cb, NULL); + tlvdb_visit(t, print_cb, NULL, 0); tlvdb_free(t); } else { PrintAndLog("TLV ERROR: Can't parse response as TLV tree."); diff --git a/client/emv/tlv.c b/client/emv/tlv.c index d78f049e..7feaa9aa 100644 --- a/client/emv/tlv.c +++ b/client/emv/tlv.c @@ -308,7 +308,7 @@ void tlvdb_add(struct tlvdb *tlvdb, struct tlvdb *other) tlvdb->next = other; } -void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data) +void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data, int level) { struct tlvdb *next = NULL; @@ -317,8 +317,8 @@ void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data) for (; tlvdb; tlvdb = next) { next = tlvdb->next; - cb(data, &tlvdb->tag); - tlvdb_visit(tlvdb->children, cb, data); + cb(data, &tlvdb->tag, level); + tlvdb_visit(tlvdb->children, cb, data, level+1); } } diff --git a/client/emv/tlv.h b/client/emv/tlv.h index 3fd3f347..187781f8 100644 --- a/client/emv/tlv.h +++ b/client/emv/tlv.h @@ -31,7 +31,7 @@ struct tlv { }; struct tlvdb; -typedef bool (*tlv_cb)(void *data, const struct tlv *tlv); +typedef bool (*tlv_cb)(void *data, const struct tlv *tlv, int level); struct tlvdb *tlvdb_fixed(tlv_tag_t tag, size_t len, const unsigned char *value); struct tlvdb *tlvdb_external(tlv_tag_t tag, size_t len, const unsigned char *value); @@ -41,7 +41,7 @@ void tlvdb_free(struct tlvdb *tlvdb); void tlvdb_add(struct tlvdb *tlvdb, struct tlvdb *other); -void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data); +void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data, int level); const struct tlv *tlvdb_get(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev); bool tlv_parse_tl(const unsigned char **buf, size_t *len, struct tlv *tlv); From 217cfb6b29773989774cbe573af0372df09f056b Mon Sep 17 00:00:00 2001 From: marshmellow42 Date: Fri, 10 Nov 2017 15:56:52 -0500 Subject: [PATCH 068/310] fix lf search bugs when no tag is on antenna cotag read could enter endless loop, now cancels if the next bit doesn't appear em4x05 detection would loop due to a threshold never being met, now has a dump out after 1000 samples tested. fixed some indenting in hitag2 while i was reviewing that code for potential endless loops... --- armsrc/hitag2.c | 59 ++++++++++++++++++++++++--------------------- armsrc/lfops.c | 8 +++--- armsrc/lfsampling.c | 32 +++++++++++++++--------- armsrc/lfsampling.h | 2 +- client/cmdlf.c | 2 +- client/cmdlfem4x.c | 1 - client/cmdlfhitag.c | 1 + 7 files changed, 59 insertions(+), 46 deletions(-) diff --git a/armsrc/hitag2.c b/armsrc/hitag2.c index 7702025b..aec01860 100644 --- a/armsrc/hitag2.c +++ b/armsrc/hitag2.c @@ -1217,7 +1217,7 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { int reset_sof; int tag_sof; int t_wait = HITAG_T_WAIT_MAX; - bool bStop; + bool bStop = false; bool bQuitTraceFull = false; FpgaDownloadAndGo(FPGA_BITSTREAM_LF); @@ -1324,7 +1324,6 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { frame_count = 0; response = 0; lastbit = 1; - bStop = false; // Tag specific configuration settings (sof, timings, etc.) if (htf < 10){ @@ -1369,46 +1368,46 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { // By default reset the transmission buffer tx = txbuf; switch(htf) { - case RHT2F_PASSWORD: { - bStop = !hitag2_password(rx,rxlen,tx,&txlen); - } break; - case RHT2F_AUTHENTICATE: { - bStop = !hitag2_authenticate(rx,rxlen,tx,&txlen); - } break; - case RHT2F_CRYPTO: { - bStop = !hitag2_crypto(rx,rxlen,tx,&txlen, false); - } break; - case RHT2F_TEST_AUTH_ATTEMPTS: { - bStop = !hitag2_test_auth_attempts(rx,rxlen,tx,&txlen); - } break; - case RHT2F_UID_ONLY: { - bStop = !hitag2_read_uid(rx, rxlen, tx, &txlen); - attempt_count++; //attempt 3 times to get uid then quit - if (!bStop && attempt_count == 3) bStop = true; - } break; - default: { - Dbprintf("Error, unknown function: %d",htf); - return; - } break; + case RHT2F_PASSWORD: { + bStop = !hitag2_password(rx,rxlen,tx,&txlen); + } break; + case RHT2F_AUTHENTICATE: { + bStop = !hitag2_authenticate(rx,rxlen,tx,&txlen); + } break; + case RHT2F_CRYPTO: { + bStop = !hitag2_crypto(rx,rxlen,tx,&txlen, false); + } break; + case RHT2F_TEST_AUTH_ATTEMPTS: { + bStop = !hitag2_test_auth_attempts(rx,rxlen,tx,&txlen); + } break; + case RHT2F_UID_ONLY: { + bStop = !hitag2_read_uid(rx, rxlen, tx, &txlen); + attempt_count++; //attempt 3 times to get uid then quit + if (!bStop && attempt_count == 3) bStop = true; + } break; + default: { + Dbprintf("Error, unknown function: %d",htf); + return; + } break; } - + // Send and store the reader command // Disable timer 1 with external trigger to avoid triggers during our own modulation AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; - + // Wait for HITAG_T_WAIT_2 carrier periods after the last tag bit before transmitting, // Since the clock counts since the last falling edge, a 'one' means that the // falling edge occured halfway the period. with respect to this falling edge, // we need to wait (T_Wait2 + half_tag_period) when the last was a 'one'. // All timer values are in terms of T0 units while(AT91C_BASE_TC0->TC_CV < T0*(t_wait+(HITAG_T_TAG_HALF_PERIOD*lastbit))); - + //Dbprintf("DEBUG: Sending reader frame"); // Transmit the reader frame hitag_reader_send_frame(tx,txlen); - // Enable and reset external trigger in timer for capturing future frames + // Enable and reset external trigger in timer for capturing future frames AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // Add transmitted frame to total count @@ -1521,7 +1520,11 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); //Dbprintf("frame received: %d",frame_count); //DbpString("All done"); - cmd_send(CMD_ACK,bSuccessful,0,0,(byte_t*)tag.sectors,48); + if (bSuccessful) + cmd_send(CMD_ACK,bSuccessful,0,0,(byte_t*)tag.sectors,48); + else + cmd_send(CMD_ACK,bSuccessful,0,0,0,0); + } void WriterHitag(hitag_function htf, hitag_data* htd, int page) { diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 641c02e8..4344742b 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -1159,7 +1159,7 @@ void T55xxResetRead(void) { TurnReadLFOn(READ_GAP); // Acquisition - DoPartialAcquisition(0, true, BigBuf_max_traceLen()); + DoPartialAcquisition(0, true, BigBuf_max_traceLen(), 0); // Turn the field off FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); // field off @@ -1291,7 +1291,7 @@ void T55xxReadBlock(uint16_t arg0, uint8_t Block, uint32_t Pwd) { // Acquisition // Now do the acquisition - DoPartialAcquisition(0, true, 12000); + DoPartialAcquisition(0, true, 12000, 0); // Turn the field off FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); // field off @@ -1690,7 +1690,7 @@ void EM4xReadWord(uint8_t Address, uint32_t Pwd, uint8_t PwdMode) { SendForward(fwd_bit_count); WaitUS(400); // Now do the acquisition - DoPartialAcquisition(20, true, 6000); + DoPartialAcquisition(20, true, 6000, 1000); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); // field off LED_A_OFF(); @@ -1723,7 +1723,7 @@ void EM4xWriteWord(uint32_t flag, uint32_t Data, uint32_t Pwd) { WaitUS(6500); //Capture response if one exists - DoPartialAcquisition(20, true, 6000); + DoPartialAcquisition(20, true, 6000, 1000); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); // field off LED_A_OFF(); diff --git a/armsrc/lfsampling.c b/armsrc/lfsampling.c index 5d1f9248..643b3168 100644 --- a/armsrc/lfsampling.c +++ b/armsrc/lfsampling.c @@ -119,7 +119,7 @@ void LFSetupFPGAForADC(int divisor, bool lf_field) * @param silent - is true, now outputs are made. If false, dbprints the status * @return the number of bits occupied by the samples. */ -uint32_t DoAcquisition(uint8_t decimation, uint32_t bits_per_sample, bool averaging, int trigger_threshold, bool silent, int bufsize) +uint32_t DoAcquisition(uint8_t decimation, uint32_t bits_per_sample, bool averaging, int trigger_threshold, bool silent, int bufsize, int cancel_after) { //. uint8_t *dest = BigBuf_get_addr(); @@ -140,6 +140,7 @@ uint32_t DoAcquisition(uint8_t decimation, uint32_t bits_per_sample, bool averag uint32_t sample_sum =0 ; uint32_t sample_total_numbers =0 ; uint32_t sample_total_saved =0 ; + uint32_t cancel_counter = 0; while(!BUTTON_PRESS() && !usb_poll_validate_length() ) { WDT_HIT(); @@ -151,9 +152,11 @@ uint32_t DoAcquisition(uint8_t decimation, uint32_t bits_per_sample, bool averag sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; LED_D_OFF(); // threshold either high or low values 128 = center 0. if trigger = 178 - if ((trigger_threshold > 0) && (sample < (trigger_threshold+128)) && (sample > (128-trigger_threshold))) // + if ((trigger_threshold > 0) && (sample < (trigger_threshold+128)) && (sample > (128-trigger_threshold))) { // + if (cancel_after > 0) cancel_counter++; + if (cancel_after == cancel_counter) break; continue; - + } trigger_threshold = 0; sample_total_numbers++; @@ -213,7 +216,7 @@ uint32_t DoAcquisition(uint8_t decimation, uint32_t bits_per_sample, bool averag */ uint32_t DoAcquisition_default(int trigger_threshold, bool silent) { - return DoAcquisition(1,8,0,trigger_threshold,silent,0); + return DoAcquisition(1,8,0,trigger_threshold,silent,0,0); } uint32_t DoAcquisition_config(bool silent, int sample_size) { @@ -222,11 +225,12 @@ uint32_t DoAcquisition_config(bool silent, int sample_size) ,config.averaging ,config.trigger_threshold ,silent - ,sample_size); + ,sample_size + ,0); } -uint32_t DoPartialAcquisition(int trigger_threshold, bool silent, int sample_size) { - return DoAcquisition(1,8,0,trigger_threshold,silent,sample_size); +uint32_t DoPartialAcquisition(int trigger_threshold, bool silent, int sample_size, int cancel_after) { + return DoAcquisition(1,8,0,trigger_threshold,silent,sample_size,cancel_after); } uint32_t ReadLF(bool activeField, bool silent, int sample_size) @@ -329,8 +333,8 @@ uint32_t doCotagAcquisitionManchester() { uint8_t sample = 0, firsthigh = 0, firstlow = 0; uint16_t sample_counter = 0, period = 0; uint8_t curr = 0, prev = 0; - - while (!BUTTON_PRESS() && !usb_poll_validate_length() && (sample_counter < bufsize) ) { + uint16_t noise_counter = 0; + while (!BUTTON_PRESS() && !usb_poll_validate_length() && (sample_counter < bufsize) && (noiseCounter < (COTAG_T1<<1)) ) { WDT_HIT(); if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { AT91C_BASE_SSC->SSC_THR = 0x43; @@ -343,14 +347,20 @@ uint32_t doCotagAcquisitionManchester() { // find first peak if ( !firsthigh ) { - if (sample < COTAG_ONE_THRESHOLD) + if (sample < COTAG_ONE_THRESHOLD) { + noise_counter++; continue; + } + noise_counter = 0; firsthigh = 1; } if ( !firstlow ){ - if (sample > COTAG_ZERO_THRESHOLD ) + if (sample > COTAG_ZERO_THRESHOLD ) { + noise_counter++; continue; + } + noise_counter=0; firstlow = 1; } diff --git a/armsrc/lfsampling.h b/armsrc/lfsampling.h index 0531e0c9..ea044f3c 100644 --- a/armsrc/lfsampling.h +++ b/armsrc/lfsampling.h @@ -21,7 +21,7 @@ uint32_t SampleLF(bool silent, int sample_size); uint32_t SnoopLF(); // adds sample size to default options -uint32_t DoPartialAcquisition(int trigger_threshold, bool silent, int sample_size); +uint32_t DoPartialAcquisition(int trigger_threshold, bool silent, int sample_size, int cancel_after); /** * @brief Does sample acquisition, ignoring the config values set in the sample_config. diff --git a/client/cmdlf.c b/client/cmdlf.c index eb664a11..cdfbee2d 100644 --- a/client/cmdlf.c +++ b/client/cmdlf.c @@ -945,7 +945,7 @@ int CmdLFfind(const char *Cmd) PrintAndLog("\nValid EM4x05/EM4x69 Chip Found\nUse lf em 4x05readword/dump commands to read\n"); return 1; } - ans=CmdLFHitagReader("26"); + ans=CmdLFHitagReader("26"); // 26 = RHT2F_UID_ONLY if (ans==0) { return 1; } diff --git a/client/cmdlfem4x.c b/client/cmdlfem4x.c index f5dfee35..e6a25764 100644 --- a/client/cmdlfem4x.c +++ b/client/cmdlfem4x.c @@ -950,7 +950,6 @@ int EM4x05ReadWord_ext(uint8_t addr, uint32_t pwd, bool usePwd, uint32_t *wordDa } int testLen = (GraphTraceLen < 1000) ? GraphTraceLen : 1000; if (graphJustNoise(GraphBuffer, testLen)) { - PrintAndLog("no tag not found"); return -1; } //attempt demod: diff --git a/client/cmdlfhitag.c b/client/cmdlfhitag.c index 103f2b77..73c02a14 100644 --- a/client/cmdlfhitag.c +++ b/client/cmdlfhitag.c @@ -239,6 +239,7 @@ int CmdLFHitagReader(const char *Cmd) { c.arg[0] = htf; // Send the command to the proxmark + clearCommandBuffer(); SendCommand(&c); UsbCommand resp; From cb593491df72561bacc60ab246ef4ef94ecdb14b Mon Sep 17 00:00:00 2001 From: marshmellow42 Date: Fri, 10 Nov 2017 16:03:55 -0500 Subject: [PATCH 069/310] fix incorrect variable name that is what i get for going back over my code and trying to mimic the naming style used previously... --- armsrc/lfsampling.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/armsrc/lfsampling.c b/armsrc/lfsampling.c index 643b3168..999f56bb 100644 --- a/armsrc/lfsampling.c +++ b/armsrc/lfsampling.c @@ -334,7 +334,7 @@ uint32_t doCotagAcquisitionManchester() { uint16_t sample_counter = 0, period = 0; uint8_t curr = 0, prev = 0; uint16_t noise_counter = 0; - while (!BUTTON_PRESS() && !usb_poll_validate_length() && (sample_counter < bufsize) && (noiseCounter < (COTAG_T1<<1)) ) { + while (!BUTTON_PRESS() && !usb_poll_validate_length() && (sample_counter < bufsize) && (noise_counter < (COTAG_T1<<1)) ) { WDT_HIT(); if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { AT91C_BASE_SSC->SSC_THR = 0x43; From 33a9982c76489add2a4a49f4d16f7273895c526d Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Fri, 10 Nov 2017 23:09:08 +0100 Subject: [PATCH 070/310] dump only leaf TLV data --- client/emv/dump.h | 1 + client/emv/emvcore.c | 6 ++++-- client/emv/tlv.c | 3 ++- client/emv/tlv.h | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/client/emv/dump.h b/client/emv/dump.h index ad69ea83..567a134f 100644 --- a/client/emv/dump.h +++ b/client/emv/dump.h @@ -17,6 +17,7 @@ #define DUMP_H #include +#include void dump_buffer_simple(const unsigned char *ptr, size_t len, FILE *f); void dump_buffer(const unsigned char *ptr, size_t len, FILE *f, int level); diff --git a/client/emv/emvcore.c b/client/emv/emvcore.c index 8dc93259..63a69baa 100644 --- a/client/emv/emvcore.c +++ b/client/emv/emvcore.c @@ -10,9 +10,11 @@ #include "emvcore.h" -static bool print_cb(void *data, const struct tlv *tlv, int level) { +static bool print_cb(void *data, const struct tlv *tlv, int level, bool is_leaf) { emv_tag_dump(tlv, stdout, level); - dump_buffer(tlv->value, tlv->len, stdout, level); + if (is_leaf) { + dump_buffer(tlv->value, tlv->len, stdout, level); + } return true; } diff --git a/client/emv/tlv.c b/client/emv/tlv.c index 7feaa9aa..24125cc7 100644 --- a/client/emv/tlv.c +++ b/client/emv/tlv.c @@ -317,7 +317,8 @@ void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data, int level) for (; tlvdb; tlvdb = next) { next = tlvdb->next; - cb(data, &tlvdb->tag, level); + bool is_leaf = (tlvdb->children == NULL); + cb(data, &tlvdb->tag, level, is_leaf); tlvdb_visit(tlvdb->children, cb, data, level+1); } } diff --git a/client/emv/tlv.h b/client/emv/tlv.h index 187781f8..5a573566 100644 --- a/client/emv/tlv.h +++ b/client/emv/tlv.h @@ -31,7 +31,7 @@ struct tlv { }; struct tlvdb; -typedef bool (*tlv_cb)(void *data, const struct tlv *tlv, int level); +typedef bool (*tlv_cb)(void *data, const struct tlv *tlv, int level, bool is_leaf); struct tlvdb *tlvdb_fixed(tlv_tag_t tag, size_t len, const unsigned char *value); struct tlvdb *tlvdb_external(tlv_tag_t tag, size_t len, const unsigned char *value); From 77499f6fb24f42fdcd20e37659e80adf94641077 Mon Sep 17 00:00:00 2001 From: merlokk Date: Sat, 11 Nov 2017 00:43:29 +0200 Subject: [PATCH 071/310] fix #468 --- client/emv/apduinfo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/emv/apduinfo.c b/client/emv/apduinfo.c index fbdd714a..d1491cc3 100644 --- a/client/emv/apduinfo.c +++ b/client/emv/apduinfo.c @@ -278,7 +278,7 @@ int CodeCmp(const char *code1, const char *code2) { } const APDUCode* const GetAPDUCode(uint8_t sw1, uint8_t sw2) { - char buf[4] = {0}; + char buf[5] = {0}; int res; int mineq = 100; int mineqindx = 0; From 872337e019bc95bdd7f0c5978659c33c10113ab9 Mon Sep 17 00:00:00 2001 From: merlokk Date: Sat, 11 Nov 2017 01:19:34 +0200 Subject: [PATCH 072/310] change `ul_switch_off_field` to `DropField` fix #473 --- client/cmdhficlass.c | 24 ++++++++++++------------ client/cmdhfmfu.c | 36 ++++++++++++++++++------------------ client/cmdhfmfu.h | 1 - 3 files changed, 30 insertions(+), 31 deletions(-) diff --git a/client/cmdhficlass.c b/client/cmdhficlass.c index d02eff8d..63634cd4 100644 --- a/client/cmdhficlass.c +++ b/client/cmdhficlass.c @@ -683,7 +683,7 @@ int CmdHFiClassReader_Dump(const char *Cmd) { SendCommand(&c); if (!WaitForResponseTimeout(CMD_ACK, &resp, 4500)) { PrintAndLog("Command execute timeout"); - ul_switch_off_field(); + DropField(); return 0; } uint8_t readStatus = resp.arg[0] & 0xff; @@ -691,7 +691,7 @@ int CmdHFiClassReader_Dump(const char *Cmd) { if(readStatus == 0){ PrintAndLog("No tag found..."); - ul_switch_off_field(); + DropField(); return 0; } if( readStatus & (FLAG_ICLASS_READER_CSN|FLAG_ICLASS_READER_CONF|FLAG_ICLASS_READER_CC)){ @@ -702,12 +702,12 @@ int CmdHFiClassReader_Dump(const char *Cmd) { // large memory - not able to dump pages currently if (numblks > maxBlk) numblks = maxBlk; } - ul_switch_off_field(); + DropField(); // authenticate debit key and get div_key - later store in dump block 3 if (!select_and_auth(KEY, MAC, div_key, use_credit_key, elite, rawkey, false)){ //try twice - for some reason it sometimes fails the first time... if (!select_and_auth(KEY, MAC, div_key, use_credit_key, elite, rawkey, false)){ - ul_switch_off_field(); + DropField(); return 0; } } @@ -718,14 +718,14 @@ int CmdHFiClassReader_Dump(const char *Cmd) { SendCommand(&w); if (!WaitForResponseTimeout(CMD_ACK, &resp, 4500)) { PrintAndLog("Command execute time-out 1"); - ul_switch_off_field(); + DropField(); return 1; } uint32_t blocksRead = resp.arg[1]; uint8_t isOK = resp.arg[0] & 0xff; if (!isOK && !blocksRead) { PrintAndLog("Read Block Failed"); - ul_switch_off_field(); + DropField(); return 0; } uint32_t startindex = resp.arg[2]; @@ -741,13 +741,13 @@ int CmdHFiClassReader_Dump(const char *Cmd) { // try AA2 if (have_credit_key) { //turn off hf field before authenticating with different key - ul_switch_off_field(); + DropField(); memset(MAC,0,4); // AA2 authenticate credit key and git c_div_key - later store in dump block 4 if (!select_and_auth(CreditKEY, MAC, c_div_key, true, false, false, false)){ //try twice - for some reason it sometimes fails the first time... if (!select_and_auth(CreditKEY, MAC, c_div_key, true, false, false, false)){ - ul_switch_off_field(); + DropField(); return 0; } } @@ -760,14 +760,14 @@ int CmdHFiClassReader_Dump(const char *Cmd) { SendCommand(&w); if (!WaitForResponseTimeout(CMD_ACK, &resp, 4500)) { PrintAndLog("Command execute timeout 2"); - ul_switch_off_field(); + DropField(); return 0; } uint8_t isOK = resp.arg[0] & 0xff; blocksRead = resp.arg[1]; if (!isOK && !blocksRead) { PrintAndLog("Read Block Failed 2"); - ul_switch_off_field(); + DropField(); return 0; } @@ -782,7 +782,7 @@ int CmdHFiClassReader_Dump(const char *Cmd) { gotBytes += blocksRead*8; } else { //field is still on - turn it off... - ul_switch_off_field(); + DropField(); } } @@ -930,7 +930,7 @@ int CmdHFiClass_WriteBlock(const char *Cmd) { if (cmdp < 6) return usage_hf_iclass_writeblock(); int ans = WriteBlock(blockno, bldata, KEY, use_credit_key, elite, rawkey, true); - ul_switch_off_field(); + DropField(); return ans; } diff --git a/client/cmdhfmfu.c b/client/cmdhfmfu.c index c95fa20b..7dd344e8 100644 --- a/client/cmdhfmfu.c +++ b/client/cmdhfmfu.c @@ -153,7 +153,7 @@ static int ul_select( iso14a_card_select_t *card ){ ans = WaitForResponseTimeout(CMD_ACK, &resp, 1500); if (!ans || resp.arg[0] < 1) { PrintAndLog("iso14443a card select failed"); - ul_switch_off_field(); + DropField(); return 0; } @@ -226,7 +226,7 @@ static int ul_auth_select( iso14a_card_select_t *card, TagTypeUL_t tagtype, bool if (hasAuthKey) { if (ulev1_requestAuthentication(authenticationkey, pack, packSize) < 1) { - ul_switch_off_field(); + DropField(); PrintAndLog("Error: Authentication Failed UL-EV1/NTAG"); return 0; } @@ -550,7 +550,7 @@ static int ulc_magic_test(){ } else { returnValue = UL; } - ul_switch_off_field(); + DropField(); return returnValue; } */ @@ -563,7 +563,7 @@ static int ul_magic_test(){ if ( !ul_select(&card) ) return UL_ERROR; int status = ul_comp_write(0, NULL, 0); - ul_switch_off_field(); + DropField(); if ( status == 0 ) return MAGIC; return 0; @@ -582,14 +582,14 @@ uint32_t GetHF14AMfU_Type(void){ // Ultralight - ATQA / SAK if ( card.atqa[1] != 0x00 || card.atqa[0] != 0x44 || card.sak != 0x00 ) { PrintAndLog("Tag is not Ultralight | NTAG | MY-D [ATQA: %02X %02X SAK: %02X]\n", card.atqa[1], card.atqa[0], card.sak); - ul_switch_off_field(); + DropField(); return UL_ERROR; } if ( card.uid[0] != 0x05) { len = ulev1_getVersion(version, sizeof(version)); - ul_switch_off_field(); + DropField(); switch (len) { case 0x0A: { @@ -629,7 +629,7 @@ uint32_t GetHF14AMfU_Type(void){ // do UL_C check first... uint8_t nonce[11] = {0x00}; status = ulc_requestAuthentication(nonce, sizeof(nonce)); - ul_switch_off_field(); + DropField(); if (status > 1) { tagtype = UL_C; } else { @@ -650,15 +650,15 @@ uint32_t GetHF14AMfU_Type(void){ tagtype = UNKNOWN; } } - ul_switch_off_field(); + DropField(); } } if (tagtype & UL) { tagtype = ul_fudan_check(); - ul_switch_off_field(); + DropField(); } } else { - ul_switch_off_field(); + DropField(); // Infinition MY-D tests Exam high nibble uint8_t nib = (card.uid[1] & 0xf0) >> 4; switch ( nib ){ @@ -745,7 +745,7 @@ int CmdHF14AMfUInfo(const char *Cmd){ // read pages 0,1,2,3 (should read 4pages) status = ul_read(0, data, sizeof(data)); if ( status == -1 ) { - ul_switch_off_field(); + DropField(); PrintAndLog("Error: tag didn't answer to READ"); return status; } else if (status == 16) { @@ -763,7 +763,7 @@ int CmdHF14AMfUInfo(const char *Cmd){ status = ul_read(0x28, ulc_conf, sizeof(ulc_conf)); if ( status == -1 ){ PrintAndLog("Error: tag didn't answer to READ UL-C"); - ul_switch_off_field(); + DropField(); return status; } if (status == 16) ulc_print_configuration(ulc_conf); @@ -774,14 +774,14 @@ int CmdHF14AMfUInfo(const char *Cmd){ uint8_t ulc_deskey[16] = {0x00}; status = ul_read(0x2C, ulc_deskey, sizeof(ulc_deskey)); if ( status == -1 ) { - ul_switch_off_field(); + DropField(); PrintAndLog("Error: tag didn't answer to READ magic"); return status; } if (status == 16) ulc_print_3deskey(ulc_deskey); } else { - ul_switch_off_field(); + DropField(); // if we called info with key, just return if ( hasAuthKey ) return 1; @@ -816,7 +816,7 @@ int CmdHF14AMfUInfo(const char *Cmd){ status = ulev1_readSignature( ulev1_signature, sizeof(ulev1_signature)); if ( status == -1 ) { PrintAndLog("Error: tag didn't answer to READ SIGNATURE"); - ul_switch_off_field(); + DropField(); return status; } if (status == 32) ulev1_print_signature( ulev1_signature, sizeof(ulev1_signature)); @@ -831,7 +831,7 @@ int CmdHF14AMfUInfo(const char *Cmd){ status = ulev1_getVersion(version, sizeof(version)); if ( status == -1 ) { PrintAndLog("Error: tag didn't answer to GETVERSION"); - ul_switch_off_field(); + DropField(); return status; } else if (status == 10) { ulev1_print_version(version); @@ -851,7 +851,7 @@ int CmdHF14AMfUInfo(const char *Cmd){ status = ul_read(startconfigblock, ulev1_conf, sizeof(ulev1_conf)); if ( status == -1 ) { PrintAndLog("Error: tag didn't answer to READ EV1"); - ul_switch_off_field(); + DropField(); return status; } else if (status == 16) { // save AUTHENTICATION LIMITS for later: @@ -881,7 +881,7 @@ int CmdHF14AMfUInfo(const char *Cmd){ } } - ul_switch_off_field(); + DropField(); if (locked) PrintAndLog("\nTag appears to be locked, try using the key to get more info"); PrintAndLog(""); return 1; diff --git a/client/cmdhfmfu.h b/client/cmdhfmfu.h index a5e9f95c..43e71d96 100644 --- a/client/cmdhfmfu.h +++ b/client/cmdhfmfu.h @@ -16,7 +16,6 @@ int CmdHF14AMfUInfo(const char *Cmd); uint32_t GetHF14AMfU_Type(void); int ul_print_type(uint32_t tagtype, uint8_t spacer); -#define ul_switch_off_field DropField int usage_hf_mfu_dump(void); int usage_hf_mfu_info(void); From 298143218af2c123d27929638ff42c579f218c60 Mon Sep 17 00:00:00 2001 From: merlokk Date: Sat, 11 Nov 2017 01:28:20 +0200 Subject: [PATCH 073/310] true fix #472 ))) --- client/emv/apduinfo.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/emv/apduinfo.c b/client/emv/apduinfo.c index d1491cc3..ec7a64a1 100644 --- a/client/emv/apduinfo.c +++ b/client/emv/apduinfo.c @@ -283,8 +283,8 @@ const APDUCode* const GetAPDUCode(uint8_t sw1, uint8_t sw2) { int mineq = 100; int mineqindx = 0; - sprintf(&buf[0], "%02X ", sw1); - sprintf(&buf[2], "%02X ", sw2); + sprintf(&buf[0], "%02X", sw1); + sprintf(&buf[2], "%02X", sw2); for (int i = 0; i < APDUCodeTableLen; i++) { res = CodeCmp(APDUCodeTable[i].ID, buf); From a63505c9537d883e7657498ac341fe2fcb865855 Mon Sep 17 00:00:00 2001 From: merlokk Date: Sat, 11 Nov 2017 22:56:50 +0200 Subject: [PATCH 074/310] Added work with S-Block WTX in 14a apdu on arm side --- armsrc/iso14443a.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 8e2c56b0..3f49eedb 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1894,10 +1894,23 @@ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data) { if (!len) { return 0; //DATA LINK ERROR - - // if we received an I- or R(ACK)-Block with a block number equal to the - // current block number, toggle the current block number } else{ + // S-Block WTX + while((data_bytes[0] & 0xF2) == 0xF2) { + // Transmit WTX back + // byte1 - WTXM [1..59]. command FWT=FWT*WTXM + data_bytes[1] = data_bytes[1] & 0x3f; // 2 high bits mandatory set to 0b + // now need to fix CRC. + AppendCrc14443a(data_bytes, len - 2); + // transmit S-Block + ReaderTransmit(data_bytes, len, NULL); + // retrieve the result again + len = ReaderReceive(data, parity); + data_bytes = data; + } + + // if we received an I- or R(ACK)-Block with a block number equal to the + // current block number, toggle the current block number if (len >= 3 // PCB+CRC = 3 bytes && ((data_bytes[0] & 0xC0) == 0 // I-Block || (data_bytes[0] & 0xD0) == 0x80) // R-Block with ACK bit set to 0 From ba4f95b4af2385f3ae72284133fc1db4cf591f05 Mon Sep 17 00:00:00 2001 From: merlokk Date: Sat, 11 Nov 2017 23:05:08 +0200 Subject: [PATCH 075/310] added some info from @peterfillmore fork --- armsrc/iso14443a.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 3f49eedb..ebbddbd8 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1875,7 +1875,30 @@ void iso14443a_setup(uint8_t fpga_minor_mode) { iso14a_set_timeout(1060); // 10ms default } - +/* Peter Fillmore 2015 +Added card id field to the function + info from ISO14443A standard +b1 = Block Number +b2 = RFU (always 1) +b3 = depends on block +b4 = Card ID following if set to 1 +b5 = depends on block type +b6 = depends on block type +b7,b8 = block type. +Coding of I-BLOCK: +b8 b7 b6 b5 b4 b3 b2 b1 +0 0 0 x x x 1 x +b5 = chaining bit +Coding of R-block: +b8 b7 b6 b5 b4 b3 b2 b1 +1 0 1 x x 0 1 x +b5 = ACK/NACK +Coding of S-block: +b8 b7 b6 b5 b4 b3 b2 b1 +1 1 x x x 0 1 0 +b5,b6 = 00 - DESELECT + 11 - WTX +*/ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data) { uint8_t parity[MAX_PARITY_SIZE]; uint8_t real_cmd[cmd_len + 4]; From 874572d4197e2a02152ea8c2185df90531b576c3 Mon Sep 17 00:00:00 2001 From: "William S. Moses" Date: Sat, 11 Nov 2017 16:08:57 -0500 Subject: [PATCH 076/310] Fix memory bounds error --- client/cmdcrc.c | 6 +++--- client/cmdhf.c | 2 +- client/cmdhficlass.c | 36 ++++++++++++++++++------------------ client/cmdhfmf.c | 16 ++++++++-------- client/cmdhfmfu.c | 6 +++--- client/cmdlf.c | 14 +++++++------- client/cmdlfem4x.c | 2 +- client/cmdlfpresco.c | 2 +- client/cmdlft55xx.c | 2 +- client/util.c | 17 ++++++++++++++--- client/util.h | 2 +- 11 files changed, 58 insertions(+), 47 deletions(-) diff --git a/client/cmdcrc.c b/client/cmdcrc.c index 01f65f55..27d081b9 100644 --- a/client/cmdcrc.c +++ b/client/cmdcrc.c @@ -434,9 +434,9 @@ int CmdrevengTestC(const char *Cmd){ char result[30]; int dataLen; char endian = 0; - dataLen = param_getstr(Cmd, cmdp++, inModel); + dataLen = param_getstr(Cmd, cmdp++, inModel, sizeof(inModel)); if (dataLen < 4) return 0; - dataLen = param_getstr(Cmd, cmdp++, inHexStr); + dataLen = param_getstr(Cmd, cmdp++, inHexStr, sizeof(inHexStr)); if (dataLen < 4) return 0; bool reverse = (param_get8(Cmd, cmdp++)) ? true : false; endian = param_getchar(Cmd, cmdp++); @@ -464,7 +464,7 @@ char *SwapEndianStr(const char *inStr, const size_t len, const uint8_t blockSize // takes hex string in and searches for a matching result (hex string must include checksum) int CmdrevengSearch(const char *Cmd){ char inHexStr[50] = {0x00}; - int dataLen = param_getstr(Cmd, 0, inHexStr); + int dataLen = param_getstr(Cmd, 0, inHexStr, sizeof(inHexStr)); if (dataLen < 4) return 0; char *Models[80]; diff --git a/client/cmdhf.c b/client/cmdhf.c index 168296ba..453635b7 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -562,7 +562,7 @@ int CmdHFList(const char *Cmd) bool showWaitCycles = false; bool markCRCBytes = false; char type[40] = {0}; - int tlen = param_getstr(Cmd,0,type); + int tlen = param_getstr(Cmd,0,type, sizeof(type)); char param1 = param_getchar(Cmd, 1); char param2 = param_getchar(Cmd, 2); bool errors = false; diff --git a/client/cmdhficlass.c b/client/cmdhficlass.c index 63634cd4..60713a01 100644 --- a/client/cmdhficlass.c +++ b/client/cmdhficlass.c @@ -278,7 +278,7 @@ int CmdHFiClassELoad(const char *Cmd) { //File handling and reading FILE *f; char filename[FILE_PATH_SIZE]; - if(opt == 'f' && param_getstr(Cmd, 1, filename) > 0) + if(opt == 'f' && param_getstr(Cmd, 1, filename, sizeof(filename)) > 0) { f = fopen(filename, "rb"); }else{ @@ -384,7 +384,7 @@ int CmdHFiClassDecrypt(const char *Cmd) { //Open the tagdump-file FILE *f; char filename[FILE_PATH_SIZE]; - if(opt == 'f' && param_getstr(Cmd, 1, filename) > 0) { + if(opt == 'f' && param_getstr(Cmd, 1, filename, sizeof(filename)) > 0) { f = fopen(filename, "rb"); if ( f == NULL ) { PrintAndLog("Could not find file %s", filename); @@ -605,7 +605,7 @@ int CmdHFiClassReader_Dump(const char *Cmd) { case 'c': case 'C': have_credit_key = true; - dataLen = param_getstr(Cmd, cmdp+1, tempStr); + dataLen = param_getstr(Cmd, cmdp+1, tempStr, sizeof(tempStr)); if (dataLen == 16) { errors = param_gethex(tempStr, 0, CreditKEY, dataLen); } else if (dataLen == 1) { @@ -629,7 +629,7 @@ int CmdHFiClassReader_Dump(const char *Cmd) { break; case 'f': case 'F': - fileNameLen = param_getstr(Cmd, cmdp+1, filename); + fileNameLen = param_getstr(Cmd, cmdp+1, filename, sizeof(filename)); if (fileNameLen < 1) { PrintAndLog("No filename found after f"); errors = true; @@ -639,7 +639,7 @@ int CmdHFiClassReader_Dump(const char *Cmd) { case 'k': case 'K': have_debit_key = true; - dataLen = param_getstr(Cmd, cmdp+1, tempStr); + dataLen = param_getstr(Cmd, cmdp+1, tempStr, sizeof(tempStr)); if (dataLen == 16) { errors = param_gethex(tempStr, 0, KEY, dataLen); } else if (dataLen == 1) { @@ -898,7 +898,7 @@ int CmdHFiClass_WriteBlock(const char *Cmd) { break; case 'k': case 'K': - dataLen = param_getstr(Cmd, cmdp+1, tempStr); + dataLen = param_getstr(Cmd, cmdp+1, tempStr, sizeof(tempStr)); if (dataLen == 16) { errors = param_gethex(tempStr, 0, KEY, dataLen); } else if (dataLen == 1) { @@ -992,7 +992,7 @@ int CmdHFiClassCloneTag(const char *Cmd) { break; case 'f': case 'F': - fileNameLen = param_getstr(Cmd, cmdp+1, filename); + fileNameLen = param_getstr(Cmd, cmdp+1, filename, sizeof(filename)); if (fileNameLen < 1) { PrintAndLog("No filename found after f"); errors = true; @@ -1001,7 +1001,7 @@ int CmdHFiClassCloneTag(const char *Cmd) { break; case 'k': case 'K': - dataLen = param_getstr(Cmd, cmdp+1, tempStr); + dataLen = param_getstr(Cmd, cmdp+1, tempStr, sizeof(tempStr)); if (dataLen == 16) { errors = param_gethex(tempStr, 0, KEY, dataLen); } else if (dataLen == 1) { @@ -1196,7 +1196,7 @@ int CmdHFiClass_ReadBlock(const char *Cmd) { case 'k': case 'K': auth = true; - dataLen = param_getstr(Cmd, cmdp+1, tempStr); + dataLen = param_getstr(Cmd, cmdp+1, tempStr, sizeof(tempStr)); if (dataLen == 16) { errors = param_gethex(tempStr, 0, KEY, dataLen); } else if (dataLen == 1) { @@ -1253,7 +1253,7 @@ int CmdHFiClass_loclass(const char *Cmd) { char fileName[255] = {0}; if(opt == 'f') { - if(param_getstr(Cmd, 1, fileName) > 0) + if(param_getstr(Cmd, 1, fileName, sizeof(fileName)) > 0) { return bruteforceFileNoKeys(fileName); }else @@ -1318,14 +1318,14 @@ int CmdHFiClassReadTagFile(const char *Cmd) { char tempnum[5]; FILE *f; char filename[FILE_PATH_SIZE]; - if (param_getstr(Cmd, 0, filename) < 1) + if (param_getstr(Cmd, 0, filename, sizeof(filename)) < 1) return usage_hf_iclass_readtagfile(); - if (param_getstr(Cmd,1,(char *)&tempnum) < 1) + if (param_getstr(Cmd, 1, tempnum, sizeof(tempnum)) < 1) startblock = 0; else sscanf(tempnum,"%d",&startblock); - if (param_getstr(Cmd,2,(char *)&tempnum) < 1) + if (param_getstr(Cmd,2, tempnum, sizeof(tempnum)) < 1) endblock = 0; else sscanf(tempnum,"%d",&endblock); @@ -1458,7 +1458,7 @@ int CmdHFiClassCalcNewKey(const char *Cmd) { return usage_hf_iclass_calc_newkey(); case 'e': case 'E': - dataLen = param_getstr(Cmd, cmdp, tempStr); + dataLen = param_getstr(Cmd, cmdp, tempStr, sizeof(tempStr)); if (dataLen==2) oldElite = true; elite = true; @@ -1466,7 +1466,7 @@ int CmdHFiClassCalcNewKey(const char *Cmd) { break; case 'n': case 'N': - dataLen = param_getstr(Cmd, cmdp+1, tempStr); + dataLen = param_getstr(Cmd, cmdp+1, tempStr, sizeof(tempStr)); if (dataLen == 16) { errors = param_gethex(tempStr, 0, NEWKEY, dataLen); } else if (dataLen == 1) { @@ -1485,7 +1485,7 @@ int CmdHFiClassCalcNewKey(const char *Cmd) { break; case 'o': case 'O': - dataLen = param_getstr(Cmd, cmdp+1, tempStr); + dataLen = param_getstr(Cmd, cmdp+1, tempStr, sizeof(tempStr)); if (dataLen == 16) { errors = param_gethex(tempStr, 0, OLDKEY, dataLen); } else if (dataLen == 1) { @@ -1626,7 +1626,7 @@ int CmdHFiClassManageKeys(const char *Cmd) { return usage_hf_iclass_managekeys(); case 'f': case 'F': - fileNameLen = param_getstr(Cmd, cmdp+1, filename); + fileNameLen = param_getstr(Cmd, cmdp+1, filename, sizeof(filename)); if (fileNameLen < 1) { PrintAndLog("No filename found after f"); errors = true; @@ -1645,7 +1645,7 @@ int CmdHFiClassManageKeys(const char *Cmd) { case 'k': case 'K': operation += 3; //set key - dataLen = param_getstr(Cmd, cmdp+1, tempStr); + dataLen = param_getstr(Cmd, cmdp+1, tempStr, sizeof(tempStr)); if (dataLen == 16) { //ul-c or ev1/ntag key length errors = param_gethex(tempStr, 0, KEY, dataLen); } else { diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 553803c1..83060b01 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -533,7 +533,7 @@ static void parseParamTDS(const char *Cmd, const uint8_t indx, bool *paramT, boo char ctmp3[3] = {0}; int len = param_getlength(Cmd, indx); if (len > 0 && len < 4){ - param_getstr(Cmd, indx, ctmp3); + param_getstr(Cmd, indx, ctmp3, sizeof(ctmp3)); *paramT |= (ctmp3[0] == 't' || ctmp3[0] == 'T'); *paramD |= (ctmp3[0] == 'd' || ctmp3[0] == 'D'); @@ -1043,7 +1043,7 @@ int CmdHF14AMfChk(const char *Cmd) // double parameters - ts, ds int clen = param_getlength(Cmd, 2); if (clen == 2 || clen == 3){ - param_getstr(Cmd, 2, ctmp3); + param_getstr(Cmd, 2, ctmp3, sizeof(ctmp3)); ctmp = ctmp3[1]; } //parse @@ -1075,7 +1075,7 @@ int CmdHF14AMfChk(const char *Cmd) keycnt++; } else { // May be a dic file - if ( param_getstr(Cmd, 2 + i,filename) >= FILE_PATH_SIZE ) { + if ( param_getstr(Cmd, 2 + i, filename, sizeof(filename)) >= FILE_PATH_SIZE ) { PrintAndLog("File name too long"); free(keyBlock); return 2; @@ -1398,7 +1398,7 @@ int CmdHF14AMf1kSim(const char *Cmd) { break; case 'f': case 'F': - len = param_getstr(Cmd, cmdp+1, filename); + len = param_getstr(Cmd, cmdp+1, filename, sizeof(filename)); if (len < 1) { PrintAndLog("error no filename found"); return 0; @@ -1674,7 +1674,7 @@ int CmdHF14AMfELoad(const char *Cmd) } } - len = param_getstr(Cmd,nameParamNo,filename); + len = param_getstr(Cmd,nameParamNo,filename,sizeof(filename)); if (len > FILE_PATH_SIZE - 5) len = FILE_PATH_SIZE - 5; @@ -1773,7 +1773,7 @@ int CmdHF14AMfESave(const char *Cmd) } } - len = param_getstr(Cmd,nameParamNo,filename); + len = param_getstr(Cmd,nameParamNo,filename,sizeof(filename)); if (len > FILE_PATH_SIZE - 5) len = FILE_PATH_SIZE - 5; @@ -2137,7 +2137,7 @@ int CmdHF14AMfCLoad(const char *Cmd) } return 0; } else { - param_getstr(Cmd, 0, filename); + param_getstr(Cmd, 0, filename, sizeof(filename)); len = strlen(filename); if (len > FILE_PATH_SIZE - 5) len = FILE_PATH_SIZE - 5; @@ -2348,7 +2348,7 @@ int CmdHF14AMfCSave(const char *Cmd) { } return 0; } else { - param_getstr(Cmd, 0, filename); + param_getstr(Cmd, 0, filename, sizeof(filename)); len = strlen(filename); if (len > FILE_PATH_SIZE - 5) len = FILE_PATH_SIZE - 5; diff --git a/client/cmdhfmfu.c b/client/cmdhfmfu.c index 7dd344e8..63c41728 100644 --- a/client/cmdhfmfu.c +++ b/client/cmdhfmfu.c @@ -703,7 +703,7 @@ int CmdHF14AMfUInfo(const char *Cmd){ return usage_hf_mfu_info(); case 'k': case 'K': - dataLen = param_getstr(Cmd, cmdp+1, tempStr); + dataLen = param_getstr(Cmd, cmdp+1, tempStr, sizeof(tempStr)); if (dataLen == 32 || dataLen == 8) { //ul-c or ev1/ntag key length errors = param_gethex(tempStr, 0, authenticationkey, dataLen); dataLen /= 2; // handled as bytes from now on @@ -1238,7 +1238,7 @@ int CmdHF14AMfUDump(const char *Cmd){ return usage_hf_mfu_dump(); case 'k': case 'K': - dataLen = param_getstr(Cmd, cmdp+1, tempStr); + dataLen = param_getstr(Cmd, cmdp+1, tempStr, sizeof(tempStr)); if (dataLen == 32 || dataLen == 8) { //ul-c or ev1/ntag key length errors = param_gethex(tempStr, 0, authenticationkey, dataLen); dataLen /= 2; @@ -1256,7 +1256,7 @@ int CmdHF14AMfUDump(const char *Cmd){ break; case 'n': case 'N': - fileNlen = param_getstr(Cmd, cmdp+1, filename); + fileNlen = param_getstr(Cmd, cmdp+1, filename, sizeof(filename)); if (!fileNlen) errors = true; if (fileNlen > FILE_PATH_SIZE-5) fileNlen = FILE_PATH_SIZE-5; cmdp += 2; diff --git a/client/cmdlf.c b/client/cmdlf.c index cdfbee2d..ef9c3cbb 100644 --- a/client/cmdlf.c +++ b/client/cmdlf.c @@ -93,7 +93,7 @@ int CmdLFCommandRead(const char *Cmd) cmdp++; break; case 'c': - param_getstr(Cmd, cmdp+1, (char *)&c.d.asBytes); + param_getstr(Cmd, cmdp+1, (char *)&c.d.asBytes, sizeof(c.d.asBytes)); cmdp+=2; break; case 'd': @@ -491,7 +491,7 @@ int CmdLFfskSim(const char *Cmd) uint8_t fcHigh=0, fcLow=0, clk=0; uint8_t invert=0; bool errors = false; - char hexData[32] = {0x00}; // store entered hex data + char hexData[64] = {0x00}; // store entered hex data uint8_t data[255] = {0x00}; int dataLen = 0; uint8_t cmdp = 0; @@ -522,7 +522,7 @@ int CmdLFfskSim(const char *Cmd) // cmdp++; // break; case 'd': - dataLen = param_getstr(Cmd, cmdp+1, hexData); + dataLen = param_getstr(Cmd, cmdp+1, hexData, sizeof(hexData)); if (dataLen==0) { errors=true; } else { @@ -593,7 +593,7 @@ int CmdLFaskSim(const char *Cmd) uint8_t encoding = 1, separator = 0; uint8_t clk=0, invert=0; bool errors = false; - char hexData[32] = {0x00}; + char hexData[64] = {0x00}; uint8_t data[255]= {0x00}; // store entered hex data int dataLen = 0; uint8_t cmdp = 0; @@ -628,7 +628,7 @@ int CmdLFaskSim(const char *Cmd) cmdp++; break; case 'd': - dataLen = param_getstr(Cmd, cmdp+1, hexData); + dataLen = param_getstr(Cmd, cmdp+1, hexData, sizeof(hexData)); if (dataLen==0) { errors=true; } else { @@ -687,7 +687,7 @@ int CmdLFpskSim(const char *Cmd) uint8_t carrier=0, clk=0; uint8_t invert=0; bool errors = false; - char hexData[32] = {0x00}; // store entered hex data + char hexData[64] = {0x00}; // store entered hex data uint8_t data[255] = {0x00}; int dataLen = 0; uint8_t cmdp = 0; @@ -723,7 +723,7 @@ int CmdLFpskSim(const char *Cmd) cmdp++; break; case 'd': - dataLen = param_getstr(Cmd, cmdp+1, hexData); + dataLen = param_getstr(Cmd, cmdp+1, hexData, sizeof(hexData)); if (dataLen==0) { errors=true; } else { diff --git a/client/cmdlfem4x.c b/client/cmdlfem4x.c index e6a25764..6d562be0 100644 --- a/client/cmdlfem4x.c +++ b/client/cmdlfem4x.c @@ -335,7 +335,7 @@ int CmdEM410xBrute(const char *Cmd) delay = param_get32ex(Cmd, 4, 1000, 10); } - param_getstr(Cmd, 0, filename); + param_getstr(Cmd, 0, filename, sizeof(filename)); uidBlock = calloc(stUidBlock, 5); if (uidBlock == NULL) return 1; diff --git a/client/cmdlfpresco.c b/client/cmdlfpresco.c index 29fc81f2..2f4bacfe 100644 --- a/client/cmdlfpresco.c +++ b/client/cmdlfpresco.c @@ -71,7 +71,7 @@ int GetWiegandFromPresco(const char *Cmd, uint32_t *sitecode, uint32_t *usercode case 'D': case 'd': //param get string int param_getstr(const char *line, int paramnum, char * str) - stringlen = param_getstr(Cmd, cmdp+1, id); + stringlen = param_getstr(Cmd, cmdp+1, id, sizeof(id)); if (stringlen < 2) return -1; cmdp+=2; break; diff --git a/client/cmdlft55xx.c b/client/cmdlft55xx.c index 252aaaa2..92a00bce 100644 --- a/client/cmdlft55xx.c +++ b/client/cmdlft55xx.c @@ -236,7 +236,7 @@ int CmdT55xxSetConfig(const char *Cmd) { cmdp+=2; break; case 'd': - param_getstr(Cmd, cmdp+1, modulation); + param_getstr(Cmd, cmdp+1, modulation, sizeof(modulation)); cmdp += 2; if ( strcmp(modulation, "FSK" ) == 0) { diff --git a/client/util.c b/client/util.c index 9dab4655..de62ac79 100644 --- a/client/util.c +++ b/client/util.c @@ -531,11 +531,19 @@ int param_gethex_to_eol(const char *line, int paramnum, uint8_t * data, int maxd return 0; } -int param_getstr(const char *line, int paramnum, char * str) +int param_getstr(const char *line, int paramnum, char * str, size_t buffersize) { int bg, en; - if (param_getptr(line, &bg, &en, paramnum)) return 0; + if (param_getptr(line, &bg, &en, paramnum)) { + return 0; + } + + // Prevent out of bounds errors + if (en - bg + 1 >= buffersize) { + printf("out of bounds error: want %lu bytes have %lu bytes\n", en - bg + 1 + 1, buffersize); + return 0; + } memcpy(str, line + bg, en - bg + 1); str[en - bg + 1] = 0; @@ -553,6 +561,7 @@ https://github.com/ApertureLabsLtd/RFIDler/blob/master/firmware/Pic32/RFIDler.X/ int hextobinarray(char *target, char *source) { int length, i, count= 0; + char* start = source; char x; length = strlen(source); @@ -568,8 +577,10 @@ int hextobinarray(char *target, char *source) x -= '0'; else if (x >= 'A' && x <= 'F') x -= 'A' - 10; - else + else { + printf("Discovered unknown character %c %d at idx %d of %s\n", x, x, source - start, start); return 0; + } // output for(i= 0 ; i < 4 ; ++i, ++count) *(target++)= (x >> (3 - i)) & 1; diff --git a/client/util.h b/client/util.h index f42625b1..8bab8cfd 100644 --- a/client/util.h +++ b/client/util.h @@ -64,7 +64,7 @@ extern uint8_t param_isdec(const char *line, int paramnum); extern int param_gethex(const char *line, int paramnum, uint8_t * data, int hexcnt); extern int param_gethex_ex(const char *line, int paramnum, uint8_t * data, int *hexcnt); extern int param_gethex_to_eol(const char *line, int paramnum, uint8_t * data, int maxdatalen, int *datalen); -extern int param_getstr(const char *line, int paramnum, char * str); +extern int param_getstr(const char *line, int paramnum, char * str, size_t buffersize); extern int hextobinarray( char *target, char *source); extern int hextobinstring( char *target, char *source); From e537c3e894d12546b97eb61b572a97f6070e686c Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Sun, 12 Nov 2017 18:08:57 +0100 Subject: [PATCH 077/310] fix memory overflow in hf mf nested (issue #479) --- client/cmdhfmf.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 83060b01..eed6b34c 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -527,7 +527,6 @@ int CmdHF14AMfRestore(const char *Cmd) //---------------------------------------------- // Nested //---------------------------------------------- -# define NESTED_KEY_COUNT 15 static void parseParamTDS(const char *Cmd, const uint8_t indx, bool *paramT, bool *paramD, uint8_t *timeout) { char ctmp3[3] = {0}; @@ -563,7 +562,7 @@ int CmdHF14AMfNested(const char *Cmd) uint8_t trgKeyType = 0; uint8_t SectorsCnt = 0; uint8_t key[6] = {0, 0, 0, 0, 0, 0}; - uint8_t keyBlock[NESTED_KEY_COUNT * 6]; + uint8_t keyBlock[MifareDefaultKeysSize * 6]; uint64_t key64 = 0; // timeout in units. (ms * 106)/10 or us*0.0106 uint8_t btimeout14a = MF_CHKKEYS_DEFTIMEOUT; // fast by default @@ -713,7 +712,7 @@ int CmdHF14AMfNested(const char *Cmd) } PrintAndLog("Testing known keys. Sector count=%d", SectorsCnt); - mfCheckKeysSec(SectorsCnt, 2, btimeout14a, true, NESTED_KEY_COUNT, keyBlock, e_sector); + mfCheckKeysSec(SectorsCnt, 2, btimeout14a, true, MifareDefaultKeysSize, keyBlock, e_sector); // get known key from array bool keyFound = false; From db68bcdb06618c97a45a39ff5ed6acf666da62e2 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Wed, 15 Nov 2017 07:47:18 +0100 Subject: [PATCH 078/310] Complete WTX handling --- armsrc/iso14443a.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index ebbddbd8..89ef23d4 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -81,6 +81,8 @@ typedef struct { } tUart; static uint32_t iso14a_timeout; +#define MAX_ISO14A_TIMEOUT 524288 + int rsamples = 0; uint8_t trigger = 0; // the block number for the ISO14443-4 PCB @@ -1920,6 +1922,9 @@ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data) { } else{ // S-Block WTX while((data_bytes[0] & 0xF2) == 0xF2) { + uint32_t save_iso14a_timeout = iso14a_timeout; + // temporarily increase timeout + iso14a_timeout = MAX((data_bytes[1] & 0x3f) * iso14a_timeout, MAX_ISO14A_TIMEOUT); // Transmit WTX back // byte1 - WTXM [1..59]. command FWT=FWT*WTXM data_bytes[1] = data_bytes[1] & 0x3f; // 2 high bits mandatory set to 0b @@ -1927,9 +1932,11 @@ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data) { AppendCrc14443a(data_bytes, len - 2); // transmit S-Block ReaderTransmit(data_bytes, len, NULL); - // retrieve the result again + // retrieve the result again (with increased timeout) len = ReaderReceive(data, parity); data_bytes = data; + // restore timeout + iso14a_timeout = save_iso14a_timeout; } // if we received an I- or R(ACK)-Block with a block number equal to the From c95affa8d3d84f0bd5db7821f65c22e13870d4e2 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Wed, 15 Nov 2017 08:49:18 +0200 Subject: [PATCH 079/310] minor fixes * fix timeout set in PR #481 * fix warning in util.c printf --- client/cmdhf14a.c | 4 ++-- client/util.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 4b3975b3..50071464 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -660,8 +660,8 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea // "Command APDU" length should be 5+255+1, but javacard's APDU buffer might be smaller - 133 bytes // https://stackoverflow.com/questions/32994936/safe-max-java-card-apdu-data-command-and-respond-size // here length USB_CMD_DATA_SIZE=512 - // timeout timeout14a * 1.06 / 100, true, size, &keyBlock[6 * c], e_sector); // timeout is (ms * 106)/10 or us*0.0106 - UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_APDU | ISO14A_SET_TIMEOUT | cmdc, (datainlen & 0xFFFF), 1000 * 1000 * 1.06 / 100}}; + // timeout must be authomatically set by "get ATS" + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_APDU | cmdc, (datainlen & 0xFFFF), 0}}; memcpy(c.d.asBytes, datain, datainlen); SendCommand(&c); diff --git a/client/util.c b/client/util.c index de62ac79..a1caafdb 100644 --- a/client/util.c +++ b/client/util.c @@ -541,7 +541,7 @@ int param_getstr(const char *line, int paramnum, char * str, size_t buffersize) // Prevent out of bounds errors if (en - bg + 1 >= buffersize) { - printf("out of bounds error: want %lu bytes have %lu bytes\n", en - bg + 1 + 1, buffersize); + printf("out of bounds error: want %d bytes have %zd bytes\n", en - bg + 1 + 1, buffersize); return 0; } From 0363be935895ba8d746703fb57c74856b7a2c3d4 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Fri, 17 Nov 2017 09:13:37 +0100 Subject: [PATCH 080/310] fix: USB Power requirements * correctly indicate the maximum current drawn from the USB bus --- common/usb_cdc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/usb_cdc.c b/common/usb_cdc.c index 91a63cf3..3d17f81c 100644 --- a/common/usb_cdc.c +++ b/common/usb_cdc.c @@ -77,8 +77,8 @@ static const char cfgDescriptor[] = { 0x02, // CbNumInterfaces 0x01, // CbConfigurationValue 0x00, // CiConfiguration - 0xC0, // CbmAttributes 0xA0 - 0xFA, // CMaxPower + 0xC0, // CbmAttributes (Self Powered - for those with a battery) + 0x4B, // CMaxPower (150mA max current drawn from bus without battery) /* Interface 0 Descriptor: Communication Class Interface */ 0x09, // bLength From dfa1628ac35efc08e4ec5ac359adbf214632592b Mon Sep 17 00:00:00 2001 From: Kenzy Carey <33557606+KenzyCarey@users.noreply.github.com> Date: Fri, 17 Nov 2017 14:42:52 -0800 Subject: [PATCH 081/310] Add files via upload --- client/scripts/brutesim.lua | 229 ++++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 client/scripts/brutesim.lua diff --git a/client/scripts/brutesim.lua b/client/scripts/brutesim.lua new file mode 100644 index 00000000..326966c6 --- /dev/null +++ b/client/scripts/brutesim.lua @@ -0,0 +1,229 @@ +desc = [[ + + .-----------------------------------------------------------------. + / .-. .-. \ +| / \ BruteSim / \ | +| |\_. | (bruteforce simulation for multiple tags) | /| | +|\| | /| by |\ | |/| +| `---' | Kenzy Carey | `---' | +| | | | +| |-----------------------------------------------------| | +\ | | / + \ / \ / + `---' `---' +]] +author = [[ Kenzy Carey ]] +usage = [[ + +USAGE: +script run brutesim -r rfid_tag -f facility_code -b base_card_number -c count -t timeout -d direction +option argument description +------ -------- ----------- +-r *see below RFID Tag: the RFID tag to emulate +-f 0-999 Facility Code: The facility code (dfx: country id, 14a: type) +-b 0-65535 Base Card Number: base card number to start from +-c 1-65536 Count: number of cards to try +-t .0-99999, pause Timeout: timeout between cards (use the word 'pause' to wait for user input) +-d up, down Direction: direction to move through card numbers +-h Show this + +*SUPPORTED TAGS: pyramid, awid, fdx, jablotron, noralsy, presco, visa2000, 14a, hid + +EXAMPLE: +script run brutesim -r pyramid -f 10 -b 1000 -c 10 -t 1 -d down +(the above example would bruteforce pyramid tags, starting at 10:1000, ending at 10:991, and waiting 1 second between each card) +]] + +-- I wrote this as i was doing a PACS audit. This is far from complete, but is easily expandable. +-- The idea was based on proxbrute, but i needed more options, and support for different readers. +-- I dont know LUA, so I used Brian Redbeards lf_bulk_program.lua script as a starting point, sorry if its kludgy. + +getopt = require('getopt') -- Used to get get command line arguments +bit32 = require('bit32') -- Used to convert FC/CN to hex + +local function isempty(s) -- Check if a string is empty + return s == nil or s == '' +end + +local function main(args) + + print("") -- Print a blank line to make things look cleaner + + for o, a in getopt.getopt(args, 'r:f:b:c:t:d:h') do -- Populate command like arguments + if o == 'r' then rfidtag = a end + if o == 'f' then facility = a end + if o == 'b' then baseid = a end + if o == 'c' then count = a end + if o == 't' then timeout = a end + if o == 'd' then direction = a end + if o == 'h' then return print(usage) end + end + + if isempty(rfidtag) then -- Check to see if -r argument was passed + print("You must supply the flag -r (rfid tag)") + print(usage) + return + end + -- Check what RFID Tag we are using + if rfidtag == 'pyramid' then -- For eaach RFID Tag: + consolecommand = 'lf pyramid sim' -- Set the console command + rfidtagname = 'Farpointe/Pyramid' -- Set the display name + facilityrequired = 1 -- Set if FC is required + elseif rfidtag == 'awid' then + consolecommand = 'lf awid sim' + rfidtagname = 'AWID' + facilityrequired = 1 + elseif rfidtag == 'fdx' then -- I'm not sure why you would need to bruteforce this ¯\_(ツ)_/¯ + consolecommand = 'lf fdx sim' + rfidtagname = 'FDX-B' + facilityrequired = 1 + elseif rfidtag == 'jablotron' then + consolecommand = 'lf jablotron sim' + rfidtagname = 'Jablotron' + facilityrequired = 0 + elseif rfidtag == 'noralsy' then + consolecommand = 'lf noralsy sim' + rfidtagname = 'Noralsy' + facilityrequired = 0 + elseif rfidtag == 'presco' then + consolecommand = 'lf presco sim d' + rfidtagname = 'Presco' + facilityrequired = 0 + elseif rfidtag == 'visa2000' then + consolecommand = 'lf visa2000 sim' + rfidtagname = 'Visa2000' + facilityrequired = 0 + elseif rfidtag == '14a' then + consolecommand = 'hf 14a sim' + if facility == "1" then rfidtagname = 'MIFARE Classic' -- Here we use the -f option to read the 14a type instead of the facility code + elseif facility == "2" then rfidtagname = 'MIFARE Ultralight' + elseif facility == "3" then rfidtagname = 'MIFARE Desfire' + elseif facility == "4" then rfidtagname = 'ISO/IEC 14443-4' + elseif facility == "5" then rfidtagname = 'MIFARE Tnp3xxx' + else + print("Invalid 14a type (-f) supplied. Must be 1-5") + print(usage) + return + end + facilityrequired = 0 -- Disable the FC required check, as we used it for type instead of FC + elseif rfidtag == 'hid' then + consolecommand = 'lf hid sim' + rfidtagname = 'HID' + facilityrequired = 1 + else -- Display error and exit out if bad RFID tag was supplied + print("Invalid rfid tag (-r) supplied") + print(usage) + return + end + + if isempty(baseid) then -- Display error and exit out if no starting id is set + print("You must supply the flag -b (base id)") + print(usage) + return + end + + if isempty(count) then -- Display error and exit out of no count is set + print("You must supply the flag -c (count)") + print(usage) + return + end + + if facilityrequired == 1 then -- If FC is required + facilitymessage = " - Facility Code: " -- Add FC to status message + if isempty(facility) then -- If FC was left blank, display warning and set FC to 0 + print("Using 0 for the facility code as -f was not supplied") + facility = 0 + end + else -- If FC is not required + facility = "" -- Clear FC + facilitymessage = "" -- Remove FC from status message + end + + if isempty(timeout) then -- If timeout was not supplied, show warning and set timeout to 0 + print("Using 0 for the timeout as -t was not supplied") + timeout = 0 + end + + if isempty(direction) then -- If direction was not supplied, show warning and set direction to down + print("Using down for direction as -d was not supplied") + direction = 'down' + end + + if tonumber(count) < 1 then + print("Count -c must be set to 1 or higher") + return + else + count = count -1 -- Make our count accurate by removing 1, because math + end + + if direction == 'down' then -- If counting down, set up our for loop to count down + endid = baseid - count + fordirection = -1 + elseif direction == 'up' then -- If counting up, set our for loop to count up + endid = baseid + count + fordirection = 1 + else -- If invalid direction was set, show warning and set up our for loop to count down + print("Invalid direction (-d) supplied, using down") + endid = baseid - count + fordirection = -1 + end + + -- The code below was blatantly stolen from Brian Redbeard's lf_bulk_program.lua script + function toBits(num,bits) + bits = bits or math.max(1, select(2, math.frexp(num))) + local t = {} + for b = bits, 1, -1 do + t[b] = math.fmod(num, 2) + num = math.floor((num - t[b]) / 2) + end + return table.concat(t) + end + + local function evenparity(s) + local _, count = string.gsub(s, "1", "") + local p = count % 2 + if (p == 0) then + return(false) + else + return(true) + end + end + + local function isempty(s) + return s == nil or s == '' + end + + local function cardHex(i,f) + fac = bit32.lshift(f,16) + id = bit32.bor(i, fac) + stream=toBits(id,26) + high = evenparity(string.sub(stream,0,12)) and 1 or 0 + low = not evenparity(string.sub(stream,13)) and 1 or 0 + bits = bit32.bor(bit32.lshift(id,1), low) + bits = bit32.bor(bits, bit32.lshift(high,25)) + preamble = bit32.bor(0, bit32.lshift(1,5)) + bits = bit32.bor(bits, bit32.lshift(1,26)) + return ("%04x%08x"):format(preamble,bits) + end + -- End stolen code + + + print("") -- Display status message + print("BruteForcing "..rfidtagname..""..facilitymessage..""..facility.." - CardNumber Start: "..baseid.." - CardNumber End: "..endid.." - TimeOut: "..timeout) + print("") + for cardnum = baseid,endid,fordirection do -- Loop through for each count (-c) + if rfidtag == 'hid' then cardnum = cardHex(cardnum, facility) end -- If rfid tag is set to HID, convert card to HEX using the stolen code above + core.console(consolecommand..' '..facility..' '..cardnum) -- Send command to proxmark + if timeout == 'pause' then -- If timeout is set to pause, wait for user input + print("Press enter to continue ...") + io.read() + else -- Otherwise sleep for timeout duration + os.execute("sleep "..timeout.."") + end + end + core.console('hw ping') -- Ping the proxmark to stop emulation and see if its still responding + +end -- Go bye bye + + +main(args) -- Do the thing From 4058a2d7df35e5a4f59f80ae1b4aa7d7598056dc Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Tue, 21 Nov 2017 07:31:42 +0100 Subject: [PATCH 082/310] fix: avoid SspClk overflow due to incomplete reset * should fix watchdog reset during hf mf mifare. Thanks @iceman1001 for the hint. --- armsrc/util.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/armsrc/util.c b/armsrc/util.c index a0fa50d3..e25c6e0b 100644 --- a/armsrc/util.c +++ b/armsrc/util.c @@ -292,6 +292,7 @@ void FormatVersionInformation(char *dst, int len, const char *prefix, void *vers strncat(dst, "\n", len - strlen(dst) - 1); } + // ------------------------------------------------------------------------- // timer lib // ------------------------------------------------------------------------- @@ -312,6 +313,7 @@ void StartTickCount() // note: worst case precision is approx 2.5% } + /* * Get the current count. */ @@ -319,6 +321,7 @@ uint32_t RAMFUNC GetTickCount(){ return AT91C_BASE_RTTC->RTTC_RTVR;// was * 2; } + // ------------------------------------------------------------------------- // microseconds timer // ------------------------------------------------------------------------- @@ -344,10 +347,12 @@ void StartCountUS() AT91C_BASE_TCB->TCB_BCR = 1; } + uint32_t RAMFUNC GetCountUS(){ return (AT91C_BASE_TC1->TC_CV * 0x8000) + ((AT91C_BASE_TC0->TC_CV * 2) / 3); //was /15) * 10); } + static uint32_t GlobalUsCounter = 0; uint32_t RAMFUNC GetDeltaCountUS(){ @@ -402,7 +407,7 @@ void StartCountSspClk() AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN; // enable TC2 // - // synchronize the counter with the ssp_frame signal. Note: FPGA must be in any iso14446 mode, otherwise the frame signal would not be present + // synchronize the counter with the ssp_frame signal. Note: FPGA must be in any iso14443 mode, otherwise SSC_FRAME and SSC_CLK signals would not be present // while(!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME)); // wait for ssp_frame to go high (start of frame) while(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME); // wait for ssp_frame to be low @@ -416,8 +421,11 @@ void StartCountSspClk() // (just started with the transfer of the 4th Bit). // The high word of the counter (TC2) will not reset until the low word (TC0) overflows. Therefore need to wait quite some time before // we can use the counter. - while (AT91C_BASE_TC0->TC_CV < 0xFFF0); + while (AT91C_BASE_TC0->TC_CV < 0xFFFF); + // Note: needs one more SSP_CLK cycle (1.18 us) until TC2 resets. Don't call GetCountSspClk() that soon. } + + void ResetSspClk(void) { //enable clock of timer and software trigger AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; @@ -425,6 +433,8 @@ void ResetSspClk(void) { AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; while (AT91C_BASE_TC2->TC_CV > 0); } + + uint32_t RAMFUNC GetCountSspClk(){ uint32_t tmp_count; tmp_count = (AT91C_BASE_TC2->TC_CV << 16) | AT91C_BASE_TC0->TC_CV; @@ -436,6 +446,7 @@ uint32_t RAMFUNC GetCountSspClk(){ } } + // ------------------------------------------------------------------------- // Timer for bitbanging, or LF stuff when you need a very precis timer // 1us = 1.5ticks @@ -464,6 +475,7 @@ void StartTicks(void){ while (AT91C_BASE_TC1->TC_CV > 0); } + // Wait - Spindelay in ticks. // if called with a high number, this will trigger the WDT... void WaitTicks(uint32_t ticks){ @@ -471,32 +483,43 @@ void WaitTicks(uint32_t ticks){ ticks += GET_TICKS; while (GET_TICKS < ticks); } + + // Wait / Spindelay in us (microseconds) // 1us = 1.5ticks. void WaitUS(uint16_t us){ if ( us == 0 ) return; WaitTicks( (uint32_t)(us * 1.5) ); } + + void WaitMS(uint16_t ms){ if (ms == 0) return; WaitTicks( (uint32_t)(ms * 1500) ); } + + // Starts Clock and waits until its reset void ResetTicks(void){ AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; while (AT91C_BASE_TC1->TC_CV > 0); } + + void ResetTimer(AT91PS_TC timer){ timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; while(timer->TC_CV > 0) ; } + + // stop clock void StopTicks(void){ AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; } + static uint64_t next_random = 1; /* Generates a (non-cryptographically secure) 32-bit random number. @@ -512,4 +535,3 @@ uint32_t prand() { next_random = next_random * 6364136223846793005 + 1; return (uint32_t)(next_random >> 32) % 0xffffffff; } - From 3c5fce2ba7d49f3ebea05eed187a9b5ee8189803 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Wed, 22 Nov 2017 07:16:33 +0200 Subject: [PATCH 083/310] Add: Emv first part of commands * hf emv search * hf emv pse * hf emv select * hf emv exec command - only part of functionality --- CHANGELOG.md | 4 + client/Makefile | 2 + client/cmdhf.c | 2 + client/cmdhf14a.c | 10 +- client/cmdhf14a.h | 2 +- client/emv/cmdemv.c | 552 ++++++++++++++++++++++++++++++++++++++++++ client/emv/cmdemv.h | 31 +++ client/emv/dol.c | 135 +++++++++++ client/emv/dol.h | 25 ++ client/emv/dump.c | 2 +- client/emv/dump.h | 1 + client/emv/emv_tags.c | 78 +++++- client/emv/emvcore.c | 432 ++++++++++++++++++++++++++++++++- client/emv/emvcore.h | 24 +- client/emv/tlv.c | 44 +++- client/emv/tlv.h | 5 + client/util.c | 37 ++- client/util.h | 3 + 18 files changed, 1370 insertions(+), 19 deletions(-) create mode 100644 client/emv/cmdemv.c create mode 100644 client/emv/cmdemv.h create mode 100644 client/emv/dol.c create mode 100644 client/emv/dol.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 10e6b827..fabd04b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Changed proxmark command line parameter `flush` to `-f` or `-flush` (Merlok) - Changed `hf 14a reader` to just reqest-anticilission-select sequence (Merlok) - Changed `hf 14a raw` - works with LED's and some exchange logic (Merlok) +- Changed TLV parser messages to more convenient (Merlok) ### Fixed - Changed start sequence in Qt mode (fix: short commands hangs main Qt thread) (Merlok) @@ -38,6 +39,9 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added to `hf 14a info` detection of weak prng from Iceman1001 fork (Merlok) - Added to `hf 14a apdu` - exchange apdu via iso1443-4 (Merlok) - Added to `hf 14a apdu` - apdu and tlv results parser (Merlok) +- Added `hf emv` group of commands (Merlok) +- Added `hf emv search` `hf emv pse` - commands for selection of EMV application (Merlok) +- Added `hf emv select` - command for select EMV application (Merlok) ## [3.0.1][2017-06-08] diff --git a/client/Makefile b/client/Makefile index 30e91efb..5df99de7 100644 --- a/client/Makefile +++ b/client/Makefile @@ -111,7 +111,9 @@ CMDSRCS = crapto1/crapto1.c\ emv/dump.c\ emv/tlv.c\ emv/emv_tags.c\ + emv/dol.c\ emv/emvcore.c\ + emv/cmdemv.c\ cmdhf.c \ cmdhf14a.c \ cmdhf14b.c \ diff --git a/client/cmdhf.c b/client/cmdhf.c index 453635b7..7a2f3252 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -30,6 +30,7 @@ #include "cmdhfmfu.h" #include "cmdhftopaz.h" #include "protocols.h" +#include "emv/cmdemv.h" static int CmdHelp(const char *Cmd); @@ -703,6 +704,7 @@ static command_t CommandTable[] = {"14b", CmdHF14B, 1, "{ ISO14443B RFIDs... }"}, {"15", CmdHF15, 1, "{ ISO15693 RFIDs... }"}, {"epa", CmdHFEPA, 1, "{ German Identification Card... }"}, + {"emv", CmdHFEMV, 1, "{ EMV cards... }"}, {"legic", CmdHFLegic, 0, "{ LEGIC RFIDs... }"}, {"iclass", CmdHFiClass, 1, "{ ICLASS RFIDs... }"}, {"mf", CmdHFMF, 1, "{ MIFARE RFIDs... }"}, diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 50071464..a0f3ce57 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -648,7 +648,7 @@ void DropField() { SendCommand(&c); } -int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int *dataoutlen) { +int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { uint16_t cmdc = 0; if (activateField) { @@ -686,6 +686,12 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea *dataoutlen = iLen - 2; if (*dataoutlen < 0) *dataoutlen = 0; + + if (maxdataoutlen && *dataoutlen > maxdataoutlen) { + PrintAndLog("APDU ERROR: Buffer too small(%d). Needs %d bytes", *dataoutlen, maxdataoutlen); + return 2; + } + memcpy(dataout, recv, *dataoutlen); if(!iLen) { @@ -779,7 +785,7 @@ int CmdHF14AAPDU(const char *cmd) { PrintAndLog(">>>>[%s%s%s] %s", activateField ? "sel ": "", leaveSignalON ? "keep ": "", decodeTLV ? "TLV": "", sprint_hex(data, datalen)); - int res = ExchangeAPDU14a(data, datalen, activateField, leaveSignalON, data, &datalen); + int res = ExchangeAPDU14a(data, datalen, activateField, leaveSignalON, data, USB_CMD_DATA_SIZE, &datalen); if (res) return res; diff --git a/client/cmdhf14a.h b/client/cmdhf14a.h index 6f6df907..401cead0 100644 --- a/client/cmdhf14a.h +++ b/client/cmdhf14a.h @@ -31,6 +31,6 @@ int CmdHF14ASnoop(const char *Cmd); char* getTagInfo(uint8_t uid); extern void DropField(); -extern int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int *dataoutlen); +extern int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); #endif diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c new file mode 100644 index 00000000..3c3c1f11 --- /dev/null +++ b/client/emv/cmdemv.c @@ -0,0 +1,552 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 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. +//----------------------------------------------------------------------------- +// EMV commands +//----------------------------------------------------------------------------- + +#include "cmdemv.h" + +int UsageCmdHFEMVSelect(void) { + PrintAndLog("HELP : Executes select applet command:\n"); + PrintAndLog("Usage: hf emv select [-s][-k][-a][-t] \n"); + PrintAndLog(" Options:"); + PrintAndLog(" -s : select card"); + PrintAndLog(" -k : keep field for next command"); + PrintAndLog(" -a : show APDU reqests and responses\n"); + PrintAndLog(" -t : TLV decode results\n"); + PrintAndLog("Samples:"); + PrintAndLog(" hf emv select -s a00000000101 -> select card, select applet"); + PrintAndLog(" hf emv select -s -t a00000000101 -> select card, select applet, show result in TLV"); + return 0; +} + +int CmdHFEMVSelect(const char *cmd) { + uint8_t data[APDU_AID_LEN] = {0}; + int datalen = 0; + bool activateField = false; + bool leaveSignalON = false; + bool decodeTLV = false; + + if (strlen(cmd) < 1) { + UsageCmdHFEMVSelect(); + return 0; + } + + SetAPDULogging(false); + + int cmdp = 0; + while(param_getchar(cmd, cmdp) != 0x00) { + char c = param_getchar(cmd, cmdp); + if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) + switch (param_getchar_indx(cmd, 1, cmdp)) { + case 'h': + case 'H': + UsageCmdHFEMVSelect(); + return 0; + case 's': + case 'S': + activateField = true; + break; + case 'k': + case 'K': + leaveSignalON = true; + break; + case 'a': + case 'A': + SetAPDULogging(true); + break; + case 't': + case 'T': + decodeTLV = true; + break; + default: + PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); + return 1; + } + + if (isxdigit(c)) { + switch(param_gethex_to_eol(cmd, cmdp, data, sizeof(data), &datalen)) { + case 1: + PrintAndLog("Invalid HEX value."); + return 1; + case 2: + PrintAndLog("AID too large."); + return 1; + case 3: + PrintAndLog("Hex must have even number of digits."); + return 1; + } + + // we get all the hex to end of line with spaces + break; + } + + + cmdp++; + } + + // exec + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + int res = EMVSelect(activateField, leaveSignalON, data, datalen, buf, sizeof(buf), &len, &sw, NULL); + + if (sw) + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + + if (res) + return res; + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + return 0; +} + +int UsageCmdHFEMVSearch(void) { + PrintAndLog("HELP : Tries to select all applets from applet list:\n"); + PrintAndLog("Usage: hf emv search [-s][-k][-a][-t]\n"); + PrintAndLog(" Options:"); + PrintAndLog(" -s : select card"); + PrintAndLog(" -k : keep field for next command"); + PrintAndLog(" -a : show APDU reqests and responses\n"); + PrintAndLog(" -t : TLV decode results of selected applets\n"); + PrintAndLog("Samples:"); + PrintAndLog(" hf emv search -s -> select card and search"); + PrintAndLog(" hf emv search -s -t -> select card, search and show result in TLV"); + return 0; +} + +int CmdHFEMVSearch(const char *cmd) { + + bool activateField = false; + bool leaveSignalON = false; + bool decodeTLV = false; + + if (strlen(cmd) < 1) { + UsageCmdHFEMVSearch(); + return 0; + } + + SetAPDULogging(false); + + int cmdp = 0; + while(param_getchar(cmd, cmdp) != 0x00) { + char c = param_getchar(cmd, cmdp); + if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) + switch (param_getchar_indx(cmd, 1, cmdp)) { + case 'h': + case 'H': + UsageCmdHFEMVSearch(); + return 0; + case 's': + case 'S': + activateField = true; + break; + case 'k': + case 'K': + leaveSignalON = true; + break; + case 'a': + case 'A': + SetAPDULogging(true); + break; + case 't': + case 'T': + decodeTLV = true; + break; + default: + PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); + return 1; + } + cmdp++; + } + + struct tlvdb *t = NULL; + const char *al = "Applets list"; + t = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); + + if (EMVSearch(activateField, leaveSignalON, decodeTLV, t)) { + tlvdb_free(t); + return 2; + } + + PrintAndLog("Search completed."); + + // print list here + if (!decodeTLV) { + TLVPrintAIDlistFromSelectTLV(t); + } + + tlvdb_free(t); + + return 0; +} + +int UsageCmdHFEMVPPSE(void) { + PrintAndLog("HELP : Executes PSE/PPSE select command. It returns list of applet on the card:\n"); + PrintAndLog("Usage: hf emv pse [-s][-k][-1][-2][-a][-t]\n"); + PrintAndLog(" Options:"); + PrintAndLog(" -s : select card"); + PrintAndLog(" -k : keep field for next command"); + PrintAndLog(" -1 : ppse (1PAY.SYS.DDF01)"); + PrintAndLog(" -2 : pse (2PAY.SYS.DDF01)"); + PrintAndLog(" -a : show APDU reqests and responses\n"); + PrintAndLog(" -t : TLV decode results\n"); + PrintAndLog("Samples:"); + PrintAndLog(" hf emv pse -s -1 -> select, get pse"); + PrintAndLog(" hf emv pse -s -k -2 -> select, get ppse, keep field"); + PrintAndLog(" hf emv pse -s -t -2 -> select, get ppse, show result in TLV"); + return 0; +} + +int CmdHFEMVPPSE(const char *cmd) { + + uint8_t PSENum = 2; + bool activateField = false; + bool leaveSignalON = false; + bool decodeTLV = false; + + if (strlen(cmd) < 1) { + UsageCmdHFEMVPPSE(); + return 0; + } + + SetAPDULogging(false); + + int cmdp = 0; + while(param_getchar(cmd, cmdp) != 0x00) { + char c = param_getchar(cmd, cmdp); + if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) + switch (param_getchar_indx(cmd, 1, cmdp)) { + case 'h': + case 'H': + UsageCmdHFEMVPPSE(); + return 0; + case 's': + case 'S': + activateField = true; + break; + case 'k': + case 'K': + leaveSignalON = true; + break; + case 'a': + case 'A': + SetAPDULogging(true); + break; + case 't': + case 'T': + decodeTLV = true; + break; + case '1': + PSENum = 1; + break; + case '2': + PSENum = 2; + break; + default: + PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); + return 1; + } + cmdp++; + } + + // exec + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + int res = EMVSelectPSE(activateField, leaveSignalON, PSENum, buf, sizeof(buf), &len, &sw); + + if (sw) + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + + if (res) + return res; + + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + return 0; +} + +int UsageCmdHFEMVExec(void) { + PrintAndLog("HELP : Executes EMV contactless transaction:\n"); + PrintAndLog("Usage: hf emv exec [-s][-a][-t]\n"); + PrintAndLog(" Options:"); + PrintAndLog(" -s : select card"); + PrintAndLog(" -a : show APDU reqests and responses\n"); + PrintAndLog(" -t : TLV decode results\n"); + PrintAndLog(" -f : force search AID. Search AID instead of execute PPSE.\n"); + PrintAndLog("Samples:"); + PrintAndLog(" hf emv pse -s -> select card"); + PrintAndLog(" hf emv pse -s -t -a -> select card, show responses in TLV, show APDU"); + return 0; +} + +#define TLV_ADD(tag, value)( tlvdb_add(tlvRoot, tlvdb_fixed(tag, sizeof(value) - 1, (const unsigned char *)value)) ) + +int CmdHFEMVExec(const char *cmd) { + bool activateField = false; + bool showAPDU = false; + bool decodeTLV = false; + bool forceSearch = false; + + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + uint8_t AID[APDU_AID_LEN] = {0}; + size_t AIDlen = 0; + + int res; + + if (strlen(cmd) < 1) { + UsageCmdHFEMVExec(); + return 0; + } + + int cmdp = 0; + while(param_getchar(cmd, cmdp) != 0x00) { + char c = param_getchar(cmd, cmdp); + if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) + switch (param_getchar_indx(cmd, 1, cmdp)) { + case 'h': + case 'H': + UsageCmdHFEMVPPSE(); + return 0; + case 's': + case 'S': + activateField = true; + break; + case 'a': + case 'A': + showAPDU = true; + break; + case 't': + case 'T': + decodeTLV = true; + break; + case 'f': + case 'F': + forceSearch = true; + break; + default: + PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); + return 1; + } + cmdp++; + } + + + // init applets list tree + struct tlvdb *tlvSelect = NULL; + const char *al = "Applets list"; + tlvSelect = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); + + // Application Selection + // https://www.openscdp.org/scripts/tutorial/emv/applicationselection.html + if (!forceSearch) { + // PPSE + PrintAndLog("\n* PPSE."); + SetAPDULogging(showAPDU); + res = EMVSearchPSE(activateField, true, decodeTLV, tlvSelect); + + // check PPSE and select application id + if (!res) { + TLVPrintAIDlistFromSelectTLV(tlvSelect); + EMVSelectApplication(tlvSelect, AID, &AIDlen); + } + } + + // Search + if (!AIDlen) { + PrintAndLog("\n* Search AID in list."); + SetAPDULogging(false); + if (EMVSearch(activateField, true, decodeTLV, tlvSelect)) { + tlvdb_free(tlvSelect); + return 2; + } + + // check search and select application id + TLVPrintAIDlistFromSelectTLV(tlvSelect); + EMVSelectApplication(tlvSelect, AID, &AIDlen); + } + + // Init TLV tree + struct tlvdb *tlvRoot = NULL; + const char *alr = "Root terminal TLV tree"; + tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr); + + // check if we found EMV application on card + if (!AIDlen) { + PrintAndLog("Can't select AID. EMV AID not found"); + return 2; + } + + // Select + PrintAndLog("\n* Selecting AID:%s", sprint_hex_inrow(AID, AIDlen)); + SetAPDULogging(showAPDU); + res = EMVSelect(false, true, AID, AIDlen, buf, sizeof(buf), &len, &sw, tlvRoot); + + if (res) { + PrintAndLog("Can't select AID (%d). Exit...", res); + return 3; + } + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + PrintAndLog("* Selected."); + +PrintAndLog("-----BREAK."); +return 0; + PrintAndLog("\n* Init transaction parameters."); + + //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 + TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // E6 + //9F02:(Amount, Authorised (Numeric)) len:6 + TLV_ADD(0x9F02, "\x00\x00\x00\x00\x01\x00"); + //9F1A:(Terminal Country Code) len:2 + TLV_ADD(0x9F1A, "ru"); + //5F2A:(Transaction Currency Code) len:2 + // USD 840, EUR 978, RUR 810, RUB 643, RUR 810(old), UAH 980, AZN 031, n/a 999 + TLV_ADD(0x5F2A, "\x09\x80"); + //9A:(Transaction Date) len:3 + TLV_ADD(0x9A, "\x00\x00\x00"); + //9C:(Transaction Type) len:1 | 00 => Goods and service #01 => Cash + TLV_ADD(0x9C, "\x00"); + // 9F37 Unpredictable Number len:4 + TLV_ADD(0x9F37, "\x01\x02\x03\x04"); + + TLVPrintFromTLV(tlvRoot); + + PrintAndLog("\n* Calc PDOL."); + struct tlv *pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83); + if (!pdol_data_tlv){ + PrintAndLog("ERROR: can't create PDOL TLV."); + return 4; + } + + size_t pdol_data_tlv_data_len; + unsigned char *pdol_data_tlv_data = tlv_encode(pdol_data_tlv, &pdol_data_tlv_data_len); + if (!pdol_data_tlv_data) { + PrintAndLog("ERROR: can't create PDOL data."); + return 4; + } + PrintAndLog("PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); + +//PrintAndLog("-----BREAK."); +//return 0; + PrintAndLog("\n* GPO."); + res = EMVGPO(true, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); + + free(pdol_data_tlv); + + if (res) { + PrintAndLog("GPO error(%d): %4x. Exit...", res, sw); + return 5; + } + + // process response template format 1 [id:80 2b AIP + x4b AFL] and format 2 [id:77 TLV] + if (buf[0] == 0x80) { + + + + if (decodeTLV){ + PrintAndLog("GPO response format1:"); + TLVPrintFromBuffer(buf, len); + } + } else { + + + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + } + + PrintAndLog("\n* Read records from AFL."); + const struct tlv *AFL = tlvdb_get(tlvRoot, 0x94, NULL); + if (!AFL || !AFL->len) { + PrintAndLog("WARNING: AFL not found."); + } + + while(AFL && AFL->len) { + if (AFL->len % 4) { + PrintAndLog("ERROR: Wrong AFL length: %d", AFL->len); + break; + } + + for (int i = 0; i < AFL->len / 4; i++) { + uint8_t SFI = AFL->value[i * 4 + 0] >> 3; + uint8_t SFIstart = AFL->value[i * 4 + 1]; + uint8_t SFIend = AFL->value[i * 4 + 2]; + uint8_t SFIoffline = AFL->value[i * 4 + 3]; + + PrintAndLog("* * SFI[%02x] start:%02x end:%02x offline:%02x", SFI, SFIstart, SFIend, SFIoffline); + if (SFI == 0 || SFI == 31 || SFIstart == 0 || SFIstart > SFIend) { + PrintAndLog("SFI ERROR! Skipped..."); + continue; + } + + for(int n = SFIstart; n <= SFIend; n++) { + PrintAndLog("* * * SFI[%02x] %d", SFI, n); + + res = EMVReadRecord(true, SFI, n, buf, sizeof(buf), &len, &sw, tlvRoot); + if (res) { + PrintAndLog("ERROR SFI[%02x]. APDU error %4x", SFI, sw); + continue; + } + + if (decodeTLV) { + TLVPrintFromBuffer(buf, len); + PrintAndLog(""); + } + + if (SFIoffline) { + // here will be offline records storing... + // dont foget: if (sfi < 11) + } + } + } + + break; + } + + // additional contacless EMV commands (fDDA, CDA, external authenticate) + + + // DropField + DropField(); + + // Destroy TLV's + tlvdb_free(tlvSelect); + tlvdb_free(tlvRoot); + + PrintAndLog("\n* Transaction completed."); + + return 0; +} + +int CmdHelp(const char *Cmd); +static command_t CommandTable[] = { + {"help", CmdHelp, 1, "This help"}, + {"exec", CmdHFEMVExec, 0, "Executes EMV contactless transaction."}, + {"pse", CmdHFEMVPPSE, 0, "Execute PPSE. It selects 2PAY.SYS.DDF01 or 1PAY.SYS.DDF01 directory."}, + {"search", CmdHFEMVSearch, 0, "Try to select all applets from applets list and print installed applets."}, + {"select", CmdHFEMVSelect, 0, "Select applet."}, + {NULL, NULL, 0, NULL} +}; + +int CmdHFEMV(const char *Cmd) { + CmdsParse(CommandTable, Cmd); + return 0; +} + +int CmdHelp(const char *Cmd) { + CmdsHelp(CommandTable); + return 0; +} diff --git a/client/emv/cmdemv.h b/client/emv/cmdemv.h new file mode 100644 index 00000000..78796efa --- /dev/null +++ b/client/emv/cmdemv.h @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 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. +//----------------------------------------------------------------------------- +// EMV commands +//----------------------------------------------------------------------------- + +#ifndef CMDEMV_H__ +#define CMDEMV_H__ + +#include +#include +#include +#include +#include "proxmark3.h" +#include "ui.h" +#include "cmdparser.h" +#include "common.h" +#include "util.h" +#include "util_posix.h" +#include "cmdmain.h" +#include "emvcore.h" +#include "apduinfo.h" + +int CmdHFEMV(const char *Cmd); + + +#endif \ No newline at end of file diff --git a/client/emv/dol.c b/client/emv/dol.c new file mode 100644 index 00000000..0fc1c49b --- /dev/null +++ b/client/emv/dol.c @@ -0,0 +1,135 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "emv/dol.h" +#include "emv/tlv.h" + +#include +#include + +static size_t dol_calculate_len(const struct tlv *tlv, size_t data_len) +{ + if (!tlv) + return 0; + + const unsigned char *buf = tlv->value; + size_t left = tlv->len; + size_t count = 0; + + while (left) { + struct tlv tlv; + if (!tlv_parse_tl(&buf, &left, &tlv)) + return 0; + + count += tlv.len; + + /* Last tag can be of variable length */ + if (tlv.len == 0 && left == 0) + count = data_len; + } + + return count; +} + +struct tlv *dol_process(const struct tlv *tlv, const struct tlvdb *tlvdb, tlv_tag_t tag) +{ + size_t res_len; + if (!tlv || !(res_len = dol_calculate_len(tlv, 0))) { + struct tlv *res_tlv = malloc(sizeof(*res_tlv)); + + res_tlv->tag = tag; + res_tlv->len = 0; + res_tlv->value = NULL; + + return res_tlv; + } + + struct tlv *res_tlv = malloc(sizeof(*res_tlv) + res_len); + if (!res_tlv) + return NULL; + + const unsigned char *buf = tlv->value; + size_t left = tlv->len; + unsigned char *res = (unsigned char *)(res_tlv + 1); + size_t pos = 0; + + while (left) { + struct tlv cur_tlv; + if (!tlv_parse_tl(&buf, &left, &cur_tlv) || pos + cur_tlv.len > res_len) { + free(res_tlv); + + return NULL; + } + + const struct tlv *tag_tlv = tlvdb_get(tlvdb, cur_tlv.tag, NULL); + if (!tag_tlv) { + memset(res + pos, 0, cur_tlv.len); + } else if (tag_tlv->len > cur_tlv.len) { + memcpy(res + pos, tag_tlv->value, cur_tlv.len); + } else { + // FIXME: cn data should be padded with 0xFF !!! + memcpy(res + pos, tag_tlv->value, tag_tlv->len); + memset(res + pos + tag_tlv->len, 0, cur_tlv.len - tag_tlv->len); + } + pos += cur_tlv.len; + } + + res_tlv->tag = tag; + res_tlv->len = res_len; + res_tlv->value = res; + + return res_tlv; +} + +struct tlvdb *dol_parse(const struct tlv *tlv, const unsigned char *data, size_t data_len) +{ + if (!tlv) + return NULL; + + const unsigned char *buf = tlv->value; + size_t left = tlv->len; + size_t res_len = dol_calculate_len(tlv, data_len); + size_t pos = 0; + struct tlvdb *db = NULL; + + if (res_len != data_len) + return NULL; + + while (left) { + struct tlv cur_tlv; + if (!tlv_parse_tl(&buf, &left, &cur_tlv) || pos + cur_tlv.len > res_len) { + tlvdb_free(db); + return NULL; + } + + /* Last tag can be of variable length */ + if (cur_tlv.len == 0 && left == 0) + cur_tlv.len = res_len - pos; + + struct tlvdb *tag_db = tlvdb_fixed(cur_tlv.tag, cur_tlv.len, data + pos); + if (!db) + db = tag_db; + else + tlvdb_add(db, tag_db); + + pos += cur_tlv.len; + } + + return db; +} diff --git a/client/emv/dol.h b/client/emv/dol.h new file mode 100644 index 00000000..9b1b5165 --- /dev/null +++ b/client/emv/dol.h @@ -0,0 +1,25 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef DOL_H +#define DOL_H + +#include "emv/tlv.h" +#include + +struct tlv *dol_process(const struct tlv *tlv, const struct tlvdb *tlvdb, tlv_tag_t tag); +struct tlvdb *dol_parse(const struct tlv *tlv, const unsigned char *buf, size_t len); + +#endif diff --git a/client/emv/dump.c b/client/emv/dump.c index 41d7c9fd..2368680a 100644 --- a/client/emv/dump.c +++ b/client/emv/dump.c @@ -18,8 +18,8 @@ #endif #include "dump.h" - #include +#include #define PRINT_INDENT(level) {for (int i = 0; i < (level); i++) fprintf(f, "\t");} diff --git a/client/emv/dump.h b/client/emv/dump.h index 567a134f..a981615a 100644 --- a/client/emv/dump.h +++ b/client/emv/dump.h @@ -21,5 +21,6 @@ void dump_buffer_simple(const unsigned char *ptr, size_t len, FILE *f); void dump_buffer(const unsigned char *ptr, size_t len, FILE *f, int level); +void dump_buffer_tab(const unsigned char *ptr, size_t len, FILE *f, int tabs); #endif diff --git a/client/emv/emv_tags.c b/client/emv/emv_tags.c index d91685c4..a7c11c3e 100644 --- a/client/emv/emv_tags.c +++ b/client/emv/emv_tags.c @@ -29,6 +29,7 @@ enum emv_tag_t { EMV_TAG_BITMASK, EMV_TAG_DOL, EMV_TAG_CVM_LIST, + EMV_TAG_AFL, EMV_TAG_STRING, EMV_TAG_NUMERIC, EMV_TAG_YYMMDD, @@ -57,7 +58,7 @@ static const struct emv_tag_bit EMV_AIP[] = { { EMV_BIT(1, 3), "Issuer authentication is supported" }, { EMV_BIT(1, 2), "Reserved for use by the EMV Contactless Specifications" }, { EMV_BIT(1, 1), "CDA supported" }, - { EMV_BIT(2, 8), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(2, 8), "MSD is supported (Magnetic Stripe Data)" }, { EMV_BIT(2, 7), "Reserved for use by the EMV Contactless Specifications" }, { EMV_BIT(2, 6), "Reserved for use by the EMV Contactless Specifications" }, { EMV_BIT(2, 1), "Reserved for use by the EMV Contactless Specifications" }, @@ -113,8 +114,44 @@ static const struct emv_tag_bit EMV_TVR[] = { EMV_BIT_FINISH, }; +static const struct emv_tag_bit EMV_CTQ[] = { + { EMV_BIT(1, 8), "Online PIN Required" }, + { EMV_BIT(1, 7), "Signature Required" }, + { EMV_BIT(1, 6), "Go Online if Offline Data Authentication Fails and Reader is online capable" }, + { EMV_BIT(1, 5), "Switch Interface if Offline Data Authentication fails and Reader supports VIS" }, + { EMV_BIT(1, 4), "Go Online if Application Expired" }, + { EMV_BIT(1, 3), "Switch Interface for Cash Transactions" }, + { EMV_BIT(1, 2), "Switch Interface for Cashback Transactions" }, + { EMV_BIT(2, 8), "Consumer Device CVM Performed" }, + { EMV_BIT(2, 7), "Card supports Issuer Update Processing at the POS" }, + EMV_BIT_FINISH, +}; + +static const struct emv_tag_bit EMV_TTQ[] = { + { EMV_BIT(1, 8), "MSD supported" }, + { EMV_BIT(1, 7), "VSDC supported" }, + { EMV_BIT(1, 6), "qVSDC supported" }, + { EMV_BIT(1, 5), "EMV contact chip supported" }, + { EMV_BIT(1, 4), "Offline-only reader" }, + { EMV_BIT(1, 3), "Online PIN supported" }, + { EMV_BIT(1, 2), "Signature supported" }, + { EMV_BIT(1, 1), "Offline Data Authentication (ODA) for Online Authorizations supported\nWarning!!!! Readers compliant to this specification set TTQ byte 1 bit 1 (this field) to 0b" }, + { EMV_BIT(2, 8), "Online cryptogram required" }, + { EMV_BIT(2, 7), "CVM required" }, + { EMV_BIT(2, 6), "(Contact Chip) Offline PIN supported" }, + { EMV_BIT(3, 8), "Issuer Update Processing supported" }, + { EMV_BIT(3, 7), "Mobile functionality supported (Consumer Device CVM)" }, + EMV_BIT_FINISH, +}; + +// All Data Elements by Tags used in TLV structure (according to the EMV 4.2 Standard ) +// https://www.eftlab.co.uk/index.php/site-map/knowledge-base/145-emv-nfc-tags +// http://dexterous-programmer.blogspot.in/2012/05/emv-tags.html static const struct emv_tag emv_tags[] = { { 0x00 , "Unknown ???" }, + { 0x01 , "", EMV_TAG_STRING }, // string for headers + { 0x41 , "Country code and national data" }, + { 0x42 , "Issuer Identification Number (IIN)" }, { 0x4f , "Application Dedicated File (ADF) Name" }, { 0x50 , "Application Label", EMV_TAG_STRING }, { 0x56 , "Track 1 Data" }, @@ -147,12 +184,13 @@ static const struct emv_tag emv_tags[] = { { 0x91 , "Issuer Authentication Data" }, { 0x92 , "Issuer Public Key Remainder" }, { 0x93 , "Signed Static Application Data" }, - { 0x94 , "Application File Locator (AFL)" }, + { 0x94 , "Application File Locator (AFL)", EMV_TAG_AFL }, { 0x95 , "Terminal Verification Results" }, { 0x9a , "Transaction Date", EMV_TAG_YYMMDD }, { 0x9c , "Transaction Type" }, { 0x9f02, "Amount, Authorised (Numeric)", EMV_TAG_NUMERIC }, { 0x9f03, "Amount, Other (Numeric)", EMV_TAG_NUMERIC, }, + { 0x9f06, "Application Identifier (AID), Terminal. ISO 7816-5" }, { 0x9f07, "Application Usage Control", EMV_TAG_BITMASK, &EMV_AUC }, { 0x9f08, "Application Version Number" }, { 0x9f0d, "Issuer Action Code - Default", EMV_TAG_BITMASK, &EMV_TVR }, @@ -168,6 +206,7 @@ static const struct emv_tag emv_tags[] = { { 0x9f21, "Transaction Time" }, { 0x9f26, "Application Cryptogram" }, { 0x9f27, "Cryptogram Information Data" }, + { 0x9f2a, "Kernel Identifier" }, { 0x9f2d, "ICC PIN Encipherment Public Key Certificate" }, { 0x9f2e, "ICC PIN Encipherment Public Key Exponent" }, { 0x9f2f, "ICC PIN Encipherment Public Key Remainder" }, @@ -193,9 +232,12 @@ static const struct emv_tag emv_tags[] = { { 0x9f63, "PUNATC(Track1)" }, { 0x9f64, "NATC(Track1)" }, { 0x9f65, "PCVC3(Track2)" }, - { 0x9f66, "PUNATC(Track2)" }, - { 0x9f67, "NATC(Track2)" }, + { 0x9f66, "PUNATC(Track2) / Terminal Transaction Qualifiers (TTQ)", EMV_TAG_BITMASK, &EMV_TTQ }, + { 0x9f67, "NATC(Track2) / MSD Offset" }, + { 0x9f69, "Card Authentication Related Data" }, + { 0x9f6a, "Unpredictable Number", EMV_TAG_NUMERIC }, { 0x9f6b, "Track 2 Data" }, + { 0x9f6c, "Card Transaction Qualifiers (CTQ)", EMV_TAG_BITMASK, &EMV_CTQ }, { 0xa5 , "File Control Information (FCI) Proprietary Template" }, { 0xbf0c, "File Control Information (FCI) Issuer Discretionary Data" }, }; @@ -242,10 +284,11 @@ static void emv_tag_dump_bitmask(const struct tlv *tlv, const struct emv_tag *ta PRINT_INDENT(level); fprintf(f, "\tByte %u (%02x)\n", byte, val); for (bit = 8; bit > 0; bit--, val <<= 1) { - if (val & 0x80) + if (val & 0x80){ PRINT_INDENT(level); fprintf(f, "\t\t%s - '%s'\n", bitstrings[bit - 1], bits->bit == EMV_BIT(byte, bit) ? bits->name : "Unknown"); + } if (bits->bit == EMV_BIT(byte, bit)) bits ++; } @@ -276,10 +319,8 @@ static void emv_tag_dump_dol(const struct tlv *tlv, const struct emv_tag *tag, F static void emv_tag_dump_string(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) { - PRINT_INDENT(level); fprintf(f, "\tString value '"); fwrite(tlv->value, 1, tlv->len, f); - PRINT_INDENT(level); fprintf(f, "'\n"); } @@ -433,6 +474,19 @@ static void emv_tag_dump_cvm_list(const struct tlv *tlv, const struct emv_tag *t } } +static void emv_tag_dump_afl(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level){ + if (tlv->len < 4 || tlv->len % 4) { + PRINT_INDENT(level); + fprintf(f, "\tINVALID!\n"); + return; + } + + for (int i = 0; i < tlv->len / 4; i++) { + PRINT_INDENT(level); + fprintf(f, "SFI[%02x] start:%02x end:%02x offline:%02x\n", tlv->value[i * 4 + 0] >> 3, tlv->value[i * 4 + 1], tlv->value[i * 4 + 2], tlv->value[i * 4 + 3]); + } +} + bool emv_tag_dump(const struct tlv *tlv, FILE *f, int level) { if (!tlv) { @@ -443,20 +497,28 @@ bool emv_tag_dump(const struct tlv *tlv, FILE *f, int level) const struct emv_tag *tag = emv_get_tag(tlv); PRINT_INDENT(level); - fprintf(f, "--%2hx[%02zx] '%s':\n", tlv->tag, tlv->len, tag->name); + fprintf(f, "--%2hx[%02zx] '%s':", tlv->tag, tlv->len, tag->name); switch (tag->type) { case EMV_TAG_GENERIC: + fprintf(f, "\n"); break; case EMV_TAG_BITMASK: + fprintf(f, "\n"); emv_tag_dump_bitmask(tlv, tag, f, level); break; case EMV_TAG_DOL: + fprintf(f, "\n"); emv_tag_dump_dol(tlv, tag, f, level); break; case EMV_TAG_CVM_LIST: + fprintf(f, "\n"); emv_tag_dump_cvm_list(tlv, tag, f, level); break; + case EMV_TAG_AFL: + fprintf(f, "\n"); + emv_tag_dump_afl(tlv, tag, f, level); + break; case EMV_TAG_STRING: emv_tag_dump_string(tlv, tag, f, level); break; diff --git a/client/emv/emvcore.c b/client/emv/emvcore.c index 63a69baa..5c737b05 100644 --- a/client/emv/emvcore.c +++ b/client/emv/emvcore.c @@ -10,6 +10,77 @@ #include "emvcore.h" +// Got from here. Thanks) +// https://eftlab.co.uk/index.php/site-map/knowledge-base/211-emv-aid-rid-pix +const char *PSElist [] = { + "325041592E5359532E4444463031", // 2PAY.SYS.DDF01 - Visa Proximity Payment System Environment - PPSE + "315041592E5359532E4444463031" // 1PAY.SYS.DDF01 - Visa Payment System Environment - PSE +}; +const size_t PSElistLen = sizeof(PSElist)/sizeof(char*); + +const char *AIDlist [] = { + // Visa International + "A00000000305076010", // VISA ELO Credit + "A0000000031010", // VISA Debit/Credit (Classic) + "A0000000031010", // ddddddddddddddddddddddddddddddddddddddddddddddddddddddddd + "A000000003101001", // VISA Credit + "A000000003101002", // VISA Debit + "A0000000032010", // VISA Electron + "A0000000032020", // VISA + "A0000000033010", // VISA Interlink + "A0000000034010", // VISA Specific + "A0000000035010", // VISA Specific + "A0000000036010", // Domestic Visa Cash Stored Value + "A0000000036020", // International Visa Cash Stored Value + "A0000000038002", // VISA Auth, VisaRemAuthen EMV-CAP (DPA) + "A0000000038010", // VISA Plus + "A0000000039010", // VISA Loyalty + "A000000003999910", // VISA Proprietary ATM + // Visa USA + "A000000098", // Debit Card + "A0000000980848", // Debit Card + // Mastercard International + "A00000000401", // MasterCard PayPass + "A0000000041010", // MasterCard Credit + "A00000000410101213", // MasterCard Credit + "A00000000410101215", // MasterCard Credit + "A0000000042010", // MasterCard Specific + "A0000000043010", // MasterCard Specific + "A0000000043060", // Maestro (Debit) + "A000000004306001", // Maestro (Debit) + "A0000000044010", // MasterCard Specific + "A0000000045010", // MasterCard Specific + "A0000000046000", // Cirrus + "A0000000048002", // SecureCode Auth EMV-CAP + "A0000000049999", // MasterCard PayPass + // American Express + "A000000025", + "A0000000250000", + "A00000002501", + "A000000025010402", + "A000000025010701", + "A000000025010801", + // Groupement des Cartes Bancaires "CB" + "A0000000421010", // Cartes Bancaire EMV Card + "A0000000422010", + "A0000000423010", + "A0000000424010", + "A0000000425010", + // JCB CO., LTD. + "A00000006510", // JCB + "A0000000651010", // JCB J Smart Credit + "A0000001544442", // Banricompras Debito - Banrisul - Banco do Estado do Rio Grande do SUL - S.A. + "F0000000030001", // BRADESCO + "A0000005241010", // RuPay - RuPay + "D5780000021010" // Bankaxept - Bankaxept +}; +const size_t AIDlistLen = sizeof(AIDlist)/sizeof(char*); + +static bool APDULogging = false; +void SetAPDULogging(bool logging) { + APDULogging = logging; +} + static bool print_cb(void *data, const struct tlv *tlv, int level, bool is_leaf) { emv_tag_dump(tlv, stdout, level); if (is_leaf) { @@ -23,7 +94,7 @@ void TLVPrintFromBuffer(uint8_t *data, int datalen) { struct tlvdb *t = NULL; t = tlvdb_parse_multi(data, datalen); if (t) { - PrintAndLog("TLV decoded:"); + PrintAndLog("-------------------- TLV decoded --------------------"); tlvdb_visit(t, print_cb, NULL, 0); tlvdb_free(t); @@ -31,3 +102,362 @@ void TLVPrintFromBuffer(uint8_t *data, int datalen) { PrintAndLog("TLV ERROR: Can't parse response as TLV tree."); } } + +void TLVPrintFromTLV(struct tlvdb *tlv) { + if (!tlv) + return; + + tlvdb_visit(tlv, print_cb, NULL, 0); +} + +void TLVPrintAIDlistFromSelectTLV(struct tlvdb *tlv) { + PrintAndLog("|------------------|--------|-------------------------|"); + PrintAndLog("| AID |Priority| Name |"); + PrintAndLog("|------------------|--------|-------------------------|"); + + struct tlvdb *ttmp = tlvdb_find(tlv, 0x6f); + if (!ttmp) + PrintAndLog("| none |"); + + while (ttmp) { + const struct tlv *tgAID = tlvdb_get_inchild(ttmp, 0x84, NULL); + const struct tlv *tgName = tlvdb_get_inchild(ttmp, 0x50, NULL); + const struct tlv *tgPrio = tlvdb_get_inchild(ttmp, 0x87, NULL); + if (!tgAID) + break; + PrintAndLog("|%s| %s |%s|", + sprint_hex_inrow_ex(tgAID->value, tgAID->len, 18), + (tgPrio) ? sprint_hex(tgPrio->value, 1) : " ", + (tgName) ? sprint_ascii_ex(tgName->value, tgName->len, 25) : " "); + + ttmp = tlvdb_find_next(ttmp, 0x6f); + } + + PrintAndLog("|------------------|--------|-------------------------|"); +} + + +int EMVSelect(bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { + uint8_t data[APDU_RES_LEN] = {0}; + *ResultLen = 0; + if (sw) *sw = 0; + uint16_t isw = 0; + + // select APDU + data[0] = 0x00; + data[1] = 0xA4; + data[2] = 0x04; + data[3] = 0x00; + data[4] = AIDLen; + memcpy(&data[5], AID, AIDLen); + + if (ActivateField) + DropField(); + + if (APDULogging) + PrintAndLog(">>>> %s", sprint_hex(data, AIDLen + 6)); + + int res = ExchangeAPDU14a(data, AIDLen + 6, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); + + if (APDULogging) + PrintAndLog("<<<< %s", sprint_hex(Result, *ResultLen)); + + if (res) { + return res; + } + + if (*ResultLen < 2) { + PrintAndLog("SELECT ERROR: returned %d bytes", *ResultLen); + return 5; + } + + *ResultLen -= 2; + isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1]; + if (sw) + *sw = isw; + + if (isw != 0x9000) { + if (APDULogging) + PrintAndLog("SELECT ERROR: [%4X] %s", isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xff)); + return 5; + } + + // add to tlv tree + if (tlv) { + struct tlvdb *t = tlvdb_parse_multi(Result, *ResultLen); + tlvdb_add(tlv, t); + } + + return 0; +} + +int EMVSelectPSE(bool ActivateField, bool LeaveFieldON, uint8_t PSENum, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { + uint8_t buf[APDU_AID_LEN] = {0}; + *ResultLen = 0; + int len = 0; + int res = 0; + switch (PSENum) { + case 1: + param_gethex_to_eol(PSElist[1], 0, buf, sizeof(buf), &len); + break; + case 2: + param_gethex_to_eol(PSElist[0], 0, buf, sizeof(buf), &len); + break; + default: + return -1; + } + + // select + res = EMVSelect(ActivateField, LeaveFieldON, buf, len, Result, MaxResultLen, ResultLen, sw, NULL); + + return res; +} + +int EMVSearchPSE(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv) { + uint8_t data[APDU_RES_LEN] = {0}; + size_t datalen = 0; + uint16_t sw = 0; + int res; + + // select PPSE + res = EMVSelectPSE(ActivateField, true, 2, data, sizeof(data), &datalen, &sw); + + if (!res){ + struct tlvdb *t = NULL; + t = tlvdb_parse_multi(data, datalen); + if (t) { + int retrycnt = 0; + struct tlvdb *ttmp = tlvdb_find_path(t, (tlv_tag_t[]){0x6f, 0xa5, 0xbf0c, 0x61, 0x00}); + if (!ttmp) + PrintAndLog("PPSE don't have records."); + + while (ttmp) { + const struct tlv *tgAID = tlvdb_get_inchild(ttmp, 0x4f, NULL); + if (tgAID) { + res = EMVSelect(false, true, (uint8_t *)tgAID->value, tgAID->len, data, sizeof(data), &datalen, &sw, tlv); + + // retry if error and not returned sw error + if (res && res != 5) { + if (++retrycnt < 3){ + continue; + } else { + // card select error, proxmark error + if (res == 1) { + PrintAndLog("Exit..."); + return 1; + } + + retrycnt = 0; + PrintAndLog("Retry failed [%s]. Skiped...", sprint_hex_inrow(tgAID->value, tgAID->len)); + } + + // next element + ttmp = tlvdb_find_next(ttmp, 0x61); + continue; + } + retrycnt = 0; + + // all is ok + if (decodeTLV){ + PrintAndLog("%s:", sprint_hex_inrow(tgAID->value, tgAID->len)); + TLVPrintFromBuffer(data, datalen); + } + } + + ttmp = tlvdb_find_next(ttmp, 0x61); + } + + tlvdb_free(t); + } else { + PrintAndLog("PPSE ERROR: Can't get TLV from response."); + } + } else { + PrintAndLog("PPSE ERROR: Can't select PPSE AID. Error: %d", res); + } + + if(!LeaveFieldON) + DropField(); + + return res; +} + +int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv) { + uint8_t aidbuf[APDU_AID_LEN] = {0}; + int aidlen = 0; + uint8_t data[APDU_RES_LEN] = {0}; + size_t datalen = 0; + uint16_t sw = 0; + + int res = 0; + int retrycnt = 0; + for(int i = 0; i < AIDlistLen; i ++) { + param_gethex_to_eol(AIDlist[i], 0, aidbuf, sizeof(aidbuf), &aidlen); + res = EMVSelect((i == 0) ? ActivateField : false, (i == AIDlistLen - 1) ? LeaveFieldON : true, aidbuf, aidlen, data, sizeof(data), &datalen, &sw, tlv); + // retry if error and not returned sw error + if (res && res != 5) { + if (++retrycnt < 3){ + i--; + } else { + // card select error, proxmark error + if (res == 1) { + PrintAndLog("Exit..."); + return 1; + } + + retrycnt = 0; + PrintAndLog("Retry failed [%s]. Skiped...", AIDlist[i]); + } + continue; + } + retrycnt = 0; + + if (res) + continue; + + if (decodeTLV){ + PrintAndLog("%s:", AIDlist[i]); + TLVPrintFromBuffer(data, datalen); + } + } + + return 0; +} + +int EMVSelectApplication(struct tlvdb *tlv, uint8_t *AID, size_t *AIDlen) { + // needs to check priority. 0x00 - highest + int prio = 0xffff; + + *AIDlen = 0; + + struct tlvdb *ttmp = tlvdb_find(tlv, 0x6f); + if (!ttmp) + return 1; + + while (ttmp) { + const struct tlv *tgAID = tlvdb_get_inchild(ttmp, 0x84, NULL); + const struct tlv *tgPrio = tlvdb_get_inchild(ttmp, 0x87, NULL); + + if (!tgAID) + break; + + if (tgPrio) { + int pt = bytes_to_num((uint8_t*)tgPrio->value, (tgPrio->len < 2) ? tgPrio->len : 2); + if (pt < prio) { + prio = pt; + + memcpy(AID, tgAID->value, tgAID->len); + *AIDlen = tgAID->len; + } + } else { + // takes the first application from list wo priority + if (!*AIDlen) { + memcpy(AID, tgAID->value, tgAID->len); + *AIDlen = tgAID->len; + } + } + + ttmp = tlvdb_find_next(ttmp, 0x6f); + } + + return 0; +} + +int EMVGPO(bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { + uint8_t data[APDU_RES_LEN] = {0}; + *ResultLen = 0; + if (sw) *sw = 0; + uint16_t isw = 0; + + // GPO APDU + data[0] = 0x80; + data[1] = 0xA8; + data[2] = 0x00; + data[3] = 0x00; + data[4] = PDOLLen; + if (PDOL) + memcpy(&data[5], PDOL, PDOLLen); + + + if (APDULogging) + PrintAndLog(">>>> %s", sprint_hex(data, PDOLLen + 5)); + + int res = ExchangeAPDU14a(data, PDOLLen + 5, false, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); + + if (APDULogging) + PrintAndLog("<<<< %s", sprint_hex(Result, *ResultLen)); + + if (res) { + return res; + } + + if (*ResultLen < 2) { + PrintAndLog("GPO ERROR: returned %d bytes", *ResultLen); + return 5; + } + + *ResultLen -= 2; + isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1]; + if (sw) + *sw = isw; + + if (isw != 0x9000) { + if (APDULogging) + PrintAndLog("GPO ERROR: [%4X] %s", isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xff)); + return 5; + } + + // add to tlv tree + if (tlv) { + struct tlvdb *t = tlvdb_parse_multi(Result, *ResultLen); + tlvdb_add(tlv, t); + } + + return 0; +} + +int EMVReadRecord(bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { + uint8_t data[10] = {0}; + *ResultLen = 0; + if (sw) *sw = 0; + uint16_t isw = 0; + + // read record APDU + data[0] = 0x00; + data[1] = 0xb2; + data[2] = SFIrec; + data[3] = (SFI << 3) | 0x04; + data[4] = 0; + + if (APDULogging) + PrintAndLog(">>>> %s", sprint_hex(data, 5)); + + int res = ExchangeAPDU14a(data, 5, false, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); + + if (APDULogging) + PrintAndLog("<<<< %s", sprint_hex(Result, *ResultLen)); + + if (res) { + return res; + } + + *ResultLen -= 2; + isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1]; + if (sw) + *sw = isw; + + if (isw != 0x9000) { + if (APDULogging) + PrintAndLog("Read record ERROR: [%4X] %s", isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xff)); + return 5; + } + + // add to tlv tree + if (tlv) { + struct tlvdb *t = tlvdb_parse_multi(Result, *ResultLen); + tlvdb_add(tlv, t); + } + + return 0; +} + + diff --git a/client/emv/emvcore.h b/client/emv/emvcore.h index 523b92ac..fdd3ab86 100644 --- a/client/emv/emvcore.h +++ b/client/emv/emvcore.h @@ -5,7 +5,7 @@ // at your option, any later version. See the LICENSE.txt file for the text of // the license. //----------------------------------------------------------------------------- -// EMV core functions +// EMV core functionality //----------------------------------------------------------------------------- #ifndef EMVCORE_H__ @@ -15,14 +15,36 @@ #include #include #include +#include #include "util.h" #include "common.h" #include "ui.h" +#include "cmdhf14a.h" +#include "emv/apduinfo.h" #include "emv/tlv.h" +#include "emv/dol.h" #include "emv/dump.h" #include "emv/emv_tags.h" +#define APDU_RES_LEN 260 +#define APDU_AID_LEN 50 + extern void TLVPrintFromBuffer(uint8_t *data, int datalen); +extern void TLVPrintFromTLV(struct tlvdb *tlv); +extern void TLVPrintAIDlistFromSelectTLV(struct tlvdb *tlv); + +extern void SetAPDULogging(bool logging); + +// search application +extern int EMVSearchPSE(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv); +extern int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv); +extern int EMVSelectPSE(bool ActivateField, bool LeaveFieldON, uint8_t PSENum, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw); +extern int EMVSelect(bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); +// select application +extern int EMVSelectApplication(struct tlvdb *tlv, uint8_t *AID, size_t *AIDlen); +// Get Processing Options +extern int EMVGPO(bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); +extern int EMVReadRecord(bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); #endif diff --git a/client/emv/tlv.c b/client/emv/tlv.c index 24125cc7..1be91777 100644 --- a/client/emv/tlv.c +++ b/client/emv/tlv.c @@ -299,6 +299,40 @@ void tlvdb_free(struct tlvdb *tlvdb) } } +struct tlvdb *tlvdb_find_next(struct tlvdb *tlvdb, tlv_tag_t tag) { + if (!tlvdb) + return NULL; + + return tlvdb_find(tlvdb->next, tag); +} + +struct tlvdb *tlvdb_find(struct tlvdb *tlvdb, tlv_tag_t tag) { + if (!tlvdb) + return NULL; + + for (; tlvdb; tlvdb = tlvdb->next) { + if (tlvdb->tag.tag == tag) + return tlvdb; + } + + return NULL; +} + +struct tlvdb *tlvdb_find_path(struct tlvdb *tlvdb, tlv_tag_t tag[]) { + int i = 0; + struct tlvdb *tnext = tlvdb; + + while (tnext && tag[i]) { + tnext = tlvdb_find(tnext, tag[i]); + i++; + if (tag[i] && tnext) { + tnext = tnext->children; + } + } + + return tnext; +} + void tlvdb_add(struct tlvdb *tlvdb, struct tlvdb *other) { while (tlvdb->next) { @@ -317,9 +351,8 @@ void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data, int level) for (; tlvdb; tlvdb = next) { next = tlvdb->next; - bool is_leaf = (tlvdb->children == NULL); - cb(data, &tlvdb->tag, level, is_leaf); - tlvdb_visit(tlvdb->children, cb, data, level+1); + cb(data, &tlvdb->tag, level, (tlvdb->children == NULL)); + tlvdb_visit(tlvdb->children, cb, data, level + 1); } } @@ -356,6 +389,11 @@ const struct tlv *tlvdb_get(const struct tlvdb *tlvdb, tlv_tag_t tag, const stru return NULL; } +const struct tlv *tlvdb_get_inchild(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev) { + tlvdb = tlvdb->children; + return tlvdb_get(tlvdb, tag, prev); +} + unsigned char *tlv_encode(const struct tlv *tlv, size_t *len) { size_t size = tlv->len; diff --git a/client/emv/tlv.h b/client/emv/tlv.h index 5a573566..5b8b6825 100644 --- a/client/emv/tlv.h +++ b/client/emv/tlv.h @@ -39,10 +39,15 @@ struct tlvdb *tlvdb_parse(const unsigned char *buf, size_t len); struct tlvdb *tlvdb_parse_multi(const unsigned char *buf, size_t len); void tlvdb_free(struct tlvdb *tlvdb); +struct tlvdb *tlvdb_find(struct tlvdb *tlvdb, tlv_tag_t tag); +struct tlvdb *tlvdb_find_next(struct tlvdb *tlvdb, tlv_tag_t tag); +struct tlvdb *tlvdb_find_path(struct tlvdb *tlvdb, tlv_tag_t tag[]); + void tlvdb_add(struct tlvdb *tlvdb, struct tlvdb *other); void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data, int level); const struct tlv *tlvdb_get(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev); +const struct tlv *tlvdb_get_inchild(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev); bool tlv_parse_tl(const unsigned char **buf, size_t *len, struct tlv *tlv); unsigned char *tlv_encode(const struct tlv *tlv, size_t *len); diff --git a/client/util.c b/client/util.c index a1caafdb..92b0e7a6 100644 --- a/client/util.c +++ b/client/util.c @@ -154,6 +154,28 @@ char *sprint_hex(const uint8_t *data, const size_t len) { return buf; } +char *sprint_hex_inrow_ex(const uint8_t *data, const size_t len, const size_t min_str_len) { + + int maxLen = ( len > 1024/2) ? 1024/2 : len; + static char buf[1024] = {0}; + char *tmp = buf; + size_t i; + + for (i = 0; i < maxLen; ++i, tmp += 2) + sprintf(tmp, "%02x", (unsigned int) data[i]); + + i *= 2; + int minStrLen = min_str_len > i ? min_str_len : 0; + for(; i < minStrLen; i++, tmp += 1) + sprintf(tmp, " "); + + return buf; +} + +char *sprint_hex_inrow(const uint8_t *data, const size_t len) { + return sprint_hex_inrow_ex(data, len, 0); +} + char *sprint_bin_break(const uint8_t *data, const size_t len, const uint8_t breaks) { // make sure we don't go beyond our char array memory int max_len; @@ -210,7 +232,7 @@ char *sprint_hex_ascii(const uint8_t *data, const size_t len) { return buf; } -char *sprint_ascii(const uint8_t *data, const size_t len) { +char *sprint_ascii_ex(const uint8_t *data, const size_t len, const size_t min_str_len) { static char buf[1024]; char *tmp = buf; memset(buf, 0x00, 1024); @@ -221,9 +243,18 @@ char *sprint_ascii(const uint8_t *data, const size_t len) { tmp[i] = ((c < 32) || (c == 127)) ? '.' : c; ++i; } + + int minStrLen = min_str_len > i ? min_str_len : 0; + for(; i < minStrLen; ++i) + tmp[i] = ' '; + return buf; } +char *sprint_ascii(const uint8_t *data, const size_t len) { + return sprint_ascii_ex(data, len, 0); +} + void num_to_bytes(uint64_t n, size_t len, uint8_t* dest) { while (len--) { @@ -498,8 +529,10 @@ int param_gethex_to_eol(const char *line, int paramnum, uint8_t * data, int maxd int indx = bg; while (line[indx]) { - if (line[indx] == '\t' || line[indx] == ' ') + if (line[indx] == '\t' || line[indx] == ' ') { + indx++; continue; + } if (isxdigit(line[indx])) { buf[strlen(buf) + 1] = 0x00; diff --git a/client/util.h b/client/util.h index 8bab8cfd..e9c48b03 100644 --- a/client/util.h +++ b/client/util.h @@ -37,10 +37,13 @@ extern void FillFileNameByUID(char *fileName, uint8_t * uid, char *ext, int byte extern void print_hex(const uint8_t * data, const size_t len); extern char *sprint_hex(const uint8_t * data, const size_t len); +extern char *sprint_hex_inrow(const uint8_t *data, const size_t len); +extern char *sprint_hex_inrow_ex(const uint8_t *data, const size_t len, const size_t min_str_len); extern char *sprint_bin(const uint8_t * data, const size_t len); extern char *sprint_bin_break(const uint8_t *data, const size_t len, const uint8_t breaks); extern char *sprint_hex_ascii(const uint8_t *data, const size_t len); extern char *sprint_ascii(const uint8_t *data, const size_t len); +extern char *sprint_ascii_ex(const uint8_t *data, const size_t len, const size_t min_str_len); extern void num_to_bytes(uint64_t n, size_t len, uint8_t* dest); extern uint64_t bytes_to_num(uint8_t* src, size_t len); From 47b78133a07719094a01d745c6beea0f0954b615 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Wed, 22 Nov 2017 17:46:00 +0100 Subject: [PATCH 084/310] minor iso14443a timing fixes to avoid select errors: * take setup frame guard time (SFGT) into account * adjust iso14a_timeout by FPGA delays * don't give up on first WUPA. Retry several times --- armsrc/iso14443a.c | 95 +++++++++++++++++++++++++++++++--------------- 1 file changed, 64 insertions(+), 31 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 89ef23d4..a0fe2326 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -189,33 +189,15 @@ void iso14a_set_trigger(bool enable) { void iso14a_set_timeout(uint32_t timeout) { - iso14a_timeout = timeout; - if(MF_DBGLEVEL >= 3) Dbprintf("ISO14443A Timeout set to %ld (%dms)", iso14a_timeout, iso14a_timeout / 106); + iso14a_timeout = timeout - (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER)/(16*8); + if(MF_DBGLEVEL >= 3) Dbprintf("ISO14443A Timeout set to %ld (%dms)", timeout, timeout / 106); } -static void iso14a_set_ATS_timeout(uint8_t *ats) { - - uint8_t tb1; - uint8_t fwi; - uint32_t fwt; - - if (ats[0] > 1) { // there is a format byte T0 - if ((ats[1] & 0x20) == 0x20) { // there is an interface byte TB(1) - if ((ats[1] & 0x10) == 0x10) { // there is an interface byte TA(1) preceding TB(1) - tb1 = ats[3]; - } else { - tb1 = ats[2]; - } - fwi = (tb1 & 0xf0) >> 4; // frame waiting indicator (FWI) - fwt = 256 * 16 * (1 << fwi); // frame waiting time (FWT) in 1/fc - - iso14a_set_timeout(fwt/(8*16)); - } - } +uint32_t iso14a_get_timeout(void) { + return iso14a_timeout + (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER)/(16*8); } - //----------------------------------------------------------------------------- // Generate the parity value for a byte sequence // @@ -1277,7 +1259,7 @@ static void PrepareDelayedTransfer(uint16_t delay) // Transmit the command (to the tag) that was placed in ToSend[]. // Parameter timing: // if NULL: transfer at next possible time, taking into account -// request guard time and frame delay time +// request guard time, startup frame guard time and frame delay time // if == 0: transfer immediately and return time of transfer // if != 0: delay transfer until time specified //------------------------------------------------------------------------------------- @@ -1685,6 +1667,59 @@ int ReaderReceive(uint8_t *receivedAnswer, uint8_t *parity) return Demod.len; } + +static void iso14a_set_ATS_times(uint8_t *ats) { + + uint8_t tb1; + uint8_t fwi, sfgi; + uint32_t fwt, sfgt; + + if (ats[0] > 1) { // there is a format byte T0 + if ((ats[1] & 0x20) == 0x20) { // there is an interface byte TB(1) + if ((ats[1] & 0x10) == 0x10) { // there is an interface byte TA(1) preceding TB(1) + tb1 = ats[3]; + } else { + tb1 = ats[2]; + } + fwi = (tb1 & 0xf0) >> 4; // frame waiting time integer (FWI) + if (fwi != 15) { + fwt = 256 * 16 * (1 << fwi); // frame waiting time (FWT) in 1/fc + iso14a_set_timeout(fwt/(8*16)); + } + sfgi = tb1 & 0x0f; // startup frame guard time integer (SFGI) + if (sfgi != 0 && sfgi != 15) { + sfgt = 256 * 16 * (1 << sfgi); // startup frame guard time (SFGT) in 1/fc + NextTransferTime = MAX(NextTransferTime, Demod.endTime + (sfgt - DELAY_AIR2ARM_AS_READER - DELAY_ARM2AIR_AS_READER)/16); + } + } + } +} + + +static int GetATQA(uint8_t *resp, uint8_t *resp_par) { + +#define WUPA_RETRY_TIMEOUT 10 // 10ms + uint8_t wupa[] = { 0x52 }; // 0x26 - REQA 0x52 - WAKE-UP + + uint32_t save_iso14a_timeout = iso14a_get_timeout(); + iso14a_set_timeout(1236/(16*8)+1); // response to WUPA is expected at exactly 1236/fc. No need to wait longer. + + uint32_t start_time = GetTickCount(); + int len; + + // we may need several tries if we did send an unknown command or a wrong authentication before... + do { + // Broadcast for a card, WUPA (0x52) will force response from all cards in the field + ReaderTransmitBitsPar(wupa, 7, NULL, NULL); + // Receive the ATQA + len = ReaderReceive(resp, resp_par); + } while (len == 0 && GetTickCount() <= start_time + WUPA_RETRY_TIMEOUT); + + iso14a_set_timeout(save_iso14a_timeout); + return len; +} + + // performs iso14443a anticollision (optional) and card select procedure // fills the uid and cuid pointer unless NULL // fills the card info record unless NULL @@ -1692,7 +1727,6 @@ int ReaderReceive(uint8_t *receivedAnswer, uint8_t *parity) // and num_cascades must be set (1: 4 Byte UID, 2: 7 Byte UID, 3: 10 Byte UID) // requests ATS unless no_rats is true int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats) { - uint8_t wupa[] = { 0x52 }; // 0x26 - REQA 0x52 - WAKE-UP uint8_t sel_all[] = { 0x93,0x20 }; uint8_t sel_uid[] = { 0x93,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; uint8_t rats[] = { 0xE0,0x80,0x00,0x00 }; // FSD=256, FSDI=8, CID=0 @@ -1712,11 +1746,9 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u p_hi14a_card->ats_len = 0; } - // Broadcast for a card, WUPA (0x52) will force response from all cards in the field - ReaderTransmitBitsPar(wupa, 7, NULL, NULL); - - // Receive the ATQA - if(!ReaderReceive(resp, resp_par)) return 0; + if (!GetATQA(resp, resp_par)) { + return 0; + } if(p_hi14a_card) { memcpy(p_hi14a_card->atqa, resp, 2); @@ -1845,8 +1877,9 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u // reset the PCB block number iso14_pcb_blocknum = 0; - // set default timeout based on ATS - iso14a_set_ATS_timeout(resp); + // set default timeout and delay next transfer based on ATS + iso14a_set_ATS_times(resp); + } return 1; } From 66efdc1f646eb4e8e0c0f50003cf9d469f4e54c1 Mon Sep 17 00:00:00 2001 From: merlokk Date: Sat, 25 Nov 2017 12:58:50 +0200 Subject: [PATCH 085/310] part of changes --- client/emv/cmdemv.c | 172 ++++++++++++++++++++++++++++++++++++++---- client/emv/emv_tags.c | 100 ++++++++++++++++++++++++ client/emv/emvcore.h | 26 +++++++ client/util.c | 49 +++++++----- client/util.h | 4 + 5 files changed, 318 insertions(+), 33 deletions(-) diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index 3c3c1f11..679c26ed 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -402,12 +402,12 @@ int CmdHFEMVExec(const char *cmd) { TLVPrintFromBuffer(buf, len); PrintAndLog("* Selected."); -PrintAndLog("-----BREAK."); -return 0; PrintAndLog("\n* Init transaction parameters."); //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 - TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // E6 +// TLV_ADD(0x9F66, "\x86\x00\x00\x00"); // MSD + TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC +// TLV_ADD(0x9F66, "\x8e\x00\x00\x00"); // CDA //9F02:(Amount, Authorised (Numeric)) len:6 TLV_ADD(0x9F02, "\x00\x00\x00\x00\x01\x00"); //9F1A:(Terminal Country Code) len:2 @@ -421,8 +421,10 @@ return 0; TLV_ADD(0x9C, "\x00"); // 9F37 Unpredictable Number len:4 TLV_ADD(0x9F37, "\x01\x02\x03\x04"); + // 9F6A Unpredictable Number (MSD for UDOL) len:4 + TLV_ADD(0x9F6A, "\x01\x02\x03\x04"); - TLVPrintFromTLV(tlvRoot); + TLVPrintFromTLV(tlvRoot); // TODO delete!!! PrintAndLog("\n* Calc PDOL."); struct tlv *pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83); @@ -439,8 +441,6 @@ return 0; } PrintAndLog("PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); -//PrintAndLog("-----BREAK."); -//return 0; PrintAndLog("\n* GPO."); res = EMVGPO(true, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); @@ -453,21 +453,49 @@ return 0; // process response template format 1 [id:80 2b AIP + x4b AFL] and format 2 [id:77 TLV] if (buf[0] == 0x80) { - - - if (decodeTLV){ PrintAndLog("GPO response format1:"); TLVPrintFromBuffer(buf, len); } + + if (len < 4 || (len - 4) % 4) { + PrintAndLog("ERROR: GPO response format1 parsing error. length=%d", len); + } else { + // AIP + struct tlvdb * f1AIP = tlvdb_fixed(0x82, 2, buf + 2); + tlvdb_add(tlvRoot, f1AIP); + if (decodeTLV){ + PrintAndLog("\n* * Decode response format 1 (0x80) AIP and AFL:"); + TLVPrintFromTLV(f1AIP); + } + + // AFL + struct tlvdb * f1AFL = tlvdb_fixed(0x94, len - 4, buf + 2 + 2); + tlvdb_add(tlvRoot, f1AFL); + if (decodeTLV) + TLVPrintFromTLV(f1AFL); + } } else { - - - if (decodeTLV) TLVPrintFromBuffer(buf, len); } + // extract PAN from track2 + { + const struct tlv *track2 = tlvdb_get(tlvRoot, 0x57, NULL); + if (!tlvdb_get(tlvRoot, 0x5a, NULL) && track2 && track2->len >= 8) { + struct tlvdb *pan = GetPANFromTrack2(track2); + if (pan) { + tlvdb_add(tlvRoot, pan); + + const struct tlv *pantlv = tlvdb_get(tlvRoot, 0x5a, NULL); + PrintAndLog("\n* * Extracted PAN from track2: %s", sprint_hex(pantlv->value, pantlv->len)); + } else { + PrintAndLog("\n* * WARNING: Can't extract PAN from track2."); + } + } + } + PrintAndLog("\n* Read records from AFL."); const struct tlv *AFL = tlvdb_get(tlvRoot, 0x94, NULL); if (!AFL || !AFL->len) { @@ -516,7 +544,125 @@ return 0; break; } - // additional contacless EMV commands (fDDA, CDA, external authenticate) + // transaction check + const struct tlv *AIPtlv = tlvdb_get(tlvRoot, 0x82, NULL); + uint16_t AIP = AIPtlv->value[0] + AIPtlv->value[1] * 0x100; + PrintAndLog("* * AIP=%x", AIP); + + // qVSDC + { + // 9F26: Application Cryptogram + const struct tlv *AC = tlvdb_get(tlvRoot, 0x9F26, NULL); + if (AC) { + PrintAndLog("\n--> qVSDC transaction."); + PrintAndLog("* AC path"); + + // 9F36: Application Transaction Counter (ATC) + const struct tlv *ATC = tlvdb_get(tlvRoot, 0x9F36, NULL); + if (ATC) { + + // 9F10: Issuer Application Data - optional + const struct tlv *IAD = tlvdb_get(tlvRoot, 0x9F10, NULL); + + // print AC data + PrintAndLog("ATC: %s", sprint_hex(ATC->value, ATC->len)); + PrintAndLog("AC: %s", sprint_hex(AC->value, AC->len)); + if (IAD){ + PrintAndLog("IAD: %s", sprint_hex(IAD->value, IAD->len)); + + if (IAD->len >= IAD->value[0] + 1) { + PrintAndLog("\tKey index: 0x%02x", IAD->value[1]); + PrintAndLog("\tCrypto ver: 0x%02x(%03d)", IAD->value[2], IAD->value[2]); + PrintAndLog("\tCVR:", sprint_hex(&IAD->value[3], IAD->value[0] - 2)); + struct tlvdb * cvr = tlvdb_fixed(0x20, IAD->value[0] - 2, &IAD->value[3]); + TLVPrintFromTLVLev(cvr, 1); + } + } else { + PrintAndLog("WARNING: IAD not found."); + } + + } else { + PrintAndLog("ERROR AC: Application Transaction Counter (ATC) not found."); + } + } + } + + // TODO: Mastercard M/CHIP + { + const struct tlv *CDOL1 = tlvdb_get(tlvRoot, 0x8c, NULL); + if (CDOL1 && GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD) { // and m/chip transaction flag + + } + } + + // MSD + if (AIP & 0x8000) { + PrintAndLog("\n--> MSD transaction."); + + + PrintAndLog("* MSD dCVV path. Check dCVV"); + + const struct tlv *track2 = tlvdb_get(tlvRoot, 0x57, NULL); + if (track2) { + PrintAndLog("Track2: %s", sprint_hex(track2->value, track2->len)); + + struct tlvdb *dCVV = GetdCVVRawFromTrack2(track2); + PrintAndLog("dCVV raw data:"); + TLVPrintFromTLV(dCVV); + + if (GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD) { + PrintAndLog("\n* Mastercard calculate UDOL"); + + // UDOL (9F69) + const struct tlv *UDOL = tlvdb_get(tlvRoot, 0x9F69, NULL); + // UDOL(9F69) default: 9F6A (Unpredictable number) 4 bytes + const struct tlv defUDOL = { + .tag = 0x01, + .len = 3, + .value = (uint8_t *)"\x9f\x6a\x04", + }; + if (!UDOL) + PrintAndLog("Use default UDOL."); + + struct tlv *udol_data_tlv = dol_process(UDOL ? UDOL : &defUDOL, tlvRoot, 0x01); // 0x01 - fake tag! + if (!udol_data_tlv){ + PrintAndLog("ERROR: can't create UDOL TLV."); + return 4; + } + + size_t udol_data_tlv_data_len; + unsigned char *udol_data_tlv_data = tlv_encode(udol_data_tlv, &udol_data_tlv_data_len); + if (!udol_data_tlv_data) { + PrintAndLog("ERROR: can't create UDOL data."); + return 4; + } + + // eliminate fake tag + udol_data_tlv_data_len -= 2; + udol_data_tlv_data += 2; + + PrintAndLog("UDOL data[%d]: %s", udol_data_tlv_data_len, sprint_hex(udol_data_tlv_data, udol_data_tlv_data_len)); + + PrintAndLog("\n* Mastercard compute cryptographic checksum(UDOL)"); + + res = MSCComputeCryptoChecksum(true, udol_data_tlv_data, udol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); + if (res) { + PrintAndLog("ERROR Compute Crypto Checksum. APDU error %4x", sw); + return 5; + } + + if (decodeTLV) { + TLVPrintFromBuffer(buf, len); + PrintAndLog(""); + } + + } + } else { + PrintAndLog("ERROR MSD: Track2 data not found."); + } + } + + // additional contacless EMV commands (fDDA, external authenticate) // DropField diff --git a/client/emv/emv_tags.c b/client/emv/emv_tags.c index a7c11c3e..a69d7196 100644 --- a/client/emv/emv_tags.c +++ b/client/emv/emv_tags.c @@ -21,6 +21,7 @@ #include "emv_tags.h" #include +#include #define PRINT_INDENT(level) {for (int i = 0; i < (level); i++) fprintf(f, "\t");} @@ -33,6 +34,7 @@ enum emv_tag_t { EMV_TAG_STRING, EMV_TAG_NUMERIC, EMV_TAG_YYMMDD, + EMV_TAG_CVR, }; struct emv_tag { @@ -144,12 +146,38 @@ static const struct emv_tag_bit EMV_TTQ[] = { EMV_BIT_FINISH, }; +static const struct emv_tag_bit EMV_CVR[] = { + // mask 0F 0F F0 0F + { EMV_BIT(1, 4), "CDA Performed" }, + { EMV_BIT(1, 3), "Offline DDA Performed" }, + { EMV_BIT(1, 2), "Issuer Authentication Not Performed" }, + { EMV_BIT(1, 1), "Issuer Authentication performed and Failed" }, + { EMV_BIT(2, 4), "Offline PIN Verification Performed" }, + { EMV_BIT(2, 3), "Offline PIN Verification Performed and PIN Not Successfully Verified" }, + { EMV_BIT(2, 2), "PIN Try Limit Exceeded" }, + { EMV_BIT(2, 1), "Last Online Transaction Not Completed" }, + { EMV_BIT(3, 8), "Lower Offline Transaction Count Limit Exceeded" }, + { EMV_BIT(3, 7), "Upper Offline Transaction Count Limit Exceeded" }, + { EMV_BIT(3, 6), "Lower Cumulative Offline Amount Limit Exceeded" }, + { EMV_BIT(3, 5), "Upper Cumulative Offline Amount Limit Exceeded" }, + { EMV_BIT(4, 4), "Issuer script processing failed on last transaction" }, + { EMV_BIT(4, 3), "Offline data authentication failed on previous transaction and transaction declined offline" }, + { EMV_BIT(4, 2), "Go Online on Next Transaction Was Set" }, + { EMV_BIT(4, 1), "Unable to go Online" }, + EMV_BIT_FINISH, +}; + // All Data Elements by Tags used in TLV structure (according to the EMV 4.2 Standard ) // https://www.eftlab.co.uk/index.php/site-map/knowledge-base/145-emv-nfc-tags // http://dexterous-programmer.blogspot.in/2012/05/emv-tags.html static const struct emv_tag emv_tags[] = { + // internal { 0x00 , "Unknown ???" }, { 0x01 , "", EMV_TAG_STRING }, // string for headers + { 0x02 , "Raw data", }, // data + { 0x20 , "Cardholder Verification Results (CVR)", EMV_TAG_CVR }, // not standard! + + // EMV { 0x41 , "Country code and national data" }, { 0x42 , "Issuer Identification Number (IIN)" }, { 0x4f , "Application Dedicated File (ADF) Name" }, @@ -228,6 +256,8 @@ static const struct emv_tag emv_tags[] = { { 0x9f4c, "ICC Dynamic Number" }, { 0x9f4d, "Log Entry" }, { 0x9f4f, "Log Format", EMV_TAG_DOL }, + { 0x9f60, "CVC3 (Track1)" }, + { 0x9f61, "CVC3 (Track2)" }, { 0x9f62, "PCVC3(Track1)" }, { 0x9f63, "PUNATC(Track1)" }, { 0x9f64, "NATC(Track1)" }, @@ -375,6 +405,72 @@ static uint32_t emv_get_binary(const unsigned char *S) return (S[0] << 24) | (S[1] << 16) | (S[2] << 8) | (S[3] << 0); } +// https://github.com/binaryfoo/emv-bertlv/blob/master/src/main/resources/fields/visa-cvr.txt +static void emv_tag_dump_cvr(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) { + if (!tlv || tlv->len < 1) { + PRINT_INDENT(level); + fprintf(f, "\tINVALID!\n"); + return; + } + + if (tlv->len != tlv->value[0] + 1) { + PRINT_INDENT(level); + fprintf(f, "\tINVALID length!\n"); + return; + } + + if (tlv->len >= 2) { + // AC1 + PRINT_INDENT(level); + if ((tlv->value[1] & 0xC0) == 0x00) fprintf(f, "\tAC1: AAC (Transaction declined)\n"); + if ((tlv->value[1] & 0xC0) == 0x40) fprintf(f, "\tAC1: TC (Transaction approved)\n"); + if ((tlv->value[1] & 0xC0) == 0x80) fprintf(f, "\tAC1: ARQC (Online authorisation requested)\n"); + if ((tlv->value[1] & 0xC0) == 0xC0) fprintf(f, "\tAC1: RFU\n"); + // AC2 + PRINT_INDENT(level); + if ((tlv->value[1] & 0x30) == 0x00) fprintf(f, "\tAC2: AAC (Transaction declined)\n"); + if ((tlv->value[1] & 0x30) == 0x10) fprintf(f, "\tAC2: TC (Transaction approved)\n"); + if ((tlv->value[1] & 0x30) == 0x20) fprintf(f, "\tAC2: not requested (ARQC)\n"); + if ((tlv->value[1] & 0x30) == 0x30) fprintf(f, "\tAC2: RFU\n"); + } + if (tlv->len >= 3 && (tlv->value[2] >> 4)) { + PRINT_INDENT(level); + fprintf(f, "\tPIN try: %x\n", tlv->value[2] >> 4); + } + if (tlv->len >= 4 && (tlv->value[3] & 0x0F)) { + PRINT_INDENT(level); + fprintf(f, "\tIssuer discretionary bits: %x\n", tlv->value[3] & 0x0F); + } + if (tlv->len >= 5 && (tlv->value[4] >> 4)) { + PRINT_INDENT(level); + fprintf(f, "\tSuccessfully processed issuer script commands: %x\n", tlv->value[4] >> 4); + } + + // mask 0F 0F F0 0F + uint8_t data[20] = {0}; + memcpy(data, &tlv->value[1], tlv->len - 1); + data[0] &= 0x0F; + data[1] &= 0x0F; + data[2] &= 0xF0; + data[3] &= 0x0F; + const struct tlv bit_tlv = { + .tag = tlv->tag, + .len = tlv->len - 1, + .value = data, + }; + const struct emv_tag bit_tag = { + .tag = tag->tag, + .name = tag->name, + .type = EMV_TAG_BITMASK, + .data = EMV_CVR, + }; + + if (data[0] || data[1] || data[2] || data[3]) + emv_tag_dump_bitmask(&bit_tlv, &bit_tag, f, level); + + return; +} + static void emv_tag_dump_cvm_list(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) { uint32_t X, Y; @@ -528,6 +624,10 @@ bool emv_tag_dump(const struct tlv *tlv, FILE *f, int level) case EMV_TAG_YYMMDD: emv_tag_dump_yymmdd(tlv, tag, f, level); break; + case EMV_TAG_CVR: + fprintf(f, "\n"); + emv_tag_dump_cvr(tlv, tag, f, level); + break; }; return true; diff --git a/client/emv/emvcore.h b/client/emv/emvcore.h index fdd3ab86..03d5c956 100644 --- a/client/emv/emvcore.h +++ b/client/emv/emvcore.h @@ -29,10 +29,34 @@ #define APDU_RES_LEN 260 #define APDU_AID_LEN 50 +typedef struct { + uint8_t CLA; + uint8_t INS; + uint8_t P1; + uint8_t P2; + uint8_t Lc; + uint8_t *data; +} sAPDU; + +enum CardPSVendor { + CV_NA, + CV_VISA, + CV_MASTERCARD, + CV_AMERICANEXPRESS, + CV_JCB, + CV_CB, + CV_OTHER, +}; +extern enum CardPSVendor GetCardPSVendor(uint8_t * AID, size_t AIDlen); + extern void TLVPrintFromBuffer(uint8_t *data, int datalen); extern void TLVPrintFromTLV(struct tlvdb *tlv); +extern void TLVPrintFromTLVLev(struct tlvdb *tlv, int level); extern void TLVPrintAIDlistFromSelectTLV(struct tlvdb *tlv); +extern struct tlvdb *GetPANFromTrack2(const struct tlv *track2); +extern struct tlvdb *GetdCVVRawFromTrack2(const struct tlv *track2); + extern void SetAPDULogging(bool logging); // search application @@ -45,6 +69,8 @@ extern int EMVSelectApplication(struct tlvdb *tlv, uint8_t *AID, size_t *AIDlen) // Get Processing Options extern int EMVGPO(bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); extern int EMVReadRecord(bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); +// Mastercard +int MSCComputeCryptoChecksum(bool LeaveFieldON, uint8_t *UDOL, uint8_t UDOLlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); #endif diff --git a/client/util.c b/client/util.c index 92b0e7a6..b7f07bde 100644 --- a/client/util.c +++ b/client/util.c @@ -111,6 +111,31 @@ void FillFileNameByUID(char *fileName, uint8_t * uid, char *ext, int byteCount) sprintf(fnameptr, "%s", ext); } +void hex_to_buffer(const uint8_t *buf, const uint8_t *hex_data, const size_t hex_len, const size_t hex_max_len, + const size_t min_str_len, const size_t spaces_between, bool uppercase) { + + char *tmp = (char *)buf; + size_t i; + + int maxLen = ( hex_len > hex_max_len) ? hex_max_len : hex_len; + + for (i = 0; i < maxLen; ++i, tmp += 2 + spaces_between) { + sprintf(tmp, (uppercase) ? "%02X" : "%02x", (unsigned int) hex_data[i]); + + for (int j = 0; j < spaces_between; j++) + sprintf(tmp + 2 + j, " "); + } + + i *= (2 + spaces_between); + int minStrLen = min_str_len > i ? min_str_len : 0; + if (minStrLen > hex_max_len) + minStrLen = hex_max_len; + for(; i < minStrLen; i++, tmp += 1) + sprintf(tmp, " "); + + return; +} + // printing and converting functions void print_hex(const uint8_t * data, const size_t len) @@ -141,33 +166,17 @@ void print_hex_break(const uint8_t *data, const size_t len, uint8_t breaks) { } char *sprint_hex(const uint8_t *data, const size_t len) { + static char buf[1025] = {0}; - int maxLen = ( len > 1024/3) ? 1024/3 : len; - static char buf[1024]; - memset(buf, 0x00, 1024); - char *tmp = buf; - size_t i; - - for (i=0; i < maxLen; ++i, tmp += 3) - sprintf(tmp, "%02x ", (unsigned int) data[i]); + hex_to_buffer((uint8_t *)buf, data, len, sizeof(buf) - 1, 0, 1, false); return buf; } char *sprint_hex_inrow_ex(const uint8_t *data, const size_t len, const size_t min_str_len) { - - int maxLen = ( len > 1024/2) ? 1024/2 : len; - static char buf[1024] = {0}; - char *tmp = buf; - size_t i; + static char buf[1025] = {0}; - for (i = 0; i < maxLen; ++i, tmp += 2) - sprintf(tmp, "%02x", (unsigned int) data[i]); - - i *= 2; - int minStrLen = min_str_len > i ? min_str_len : 0; - for(; i < minStrLen; i++, tmp += 1) - sprintf(tmp, " "); + hex_to_buffer((uint8_t *)buf, data, len, sizeof(buf) - 1, min_str_len, 0, false); return buf; } diff --git a/client/util.h b/client/util.h index e9c48b03..fd7ceaff 100644 --- a/client/util.h +++ b/client/util.h @@ -12,6 +12,7 @@ #define UTIL_H__ #include +#include #include #ifndef ROTR @@ -35,6 +36,9 @@ extern void AddLogUint64(char *fileName, char *extData, const uint64_t data); extern void AddLogCurrentDT(char *fileName); extern void FillFileNameByUID(char *fileName, uint8_t * uid, char *ext, int byteCount); +extern void hex_to_buffer(const uint8_t *buf, const uint8_t *hex_data, const size_t hex_len, + const size_t hex_max_len, const size_t min_str_len, const size_t spaces_between, bool uppercase); + extern void print_hex(const uint8_t * data, const size_t len); extern char *sprint_hex(const uint8_t * data, const size_t len); extern char *sprint_hex_inrow(const uint8_t *data, const size_t len); From 87fc2dad14a1ab0103140c5041c0ae78cead5a52 Mon Sep 17 00:00:00 2001 From: merlokk Date: Sat, 25 Nov 2017 14:30:53 +0200 Subject: [PATCH 086/310] merged emvcore --- client/emv/emvcore.c | 342 ++++++++++++++++++++++--------------------- 1 file changed, 174 insertions(+), 168 deletions(-) diff --git a/client/emv/emvcore.c b/client/emv/emvcore.c index 5c737b05..825e9bf9 100644 --- a/client/emv/emvcore.c +++ b/client/emv/emvcore.c @@ -12,75 +12,96 @@ // Got from here. Thanks) // https://eftlab.co.uk/index.php/site-map/knowledge-base/211-emv-aid-rid-pix -const char *PSElist [] = { +static const char *PSElist [] = { "325041592E5359532E4444463031", // 2PAY.SYS.DDF01 - Visa Proximity Payment System Environment - PPSE "315041592E5359532E4444463031" // 1PAY.SYS.DDF01 - Visa Payment System Environment - PSE }; -const size_t PSElistLen = sizeof(PSElist)/sizeof(char*); +static const size_t PSElistLen = sizeof(PSElist)/sizeof(char*); -const char *AIDlist [] = { +typedef struct { + enum CardPSVendor vendor; + const char* aid; +} TAIDList; + +static const TAIDList AIDlist [] = { // Visa International - "A00000000305076010", // VISA ELO Credit - "A0000000031010", // VISA Debit/Credit (Classic) - "A0000000031010", // ddddddddddddddddddddddddddddddddddddddddddddddddddddddddd - "A000000003101001", // VISA Credit - "A000000003101002", // VISA Debit - "A0000000032010", // VISA Electron - "A0000000032020", // VISA - "A0000000033010", // VISA Interlink - "A0000000034010", // VISA Specific - "A0000000035010", // VISA Specific - "A0000000036010", // Domestic Visa Cash Stored Value - "A0000000036020", // International Visa Cash Stored Value - "A0000000038002", // VISA Auth, VisaRemAuthen EMV-CAP (DPA) - "A0000000038010", // VISA Plus - "A0000000039010", // VISA Loyalty - "A000000003999910", // VISA Proprietary ATM + { CV_VISA, "A00000000305076010"}, // VISA ELO Credit + { CV_VISA, "A0000000031010" }, // VISA Debit/Credit (Classic) + { CV_VISA, "A000000003101001" }, // VISA Credit + { CV_VISA, "A000000003101002" }, // VISA Debit + { CV_VISA, "A0000000032010" }, // VISA Electron + { CV_VISA, "A0000000032020" }, // VISA + { CV_VISA, "A0000000033010" }, // VISA Interlink + { CV_VISA, "A0000000034010" }, // VISA Specific + { CV_VISA, "A0000000035010" }, // VISA Specific + { CV_VISA, "A0000000036010" }, // Domestic Visa Cash Stored Value + { CV_VISA, "A0000000036020" }, // International Visa Cash Stored Value + { CV_VISA, "A0000000038002" }, // VISA Auth, VisaRemAuthen EMV-CAP (DPA) + { CV_VISA, "A0000000038010" }, // VISA Plus + { CV_VISA, "A0000000039010" }, // VISA Loyalty + { CV_VISA, "A000000003999910" }, // VISA Proprietary ATM // Visa USA - "A000000098", // Debit Card - "A0000000980848", // Debit Card + { CV_VISA, "A000000098" }, // Debit Card + { CV_VISA, "A0000000980848" }, // Debit Card // Mastercard International - "A00000000401", // MasterCard PayPass - "A0000000041010", // MasterCard Credit - "A00000000410101213", // MasterCard Credit - "A00000000410101215", // MasterCard Credit - "A0000000042010", // MasterCard Specific - "A0000000043010", // MasterCard Specific - "A0000000043060", // Maestro (Debit) - "A000000004306001", // Maestro (Debit) - "A0000000044010", // MasterCard Specific - "A0000000045010", // MasterCard Specific - "A0000000046000", // Cirrus - "A0000000048002", // SecureCode Auth EMV-CAP - "A0000000049999", // MasterCard PayPass + { CV_MASTERCARD, "A00000000401" }, // MasterCard PayPass + { CV_MASTERCARD, "A0000000041010" }, // MasterCard Credit + { CV_MASTERCARD, "A00000000410101213" }, // MasterCard Credit + { CV_MASTERCARD, "A00000000410101215" }, // MasterCard Credit + { CV_MASTERCARD, "A0000000042010" }, // MasterCard Specific + { CV_MASTERCARD, "A0000000043010" }, // MasterCard Specific + { CV_MASTERCARD, "A0000000043060" }, // Maestro (Debit) + { CV_MASTERCARD, "A000000004306001" }, // Maestro (Debit) + { CV_MASTERCARD, "A0000000044010" }, // MasterCard Specific + { CV_MASTERCARD, "A0000000045010" }, // MasterCard Specific + { CV_MASTERCARD, "A0000000046000" }, // Cirrus + { CV_MASTERCARD, "A0000000048002" }, // SecureCode Auth EMV-CAP + { CV_MASTERCARD, "A0000000049999" }, // MasterCard PayPass // American Express - "A000000025", - "A0000000250000", - "A00000002501", - "A000000025010402", - "A000000025010701", - "A000000025010801", + { CV_AMERICANEXPRESS, "A000000025" }, + { CV_AMERICANEXPRESS, "A0000000250000" }, + { CV_AMERICANEXPRESS, "A00000002501" }, + { CV_AMERICANEXPRESS, "A000000025010402" }, + { CV_AMERICANEXPRESS, "A000000025010701" }, + { CV_AMERICANEXPRESS, "A000000025010801" }, // Groupement des Cartes Bancaires "CB" - "A0000000421010", // Cartes Bancaire EMV Card - "A0000000422010", - "A0000000423010", - "A0000000424010", - "A0000000425010", + { CV_CB, "A0000000421010" }, // Cartes Bancaire EMV Card + { CV_CB, "A0000000422010" }, + { CV_CB, "A0000000423010" }, + { CV_CB, "A0000000424010" }, + { CV_CB, "A0000000425010" }, // JCB CO., LTD. - "A00000006510", // JCB - "A0000000651010", // JCB J Smart Credit - "A0000001544442", // Banricompras Debito - Banrisul - Banco do Estado do Rio Grande do SUL - S.A. - "F0000000030001", // BRADESCO - "A0000005241010", // RuPay - RuPay - "D5780000021010" // Bankaxept - Bankaxept + { CV_JCB, "A00000006510" }, // JCB + { CV_JCB, "A0000000651010" }, // JCB J Smart Credit + // Other + { CV_OTHER, "A0000001544442" }, // Banricompras Debito - Banrisul - Banco do Estado do Rio Grande do SUL - S.A. + { CV_OTHER, "F0000000030001" }, // BRADESCO + { CV_OTHER, "A0000005241010" }, // RuPay - RuPay + { CV_OTHER, "D5780000021010" } // Bankaxept - Bankaxept }; -const size_t AIDlistLen = sizeof(AIDlist)/sizeof(char*); +static const size_t AIDlistLen = sizeof(AIDlist)/sizeof(TAIDList); static bool APDULogging = false; void SetAPDULogging(bool logging) { APDULogging = logging; } +enum CardPSVendor GetCardPSVendor(uint8_t * AID, size_t AIDlen) { + char buf[100] = {0}; + if (AIDlen < 1) + return CV_NA; + + hex_to_buffer((uint8_t *)buf, AID, AIDlen, sizeof(buf) - 1, 0, 0, true); + + for(int i = 0; i < AIDlistLen; i ++) { + if (strncmp(AIDlist[i].aid, buf, strlen(AIDlist[i].aid)) == 0){ + return AIDlist[i].vendor; + } + } + + return CV_NA; +} + static bool print_cb(void *data, const struct tlv *tlv, int level, bool is_leaf) { emv_tag_dump(tlv, stdout, level); if (is_leaf) { @@ -103,11 +124,15 @@ void TLVPrintFromBuffer(uint8_t *data, int datalen) { } } -void TLVPrintFromTLV(struct tlvdb *tlv) { +void TLVPrintFromTLVLev(struct tlvdb *tlv, int level) { if (!tlv) return; - tlvdb_visit(tlv, print_cb, NULL, 0); + tlvdb_visit(tlv, print_cb, NULL, level); +} + +void TLVPrintFromTLV(struct tlvdb *tlv) { + TLVPrintFromTLVLev(tlv, 0); } void TLVPrintAIDlistFromSelectTLV(struct tlvdb *tlv) { @@ -136,28 +161,86 @@ void TLVPrintAIDlistFromSelectTLV(struct tlvdb *tlv) { PrintAndLog("|------------------|--------|-------------------------|"); } +struct tlvdb *GetPANFromTrack2(const struct tlv *track2) { + char track2Hex[200] = {0}; + uint8_t PAN[100] = {0}; + int PANlen = 0; + char *tmp = track2Hex; -int EMVSelect(bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { + if (!track2) + return NULL; + + for (int i = 0; i < track2->len; ++i, tmp += 2) + sprintf(tmp, "%02x", (unsigned int)track2->value[i]); + + int posD = strchr(track2Hex, 'd') - track2Hex; + if (posD < 1) + return NULL; + + track2Hex[posD] = 0; + if (strlen(track2Hex) % 2) { + track2Hex[posD] = 'F'; + track2Hex[posD + 1] = '\0'; + } + + param_gethex_to_eol(track2Hex, 0, PAN, sizeof(PAN), &PANlen); + + return tlvdb_fixed(0x5a, PANlen, PAN); +} + +struct tlvdb *GetdCVVRawFromTrack2(const struct tlv *track2) { + char track2Hex[200] = {0}; + char dCVVHex[100] = {0}; + uint8_t dCVV[100] = {0}; + int dCVVlen = 0; + const int PINlen = 5; // must calculated from 9F67 MSD Offset but i have not seen this tag) + char *tmp = track2Hex; + + if (!track2) + return NULL; + + for (int i = 0; i < track2->len; ++i, tmp += 2) + sprintf(tmp, "%02x", (unsigned int)track2->value[i]); + + int posD = strchr(track2Hex, 'd') - track2Hex; + if (posD < 1) + return NULL; + + memset(dCVVHex, '0', 32); + // ATC + memcpy(dCVVHex + 0, track2Hex + posD + PINlen + 11, 4); + // PAN 5 hex + memcpy(dCVVHex + 4, track2Hex, 5); + // expire date + memcpy(dCVVHex + 9, track2Hex + posD + 1, 4); + // service code + memcpy(dCVVHex + 13, track2Hex + posD + 5, 3); + + param_gethex_to_eol(dCVVHex, 0, dCVV, sizeof(dCVV), &dCVVlen); + + return tlvdb_fixed(0x02, dCVVlen, dCVV); +} + +int EMVExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { uint8_t data[APDU_RES_LEN] = {0}; + *ResultLen = 0; if (sw) *sw = 0; uint16_t isw = 0; - // select APDU - data[0] = 0x00; - data[1] = 0xA4; - data[2] = 0x04; - data[3] = 0x00; - data[4] = AIDLen; - memcpy(&data[5], AID, AIDLen); - if (ActivateField) DropField(); + // COMPUTE APDU + memcpy(data, &apdu, 5); + if (apdu.data) + memcpy(&data[5], apdu.data, apdu.Lc); + if (APDULogging) - PrintAndLog(">>>> %s", sprint_hex(data, AIDLen + 6)); + PrintAndLog(">>>> %s", sprint_hex(data, 6 + apdu.Lc)); - int res = ExchangeAPDU14a(data, AIDLen + 6, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); + // 6 byes + data = INS + CLA + P1 + P2 + Lc + + Le + int res = ExchangeAPDU14a(data, 6 + apdu.Lc, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); if (APDULogging) PrintAndLog("<<<< %s", sprint_hex(Result, *ResultLen)); @@ -165,12 +248,7 @@ int EMVSelect(bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen if (res) { return res; } - - if (*ResultLen < 2) { - PrintAndLog("SELECT ERROR: returned %d bytes", *ResultLen); - return 5; - } - + *ResultLen -= 2; isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1]; if (sw) @@ -178,7 +256,7 @@ int EMVSelect(bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen if (isw != 0x9000) { if (APDULogging) - PrintAndLog("SELECT ERROR: [%4X] %s", isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xff)); + PrintAndLog("APDU(%02x%02x) ERROR: [%4X] %s", apdu.CLA, apdu.INS, isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xff)); return 5; } @@ -191,6 +269,14 @@ int EMVSelect(bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen return 0; } +int EMVExchange(bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { + return EMVExchangeEx(false, LeaveFieldON, apdu, Result, MaxResultLen, ResultLen, sw, tlv); +} + +int EMVSelect(bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { + return EMVExchangeEx(ActivateField, LeaveFieldON, (sAPDU){0x00, 0xa4, 0x04, 0x00, AIDLen, AID}, Result, MaxResultLen, ResultLen, sw, tlv); +} + int EMVSelectPSE(bool ActivateField, bool LeaveFieldON, uint8_t PSENum, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { uint8_t buf[APDU_AID_LEN] = {0}; *ResultLen = 0; @@ -291,7 +377,7 @@ int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvd int res = 0; int retrycnt = 0; for(int i = 0; i < AIDlistLen; i ++) { - param_gethex_to_eol(AIDlist[i], 0, aidbuf, sizeof(aidbuf), &aidlen); + param_gethex_to_eol(AIDlist[i].aid, 0, aidbuf, sizeof(aidbuf), &aidlen); res = EMVSelect((i == 0) ? ActivateField : false, (i == AIDlistLen - 1) ? LeaveFieldON : true, aidbuf, aidlen, data, sizeof(data), &datalen, &sw, tlv); // retry if error and not returned sw error if (res && res != 5) { @@ -305,7 +391,7 @@ int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvd } retrycnt = 0; - PrintAndLog("Retry failed [%s]. Skiped...", AIDlist[i]); + PrintAndLog("Retry failed [%s]. Skiped...", AIDlist[i].aid); } continue; } @@ -315,7 +401,7 @@ int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvd continue; if (decodeTLV){ - PrintAndLog("%s:", AIDlist[i]); + PrintAndLog("%s:", AIDlist[i].aid); TLVPrintFromBuffer(data, datalen); } } @@ -324,7 +410,7 @@ int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvd } int EMVSelectApplication(struct tlvdb *tlv, uint8_t *AID, size_t *AIDlen) { - // needs to check priority. 0x00 - highest + // check priority. 0x00 - highest int prio = 0xffff; *AIDlen = 0; @@ -363,101 +449,21 @@ int EMVSelectApplication(struct tlvdb *tlv, uint8_t *AID, size_t *AIDlen) { } int EMVGPO(bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { - uint8_t data[APDU_RES_LEN] = {0}; - *ResultLen = 0; - if (sw) *sw = 0; - uint16_t isw = 0; - - // GPO APDU - data[0] = 0x80; - data[1] = 0xA8; - data[2] = 0x00; - data[3] = 0x00; - data[4] = PDOLLen; - if (PDOL) - memcpy(&data[5], PDOL, PDOLLen); - - - if (APDULogging) - PrintAndLog(">>>> %s", sprint_hex(data, PDOLLen + 5)); - - int res = ExchangeAPDU14a(data, PDOLLen + 5, false, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); - - if (APDULogging) - PrintAndLog("<<<< %s", sprint_hex(Result, *ResultLen)); - - if (res) { - return res; - } - - if (*ResultLen < 2) { - PrintAndLog("GPO ERROR: returned %d bytes", *ResultLen); - return 5; - } - - *ResultLen -= 2; - isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1]; - if (sw) - *sw = isw; - - if (isw != 0x9000) { - if (APDULogging) - PrintAndLog("GPO ERROR: [%4X] %s", isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xff)); - return 5; - } - - // add to tlv tree - if (tlv) { - struct tlvdb *t = tlvdb_parse_multi(Result, *ResultLen); - tlvdb_add(tlv, t); - } - - return 0; + return EMVExchange(LeaveFieldON, (sAPDU){0x80, 0xa8, 0x00, 0x00, PDOLLen, PDOL}, Result, MaxResultLen, ResultLen, sw, tlv); } int EMVReadRecord(bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { - uint8_t data[10] = {0}; - *ResultLen = 0; - if (sw) *sw = 0; - uint16_t isw = 0; - - // read record APDU - data[0] = 0x00; - data[1] = 0xb2; - data[2] = SFIrec; - data[3] = (SFI << 3) | 0x04; - data[4] = 0; - - if (APDULogging) - PrintAndLog(">>>> %s", sprint_hex(data, 5)); - - int res = ExchangeAPDU14a(data, 5, false, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); - - if (APDULogging) - PrintAndLog("<<<< %s", sprint_hex(Result, *ResultLen)); - - if (res) { - return res; - } - - *ResultLen -= 2; - isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1]; - if (sw) - *sw = isw; - - if (isw != 0x9000) { - if (APDULogging) - PrintAndLog("Read record ERROR: [%4X] %s", isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xff)); - return 5; - } - - // add to tlv tree - if (tlv) { - struct tlvdb *t = tlvdb_parse_multi(Result, *ResultLen); - tlvdb_add(tlv, t); - } - - return 0; + return EMVExchange(LeaveFieldON, (sAPDU){0x00, 0xb2, SFIrec, (SFI << 3) | 0x04, 0, NULL}, Result, MaxResultLen, ResultLen, sw, tlv); } +int EMVAC(bool LeaveFieldON, uint8_t RefControl, uint8_t *CDOL, size_t CDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { + return EMVExchange(LeaveFieldON, (sAPDU){0x80, 0xae, RefControl, 0x00, CDOLLen, CDOL}, Result, MaxResultLen, ResultLen, sw, tlv); +} +int EMVGenerateChallenge(bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { + return EMVExchange(LeaveFieldON, (sAPDU){0x00, 0x84, 0x00, 0x00, 0x00, NULL}, Result, MaxResultLen, ResultLen, sw, tlv); +} + +int MSCComputeCryptoChecksum(bool LeaveFieldON, uint8_t *UDOL, uint8_t UDOLlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { + return EMVExchange(LeaveFieldON, (sAPDU){0x80, 0x2a, 0x8e, 0x80, UDOLlen, UDOL}, Result, MaxResultLen, ResultLen, sw, tlv); +} From 44128a0e9aa2e5845afbe6897b7bd3457ff76ab5 Mon Sep 17 00:00:00 2001 From: merlokk Date: Sat, 25 Nov 2017 14:40:23 +0200 Subject: [PATCH 087/310] Added `hf emv exec` - command for execute EMV transaction Added to `hf emv exec` MSD path for VISA and Mastercard and some other compatible EMV cards --- CHANGELOG.md | 2 ++ client/emv/cmdemv.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fabd04b0..644f6c0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,8 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added `hf emv` group of commands (Merlok) - Added `hf emv search` `hf emv pse` - commands for selection of EMV application (Merlok) - Added `hf emv select` - command for select EMV application (Merlok) +- Added `hf emv exec` - command for execute EMV transaction (Merlok) +- Added to `hf emv exec` MSD path for VISA and Mastercard and some other compatible EMV cards (Merlok) ## [3.0.1][2017-06-08] diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index 679c26ed..da444a26 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -405,8 +405,8 @@ int CmdHFEMVExec(const char *cmd) { PrintAndLog("\n* Init transaction parameters."); //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 -// TLV_ADD(0x9F66, "\x86\x00\x00\x00"); // MSD - TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC + TLV_ADD(0x9F66, "\x86\x00\x00\x00"); // MSD +// TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC // TLV_ADD(0x9F66, "\x8e\x00\x00\x00"); // CDA //9F02:(Amount, Authorised (Numeric)) len:6 TLV_ADD(0x9F02, "\x00\x00\x00\x00\x01\x00"); From 78528074b102f8f29d9608049a058c52bedf0b77 Mon Sep 17 00:00:00 2001 From: merlokk Date: Sat, 25 Nov 2017 21:17:56 +0200 Subject: [PATCH 088/310] optimization UDOL creation. does not affect on functionality. --- client/emv/cmdemv.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index da444a26..b188555e 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -629,23 +629,12 @@ int CmdHFEMVExec(const char *cmd) { PrintAndLog("ERROR: can't create UDOL TLV."); return 4; } - - size_t udol_data_tlv_data_len; - unsigned char *udol_data_tlv_data = tlv_encode(udol_data_tlv, &udol_data_tlv_data_len); - if (!udol_data_tlv_data) { - PrintAndLog("ERROR: can't create UDOL data."); - return 4; - } - // eliminate fake tag - udol_data_tlv_data_len -= 2; - udol_data_tlv_data += 2; - - PrintAndLog("UDOL data[%d]: %s", udol_data_tlv_data_len, sprint_hex(udol_data_tlv_data, udol_data_tlv_data_len)); + PrintAndLog("UDOL data[%d]: %s", udol_data_tlv->len, sprint_hex(udol_data_tlv->value, udol_data_tlv->len)); PrintAndLog("\n* Mastercard compute cryptographic checksum(UDOL)"); - res = MSCComputeCryptoChecksum(true, udol_data_tlv_data, udol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); + res = MSCComputeCryptoChecksum(true, (uint8_t *)udol_data_tlv->value, udol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); if (res) { PrintAndLog("ERROR Compute Crypto Checksum. APDU error %4x", sw); return 5; From 1ccdd4f631f751bda0ddb899f1e32fb13d2b7faa Mon Sep 17 00:00:00 2001 From: Qais Patankar Date: Sun, 26 Nov 2017 04:41:24 +0000 Subject: [PATCH 089/310] Make README file markdown --- README.txt => README.md | 55 ++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 37 deletions(-) rename README.txt => README.md (66%) diff --git a/README.txt b/README.md similarity index 66% rename from README.txt rename to README.md index c8cac497..7258f918 100644 --- a/README.txt +++ b/README.md @@ -1,56 +1,37 @@ -NOTICE: -(2014-03-26) -This is now the official Proxmark repository! - -INTRODUCTION: +# proxmark3: the official Proxmark repository! The proxmark3 is a powerful general purpose RFID tool, the size of a deck of cards, designed to snoop, listen and emulate everything from -Low Frequency (125kHz) to High Frequency (13.56MHz) tags. +**Low Frequency (125kHz)** to **High Frequency (13.56MHz)** tags. This repository contains enough software, logic (for the FPGA), and design documentation for the hardware that you could, at least in theory, do something useful with a proxmark3. -RESOURCES: +## Resources - * This repository! - https://github.com/Proxmark/proxmark3 - - * The Wiki - https://github.com/Proxmark/proxmark3/wiki - - * The GitHub page - http://proxmark.github.io/proxmark3/ - - * The Forum - http://www.proxmark.org/forum - - * The IRC chanel - irc.freenode.org #proxmark3 - -or- - http://webchat.freenode.net/?channels=#proxmark3 - - * The Homebrew formula repository - https://github.com/Proxmark/homebrew-proxmark3 +* [This repository!](https://github.com/Proxmark/proxmark3) +* [The Wiki](https://github.com/Proxmark/proxmark3/wiki) +* [The GitHub Pages website](http://proxmark.github.io/proxmark3/) +* [The Forum](http://www.proxmark.org/forum) +* The IRC channel: irc.freenode.org #proxmark3 ([chat in your browser](http://webchat.freenode.net/?channels=#proxmark3)) +* [The Homebrew formula repository](https://github.com/Proxmark/homebrew-proxmark3) -DEVELOPMENT: +## Development The tools required to build or run the project will vary depending on -your operating system. Please refer to the Wiki for details. +your operating system. Please refer to [the wiki](https://github.com/Proxmark/proxmark3/wiki) for details. - * https://github.com/Proxmark/proxmark3/wiki - -OBTAINING HARDWARE: +## Obtaining hardware The Proxmark3 is available for purchase (assembled and tested) from the following locations: - * https://proxmark3.com/ - RyscCorp (us) - * http://www.elechouse.com/ - Elechouse (HK) - * https://lab401.com/ - Lab401 (FR) - * http://www.rfxsecure.com/ - RFxSecure (SG) - * http://proxmark3.tictail.com/ - IceSQL (SE) +* [RyscCorp](https://proxmark3.com/) (US) +* [Elechouse](http://www.elechouse.com/) (HK) +* [Lab401](https://lab401.com/) (FR) +* [RFxSecure](http://www.rfxsecure.com/) (SG) +* [IceSQL](http://proxmark3.tictail.com/) (SE) Most of the ultra-low-volume contract assemblers could put something like this together with a reasonable yield. A run of around @@ -70,7 +51,7 @@ The printed circuit board artwork is also available, as Gerbers and an Excellon drill file. -LICENSING: +## License This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by From f380317220a8326545c528f5a054238d54b9046c Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Tue, 28 Nov 2017 21:22:29 +0100 Subject: [PATCH 090/310] Change USB Configuration to "Bus Powered" --- common/usb_cdc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/usb_cdc.c b/common/usb_cdc.c index 3d17f81c..348b97e9 100644 --- a/common/usb_cdc.c +++ b/common/usb_cdc.c @@ -77,8 +77,8 @@ static const char cfgDescriptor[] = { 0x02, // CbNumInterfaces 0x01, // CbConfigurationValue 0x00, // CiConfiguration - 0xC0, // CbmAttributes (Self Powered - for those with a battery) - 0x4B, // CMaxPower (150mA max current drawn from bus without battery) + 0x80, // CbmAttributes (Bus Powered) + 0x4B, // CMaxPower (150mA max current drawn from bus) /* Interface 0 Descriptor: Communication Class Interface */ 0x09, // bLength From dc93020701a50f1b593be08ccf0efa391670ce0f Mon Sep 17 00:00:00 2001 From: Iceman Date: Thu, 30 Nov 2017 12:46:40 +0100 Subject: [PATCH 091/310] Update fpgaloader.c (#500) This address part1 in issue https://github.com/Proxmark/proxmark3/issues/499 Device power out 13.56Mhz when started (initialised). This output is not stopped until arbitary command is sent. --- armsrc/fpgaloader.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/armsrc/fpgaloader.c b/armsrc/fpgaloader.c index 1deb5b7d..c0b04f3c 100644 --- a/armsrc/fpgaloader.c +++ b/armsrc/fpgaloader.c @@ -434,6 +434,9 @@ void FpgaDownloadAndGo(int bitstream_version) inflateEnd(&compressed_fpga_stream); + // turn off antenna + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + // free eventually allocated BigBuf memory BigBuf_free(); BigBuf_Clear_ext(false); } From 606eddca8dd9e16e8752146bd6a42f812e42b593 Mon Sep 17 00:00:00 2001 From: merlokk Date: Thu, 30 Nov 2017 19:34:20 +0200 Subject: [PATCH 092/310] added hint on `hf 14a apdu`. close #495 --- client/cmdhf14a.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index a0f3ce57..8064724c 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -725,6 +725,7 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea return 0; } +// ISO14443-4. 7. Half-duplex block transmission protocol int CmdHF14AAPDU(const char *cmd) { uint8_t data[USB_CMD_DATA_SIZE]; int datalen = 0; @@ -734,6 +735,7 @@ int CmdHF14AAPDU(const char *cmd) { if (strlen(cmd) < 2) { PrintAndLog("Usage: hf 14a apdu [-s] [-k] [-t] "); + PrintAndLog("Command sends an ISO 7816-4 APDU via ISO 14443-4 block transmission protocol (T=CL)"); PrintAndLog(" -s activate field and select card"); PrintAndLog(" -k leave the signal field ON after receive response"); PrintAndLog(" -t executes TLV decoder if it possible. TODO!!!!"); @@ -1019,7 +1021,7 @@ static command_t CommandTable[] = {"cuids", CmdHF14ACUIDs, 0, " Collect n>0 ISO14443 Type A UIDs in one go"}, {"sim", CmdHF14ASim, 0, " -- Simulate ISO 14443a tag"}, {"snoop", CmdHF14ASnoop, 0, "Eavesdrop ISO 14443 Type A"}, - {"apdu", CmdHF14AAPDU, 0, "Send ISO 14443-4 APDU to tag"}, + {"apdu", CmdHF14AAPDU, 0, "Send an ISO 7816-4 APDU via ISO 14443-4 block transmission protocol"}, {"raw", CmdHF14ACmdRaw, 0, "Send raw hex data to tag"}, {NULL, NULL, 0, NULL} }; From b35c11f95e563899956ba1ac14f2ea2576453e3c Mon Sep 17 00:00:00 2001 From: merlokk Date: Fri, 1 Dec 2017 18:49:31 +0200 Subject: [PATCH 093/310] init travis from https://github.com/pwpiwi/proxmark3/tree/travis_test --- travis.yml | 48 ++++++++++++++++++++++++++++++++++++++++ travis_test_commands.scr | 2 ++ 2 files changed, 50 insertions(+) create mode 100644 travis.yml create mode 100644 travis_test_commands.scr diff --git a/travis.yml b/travis.yml new file mode 100644 index 00000000..f9d505b9 --- /dev/null +++ b/travis.yml @@ -0,0 +1,48 @@ +# Travis-CI config +language: c + +compiler: gcc + +# Test on Linux and MacOS +matrix: + include: + - os: osx + osx_image: xcode7.3 # OS X 10.11 + - os: osx + osx_image: xcode8 # OS X 10.11 + - os: osx + osx_image: xcode8.1 # OS X 10.12 + - os: osx + osx_image: xcode8.3 # OS X 10.12 + - os: linux + dist: trusty + sudo: required + +before_install: +## Install ARM toolchain on Linux. +## add our homebrew tap for MacOS +## Note: all dependencies on MacOS should be resolved by the brew install command + if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then + sudo apt-get update -qq; + sudo apt-get install -y gcc-arm-none-eabi; + elif [[ "$TRAVIS_OS_NAME" == "osx" ]]; then + brew update; + brew tap pwpiwi/proxmark3; + fi + +install: + if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then + brew install -v --HEAD proxmark3; + elif [[ "$TRAVIS_OS_NAME" == "linux" ]]; then + make all; + fi + +before_script: + +script: +## for the time being we are satisfied if it can be build and then successfully started + if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then + proxmark3 /dev/notexists travis_test_commands.scr ; + elif [[ "$TRAVIS_OS_NAME" == "linux" ]]; then + ./client/proxmark3 /dev/notexists travis_test_commands.scr ; + fi diff --git a/travis_test_commands.scr b/travis_test_commands.scr new file mode 100644 index 00000000..0dba3526 --- /dev/null +++ b/travis_test_commands.scr @@ -0,0 +1,2 @@ +hf mf hardnested t 1 000000000000 +exit From b6c370f1714a5731cc438b4d9f2dfd90684c875e Mon Sep 17 00:00:00 2001 From: merlokk Date: Fri, 1 Dec 2017 18:54:54 +0200 Subject: [PATCH 094/310] added REPOSITORY_EP var --- travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/travis.yml b/travis.yml index f9d505b9..b52d1f65 100644 --- a/travis.yml +++ b/travis.yml @@ -1,4 +1,5 @@ # Travis-CI config +# variable REPOSITORY_EP must be filled with repository name. as sample: "merlokk/proxmark3" language: c compiler: gcc @@ -27,7 +28,7 @@ before_install: sudo apt-get install -y gcc-arm-none-eabi; elif [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; - brew tap pwpiwi/proxmark3; + brew tap "$REPOSITORY_EP"; fi install: From 755c72b798438b795aa15688b9a3d5ac92487818 Mon Sep 17 00:00:00 2001 From: merlokk Date: Fri, 1 Dec 2017 18:57:31 +0200 Subject: [PATCH 095/310] rename travis file --- travis.yml => .travis.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename travis.yml => .travis.yml (100%) diff --git a/travis.yml b/.travis.yml similarity index 100% rename from travis.yml rename to .travis.yml From 2e35aa3aa7d09cf2caa85a5a9f2b6cdfc74afb87 Mon Sep 17 00:00:00 2001 From: merlokk Date: Sat, 2 Dec 2017 11:51:12 +0200 Subject: [PATCH 096/310] added info --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index b52d1f65..22f6ed7b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,6 +33,7 @@ before_install: install: if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then + brew info proxmark3; brew install -v --HEAD proxmark3; elif [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make all; From a36b278fadd47997b4d8e69dfeb4a126aa1932ee Mon Sep 17 00:00:00 2001 From: merlokk Date: Sat, 2 Dec 2017 13:06:51 +0200 Subject: [PATCH 097/310] add options show --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 22f6ed7b..ecff0810 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,6 +34,7 @@ before_install: install: if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew info proxmark3; + brew options proxmark3; brew install -v --HEAD proxmark3; elif [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make all; From 3c9b6573ecb23e7888e8077bc980de0ade361afa Mon Sep 17 00:00:00 2001 From: merlokk Date: Sat, 2 Dec 2017 16:24:47 +0200 Subject: [PATCH 098/310] remove show options --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ecff0810..22f6ed7b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,7 +34,6 @@ before_install: install: if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew info proxmark3; - brew options proxmark3; brew install -v --HEAD proxmark3; elif [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make all; From 245a6b208328a0a1df819af53b182d720b3e2196 Mon Sep 17 00:00:00 2001 From: merlokk Date: Sat, 2 Dec 2017 16:41:39 +0200 Subject: [PATCH 099/310] added xcode 9 and 9.1 coz 8 and 8.1 links to 8.3 --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 22f6ed7b..eed0f8e5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,12 +9,12 @@ matrix: include: - os: osx osx_image: xcode7.3 # OS X 10.11 - - os: osx - osx_image: xcode8 # OS X 10.11 - - os: osx - osx_image: xcode8.1 # OS X 10.12 - os: osx osx_image: xcode8.3 # OS X 10.12 + - os: osx + osx_image: xcode9 # OS X 10.13 + - os: osx + osx_image: xcode9.1 # OS X 10.13.1 - os: linux dist: trusty sudo: required From 10d4f823385cda5adfc8a3e0e07b761447aa5ec6 Mon Sep 17 00:00:00 2001 From: merlokk Date: Sat, 2 Dec 2017 21:47:30 +0200 Subject: [PATCH 100/310] transactions MSD and M/Chip, qVSDC works --- client/emv/cmdemv.c | 141 +++++++++++++++++++++++++++++++++++++----- client/emv/emv_tags.c | 53 +++++++++++++++- client/emv/emvcore.c | 32 +++++++++- client/emv/emvcore.h | 19 ++++++ 4 files changed, 226 insertions(+), 19 deletions(-) diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index b188555e..268d9e63 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -283,6 +283,10 @@ int UsageCmdHFEMVExec(void) { PrintAndLog(" -a : show APDU reqests and responses\n"); PrintAndLog(" -t : TLV decode results\n"); PrintAndLog(" -f : force search AID. Search AID instead of execute PPSE.\n"); + PrintAndLog(" -v : transaction type - qVSDC or M/Chip.\n"); + PrintAndLog(" -c : transaction type - qVSDC or M/Chip plus CDA (SDAD generation).\n"); + PrintAndLog(" -x : transaction type - VSDC. For test only. Not a standart behavior.\n"); + PrintAndLog("By default : transaction type - MSD.\n"); PrintAndLog("Samples:"); PrintAndLog(" hf emv pse -s -> select card"); PrintAndLog(" hf emv pse -s -t -a -> select card, show responses in TLV, show APDU"); @@ -296,6 +300,7 @@ int CmdHFEMVExec(const char *cmd) { bool showAPDU = false; bool decodeTLV = false; bool forceSearch = false; + enum TransactionType TrType = TT_MSD; uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; @@ -335,6 +340,18 @@ int CmdHFEMVExec(const char *cmd) { case 'F': forceSearch = true; break; + case 'x': + case 'X': + TrType = TT_VSDC; + break; + case 'v': + case 'V': + TrType = TT_QVSDCMCHIP; + break; + case 'c': + case 'C': + TrType = TT_CDA; + break; default: PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); return 1; @@ -405,9 +422,24 @@ int CmdHFEMVExec(const char *cmd) { PrintAndLog("\n* Init transaction parameters."); //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 - TLV_ADD(0x9F66, "\x86\x00\x00\x00"); // MSD -// TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC -// TLV_ADD(0x9F66, "\x8e\x00\x00\x00"); // CDA + switch(TrType) { + case TT_MSD: + TLV_ADD(0x9F66, "\x86\x00\x00\x00"); // MSD + break; + // not standart for contactless. just for test. + case TT_VSDC: + TLV_ADD(0x9F66, "\x46\x00\x00\x00"); // VSDC + break; + case TT_QVSDCMCHIP: + TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC + break; + case TT_CDA: + TLV_ADD(0x9F66, "\x86\x80\x00\x00"); // CDA + break; + default: + TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC + break; + } //9F02:(Amount, Authorised (Numeric)) len:6 TLV_ADD(0x9F02, "\x00\x00\x00\x00\x01\x00"); //9F1A:(Terminal Country Code) len:2 @@ -444,6 +476,7 @@ int CmdHFEMVExec(const char *cmd) { PrintAndLog("\n* GPO."); res = EMVGPO(true, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); + free(pdol_data_tlv_data); free(pdol_data_tlv); if (res) { @@ -544,13 +577,27 @@ int CmdHFEMVExec(const char *cmd) { break; } - // transaction check + // get AIP const struct tlv *AIPtlv = tlvdb_get(tlvRoot, 0x82, NULL); uint16_t AIP = AIPtlv->value[0] + AIPtlv->value[1] * 0x100; - PrintAndLog("* * AIP=%x", AIP); + PrintAndLog("* * AIP=%04x", AIP); + // SDA + if (AIP & 0x0040) { + PrintAndLog("\n* SDA"); + trSDA(AID, AIDlen, tlvRoot); + } + + // DDA + if (AIP & 0x0020) { + PrintAndLog("\n* DDA"); + + } + + // transaction check + // qVSDC - { + if (TrType == TT_QVSDCMCHIP|| TrType == TT_CDA){ // 9F26: Application Cryptogram const struct tlv *AC = tlvdb_get(tlvRoot, 0x9F26, NULL); if (AC) { @@ -587,19 +634,88 @@ int CmdHFEMVExec(const char *cmd) { } } - // TODO: Mastercard M/CHIP - { + // Mastercard M/CHIP + if (GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD && (TrType == TT_QVSDCMCHIP || TrType == TT_CDA)){ const struct tlv *CDOL1 = tlvdb_get(tlvRoot, 0x8c, NULL); if (CDOL1 && GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD) { // and m/chip transaction flag + PrintAndLog("\n--> Mastercard M/Chip transaction."); + + PrintAndLog("* * Generate challenge"); + res = EMVGenerateChallenge(true, buf, sizeof(buf), &len, &sw, tlvRoot); + if (res) { + PrintAndLog("ERROR GetChallenge. APDU error %4x", sw); + return 5; + } + if (len < 4) { + PrintAndLog("ERROR GetChallenge. Wrong challenge length %d", len); + return 5; + } + + // ICC Dynamic Number + struct tlvdb * ICCDynN = tlvdb_fixed(0x9f4c, len, buf); + tlvdb_add(tlvRoot, ICCDynN); + if (decodeTLV){ + PrintAndLog("\n* * ICC Dynamic Number:"); + TLVPrintFromTLV(ICCDynN); + } + + PrintAndLog("* * Calc CDOL1"); + struct tlv *cdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x8c, NULL), tlvRoot, 0x01); // 0x01 - dummy tag + if (!cdol_data_tlv){ + PrintAndLog("ERROR: can't create CDOL1 TLV."); + return 4; + } + PrintAndLog("CDOL1 data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len)); + + PrintAndLog("* * AC1"); + // EMVAC_TC + EMVAC_CDAREQ --- to get SDAD + res = EMVAC(true, (TrType == TT_CDA) ? EMVAC_TC + EMVAC_CDAREQ : EMVAC_TC, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); + + free(cdol_data_tlv); + + if (res) { + PrintAndLog("AC1 error(%d): %4x. Exit...", res, sw); + return 5; + } + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + PrintAndLog("* M/Chip transaction result:"); + // 9F27: Cryptogram Information Data (CID) + const struct tlv *CID = tlvdb_get(tlvRoot, 0x9F27, NULL); + if (CID) { + emv_tag_dump(CID, stdout, 0); + PrintAndLog("------------------------------"); + if (CID->len > 0) { + switch(CID->value[0] & EMVAC_AC_MASK){ + case EMVAC_AAC: + PrintAndLog("Transaction DECLINED."); + break; + case EMVAC_TC: + PrintAndLog("Transaction approved OFFLINE."); + break; + case EMVAC_ARQC: + PrintAndLog("Transaction approved ONLINE."); + break; + default: + PrintAndLog("ERROR: CID transaction code error %2x", CID->value[0] & EMVAC_AC_MASK); + break; + } + } else { + PrintAndLog("ERROR: Wrong CID length %d", CID->len); + } + } else { + PrintAndLog("ERROR: CID(9F27) not found."); + } } } // MSD - if (AIP & 0x8000) { + if (AIP & 0x8000 && TrType == TT_MSD) { PrintAndLog("\n--> MSD transaction."); - PrintAndLog("* MSD dCVV path. Check dCVV"); const struct tlv *track2 = tlvdb_get(tlvRoot, 0x57, NULL); @@ -624,7 +740,7 @@ int CmdHFEMVExec(const char *cmd) { if (!UDOL) PrintAndLog("Use default UDOL."); - struct tlv *udol_data_tlv = dol_process(UDOL ? UDOL : &defUDOL, tlvRoot, 0x01); // 0x01 - fake tag! + struct tlv *udol_data_tlv = dol_process(UDOL ? UDOL : &defUDOL, tlvRoot, 0x01); // 0x01 - dummy tag if (!udol_data_tlv){ PrintAndLog("ERROR: can't create UDOL TLV."); return 4; @@ -651,9 +767,6 @@ int CmdHFEMVExec(const char *cmd) { } } - // additional contacless EMV commands (fDDA, external authenticate) - - // DropField DropField(); diff --git a/client/emv/emv_tags.c b/client/emv/emv_tags.c index a69d7196..1c5cbb9d 100644 --- a/client/emv/emv_tags.c +++ b/client/emv/emv_tags.c @@ -35,6 +35,7 @@ enum emv_tag_t { EMV_TAG_NUMERIC, EMV_TAG_YYMMDD, EMV_TAG_CVR, + EMV_TAG_CID, }; struct emv_tag { @@ -59,7 +60,7 @@ static const struct emv_tag_bit EMV_AIP[] = { { EMV_BIT(1, 4), "Terminal risk management is to be performed" }, { EMV_BIT(1, 3), "Issuer authentication is supported" }, { EMV_BIT(1, 2), "Reserved for use by the EMV Contactless Specifications" }, - { EMV_BIT(1, 1), "CDA supported" }, + { EMV_BIT(1, 1), "CDA supported (Combined Dynamic Data Authentication / Application Cryptogram Generation)" }, { EMV_BIT(2, 8), "MSD is supported (Magnetic Stripe Data)" }, { EMV_BIT(2, 7), "Reserved for use by the EMV Contactless Specifications" }, { EMV_BIT(2, 6), "Reserved for use by the EMV Contactless Specifications" }, @@ -233,7 +234,7 @@ static const struct emv_tag emv_tags[] = { { 0x9f1f, "Track 1 Discretionary Data", EMV_TAG_STRING }, { 0x9f21, "Transaction Time" }, { 0x9f26, "Application Cryptogram" }, - { 0x9f27, "Cryptogram Information Data" }, + { 0x9f27, "Cryptogram Information Data", EMV_TAG_CID }, { 0x9f2a, "Kernel Identifier" }, { 0x9f2d, "ICC PIN Encipherment Public Key Certificate" }, { 0x9f2e, "ICC PIN Encipherment Public Key Exponent" }, @@ -471,6 +472,50 @@ static void emv_tag_dump_cvr(const struct tlv *tlv, const struct emv_tag *tag, F return; } +// EMV Book 3 +static void emv_tag_dump_cid(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) { + if (!tlv || tlv->len < 1) { + PRINT_INDENT(level); + fprintf(f, "\tINVALID!\n"); + return; + } + + PRINT_INDENT(level); + if ((tlv->value[0] & 0xC0) == 0x00) fprintf(f, "\tAC1: AAC (Transaction declined)\n"); + if ((tlv->value[0] & 0xC0) == 0x40) fprintf(f, "\tAC1: TC (Transaction approved)\n"); + if ((tlv->value[0] & 0xC0) == 0x80) fprintf(f, "\tAC1: ARQC (Online authorisation requested)\n"); + if ((tlv->value[0] & 0xC0) == 0xC0) fprintf(f, "\tAC1: RFU\n"); + + if ((tlv->value[0] & 0x08) != 0x00) { + PRINT_INDENT(level); + fprintf(f, "\tAdvice required!\n"); + } + + if ((tlv->value[0] & 0x07) != 0x00) { + PRINT_INDENT(level); + fprintf(f, "\tReason/advice/referral code: "); + switch((tlv->value[0] & 0x07)) { + case 0: + fprintf(f, "No information given\n"); + break; + case 1: + fprintf(f, "Service not allowed\n"); + break; + case 2: + fprintf(f, "PIN Try Limit exceeded\n"); + break; + case 3: + fprintf(f, "Issuer authentication failed\n"); + break; + default: + fprintf(f, "\tRFU: %2x\n", (tlv->value[0] & 0x07)); + break; + } + } + + return; +} + static void emv_tag_dump_cvm_list(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) { uint32_t X, Y; @@ -628,6 +673,10 @@ bool emv_tag_dump(const struct tlv *tlv, FILE *f, int level) fprintf(f, "\n"); emv_tag_dump_cvr(tlv, tag, f, level); break; + case EMV_TAG_CID: + fprintf(f, "\n"); + emv_tag_dump_cid(tlv, tag, f, level); + break; }; return true; diff --git a/client/emv/emvcore.c b/client/emv/emvcore.c index 825e9bf9..36438413 100644 --- a/client/emv/emvcore.c +++ b/client/emv/emvcore.c @@ -242,13 +242,13 @@ int EMVExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, uint8_t *Re // 6 byes + data = INS + CLA + P1 + P2 + Lc + + Le int res = ExchangeAPDU14a(data, 6 + apdu.Lc, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); - if (APDULogging) - PrintAndLog("<<<< %s", sprint_hex(Result, *ResultLen)); - if (res) { return res; } + if (APDULogging) + PrintAndLog("<<<< %s", sprint_hex(Result, *ResultLen)); + *ResultLen -= 2; isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1]; if (sw) @@ -467,3 +467,29 @@ int EMVGenerateChallenge(bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen int MSCComputeCryptoChecksum(bool LeaveFieldON, uint8_t *UDOL, uint8_t UDOLlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { return EMVExchange(LeaveFieldON, (sAPDU){0x80, 0x2a, 0x8e, 0x80, UDOLlen, UDOL}, Result, MaxResultLen, ResultLen, sw, tlv); } + +// Authentication +int trSDA(uint8_t *AID, size_t AIDlen, struct tlvdb *tlv) { + if (AIDlen < 5) + return 1; + + // Get public key index (0x8F) + //int PubKeyIndx = 0; + + // Get public key from key storage + // GetPublicKey(AID(0..5), PubKeyIndx) + + // Processing of Issuer Public Key Certificate (0x90) + //Certificate = + + // check issuer public key certificate + + // Verification of Signed Static Application Data (SSAD) (0x93) + + // get 9F4A Static Data Authentication Tag List + + // set Data Auth Code (9F45) from SSAD + + return 0; +} + diff --git a/client/emv/emvcore.h b/client/emv/emvcore.h index 03d5c956..94c5d9b0 100644 --- a/client/emv/emvcore.h +++ b/client/emv/emvcore.h @@ -29,6 +29,20 @@ #define APDU_RES_LEN 260 #define APDU_AID_LEN 50 +// AC +# define EMVAC_AC_MASK 0xC0 +# define EMVAC_AAC 0x00 +# define EMVAC_TC 0x40 +# define EMVAC_ARQC 0x80 +# define EMVAC_CDAREQ 0x10 + +enum TransactionType { + TT_MSD, + TT_VSDC, // not standart for contactless!!!! + TT_QVSDCMCHIP, + TT_CDA, +}; + typedef struct { uint8_t CLA; uint8_t INS; @@ -69,8 +83,13 @@ extern int EMVSelectApplication(struct tlvdb *tlv, uint8_t *AID, size_t *AIDlen) // Get Processing Options extern int EMVGPO(bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); extern int EMVReadRecord(bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); +// AC +extern int EMVGenerateChallenge(bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); +extern int EMVAC(bool LeaveFieldON, uint8_t RefControl, uint8_t *CDOL, size_t CDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); // Mastercard int MSCComputeCryptoChecksum(bool LeaveFieldON, uint8_t *UDOL, uint8_t UDOLlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); +// Auth +extern int trSDA(uint8_t *AID, size_t AIDlen, struct tlvdb *tlv); #endif From b0c73387f0cd76d921ea2102a63462aad66008e8 Mon Sep 17 00:00:00 2001 From: merlokk Date: Sat, 2 Dec 2017 22:10:25 +0200 Subject: [PATCH 101/310] added if --- .travis.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index eed0f8e5..94030db5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,7 +28,11 @@ before_install: sudo apt-get install -y gcc-arm-none-eabi; elif [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; - brew tap "$REPOSITORY_EP"; + if [[ "$REPOSITORY_EP" == "" ]]; then + brew tap proxmark/proxmark3; + else + brew tap "$REPOSITORY_EP"; + fi fi install: From 1e431c5d360fc1fde456c0dac3fb5c6085826aff Mon Sep 17 00:00:00 2001 From: merlokk Date: Sat, 2 Dec 2017 22:12:41 +0200 Subject: [PATCH 102/310] get rid of tabs) --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 94030db5..918240c7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,11 +28,11 @@ before_install: sudo apt-get install -y gcc-arm-none-eabi; elif [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; - if [[ "$REPOSITORY_EP" == "" ]]; then + if [[ "$REPOSITORY_EP" == "" ]]; then brew tap proxmark/proxmark3; - else + else brew tap "$REPOSITORY_EP"; - fi + fi fi install: From f7ec230e985e85f75065e914fe3968b801b18666 Mon Sep 17 00:00:00 2001 From: merlokk Date: Sun, 3 Dec 2017 20:28:36 +0200 Subject: [PATCH 103/310] added a tag --- client/emv/emv_tags.c | 1 + 1 file changed, 1 insertion(+) diff --git a/client/emv/emv_tags.c b/client/emv/emv_tags.c index 1c5cbb9d..89626cee 100644 --- a/client/emv/emv_tags.c +++ b/client/emv/emv_tags.c @@ -271,6 +271,7 @@ static const struct emv_tag emv_tags[] = { { 0x9f6c, "Card Transaction Qualifiers (CTQ)", EMV_TAG_BITMASK, &EMV_CTQ }, { 0xa5 , "File Control Information (FCI) Proprietary Template" }, { 0xbf0c, "File Control Information (FCI) Issuer Discretionary Data" }, + { 0xdf20, "Issuer Proprietary Bitmap (IPB)" }, }; static int emv_sort_tag(tlv_tag_t tag) From 5a1b25ac31a1436455e54e76b6525721eace24bf Mon Sep 17 00:00:00 2001 From: merlokk Date: Sun, 3 Dec 2017 20:36:04 +0200 Subject: [PATCH 104/310] added 9f0a tag --- client/emv/emv_tags.c | 1 + 1 file changed, 1 insertion(+) diff --git a/client/emv/emv_tags.c b/client/emv/emv_tags.c index 89626cee..80b4c29d 100644 --- a/client/emv/emv_tags.c +++ b/client/emv/emv_tags.c @@ -222,6 +222,7 @@ static const struct emv_tag emv_tags[] = { { 0x9f06, "Application Identifier (AID), Terminal. ISO 7816-5" }, { 0x9f07, "Application Usage Control", EMV_TAG_BITMASK, &EMV_AUC }, { 0x9f08, "Application Version Number" }, + { 0x9f0a, "Application Selection Registered Proprietary Data" }, // https://blog.ul-ts.com/posts/electronic-card-identifier-one-more-step-for-mif-compliance/ { 0x9f0d, "Issuer Action Code - Default", EMV_TAG_BITMASK, &EMV_TVR }, { 0x9f0e, "Issuer Action Code - Denial", EMV_TAG_BITMASK, &EMV_TVR }, { 0x9f0f, "Issuer Action Code - Online", EMV_TAG_BITMASK, &EMV_TVR }, From 7a7afeba5cbbb518216d0d1d5bb5228a41a4db17 Mon Sep 17 00:00:00 2001 From: merlokk Date: Mon, 4 Dec 2017 22:20:29 +0200 Subject: [PATCH 105/310] small refactoring --- client/emv/cmdemv.c | 2 +- client/emv/emv_tags.c | 16 ++++++++-------- client/emv/emv_tags.h | 11 +++++++++++ client/emv/emvcore.h | 7 ------- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index 268d9e63..92ae01f9 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -322,7 +322,7 @@ int CmdHFEMVExec(const char *cmd) { switch (param_getchar_indx(cmd, 1, cmdp)) { case 'h': case 'H': - UsageCmdHFEMVPPSE(); + UsageCmdHFEMVExec(); return 0; case 's': case 'S': diff --git a/client/emv/emv_tags.c b/client/emv/emv_tags.c index 80b4c29d..25ad9658 100644 --- a/client/emv/emv_tags.c +++ b/client/emv/emv_tags.c @@ -483,20 +483,20 @@ static void emv_tag_dump_cid(const struct tlv *tlv, const struct emv_tag *tag, F } PRINT_INDENT(level); - if ((tlv->value[0] & 0xC0) == 0x00) fprintf(f, "\tAC1: AAC (Transaction declined)\n"); - if ((tlv->value[0] & 0xC0) == 0x40) fprintf(f, "\tAC1: TC (Transaction approved)\n"); - if ((tlv->value[0] & 0xC0) == 0x80) fprintf(f, "\tAC1: ARQC (Online authorisation requested)\n"); - if ((tlv->value[0] & 0xC0) == 0xC0) fprintf(f, "\tAC1: RFU\n"); + if ((tlv->value[0] & EMVAC_AC_MASK) == EMVAC_AAC) fprintf(f, "\tAC1: AAC (Transaction declined)\n"); + if ((tlv->value[0] & EMVAC_AC_MASK) == EMVAC_TC) fprintf(f, "\tAC1: TC (Transaction approved)\n"); + if ((tlv->value[0] & EMVAC_AC_MASK) == EMVAC_ARQC) fprintf(f, "\tAC1: ARQC (Online authorisation requested)\n"); + if ((tlv->value[0] & EMVAC_AC_MASK) == EMVAC_AC_MASK) fprintf(f, "\tAC1: RFU\n"); - if ((tlv->value[0] & 0x08) != 0x00) { + if (tlv->value[0] & EMVCID_ADVICE) { PRINT_INDENT(level); fprintf(f, "\tAdvice required!\n"); } - if ((tlv->value[0] & 0x07) != 0x00) { + if (tlv->value[0] & EMVCID_REASON_MASK) { PRINT_INDENT(level); fprintf(f, "\tReason/advice/referral code: "); - switch((tlv->value[0] & 0x07)) { + switch((tlv->value[0] & EMVCID_REASON_MASK)) { case 0: fprintf(f, "No information given\n"); break; @@ -510,7 +510,7 @@ static void emv_tag_dump_cid(const struct tlv *tlv, const struct emv_tag *tag, F fprintf(f, "Issuer authentication failed\n"); break; default: - fprintf(f, "\tRFU: %2x\n", (tlv->value[0] & 0x07)); + fprintf(f, "\tRFU: %2x\n", (tlv->value[0] & EMVCID_REASON_MASK)); break; } } diff --git a/client/emv/emv_tags.h b/client/emv/emv_tags.h index 8dbd9e00..f6d44f01 100644 --- a/client/emv/emv_tags.h +++ b/client/emv/emv_tags.h @@ -19,6 +19,17 @@ #include "tlv.h" #include +// AC +# define EMVAC_AC_MASK 0xC0 +# define EMVAC_AAC 0x00 +# define EMVAC_TC 0x40 +# define EMVAC_ARQC 0x80 +# define EMVAC_CDAREQ 0x10 + +// CID +# define EMVCID_ADVICE 0x08 +# define EMVCID_REASON_MASK 0x07 + bool emv_tag_dump(const struct tlv *tlv, FILE *f, int level); #endif diff --git a/client/emv/emvcore.h b/client/emv/emvcore.h index 94c5d9b0..7df58850 100644 --- a/client/emv/emvcore.h +++ b/client/emv/emvcore.h @@ -29,13 +29,6 @@ #define APDU_RES_LEN 260 #define APDU_AID_LEN 50 -// AC -# define EMVAC_AC_MASK 0xC0 -# define EMVAC_AAC 0x00 -# define EMVAC_TC 0x40 -# define EMVAC_ARQC 0x80 -# define EMVAC_CDAREQ 0x10 - enum TransactionType { TT_MSD, TT_VSDC, // not standart for contactless!!!! From c656c870ae9a4ab52874f945748065227cdb1ad7 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Tue, 5 Dec 2017 08:25:10 +0100 Subject: [PATCH 106/310] fix: AVX512 detection --- client/Makefile | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/client/Makefile b/client/Makefile index 5df99de7..b9f5e78b 100644 --- a/client/Makefile +++ b/client/Makefile @@ -192,14 +192,7 @@ MULTIARCHOBJS = $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_NOSIMD.o) \ $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_AVX.o) \ $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_AVX2.o) -GCC_VERSION := $(shell gcc --version | awk '/gcc/{print $$NF;}' | sed -e 's/\.\([0-9][0-9]\)/\1/g' -e 's/\.\([0-9]\)/0\1/g' -e 's/^[0-9]\{3,4\}$$/&00/') -CLANG_VERSION := $(shell gcc --version | awk '/Apple LLVM version/{print $$4;}' | sed -e 's/\.\([0-9][0-9]\)/\1/g' -e 's/\.\([0-9]\)/0\1/g' -e 's/^[0-9]\{3,4\}$$/&00/') -ifneq ($(CLANG_VERSION), ) - SUPPORTS_AVX512 := $(shell [ $(CLANG_VERSION) -ge 80000 ] && echo "True" ) -endif -ifneq ($(GCC_VERSION), ) - SUPPORTS_AVX512 := $(shell [ $(GCC_VERSION) -ge 40900 ] && echo "True" ) -endif +SUPPORTS_AVX512 := $(shell echo | gcc -E -mavx512f - &> /dev/null && echo "True" ) HARD_SWITCH_NOSIMD = -mno-mmx -mno-sse2 -mno-avx -mno-avx2 HARD_SWITCH_MMX = -mmmx -mno-sse2 -mno-avx -mno-avx2 HARD_SWITCH_SSE2 = -mmmx -msse2 -mno-avx -mno-avx2 From 1ff75f168baaf43986c7886cba4fd98a6bb9aaac Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Wed, 6 Dec 2017 09:25:10 +0100 Subject: [PATCH 107/310] fixing the fix: older shells don't know about &> --- client/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/Makefile b/client/Makefile index b9f5e78b..ec593558 100644 --- a/client/Makefile +++ b/client/Makefile @@ -192,7 +192,7 @@ MULTIARCHOBJS = $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_NOSIMD.o) \ $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_AVX.o) \ $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_AVX2.o) -SUPPORTS_AVX512 := $(shell echo | gcc -E -mavx512f - &> /dev/null && echo "True" ) +SUPPORTS_AVX512 := $(shell echo | gcc -E -mavx512f - > /dev/null 2>&1 && echo "True" ) HARD_SWITCH_NOSIMD = -mno-mmx -mno-sse2 -mno-avx -mno-avx2 HARD_SWITCH_MMX = -mmmx -mno-sse2 -mno-avx -mno-avx2 HARD_SWITCH_SSE2 = -mmmx -msse2 -mno-avx -mno-avx2 From 6148817a7f7c7d934e41dd8ea0329bb97fee5112 Mon Sep 17 00:00:00 2001 From: ikarus Date: Wed, 6 Dec 2017 23:30:27 +0100 Subject: [PATCH 108/310] fix: usage and sample text of hf mf cwipe --- client/cmdhfmf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index eed6b34c..d9578af2 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -1985,8 +1985,8 @@ int CmdHF14AMfCWipe(const char *Cmd) bool fillCard = false; if (strlen(Cmd) < 1 || param_getchar(Cmd, 0) == 'h') { - PrintAndLog("Usage: hf mf cwipe [card size] [w] [p]"); - PrintAndLog("sample: hf mf cwipe 1 w s"); + PrintAndLog("Usage: hf mf cwipe [card size] [w] [f]"); + PrintAndLog("sample: hf mf cwipe 1 w f"); PrintAndLog("[card size]: 0 = 320 bytes (Mifare Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); PrintAndLog("w - Wipe magic Chinese card (only works with gen:1a cards)"); PrintAndLog("f - Fill the card with default data and keys (works with gen:1a and gen:1b cards only)"); From 7c7327e7c89c3640c5f23330ea5263829cea8b6f Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Sun, 10 Dec 2017 11:57:15 +0100 Subject: [PATCH 109/310] fix: hf mf hardnested failed with new WUPA timing * allow additional 2 ssp_frame cycles to detect SOF * use only respective functions to get/set iso14a_timeout * remove waiting time in MifareAcquireEncryptedNonces(). This is covered by GetATQA() now. --- armsrc/iso14443a.c | 10 +++++----- armsrc/mifarecmd.c | 10 +++------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index a0fe2326..0a9f7c03 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -189,13 +189,13 @@ void iso14a_set_trigger(bool enable) { void iso14a_set_timeout(uint32_t timeout) { - iso14a_timeout = timeout - (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER)/(16*8); + iso14a_timeout = timeout - (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER)/(16*8) + 2; if(MF_DBGLEVEL >= 3) Dbprintf("ISO14443A Timeout set to %ld (%dms)", timeout, timeout / 106); } uint32_t iso14a_get_timeout(void) { - return iso14a_timeout + (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER)/(16*8); + return iso14a_timeout + (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER)/(16*8) - 2; } //----------------------------------------------------------------------------- @@ -1955,9 +1955,9 @@ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data) { } else{ // S-Block WTX while((data_bytes[0] & 0xF2) == 0xF2) { - uint32_t save_iso14a_timeout = iso14a_timeout; + uint32_t save_iso14a_timeout = iso14a_get_timeout(); // temporarily increase timeout - iso14a_timeout = MAX((data_bytes[1] & 0x3f) * iso14a_timeout, MAX_ISO14A_TIMEOUT); + iso14a_set_timeout(MAX((data_bytes[1] & 0x3f) * save_iso14a_timeout, MAX_ISO14A_TIMEOUT)); // Transmit WTX back // byte1 - WTXM [1..59]. command FWT=FWT*WTXM data_bytes[1] = data_bytes[1] & 0x3f; // 2 high bits mandatory set to 0b @@ -1969,7 +1969,7 @@ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data) { len = ReaderReceive(data, parity); data_bytes = data; // restore timeout - iso14a_timeout = save_iso14a_timeout; + iso14a_set_timeout(save_iso14a_timeout); } // if we received an I- or R(ACK)-Block with a block number equal to the diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 00fd638c..fcfd7e8f 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -694,10 +694,9 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, continue; } - // send a dummy byte as reader response in order to trigger the cards authentication timeout - uint8_t dummy_answer = 0; - ReaderTransmit(&dummy_answer, 1, NULL); - timeout = GetCountSspClk() + AUTHENTICATION_TIMEOUT; + // send a dummy response in order to trigger the cards authentication failure timeout + uint8_t dummy_answer[8] = {0}; + ReaderTransmit(dummy_answer, 8, NULL); num_nonces++; if (num_nonces % 2) { @@ -710,9 +709,6 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, i += 9; } - // wait for the card to become ready again - while(GetCountSspClk() < timeout); - } LED_C_OFF(); From fa85b08504eda7045b4b7d50fee1b75c4f5e5dd2 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Mon, 11 Dec 2017 20:40:09 +0100 Subject: [PATCH 110/310] fix iso14a_set_timeout() adjustment --- armsrc/iso14443a.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 0a9f7c03..94ca52f5 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -189,13 +189,14 @@ void iso14a_set_trigger(bool enable) { void iso14a_set_timeout(uint32_t timeout) { - iso14a_timeout = timeout - (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER)/(16*8) + 2; + // adjust timeout by FPGA delays and 2 additional ssp_frames to detect SOF + iso14a_timeout = timeout + (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER)/(16*8) + 2; if(MF_DBGLEVEL >= 3) Dbprintf("ISO14443A Timeout set to %ld (%dms)", timeout, timeout / 106); } uint32_t iso14a_get_timeout(void) { - return iso14a_timeout + (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER)/(16*8) - 2; + return iso14a_timeout - (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER)/(16*8) - 2; } //----------------------------------------------------------------------------- From 678a3cb2feecf267d2956bec3ac69205c68400c5 Mon Sep 17 00:00:00 2001 From: Iceman Date: Fri, 15 Dec 2017 15:48:25 +0100 Subject: [PATCH 111/310] Update cmdhficlass.h add function head --- client/cmdhficlass.h | 1 + 1 file changed, 1 insertion(+) diff --git a/client/cmdhficlass.h b/client/cmdhficlass.h index 9a3adf50..3b99b019 100644 --- a/client/cmdhficlass.h +++ b/client/cmdhficlass.h @@ -34,6 +34,7 @@ int CmdHFiClassSnoop(const char *Cmd); int CmdHFiClassSim(const char *Cmd); int CmdHFiClassWriteKeyFile(const char *Cmd); int CmdHFiClass_WriteBlock(const char *Cmd); +int CmdHF14AMfChk(const char *Cmd); void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t endblock, size_t filesize); void HFiClassCalcDivKey(uint8_t *CSN, uint8_t *KEY, uint8_t *div_key, bool elite); #endif From 385c1a5ebc90f134d9856f491065476e8ef44741 Mon Sep 17 00:00:00 2001 From: Iceman Date: Fri, 15 Dec 2017 15:53:58 +0100 Subject: [PATCH 112/310] Update cmdhficlass.c ADD: 'hf iclass chk' - new command to check for keys from a dictionary file. hf iclass chk h hf iclass chk f default_iclass_keys.dic --- client/cmdhficlass.c | 138 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 135 insertions(+), 3 deletions(-) diff --git a/client/cmdhficlass.c b/client/cmdhficlass.c index 60713a01..37cf4dee 100644 --- a/client/cmdhficlass.c +++ b/client/cmdhficlass.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "iso14443crc.h" // Can also be used for iClass, using 0xE012 as CRC-type #include "data.h" #include "proxmark3.h" @@ -32,6 +33,7 @@ #include "protocols.h" #include "usb_cmd.h" #include "cmdhfmfu.h" +#include "util_posix.h" static int CmdHelp(const char *Cmd); @@ -51,6 +53,14 @@ typedef struct iclass_block { uint8_t d[8]; } iclass_block_t; +int usage_hf_iclass_chk(void) { + PrintAndLog("Usage: hf iclass chk [h] "); + PrintAndLog("Options:"); + PrintAndLog("h Show this help"); + PrintAndLog("f Dictionary file with default iclass keys"); + return 0; +} + int xorbits_8(uint8_t val) { uint8_t res = val ^ (val >> 1); //1st pass res = res ^ (res >> 1); // 2nd pass @@ -531,7 +541,7 @@ static bool select_and_auth(uint8_t *KEY, uint8_t *MAC, uint8_t *div_key, bool u memcpy(div_key, KEY, 8); else HFiClassCalcDivKey(CSN, KEY, div_key, elite); - PrintAndLog("Authing with %s: %02x%02x%02x%02x%02x%02x%02x%02x", rawkey ? "raw key" : "diversified key", div_key[0],div_key[1],div_key[2],div_key[3],div_key[4],div_key[5],div_key[6],div_key[7]); + if (verbose) PrintAndLog("Authing with %s: %02x%02x%02x%02x%02x%02x%02x%02x", rawkey ? "raw key" : "diversified key", div_key[0],div_key[1],div_key[2],div_key[3],div_key[4],div_key[5],div_key[6],div_key[7]); doMAC(CCNR, div_key, MAC); UsbCommand resp; @@ -541,12 +551,12 @@ static bool select_and_auth(uint8_t *KEY, uint8_t *MAC, uint8_t *div_key, bool u SendCommand(&d); if (!WaitForResponseTimeout(CMD_ACK,&resp,4500)) { - PrintAndLog("Auth Command execute timeout"); + if (verbose) PrintAndLog("Auth Command execute timeout"); return false; } uint8_t isOK = resp.arg[0] & 0xff; if (!isOK) { - PrintAndLog("Authentication error"); + if (verbose) PrintAndLog("Authentication error"); return false; } return true; @@ -1699,10 +1709,131 @@ int CmdHFiClassManageKeys(const char *Cmd) { return 0; } +int CmdHFiClassCheckKeys(const char *Cmd) { + + char ctmp = 0x00; + ctmp = param_getchar(Cmd, 0); + if (ctmp == 'h' || ctmp == 'H') return usage_hf_iclass_chk(); + + uint8_t mac[4] = {0x00,0x00,0x00,0x00}; + uint8_t key[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t div_key[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + // elite key, raw key, standard key + bool elite = false; + bool rawkey = false; + bool found_debit = false; + bool found_credit = false; + + FILE * f; + char filename[FILE_PATH_SIZE] = {0}; + char buf[17]; + uint8_t *keyBlock = NULL, *p; + int keyitems = 0, keycnt = 0; + + + // May be a dictionary file + if ( param_getstr(Cmd, 1, filename, sizeof(filename)) >= FILE_PATH_SIZE ) { + PrintAndLog("File name too long"); + free(keyBlock); + return 2; + } + + if ( !(f = fopen( filename , "r")) ) { + PrintAndLog("File: %s: not found or locked.", filename); + free(keyBlock); + return 1; + } + + while( fgets(buf, sizeof(buf), f) ){ + if (strlen(buf) < 16 || buf[15] == '\n') + continue; + + while (fgetc(f) != '\n' && !feof(f)) ; //goto next line + + if( buf[0]=='#' ) continue; //The line start with # is comment, skip + + if (!isxdigit(buf[0])){ + PrintAndLog("File content error. '%s' must include 16 HEX symbols",buf); + continue; + } + + buf[16] = 0; + + p = realloc(keyBlock, 8 * (keyitems += 64)); + if (!p) { + PrintAndLog("Cannot allocate memory for default keys"); + free(keyBlock); + fclose(f); + return 2; + } + keyBlock = p; + + memset(keyBlock + 8 * keycnt, 0, 8); + num_to_bytes(strtoull(buf, NULL, 16), 8, keyBlock + 8 * keycnt); + + //PrintAndLog("check key[%2d] %016" PRIx64, keycnt, bytes_to_num(keyBlock + 8*keycnt, 8)); + keycnt++; + memset(buf, 0, sizeof(buf)); + } + fclose(f); + PrintAndLog("Loaded %2d keys from %s", keycnt, filename); + + // time + uint64_t t1 = msclock(); + + for (uint32_t c = 0; c < keycnt; c += 1) { + printf("."); fflush(stdout); + if (ukbhit()) { + int gc = getchar(); (void)gc; + printf("\naborted via keyboard!\n"); + break; + } + + memcpy(key, keyBlock + 8 * c , 8); + + // debit key. try twice + for (int foo = 0; foo < 2 && !found_debit; foo++) { + if (!select_and_auth(key, mac, div_key, false, elite, rawkey, false)) + continue; + + // key found. + PrintAndLog("\n--------------------------------------------------------"); + PrintAndLog(" Found AA1 debit key\t\t[%s]", sprint_hex(key, 8)); + found_debit = true; + } + + // credit key. try twice + for (int foo = 0; foo < 2 && !found_credit; foo++) { + if (!select_and_auth(key, mac, div_key, true, elite, rawkey, false)) + continue; + + // key found + PrintAndLog("\n--------------------------------------------------------"); + PrintAndLog(" Found AA2 credit key\t\t[%s]", sprint_hex(key, 8)); + found_credit = true; + } + + // both keys found. + if ( found_debit && found_credit ) + break; + } + + t1 = msclock() - t1; + + PrintAndLog("\nTime in iclass checkkeys: %.0f seconds\n", (float)t1/1000.0); + + DropField(); + free(keyBlock); + PrintAndLog(""); + return 0; +} + static command_t CommandTable[] = { {"help", CmdHelp, 1, "This help"}, {"calcnewkey", CmdHFiClassCalcNewKey, 1, "[options..] Calc Diversified keys (blocks 3 & 4) to write new keys"}, + {"chk", CmdHFiClassCheckKeys, 0, " Check keys"}, {"clone", CmdHFiClassCloneTag, 0, "[options..] Authenticate and Clone from iClass bin file"}, {"decrypt", CmdHFiClassDecrypt, 1, "[f ] Decrypt tagdump" }, {"dump", CmdHFiClassReader_Dump, 0, "[options..] Authenticate and Dump iClass tag's AA1"}, @@ -1723,6 +1854,7 @@ static command_t CommandTable[] = int CmdHFiClass(const char *Cmd) { + clearCommandBuffer(); CmdsParse(CommandTable, Cmd); return 0; } From 909c40c835033ec7f373a83316954939897ea605 Mon Sep 17 00:00:00 2001 From: Iceman Date: Sat, 16 Dec 2017 17:08:45 +0100 Subject: [PATCH 113/310] Update cmdhficlass.h ...hm.. cut'nd'paste error... --- client/cmdhficlass.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/cmdhficlass.h b/client/cmdhficlass.h index 3b99b019..456bb1c3 100644 --- a/client/cmdhficlass.h +++ b/client/cmdhficlass.h @@ -34,7 +34,7 @@ int CmdHFiClassSnoop(const char *Cmd); int CmdHFiClassSim(const char *Cmd); int CmdHFiClassWriteKeyFile(const char *Cmd); int CmdHFiClass_WriteBlock(const char *Cmd); -int CmdHF14AMfChk(const char *Cmd); +int CmdHFiClassCheckKeys(const char *Cmd); void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t endblock, size_t filesize); void HFiClassCalcDivKey(uint8_t *CSN, uint8_t *KEY, uint8_t *div_key, bool elite); #endif From 5e4932e8466622070c26efcee815f5ada987333f Mon Sep 17 00:00:00 2001 From: Iceman Date: Sun, 17 Dec 2017 12:53:21 +0100 Subject: [PATCH 114/310] 'hf iclass chk' part3 (#525) added the options E - target elite keys R - interpret dictionary keys as raw (already diversified keys) --- client/cmdhficlass.c | 61 ++++++++++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/client/cmdhficlass.c b/client/cmdhficlass.c index 37cf4dee..d42f7eef 100644 --- a/client/cmdhficlass.c +++ b/client/cmdhficlass.c @@ -54,10 +54,16 @@ typedef struct iclass_block { } iclass_block_t; int usage_hf_iclass_chk(void) { - PrintAndLog("Usage: hf iclass chk [h] "); + PrintAndLog("Checkkeys loads a dictionary text file with 8byte hex keys to test authenticating against a iClass tag"); + PrintAndLog("Usage: hf iclass chk [h|e|r] "); PrintAndLog("Options:"); PrintAndLog("h Show this help"); PrintAndLog("f Dictionary file with default iclass keys"); + PrintAndLog(" e target Elite / High security key scheme"); + PrintAndLog(" r interpret dictionary file as raw (diversified keys)"); + PrintAndLog("Samples:"); + PrintAndLog(" hf iclass chk f default_iclass_keys.dic"); + PrintAndLog(" hf iclass chk f default_iclass_keys.dic e"); return 0; } @@ -1711,37 +1717,58 @@ int CmdHFiClassManageKeys(const char *Cmd) { int CmdHFiClassCheckKeys(const char *Cmd) { - char ctmp = 0x00; - ctmp = param_getchar(Cmd, 0); - if (ctmp == 'h' || ctmp == 'H') return usage_hf_iclass_chk(); - uint8_t mac[4] = {0x00,0x00,0x00,0x00}; uint8_t key[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; uint8_t div_key[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; // elite key, raw key, standard key - bool elite = false; - bool rawkey = false; + bool use_elite = false; + bool use_raw = false; bool found_debit = false; bool found_credit = false; - + bool errors = false; + uint8_t cmdp = 0x00; FILE * f; char filename[FILE_PATH_SIZE] = {0}; + uint8_t fileNameLen = 0; char buf[17]; uint8_t *keyBlock = NULL, *p; int keyitems = 0, keycnt = 0; - - // May be a dictionary file - if ( param_getstr(Cmd, 1, filename, sizeof(filename)) >= FILE_PATH_SIZE ) { - PrintAndLog("File name too long"); - free(keyBlock); - return 2; + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (param_getchar(Cmd, cmdp)) { + case 'h': + case 'H': + return usage_hf_iclass_chk(); + case 'f': + case 'F': + fileNameLen = param_getstr(Cmd, cmdp+1, filename, sizeof(filename)); + if (fileNameLen < 1) { + PrintAndLog("No filename found after f"); + errors = true; + } + cmdp += 2; + break; + case 'e': + case 'E': + use_elite = true; + cmdp++; + break; + case 'r': + case 'R': + use_raw = true; + cmdp++; + break; + default: + PrintAndLog("Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); + errors = true; + break; + } } + if (errors) return usage_hf_iclass_chk(); if ( !(f = fopen( filename , "r")) ) { PrintAndLog("File: %s: not found or locked.", filename); - free(keyBlock); return 1; } @@ -1794,7 +1821,7 @@ int CmdHFiClassCheckKeys(const char *Cmd) { // debit key. try twice for (int foo = 0; foo < 2 && !found_debit; foo++) { - if (!select_and_auth(key, mac, div_key, false, elite, rawkey, false)) + if (!select_and_auth(key, mac, div_key, false, use_elite, use_raw, false)) continue; // key found. @@ -1805,7 +1832,7 @@ int CmdHFiClassCheckKeys(const char *Cmd) { // credit key. try twice for (int foo = 0; foo < 2 && !found_credit; foo++) { - if (!select_and_auth(key, mac, div_key, true, elite, rawkey, false)) + if (!select_and_auth(key, mac, div_key, true, use_elite, use_raw, false)) continue; // key found From 6469d5503a682843d2d90cdfdbff81dd9ed9298b Mon Sep 17 00:00:00 2001 From: Iceman Date: Thu, 21 Dec 2017 07:55:51 +0100 Subject: [PATCH 115/310] Update lfsampling.c @marshmellow42 's fix for #514 --- armsrc/lfsampling.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/armsrc/lfsampling.c b/armsrc/lfsampling.c index 999f56bb..084201a5 100644 --- a/armsrc/lfsampling.c +++ b/armsrc/lfsampling.c @@ -153,8 +153,10 @@ uint32_t DoAcquisition(uint8_t decimation, uint32_t bits_per_sample, bool averag LED_D_OFF(); // threshold either high or low values 128 = center 0. if trigger = 178 if ((trigger_threshold > 0) && (sample < (trigger_threshold+128)) && (sample > (128-trigger_threshold))) { // - if (cancel_after > 0) cancel_counter++; - if (cancel_after == cancel_counter) break; + if (cancel_after > 0) { + cancel_counter++; + if (cancel_after == cancel_counter) break; + } continue; } trigger_threshold = 0; From d03fb293bce1b06d49fb9faf5c492a1e2effc24b Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Sat, 23 Dec 2017 17:46:43 +0200 Subject: [PATCH 116/310] Emv4 - more additions to hf emv exec * added rsa polarssl and changed sha1 location to polarssl dir * deleted old sha1 * added emv-tools pk files * added polarrssl wrapper sketch to emv_tols * added test command * added crypto polarssl with sda test * added crypto tests and crypto_polarssl sha logic * added SDA,DDA,fDDA,CDA --- CHANGELOG.md | 2 + client/Makefile | 16 +- client/emv/capk.txt | 32 + client/emv/cmdemv.c | 117 +- client/emv/cmdemv.h | 1 + client/emv/crypto.c | 179 +++ client/emv/crypto.h | 48 + client/emv/crypto_backend.h | 50 + client/emv/crypto_polarssl.c | 351 +++++ client/emv/emv_pk.c | 524 +++++++ client/emv/emv_pk.h | 48 + client/emv/emv_pki.c | 511 +++++++ client/emv/emv_pki.h | 45 + client/emv/emv_pki_priv.c | 283 ++++ client/emv/emv_pki_priv.h | 35 + client/emv/emv_tags.c | 2 + client/emv/emvcore.c | 384 +++++- client/emv/emvcore.h | 18 +- client/emv/test/cda_test.c | 442 ++++++ client/emv/test/cda_test.h | 18 + client/emv/test/crypto_test.c | 327 +++++ client/emv/test/crypto_test.h | 18 + client/emv/test/cryptotest.c | 65 + client/emv/test/cryptotest.h | 13 + client/emv/test/dda_test.c | 390 ++++++ client/emv/test/dda_test.h | 18 + client/emv/test/sda_test.c | 277 ++++ client/emv/test/sda_test.h | 16 + client/obj/emv/test/.dummy | 0 client/scripting.c | 4 +- common/polarssl/aes.h | 1 - common/polarssl/bignum.c | 2143 +++++++++++++++++++++++++++++ common/polarssl/bignum.h | 685 +++++++++ common/polarssl/bn_mul.h | 864 ++++++++++++ common/polarssl/des.c | 2 +- common/polarssl/polarssl_config.h | 4 +- common/polarssl/rsa.c | 1466 ++++++++++++++++++++ common/polarssl/rsa.h | 597 ++++++++ common/{ => polarssl}/sha1.c | 119 +- common/{ => polarssl}/sha1.h | 59 +- 40 files changed, 9983 insertions(+), 191 deletions(-) create mode 100644 client/emv/capk.txt create mode 100644 client/emv/crypto.c create mode 100644 client/emv/crypto.h create mode 100644 client/emv/crypto_backend.h create mode 100644 client/emv/crypto_polarssl.c create mode 100644 client/emv/emv_pk.c create mode 100644 client/emv/emv_pk.h create mode 100644 client/emv/emv_pki.c create mode 100644 client/emv/emv_pki.h create mode 100644 client/emv/emv_pki_priv.c create mode 100644 client/emv/emv_pki_priv.h create mode 100644 client/emv/test/cda_test.c create mode 100644 client/emv/test/cda_test.h create mode 100644 client/emv/test/crypto_test.c create mode 100644 client/emv/test/crypto_test.h create mode 100644 client/emv/test/cryptotest.c create mode 100644 client/emv/test/cryptotest.h create mode 100644 client/emv/test/dda_test.c create mode 100644 client/emv/test/dda_test.h create mode 100644 client/emv/test/sda_test.c create mode 100644 client/emv/test/sda_test.h create mode 100644 client/obj/emv/test/.dummy create mode 100644 common/polarssl/bignum.c create mode 100644 common/polarssl/bignum.h create mode 100644 common/polarssl/bn_mul.h create mode 100644 common/polarssl/rsa.c create mode 100644 common/polarssl/rsa.h rename common/{ => polarssl}/sha1.c (87%) rename common/{ => polarssl}/sha1.h (81%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 644f6c0f..4162c638 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,8 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added `hf emv select` - command for select EMV application (Merlok) - Added `hf emv exec` - command for execute EMV transaction (Merlok) - Added to `hf emv exec` MSD path for VISA and Mastercard and some other compatible EMV cards (Merlok) +- Added to `hf emv exec` SDA, DDA, fast DDA, CDA calculations for VISA and Mastercard and some other compatible EMV cards (Merlok) +- Added `hf emv test` - crypto tests for DES, AES, SHA, RSA, SDA, DDA, CDA and some other crypto functions (Merlok) ## [3.0.1][2017-06-08] diff --git a/client/Makefile b/client/Makefile index ec593558..52a106bd 100644 --- a/client/Makefile +++ b/client/Makefile @@ -20,7 +20,7 @@ OBJDIR = obj LDLIBS = -L/opt/local/lib -L/usr/local/lib -lreadline -lpthread -lm LUALIB = ../liblua/liblua.a LDFLAGS = $(ENV_LDFLAGS) -CFLAGS = $(ENV_CFLAGS) -std=c99 -D_ISOC99_SOURCE -I. -I../include -I../common -I../zlib -I../uart -I/opt/local/include -I../liblua -Wall -g -O3 +CFLAGS = $(ENV_CFLAGS) -std=c99 -D_ISOC99_SOURCE -I. -I../include -I../common -I../common/polarssl -I../zlib -I../uart -I/opt/local/include -I../liblua -Wall -g -O3 CXXFLAGS = -I../include -Wall -O3 LUAPLATFORM = generic @@ -88,6 +88,9 @@ CMDSRCS = crapto1/crapto1.c\ crapto1/crypto1.c\ polarssl/des.c \ polarssl/aes.c\ + polarssl/bignum.c\ + polarssl/rsa.c\ + polarssl/sha1.c\ mfkey.c\ loclass/cipher.c \ loclass/cipherutils.c \ @@ -107,12 +110,22 @@ CMDSRCS = crapto1/crapto1.c\ ui.c \ cmddata.c \ lfdemod.c \ + emv/crypto_polarssl.c\ + emv/crypto.c\ + emv/emv_pk.c\ + emv/emv_pki.c\ + emv/emv_pki_priv.c\ + emv/test/cryptotest.c\ emv/apduinfo.c\ emv/dump.c\ emv/tlv.c\ emv/emv_tags.c\ emv/dol.c\ emv/emvcore.c\ + emv/test/crypto_test.c\ + emv/test/sda_test.c\ + emv/test/dda_test.c\ + emv/test/cda_test.c\ emv/cmdemv.c\ cmdhf.c \ cmdhf14a.c \ @@ -157,7 +170,6 @@ CMDSRCS = crapto1/crapto1.c\ pm3_binlib.c\ pm3_bitlib.c\ protocols.c\ - sha1.c\ cmdcrc.c\ reveng/reveng.c\ reveng/cli.c\ diff --git a/client/emv/capk.txt b/client/emv/capk.txt new file mode 100644 index 00000000..0925fbad --- /dev/null +++ b/client/emv/capk.txt @@ -0,0 +1,32 @@ +a0:00:00:00:03 01 091231 rsa 03 c6:96:03:42:13:d7:d8:54:69:84:57:9d:1d:0f:0e:a5:19:cf:f8:de:ff:c4:29:35:4c:f3:a8:71:a6:f7:18:3f:12:28:da:5c:74:70:c0:55:38:71:00:cb:93:5a:71:2c:4e:28:64:df:5d:64:ba:93:fe:7e:63:e7:1f:25:b1:e5:f5:29:85:75:eb:e1:c6:3a:a6:17:70:69:17:91:1d:c2:a7:5a:c2:8b:25:1c:7e:f4:0f:23:65:91:24:90:b9:39:bc:a2:12:4a:30:a2:8f:54:40:2c:34:ae:ca:33:1a:b6:7e:1e:79:b2:85:dd:57:71:b5:d9:ff:79:ea:63:0b:75 sha1 d3:4a:6a:77:60:11:c7:e7:ce:3a:ec:5f:03:ad:2f:8c:fc:55:03:cc +a0:00:00:00:03 07 171231 rsa 03 a8:9f:25:a5:6f:a6:da:25:8c:8c:a8:b4:04:27:d9:27:b4:a1:eb:4d:7e:a3:26:bb:b1:2f:97:de:d7:0a:e5:e4:48:0f:c9:c5:e8:a9:72:17:71:10:a1:cc:31:8d:06:d2:f8:f5:c4:84:4a:c5:fa:79:a4:dc:47:0b:b1:1e:d6:35:69:9c:17:08:1b:90:f1:b9:84:f1:2e:92:c1:c5:29:27:6d:8a:f8:ec:7f:28:49:20:97:d8:cd:5b:ec:ea:16:fe:40:88:f6:cf:ab:4a:1b:42:32:8a:1b:99:6f:92:78:b0:b7:e3:31:1c:a5:ef:85:6c:2f:88:84:74:b8:36:12:a8:2e:4e:00:d0:cd:40:69:a6:78:31:40:43:3d:50:72:5f sha1 b4:bc:56:cc:4e:88:32:49:32:cb:c6:43:d6:89:8f:6f:e5:93:b1:72 +a0:00:00:00:03 08 221231 rsa 03 d9:fd:6e:d7:5d:51:d0:e3:06:64:bd:15:70:23:ea:a1:ff:a8:71:e4:da:65:67:2b:86:3d:25:5e:81:e1:37:a5:1d:e4:f7:2b:cc:9e:44:ac:e1:21:27:f8:7e:26:3d:3a:f9:dd:9c:f3:5c:a4:a7:b0:1e:90:70:00:ba:85:d2:49:54:c2:fc:a3:07:48:25:dd:d4:c0:c8:f1:86:cb:02:0f:68:3e:02:f2:de:ad:39:69:13:3f:06:f7:84:51:66:ac:eb:57:ca:0f:c2:60:34:45:46:98:11:d2:93:bf:ef:ba:fa:b5:76:31:b3:dd:91:e7:96:bf:85:0a:25:01:2f:1a:e3:8f:05:aa:5c:4d:6d:03:b1:dc:2e:56:86:12:78:59:38:bb:c9:b3:cd:3a:91:0c:1d:a5:5a:5a:92:18:ac:e0:f7:a2:12:87:75:26:82:f1:58:32:a6:78:d6:e1:ed:0b sha1 20:d2:13:12:69:55:de:20:5a:dc:2f:d2:82:2b:d2:2d:e2:1c:f9:a8 +a0:00:00:00:03 09 221231 rsa 03 9d:91:22:48:de:0a:4e:39:c1:a7:dd:e3:f6:d2:58:89:92:c1:a4:09:5a:fb:d1:82:4d:1b:a7:48:47:f2:bc:49:26:d2:ef:d9:04:b4:b5:49:54:cd:18:9a:54:c5:d1:17:96:54:f8:f9:b0:d2:ab:5f:03:57:eb:64:2f:ed:a9:5d:39:12:c6:57:69:45:fa:b8:97:e7:06:2c:aa:44:a4:aa:06:b8:fe:6e:3d:ba:18:af:6a:e3:73:8e:30:42:9e:e9:be:03:42:7c:9d:64:f6:95:fa:8c:ab:4b:fe:37:68:53:ea:34:ad:1d:76:bf:ca:d1:59:08:c0:77:ff:e6:dc:55:21:ec:ef:5d:27:8a:96:e2:6f:57:35:9f:fa:ed:a1:94:34:b9:37:f1:ad:99:9d:c5:c4:1e:b1:19:35:b4:4c:18:10:0e:85:7f:43:1a:4a:5a:6b:b6:51:14:f1:74:c2:d7:b5:9f:df:23:7d:6b:b1:dd:09:16:e6:44:d7:09:de:d5:64:81:47:7c:75:d9:5c:dd:68:25:46:15:f7:74:0e:c0:7f:33:0a:c5:d6:7b:cd:75:bf:23:d2:8a:14:08:26:c0:26:db:de:97:1a:37:cd:3e:f9:b8:df:64:4a:c3:85:01:05:01:ef:c6:50:9d:7a:41 sha1 1f:f8:0a:40:17:3f:52:d7:d2:7e:0f:26:a1:46:a1:c8:cc:b2:90:46 +a0:00:00:00:03 95 000000 rsa 03 be:9e:1f:a5:e9:a8:03:85:29:99:c4:ab:43:2d:b2:86:00:dc:d9:da:b7:6d:fa:aa:47:35:5a:0f:e3:7b:15:08:ac:6b:f3:88:60:d3:c6:c2:e5:b1:2a:3c:aa:f2:a7:00:5a:72:41:eb:aa:77:71:11:2c:74:cf:9a:06:34:65:2f:bc:a0:e5:98:0c:54:a6:47:61:ea:10:1a:11:4e:0f:0b:55:72:ad:d5:7d:01:0b:7c:9c:88:7e:10:4c:a4:ee:12:72:da:66:d9:97:b9:a9:0b:5a:6d:62:4a:b6:c5:7e:73:c8:f9:19:00:0e:b5:f6:84:89:8e:f8:c3:db:ef:b3:30:c6:26:60:be:d8:8e:a7:8e:90:9a:ff:05:f6:da:62:7b sha1 ee:15:11:ce:c7:10:20:a9:b9:04:43:b3:7b:1d:5f:6e:70:30:30:f6 +a0:00:00:00:03 92 000000 rsa 03 99:6a:f5:6f:56:91:87:d0:92:93:c1:48:10:45:0e:d8:ee:33:57:39:7b:18:a2:45:8e:fa:a9:2d:a3:b6:df:65:14:ec:06:01:95:31:8f:d4:3b:e9:b8:f0:cc:66:9e:3f:84:40:57:cb:dd:f8:bd:a1:91:bb:64:47:3b:c8:dc:9a:73:0d:b8:f6:b4:ed:e3:92:41:86:ff:d9:b8:c7:73:57:89:c2:3a:36:ba:0b:8a:f6:53:72:eb:57:ea:5d:89:e7:d1:4e:9c:7b:6b:55:74:60:f1:08:85:da:16:ac:92:3f:15:af:37:58:f0:f0:3e:bd:3c:5c:2c:94:9c:ba:30:6d:b4:4e:6a:2c:07:6c:5f:67:e2:81:d7:ef:56:78:5d:c4:d7:59:45:e4:91:f0:19:18:80:0a:9e:2d:c6:6f:60:08:05:66:ce:0d:af:8d:17:ea:d4:6a:d8:e3:0a:24:7c:9f sha1 42:9c:95:4a:38:59:ce:f9:12:95:f6:63:c9:63:e5:82:ed:6e:b2:53 +a0:00:00:00:03 94 000000 rsa 03 ac:d2:b1:23:02:ee:64:4f:3f:83:5a:bd:1f:c7:a6:f6:2c:ce:48:ff:ec:62:2a:a8:ef:06:2b:ef:6f:b8:ba:8b:c6:8b:bf:6a:b5:87:0e:ed:57:9b:c3:97:3e:12:13:03:d3:48:41:a7:96:d6:dc:bc:41:db:f9:e5:2c:46:09:79:5c:0c:cf:7e:e8:6f:a1:d5:cb:04:10:71:ed:2c:51:d2:20:2f:63:f1:15:6c:58:a9:2d:38:bc:60:bd:f4:24:e1:77:6e:2b:c9:64:80:78:a0:3b:36:fb:55:43:75:fc:53:d5:7c:73:f5:16:0e:a5:9f:3a:fc:53:98:ec:7b:67:75:8d:65:c9:bf:f7:82:8b:6b:82:d4:be:12:4a:41:6a:b7:30:19:14:31:1e:a4:62:c1:9f:77:1f:31:b3:b5:73:36:00:0d:ff:73:2d:3b:83:de:07:05:2d:73:03:54:d2:97:be:c7:28:71:dc:cf:0e:19:3f:17:1a:ba:27:ee:46:4c:6a:97:69:09:43:d5:9b:da:bb:2a:27:eb:71:ce:eb:da:fa:11:76:04:64:78:fd:62:fe:c4:52:d5:ca:39:32:96:53:0a:a3:f4:19:27:ad:fe:43:4a:2d:f2:ae:30:54:f8:84:06:57:a2:6e:0f:c6:17 sha1 c4:a3:c4:3c:cf:87:32:7d:13:6b:80:41:60:e4:7d:43:b6:0e:6e:0f +a0:00:00:00:04 03 000000 rsa 03 c2:49:07:47:fe:17:eb:05:84:c8:8d:47:b1:60:27:04:15:0a:dc:88:c5:b9:98:bd:59:ce:04:3e:de:bf:0f:fe:e3:09:3a:c7:95:6a:d3:b6:ad:45:54:c6:de:19:a1:78:d6:da:29:5b:e1:5d:52:20:64:5e:3c:81:31:66:6f:a4:be:5b:84:fe:13:1e:a4:4b:03:93:07:63:8b:9e:74:a8:c4:25:64:f8:92:a6:4d:f1:cb:15:71:2b:73:6e:33:74:f1:bb:b6:81:93:71:60:2d:89:70:e9:7b:90:07:93:c7:c2:a8:9a:4a:16:49:a5:9b:e6:80:57:4d:d0:b6:01:45 sha1 5a:dd:f2:1d:09:27:86:61:14:11:79:cb:ef:f2:72:ea:38:4b:13:bb +a0:00:00:00:04 04 000000 rsa 03 a6:da:42:83:87:a5:02:d7:dd:fb:7a:74:d3:f4:12:be:76:26:27:19:7b:25:43:5b:7a:81:71:6a:70:01:57:dd:d0:6f:7c:c9:9d:6c:a2:8c:24:70:52:7e:2c:03:61:6b:9c:59:21:73:57:c2:67:4f:58:3b:3b:a5:c7:dc:f2:83:86:92:d0:23:e3:56:24:20:b4:61:5c:43:9c:a9:7c:44:dc:9a:24:9c:fc:e7:b3:bf:b2:2f:68:22:8c:3a:f1:33:29:aa:4a:61:3c:f8:dd:85:35:02:37:3d:62:e4:9a:b2:56:d2:bc:17:12:0e:54:ae:dc:ed:6d:96:a4:28:7a:cc:5c:04:67:7d:4a:5a:32:0d:b8:be:e2:f7:75:e5:fe:c5 sha1 38:1a:03:5d:a5:8b:48:2e:e2:af:75:f4:c3:f2:ca:46:9b:a4:aa:6c +a0:00:00:00:04 05 000000 rsa 03 b8:04:8a:bc:30:c9:0d:97:63:36:54:3e:3f:d7:09:1c:8f:e4:80:0d:f8:20:ed:55:e7:e9:48:13:ed:00:55:5b:57:3f:ec:a3:d8:4a:f6:13:1a:65:1d:66:cf:f4:28:4f:b1:3b:63:5e:dd:0e:e4:01:76:d8:bf:04:b7:fd:1c:7b:ac:f9:ac:73:27:df:aa:8a:a7:2d:10:db:3b:8e:70:b2:dd:d8:11:cb:41:96:52:5e:a3:86:ac:c3:3c:0d:9d:45:75:91:64:69:c4:e4:f5:3e:8e:1c:91:2c:c6:18:cb:22:dd:e7:c3:56:8e:90:02:2e:6b:ba:77:02:02:e4:52:2a:2d:d6:23:d1:80:e2:15:bd:1d:15:07:fe:3d:c9:0c:a3:10:d2:7b:3e:fc:cd:8f:83:de:30:52:ca:d1:e4:89:38:c6:8d:09:5a:ac:91:b5:f3:7e:28:bb:49:ec:7e:d5:97 sha1 eb:fa:0d:5d:06:d8:ce:70:2d:a3:ea:e8:90:70:1d:45:e2:74:c8:45 +a0:00:00:00:04 06 000000 rsa 03 cb:26:fc:83:0b:43:78:5b:2b:ce:37:c8:1e:d3:34:62:2f:96:22:f4:c8:9a:ae:64:10:46:b2:35:34:33:88:3f:30:7f:b7:c9:74:16:2d:a7:2f:7a:4e:c7:5d:9d:65:73:36:86:5b:8d:30:23:d3:d6:45:66:76:25:c9:a0:7a:6b:7a:13:7c:f0:c6:41:98:ae:38:fc:23:80:06:fb:26:03:f4:1f:4f:3b:b9:da:13:47:27:0f:2f:5d:8c:60:6e:42:09:58:c5:f7:d5:0a:71:de:30:14:2f:70:de:46:88:89:b5:e3:a0:86:95:b9:38:a5:0f:c9:80:39:3a:9c:bc:e4:4a:d2:d6:4f:63:0b:b3:3a:d3:f5:f5:fd:49:5d:31:f3:78:18:c1:d9:40:71:34:2e:07:f1:be:c2:19:4f:60:35:ba:5d:ed:39:36:50:0e:b8:2d:fd:a6:e8:af:b6:55:b1:ef:3d:0d:7e:bf:86:b6:6d:d9:f2:9f:6b:1d:32:4f:e8:b2:6c:e3:8a:b2:01:3d:d1:3f:61:1e:7a:59:4d:67:5c:44:32:35:0e:a2:44:cc:34:f3:87:3c:ba:06:59:29:87:a1:d7:e8:52:ad:c2:2e:f5:a2:ee:28:13:20:31:e4:8f:74:03:7e:3b:34:ab:74:7f sha1 f9:10:a1:50:4d:5f:fb:79:3d:94:f3:b5:00:76:5e:1a:bc:ad:72:d9 +a0:00:00:00:04 00 000000 rsa 03 9c:6b:e5:ad:b1:0b:4b:e3:dc:e2:09:9b:4b:21:06:72:b8:96:56:eb:a0:91:20:4f:61:3e:cc:62:3b:ed:c9:c6:d7:7b:66:0e:8b:ae:ea:7f:7c:e3:0f:1b:15:38:79:a4:e3:64:59:34:3d:1f:e4:7a:cd:bd:41:fc:d7:10:03:0c:2b:a1:d9:46:15:97:98:2c:6e:1b:dd:08:55:4b:72:6f:5e:ff:79:13:ce:59:e7:9e:35:72:95:c3:21:e2:6d:0b:8b:e2:70:a9:44:23:45:c7:53:e2:aa:2a:cf:c9:d3:08:50:60:2f:e6:ca:c0:0c:6d:df:6b:8d:9d:9b:48:79:b2:82:6b:04:2a:07:f0:e5:ae:52:6a:3d:3c:4d:22:c7:2b:9e:aa:52:ee:d8:89:38:66:f8:66:38:7a:c0:5a:13:99 sha1 ec:0a:59:d3:5d:19:f0:31:e9:e8:cb:ec:56:db:80:e2:2b:1d:e1:30 +a0:00:00:00:04 02 000000 rsa 03 a9:9a:6d:3e:07:18:89:ed:9e:3a:0c:39:1c:69:b0:b8:04:fc:16:0b:2b:4b:dd:57:0c:92:dd:5a:0f:45:f5:3e:86:21:f7:c9:6c:40:22:42:66:73:5e:1e:e1:b3:c0:62:38:ae:35:04:63:20:fd:8e:81:f8:ce:b3:f8:b4:c9:7b:94:09:30:a3:ac:5e:79:00:86:da:d4:1a:6a:4f:51:17:ba:1c:e2:43:8a:51:ac:05:3e:b0:02:ae:d8:66:d2:c4:58:fd:73:35:90:21:a1:20:29:a0:c0:43:04:5c:11:66:4f:e0:21:9e:c6:3c:10:bf:21:55:bb:27:84:60:9a:10:64:21:d4:51:63:79:97:38:c1:c3:09:09:bb:6c:6f:e5:2b:bb:76:39:7b:97:40:ce:06:4a:61:3f:f8:41:11:85:f0:88:42:a4:23:ea:d2:0e:df:fb:ff:1c:d6:c3:fe:0c:98:21:47:91:99:c2:6d:85:72:cc:8a:ff:f0:87:a9:c3 sha1 33:40:8b:96:c8:14:74:2a:d7:35:36:c7:2f:09:26:e4:47:1e:8e:47 +a0:00:00:00:04 05 000000 rsa 03 a1:f5:e1:c9:bd:86:50:bd:43:ab:6e:e5:6b:89:1e:f7:45:9c:0a:24:fa:84:f9:12:7d:1a:6c:79:d4:93:0f:6d:b1:85:2e:25:10:f1:8b:61:cd:35:4d:b8:3a:35:6b:d1:90:b8:8a:b8:df:04:28:4d:02:a4:20:4a:7b:6c:b7:c5:55:19:77:a9:b3:63:79:ca:3d:e1:a0:8e:69:f3:01:c9:5c:c1:c2:05:06:95:92:75:f4:17:23:dd:5d:29:25:29:05:79:e5:a9:5b:0d:f6:32:3f:c8:e9:27:3d:6f:84:91:98:c4:99:62:09:16:6d:9b:fc:97:3c:36:1c:c8:26:e1 sha1 53:d0:49:03:b4:96:f5:95:44:a8:43:09:af:16:92:51:f2:89:68:74 +a0:00:00:00:04 ef 000000 rsa 03 a1:91:cb:87:47:3f:29:34:9b:5d:60:a8:8b:3e:ae:e0:97:3a:a6:f1:a0:82:f3:58:d8:49:fd:df:f9:c0:91:f8:99:ed:a9:79:2c:af:09:ef:28:f5:d2:24:04:b8:8a:22:93:ee:bb:c1:94:9c:43:be:a4:d6:0c:fd:87:9a:15:39:54:4e:09:e0:f0:9f:60:f0:65:b2:bf:2a:13:ec:c7:05:f3:d4:68:b9:d3:3a:e7:7a:d9:d3:f1:9c:a4:0f:23:dc:f5:eb:7c:04:dc:8f:69:eb:a5:65:b1:eb:cb:46:86:cd:27:47:85:53:0f:f6:f6:e9:ee:43:aa:43:fd:b0:2c:e0:0d:ae:c1:5c:7b:8f:d6:a9:b3:94:ba:ba:41:9d:3f:6d:c8:5e:16:56:9b:e8:e7:69:89:68:8e:fe:a2:df:22:ff:7d:35:c0:43:33:8d:ea:a9:82:a0:2b:86:6d:e5:32:85:19:eb:bc:d6:f0:3c:dd:68:66:73:84:7f:84:db:65:1a:b8:6c:28:cf:14:62:56:2c:57:7b:85:35:64:a2:90:c8:55:6d:81:85:31:26:8d:25:cc:98:a4:cc:6a:0b:df:ff:da:2d:cc:a3:a9:4c:99:85:59:e3:07:fd:df:91:50:06:d9:a9:87:b0:7d:da:eb:3b sha1 21:76:6e:bb:0e:e1:22:af:b6:5d:78:45:b7:3d:b4:6b:ab:65:42:7a +a0:00:00:00:04 f1 000000 rsa 03 a0:dc:f4:bd:e1:9c:35:46:b4:b6:f0:41:4d:17:4d:de:29:4a:ab:bb:82:8c:5a:83:4d:73:aa:e2:7c:99:b0:b0:53:a9:02:78:00:72:39:b6:45:9f:f0:bb:cd:7b:4b:9c:6c:50:ac:02:ce:91:36:8d:a1:bd:21:aa:ea:db:c6:53:47:33:7d:89:b6:8f:5c:99:a0:9d:05:be:02:dd:1f:8c:5b:a2:0e:2f:13:fb:2a:27:c4:1d:3f:85:ca:d5:cf:66:68:e7:58:51:ec:66:ed:bf:98:85:1f:d4:e4:2c:44:c1:d5:9f:59:84:70:3b:27:d5:b9:f2:1b:8f:a0:d9:32:79:fb:bf:69:e0:90:64:29:09:c9:ea:27:f8:98:95:95:41:aa:67:57:f5:f6:24:10:4f:6e:1d:3a:95:32:f2:a6:e5:15:15:ae:ad:1b:43:b3:d7:83:50:88:a2:fa:fa:7b:e7 sha1 d8:e6:8d:a1:67:ab:5a:85:d8:c3:d5:5e:cb:9b:05:17:a1:a5:b4:bb +a0:00:00:00:04 f3 000000 rsa 03 98:f0:c7:70:f2:38:64:c2:e7:66:df:02:d1:e8:33:df:f4:ff:e9:2d:69:6e:16:42:f0:a8:8c:56:94:c6:47:9d:16:db:15:37:bf:e2:9e:4f:dc:6e:6e:8a:fd:1b:0e:b7:ea:01:24:72:3c:33:31:79:bf:19:e9:3f:10:65:8b:2f:77:6e:82:9e:87:da:ed:a9:c9:4a:8b:33:82:19:9a:35:0c:07:79:77:c9:7a:ff:08:fd:11:31:0a:c9:50:a7:2c:3c:a5:00:2e:f5:13:fc:cc:28:6e:64:6e:3c:53:87:53:5d:50:95:14:b3:b3:26:e1:23:4f:9c:b4:8c:36:dd:d4:4b:41:6d:23:65:40:34:a6:6f:40:3b:a5:11:c5:ef:a3 sha1 a6:9a:c7:60:3d:af:56:6e:97:2d:ed:c2:cb:43:3e:07:e8:b0:1a:9a +a0:00:00:00:04 f5 000000 rsa 01:00:01 a6:e6:fb:72:17:95:06:f8:60:cc:ca:8c:27:f9:9c:ec:d9:4c:7d:4f:31:91:d3:03:bb:ee:37:48:1c:7a:a1:5f:23:3b:a7:55:e9:e4:37:63:45:a9:a6:7e:79:94:bd:c1:c6:80:bb:35:22:d8:c9:3e:b0:cc:c9:1a:d3:1a:d4:50:da:30:d3:37:66:2d:19:ac:03:e2:b4:ef:5f:6e:c1:82:82:d4:91:e1:97:67:d7:b2:45:42:df:de:ff:6f:62:18:55:03:53:20:69:bb:b3:69:e3:bb:9f:b1:9a:c6:f1:c3:0b:97:d2:49:ee:e7:64:e0:ba:c9:7f:25:c8:73:d9:73:95:3e:51:53:a4:20:64:bb:fa:bf:d0:6a:4b:b4:86:86:0b:f6:63:74:06:c9:fc:36:81:3a:4a:75:f7:5c:31:cc:a9:f6:9f:8d:e5:9a:de:ce:f6:bd:e7:e0:78:00:fc:be:03:5d:31:76:af:84:73:e2:3e:9a:a3:df:ee:22:11:96:d1:14:83:02:67:7c:72:0c:fe:25:44:a0:3d:b5:53:e7:f1:b8:42:7b:a1:cc:72:b0:f2:9b:12:df:ef:4c:08:1d:07:6d:35:3e:71:88:0a:ad:ff:38:63:52:af:0a:b7:b2:8e:d4:9e:1e:67:2d:11:f9 sha1 c2:23:98:04:c8:09:81:70:be:52:d6:d5:d4:15:9e:81:ce:84:66:bf +a0:00:00:00:04 f6 000000 rsa 03 a2:5a:6b:d7:83:a5:ef:6b:8f:b6:f8:30:55:c2:60:f5:f9:9e:a1:66:78:f3:b9:05:3e:0f:64:98:e8:2c:3f:5d:1e:8c:38:f1:35:88:01:7e:2b:12:b3:d8:ff:6f:50:16:7f:46:44:29:10:72:9e:9e:4d:1b:37:39:e5:06:7c:0a:c7:a1:f4:48:7e:35:f6:75:bc:16:e2:33:31:51:65:cb:14:2b:fd:b2:5e:30:1a:63:2a:54:a3:37:1e:ba:b6:57:2d:ee:ba:f3:70:f3:37:f0:57:ee:73:b4:ae:46:d1:a8:bc:4d:a8:53:ec:3c:c1:2c:8c:bc:2d:a1:83:22:d6:85:30:c7:0b:22:bd:ac:35:1d:d3:60:68:ae:32:1e:11:ab:f2:64:f4:d3:56:9b:b7:12:14:54:50:05:55:8d:e2:60:83:c7:35:db:77:63:68:17:2f:e8:c2:f5:c8:5e:8b:5b:89:0c:c6:82:91:1d:2d:e7:1f:a6:26:b8:81:7f:cc:c0:89:22:b7:03:86:9f:3b:ae:ac:14:59:d7:7c:d8:53:76:bc:36:18:2f:42:38:31:4d:6c:42:12:fb:dd:7f:23:d3 sha1 50:29:09:ed:54:5e:3c:8d:bd:00:ea:58:2d:06:17:fe:e9:f6:f6:84 +a0:00:00:00:04 f7 000000 rsa 01:00:01 94:ea:62:f6:d5:83:20:e3:54:c0:22:ad:dc:f0:55:9d:8c:f2:06:cd:92:e8:69:56:49:05:ce:21:d7:20:f9:71:b7:ae:a3:74:83:0e:be:17:57:11:5a:85:e0:88:d4:1c:6b:77:cf:5e:c8:21:f3:0b:1d:89:04:17:bf:2f:a3:1e:59:08:de:d5:fa:67:7f:8c:7b:18:4a:d0:90:28:fd:de:96:b6:a6:10:98:50:aa:80:01:75:ea:bc:db:bb:68:4a:96:c2:eb:63:79:df:ea:08:d3:2f:e2:33:1f:e1:03:23:3a:d5:8d:cd:b1:e6:e0:77:cb:9f:24:ea:ec:5c:25:af sha1 ee:b0:dd:9b:24:77:be:e3:20:9a:91:4c:db:a9:4c:1c:4a:9b:de:d9 +a0:00:00:00:04 f8 000000 rsa 03 a1:f5:e1:c9:bd:86:50:bd:43:ab:6e:e5:6b:89:1e:f7:45:9c:0a:24:fa:84:f9:12:7d:1a:6c:79:d4:93:0f:6d:b1:85:2e:25:10:f1:8b:61:cd:35:4d:b8:3a:35:6b:d1:90:b8:8a:b8:df:04:28:4d:02:a4:20:4a:7b:6c:b7:c5:55:19:77:a9:b3:63:79:ca:3d:e1:a0:8e:69:f3:01:c9:5c:c1:c2:05:06:95:92:75:f4:17:23:dd:5d:29:25:29:05:79:e5:a9:5b:0d:f6:32:3f:c8:e9:27:3d:6f:84:91:98:c4:99:62:09:16:6d:9b:fc:97:3c:36:1c:c8:26:e1 sha1 f0:6e:cc:6d:2a:ae:bf:25:9b:7e:75:5a:38:d9:a9:b2:4e:2f:f3:dd +a0:00:00:00:04 f9 000000 rsa 03 a9:9a:6d:3e:07:18:89:ed:9e:3a:0c:39:1c:69:b0:b8:04:fc:16:0b:2b:4b:dd:57:0c:92:dd:5a:0f:45:f5:3e:86:21:f7:c9:6c:40:22:42:66:73:5e:1e:e1:b3:c0:62:38:ae:35:04:63:20:fd:8e:81:f8:ce:b3:f8:b4:c9:7b:94:09:30:a3:ac:5e:79:00:86:da:d4:1a:6a:4f:51:17:ba:1c:e2:43:8a:51:ac:05:3e:b0:02:ae:d8:66:d2:c4:58:fd:73:35:90:21:a1:20:29:a0:c0:43:04:5c:11:66:4f:e0:21:9e:c6:3c:10:bf:21:55:bb:27:84:60:9a:10:64:21:d4:51:63:79:97:38:c1:c3:09:09:bb:6c:6f:e5:2b:bb:76:39:7b:97:40:ce:06:4a:61:3f:f8:41:11:85:f0:88:42:a4:23:ea:d2:0e:df:fb:ff:1c:d6:c3:fe:0c:98:21:47:91:99:c2:6d:85:72:cc:8a:ff:f0:87:a9:c3 sha1 33:67:12:dc:c2:85:54:80:9c:6a:a9:b0:23:58:de:6f:75:51:64:db +a0:00:00:00:04 fa 000000 rsa 03 a9:0f:cd:55:aa:2d:5d:99:63:e3:5e:d0:f4:40:17:76:99:83:2f:49:c6:ba:b1:5c:da:e5:79:4b:e9:3f:93:4d:44:62:d5:d1:27:62:e4:8c:38:ba:83:d8:44:5d:ea:a7:41:95:a3:01:a1:02:b2:f1:14:ea:da:0d:18:0e:e5:e7:a5:c7:3e:0c:4e:11:f6:7a:43:dd:ab:5d:55:68:3b:14:74:cc:06:27:f4:4b:8d:30:88:a4:92:ff:aa:da:d4:f4:24:22:d0:e7:01:35:36:c3:c4:9a:d3:d0:fa:e9:64:59:b0:f6:b1:b6:05:65:38:a3:d6:d4:46:40:f9:44:67:b1:08:86:7d:ec:40:fa:ae:cd:74:0c:00:e2:b7:a8:85:2d sha1 5b:ed:40:68:d9:6e:a1:6d:2d:77:e0:3d:60:36:fc:7a:16:0e:a9:9c +b0:12:34:56:78 00 000000 rsa 03 9c:6b:e5:ad:b1:0b:4b:e3:dc:e2:09:9b:4b:21:06:72:b8:96:56:eb:a0:91:20:4f:61:3e:cc:62:3b:ed:c9:c6:d7:7b:66:0e:8b:ae:ea:7f:7c:e3:0f:1b:15:38:79:a4:e3:64:59:34:3d:1f:e4:7a:cd:bd:41:fc:d7:10:03:0c:2b:a1:d9:46:15:97:98:2c:6e:1b:dd:08:55:4b:72:6f:5e:ff:79:13:ce:59:e7:9e:35:72:95:c3:21:e2:6d:0b:8b:e2:70:a9:44:23:45:c7:53:e2:aa:2a:cf:c9:d3:08:50:60:2f:e6:ca:c0:0c:6d:df:6b:8d:9d:9b:48:79:b2:82:6b:04:2a:07:f0:e5:ae:52:6a:3d:3c:4d:22:c7:2b:9e:aa:52:ee:d8:89:38:66:f8:66:38:7a:c0:5a:13:99 sha1 5d:29:70:e6:46:75:72:7e:60:46:07:65:a8:db:75:34:2a:e1:47:83 +b0:12:34:56:78 02 000000 rsa 03 a9:9a:6d:3e:07:18:89:ed:9e:3a:0c:39:1c:69:b0:b8:04:fc:16:0b:2b:4b:dd:57:0c:92:dd:5a:0f:45:f5:3e:86:21:f7:c9:6c:40:22:42:66:73:5e:1e:e1:b3:c0:62:38:ae:35:04:63:20:fd:8e:81:f8:ce:b3:f8:b4:c9:7b:94:09:30:a3:ac:5e:79:00:86:da:d4:1a:6a:4f:51:17:ba:1c:e2:43:8a:51:ac:05:3e:b0:02:ae:d8:66:d2:c4:58:fd:73:35:90:21:a1:20:29:a0:c0:43:04:5c:11:66:4f:e0:21:9e:c6:3c:10:bf:21:55:bb:27:84:60:9a:10:64:21:d4:51:63:79:97:38:c1:c3:09:09:bb:6c:6f:e5:2b:bb:76:39:7b:97:40:ce:06:4a:61:3f:f8:41:11:85:f0:88:42:a4:23:ea:d2:0e:df:fb:ff:1c:d6:c3:fe:0c:98:21:47:91:99:c2:6d:85:72:cc:8a:ff:f0:87:a9:c3 sha1 29:4b:e2:02:39:ab:15:24:5a:63:be:a4:6c:c6:c1:75:a2:55:62:d1 +b0:12:34:56:78 05 000000 rsa 03 a1:f5:e1:c9:bd:86:50:bd:43:ab:6e:e5:6b:89:1e:f7:45:9c:0a:24:fa:84:f9:12:7d:1a:6c:79:d4:93:0f:6d:b1:85:2e:25:10:f1:8b:61:cd:35:4d:b8:3a:35:6b:d1:90:b8:8a:b8:df:04:28:4d:02:a4:20:4a:7b:6c:b7:c5:55:19:77:a9:b3:63:79:ca:3d:e1:a0:8e:69:f3:01:c9:5c:c1:c2:05:06:95:92:75:f4:17:23:dd:5d:29:25:29:05:79:e5:a9:5b:0d:f6:32:3f:c8:e9:27:3d:6f:84:91:98:c4:99:62:09:16:6d:9b:fc:97:3c:36:1c:c8:26:e1 sha1 b9:a1:d6:5c:af:e0:6b:05:4e:dd:7e:a8:25:97:ab:85:f1:30:e6:63 +b0:12:34:56:78 f3 000000 rsa 01:00:01 94:ea:62:f6:d5:83:20:e3:54:c0:22:ad:dc:f0:55:9d:8c:f2:06:cd:92:e8:69:56:49:05:ce:21:d7:20:f9:71:b7:ae:a3:74:83:0e:be:17:57:11:5a:85:e0:88:d4:1c:6b:77:cf:5e:c8:21:f3:0b:1d:89:04:17:bf:2f:a3:1e:59:08:de:d5:fa:67:7f:8c:7b:18:4a:d0:90:28:fd:de:96:b6:a6:10:98:50:aa:80:01:75:ea:bc:db:bb:68:4a:96:c2:eb:63:79:df:ea:08:d3:2f:e2:33:1f:e1:03:23:3a:d5:8d:cd:b1:e6:e0:77:cb:9f:24:ea:ec:5c:25:af sha1 56:94:b0:d2:78:48:18:14:a0:5e:12:b5:58:ce:c1:23:48:65:aa:5d +b0:12:34:56:78 f5 000000 rsa 03 a2:5a:6b:d7:83:a5:ef:6b:8f:b6:f8:30:55:c2:60:f5:f9:9e:a1:66:78:f3:b9:05:3e:0f:64:98:e8:2c:3f:5d:1e:8c:38:f1:35:88:01:7e:2b:12:b3:d8:ff:6f:50:16:7f:46:44:29:10:72:9e:9e:4d:1b:37:39:e5:06:7c:0a:c7:a1:f4:48:7e:35:f6:75:bc:16:e2:33:31:51:65:cb:14:2b:fd:b2:5e:30:1a:63:2a:54:a3:37:1e:ba:b6:57:2d:ee:ba:f3:70:f3:37:f0:57:ee:73:b4:ae:46:d1:a8:bc:4d:a8:53:ec:3c:c1:2c:8c:bc:2d:a1:83:22:d6:85:30:c7:0b:22:bd:ac:35:1d:d3:60:68:ae:32:1e:11:ab:f2:64:f4:d3:56:9b:b7:12:14:54:50:05:55:8d:e2:60:83:c7:35:db:77:63:68:17:2f:e8:c2:f5:c8:5e:8b:5b:89:0c:c6:82:91:1d:2d:e7:1f:a6:26:b8:81:7f:cc:c0:89:22:b7:03:86:9f:3b:ae:ac:14:59:d7:7c:d8:53:76:bc:36:18:2f:42:38:31:4d:6c:42:12:fb:dd:7f:23:d3 sha1 f7:5e:88:02:85:5c:9b:14:02:7e:51:73:45:71:7e:5c:36:35:b9:1b +b0:12:34:56:78 f6 000000 rsa 03 a1:f5:e1:c9:bd:86:50:bd:43:ab:6e:e5:6b:89:1e:f7:45:9c:0a:24:fa:84:f9:12:7d:1a:6c:79:d4:93:0f:6d:b1:85:2e:25:10:f1:8b:61:cd:35:4d:b8:3a:35:6b:d1:90:b8:8a:b8:df:04:28:4d:02:a4:20:4a:7b:6c:b7:c5:55:19:77:a9:b3:63:79:ca:3d:e1:a0:8e:69:f3:01:c9:5c:c1:c2:05:06:95:92:75:f4:17:23:dd:5d:29:25:29:05:79:e5:a9:5b:0d:f6:32:3f:c8:e9:27:3d:6f:84:91:98:c4:99:62:09:16:6d:9b:fc:97:3c:36:1c:c8:26:e1 sha1 e9:40:6b:65:10:c1:43:ab:1e:9b:9d:79:a3:c1:df:f8:90:9a:34:7c +b0:12:34:56:78 f7 000000 rsa 03 98:f0:c7:70:f2:38:64:c2:e7:66:df:02:d1:e8:33:df:f4:ff:e9:2d:69:6e:16:42:f0:a8:8c:56:94:c6:47:9d:16:db:15:37:bf:e2:9e:4f:dc:6e:6e:8a:fd:1b:0e:b7:ea:01:24:72:3c:33:31:79:bf:19:e9:3f:10:65:8b:2f:77:6e:82:9e:87:da:ed:a9:c9:4a:8b:33:82:19:9a:35:0c:07:79:77:c9:7a:ff:08:fd:11:31:0a:c9:50:a7:2c:3c:a5:00:2e:f5:13:fc:cc:28:6e:64:6e:3c:53:87:53:5d:50:95:14:b3:b3:26:e1:23:4f:9c:b4:8c:36:dd:d4:4b:41:6d:23:65:40:34:a6:6f:40:3b:a5:11:c5:ef:a3 sha1 f7:81:13:e8:60:f0:30:a8:72:92:3f:ce:93:e3:38:1c:77:a4:2a:30 +b0:12:34:56:78 f8 000000 rsa 03 a9:9a:6d:3e:07:18:89:ed:9e:3a:0c:39:1c:69:b0:b8:04:fc:16:0b:2b:4b:dd:57:0c:92:dd:5a:0f:45:f5:3e:86:21:f7:c9:6c:40:22:42:66:73:5e:1e:e1:b3:c0:62:38:ae:35:04:63:20:fd:8e:81:f8:ce:b3:f8:b4:c9:7b:94:09:30:a3:ac:5e:79:00:86:da:d4:1a:6a:4f:51:17:ba:1c:e2:43:8a:51:ac:05:3e:b0:02:ae:d8:66:d2:c4:58:fd:73:35:90:21:a1:20:29:a0:c0:43:04:5c:11:66:4f:e0:21:9e:c6:3c:10:bf:21:55:bb:27:84:60:9a:10:64:21:d4:51:63:79:97:38:c1:c3:09:09:bb:6c:6f:e5:2b:bb:76:39:7b:97:40:ce:06:4a:61:3f:f8:41:11:85:f0:88:42:a4:23:ea:d2:0e:df:fb:ff:1c:d6:c3:fe:0c:98:21:47:91:99:c2:6d:85:72:cc:8a:ff:f0:87:a9:c3 sha1 66:46:9c:88:e7:dc:11:15:29:c7:d3:79:d7:93:8c:8d:f3:e4:c2:5e +b0:12:34:56:78 f9 000000 rsa 01:00:01 a6:e6:fb:72:17:95:06:f8:60:cc:ca:8c:27:f9:9c:ec:d9:4c:7d:4f:31:91:d3:03:bb:ee:37:48:1c:7a:a1:5f:23:3b:a7:55:e9:e4:37:63:45:a9:a6:7e:79:94:bd:c1:c6:80:bb:35:22:d8:c9:3e:b0:cc:c9:1a:d3:1a:d4:50:da:30:d3:37:66:2d:19:ac:03:e2:b4:ef:5f:6e:c1:82:82:d4:91:e1:97:67:d7:b2:45:42:df:de:ff:6f:62:18:55:03:53:20:69:bb:b3:69:e3:bb:9f:b1:9a:c6:f1:c3:0b:97:d2:49:ee:e7:64:e0:ba:c9:7f:25:c8:73:d9:73:95:3e:51:53:a4:20:64:bb:fa:bf:d0:6a:4b:b4:86:86:0b:f6:63:74:06:c9:fc:36:81:3a:4a:75:f7:5c:31:cc:a9:f6:9f:8d:e5:9a:de:ce:f6:bd:e7:e0:78:00:fc:be:03:5d:31:76:af:84:73:e2:3e:9a:a3:df:ee:22:11:96:d1:14:83:02:67:7c:72:0c:fe:25:44:a0:3d:b5:53:e7:f1:b8:42:7b:a1:cc:72:b0:f2:9b:12:df:ef:4c:08:1d:07:6d:35:3e:71:88:0a:ad:ff:38:63:52:af:0a:b7:b2:8e:d4:9e:1e:67:2d:11:f9 sha1 ae:ac:a4:54:80:c8:83:4c:b0:be:bd:cc:57:0b:7b:2b:74:bb:4b:79 diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index 92ae01f9..42c8524a 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -9,6 +9,7 @@ //----------------------------------------------------------------------------- #include "cmdemv.h" +#include "test/cryptotest.h" int UsageCmdHFEMVSelect(void) { PrintAndLog("HELP : Executes select applet command:\n"); @@ -277,7 +278,7 @@ int CmdHFEMVPPSE(const char *cmd) { int UsageCmdHFEMVExec(void) { PrintAndLog("HELP : Executes EMV contactless transaction:\n"); - PrintAndLog("Usage: hf emv exec [-s][-a][-t]\n"); + PrintAndLog("Usage: hf emv exec [-s][-a][-t][-f][-v][-c][-x][-g]\n"); PrintAndLog(" Options:"); PrintAndLog(" -s : select card"); PrintAndLog(" -a : show APDU reqests and responses\n"); @@ -286,14 +287,16 @@ int UsageCmdHFEMVExec(void) { PrintAndLog(" -v : transaction type - qVSDC or M/Chip.\n"); PrintAndLog(" -c : transaction type - qVSDC or M/Chip plus CDA (SDAD generation).\n"); PrintAndLog(" -x : transaction type - VSDC. For test only. Not a standart behavior.\n"); + PrintAndLog(" -g : VISA. generate AC from GPO\n"); PrintAndLog("By default : transaction type - MSD.\n"); PrintAndLog("Samples:"); - PrintAndLog(" hf emv pse -s -> select card"); - PrintAndLog(" hf emv pse -s -t -a -> select card, show responses in TLV, show APDU"); + PrintAndLog(" hf emv exec -s -a -t -> execute MSD transaction"); + PrintAndLog(" hf emv exec -s -a -t -c -> execute CDA transaction"); return 0; } #define TLV_ADD(tag, value)( tlvdb_add(tlvRoot, tlvdb_fixed(tag, sizeof(value) - 1, (const unsigned char *)value)) ) +#define dreturn(n) {free(pdol_data_tlv);tlvdb_free(tlvSelect);tlvdb_free(tlvRoot);DropField();return n;} int CmdHFEMVExec(const char *cmd) { bool activateField = false; @@ -301,15 +304,22 @@ int CmdHFEMVExec(const char *cmd) { bool decodeTLV = false; bool forceSearch = false; enum TransactionType TrType = TT_MSD; + bool GenACGPO = false; uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; uint16_t sw = 0; uint8_t AID[APDU_AID_LEN] = {0}; size_t AIDlen = 0; + uint8_t ODAiList[4096]; + size_t ODAiListLen = 0; int res; + struct tlvdb *tlvSelect = NULL; + struct tlvdb *tlvRoot = NULL; + struct tlv *pdol_data_tlv = NULL; + if (strlen(cmd) < 1) { UsageCmdHFEMVExec(); return 0; @@ -352,6 +362,10 @@ int CmdHFEMVExec(const char *cmd) { case 'C': TrType = TT_CDA; break; + case 'g': + case 'G': + GenACGPO = true; + break; default: PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); return 1; @@ -361,7 +375,6 @@ int CmdHFEMVExec(const char *cmd) { // init applets list tree - struct tlvdb *tlvSelect = NULL; const char *al = "Applets list"; tlvSelect = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); @@ -385,8 +398,7 @@ int CmdHFEMVExec(const char *cmd) { PrintAndLog("\n* Search AID in list."); SetAPDULogging(false); if (EMVSearch(activateField, true, decodeTLV, tlvSelect)) { - tlvdb_free(tlvSelect); - return 2; + dreturn(2); } // check search and select application id @@ -395,14 +407,13 @@ int CmdHFEMVExec(const char *cmd) { } // Init TLV tree - struct tlvdb *tlvRoot = NULL; const char *alr = "Root terminal TLV tree"; tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr); // check if we found EMV application on card if (!AIDlen) { PrintAndLog("Can't select AID. EMV AID not found"); - return 2; + dreturn(2); } // Select @@ -412,7 +423,7 @@ int CmdHFEMVExec(const char *cmd) { if (res) { PrintAndLog("Can't select AID (%d). Exit...", res); - return 3; + dreturn(3); } if (decodeTLV) @@ -422,25 +433,30 @@ int CmdHFEMVExec(const char *cmd) { PrintAndLog("\n* Init transaction parameters."); //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 + char *qVSDC = "\x26\x00\x00\x00"; + if (GenACGPO) { + qVSDC = "\x26\x80\x00\x00"; + } switch(TrType) { case TT_MSD: TLV_ADD(0x9F66, "\x86\x00\x00\x00"); // MSD break; - // not standart for contactless. just for test. + // not standard for contactless. just for test. case TT_VSDC: TLV_ADD(0x9F66, "\x46\x00\x00\x00"); // VSDC break; case TT_QVSDCMCHIP: - TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC + TLV_ADD(0x9F66, qVSDC); // qVSDC break; case TT_CDA: - TLV_ADD(0x9F66, "\x86\x80\x00\x00"); // CDA + TLV_ADD(0x9F66, qVSDC); // qVSDC (VISA CDA not enabled) break; default: TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC break; } - //9F02:(Amount, Authorised (Numeric)) len:6 + + //9F02:(Amount, authorized (Numeric)) len:6 TLV_ADD(0x9F02, "\x00\x00\x00\x00\x01\x00"); //9F1A:(Terminal Country Code) len:2 TLV_ADD(0x9F1A, "ru"); @@ -459,17 +475,17 @@ int CmdHFEMVExec(const char *cmd) { TLVPrintFromTLV(tlvRoot); // TODO delete!!! PrintAndLog("\n* Calc PDOL."); - struct tlv *pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83); + pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83); if (!pdol_data_tlv){ PrintAndLog("ERROR: can't create PDOL TLV."); - return 4; + dreturn(4); } size_t pdol_data_tlv_data_len; unsigned char *pdol_data_tlv_data = tlv_encode(pdol_data_tlv, &pdol_data_tlv_data_len); if (!pdol_data_tlv_data) { PrintAndLog("ERROR: can't create PDOL data."); - return 4; + dreturn(4); } PrintAndLog("PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); @@ -477,11 +493,11 @@ int CmdHFEMVExec(const char *cmd) { res = EMVGPO(true, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); free(pdol_data_tlv_data); - free(pdol_data_tlv); + //free(pdol_data_tlv); --- free on exit. if (res) { PrintAndLog("GPO error(%d): %4x. Exit...", res, sw); - return 5; + dreturn(5); } // process response template format 1 [id:80 2b AIP + x4b AFL] and format 2 [id:77 TLV] @@ -567,9 +583,23 @@ int CmdHFEMVExec(const char *cmd) { PrintAndLog(""); } + // Build Input list for Offline Data Authentication + // EMV 4.3 book3 10.3, page 96 if (SFIoffline) { - // here will be offline records storing... - // dont foget: if (sfi < 11) + if (SFI < 11) { + const unsigned char *abuf = buf; + size_t elmlen = len; + struct tlv e; + if (tlv_parse_tl(&abuf, &elmlen, &e)) { + memcpy(&ODAiList[ODAiListLen], &buf[len - elmlen], elmlen); + ODAiListLen += elmlen; + } else { + PrintAndLog("ERROR SFI[%02x]. Creating input list for Offline Data Authentication error.", SFI); + } + } else { + memcpy(&ODAiList[ODAiListLen], buf, len); + ODAiListLen += len; + } } } } @@ -577,6 +607,13 @@ int CmdHFEMVExec(const char *cmd) { break; } + // copy Input list for Offline Data Authentication + if (ODAiListLen) { + struct tlvdb *oda = tlvdb_fixed(0x21, ODAiListLen, ODAiList); // not a standard tag + tlvdb_add(tlvRoot, oda); + PrintAndLog("* Input list for Offline Data Authentication added to TLV. len=%d \n", ODAiListLen); + } + // get AIP const struct tlv *AIPtlv = tlvdb_get(tlvRoot, 0x82, NULL); uint16_t AIP = AIPtlv->value[0] + AIPtlv->value[1] * 0x100; @@ -585,13 +622,13 @@ int CmdHFEMVExec(const char *cmd) { // SDA if (AIP & 0x0040) { PrintAndLog("\n* SDA"); - trSDA(AID, AIDlen, tlvRoot); + trSDA(tlvRoot); } // DDA if (AIP & 0x0020) { PrintAndLog("\n* DDA"); - + trDDA(decodeTLV, tlvRoot); } // transaction check @@ -644,11 +681,11 @@ int CmdHFEMVExec(const char *cmd) { res = EMVGenerateChallenge(true, buf, sizeof(buf), &len, &sw, tlvRoot); if (res) { PrintAndLog("ERROR GetChallenge. APDU error %4x", sw); - return 5; + dreturn(6); } if (len < 4) { PrintAndLog("ERROR GetChallenge. Wrong challenge length %d", len); - return 5; + dreturn(6); } // ICC Dynamic Number @@ -663,7 +700,7 @@ int CmdHFEMVExec(const char *cmd) { struct tlv *cdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x8c, NULL), tlvRoot, 0x01); // 0x01 - dummy tag if (!cdol_data_tlv){ PrintAndLog("ERROR: can't create CDOL1 TLV."); - return 4; + dreturn(6); } PrintAndLog("CDOL1 data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len)); @@ -671,17 +708,25 @@ int CmdHFEMVExec(const char *cmd) { // EMVAC_TC + EMVAC_CDAREQ --- to get SDAD res = EMVAC(true, (TrType == TT_CDA) ? EMVAC_TC + EMVAC_CDAREQ : EMVAC_TC, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); - free(cdol_data_tlv); - if (res) { PrintAndLog("AC1 error(%d): %4x. Exit...", res, sw); - return 5; + dreturn(7); } if (decodeTLV) TLVPrintFromBuffer(buf, len); - PrintAndLog("* M/Chip transaction result:"); + // CDA + PrintAndLog("\n* CDA:"); + struct tlvdb *ac_tlv = tlvdb_parse_multi(buf, len); + res = trCDA(tlvRoot, ac_tlv, pdol_data_tlv, cdol_data_tlv); + if (res) { + PrintAndLog("CDA error (%d)", res); + } + free(ac_tlv); + free(cdol_data_tlv); + + PrintAndLog("\n* M/Chip transaction result:"); // 9F27: Cryptogram Information Data (CID) const struct tlv *CID = tlvdb_get(tlvRoot, 0x9F27, NULL); if (CID) { @@ -743,7 +788,7 @@ int CmdHFEMVExec(const char *cmd) { struct tlv *udol_data_tlv = dol_process(UDOL ? UDOL : &defUDOL, tlvRoot, 0x01); // 0x01 - dummy tag if (!udol_data_tlv){ PrintAndLog("ERROR: can't create UDOL TLV."); - return 4; + dreturn(8); } PrintAndLog("UDOL data[%d]: %s", udol_data_tlv->len, sprint_hex(udol_data_tlv->value, udol_data_tlv->len)); @@ -753,24 +798,27 @@ int CmdHFEMVExec(const char *cmd) { res = MSCComputeCryptoChecksum(true, (uint8_t *)udol_data_tlv->value, udol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); if (res) { PrintAndLog("ERROR Compute Crypto Checksum. APDU error %4x", sw); - return 5; + free(udol_data_tlv); + dreturn(9); } if (decodeTLV) { TLVPrintFromBuffer(buf, len); PrintAndLog(""); } + free(udol_data_tlv); } } else { PrintAndLog("ERROR MSD: Track2 data not found."); } } - + // DropField DropField(); // Destroy TLV's + free(pdol_data_tlv); tlvdb_free(tlvSelect); tlvdb_free(tlvRoot); @@ -779,6 +827,10 @@ int CmdHFEMVExec(const char *cmd) { return 0; } +int CmdHFEMVTest(const char *cmd) { + return ExecuteCryptoTests(true); +} + int CmdHelp(const char *Cmd); static command_t CommandTable[] = { {"help", CmdHelp, 1, "This help"}, @@ -786,6 +838,7 @@ static command_t CommandTable[] = { {"pse", CmdHFEMVPPSE, 0, "Execute PPSE. It selects 2PAY.SYS.DDF01 or 1PAY.SYS.DDF01 directory."}, {"search", CmdHFEMVSearch, 0, "Try to select all applets from applets list and print installed applets."}, {"select", CmdHFEMVSelect, 0, "Select applet."}, + {"test", CmdHFEMVTest, 0, "Crypto logic test."}, {NULL, NULL, 0, NULL} }; diff --git a/client/emv/cmdemv.h b/client/emv/cmdemv.h index 78796efa..b3f76508 100644 --- a/client/emv/cmdemv.h +++ b/client/emv/cmdemv.h @@ -15,6 +15,7 @@ #include #include #include +#include #include "proxmark3.h" #include "ui.h" #include "cmdparser.h" diff --git a/client/emv/crypto.c b/client/emv/crypto.c new file mode 100644 index 00000000..c8ced6d4 --- /dev/null +++ b/client/emv/crypto.c @@ -0,0 +1,179 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "crypto.h" +#include "crypto_backend.h" + +#include + +static struct crypto_backend *crypto_backend; + +static bool crypto_init(void) +{ + if (crypto_backend) + return true; + + crypto_backend = crypto_polarssl_init(); + + if (!crypto_backend) + return false; + + return true; +} + +struct crypto_hash *crypto_hash_open(enum crypto_algo_hash hash) +{ + struct crypto_hash *ch; + + if (!crypto_init()) + return NULL; + + ch = crypto_backend->hash_open(hash); + if (ch) + ch->algo = hash; + + return ch; +} + +void crypto_hash_close(struct crypto_hash *ch) +{ + ch->close(ch); +} + +void crypto_hash_write(struct crypto_hash *ch, const unsigned char *buf, size_t len) +{ + ch->write(ch, buf, len); +} + +unsigned char *crypto_hash_read(struct crypto_hash *ch) +{ + return ch->read(ch); +} + +size_t crypto_hash_get_size(const struct crypto_hash *ch) +{ + return ch->get_size(ch); +} + +struct crypto_pk *crypto_pk_open(enum crypto_algo_pk pk, ...) +{ + struct crypto_pk *cp; + va_list vl; + + if (!crypto_init()) + return NULL; + + va_start(vl, pk); + cp = crypto_backend->pk_open(pk, vl); + va_end(vl); + + if (cp) + cp->algo = pk; + + return cp; +} + +struct crypto_pk *crypto_pk_open_priv(enum crypto_algo_pk pk, ...) +{ + struct crypto_pk *cp; + va_list vl; + + if (!crypto_init()) + return NULL; + + if (!crypto_backend->pk_open_priv) + return NULL; + + va_start(vl, pk); + cp = crypto_backend->pk_open_priv(pk, vl); + va_end(vl); + + if (cp) + cp->algo = pk; + + return cp; +} + +struct crypto_pk *crypto_pk_genkey(enum crypto_algo_pk pk, ...) +{ + struct crypto_pk *cp; + va_list vl; + + if (!crypto_init()) + return NULL; + + if (!crypto_backend->pk_genkey) + return NULL; + + va_start(vl, pk); + cp = crypto_backend->pk_genkey(pk, vl); + va_end(vl); + + if (cp) + cp->algo = pk; + + return cp; +} + +void crypto_pk_close(struct crypto_pk *cp) +{ + cp->close(cp); +} + +unsigned char *crypto_pk_encrypt(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen) +{ + return cp->encrypt(cp, buf, len, clen); +} + +unsigned char *crypto_pk_decrypt(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen) +{ + if (!cp->decrypt) { + *clen = 0; + + return NULL; + } + + return cp->decrypt(cp, buf, len, clen); +} + +enum crypto_algo_pk crypto_pk_get_algo(const struct crypto_pk *cp) +{ + if (!cp) + return PK_INVALID; + + return cp->algo; +} + +size_t crypto_pk_get_nbits(const struct crypto_pk *cp) +{ + if (!cp->get_nbits) + return 0; + + return cp->get_nbits(cp); +} + +unsigned char *crypto_pk_get_parameter(const struct crypto_pk *cp, unsigned param, size_t *plen) +{ + *plen = 0; + + if (!cp->get_parameter) + return NULL; + + return cp->get_parameter(cp, param, plen); +} diff --git a/client/emv/crypto.h b/client/emv/crypto.h new file mode 100644 index 00000000..940e8b2b --- /dev/null +++ b/client/emv/crypto.h @@ -0,0 +1,48 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef CRYPTO_H +#define CRYPTO_H + +#include +#include + +enum crypto_algo_hash { + HASH_INVALID, + HASH_SHA_1, +}; + +struct crypto_hash *crypto_hash_open(enum crypto_algo_hash hash); +void crypto_hash_close(struct crypto_hash *ch); +void crypto_hash_write(struct crypto_hash *ch, const unsigned char *buf, size_t len); +unsigned char *crypto_hash_read(struct crypto_hash *ch); +size_t crypto_hash_get_size(const struct crypto_hash *ch); + +enum crypto_algo_pk { + PK_INVALID, + PK_RSA, +}; + +struct crypto_pk *crypto_pk_open(enum crypto_algo_pk pk, ...); +struct crypto_pk *crypto_pk_open_priv(enum crypto_algo_pk pk, ...); +struct crypto_pk *crypto_pk_genkey(enum crypto_algo_pk pk, ...); +void crypto_pk_close(struct crypto_pk *cp); +unsigned char *crypto_pk_encrypt(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen); +unsigned char *crypto_pk_decrypt(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen); +enum crypto_algo_pk crypto_pk_get_algo(const struct crypto_pk *cp); +size_t crypto_pk_get_nbits(const struct crypto_pk *cp); +unsigned char *crypto_pk_get_parameter(const struct crypto_pk *cp, unsigned param, size_t *plen); + +#endif diff --git a/client/emv/crypto_backend.h b/client/emv/crypto_backend.h new file mode 100644 index 00000000..a815ae11 --- /dev/null +++ b/client/emv/crypto_backend.h @@ -0,0 +1,50 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef CRYPTO_BACKEND_H +#define CRYPTO_BACKEND_H + +#include "crypto.h" + +#include +#include + +struct crypto_hash { + enum crypto_algo_hash algo; + void (*write)(struct crypto_hash *ch, const unsigned char *buf, size_t len); + unsigned char *(*read)(struct crypto_hash *ch); + void (*close)(struct crypto_hash *ch); + size_t (*get_size)(const struct crypto_hash *ch); +}; + +struct crypto_pk { + enum crypto_algo_pk algo; + unsigned char *(*encrypt)(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen); + unsigned char *(*decrypt)(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen); + unsigned char *(*get_parameter)(const struct crypto_pk *cp, unsigned param, size_t *plen); + size_t (*get_nbits)(const struct crypto_pk *cp); + void (*close)(struct crypto_pk *cp); +}; + +struct crypto_backend { + struct crypto_hash *(*hash_open)(enum crypto_algo_hash hash); + struct crypto_pk *(*pk_open)(enum crypto_algo_pk pk, va_list vl); + struct crypto_pk *(*pk_open_priv)(enum crypto_algo_pk pk, va_list vl); + struct crypto_pk *(*pk_genkey)(enum crypto_algo_pk pk, va_list vl); +}; + +struct crypto_backend *crypto_polarssl_init(void); + +#endif diff --git a/client/emv/crypto_polarssl.c b/client/emv/crypto_polarssl.c new file mode 100644 index 00000000..760395c4 --- /dev/null +++ b/client/emv/crypto_polarssl.c @@ -0,0 +1,351 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * Copyright (C) 2017 Merlok + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "crypto.h" +#include "crypto_backend.h" + +#include +#include +#include + +#include "rsa.h" +#include "sha1.h" + +struct crypto_hash_polarssl { + struct crypto_hash ch; + sha1_context ctx; +}; + +static void crypto_hash_polarssl_close(struct crypto_hash *_ch) +{ + struct crypto_hash_polarssl *ch = (struct crypto_hash_polarssl *)_ch; + + free(ch); +} + +static void crypto_hash_polarssl_write(struct crypto_hash *_ch, const unsigned char *buf, size_t len) +{ + struct crypto_hash_polarssl *ch = (struct crypto_hash_polarssl *)_ch; + + sha1_update(&(ch->ctx), buf, len); +} + +static unsigned char *crypto_hash_polarssl_read(struct crypto_hash *_ch) +{ + struct crypto_hash_polarssl *ch = (struct crypto_hash_polarssl *)_ch; + + static unsigned char sha1sum[20]; + sha1_finish(&(ch->ctx), sha1sum); + return sha1sum; +} + +static size_t crypto_hash_polarssl_get_size(const struct crypto_hash *ch) +{ + if (ch->algo == HASH_SHA_1) + return 20; + else + return 0; +} + +static struct crypto_hash *crypto_hash_polarssl_open(enum crypto_algo_hash hash) +{ + if (hash != HASH_SHA_1) + return NULL; + + struct crypto_hash_polarssl *ch = malloc(sizeof(*ch)); + + sha1_starts(&(ch->ctx)); + + ch->ch.write = crypto_hash_polarssl_write; + ch->ch.read = crypto_hash_polarssl_read; + ch->ch.close = crypto_hash_polarssl_close; + ch->ch.get_size = crypto_hash_polarssl_get_size; + + return &ch->ch; +} + +struct crypto_pk_polarssl { + struct crypto_pk cp; + rsa_context ctx; +}; + +static struct crypto_pk *crypto_pk_polarssl_open_rsa(va_list vl) +{ + struct crypto_pk_polarssl *cp = malloc(sizeof(*cp)); + memset(cp, 0x00, sizeof(*cp)); + + char *mod = va_arg(vl, char *); // N + int modlen = va_arg(vl, size_t); + char *exp = va_arg(vl, char *); // E + int explen = va_arg(vl, size_t); + + rsa_init(&cp->ctx, RSA_PKCS_V15, 0); + + cp->ctx.len = modlen; // size(N) in bytes + mpi_read_binary(&cp->ctx.N, (const unsigned char *)mod, modlen); + mpi_read_binary(&cp->ctx.E, (const unsigned char *)exp, explen); + + int res = rsa_check_pubkey(&cp->ctx); + if(res != 0) { + fprintf(stderr, "PolarSSL public key error res=%x exp=%d mod=%d.\n", res * -1, explen, modlen); + + return NULL; + } + + return &cp->cp; +} + +static struct crypto_pk *crypto_pk_polarssl_open_priv_rsa(va_list vl) +{ + struct crypto_pk_polarssl *cp = malloc(sizeof(*cp)); + memset(cp, 0x00, sizeof(*cp)); + char *mod = va_arg(vl, char *); + int modlen = va_arg(vl, size_t); + char *exp = va_arg(vl, char *); + int explen = va_arg(vl, size_t); + char *d = va_arg(vl, char *); + int dlen = va_arg(vl, size_t); + char *p = va_arg(vl, char *); + int plen = va_arg(vl, size_t); + char *q = va_arg(vl, char *); + int qlen = va_arg(vl, size_t); + char *dp = va_arg(vl, char *); + int dplen = va_arg(vl, size_t); + char *dq = va_arg(vl, char *); + int dqlen = va_arg(vl, size_t); + // calc QP via Q and P +// char *inv = va_arg(vl, char *); +// int invlen = va_arg(vl, size_t); + + rsa_init(&cp->ctx, RSA_PKCS_V15, 0); + + cp->ctx.len = modlen; // size(N) in bytes + mpi_read_binary(&cp->ctx.N, (const unsigned char *)mod, modlen); + mpi_read_binary(&cp->ctx.E, (const unsigned char *)exp, explen); + + mpi_read_binary(&cp->ctx.D, (const unsigned char *)d, dlen); + mpi_read_binary(&cp->ctx.P, (const unsigned char *)p, plen); + mpi_read_binary(&cp->ctx.Q, (const unsigned char *)q, qlen); + mpi_read_binary(&cp->ctx.DP, (const unsigned char *)dp, dplen); + mpi_read_binary(&cp->ctx.DQ, (const unsigned char *)dq, dqlen); + mpi_inv_mod(&cp->ctx.QP, &cp->ctx.Q, &cp->ctx.P); + + int res = rsa_check_privkey(&cp->ctx); + if(res != 0) { + fprintf(stderr, "PolarSSL private key error res=%x exp=%d mod=%d.\n", res * -1, explen, modlen); + return NULL; + } + + return &cp->cp; +} + +static int myrand(void *rng_state, unsigned char *output, size_t len) { + size_t i; + + if(rng_state != NULL) + rng_state = NULL; + + for( i = 0; i < len; ++i ) + output[i] = rand(); + + return 0; +} + + +static struct crypto_pk *crypto_pk_polarssl_genkey_rsa(va_list vl) +{ + struct crypto_pk_polarssl *cp = malloc(sizeof(*cp)); + memset(cp, 0x00, sizeof(*cp)); + + int transient = va_arg(vl, int); + unsigned int nbits = va_arg(vl, unsigned int); + unsigned int exp = va_arg(vl, unsigned int); + + if (transient) { + } + + int res = rsa_gen_key(&cp->ctx, &myrand, NULL, nbits, exp); + if (res) { + fprintf(stderr, "PolarSSL private key generation error res=%x exp=%d nbits=%d.\n", res * -1, exp, nbits); + return NULL; + } + + return &cp->cp; +} + +static void crypto_pk_polarssl_close(struct crypto_pk *_cp) +{ + struct crypto_pk_polarssl *cp = (struct crypto_pk_polarssl *)_cp; + + rsa_free(&cp->ctx); + free(cp); +} + +static unsigned char *crypto_pk_polarssl_encrypt(const struct crypto_pk *_cp, const unsigned char *buf, size_t len, size_t *clen) +{ + struct crypto_pk_polarssl *cp = (struct crypto_pk_polarssl *)_cp; + int res; + unsigned char *result; + + *clen = 0; + size_t keylen = mpi_size(&cp->ctx.N); + + result = malloc(keylen); + if (!result) { + printf("RSA encrypt failed. Can't allocate result memory.\n"); + return NULL; + } + + res = rsa_public(&cp->ctx, buf, result); + if(res) { + printf("RSA encrypt failed. Error: %x data len: %d key len: %d\n", res * -1, len, keylen); + return NULL; + } + + *clen = keylen; + + return result; +} + +static unsigned char *crypto_pk_polarssl_decrypt(const struct crypto_pk *_cp, const unsigned char *buf, size_t len, size_t *clen) +{ + struct crypto_pk_polarssl *cp = (struct crypto_pk_polarssl *)_cp; + int res; + unsigned char *result; + + *clen = 0; + size_t keylen = mpi_size(&cp->ctx.N); + + result = malloc(keylen); + if (!result) { + printf("RSA encrypt failed. Can't allocate result memory.\n"); + return NULL; + } + + res = rsa_private(&cp->ctx, buf, result); // CHECK??? + if(res) { + printf("RSA decrypt failed. Error: %x data len: %d key len: %d\n", res * -1, len, keylen); + return NULL; + } + + *clen = keylen; + + return result; +} + +static size_t crypto_pk_polarssl_get_nbits(const struct crypto_pk *_cp) +{ + struct crypto_pk_polarssl *cp = (struct crypto_pk_polarssl *)_cp; + + return cp->ctx.len * 8; +return 0; +} + +static unsigned char *crypto_pk_polarssl_get_parameter(const struct crypto_pk *_cp, unsigned param, size_t *plen) +{ + struct crypto_pk_polarssl *cp = (struct crypto_pk_polarssl *)_cp; + unsigned char *result = NULL; + switch(param){ + // mod + case 0: + *plen = mpi_size(&cp->ctx.N); + result = malloc(*plen); + memset(result, 0x00, *plen); + mpi_write_binary(&cp->ctx.N, result, *plen); + break; + // exp + case 1: + *plen = mpi_size(&cp->ctx.E); + result = malloc(*plen); + memset(result, 0x00, *plen); + mpi_write_binary(&cp->ctx.E, result, *plen); + break; + default: + printf("Error get parameter. Param=%d", param); + break; + } + + return result; +} + +static struct crypto_pk *crypto_pk_polarssl_open(enum crypto_algo_pk pk, va_list vl) +{ + struct crypto_pk *cp; + + if (pk == PK_RSA) + cp = crypto_pk_polarssl_open_rsa(vl); + else + return NULL; + + cp->close = crypto_pk_polarssl_close; + cp->encrypt = crypto_pk_polarssl_encrypt; + cp->get_parameter = crypto_pk_polarssl_get_parameter; + cp->get_nbits = crypto_pk_polarssl_get_nbits; + + return cp; +} + +static struct crypto_pk *crypto_pk_polarssl_open_priv(enum crypto_algo_pk pk, va_list vl) +{ + struct crypto_pk *cp; + + if (pk == PK_RSA) + cp = crypto_pk_polarssl_open_priv_rsa(vl); + else + return NULL; + + cp->close = crypto_pk_polarssl_close; + cp->encrypt = crypto_pk_polarssl_encrypt; + cp->decrypt = crypto_pk_polarssl_decrypt; + cp->get_parameter = crypto_pk_polarssl_get_parameter; + cp->get_nbits = crypto_pk_polarssl_get_nbits; + + return cp; +} + +static struct crypto_pk *crypto_pk_polarssl_genkey(enum crypto_algo_pk pk, va_list vl) +{ + struct crypto_pk *cp; + + if (pk == PK_RSA) + cp = crypto_pk_polarssl_genkey_rsa(vl); + else + return NULL; + + cp->close = crypto_pk_polarssl_close; + cp->encrypt = crypto_pk_polarssl_encrypt; + cp->decrypt = crypto_pk_polarssl_decrypt; + cp->get_parameter = crypto_pk_polarssl_get_parameter; + cp->get_nbits = crypto_pk_polarssl_get_nbits; + + return cp; +} + +static struct crypto_backend crypto_polarssl_backend = { + .hash_open = crypto_hash_polarssl_open, + .pk_open = crypto_pk_polarssl_open, + .pk_open_priv = crypto_pk_polarssl_open_priv, + .pk_genkey = crypto_pk_polarssl_genkey, +}; + +struct crypto_backend *crypto_polarssl_init(void) +{ + return &crypto_polarssl_backend; +} diff --git a/client/emv/emv_pk.c b/client/emv/emv_pk.c new file mode 100644 index 00000000..b577855e --- /dev/null +++ b/client/emv/emv_pk.c @@ -0,0 +1,524 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* For asprintf */ +#define _GNU_SOURCE +#include + +#include "emv_pk.h" +#include "crypto.h" +#include "proxmark3.h" + +#include +#include +#include +#include + +#define BCD(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') : \ + -1) + +#define HEX(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') : \ + ((c) >= 'A' && (c) <= 'F') ? ((c) - 'A' + 10) : \ + ((c) >= 'a' && (c) <= 'f') ? ((c) - 'a' + 10) : \ + -1) + +#define TOHEX(v) ((v) < 10 ? (v) + '0' : (v) - 10 + 'a') + +static ssize_t emv_pk_read_bin(char *buf, unsigned char *bin, size_t size, size_t *read) +{ + size_t left = size; + char *p = buf; + while (*p && *p == ' ') + p++; + + while (left > 0) { + int c1, c2; + c1 = HEX(*p); + if (c1 == -1) + return -(p - buf); + p++; + c2 = HEX(*p); + if (c2 == -1) + return -(p - buf); + p++; + *bin = (c1 * 16 + c2); + bin ++; + left --; + if (*p == ':') + p++; + else if (read) { + *read = (size - left); + break; + } else if (left == 0) + break; + else + return -(p - buf); + } + + while (*p && *p == ' ') + p++; + + p--; + + return (p - buf); +} + +static ssize_t emv_pk_read_ymv(char *buf, unsigned *ymv) +{ + int i; + unsigned char temp[3]; + char *p = buf; + + *ymv = 0; + + while (*p && *p == ' ') + p++; + + for (i = 0; i < 3; i++) { + int c1, c2; + c1 = BCD(*p); + if (c1 == -1) + return -(p - buf); + p++; + c2 = BCD(*p); + if (c2 == -1) + return -(p - buf); + p++; + temp[i] = (c1 * 16 + c2); + } + + while (*p && *p == ' ') + p++; + + p--; + + if (temp[1] > 0x12 || temp[2] > 0x31) + return -(p - buf); + + *ymv = (temp[0] * 0x10000 + temp[1] * 0x100 + temp[2]); + + return (p - buf); +} + +static ssize_t emv_pk_read_string(char *buf, char *str, size_t size) +{ + char *p = buf; + while (*p && *p == ' ') + p++; + + while (size > 1) { + if (*p == ' ') + break; + else if (*p < 0x20 || *p >= 0x7f) + return -(p - buf); + *str = *p; + p++; + str ++; + size --; + } + + *str = 0; + + while (*p && *p == ' ') + p++; + + p--; + + return (p - buf); +} + + +struct emv_pk *emv_pk_parse_pk(char *buf) +{ + struct emv_pk *r = calloc(1, sizeof(*r)); + ssize_t l; + char temp[10]; + + l = emv_pk_read_bin(buf, r->rid, 5, NULL); + if (l <= 0) + goto out; + buf += l; + + l = emv_pk_read_bin(buf, &r->index, 1, NULL); + if (l <= 0) + goto out; + buf += l; + + l = emv_pk_read_ymv(buf, &r->expire); + if (l <= 0) + goto out; + buf += l; + + l = emv_pk_read_string(buf, temp, sizeof(temp)); + if (l <= 0) + goto out; + buf += l; + + if (!strcmp(temp, "rsa")) + r->pk_algo = PK_RSA; + else + goto out; + + l = emv_pk_read_bin(buf, r->exp, sizeof(r->exp), &r->elen); + if (l <= 0) + goto out; + buf += l; + + r->modulus = malloc(2048/8); + l = emv_pk_read_bin(buf, r->modulus, 2048/8, &r->mlen); + if (l <= 0) + goto out2; + buf += l; + + l = emv_pk_read_string(buf, temp, sizeof(temp)); + if (l <= 0) + goto out2; + buf += l; + + if (!strcmp(temp, "sha1")) + r->hash_algo = HASH_SHA_1; + else + goto out2; + + l = emv_pk_read_bin(buf, r->hash, 20, NULL); + if (l <= 0) + goto out2; + + return r; + +out2: + free(r->modulus); +out: + free(r); + return NULL; +} + +static size_t emv_pk_write_bin(char *out, size_t outlen, const unsigned char *buf, size_t len) +{ + int i; + size_t pos = 0; + + if (len == 0) + return 0; + if (outlen < len * 3) + return 0; + + out[pos++] = TOHEX(buf[0] >> 4); + out[pos++] = TOHEX(buf[0] & 0xf); + for (i = 1; i < len; i++) { + out[pos++] = ':'; + out[pos++] = TOHEX(buf[i] >> 4); + out[pos++] = TOHEX(buf[i] & 0xf); + } + out[pos++] = ' '; + + return pos; +} + +static size_t emv_pk_write_str(char *out, size_t outlen, const char *str) +{ + size_t len = strlen(str); + + if (len == 0) + return 0; + if (outlen < len) + return 0; + + memcpy(out, str, len); + + return len; +} + +char *emv_pk_dump_pk(const struct emv_pk *pk) +{ + size_t outsize = 1024; /* should be enough */ + char *out = malloc(outsize); /* should be enough */ + size_t outpos = 0; + size_t rc; + + if (!out) + return NULL; + + rc = emv_pk_write_bin(out + outpos, outsize - outpos, pk->rid, 5); + if (rc == 0) + goto err; + outpos += rc; + + rc = emv_pk_write_bin(out + outpos, outsize - outpos, &pk->index, 1); + if (rc == 0) + goto err; + outpos += rc; + + if (outpos + 7 > outsize) + goto err; + out[outpos++] = TOHEX((pk->expire >> 20) & 0xf); + out[outpos++] = TOHEX((pk->expire >> 16) & 0xf); + out[outpos++] = TOHEX((pk->expire >> 12) & 0xf); + out[outpos++] = TOHEX((pk->expire >> 8 ) & 0xf); + out[outpos++] = TOHEX((pk->expire >> 4 ) & 0xf); + out[outpos++] = TOHEX((pk->expire >> 0 ) & 0xf); + out[outpos++] = ' '; + + if (pk->pk_algo == PK_RSA) { + rc = emv_pk_write_str(out + outpos, outsize - outpos, "rsa"); + if (rc == 0) + goto err; + outpos += rc; + out[outpos++] = ' '; + } else { + if (outpos + 4 > outsize) + goto err; + out[outpos++] = '?'; + out[outpos++] = '?'; + out[outpos++] = TOHEX(pk->pk_algo >> 4); + out[outpos++] = TOHEX(pk->pk_algo & 0xf); + } + + rc = emv_pk_write_bin(out + outpos, outsize - outpos, pk->exp, pk->elen); + if (rc == 0) + goto err; + outpos += rc; + + rc = emv_pk_write_bin(out + outpos, outsize - outpos, pk->modulus, pk->mlen); + if (rc == 0) + goto err; + outpos += rc; + + if (pk->hash_algo == HASH_SHA_1) { + rc = emv_pk_write_str(out + outpos, outsize - outpos, "sha1"); + if (rc == 0) + goto err; + outpos += rc; + out[outpos++] = ' '; + } else { + if (outpos + 4 > outsize) + goto err; + out[outpos++] = '?'; + out[outpos++] = '?'; + out[outpos++] = TOHEX(pk->pk_algo >> 4); + out[outpos++] = TOHEX(pk->pk_algo & 0xf); + } + + + rc = emv_pk_write_bin(out + outpos, outsize - outpos, pk->hash, 20); + if (rc == 0) + goto err; + outpos += rc; + + out[outpos-1] = '\0'; + + return out; + +err: + free(out); + return NULL; +} + +bool emv_pk_verify(const struct emv_pk *pk) +{ + struct crypto_hash *ch = crypto_hash_open(pk->hash_algo); + if (!ch) + return false; + + crypto_hash_write(ch, pk->rid, sizeof(pk->rid)); + crypto_hash_write(ch, &pk->index, 1); + crypto_hash_write(ch, pk->modulus, pk->mlen); + crypto_hash_write(ch, pk->exp, pk->elen); + + unsigned char *h = crypto_hash_read(ch); + if (!h) { + crypto_hash_close(ch); + return false; + } + + size_t hsize = crypto_hash_get_size(ch); + bool r = hsize && !memcmp(h, pk->hash, hsize) ? true : false; + + crypto_hash_close(ch); + + return r; +} + +struct emv_pk *emv_pk_new(size_t modlen, size_t explen) +{ + struct emv_pk *pk; + + /* Not supported ATM */ + if (explen > 3) + return NULL; + + pk = calloc(1, sizeof(*pk)); + if (!pk) + return NULL; + + pk->mlen = modlen; + pk->elen = explen; + + pk->modulus = calloc(modlen, 1); + if (!pk->modulus) { + free(pk); + pk = NULL; + } + + return pk; +} + +void emv_pk_free(struct emv_pk *pk) +{ + if (!pk) + return; + + free(pk->modulus); + free(pk); +} + +static struct emv_pk *emv_pk_get_ca_pk_from_file(const char *fname, + const unsigned char *rid, + unsigned char idx) +{ + if (!fname) + return NULL; + + FILE *f = fopen(fname, "r"); + if (!f) { + perror("fopen"); + return NULL; + } + + while (!feof(f)) { + char buf[2048]; + if (fgets(buf, sizeof(buf), f) == NULL) + break; + + struct emv_pk *pk = emv_pk_parse_pk(buf); + if (!pk) + continue; + if (memcmp(pk->rid, rid, 5) || pk->index != idx) { + emv_pk_free(pk); + continue; + } + + fclose(f); + + return pk; + } + + fclose(f); + + return NULL; +} + +char *emv_pk_get_ca_pk_file(const char *dirname, const unsigned char *rid, unsigned char idx) +{ + if (!dirname) + dirname = ".";//openemv_config_get_str("capk.dir", NULL); + + if (!dirname) + return NULL; + + char *filename; + int ret = asprintf(&filename, "%s/%02hhx%02hhx%02hhx%02hhx%02hhx_%02hhx.0", + dirname, + rid[0], + rid[1], + rid[2], + rid[3], + rid[4], + idx); + + if (ret <= 0) + return NULL; + + return filename; +} + +char *emv_pk_get_ca_pk_rid_file(const char *dirname, const unsigned char *rid) +{ + if (!dirname) + dirname = "."; //openemv_config_get_str("capk.dir", NULL); + + if (!dirname) + return NULL; + + char *filename; + int ret = asprintf(&filename, "%s/%02hhx%02hhx%02hhx%02hhx%02hhx.pks", + dirname, + rid[0], + rid[1], + rid[2], + rid[3], + rid[4]); + + if (ret <= 0) + return NULL; + + return filename; +} + +struct emv_pk *emv_pk_get_ca_pk(const unsigned char *rid, unsigned char idx) +{ + struct emv_pk *pk = NULL; + +/* if (!pk) { + char *fname = emv_pk_get_ca_pk_file(NULL, rid, idx); + if (fname) { + pk = emv_pk_get_ca_pk_from_file(fname, rid, idx); + free(fname); + } + } + + if (!pk) { + char *fname = emv_pk_get_ca_pk_rid_file(NULL, rid); + if (fname) { + pk = emv_pk_get_ca_pk_from_file(fname, rid, idx); + free(fname); + } + } +*/ + if (!pk) { + const char *relfname = "emv/capk.txt"; + + char fname[strlen(get_my_executable_directory()) + strlen(relfname) + 1]; + strcpy(fname, get_my_executable_directory()); + strcat(fname, relfname); + + pk = emv_pk_get_ca_pk_from_file(fname, rid, idx); + } + if (!pk) + return NULL; + + printf("Verifying CA PK for %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx %zd bits...", + pk->rid[0], + pk->rid[1], + pk->rid[2], + pk->rid[3], + pk->rid[4], + pk->index, + pk->mlen * 8); + if (emv_pk_verify(pk)) { + printf("OK\n"); + + return pk; + } + + printf("Failed!\n"); + emv_pk_free(pk); + + return NULL; +} diff --git a/client/emv/emv_pk.h b/client/emv/emv_pk.h new file mode 100644 index 00000000..e291a065 --- /dev/null +++ b/client/emv/emv_pk.h @@ -0,0 +1,48 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef EMV_PK_H +#define EMV_PK_H + +#include +#include + +struct emv_pk { + unsigned char rid[5]; + unsigned char index; + unsigned char serial[3]; + unsigned char pan[10]; + unsigned char hash_algo; + unsigned char pk_algo; + unsigned char hash[20]; + unsigned char exp[3]; + size_t elen; + size_t mlen; + unsigned char *modulus; + unsigned int expire; +}; + +#define EXPIRE(yy, mm, dd) 0x ## yy ## mm ## dd + +struct emv_pk *emv_pk_parse_pk(char *buf); +struct emv_pk *emv_pk_new(size_t modlen, size_t explen); +void emv_pk_free(struct emv_pk *pk); +char *emv_pk_dump_pk(const struct emv_pk *pk); +bool emv_pk_verify(const struct emv_pk *pk); + +char *emv_pk_get_ca_pk_file(const char *dirname, const unsigned char *rid, unsigned char idx); +char *emv_pk_get_ca_pk_rid_file(const char *dirname, const unsigned char *rid); +struct emv_pk *emv_pk_get_ca_pk(const unsigned char *rid, unsigned char idx); +#endif diff --git a/client/emv/emv_pki.c b/client/emv/emv_pki.c new file mode 100644 index 00000000..7803060e --- /dev/null +++ b/client/emv/emv_pki.c @@ -0,0 +1,511 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "emv_pki.h" +#include "crypto.h" +#include "dump.h" +#include "util.h" + +#include +#include +#include +#include + +static const unsigned char empty_tlv_value[] = {}; +static const struct tlv empty_tlv = {.tag = 0x0, .len = 0, .value = empty_tlv_value}; + +static size_t emv_pki_hash_psn[256] = { 0, 0, 11, 2, 17, 2, }; + +static unsigned char *emv_pki_decode_message(const struct emv_pk *enc_pk, + uint8_t msgtype, + size_t *len, + const struct tlv *cert_tlv, + ... /* A list of tlv pointers, end with NULL */ + ) +{ + struct crypto_pk *kcp; + unsigned char *data; + size_t data_len; + va_list vl; + + if (!enc_pk) + return NULL; + + if (!cert_tlv) { + printf("ERROR: Can't find certificate\n"); + return NULL; + } + + if (cert_tlv->len != enc_pk->mlen) { + printf("ERROR: Certificate length (%d) not equal key length (%d)\n", cert_tlv->len, enc_pk->mlen); + return NULL; + } + kcp = crypto_pk_open(enc_pk->pk_algo, + enc_pk->modulus, enc_pk->mlen, + enc_pk->exp, enc_pk->elen); + if (!kcp) + return NULL; + + data = crypto_pk_encrypt(kcp, cert_tlv->value, cert_tlv->len, &data_len); + crypto_pk_close(kcp); + +/* if (true){ + printf("Recovered data:\n"); + dump_buffer(data, data_len, stdout, 0); + }*/ + + if (data[data_len-1] != 0xbc || data[0] != 0x6a || data[1] != msgtype) { + printf("ERROR: Certificate format\n"); + free(data); + return NULL; + } + + size_t hash_pos = emv_pki_hash_psn[msgtype]; + if (hash_pos == 0 || hash_pos > data_len){ + printf("ERROR: Cant get hash position in the certificate\n"); + free(data); + return NULL; + } + + struct crypto_hash *ch; + ch = crypto_hash_open(data[hash_pos]); + if (!ch) { + printf("ERROR: Cant do hash\n"); + free(data); + return NULL; + } + + size_t hash_len = crypto_hash_get_size(ch); + crypto_hash_write(ch, data + 1, data_len - 2 - hash_len); + + va_start(vl, cert_tlv); + while (true) { + const struct tlv *add_tlv = va_arg(vl, const struct tlv *); + if (!add_tlv) + break; + + crypto_hash_write(ch, add_tlv->value, add_tlv->len); + } + va_end(vl); + + if (memcmp(data + data_len - 1 - hash_len, crypto_hash_read(ch), hash_len)) { + printf("ERROR: Calculated wrong hash\n"); + printf("decoded: %s\n",sprint_hex(data + data_len - 1 - hash_len, hash_len)); + printf("calculated: %s\n",sprint_hex(crypto_hash_read(ch), hash_len)); + crypto_hash_close(ch); + free(data); + return NULL; + } + + crypto_hash_close(ch); + + *len = data_len - hash_len - 1; + + return data; +} + +static unsigned emv_cn_length(const struct tlv *tlv) +{ + int i; + + for (i = 0; i < tlv->len; i++) { + unsigned char c = tlv->value[i]; + + if (c >> 4 == 0xf) + return 2 * i; + + if ((c & 0xf) == 0xf) + return 2 * i + 1; + } + + return 2 * tlv->len; +} + +static unsigned char emv_cn_get(const struct tlv *tlv, unsigned pos) +{ + if (pos > tlv->len * 2) + return 0xf; + + unsigned char c = tlv->value[pos / 2]; + + if (pos % 2) + return c & 0xf; + else + return c >> 4; +} + +static struct emv_pk *emv_pki_decode_key_ex(const struct emv_pk *enc_pk, + unsigned char msgtype, + const struct tlv *pan_tlv, + const struct tlv *cert_tlv, + const struct tlv *exp_tlv, + const struct tlv *rem_tlv, + const struct tlv *add_tlv, + bool showData + ) +{ + size_t pan_length; + unsigned char *data; + size_t data_len; + size_t pk_len; + + if (!cert_tlv || !exp_tlv || !pan_tlv) + return NULL; + + if (!rem_tlv) + rem_tlv = &empty_tlv; + + if (msgtype == 2) + pan_length = 4; + else if (msgtype == 4) + pan_length = 10; + else { + printf("ERROR: Message type must be 2 or 4\n"); + return NULL; + } + + data = emv_pki_decode_message(enc_pk, msgtype, &data_len, + cert_tlv, + rem_tlv, + exp_tlv, + add_tlv, + NULL); + if (!data || data_len < 11 + pan_length) { + printf("ERROR: Can't decode message\n"); + return NULL; + } + + if (showData){ + printf("Recovered data:\n"); + dump_buffer(data, data_len, stdout, 0); + } + + /* Perform the rest of checks here */ + + struct tlv pan2_tlv = { + .tag = 0x5a, + .len = pan_length, + .value = &data[2], + }; + unsigned pan_len = emv_cn_length(pan_tlv); + unsigned pan2_len = emv_cn_length(&pan2_tlv); + + if (((msgtype == 2) && (pan2_len < 4 || pan2_len > pan_len)) || + ((msgtype == 4) && (pan2_len != pan_len))) { + printf("ERROR: Invalid PAN lengths\n"); + free(data); + + return NULL; + } + + unsigned i; + for (i = 0; i < pan2_len; i++) + if (emv_cn_get(pan_tlv, i) != emv_cn_get(&pan2_tlv, i)) { + printf("ERROR: PAN data mismatch\n"); + printf("tlv pan=%s\n", sprint_hex(pan_tlv->value, pan_tlv->len)); + printf("cert pan=%s\n", sprint_hex(pan2_tlv.value, pan2_tlv.len)); + free(data); + + return NULL; + } + + pk_len = data[9 + pan_length]; + if (pk_len > data_len - 11 - pan_length + rem_tlv->len) { + printf("ERROR: Invalid pk length\n"); + free(data); + return NULL; + } + + if (exp_tlv->len != data[10 + pan_length]) { + free(data); + return NULL; + } + + struct emv_pk *pk = emv_pk_new(pk_len, exp_tlv->len); + + memcpy(pk->rid, enc_pk->rid, 5); + pk->index = enc_pk->index; + + pk->hash_algo = data[7 + pan_length]; + pk->pk_algo = data[8 + pan_length]; + pk->expire = (data[3 + pan_length] << 16) | (data[2 + pan_length] << 8) | 0x31; + memcpy(pk->serial, data + 4 + pan_length, 3); + memcpy(pk->pan, data + 2, pan_length); + memset(pk->pan + pan_length, 0xff, 10 - pan_length); + + memcpy(pk->modulus, data + 11 + pan_length, + pk_len < data_len - (11 + pan_length) ? + pk_len : + data_len - (11 + pan_length)); + memcpy(pk->modulus + data_len - (11 + pan_length), rem_tlv->value, rem_tlv->len); + memcpy(pk->exp, exp_tlv->value, exp_tlv->len); + + free(data); + + return pk; +} + +static struct emv_pk *emv_pki_decode_key(const struct emv_pk *enc_pk, + unsigned char msgtype, + const struct tlv *pan_tlv, + const struct tlv *cert_tlv, + const struct tlv *exp_tlv, + const struct tlv *rem_tlv, + const struct tlv *add_tlv + ) { + return emv_pki_decode_key_ex(enc_pk, msgtype, pan_tlv, cert_tlv, exp_tlv, rem_tlv, add_tlv, false); +} + +struct emv_pk *emv_pki_recover_issuer_cert(const struct emv_pk *pk, struct tlvdb *db) +{ + return emv_pki_decode_key(pk, 2, + tlvdb_get(db, 0x5a, NULL), + tlvdb_get(db, 0x90, NULL), + tlvdb_get(db, 0x9f32, NULL), + tlvdb_get(db, 0x92, NULL), + NULL); +} + +struct emv_pk *emv_pki_recover_icc_cert(const struct emv_pk *pk, struct tlvdb *db, const struct tlv *sda_tlv) +{ + return emv_pki_decode_key(pk, 4, + tlvdb_get(db, 0x5a, NULL), + tlvdb_get(db, 0x9f46, NULL), + tlvdb_get(db, 0x9f47, NULL), + tlvdb_get(db, 0x9f48, NULL), + sda_tlv); +} + +struct emv_pk *emv_pki_recover_icc_pe_cert(const struct emv_pk *pk, struct tlvdb *db) +{ + return emv_pki_decode_key(pk, 4, + tlvdb_get(db, 0x5a, NULL), + tlvdb_get(db, 0x9f2d, NULL), + tlvdb_get(db, 0x9f2e, NULL), + tlvdb_get(db, 0x9f2f, NULL), + NULL); +} + +struct tlvdb *emv_pki_recover_dac_ex(const struct emv_pk *enc_pk, const struct tlvdb *db, const struct tlv *sda_tlv, bool showData) +{ + size_t data_len; + unsigned char *data = emv_pki_decode_message(enc_pk, 3, &data_len, + tlvdb_get(db, 0x93, NULL), + sda_tlv, + NULL); + + if (!data || data_len < 5) + return NULL; + + if (showData){ + printf("Recovered data:\n"); + dump_buffer(data, data_len, stdout, 0); + } + + struct tlvdb *dac_db = tlvdb_fixed(0x9f45, 2, data+3); + + free(data); + + return dac_db; +} +struct tlvdb *emv_pki_recover_dac(const struct emv_pk *enc_pk, const struct tlvdb *db, const struct tlv *sda_tlv) { + return emv_pki_recover_dac_ex(enc_pk, db, sda_tlv, false); +} + +struct tlvdb *emv_pki_recover_idn(const struct emv_pk *enc_pk, const struct tlvdb *db, const struct tlv *dyn_tlv) { + return emv_pki_recover_idn_ex(enc_pk, db, dyn_tlv, false); +} + +struct tlvdb *emv_pki_recover_idn_ex(const struct emv_pk *enc_pk, const struct tlvdb *db, const struct tlv *dyn_tlv, bool showData) +{ + size_t data_len; + unsigned char *data = emv_pki_decode_message(enc_pk, 5, &data_len, + tlvdb_get(db, 0x9f4b, NULL), + dyn_tlv, + NULL); + + if (!data || data_len < 3) + return NULL; + + if (data[3] < 2 || data[3] > data_len - 3) { + free(data); + return NULL; + } + + if (showData){ + printf("Recovered data:\n"); + dump_buffer(data, data_len, stdout, 0); + } + + size_t idn_len = data[4]; + if (idn_len > data[3] - 1) { + free(data); + return NULL; + } + + // 9f4c ICC Dynamic Number + struct tlvdb *idn_db = tlvdb_fixed(0x9f4c, idn_len, data + 5); + + free(data); + + return idn_db; +} + +struct tlvdb *emv_pki_recover_atc_ex(const struct emv_pk *enc_pk, const struct tlvdb *db, bool showData) +{ + size_t data_len; + unsigned char *data = emv_pki_decode_message(enc_pk, 5, &data_len, + tlvdb_get(db, 0x9f4b, NULL), + tlvdb_get(db, 0x9f37, NULL), + tlvdb_get(db, 0x9f02, NULL), + tlvdb_get(db, 0x5f2a, NULL), + tlvdb_get(db, 0x9f69, NULL), + NULL); + + if (!data || data_len < 3) + return NULL; + + if (data[3] < 2 || data[3] > data_len - 3) { + free(data); + return NULL; + } + + if (showData){ + printf("Recovered data:\n"); + dump_buffer(data, data_len, stdout, 0); + } + + size_t idn_len = data[4]; + if (idn_len > data[3] - 1) { + free(data); + return NULL; + } + + // 9f36 Application Transaction Counter (ATC) + struct tlvdb *atc_db = tlvdb_fixed(0x9f36, idn_len, data + 5); + + free(data); + + return atc_db; +} + +static bool tlv_hash(void *data, const struct tlv *tlv, int level, bool is_leaf) +{ + struct crypto_hash *ch = data; + size_t tag_len; + unsigned char *tag; + + if (tlv_is_constructed(tlv)) + return true; + + if (tlv->tag == 0x9f4b) + return true; + + tag = tlv_encode(tlv, &tag_len); + crypto_hash_write(ch, tag, tag_len); + free(tag); + + return true; +} + +struct tlvdb *emv_pki_perform_cda(const struct emv_pk *enc_pk, const struct tlvdb *db, + const struct tlvdb *this_db, + const struct tlv *pdol_data_tlv, + const struct tlv *crm1_tlv, + const struct tlv *crm2_tlv) +{ + return emv_pki_perform_cda_ex(enc_pk, db, this_db, pdol_data_tlv, crm1_tlv, crm2_tlv, false); +} +struct tlvdb *emv_pki_perform_cda_ex(const struct emv_pk *enc_pk, const struct tlvdb *db, + const struct tlvdb *this_db, // AC TLV result + const struct tlv *pdol_data_tlv, // PDOL + const struct tlv *crm1_tlv, // CDOL1 + const struct tlv *crm2_tlv, // CDOL2 + bool showData) +{ + const struct tlv *un_tlv = tlvdb_get(db, 0x9f37, NULL); + const struct tlv *cid_tlv = tlvdb_get(this_db, 0x9f27, NULL); + + if (!un_tlv || !cid_tlv) + return NULL; + + size_t data_len = 0; + unsigned char *data = emv_pki_decode_message(enc_pk, 5, &data_len, + tlvdb_get(this_db, 0x9f4b, NULL), + un_tlv, + NULL); + if (!data || data_len < 3) { + printf("ERROR: can't decode message. len %d\n", data_len); + return NULL; + } + + if (showData){ + printf("Recovered data:\n"); + dump_buffer(data, data_len, stdout, 0); + } + + if (data[3] < 30 || data[3] > data_len - 4) { + printf("ERROR: Invalid data length\n"); + free(data); + return NULL; + } + + if (!cid_tlv || cid_tlv->len != 1 || cid_tlv->value[0] != data[5 + data[4]]) { + printf("ERROR: CID mismatch\n"); + free(data); + return NULL; + } + + struct crypto_hash *ch; + ch = crypto_hash_open(enc_pk->hash_algo); + if (!ch) { + printf("ERROR: can't create hash\n"); + free(data); + return NULL; + } + + if (pdol_data_tlv) + crypto_hash_write(ch, pdol_data_tlv->value, pdol_data_tlv->len); + if (crm1_tlv) + crypto_hash_write(ch, crm1_tlv->value, crm1_tlv->len); + if (crm2_tlv) + crypto_hash_write(ch, crm2_tlv->value, crm2_tlv->len); + + tlvdb_visit(this_db, tlv_hash, ch, 0); + + if (memcmp(data + 5 + data[4] + 1 + 8, crypto_hash_read(ch), 20)) { + printf("ERROR: calculated hash error\n"); + crypto_hash_close(ch); + free(data); + return NULL; + } + crypto_hash_close(ch); + + size_t idn_len = data[4]; + if (idn_len > data[3] - 1) { + printf("ERROR: Invalid IDN length\n"); + free(data); + return NULL; + } + + struct tlvdb *idn_db = tlvdb_fixed(0x9f4c, idn_len, data + 5); + free(data); + + return idn_db; +} diff --git a/client/emv/emv_pki.h b/client/emv/emv_pki.h new file mode 100644 index 00000000..e37e3c7d --- /dev/null +++ b/client/emv/emv_pki.h @@ -0,0 +1,45 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef EMV_PKI_H +#define EMV_PKI_H + +#include "emv_pk.h" +#include "tlv.h" + +#include + +struct emv_pk *emv_pki_recover_issuer_cert(const struct emv_pk *pk, struct tlvdb *db); +struct emv_pk *emv_pki_recover_icc_cert(const struct emv_pk *pk, struct tlvdb *db, const struct tlv *sda_tlv); +struct emv_pk *emv_pki_recover_icc_pe_cert(const struct emv_pk *pk, struct tlvdb *db); + +struct tlvdb *emv_pki_recover_dac(const struct emv_pk *pk, const struct tlvdb *db, const struct tlv *sda_tlv); +struct tlvdb *emv_pki_recover_dac_ex(const struct emv_pk *pk, const struct tlvdb *db, const struct tlv *sda_tlv, bool showData); +struct tlvdb *emv_pki_recover_idn(const struct emv_pk *pk, const struct tlvdb *db, const struct tlv *dyn_tlv); +struct tlvdb *emv_pki_recover_idn_ex(const struct emv_pk *pk, const struct tlvdb *db, const struct tlv *dyn_tlv, bool showData); +struct tlvdb *emv_pki_recover_atc_ex(const struct emv_pk *enc_pk, const struct tlvdb *db, bool showData); +struct tlvdb *emv_pki_perform_cda(const struct emv_pk *enc_pk, const struct tlvdb *db, + const struct tlvdb *this_db, + const struct tlv *pdol_data_tlv, + const struct tlv *crm1_tlv, + const struct tlv *crm2_tlv); +struct tlvdb *emv_pki_perform_cda_ex(const struct emv_pk *enc_pk, const struct tlvdb *db, + const struct tlvdb *this_db, + const struct tlv *pdol_data_tlv, + const struct tlv *crm1_tlv, + const struct tlv *crm2_tlv, + bool showData); + +#endif diff --git a/client/emv/emv_pki_priv.c b/client/emv/emv_pki_priv.c new file mode 100644 index 00000000..d67a8705 --- /dev/null +++ b/client/emv/emv_pki_priv.c @@ -0,0 +1,283 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "emv_pki_priv.h" +#include "crypto.h" + +#include +#include +#include + +struct emv_pk *emv_pki_make_ca(const struct crypto_pk *cp, + const unsigned char *rid, unsigned char index, + unsigned int expire, enum crypto_algo_hash hash_algo) +{ + size_t modlen, explen; + unsigned char *mod, *exp; + + if (!rid) + return NULL; + + mod = crypto_pk_get_parameter(cp, 0, &modlen); + exp = crypto_pk_get_parameter(cp, 1, &explen); + + if (!mod || !modlen || !exp || !explen) { + free(mod); + free(exp); + + return NULL; + } + + struct emv_pk *pk = emv_pk_new(modlen, explen); + memcpy(pk->rid, rid, 5); + pk->index = index; + pk->expire = expire; + pk->pk_algo = crypto_pk_get_algo(cp); + pk->hash_algo = hash_algo; + memcpy(pk->modulus, mod, modlen); + memcpy(pk->exp, exp, explen); + + free(mod); + free(exp); + + struct crypto_hash *ch = crypto_hash_open(pk->hash_algo); + if (!ch) { + emv_pk_free(pk); + return false; + } + + crypto_hash_write(ch, pk->rid, sizeof(pk->rid)); + crypto_hash_write(ch, &pk->index, 1); + crypto_hash_write(ch, pk->modulus, pk->mlen); + crypto_hash_write(ch, pk->exp, pk->elen); + + unsigned char *h = crypto_hash_read(ch); + if (!h) { + crypto_hash_close(ch); + emv_pk_free(pk); + + return NULL; + } + + memcpy(pk->hash, h, crypto_hash_get_size(ch)); + crypto_hash_close(ch); + + return pk; +} + +static struct tlvdb *emv_pki_sign_message(const struct crypto_pk *cp, + tlv_tag_t cert_tag, tlv_tag_t rem_tag, + const unsigned char *msg, size_t msg_len, + ... /* A list of tlv pointers, end with NULL */ + ) +{ + size_t tmp_len = (crypto_pk_get_nbits(cp) + 7) / 8; + unsigned char *tmp = malloc(tmp_len); + if (!tmp) + return NULL; + + // XXX + struct crypto_hash *ch = crypto_hash_open(HASH_SHA_1); + if (!ch) { + free(tmp); + + return NULL; + } + + tmp[0] = 0x6a; + tmp[tmp_len - 1] = 0xbc; + + const unsigned char *rem; + size_t rem_len; + size_t hash_len = crypto_hash_get_size(ch); + size_t part_len = tmp_len - 2 - hash_len; + if (part_len < msg_len) { + memcpy(tmp + 1, msg, part_len); + rem = msg + part_len; + rem_len = msg_len - part_len; + } else { + memcpy(tmp + 1, msg, msg_len); + memset(tmp + 1 + msg_len, 0xbb, part_len - msg_len); + rem = NULL; + rem_len = 0; + } + crypto_hash_write(ch, tmp + 1, part_len); + crypto_hash_write(ch, rem, rem_len); + + va_list vl; + va_start(vl, msg_len); + while (true) { + const struct tlv *add_tlv = va_arg(vl, const struct tlv *); + if (!add_tlv) + break; + + crypto_hash_write(ch, add_tlv->value, add_tlv->len); + } + va_end(vl); + + unsigned char *h = crypto_hash_read(ch); + if (!h) { + crypto_hash_close(ch); + free(tmp); + + return NULL; + } + + memcpy(tmp + 1 + part_len, h, hash_len); + crypto_hash_close(ch); + + size_t cert_len; + unsigned char *cert = crypto_pk_decrypt(cp, tmp, tmp_len, &cert_len); + free(tmp); + + if (!cert) + return NULL; + + struct tlvdb *db = tlvdb_fixed(cert_tag, cert_len, cert); + free(cert); + if (!db) + return NULL; + + if (rem) { + struct tlvdb *rdb = tlvdb_fixed(rem_tag, rem_len, rem); + if (!rdb) { + tlvdb_free(db); + + return NULL; + } + tlvdb_add(db, rdb); + } + + return db; +} + +static struct tlvdb *emv_pki_sign_key(const struct crypto_pk *cp, + struct emv_pk *ipk, + unsigned char msgtype, + size_t pan_len, + tlv_tag_t cert_tag, + tlv_tag_t exp_tag, + tlv_tag_t rem_tag, + const struct tlv *add_tlv + ) +{ + unsigned pos = 0; + unsigned char *msg = malloc(1 + pan_len + 2 + 3 + 1 + 1 + 1 + 1 + ipk->mlen); + + if (!msg) + return NULL; + + msg[pos++] = msgtype; + memcpy(msg + pos, ipk->pan, pan_len); pos += pan_len; + msg[pos++] = (ipk->expire >> 8) & 0xff; + msg[pos++] = (ipk->expire >> 16) & 0xff; + memcpy(msg + pos, ipk->serial, 3); pos += 3; + msg[pos++] = ipk->hash_algo; + msg[pos++] = ipk->pk_algo; + msg[pos++] = ipk->mlen; + msg[pos++] = ipk->elen; + memcpy(msg + pos, ipk->modulus, ipk->mlen); + pos += ipk->mlen; + + struct tlvdb *exp_db = tlvdb_fixed(exp_tag, ipk->elen, ipk->exp); + if (!exp_db) { + free(msg); + + return NULL; + } + + struct tlvdb *db = emv_pki_sign_message(cp, + cert_tag, rem_tag, + msg, pos, + tlvdb_get(exp_db, exp_tag, NULL), + add_tlv, + NULL); + free(msg); + if (!db) + return NULL; + + tlvdb_add(db, exp_db); + + return db; +} + +struct tlvdb *emv_pki_sign_issuer_cert(const struct crypto_pk *cp, struct emv_pk *issuer_pk) +{ + return emv_pki_sign_key(cp, issuer_pk, 2, 4, 0x90, 0x9f32, 0x92, NULL); +} + +struct tlvdb *emv_pki_sign_icc_cert(const struct crypto_pk *cp, struct emv_pk *icc_pk, const struct tlv *sda_tlv) +{ + return emv_pki_sign_key(cp, icc_pk, 4, 10, 0x9f46, 0x9f47, 0x9f48, sda_tlv); +} + +struct tlvdb *emv_pki_sign_icc_pe_cert(const struct crypto_pk *cp, struct emv_pk *icc_pe_pk) +{ + return emv_pki_sign_key(cp, icc_pe_pk, 4, 10, 0x9f2d, 0x9f2e, 0x9f2f, NULL); +} + +struct tlvdb *emv_pki_sign_dac(const struct crypto_pk *cp, const struct tlv *dac_tlv, const struct tlv *sda_tlv) +{ + unsigned pos = 0; + unsigned char *msg = malloc(1+1+dac_tlv->len); + + if (!msg) + return NULL; + + msg[pos++] = 3; + msg[pos++] = HASH_SHA_1; + memcpy(msg+pos, dac_tlv->value, dac_tlv->len); + pos += dac_tlv->len; + + struct tlvdb *db = emv_pki_sign_message(cp, + 0x93, 0, + msg, pos, + sda_tlv, + NULL); + + free(msg); + + return db; +} + +struct tlvdb *emv_pki_sign_idn(const struct crypto_pk *cp, const struct tlv *idn_tlv, const struct tlv *dyn_tlv) +{ + unsigned pos = 0; + unsigned char *msg = malloc(1+1+1+1+idn_tlv->len); + + if (!msg) + return NULL; + + msg[pos++] = 5; + msg[pos++] = HASH_SHA_1; + msg[pos++] = idn_tlv->len + 1; + msg[pos++] = idn_tlv->len; + memcpy(msg+pos, idn_tlv->value, idn_tlv->len); + pos += idn_tlv->len; + + struct tlvdb *db = emv_pki_sign_message(cp, + 0x9f4b, 0, + msg, pos, + dyn_tlv, + NULL); + + free(msg); + + return db; +} diff --git a/client/emv/emv_pki_priv.h b/client/emv/emv_pki_priv.h new file mode 100644 index 00000000..4f1639d5 --- /dev/null +++ b/client/emv/emv_pki_priv.h @@ -0,0 +1,35 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef EMV_PKI_PRIV_H +#define EMV_PKI_PRIV_H + +#include "crypto.h" +#include "emv_pk.h" +#include "tlv.h" + +#include + +struct emv_pk *emv_pki_make_ca(const struct crypto_pk *cp, + const unsigned char *rid, unsigned char index, + unsigned int expire, enum crypto_algo_hash hash_algo); +struct tlvdb *emv_pki_sign_issuer_cert(const struct crypto_pk *cp, struct emv_pk *issuer_pk); +struct tlvdb *emv_pki_sign_icc_cert(const struct crypto_pk *cp, struct emv_pk *icc_pk, const struct tlv *sda_tlv); +struct tlvdb *emv_pki_sign_icc_pe_cert(const struct crypto_pk *cp, struct emv_pk *icc_pe_pk); + +struct tlvdb *emv_pki_sign_dac(const struct crypto_pk *cp, const struct tlv *dac_tlv, const struct tlv *sda_tlv); +struct tlvdb *emv_pki_sign_idn(const struct crypto_pk *cp, const struct tlv *idn_tlv, const struct tlv *dyn_tlv); + +#endif diff --git a/client/emv/emv_tags.c b/client/emv/emv_tags.c index 25ad9658..cdd57cb5 100644 --- a/client/emv/emv_tags.c +++ b/client/emv/emv_tags.c @@ -177,6 +177,7 @@ static const struct emv_tag emv_tags[] = { { 0x01 , "", EMV_TAG_STRING }, // string for headers { 0x02 , "Raw data", }, // data { 0x20 , "Cardholder Verification Results (CVR)", EMV_TAG_CVR }, // not standard! + { 0x21 , "Input list for Offline Data Authentication" }, // not standard! data for "Offline Data Authentication" come from "read records" command. (EMV book3 10.3) // EMV { 0x41 , "Country code and national data" }, @@ -266,6 +267,7 @@ static const struct emv_tag emv_tags[] = { { 0x9f65, "PCVC3(Track2)" }, { 0x9f66, "PUNATC(Track2) / Terminal Transaction Qualifiers (TTQ)", EMV_TAG_BITMASK, &EMV_TTQ }, { 0x9f67, "NATC(Track2) / MSD Offset" }, + { 0x9f68, "Cardholder verification method list (PayPass)" }, { 0x9f69, "Card Authentication Related Data" }, { 0x9f6a, "Unpredictable Number", EMV_TAG_NUMERIC }, { 0x9f6b, "Track 2 Data" }, diff --git a/client/emv/emvcore.c b/client/emv/emvcore.c index 36438413..08c6e3db 100644 --- a/client/emv/emvcore.c +++ b/client/emv/emvcore.c @@ -16,7 +16,7 @@ static const char *PSElist [] = { "325041592E5359532E4444463031", // 2PAY.SYS.DDF01 - Visa Proximity Payment System Environment - PPSE "315041592E5359532E4444463031" // 1PAY.SYS.DDF01 - Visa Payment System Environment - PSE }; -static const size_t PSElistLen = sizeof(PSElist)/sizeof(char*); +//static const size_t PSElistLen = sizeof(PSElist)/sizeof(char*); typedef struct { enum CardPSVendor vendor; @@ -464,32 +464,378 @@ int EMVGenerateChallenge(bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen return EMVExchange(LeaveFieldON, (sAPDU){0x00, 0x84, 0x00, 0x00, 0x00, NULL}, Result, MaxResultLen, ResultLen, sw, tlv); } +int EMVInternalAuthenticate(bool LeaveFieldON, uint8_t *DDOL, size_t DDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { + return EMVExchange(LeaveFieldON, (sAPDU){0x00, 0x88, 0x00, 0x00, DDOLLen, DDOL}, Result, MaxResultLen, ResultLen, sw, tlv); +} + int MSCComputeCryptoChecksum(bool LeaveFieldON, uint8_t *UDOL, uint8_t UDOLlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { return EMVExchange(LeaveFieldON, (sAPDU){0x80, 0x2a, 0x8e, 0x80, UDOLlen, UDOL}, Result, MaxResultLen, ResultLen, sw, tlv); } // Authentication -int trSDA(uint8_t *AID, size_t AIDlen, struct tlvdb *tlv) { - if (AIDlen < 5) - return 1; +static struct emv_pk *get_ca_pk(struct tlvdb *db) { + const struct tlv *df_tlv = tlvdb_get(db, 0x84, NULL); + const struct tlv *caidx_tlv = tlvdb_get(db, 0x8f, NULL); + + if (!df_tlv || !caidx_tlv || df_tlv->len < 6 || caidx_tlv->len != 1) + return NULL; + + PrintAndLog("CA public key index 0x%0x", caidx_tlv->value[0]); + return emv_pk_get_ca_pk(df_tlv->value, caidx_tlv->value[0]); +} + +int trSDA(struct tlvdb *tlv) { + + struct emv_pk *pk = get_ca_pk(tlv); + if (!pk) { + PrintAndLog("ERROR: Key not found. Exit."); + return 2; + } - // Get public key index (0x8F) - //int PubKeyIndx = 0; + struct emv_pk *issuer_pk = emv_pki_recover_issuer_cert(pk, tlv); + if (!issuer_pk) { + emv_pk_free(pk); + PrintAndLog("ERROR: Issuer certificate not found. Exit."); + return 2; + } + + PrintAndLog("Issuer PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx", + issuer_pk->rid[0], + issuer_pk->rid[1], + issuer_pk->rid[2], + issuer_pk->rid[3], + issuer_pk->rid[4], + issuer_pk->index, + issuer_pk->serial[0], + issuer_pk->serial[1], + issuer_pk->serial[2] + ); + + const struct tlv *sda_tlv = tlvdb_get(tlv, 0x21, NULL); + if (!sda_tlv || sda_tlv->len < 1) { + emv_pk_free(issuer_pk); + emv_pk_free(pk); + PrintAndLog("ERROR: Can't find input list for Offline Data Authentication. Exit."); + return 3; + } - // Get public key from key storage - // GetPublicKey(AID(0..5), PubKeyIndx) - - // Processing of Issuer Public Key Certificate (0x90) - //Certificate = - - // check issuer public key certificate - - // Verification of Signed Static Application Data (SSAD) (0x93) - - // get 9F4A Static Data Authentication Tag List - - // set Data Auth Code (9F45) from SSAD + struct tlvdb *dac_db = emv_pki_recover_dac(issuer_pk, tlv, sda_tlv); + if (dac_db) { + const struct tlv *dac_tlv = tlvdb_get(dac_db, 0x9f45, NULL); + PrintAndLog("SDA verified OK. (%02hhx:%02hhx)\n", dac_tlv->value[0], dac_tlv->value[1]); + tlvdb_add(tlv, dac_db); + } else { + emv_pk_free(issuer_pk); + emv_pk_free(pk); + PrintAndLog("ERROR: SSAD verify error"); + return 4; + } + emv_pk_free(issuer_pk); + emv_pk_free(pk); return 0; } +static const unsigned char default_ddol_value[] = {0x9f, 0x37, 0x04}; +static struct tlv default_ddol_tlv = {.tag = 0x9f49, .len = 3, .value = default_ddol_value }; + +int trDDA(bool decodeTLV, struct tlvdb *tlv) { + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + + struct emv_pk *pk = get_ca_pk(tlv); + if (!pk) { + PrintAndLog("ERROR: Key not found. Exit."); + return 2; + } + + const struct tlv *sda_tlv = tlvdb_get(tlv, 0x21, NULL); + if (!sda_tlv || sda_tlv->len < 1) { + emv_pk_free(pk); + PrintAndLog("ERROR: Can't find input list for Offline Data Authentication. Exit."); + return 3; + } + + struct emv_pk *issuer_pk = emv_pki_recover_issuer_cert(pk, tlv); + if (!issuer_pk) { + emv_pk_free(pk); + PrintAndLog("ERROR: Issuer certificate not found. Exit."); + return 2; + } + printf("Issuer PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx\n", + issuer_pk->rid[0], + issuer_pk->rid[1], + issuer_pk->rid[2], + issuer_pk->rid[3], + issuer_pk->rid[4], + issuer_pk->index, + issuer_pk->serial[0], + issuer_pk->serial[1], + issuer_pk->serial[2] + ); + + struct emv_pk *icc_pk = emv_pki_recover_icc_cert(issuer_pk, tlv, sda_tlv); + if (!icc_pk) { + emv_pk_free(pk); + emv_pk_free(issuer_pk); + PrintAndLog("ERROR: ICC setrificate not found. Exit."); + return 2; + } + printf("ICC PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx\n", + icc_pk->rid[0], + icc_pk->rid[1], + icc_pk->rid[2], + icc_pk->rid[3], + icc_pk->rid[4], + icc_pk->index, + icc_pk->serial[0], + icc_pk->serial[1], + icc_pk->serial[2] + ); + + struct emv_pk *icc_pe_pk = emv_pki_recover_icc_pe_cert(issuer_pk, tlv); + if (!icc_pe_pk) { + PrintAndLog("WARNING: ICC PE PK recover error. "); + } else { + printf("ICC PE PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx\n", + icc_pe_pk->rid[0], + icc_pe_pk->rid[1], + icc_pe_pk->rid[2], + icc_pe_pk->rid[3], + icc_pe_pk->rid[4], + icc_pe_pk->index, + icc_pe_pk->serial[0], + icc_pe_pk->serial[1], + icc_pe_pk->serial[2] + ); + } + + // 9F4B: Signed Dynamic Application Data + const struct tlv *sdad_tlv = tlvdb_get(tlv, 0x9f4b, NULL); + // DDA with internal authenticate OR fDDA with filled 0x9F4B tag (GPO result) + // EMV kernel3 v2.4, contactless book C-3, C.1., page 147 + if (sdad_tlv) { + PrintAndLog("\n* * Got Signed Dynamic Application Data (9F4B) form GPO. Maybe fDDA..."); + + const struct tlvdb *atc_db = emv_pki_recover_atc_ex(icc_pk, tlv, true); + if (!atc_db) { + PrintAndLog("ERROR: Can't recover IDN (ICC Dynamic Number)"); + emv_pk_free(pk); + emv_pk_free(issuer_pk); + emv_pk_free(icc_pk); + return 8; + } + + // 9f36 Application Transaction Counter (ATC) + const struct tlv *atc_tlv = tlvdb_get(atc_db, 0x9f36, NULL); + if(atc_tlv) { + PrintAndLog("\nATC (Application Transaction Counter) [%zu] %s", atc_tlv->len, sprint_hex_inrow(atc_tlv->value, atc_tlv->len)); + + const struct tlv *core_atc_tlv = tlvdb_get(tlv, 0x9f36, NULL); + if(tlv_equal(core_atc_tlv, atc_tlv)) { + PrintAndLog("ATC check OK."); + PrintAndLog("fDDA (fast DDA) verified OK."); + } else { + PrintAndLog("ERROR: fDDA verified, but ATC in the certificate and ATC in the record not the same."); + } + } else { + PrintAndLog("\nERROR: fDDA (fast DDA) verify error"); + emv_pk_free(pk); + emv_pk_free(issuer_pk); + emv_pk_free(icc_pk); + return 9; + } + } else { + struct tlvdb *dac_db = emv_pki_recover_dac(issuer_pk, tlv, sda_tlv); + if (dac_db) { + const struct tlv *dac_tlv = tlvdb_get(dac_db, 0x9f45, NULL); + printf("SDA verified OK. (%02hhx:%02hhx)\n", dac_tlv->value[0], dac_tlv->value[1]); + tlvdb_add(tlv, dac_db); + } else { + PrintAndLog("ERROR: SSAD verify error"); + emv_pk_free(pk); + emv_pk_free(issuer_pk); + emv_pk_free(icc_pk); + return 4; + } + + PrintAndLog("\n* Calc DDOL"); + const struct tlv *ddol_tlv = tlvdb_get(tlv, 0x9f49, NULL); + if (!ddol_tlv) { + ddol_tlv = &default_ddol_tlv; + PrintAndLog("DDOL [9f49] not found. Using default DDOL"); + } + + struct tlv *ddol_data_tlv = dol_process(ddol_tlv, tlv, 0); + if (!ddol_data_tlv) { + PrintAndLog("ERROR: Can't create DDOL TLV"); + emv_pk_free(pk); + emv_pk_free(issuer_pk); + emv_pk_free(icc_pk); + return 5; + } + + PrintAndLog("DDOL data[%d]: %s", ddol_data_tlv->len, sprint_hex(ddol_data_tlv->value, ddol_data_tlv->len)); + + PrintAndLog("\n* Internal Authenticate"); + int res = EMVInternalAuthenticate(true, (uint8_t *)ddol_data_tlv->value, ddol_data_tlv->len, buf, sizeof(buf), &len, &sw, NULL); + if (res) { + PrintAndLog("Internal Authenticate error(%d): %4x. Exit...", res, sw); + free(ddol_data_tlv); + emv_pk_free(pk); + emv_pk_free(issuer_pk); + emv_pk_free(icc_pk); + return 6; + } + + struct tlvdb *dda_db = NULL; + if (buf[0] == 0x80) { + if (len < 3 ) { + PrintAndLog("ERROR: Internal Authenticate format1 parsing error. length=%d", len); + } else { + // 9f4b Signed Dynamic Application Data + dda_db = tlvdb_fixed(0x9f4b, len - 2, buf + 2); + tlvdb_add(tlv, dda_db); + if (decodeTLV){ + PrintAndLog("* * Decode response format 1:"); + TLVPrintFromTLV(dda_db); + } + } + } else { + dda_db = tlvdb_parse_multi(buf, len); + if(!dda_db) { + PrintAndLog("ERROR: Can't parse Internal Authenticate result as TLV"); + free(ddol_data_tlv); + emv_pk_free(pk); + emv_pk_free(issuer_pk); + emv_pk_free(icc_pk); + return 7; + } + tlvdb_add(tlv, dda_db); + + if (decodeTLV) + TLVPrintFromTLV(dda_db); + } + + struct tlvdb *idn_db = emv_pki_recover_idn_ex(icc_pk, dda_db, ddol_data_tlv, true); + free(ddol_data_tlv); + if (!idn_db) { + PrintAndLog("ERROR: Can't recover IDN (ICC Dynamic Number)"); + tlvdb_free(dda_db); + emv_pk_free(pk); + emv_pk_free(issuer_pk); + emv_pk_free(icc_pk); + return 8; + } + tlvdb_free(dda_db); + + // 9f4c ICC Dynamic Number + const struct tlv *idn_tlv = tlvdb_get(idn_db, 0x9f4c, NULL); + if(idn_tlv) { + PrintAndLog("\nIDN (ICC Dynamic Number) [%zu] %s", idn_tlv->len, sprint_hex_inrow(idn_tlv->value, idn_tlv->len)); + PrintAndLog("DDA verified OK."); + tlvdb_add(tlv, idn_db); + tlvdb_free(idn_db); + } else { + PrintAndLog("\nERROR: DDA verify error"); + tlvdb_free(idn_db); + + emv_pk_free(pk); + emv_pk_free(issuer_pk); + emv_pk_free(icc_pk); + return 9; + } + } + + emv_pk_free(pk); + emv_pk_free(issuer_pk); + emv_pk_free(icc_pk); + return 0; +} + +int trCDA(struct tlvdb *tlv, struct tlvdb *ac_tlv, struct tlv *pdol_data_tlv, struct tlv *ac_data_tlv) { + + struct emv_pk *pk = get_ca_pk(tlv); + if (!pk) { + PrintAndLog("ERROR: Key not found. Exit."); + return 2; + } + + const struct tlv *sda_tlv = tlvdb_get(tlv, 0x21, NULL); + if (!sda_tlv || sda_tlv->len < 1) { + PrintAndLog("ERROR: Can't find input list for Offline Data Authentication. Exit."); + emv_pk_free(pk); + return 3; + } + + struct emv_pk *issuer_pk = emv_pki_recover_issuer_cert(pk, tlv); + if (!issuer_pk) { + PrintAndLog("ERROR: Issuer certificate not found. Exit."); + emv_pk_free(pk); + return 2; + } + printf("Issuer PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx\n", + issuer_pk->rid[0], + issuer_pk->rid[1], + issuer_pk->rid[2], + issuer_pk->rid[3], + issuer_pk->rid[4], + issuer_pk->index, + issuer_pk->serial[0], + issuer_pk->serial[1], + issuer_pk->serial[2] + ); + + struct emv_pk *icc_pk = emv_pki_recover_icc_cert(issuer_pk, tlv, sda_tlv); + if (!icc_pk) { + PrintAndLog("ERROR: ICC setrificate not found. Exit."); + emv_pk_free(pk); + emv_pk_free(issuer_pk); + return 2; + } + printf("ICC PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx\n", + icc_pk->rid[0], + icc_pk->rid[1], + icc_pk->rid[2], + icc_pk->rid[3], + icc_pk->rid[4], + icc_pk->index, + icc_pk->serial[0], + icc_pk->serial[1], + icc_pk->serial[2] + ); + + struct tlvdb *dac_db = emv_pki_recover_dac(issuer_pk, tlv, sda_tlv); + if (dac_db) { + const struct tlv *dac_tlv = tlvdb_get(dac_db, 0x9f45, NULL); + PrintAndLog("SSAD verified OK. (%02hhx:%02hhx)", dac_tlv->value[0], dac_tlv->value[1]); + tlvdb_add(tlv, dac_db); + } else { + PrintAndLog("ERROR: SSAD verify error"); + emv_pk_free(pk); + emv_pk_free(issuer_pk); + emv_pk_free(icc_pk); + return 4; + } + + PrintAndLog("\n* * Check Signed Dynamic Application Data (SDAD)"); + struct tlvdb *idn_db = emv_pki_perform_cda_ex(icc_pk, tlv, ac_tlv, + pdol_data_tlv, // pdol + ac_data_tlv, // cdol1 + NULL, // cdol2 + true); + if (idn_db) { + const struct tlv *idn_tlv = tlvdb_get(idn_db, 0x9f4c, NULL); + PrintAndLog("\nIDN (ICC Dynamic Number) [%zu] %s", idn_tlv->len, sprint_hex_inrow(idn_tlv->value, idn_tlv->len)); + PrintAndLog("CDA verified OK."); + tlvdb_add(tlv, idn_db); + } else { + PrintAndLog("\nERROR: CDA verify error"); + } + + emv_pk_free(pk); + emv_pk_free(issuer_pk); + emv_pk_free(icc_pk); + return 0; +} diff --git a/client/emv/emvcore.h b/client/emv/emvcore.h index 7df58850..94be4fcf 100644 --- a/client/emv/emvcore.h +++ b/client/emv/emvcore.h @@ -20,11 +20,13 @@ #include "common.h" #include "ui.h" #include "cmdhf14a.h" -#include "emv/apduinfo.h" -#include "emv/tlv.h" -#include "emv/dol.h" -#include "emv/dump.h" -#include "emv/emv_tags.h" +#include "apduinfo.h" +#include "tlv.h" +#include "dol.h" +#include "dump.h" +#include "emv_tags.h" +#include "emv_pk.h" +#include "emv_pki.h" #define APDU_RES_LEN 260 #define APDU_AID_LEN 50 @@ -79,10 +81,14 @@ extern int EMVReadRecord(bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t // AC extern int EMVGenerateChallenge(bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); extern int EMVAC(bool LeaveFieldON, uint8_t RefControl, uint8_t *CDOL, size_t CDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); +// DDA +extern int EMVInternalAuthenticate(bool LeaveFieldON, uint8_t *DDOL, size_t DDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); // Mastercard int MSCComputeCryptoChecksum(bool LeaveFieldON, uint8_t *UDOL, uint8_t UDOLlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); // Auth -extern int trSDA(uint8_t *AID, size_t AIDlen, struct tlvdb *tlv); +extern int trSDA(struct tlvdb *tlv); +extern int trDDA(bool decodeTLV, struct tlvdb *tlv); +extern int trCDA(struct tlvdb *tlv, struct tlvdb *ac_tlv, struct tlv *pdol_data_tlv, struct tlv *ac_data_tlv); #endif diff --git a/client/emv/test/cda_test.c b/client/emv/test/cda_test.c new file mode 100644 index 00000000..536a5862 --- /dev/null +++ b/client/emv/test/cda_test.c @@ -0,0 +1,442 @@ +/* + * emv-tools - a set of tools to work with EMV family of smart cards + * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "../emv_pk.h" +#include "../crypto.h" +#include "../dump.h" +#include "../tlv.h" +#include "../emv_pki.h" + +#include +#include +#include + +struct emv_pk c_mchip_05 = { + .rid = { 0xa0, 0x00, 0x00, 0x00, 0x04, }, + .index = 5, + .hash_algo = HASH_SHA_1, + .pk_algo = PK_RSA, + .hash = { + 0xeb, 0xfa, 0x0d, 0x5d, + 0x06, 0xd8, 0xce, 0x70, + 0x2d, 0xa3, 0xea, 0xe8, + 0x90, 0x70, 0x1d, 0x45, + 0xe2, 0x74, 0xc8, 0x45, }, + .exp = { 0x03, }, + .elen = 1, + .mlen = 1408 / 8, + .modulus = (unsigned char[]){ + 0xb8, 0x04, 0x8a, 0xbc, 0x30, 0xc9, 0x0d, 0x97, 0x63, 0x36, 0x54, 0x3e, 0x3f, 0xd7, 0x09, 0x1c, + 0x8f, 0xe4, 0x80, 0x0d, 0xf8, 0x20, 0xed, 0x55, 0xe7, 0xe9, 0x48, 0x13, 0xed, 0x00, 0x55, 0x5b, + 0x57, 0x3f, 0xec, 0xa3, 0xd8, 0x4a, 0xf6, 0x13, 0x1a, 0x65, 0x1d, 0x66, 0xcf, 0xf4, 0x28, 0x4f, + 0xb1, 0x3b, 0x63, 0x5e, 0xdd, 0x0e, 0xe4, 0x01, 0x76, 0xd8, 0xbf, 0x04, 0xb7, 0xfd, 0x1c, 0x7b, + 0xac, 0xf9, 0xac, 0x73, 0x27, 0xdf, 0xaa, 0x8a, 0xa7, 0x2d, 0x10, 0xdb, 0x3b, 0x8e, 0x70, 0xb2, + 0xdd, 0xd8, 0x11, 0xcb, 0x41, 0x96, 0x52, 0x5e, 0xa3, 0x86, 0xac, 0xc3, 0x3c, 0x0d, 0x9d, 0x45, + 0x75, 0x91, 0x64, 0x69, 0xc4, 0xe4, 0xf5, 0x3e, 0x8e, 0x1c, 0x91, 0x2c, 0xc6, 0x18, 0xcb, 0x22, + 0xdd, 0xe7, 0xc3, 0x56, 0x8e, 0x90, 0x02, 0x2e, 0x6b, 0xba, 0x77, 0x02, 0x02, 0xe4, 0x52, 0x2a, + 0x2d, 0xd6, 0x23, 0xd1, 0x80, 0xe2, 0x15, 0xbd, 0x1d, 0x15, 0x07, 0xfe, 0x3d, 0xc9, 0x0c, 0xa3, + 0x10, 0xd2, 0x7b, 0x3e, 0xfc, 0xcd, 0x8f, 0x83, 0xde, 0x30, 0x52, 0xca, 0xd1, 0xe4, 0x89, 0x38, + 0xc6, 0x8d, 0x09, 0x5a, 0xac, 0x91, 0xb5, 0xf3, 0x7e, 0x28, 0xbb, 0x49, 0xec, 0x7e, 0xd5, 0x97, + }, +}; + +const unsigned char c_issuer_cert[] = { + 0x17, 0x14, 0x28, 0x4f, 0x76, 0x3b, 0x85, 0x86, 0xee, 0x6d, 0x31, 0x99, 0x51, 0xf7, 0xe6, 0x3f, + 0xa2, 0x50, 0x76, 0xe5, 0x0d, 0xc9, 0xd3, 0x20, 0x0b, 0xa9, 0x98, 0xd3, 0xa0, 0x52, 0xad, 0xba, + 0x9a, 0xb6, 0x9a, 0xc6, 0xad, 0x6a, 0xdd, 0x3c, 0xe0, 0x9f, 0x02, 0x78, 0xf4, 0x07, 0x4e, 0xc4, + 0xee, 0x9b, 0x1d, 0x22, 0x68, 0xa3, 0xe9, 0x53, 0x57, 0x5e, 0x45, 0x4e, 0x50, 0xcd, 0x86, 0x0b, + 0xf4, 0x24, 0xc5, 0x1c, 0x59, 0x77, 0x12, 0xd2, 0xaa, 0x05, 0x70, 0x89, 0xdd, 0x86, 0x73, 0xe5, + 0x1b, 0x1e, 0x1d, 0x71, 0x88, 0x03, 0x48, 0x92, 0x07, 0x7a, 0xc1, 0x8a, 0x6a, 0xe2, 0x34, 0x88, + 0xbe, 0xa9, 0xdf, 0x3b, 0x1a, 0x83, 0xf2, 0xc0, 0x80, 0x0c, 0xd7, 0xc5, 0xcd, 0xf2, 0xfd, 0xe0, + 0x49, 0x6f, 0x7b, 0xc3, 0x9f, 0xb4, 0xbf, 0x36, 0x32, 0x99, 0xbf, 0xa6, 0x37, 0xb2, 0xec, 0x33, + 0xc5, 0x07, 0xe3, 0x68, 0x21, 0xee, 0xc2, 0x07, 0x5f, 0x0e, 0x42, 0x0d, 0x38, 0xa1, 0xc9, 0xf3, + 0x12, 0x72, 0x61, 0xba, 0x31, 0x6c, 0x98, 0x76, 0x74, 0xfa, 0xdb, 0x20, 0xea, 0x7f, 0xeb, 0x75, + 0xee, 0x45, 0x5d, 0x12, 0x14, 0x6e, 0xa6, 0xf0, 0x2e, 0x8b, 0x01, 0xec, 0x2f, 0xa7, 0xa1, 0x15, +}; + +const unsigned char c_issuer_rem[] = { + 0x6e, 0x63, 0xb7, 0xbc, 0x70, 0xab, 0xdd, 0x09, 0x34, 0x1b, 0x34, 0xc0, 0x32, 0x86, 0xba, 0x9b, + 0xd8, 0x3b, 0xa7, 0x93, 0x6c, 0x5b, 0x77, 0x98, 0xfb, 0x22, 0xc5, 0xe5, 0x3f, 0xf2, 0x40, 0xa2, + 0x6d, 0xbd, 0x64, 0x15, +}; + +const unsigned char c_issuer_exp[] = { + 0x03, +}; + +const unsigned char c_icc_cert[] = { + 0xa4, 0x2f, 0xbe, 0xb1, 0x56, 0xb9, 0x8d, 0xcb, 0x05, 0x54, 0xda, 0x06, 0x2a, 0xdc, 0xa5, 0x30, + 0x9a, 0x91, 0xf0, 0x4f, 0xa2, 0xc7, 0xbd, 0x71, 0x02, 0xa8, 0xd7, 0x3f, 0x16, 0xa3, 0xcf, 0xad, + 0xe8, 0xaa, 0xdf, 0x4f, 0x3f, 0xe2, 0xa2, 0x12, 0x5c, 0xcd, 0xd7, 0x7c, 0x6b, 0x9f, 0x78, 0xb5, + 0xb4, 0x37, 0x1c, 0xe0, 0x80, 0x57, 0x25, 0xb0, 0xf9, 0xc0, 0x27, 0xaf, 0x14, 0x7d, 0x91, 0xe1, + 0xff, 0xdb, 0x20, 0x1e, 0x9c, 0x17, 0x0c, 0xe7, 0x77, 0x05, 0x3a, 0x17, 0x2a, 0xd5, 0x26, 0xdc, + 0xaf, 0xd3, 0x38, 0x95, 0xe1, 0xa9, 0x47, 0x30, 0x5c, 0x5b, 0x16, 0x7f, 0x2e, 0x7c, 0x6f, 0x99, + 0x15, 0x81, 0xa6, 0x52, 0xee, 0x47, 0x31, 0x54, 0x76, 0x0c, 0x2e, 0xd7, 0x74, 0x21, 0x4e, 0x50, + 0xdf, 0xec, 0xdd, 0x4c, 0xf2, 0x94, 0xc9, 0x74, 0xb8, 0x9e, 0xbc, 0xa2, 0x5b, 0x5a, 0xb3, 0xc0, + 0xbe, 0xb5, 0x0d, 0xfa, 0xf7, 0x82, 0xaf, 0xde, 0x14, 0x33, 0xd9, 0x0c, 0xa2, 0xa8, 0x9d, 0x65, + 0x1e, 0x75, 0xd6, 0x7e, 0xbc, 0x7c, 0x3e, 0x36, 0xf5, 0xa1, 0x65, 0xee, 0x61, 0x32, 0x61, 0x29, + 0x39, 0xc1, 0xec, 0xd3, 0x99, 0xe4, 0x60, 0x74, 0xb9, 0x96, 0xd9, 0x3a, 0x88, 0xe0, 0x1e, 0x0a, +}; + +const unsigned char c_icc_exp[] = { + 0x03, +}; + +const unsigned char c_sdad_cr[] = { + 0x1c, 0x00, 0x9f, 0xc4, 0x86, 0x79, 0x15, 0x7d, 0xbf, 0xf4, 0x5f, 0x65, 0xd3, 0x3f, 0xf7, 0x8d, + 0x4f, 0xcb, 0xf0, 0xcf, 0x5e, 0xa4, 0x20, 0x8d, 0x10, 0x7a, 0xe9, 0x5a, 0xa3, 0x8c, 0x54, 0x6d, + 0x0e, 0x5a, 0x18, 0xb8, 0x74, 0x03, 0xa1, 0x2b, 0xd4, 0x47, 0xa8, 0xbb, 0xfc, 0x1e, 0x49, 0xce, + 0x0b, 0x2e, 0x25, 0x13, 0x89, 0x20, 0x57, 0x03, 0xc9, 0xbb, 0x1a, 0x88, 0xcc, 0x79, 0xf1, 0xdd, + 0xc2, 0xf9, 0x84, 0x1e, 0xad, 0xf0, 0x7c, 0xe0, 0x7b, 0x62, 0x51, 0x1d, 0xdc, 0x93, 0xdf, 0x59, + 0xf2, 0x8f, 0x0e, 0x91, 0xf9, 0x23, 0x32, 0xd2, 0x9c, 0xde, 0xf2, 0xbc, 0xcb, 0x10, 0x08, 0x85, + 0x05, 0x00, 0xef, 0x3e, 0x47, 0x0a, 0x4c, 0xb1, 0x8c, 0xd9, 0x1a, 0xa5, 0xc1, 0xa1, 0x08, 0xf3, + +}; + +const unsigned char c_ssd1[] = { + 0x5f, 0x25, 0x03, 0x14, 0x05, 0x01, 0x5f, 0x24, 0x03, 0x15, 0x06, 0x30, 0x5a, 0x08, 0x52, 0x85, + 0x88, 0x12, 0x54, 0x34, 0x56, 0x53, 0x5f, 0x34, 0x01, 0x01, 0x8e, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1e, 0x03, 0x1f, 0x03, 0x9f, 0x07, 0x02, 0xff, 0x00, 0x9f, 0x0d, 0x05, + 0xbc, 0x50, 0xbc, 0x00, 0x00, 0x9f, 0x0e, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9f, 0x0f, 0x05, + 0xbc, 0x70, 0xbc, 0x98, 0x00, 0x9f, 0x4a, 0x01, 0x82, 0x5f, 0x28, 0x02, 0x06, 0x43, 0x8c, 0x21, + 0x9f, 0x02, 0x06, 0x9f, 0x03, 0x06, 0x9f, 0x1a, 0x02, 0x95, 0x05, 0x5f, 0x2a, 0x02, 0x9a, 0x03, + 0x9c, 0x01, 0x9f, 0x37, 0x04, 0x9f, 0x35, 0x01, 0x9f, 0x45, 0x02, 0x9f, 0x4c, 0x08, 0x9f, 0x34, + 0x03, 0x8d, 0x0c, 0x91, 0x0a, 0x8a, 0x02, 0x95, 0x05, 0x9f, 0x37, 0x04, 0x9f, 0x4c, 0x08, + 0x39, 0x00, +}; +static const struct tlv ssd1_tlv = { + .len = sizeof(c_ssd1), + .value = c_ssd1, +}; + +const unsigned char c_pan[] = { + 0x52, 0x85, 0x88, 0x12, 0x54, 0x34, 0x56, 0x53, +}; + +const unsigned char c_dd1[] = { + 0x12, 0x34, 0x57, 0x79, +}; + +const unsigned char c_dd2[] = { + 0x9f, 0x27, 0x01, 0x40, 0x9f, 0x36, 0x02, 0x00, 0x10, 0x9f, 0x10, 0x12, 0x00, 0x10, 0x90, 0x40, + 0x01, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, +}; + +const unsigned char c_crm1[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x43, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x43, 0x14, 0x09, 0x25, 0x50, 0x12, 0x34, 0x57, 0x79, 0x23, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x03, 0x00, +}; +static const struct tlv crm1_tlv = { + .len = sizeof(c_crm1), + .value = c_crm1, +}; + +static int cda_test_raw(bool verbose) +{ + const struct emv_pk *pk = &c_mchip_05; + + struct crypto_pk *kcp = crypto_pk_open(PK_RSA, + pk->modulus, pk->mlen, + pk->exp, pk->elen); + if (!kcp) + return 1; + + unsigned char *ipk_data; + size_t ipk_data_len; + ipk_data = crypto_pk_encrypt(kcp, c_issuer_cert, sizeof(c_issuer_cert), &ipk_data_len); + crypto_pk_close(kcp); + + if (!ipk_data) + return 1; + + if (verbose) { + printf("issuer cert:\n"); + dump_buffer(ipk_data, ipk_data_len, stdout, 0); + } + + size_t ipk_pk_len = ipk_data[13]; + unsigned char *ipk_pk = malloc(ipk_pk_len); + memcpy(ipk_pk, ipk_data + 15, ipk_data_len - 36); + memcpy(ipk_pk + ipk_data_len - 36, c_issuer_rem, sizeof(c_issuer_rem)); + + struct crypto_hash *ch; + ch = crypto_hash_open(HASH_SHA_1); + if (!ch) { + free(ipk_pk); + free(ipk_data); + return 1; + } + + crypto_hash_write(ch, ipk_data + 1, 14); + crypto_hash_write(ch, ipk_pk, ipk_pk_len); + crypto_hash_write(ch, c_issuer_exp, sizeof(c_issuer_exp)); + + unsigned char *h = crypto_hash_read(ch); + if (!h) { + crypto_hash_close(ch); + free(ipk_pk); + free(ipk_data); + return 1; + } + + if (verbose) { + printf("crypto hash:\n"); + dump_buffer(h, 20, stdout, 0); + } + + if (memcmp(ipk_data + ipk_data_len - 21, h, 20)) { + crypto_hash_close(ch); + free(ipk_pk); + free(ipk_data); + return 1; + } + + crypto_hash_close(ch); + free(ipk_data); + + struct crypto_pk *ikcp = crypto_pk_open(PK_RSA, ipk_pk, (int) ipk_pk_len, + c_issuer_exp, (int) sizeof(c_issuer_exp)); + free(ipk_pk); + if (!ikcp) + return 1; + + unsigned char *iccpk_data; + size_t iccpk_data_len; + iccpk_data = crypto_pk_encrypt(ikcp, c_icc_cert, sizeof(c_icc_cert), &iccpk_data_len); + crypto_pk_close(ikcp); + + if (!iccpk_data) + return 1; + + if (verbose) { + printf("icc cert:\n"); + dump_buffer(iccpk_data, iccpk_data_len, stdout, 0); + } + + size_t iccpk_pk_len = iccpk_data[19]; + unsigned char *iccpk_pk = malloc(iccpk_pk_len); + memcpy(iccpk_pk, iccpk_data + 21, /*iccpk_data_len - 36*/iccpk_pk_len); + /*memcpy(iccpk_pk + iccpk_data_len - 36, icc_rem, sizeof(icc_rem));*/ + + ch = crypto_hash_open(HASH_SHA_1); + if (!ch) { + free(iccpk_pk); + free(iccpk_data); + return 1; + } + + crypto_hash_write(ch, iccpk_data + 1, iccpk_data_len - 22); + crypto_hash_write(ch, c_icc_exp, sizeof(c_icc_exp)); + crypto_hash_write(ch, c_ssd1, sizeof(c_ssd1)); + + h = crypto_hash_read(ch); + if (!h) { + crypto_hash_close(ch); + free(iccpk_pk); + free(iccpk_data); + return 1; + } + + if (verbose) { + printf("crypto hash1.1:\n"); + dump_buffer(h, 20, stdout, 0); + } + + if (memcmp(iccpk_data + iccpk_data_len - 21, h, 20)) { + crypto_hash_close(ch); + free(iccpk_pk); + free(iccpk_data); + return 1; + } + + crypto_hash_close(ch); + free(iccpk_data); + + struct crypto_pk *icckcp = crypto_pk_open(PK_RSA, iccpk_pk, (int) iccpk_pk_len, + c_issuer_exp, (int) sizeof(c_issuer_exp)); + free(iccpk_pk); + if (!icckcp) + return 1; + + size_t sdad_len; + unsigned char *sdad = crypto_pk_encrypt(icckcp, c_sdad_cr, sizeof(c_sdad_cr), &sdad_len); + crypto_pk_close(icckcp); + if (!sdad) + return 1; + + if (verbose) { + printf("SDAD:\n"); + dump_buffer(sdad, sdad_len, stdout, 0); + } + + ch = crypto_hash_open(HASH_SHA_1); + if (!ch) { + free(sdad); + return 1; + } + + crypto_hash_write(ch, sdad + 1, sdad_len - 22); + crypto_hash_write(ch, c_dd1, sizeof(c_dd1)); + + unsigned char *h2 = crypto_hash_read(ch); + if (!h2) { + crypto_hash_close(ch); + free(sdad); + return 1; + } + + if (verbose) { + printf("crypto hash2:\n"); + dump_buffer(h2, 20, stdout, 0); + } + + crypto_hash_close(ch); + + ch = crypto_hash_open(HASH_SHA_1); + if (!ch) { + free(sdad); + return 1; + } + + crypto_hash_write(ch, c_crm1, sizeof(c_crm1)); + crypto_hash_write(ch, c_dd2, sizeof(c_dd2)); + + h = crypto_hash_read(ch); + if (!h) { + crypto_hash_close(ch); + free(sdad); + return 1; + } + + if (verbose) { + printf("crypto hash2.1:\n"); + dump_buffer(h, 20, stdout, 0); + } + + if (memcmp(sdad + 5 + 8 + 1 + 8, h, 20)) { + crypto_hash_close(ch); + free(sdad); + return 1; + } + + crypto_hash_close(ch); + + free(sdad); + + return 0; +} + +static int cda_test_pk(bool verbose) +{ + const struct emv_pk *pk = &c_mchip_05; + struct tlvdb *db; + + db = tlvdb_external(0x90, sizeof(c_issuer_cert), c_issuer_cert); + tlvdb_add(db, tlvdb_external(0x9f32, sizeof(c_issuer_exp), c_issuer_exp)); + tlvdb_add(db, tlvdb_external(0x92, sizeof(c_issuer_rem), c_issuer_rem)); + tlvdb_add(db, tlvdb_external(0x5a, sizeof(c_pan), c_pan)); + + struct emv_pk *ipk = emv_pki_recover_issuer_cert(pk, db); + if (!ipk) { + fprintf(stderr, "Could not recover Issuer certificate!\n"); + tlvdb_free(db); + return 2; + } + + tlvdb_add(db, tlvdb_external(0x9f46, sizeof(c_icc_cert), c_icc_cert)); + tlvdb_add(db, tlvdb_external(0x9f47, sizeof(c_icc_exp), c_icc_exp)); + /*tlvdb_add(db, tlvdb_external(0x9f48, sizeof(issuer_rem), issuer_rem));*/ + + struct emv_pk *iccpk = emv_pki_recover_icc_cert(ipk, db, &ssd1_tlv); + if (!iccpk) { + fprintf(stderr, "Could not recover ICC certificate!\n"); + emv_pk_free(ipk); + tlvdb_free(db); + return 2; + } + + tlvdb_add(db, tlvdb_fixed(0x9f37, sizeof(c_dd1), c_dd1)); + + struct tlvdb *cda_db; + cda_db = tlvdb_fixed(0x9f27, 1, (unsigned char[]){ 0x40 }); + tlvdb_add(cda_db, tlvdb_fixed(0x9f36, 2, (unsigned char[]) { 0x00, 0x10 })); + tlvdb_add(cda_db, tlvdb_external(0x9f4b, sizeof(c_sdad_cr), c_sdad_cr)); + tlvdb_add(cda_db, tlvdb_fixed(0x9f10, 0x12, + (unsigned char[]) { 0x00, 0x10, 0x90, 0x40, 0x01, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff})); + + struct tlvdb *idndb = emv_pki_perform_cda(iccpk, db, cda_db, + NULL, + &crm1_tlv, + NULL); + if (!idndb) { + fprintf(stderr, "Could not recover IDN!\n"); + tlvdb_free(cda_db); + emv_pk_free(iccpk); + emv_pk_free(ipk); + tlvdb_free(db); + return 2; + } + + const struct tlv *idn = tlvdb_get(idndb, 0x9f4c, NULL); + if (!idn) { + fprintf(stderr, "IDN not found!\n"); + tlvdb_free(idndb); + tlvdb_free(cda_db); + emv_pk_free(iccpk); + emv_pk_free(ipk); + tlvdb_free(db); + return 2; + } + + if (verbose) { + printf("IDN:\n"); + dump_buffer(idn->value, idn->len, stdout, 0); + } + + tlvdb_free(idndb); + tlvdb_free(cda_db); + emv_pk_free(iccpk); + emv_pk_free(ipk); + tlvdb_free(db); + + return 0; +} + +int exec_cda_test(bool verbose) +{ + int ret; + fprintf(stdout, "\n"); + + ret = cda_test_raw(verbose); + if (ret) { + fprintf(stderr, "CDA raw test: failed\n"); + return ret; + } + fprintf(stdout, "CDA raw test: passed\n"); + + ret = cda_test_pk(verbose); + if (ret) { + fprintf(stderr, "CDA test pk: failed\n"); + return ret; + } + fprintf(stdout, "CDA test pk: passed\n"); + + return 0; +} diff --git a/client/emv/test/cda_test.h b/client/emv/test/cda_test.h new file mode 100644 index 00000000..1d321a26 --- /dev/null +++ b/client/emv/test/cda_test.h @@ -0,0 +1,18 @@ +/* + * emv-tools - a set of tools to work with EMV family of smart cards + * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include + +extern int exec_cda_test(bool verbose); diff --git a/client/emv/test/crypto_test.c b/client/emv/test/crypto_test.c new file mode 100644 index 00000000..ff18b9da --- /dev/null +++ b/client/emv/test/crypto_test.c @@ -0,0 +1,327 @@ +/* + * emv-tools - a set of tools to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "../crypto.h" +#include "../dump.h" +#include "util_posix.h" + +#include +#include +#include + +static int test_genkey(unsigned int keylength, unsigned char *msg, size_t msg_len, bool verbose) +{ + int ret = 1; + size_t tmp_len, tmp2_len; + unsigned char *tmp, *tmp2; + struct crypto_pk *pk; + + printf("Testing key length %u ", keylength); + uint64_t ms = msclock(); + + pk = crypto_pk_genkey(PK_RSA, 1, keylength, 3); + if (!pk) { + fprintf(stderr, "ERROR: key generation error.\n"); + goto out; + } + + tmp_len = crypto_pk_get_nbits(pk); + if (tmp_len != keylength) { + fprintf(stderr, "ERROR: crypto_pk_get_nbits.\n"); + goto close; + } + + tmp = crypto_pk_decrypt(pk, msg, msg_len, &tmp_len); + if (!tmp) { + fprintf(stderr, "ERROR: crypto_pk_decrypt.\n"); + goto close; + } + + tmp2 = crypto_pk_encrypt(pk, tmp, tmp_len, &tmp2_len); + if (!tmp2) { + fprintf(stderr, "ERROR: crypto_pk_encrypt.\n"); + goto free_tmp; + } + + if (tmp2_len == msg_len && !memcmp(tmp2, msg, tmp2_len)) { + ret = 0; + } else { + fprintf(stderr, "ERROR: encrypt-decrypt sequence length or data error.\n"); + } + + free(tmp2); + printf("passed. (%"PRIu64" ms) \n", msclock() - ms); +free_tmp: + free(tmp); +close: + crypto_pk_close(pk); + +out: + return ret; +} + +static unsigned char message[4096 / 8] = + "aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbb" + "ccccccccccccccccdddddddddddddddd" + "eeeeeeeeeeeeeeeeffffffffffffffff" + "gggggggggggggggghhhhhhhhhhhhhhhh" + "iiiiiiiiiiiiiiiijjjjjjjjjjjjjjjj" + "kkkkkkkkkkkkkkkkllllllllllllllll" + "mmmmmmmmmmmmmmmmnnnnnnnnnnnnnnnn" + "oooooooooooooooopppppppppppppppp" + "qqqqqqqqqqqqqqqqrrrrrrrrrrrrrrrr" + "sssssssssssssssstttttttttttttttt" + "uuuuuuuuuuuuuuuuvvvvvvvvvvvvvvvv" + "wwwwwwwwwwwwwwwwxxxxxxxxxxxxxxxx" + "yyyyyyyyyyyyyyyyzzzzzzzzzzzzzzzz" + "aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbb" + "ccccccccccccccccdddddddddddddddd" + "eeeeeeeeeeeeeeeeffffffffffffffff" + ; + +static unsigned char pk_N[] = { + 0xdb, 0x12, 0xe4, 0xf1, 0x8d, 0x43, 0x74, 0xf0, 0xec, 0x38, 0xdc, 0xfb, 0xf9, 0x20, 0x75, 0x6d, + 0x05, 0xf4, 0x36, 0xc2, 0x21, 0xac, 0x34, 0x0d, 0x16, 0xc5, 0x23, 0xcb, 0xfc, 0x9a, 0x8a, 0xd1, + 0xe0, 0xbd, 0xda, 0xe5, 0x77, 0xd5, 0xaf, 0x65, 0x8d, 0x6b, 0x62, 0x5c, 0xcd, 0x89, 0x06, 0x8d, + 0x11, 0x19, 0x6b, 0x0e, 0x3e, 0xe2, 0x80, 0x45, 0xf6, 0x44, 0x55, 0x21, 0x9c, 0x86, 0x90, 0x00, + 0xa8, 0xaf, 0x8c, 0x94, 0xde, 0x3f, 0xe8, 0x56, 0x52, 0xfe, 0xee, 0xa5, 0x36, 0x72, 0x07, 0xf2, + 0xcf, 0x8e, 0xf0, 0xbd, 0xff, 0x36, 0xd5, 0xf2, 0xad, 0x74, 0x1d, 0x17, 0xd0, 0xb7, 0x93, 0xe2, + 0x2c, 0x8d, 0x3f, 0xb6, 0x7c, 0x65, 0x19, 0x9f, 0xa7, 0x80, 0x1f, 0x9f, 0xe5, 0x2f, 0x2d, 0x75, + 0xc9, 0xc2, 0xe9, 0x70, 0xfa, 0x1e, 0x5a, 0xc6, 0xa3, 0x82, 0xd1, 0x29, 0x5a, 0x60, 0xce, 0x1f, + 0x40, 0x2e, 0xfc, 0x2a, 0x5e, 0xde, 0xc9, 0x67, 0xfc, 0x45, 0x18, 0xce, 0xf2, 0x83, 0x94, 0x53, + 0xd6, 0x4f, 0x2e, 0xc5, 0x2d, 0xa1, 0xa5, 0x7a, 0x63, 0x26, 0x70, 0xcb, 0x76, 0xfc, 0xb5, 0x8d, + 0x0f, 0x88, 0x4c, 0x07, 0xba, 0xfa, 0x8b, 0xbc, 0xa0, 0xea, 0xea, 0x0a, 0xe6, 0xa5, 0x44, 0xa5, + 0x0d, 0x12, 0x66, 0x2b, 0xf7, 0xc4, 0x76, 0xa3, 0x82, 0xa6, 0x2b, 0xb2, 0x5a, 0x27, 0xcd, 0x11, + 0xd2, 0x9d, 0x42, 0x86, 0x8c, 0x82, 0xc8, 0xe1, 0xff, 0x7d, 0xf1, 0xd9, 0xd9, 0xa1, 0xf3, 0x3d, + 0xc3, 0x12, 0x4e, 0x47, 0xc8, 0xa2, 0xcd, 0x72, 0x5a, 0x18, 0xea, 0x89, 0x5c, 0x73, 0x28, 0x52, + 0xf8, 0xdb, 0x70, 0xdc, 0x92, 0xc9, 0xb7, 0x98, 0x10, 0x94, 0x79, 0xdc, 0x9e, 0x12, 0x6c, 0x14, + 0x78, 0xf9, 0x5a, 0xad, 0x00, 0x98, 0xc8, 0x17, 0x79, 0x8a, 0xed, 0xe7, 0xc3, 0xd3, 0xa7, 0x8b, +}; + +static unsigned char pk_E[] = { + 0x01, 0x00, 0x01, +}; + +static unsigned char pk_D[] = { + 0x01, 0x17, 0xd4, 0x0a, 0x9c, 0x80, 0xd4, 0xa9, 0x8b, 0x14, 0x31, 0x8e, 0x14, 0x4d, 0x24, 0x28, + 0xda, 0x19, 0xc0, 0xd8, 0x31, 0x20, 0xd1, 0xd5, 0xaa, 0xe2, 0x6a, 0xee, 0x4e, 0xa1, 0x5a, 0xc5, + 0xf7, 0x50, 0x1b, 0x32, 0x7f, 0xe9, 0x92, 0x09, 0x78, 0xae, 0x2b, 0x7c, 0x79, 0x0e, 0x10, 0xf9, + 0x4d, 0x37, 0x8a, 0x40, 0x34, 0xf2, 0x1e, 0x5f, 0xba, 0xfd, 0xd6, 0x4a, 0xe7, 0xa4, 0x08, 0x3d, + 0xe8, 0x99, 0x8f, 0xa3, 0x02, 0x84, 0xe1, 0x1c, 0xe5, 0x27, 0x1e, 0x7b, 0xb6, 0x8c, 0xd5, 0x1b, + 0x52, 0x0b, 0xcd, 0x89, 0xb5, 0x27, 0x49, 0xe3, 0xff, 0x17, 0x90, 0x39, 0x99, 0x32, 0x01, 0x4b, + 0xe4, 0x9b, 0x03, 0xd1, 0x5e, 0x47, 0x86, 0xdc, 0x34, 0x12, 0xc0, 0x95, 0xa4, 0xa8, 0x1a, 0x9a, + 0xf6, 0xd9, 0xc1, 0x1e, 0x6e, 0x31, 0x0e, 0x94, 0xe5, 0x25, 0xf6, 0xf3, 0x34, 0xdf, 0x3c, 0xc8, + 0x0a, 0xc5, 0x8c, 0x00, 0x5c, 0x59, 0x55, 0x06, 0xd1, 0x39, 0x84, 0x35, 0x96, 0x40, 0xe8, 0xb2, + 0xf7, 0x13, 0x83, 0x37, 0xe1, 0xe2, 0x79, 0x41, 0x90, 0x2a, 0xc3, 0x71, 0xc5, 0xcf, 0xf0, 0xaa, + 0x01, 0x2f, 0x48, 0x9c, 0x3f, 0x29, 0x7b, 0xb7, 0x5c, 0xef, 0x25, 0xde, 0x34, 0x23, 0x81, 0x7a, + 0x4c, 0x3a, 0x9b, 0xe4, 0xa7, 0x44, 0x73, 0xbf, 0xf7, 0x39, 0x43, 0xa4, 0x39, 0xa0, 0x1b, 0xf7, + 0x4f, 0x5f, 0x14, 0x49, 0x32, 0x0e, 0x66, 0xd0, 0x29, 0xb5, 0x80, 0xe0, 0xba, 0x3b, 0x88, 0x2b, + 0x14, 0xa4, 0x26, 0x00, 0x2f, 0x50, 0x20, 0x4e, 0xfa, 0xc2, 0x44, 0x72, 0x72, 0x6c, 0x2a, 0x77, + 0x85, 0x20, 0xe0, 0x1d, 0x95, 0x6a, 0x66, 0xe7, 0xb8, 0xca, 0x5b, 0xc9, 0xc3, 0xf3, 0x39, 0xef, + 0xd7, 0xd5, 0x45, 0xb6, 0x3e, 0x19, 0xea, 0x7c, 0x56, 0x20, 0x1b, 0x95, 0x86, 0x2e, 0xc7, 0x51, +}; + +static unsigned char pk_P[] = { + 0xf5, 0x93, 0x0f, 0x76, 0x00, 0xab, 0x37, 0x01, 0xb9, 0x52, 0xb6, 0x82, 0xf9, 0xf5, 0xae, 0x29, + 0x8f, 0xd5, 0x08, 0xbc, 0xf7, 0x9f, 0x84, 0xb6, 0x4c, 0x94, 0xb5, 0xfc, 0xfe, 0xe1, 0xcd, 0x6a, + 0xf4, 0x9c, 0xa7, 0x33, 0xdb, 0xd8, 0xc8, 0xc1, 0xc0, 0x8d, 0x65, 0xed, 0x29, 0x99, 0x6c, 0x5c, + 0xbe, 0x08, 0xac, 0x04, 0xe4, 0x3a, 0x18, 0xe2, 0x0f, 0x70, 0x26, 0x70, 0x9b, 0x71, 0xfc, 0x9f, + 0x22, 0xea, 0x90, 0x3b, 0xc2, 0xa5, 0x16, 0x7a, 0xcd, 0x04, 0x3e, 0xa6, 0x37, 0x49, 0xa7, 0xee, + 0xaa, 0xe4, 0x9d, 0xaa, 0x9b, 0xb0, 0xe2, 0x6a, 0x9d, 0x1e, 0xcd, 0x83, 0x4e, 0xd8, 0x59, 0x6d, + 0x03, 0xd5, 0x4c, 0x5e, 0xc5, 0x22, 0x10, 0xb7, 0xcc, 0x0c, 0x90, 0x76, 0x05, 0x21, 0xe7, 0x77, + 0x5c, 0x88, 0x5f, 0xd0, 0x5f, 0x9e, 0x2e, 0x49, 0x56, 0xf4, 0x2b, 0xa9, 0x99, 0x57, 0x74, 0x19, +}; + +static unsigned char pk_Q[] = { + 0xe4, 0x5f, 0xd2, 0x28, 0xbd, 0xf3, 0xdd, 0x70, 0x3d, 0xfd, 0x01, 0x23, 0xae, 0x93, 0x6a, 0x91, + 0xca, 0x68, 0xb1, 0xdb, 0x81, 0xab, 0x1e, 0x63, 0x76, 0x9b, 0x6d, 0xaa, 0x41, 0x87, 0x5a, 0x79, + 0xe7, 0xce, 0xd6, 0x84, 0x32, 0x53, 0xf5, 0xfc, 0xb7, 0x41, 0x7c, 0xcb, 0x88, 0x09, 0xcb, 0xe9, + 0x07, 0x16, 0x28, 0x55, 0x23, 0xe5, 0xf2, 0xf5, 0x23, 0xf5, 0xee, 0x2b, 0x9d, 0x91, 0x56, 0xc6, + 0x30, 0x91, 0x4d, 0x16, 0x11, 0x6c, 0x48, 0x45, 0xe8, 0x5d, 0x0e, 0x9e, 0x04, 0xc8, 0xb6, 0xdd, + 0xba, 0x0d, 0xdf, 0x53, 0x56, 0xfa, 0x0b, 0x0b, 0x99, 0x8d, 0xea, 0x5c, 0x45, 0x7d, 0xed, 0xad, + 0x1f, 0xc5, 0xc1, 0x7d, 0x63, 0x31, 0xf8, 0x32, 0xb5, 0x33, 0xb0, 0xef, 0xce, 0x2e, 0x74, 0x1e, + 0x77, 0x2a, 0x18, 0x35, 0x3d, 0x6e, 0x01, 0xba, 0xde, 0x21, 0x8e, 0x14, 0x12, 0xc3, 0x0d, 0x43, +}; + +static unsigned char pk_dP[] = { + 0x5a, 0xc8, 0xf7, 0x1a, 0x44, 0xbd, 0x07, 0x24, 0xd8, 0x02, 0x3f, 0xfe, 0xc3, 0xb1, 0x93, 0xa5, + 0x41, 0xcb, 0x1b, 0xe3, 0xe0, 0x17, 0x54, 0xd4, 0xa0, 0x13, 0x0a, 0x04, 0x71, 0xa5, 0xc0, 0x6f, + 0x1d, 0xe7, 0x1b, 0xd9, 0x0c, 0x19, 0x64, 0x7e, 0x5c, 0x54, 0xe9, 0xad, 0x77, 0x87, 0x84, 0x8b, + 0xf4, 0xa4, 0xf8, 0x13, 0x06, 0xdc, 0x83, 0x7e, 0x6e, 0xfe, 0xa2, 0xf7, 0x56, 0x40, 0x19, 0x88, + 0x2b, 0x3c, 0x53, 0xfe, 0x03, 0xc3, 0x4c, 0x40, 0x31, 0xb2, 0xb4, 0x06, 0x76, 0xc2, 0x00, 0x17, + 0x37, 0x8e, 0x34, 0xcb, 0x71, 0xab, 0x3e, 0xc8, 0xf3, 0x35, 0x03, 0xfc, 0xdb, 0x15, 0x18, 0x5a, + 0x38, 0xe4, 0x8d, 0xcb, 0x2b, 0x4d, 0xa0, 0xa8, 0x92, 0x02, 0xc3, 0x15, 0x1e, 0x68, 0x9e, 0x4d, + 0x7e, 0x23, 0xdc, 0x68, 0x08, 0x31, 0x4e, 0x23, 0x46, 0xc6, 0x15, 0xae, 0x29, 0x46, 0x2f, 0x61, +}; + +static unsigned char pk_dQ[] = { + 0x33, 0x61, 0x9f, 0xae, 0x0c, 0xf6, 0xc6, 0x16, 0x8f, 0xcb, 0xd1, 0xaa, 0xce, 0x87, 0x5a, 0x4d, + 0xcc, 0xe5, 0x7b, 0x46, 0xb0, 0xc8, 0xe8, 0x40, 0x66, 0x9a, 0x17, 0xb5, 0x5b, 0xa2, 0xf1, 0x67, + 0x46, 0x11, 0x52, 0x50, 0x51, 0xe6, 0x74, 0x0c, 0xd4, 0xca, 0x46, 0x22, 0xa0, 0xcb, 0xdb, 0x75, + 0xe5, 0x63, 0x45, 0xd5, 0xca, 0x0a, 0xdd, 0x7b, 0xec, 0x08, 0x53, 0xfa, 0xba, 0x2b, 0xce, 0x03, + 0x2f, 0x40, 0x31, 0xc0, 0xca, 0x50, 0xbb, 0x7e, 0x07, 0x06, 0x90, 0xd8, 0x5a, 0xa9, 0x32, 0x03, + 0x76, 0xed, 0xd2, 0x16, 0x35, 0x16, 0x72, 0xcf, 0xbc, 0x4f, 0xa2, 0xaf, 0xf9, 0xee, 0x98, 0x40, + 0x00, 0x4b, 0x04, 0xfa, 0x8a, 0x0b, 0xdf, 0x14, 0xc1, 0x92, 0x0c, 0xb8, 0x17, 0x82, 0x7a, 0x1b, + 0xb4, 0xa1, 0xe2, 0xea, 0x6f, 0x94, 0xc5, 0x8c, 0xde, 0x97, 0x5c, 0x19, 0x06, 0x13, 0x9e, 0x73, +}; + +static unsigned char pk_I[] = { + 0x75, 0x40, 0xc6, 0x02, 0x7e, 0x4f, 0xad, 0xdb, 0x95, 0xac, 0x07, 0x8d, 0x80, 0xb6, 0x80, 0x02, + 0x06, 0xdd, 0xb8, 0x5d, 0x92, 0x65, 0x69, 0x26, 0x86, 0x61, 0x6c, 0x87, 0x4e, 0xe5, 0x03, 0x68, + 0xc6, 0x10, 0x15, 0x8c, 0x43, 0x3a, 0x45, 0x63, 0x48, 0xb7, 0x8a, 0x8c, 0xa2, 0x2b, 0x34, 0xb6, + 0x83, 0xfe, 0xa8, 0x10, 0xa5, 0x74, 0xa5, 0xa9, 0x52, 0x42, 0x1f, 0xa0, 0x80, 0x6a, 0xc5, 0x35, + 0xe8, 0xb8, 0xc2, 0xa0, 0x3f, 0x49, 0x18, 0xcf, 0x0e, 0x54, 0x3c, 0x70, 0x11, 0x11, 0xd3, 0x85, + 0x8c, 0xb2, 0xe5, 0x74, 0xdf, 0x98, 0xea, 0x6c, 0xc0, 0x5f, 0x7f, 0xff, 0x69, 0xbf, 0x08, 0x8d, + 0x1b, 0xc4, 0x90, 0xcc, 0xa4, 0xcd, 0xcc, 0x34, 0x58, 0xe5, 0x91, 0x53, 0x3a, 0xd5, 0x39, 0xf4, + 0xd4, 0x42, 0xc9, 0x17, 0xb2, 0x2c, 0x92, 0x12, 0x37, 0x1b, 0xd3, 0xc5, 0x79, 0xd2, 0x65, 0x61, +}; + +static int test_pk(bool verbose) +{ + int ret = 1; + size_t tmp_len, tmp2_len; + unsigned char *tmp, *tmp2; + struct crypto_pk *pubk, *privk; + unsigned char *msg = message; + size_t msg_len = sizeof(pk_N); + + printf("Testing public keys interfaces\n"); + + pubk = crypto_pk_open(PK_RSA, + pk_N, sizeof(pk_N), + pk_E, sizeof(pk_E)); + if (!pubk) { + fprintf(stderr, "ERROR: open public key.\n"); + return 1; + } + + tmp_len = crypto_pk_get_nbits(pubk); + if (tmp_len != sizeof(pk_N) * 8) { + fprintf(stderr, "ERROR: crypto_pk_get_nbits mismatch.\n"); + goto close_pub; + } + + tmp = crypto_pk_get_parameter(pubk, 0, &tmp_len); + if (tmp_len != sizeof(pk_N) || memcmp(tmp, pk_N, tmp_len)) { + fprintf(stderr, "ERROR: crypto_pk_get_parameter(0) Modulus. param len %d len %d\n", tmp_len, sizeof(pk_N)); + free(tmp); + goto close_pub; + } + free(tmp); + + tmp = crypto_pk_get_parameter(pubk, 1, &tmp_len); + if (tmp_len != sizeof(pk_E) || memcmp(tmp, pk_E, tmp_len)) { + fprintf(stderr, "ERROR: crypto_pk_get_parameter(1) Exponent.\n"); + free(tmp); + goto close_pub; + } + free(tmp); + + privk = crypto_pk_open_priv(PK_RSA, + pk_N, sizeof(pk_N), + pk_E, sizeof(pk_E), + pk_D, sizeof(pk_D), + pk_P, sizeof(pk_P), + pk_Q, sizeof(pk_Q), + pk_dP, sizeof(pk_dP), + pk_dQ, sizeof(pk_dQ), + pk_I, sizeof(pk_I)); + if (!privk) { + fprintf(stderr, "ERROR: open private key.\n"); + goto close_pub; + } + + + tmp_len = crypto_pk_get_nbits(privk); + if (tmp_len != sizeof(pk_N) * 8) { + fprintf(stderr, "ERROR: crypto_pk_get_nbits mismatch.\n"); + goto close_pub; + } + + tmp = crypto_pk_get_parameter(privk, 0, &tmp_len); + if (tmp_len != sizeof(pk_N) || memcmp(tmp, pk_N, tmp_len)) { + fprintf(stderr, "ERROR: crypto_pk_get_parameter(0) Modulus. param len %d len %d\n", tmp_len, sizeof(pk_N)); + free(tmp); + goto close; + } + free(tmp); + + tmp = crypto_pk_get_parameter(privk, 1, &tmp_len); + if (tmp_len != sizeof(pk_E) || memcmp(tmp, pk_E, tmp_len)) { + fprintf(stderr, "ERROR: crypto_pk_get_parameter(1) Exponent.\n"); + free(tmp); + goto close; + } + free(tmp); + + tmp = crypto_pk_decrypt(privk, msg, msg_len, &tmp_len); + if (!tmp) { + fprintf(stderr, "ERROR: crypto_pk_decrypt.\n"); + goto close; + } + + tmp2 = crypto_pk_encrypt(pubk, tmp, tmp_len, &tmp2_len); + if (!tmp2) { + fprintf(stderr, "ERROR: crypto_pk_encrypt.\n"); + goto free_tmp; + } + + if (tmp2_len == msg_len && !memcmp(tmp2, msg, tmp2_len)) { + ret = 0; + } else { + fprintf(stderr, "ERROR: encrypt-decrypt sequence length or data error.\n"); + } + + free(tmp2); +free_tmp: + free(tmp); + +close: + crypto_pk_close(privk); +close_pub: + crypto_pk_close(pubk); + + return ret; +} + +int exec_crypto_test(bool verbose) +{ + unsigned int keylengths[] = {1024, 1152, 1408, 1984, 2048, 3072, 4096}; + int i; + int ret; + fprintf(stdout, "\n"); + + ret = test_pk(verbose); + if (ret) { + fprintf(stderr, "Crypto raw test: failed\n"); + return ret; + } + fprintf(stdout, "Crypto raw test: passed\n\n"); + + for (i = 0; i < sizeof(keylengths) / sizeof(keylengths[0]); i++) { + unsigned int kl = keylengths[i]; + ret = test_genkey(kl, message, kl / 8, verbose); + if (ret) { + fprintf(stderr, "Crypto generate key[%d] test: failed\n", kl); + return ret; + } + } + + return 0; +} diff --git a/client/emv/test/crypto_test.h b/client/emv/test/crypto_test.h new file mode 100644 index 00000000..084bc991 --- /dev/null +++ b/client/emv/test/crypto_test.h @@ -0,0 +1,18 @@ +/* + * emv-tools - a set of tools to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + + #include + +extern int exec_crypto_test(bool verbose); diff --git a/client/emv/test/cryptotest.c b/client/emv/test/cryptotest.c new file mode 100644 index 00000000..a6d06e68 --- /dev/null +++ b/client/emv/test/cryptotest.c @@ -0,0 +1,65 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 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. +//----------------------------------------------------------------------------- +// Crypto algorithms testing +//----------------------------------------------------------------------------- + +#include "cryptotest.h" +#include "util.h" +#include "ui.h" + +#include "bignum.h" +#include "aes.h" +#include "des.h" +#include "rsa.h" +#include "sha1.h" + +#include "crypto_test.h" +#include "sda_test.h" +#include "dda_test.h" +#include "cda_test.h" + +int ExecuteCryptoTests(bool verbose) { + int res; + bool TestFail = false; + + res = mpi_self_test(verbose); + if (res) TestFail = true; + + res = aes_self_test(verbose); + if (res) TestFail = true; + + res = des_self_test(verbose); + if (res) TestFail = true; + + res = sha1_self_test(verbose); + if (res) TestFail = true; + + res = rsa_self_test(verbose); + if (res) TestFail = true; + + res = exec_sda_test(verbose); + if (res) TestFail = true; + + res = exec_dda_test(verbose); + if (res) TestFail = true; + + res = exec_cda_test(verbose); + if (res) TestFail = true; + + res = exec_crypto_test(verbose); + if (res) TestFail = true; + + PrintAndLog("\n--------------------------"); + if (TestFail) + PrintAndLog("Test(s) [ERROR]."); + else + PrintAndLog("Tests [OK]."); + + return TestFail; +} + diff --git a/client/emv/test/cryptotest.h b/client/emv/test/cryptotest.h new file mode 100644 index 00000000..5c6aaae2 --- /dev/null +++ b/client/emv/test/cryptotest.h @@ -0,0 +1,13 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 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. +//----------------------------------------------------------------------------- +// Crypto algorithms testing +//----------------------------------------------------------------------------- + +#include + +extern int ExecuteCryptoTests(bool verbose); diff --git a/client/emv/test/dda_test.c b/client/emv/test/dda_test.c new file mode 100644 index 00000000..afdd4e20 --- /dev/null +++ b/client/emv/test/dda_test.c @@ -0,0 +1,390 @@ +/* + * emv-tools - a set of tools to work with EMV family of smart cards + * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "dda_test.h" + +#include "../emv_pk.h" +#include "../crypto.h" +#include "../dump.h" +#include "../tlv.h" +#include "../emv_pki.h" + +#include +#include +#include + +struct emv_pk mchip_05 = { + .rid = { 0xa0, 0x00, 0x00, 0x00, 0x04, }, + .index = 5, + .hash_algo = HASH_SHA_1, + .pk_algo = PK_RSA, + .hash = { + 0xeb, 0xfa, 0x0d, 0x5d, + 0x06, 0xd8, 0xce, 0x70, + 0x2d, 0xa3, 0xea, 0xe8, + 0x90, 0x70, 0x1d, 0x45, + 0xe2, 0x74, 0xc8, 0x45, }, + .exp = { 0x03, }, + .elen = 1, + .mlen = 1408 / 8, + .modulus = (unsigned char[]){ + 0xb8, 0x04, 0x8a, 0xbc, 0x30, 0xc9, 0x0d, 0x97, 0x63, 0x36, 0x54, 0x3e, 0x3f, 0xd7, 0x09, 0x1c, + 0x8f, 0xe4, 0x80, 0x0d, 0xf8, 0x20, 0xed, 0x55, 0xe7, 0xe9, 0x48, 0x13, 0xed, 0x00, 0x55, 0x5b, + 0x57, 0x3f, 0xec, 0xa3, 0xd8, 0x4a, 0xf6, 0x13, 0x1a, 0x65, 0x1d, 0x66, 0xcf, 0xf4, 0x28, 0x4f, + 0xb1, 0x3b, 0x63, 0x5e, 0xdd, 0x0e, 0xe4, 0x01, 0x76, 0xd8, 0xbf, 0x04, 0xb7, 0xfd, 0x1c, 0x7b, + 0xac, 0xf9, 0xac, 0x73, 0x27, 0xdf, 0xaa, 0x8a, 0xa7, 0x2d, 0x10, 0xdb, 0x3b, 0x8e, 0x70, 0xb2, + 0xdd, 0xd8, 0x11, 0xcb, 0x41, 0x96, 0x52, 0x5e, 0xa3, 0x86, 0xac, 0xc3, 0x3c, 0x0d, 0x9d, 0x45, + 0x75, 0x91, 0x64, 0x69, 0xc4, 0xe4, 0xf5, 0x3e, 0x8e, 0x1c, 0x91, 0x2c, 0xc6, 0x18, 0xcb, 0x22, + 0xdd, 0xe7, 0xc3, 0x56, 0x8e, 0x90, 0x02, 0x2e, 0x6b, 0xba, 0x77, 0x02, 0x02, 0xe4, 0x52, 0x2a, + 0x2d, 0xd6, 0x23, 0xd1, 0x80, 0xe2, 0x15, 0xbd, 0x1d, 0x15, 0x07, 0xfe, 0x3d, 0xc9, 0x0c, 0xa3, + 0x10, 0xd2, 0x7b, 0x3e, 0xfc, 0xcd, 0x8f, 0x83, 0xde, 0x30, 0x52, 0xca, 0xd1, 0xe4, 0x89, 0x38, + 0xc6, 0x8d, 0x09, 0x5a, 0xac, 0x91, 0xb5, 0xf3, 0x7e, 0x28, 0xbb, 0x49, 0xec, 0x7e, 0xd5, 0x97, + }, +}; + +const unsigned char d_issuer_cert[] = { + 0x17, 0x14, 0x28, 0x4f, 0x76, 0x3b, 0x85, 0x86, 0xee, 0x6d, 0x31, 0x99, 0x51, 0xf7, 0xe6, 0x3f, + 0xa2, 0x50, 0x76, 0xe5, 0x0d, 0xc9, 0xd3, 0x20, 0x0b, 0xa9, 0x98, 0xd3, 0xa0, 0x52, 0xad, 0xba, + 0x9a, 0xb6, 0x9a, 0xc6, 0xad, 0x6a, 0xdd, 0x3c, 0xe0, 0x9f, 0x02, 0x78, 0xf4, 0x07, 0x4e, 0xc4, + 0xee, 0x9b, 0x1d, 0x22, 0x68, 0xa3, 0xe9, 0x53, 0x57, 0x5e, 0x45, 0x4e, 0x50, 0xcd, 0x86, 0x0b, + 0xf4, 0x24, 0xc5, 0x1c, 0x59, 0x77, 0x12, 0xd2, 0xaa, 0x05, 0x70, 0x89, 0xdd, 0x86, 0x73, 0xe5, + 0x1b, 0x1e, 0x1d, 0x71, 0x88, 0x03, 0x48, 0x92, 0x07, 0x7a, 0xc1, 0x8a, 0x6a, 0xe2, 0x34, 0x88, + 0xbe, 0xa9, 0xdf, 0x3b, 0x1a, 0x83, 0xf2, 0xc0, 0x80, 0x0c, 0xd7, 0xc5, 0xcd, 0xf2, 0xfd, 0xe0, + 0x49, 0x6f, 0x7b, 0xc3, 0x9f, 0xb4, 0xbf, 0x36, 0x32, 0x99, 0xbf, 0xa6, 0x37, 0xb2, 0xec, 0x33, + 0xc5, 0x07, 0xe3, 0x68, 0x21, 0xee, 0xc2, 0x07, 0x5f, 0x0e, 0x42, 0x0d, 0x38, 0xa1, 0xc9, 0xf3, + 0x12, 0x72, 0x61, 0xba, 0x31, 0x6c, 0x98, 0x76, 0x74, 0xfa, 0xdb, 0x20, 0xea, 0x7f, 0xeb, 0x75, + 0xee, 0x45, 0x5d, 0x12, 0x14, 0x6e, 0xa6, 0xf0, 0x2e, 0x8b, 0x01, 0xec, 0x2f, 0xa7, 0xa1, 0x15, +}; + +const unsigned char d_issuer_rem[] = { + 0x6e, 0x63, 0xb7, 0xbc, 0x70, 0xab, 0xdd, 0x09, 0x34, 0x1b, 0x34, 0xc0, 0x32, 0x86, 0xba, 0x9b, + 0xd8, 0x3b, 0xa7, 0x93, 0x6c, 0x5b, 0x77, 0x98, 0xfb, 0x22, 0xc5, 0xe5, 0x3f, 0xf2, 0x40, 0xa2, + 0x6d, 0xbd, 0x64, 0x15, +}; + +const unsigned char d_issuer_exp[] = { + 0x03, +}; + +const unsigned char d_icc_cert[] = { + 0xa4, 0x2f, 0xbe, 0xb1, 0x56, 0xb9, 0x8d, 0xcb, 0x05, 0x54, 0xda, 0x06, 0x2a, 0xdc, 0xa5, 0x30, + 0x9a, 0x91, 0xf0, 0x4f, 0xa2, 0xc7, 0xbd, 0x71, 0x02, 0xa8, 0xd7, 0x3f, 0x16, 0xa3, 0xcf, 0xad, + 0xe8, 0xaa, 0xdf, 0x4f, 0x3f, 0xe2, 0xa2, 0x12, 0x5c, 0xcd, 0xd7, 0x7c, 0x6b, 0x9f, 0x78, 0xb5, + 0xb4, 0x37, 0x1c, 0xe0, 0x80, 0x57, 0x25, 0xb0, 0xf9, 0xc0, 0x27, 0xaf, 0x14, 0x7d, 0x91, 0xe1, + 0xff, 0xdb, 0x20, 0x1e, 0x9c, 0x17, 0x0c, 0xe7, 0x77, 0x05, 0x3a, 0x17, 0x2a, 0xd5, 0x26, 0xdc, + 0xaf, 0xd3, 0x38, 0x95, 0xe1, 0xa9, 0x47, 0x30, 0x5c, 0x5b, 0x16, 0x7f, 0x2e, 0x7c, 0x6f, 0x99, + 0x15, 0x81, 0xa6, 0x52, 0xee, 0x47, 0x31, 0x54, 0x76, 0x0c, 0x2e, 0xd7, 0x74, 0x21, 0x4e, 0x50, + 0xdf, 0xec, 0xdd, 0x4c, 0xf2, 0x94, 0xc9, 0x74, 0xb8, 0x9e, 0xbc, 0xa2, 0x5b, 0x5a, 0xb3, 0xc0, + 0xbe, 0xb5, 0x0d, 0xfa, 0xf7, 0x82, 0xaf, 0xde, 0x14, 0x33, 0xd9, 0x0c, 0xa2, 0xa8, 0x9d, 0x65, + 0x1e, 0x75, 0xd6, 0x7e, 0xbc, 0x7c, 0x3e, 0x36, 0xf5, 0xa1, 0x65, 0xee, 0x61, 0x32, 0x61, 0x29, + 0x39, 0xc1, 0xec, 0xd3, 0x99, 0xe4, 0x60, 0x74, 0xb9, 0x96, 0xd9, 0x3a, 0x88, 0xe0, 0x1e, 0x0a, +}; + +const unsigned char d_icc_exp[] = { + 0x03, +}; + +const unsigned char d_sdad_cr[] = { + 0x3d, 0x87, 0xf3, 0x10, 0x56, 0x10, 0x2d, 0x25, 0x12, 0xcf, 0xde, 0x30, 0x90, 0x06, 0x27, 0xc1, + 0x26, 0x3a, 0x76, 0xd1, 0xda, 0xa8, 0x21, 0xf5, 0x08, 0x31, 0xe6, 0x06, 0xc5, 0x45, 0x44, 0xc2, + 0x58, 0x13, 0x1e, 0xae, 0xbe, 0x87, 0x4d, 0xcb, 0x1a, 0x28, 0xcf, 0x82, 0xd3, 0xff, 0x91, 0x11, + 0x82, 0x60, 0xbc, 0x91, 0x11, 0x37, 0x11, 0xd3, 0xb2, 0x89, 0xfa, 0x41, 0xbe, 0x69, 0xc7, 0xa7, + 0xb5, 0xc7, 0x83, 0xe6, 0xf8, 0xf9, 0x7f, 0xce, 0x13, 0xf0, 0x8b, 0x13, 0xfa, 0x44, 0x18, 0x3e, + 0x37, 0x18, 0xce, 0xbf, 0x0c, 0x41, 0x47, 0x3d, 0x2b, 0x0f, 0xf4, 0xde, 0x44, 0xb6, 0xa0, 0x2d, + 0x75, 0xad, 0xb6, 0xd4, 0x96, 0x23, 0x93, 0xff, 0xdf, 0x4e, 0x69, 0x02, 0x6c, 0xdf, 0x38, 0xff, +}; + +const unsigned char d_ssd1[] = { + 0x5f, 0x25, 0x03, 0x14, 0x05, 0x01, 0x5f, 0x24, 0x03, 0x15, 0x06, 0x30, 0x5a, 0x08, 0x52, 0x85, + 0x88, 0x12, 0x54, 0x34, 0x56, 0x53, 0x5f, 0x34, 0x01, 0x01, 0x8e, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1e, 0x03, 0x1f, 0x03, 0x9f, 0x07, 0x02, 0xff, 0x00, 0x9f, 0x0d, 0x05, + 0xbc, 0x50, 0xbc, 0x00, 0x00, 0x9f, 0x0e, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9f, 0x0f, 0x05, + 0xbc, 0x70, 0xbc, 0x98, 0x00, 0x9f, 0x4a, 0x01, 0x82, 0x5f, 0x28, 0x02, 0x06, 0x43, 0x8c, 0x21, + 0x9f, 0x02, 0x06, 0x9f, 0x03, 0x06, 0x9f, 0x1a, 0x02, 0x95, 0x05, 0x5f, 0x2a, 0x02, 0x9a, 0x03, + 0x9c, 0x01, 0x9f, 0x37, 0x04, 0x9f, 0x35, 0x01, 0x9f, 0x45, 0x02, 0x9f, 0x4c, 0x08, 0x9f, 0x34, + 0x03, 0x8d, 0x0c, 0x91, 0x0a, 0x8a, 0x02, 0x95, 0x05, 0x9f, 0x37, 0x04, 0x9f, 0x4c, 0x08, + 0x39, 0x00, +}; +static const struct tlv ssd1_tlv = { + .len = sizeof(d_ssd1), + .value = d_ssd1, +}; + +const unsigned char d_pan[] = { + 0x52, 0x85, 0x88, 0x12, 0x54, 0x34, 0x56, 0x53, +}; + +const unsigned char d_dd1[] = { + 0x00, 0x00, 0x00, 0x00, +}; +static const struct tlv dd1_tlv = { + .len = sizeof(d_dd1), + .value = d_dd1, +}; + +static int dda_test_raw(bool verbose) +{ + const struct emv_pk *pk = &mchip_05; + + struct crypto_pk *kcp = crypto_pk_open(PK_RSA, + pk->modulus, pk->mlen, + pk->exp, pk->elen); + if (!kcp) + return 1; + + unsigned char *ipk_data; + size_t ipk_data_len; + ipk_data = crypto_pk_encrypt(kcp, d_issuer_cert, sizeof(d_issuer_cert), &ipk_data_len); + crypto_pk_close(kcp); + + if (!ipk_data) + return 1; + + if (verbose) { + printf("issuer cert:\n"); + dump_buffer(ipk_data, ipk_data_len, stdout, 0); + } + + size_t ipk_pk_len = ipk_data[13]; + unsigned char *ipk_pk = malloc(ipk_pk_len); + memcpy(ipk_pk, ipk_data + 15, ipk_data_len - 36); + memcpy(ipk_pk + ipk_data_len - 36, d_issuer_rem, sizeof(d_issuer_rem)); + + struct crypto_hash *ch; + ch = crypto_hash_open(HASH_SHA_1); + if (!ch) { + free(ipk_pk); + free(ipk_data); + return 1; + } + + crypto_hash_write(ch, ipk_data + 1, 14); + crypto_hash_write(ch, ipk_pk, ipk_pk_len); + crypto_hash_write(ch, d_issuer_exp, sizeof(d_issuer_exp)); + + unsigned char *h = crypto_hash_read(ch); + if (!h) { + crypto_hash_close(ch); + free(ipk_pk); + free(ipk_data); + return 1; + } + + if (verbose) { + printf("crypto hash:\n"); + dump_buffer(h, 20, stdout, 0); + } + + if (memcmp(ipk_data + ipk_data_len - 21, h, 20)) { + crypto_hash_close(ch); + free(ipk_pk); + free(ipk_data); + return 1; + } + + crypto_hash_close(ch); + free(ipk_data); + + struct crypto_pk *ikcp = crypto_pk_open(PK_RSA, ipk_pk, (int) ipk_pk_len, + d_issuer_exp, (int) sizeof(d_issuer_exp)); + free(ipk_pk); + if (!ikcp) + return 1; + + unsigned char *iccpk_data; + size_t iccpk_data_len; + iccpk_data = crypto_pk_encrypt(ikcp, d_icc_cert, sizeof(d_icc_cert), &iccpk_data_len); + crypto_pk_close(ikcp); + + if (!iccpk_data) + return 1; + + if (verbose) { + printf("icc cert:\n"); + dump_buffer(iccpk_data, iccpk_data_len, stdout, 0); + } + + size_t iccpk_pk_len = iccpk_data[19]; + unsigned char *iccpk_pk = malloc(iccpk_pk_len); + memcpy(iccpk_pk, iccpk_data + 21, /*iccpk_data_len - 36*/iccpk_pk_len); + /*memcpy(iccpk_pk + iccpk_data_len - 36, icc_rem, sizeof(icc_rem));*/ + + ch = crypto_hash_open(HASH_SHA_1); + if (!ch) { + free(iccpk_pk); + free(iccpk_data); + return 1; + } + + crypto_hash_write(ch, iccpk_data + 1, iccpk_data_len - 22); + crypto_hash_write(ch, d_icc_exp, sizeof(d_icc_exp)); + crypto_hash_write(ch, d_ssd1, sizeof(d_ssd1)); + + h = crypto_hash_read(ch); + if (!h) { + crypto_hash_close(ch); + free(iccpk_pk); + free(iccpk_data); + return 1; + } + + if (verbose) { + printf("crypto hash1.1:\n"); + dump_buffer(h, 20, stdout, 0); + } + + if (memcmp(iccpk_data + iccpk_data_len - 21, h, 20)) { + crypto_hash_close(ch); + free(iccpk_pk); + free(iccpk_data); + return 1; + } + + crypto_hash_close(ch); + free(iccpk_data); + + struct crypto_pk *icckcp = crypto_pk_open(PK_RSA, iccpk_pk, (int) iccpk_pk_len, + d_issuer_exp, (int) sizeof(d_issuer_exp)); + free(iccpk_pk); + if (!icckcp) + return 1; + + size_t sdad_len; + unsigned char *sdad = crypto_pk_encrypt(icckcp, d_sdad_cr, sizeof(d_sdad_cr), &sdad_len); + crypto_pk_close(icckcp); + if (!sdad) + return 1; + + if (verbose) { + printf("sdad:\n"); + dump_buffer(sdad, sdad_len, stdout, 0); + } + + ch = crypto_hash_open(HASH_SHA_1); + if (!ch) { + free(sdad); + return 1; + } + + crypto_hash_write(ch, sdad + 1, sdad_len - 22); + crypto_hash_write(ch, d_dd1, sizeof(d_dd1)); + + unsigned char *h2 = crypto_hash_read(ch); + if (!h2) { + crypto_hash_close(ch); + free(sdad); + return 1; + } + + if (verbose) { + printf("crypto hash2:\n"); + dump_buffer(h2, 20, stdout, 0); + } + + crypto_hash_close(ch); + + free(sdad); + + return 0; +} + +static int dda_test_pk(bool verbose) +{ + const struct emv_pk *pk = &mchip_05; + struct tlvdb *db; + + db = tlvdb_external(0x90, sizeof(d_issuer_cert), d_issuer_cert); + tlvdb_add(db, tlvdb_external(0x9f32, sizeof(d_issuer_exp), d_issuer_exp)); + tlvdb_add(db, tlvdb_external(0x92, sizeof(d_issuer_rem), d_issuer_rem)); + tlvdb_add(db, tlvdb_external(0x5a, sizeof(d_pan), d_pan)); + + struct emv_pk *ipk = emv_pki_recover_issuer_cert(pk, db); + if (!ipk) { + fprintf(stderr, "Could not recover Issuer certificate!\n"); + tlvdb_free(db); + return 2; + } + + tlvdb_add(db, tlvdb_external(0x9f46, sizeof(d_icc_cert), d_icc_cert)); + tlvdb_add(db, tlvdb_external(0x9f47, sizeof(d_icc_exp), d_icc_exp)); + /*tlvdb_add(db, tlvdb_external(0x9f48, sizeof(d_issuer_rem), d_issuer_rem));*/ + + struct emv_pk *iccpk = emv_pki_recover_icc_cert(ipk, db, &ssd1_tlv); + if (!iccpk) { + fprintf(stderr, "Could not recover ICC certificate!\n"); + emv_pk_free(ipk); + tlvdb_free(db); + return 2; + } + + tlvdb_add(db, tlvdb_external(0x9f4b, sizeof(d_sdad_cr), d_sdad_cr)); + + struct tlvdb *idndb = emv_pki_recover_idn(iccpk, db, &dd1_tlv); + if (!idndb) { + fprintf(stderr, "Could not recover IDN!\n"); + emv_pk_free(iccpk); + emv_pk_free(ipk); + tlvdb_free(db); + return 2; + } + + const struct tlv *idn = tlvdb_get(idndb, 0x9f4c, NULL); + if (!idn) { + fprintf(stderr, "IDN not found!\n"); + tlvdb_free(idndb); + emv_pk_free(iccpk); + emv_pk_free(ipk); + tlvdb_free(db); + return 2; + } + + if (verbose) { + printf("IDN:\n"); + dump_buffer(idn->value, idn->len, stdout, 0); + } + + tlvdb_free(idndb); + emv_pk_free(iccpk); + emv_pk_free(ipk); + tlvdb_free(db); + + return 0; +} + +int exec_dda_test(bool verbose) +{ + int ret; + fprintf(stdout, "\n"); + + ret = dda_test_raw(verbose); + if (ret) { + fprintf(stderr, "DDA raw test: failed\n"); + return ret; + } + fprintf(stdout, "DDA raw test: passed\n"); + + ret = dda_test_pk(verbose); + if (ret) { + fprintf(stderr, "DDA test pk: failed\n"); + return ret; + } + fprintf(stdout, "DDA test pk: passed\n"); + + return 0; +} diff --git a/client/emv/test/dda_test.h b/client/emv/test/dda_test.h new file mode 100644 index 00000000..354cd7e1 --- /dev/null +++ b/client/emv/test/dda_test.h @@ -0,0 +1,18 @@ +/* + * emv-tools - a set of tools to work with EMV family of smart cards + * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include + +extern int exec_dda_test(bool verbose); diff --git a/client/emv/test/sda_test.c b/client/emv/test/sda_test.c new file mode 100644 index 00000000..2aafcf19 --- /dev/null +++ b/client/emv/test/sda_test.c @@ -0,0 +1,277 @@ +/* + * emv-tools - a set of tools to work with EMV family of smart cards + * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "../emv_pk.h" +#include "../crypto.h" +#include "../dump.h" +#include "../tlv.h" +#include "../emv_pki.h" + +#include +#include +#include + +struct emv_pk vsdc_01 = { + .rid = { 0xa0, 0x00, 0x00, 0x00, 0x03, }, + .index = 1, + .hash_algo = HASH_SHA_1, + .pk_algo = PK_RSA, + .hash = { + 0xd3, 0x4a, 0x6a, 0x77, + 0x60, 0x11, 0xc7, 0xe7, + 0xce, 0x3a, 0xec, 0x5f, + 0x03, 0xad, 0x2f, 0x8c, + 0xfc, 0x55, 0x03, 0xcc, }, + .exp = { 0x03, }, + .elen = 1, + .mlen = 1024 / 8, + .modulus = (unsigned char[]){ + 0xc6, 0x96, 0x03, 0x42, 0x13, 0xd7, 0xd8, 0x54, 0x69, 0x84, 0x57, 0x9d, 0x1d, 0x0f, 0x0e, 0xa5, + 0x19, 0xcf, 0xf8, 0xde, 0xff, 0xc4, 0x29, 0x35, 0x4c, 0xf3, 0xa8, 0x71, 0xa6, 0xf7, 0x18, 0x3f, + 0x12, 0x28, 0xda, 0x5c, 0x74, 0x70, 0xc0, 0x55, 0x38, 0x71, 0x00, 0xcb, 0x93, 0x5a, 0x71, 0x2c, + 0x4e, 0x28, 0x64, 0xdf, 0x5d, 0x64, 0xba, 0x93, 0xfe, 0x7e, 0x63, 0xe7, 0x1f, 0x25, 0xb1, 0xe5, + 0xf5, 0x29, 0x85, 0x75, 0xeb, 0xe1, 0xc6, 0x3a, 0xa6, 0x17, 0x70, 0x69, 0x17, 0x91, 0x1d, 0xc2, + 0xa7, 0x5a, 0xc2, 0x8b, 0x25, 0x1c, 0x7e, 0xf4, 0x0f, 0x23, 0x65, 0x91, 0x24, 0x90, 0xb9, 0x39, + 0xbc, 0xa2, 0x12, 0x4a, 0x30, 0xa2, 0x8f, 0x54, 0x40, 0x2c, 0x34, 0xae, 0xca, 0x33, 0x1a, 0xb6, + 0x7e, 0x1e, 0x79, 0xb2, 0x85, 0xdd, 0x57, 0x71, 0xb5, 0xd9, 0xff, 0x79, 0xea, 0x63, 0x0b, 0x75, + }, +}; + +const unsigned char issuer_cert[] = { + 0x3c, 0x5f, 0xea, 0xd4, 0xdd, 0x7b, 0xca, 0x44, 0xf9, 0x3e, 0x90, 0xc4, 0x4f, 0x76, 0xed, 0xe5, + 0x4a, 0x32, 0x88, 0xec, 0xdc, 0x78, 0x46, 0x9f, 0xcb, 0x12, 0x25, 0xc0, 0x3b, 0x2c, 0x04, 0xf2, + 0xc2, 0xf4, 0x12, 0x28, 0x1a, 0x08, 0x22, 0xdf, 0x14, 0x64, 0x92, 0x30, 0x98, 0x9f, 0xb1, 0x49, + 0x40, 0x70, 0xda, 0xf8, 0xc9, 0x53, 0x4a, 0x78, 0x81, 0x96, 0x01, 0x48, 0x61, 0x6a, 0xce, 0x58, + 0x17, 0x88, 0x12, 0x0d, 0x35, 0x06, 0xac, 0xe4, 0xce, 0xe5, 0x64, 0xfb, 0x27, 0xee, 0x53, 0x34, + 0x1c, 0x22, 0xf0, 0xb4, 0x5b, 0x31, 0x87, 0x3d, 0x05, 0xde, 0x54, 0x5e, 0xfe, 0x33, 0xbc, 0xd2, + 0x9b, 0x21, 0x85, 0xd0, 0x35, 0xa8, 0x06, 0xad, 0x08, 0xc6, 0x97, 0x6f, 0x35, 0x05, 0xa1, 0x99, + 0x99, 0x93, 0x0c, 0xa8, 0xa0, 0x3e, 0xfa, 0x32, 0x1c, 0x48, 0x60, 0x61, 0xf7, 0xdc, 0xec, 0x9f, +}; + +const unsigned char issuer_rem[] = { + 0x1e, 0xbc, 0xa3, 0x0f, 0x00, 0xce, 0x59, 0x62, 0xa8, 0xc6, 0xe1, 0x30, 0x54, 0x4b, 0x82, 0x89, + 0x1b, 0x23, 0x6c, 0x65, 0xde, 0x29, 0x31, 0x7f, 0x36, 0x47, 0x35, 0xde, 0xe6, 0x3f, 0x65, 0x98, + 0x97, 0x58, 0x35, 0xd5 +}; + +const unsigned char issuer_exp[] = { + 0x03, +}; + +const unsigned char ssad_cr[] = { + 0x99, 0xa5, 0x58, 0xb6, 0x2b, 0x67, 0x4a, 0xa5, 0xe7, 0xd2, 0xa5, 0x7e, 0x5e, 0xf6, 0xa6, 0xf2, + 0x25, 0x8e, 0x5d, 0xa0, 0x52, 0xd0, 0x5b, 0x54, 0xe5, 0xc1, 0x15, 0xff, 0x1c, 0xec, 0xf9, 0x4a, + 0xa2, 0xdf, 0x8f, 0x39, 0xa0, 0x1d, 0x71, 0xc6, 0x19, 0xeb, 0x81, 0x9d, 0xa5, 0x2e, 0xf3, 0x81, + 0xe8, 0x49, 0x79, 0x58, 0x6a, 0xea, 0x78, 0x55, 0xff, 0xbe, 0xf4, 0x0a, 0xa3, 0xa7, 0x1c, 0xd3, + 0xb0, 0x4c, 0xfd, 0xf2, 0x70, 0xae, 0xc8, 0x15, 0x8a, 0x27, 0x97, 0xf2, 0x4f, 0xd6, 0x13, 0xb7, + 0x48, 0x13, 0x46, 0x61, 0x13, 0x5c, 0xd2, 0x90, 0xe4, 0x5b, 0x04, 0xa8, 0xe0, 0xcc, 0xc7, 0x11, + 0xae, 0x04, 0x2f, 0x15, 0x9e, 0x73, 0xc8, 0x9c, 0x2a, 0x7e, 0x65, 0xa4, 0xc2, 0xfd, 0x1d, 0x61, + 0x06, 0x02, 0x4a, 0xa2, 0x71, 0x30, 0xb0, 0xec, 0xec, 0x02, 0x38, 0xf9, 0x16, 0x59, 0xde, 0x96, +}; + +const unsigned char ssd1[] = { + 0x5f, 0x24, 0x03, 0x08, 0x12, 0x31, 0x5a, 0x08, 0x42, 0x76, 0x55, 0x00, 0x13, 0x23, 0x45, 0x99, 0x5f, 0x34, 0x01, 0x01, 0x9f, 0x07, 0x02, 0xff, 0x00, 0x9f, 0x0d, 0x05, 0xd0, 0x40, 0xac, 0xa8, 0x00, 0x9f, 0x0e, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x9f, 0x0f, 0x05, 0xd0, 0x68, 0xbc, 0xf8, 0x00, + 0x5c, 0x00, +}; +static const struct tlv ssd1_tlv = { + .len = sizeof(ssd1), + .value = ssd1, +}; + +const unsigned char pan[] = { + 0x42, 0x76, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static int sda_test_raw(bool verbose) +{ + const struct emv_pk *pk = &vsdc_01; + + struct crypto_pk *kcp = crypto_pk_open(PK_RSA, + pk->modulus, pk->mlen, + pk->exp, pk->elen); + if (!kcp) + return 1; + + unsigned char *ipk_data; + size_t ipk_data_len; + ipk_data = crypto_pk_encrypt(kcp, issuer_cert, sizeof(issuer_cert), &ipk_data_len); + crypto_pk_close(kcp); + + if (!ipk_data) + return 1; + + if (verbose) { + printf("issuer cert:\n"); + dump_buffer(ipk_data, ipk_data_len, stdout, 0); + } + + size_t ipk_pk_len = ipk_data[13]; + unsigned char *ipk_pk = malloc(ipk_pk_len); + memcpy(ipk_pk, ipk_data + 15, ipk_data_len - 36); + memcpy(ipk_pk + ipk_data_len - 36, issuer_rem, sizeof(issuer_rem)); + + struct crypto_hash *ch; + ch = crypto_hash_open(HASH_SHA_1); + if (!ch) { + free(ipk_pk); + free(ipk_data); + return 1; + } + + crypto_hash_write(ch, ipk_data + 1, 14); + crypto_hash_write(ch, ipk_pk, ipk_pk_len); + crypto_hash_write(ch, issuer_exp, sizeof(issuer_exp)); + + unsigned char *h = crypto_hash_read(ch); + if (!h) { + crypto_hash_close(ch); + free(ipk_pk); + free(ipk_data); + return 1; + } + + if (verbose) { + printf("crypto hash:\n"); + dump_buffer(h, 20, stdout, 0); + } + + if (memcmp(ipk_data + ipk_data_len - 21, h, 20)) { + crypto_hash_close(ch); + free(ipk_pk); + free(ipk_data); + return 1; + } + + crypto_hash_close(ch); + free(ipk_data); + + struct crypto_pk *ikcp = crypto_pk_open(PK_RSA, ipk_pk, (int) ipk_pk_len, + issuer_exp, (int) sizeof(issuer_exp)); + free(ipk_pk); + if (!ikcp) + return 1; + + size_t ssad_len; + unsigned char *ssad = crypto_pk_encrypt(ikcp, ssad_cr, sizeof(ssad_cr), &ssad_len); + crypto_pk_close(ikcp); + if (!ssad) + return 1; + + if (verbose) { + printf("ssad:\n"); + dump_buffer(ssad, ssad_len, stdout, 0); + } + + ch = crypto_hash_open(HASH_SHA_1); + if (!ch) { + free(ssad); + return 1; + } + + crypto_hash_write(ch, ssad + 1, ssad_len - 22); + crypto_hash_write(ch, ssd1, sizeof(ssd1)); + + unsigned char *h2 = crypto_hash_read(ch); + if (!h2) { + crypto_hash_close(ch); + free(ssad); + return 1; + } + + if (verbose) { + printf("crypto hash2:\n"); + dump_buffer(h2, 20, stdout, 0); + } + + crypto_hash_close(ch); + + free(ssad); + + return 0; +} + +static int sda_test_pk(bool verbose) +{ + const struct emv_pk *pk = &vsdc_01; + struct tlvdb *db; + + db = tlvdb_external(0x90, sizeof(issuer_cert), issuer_cert); + tlvdb_add(db, tlvdb_external(0x9f32, sizeof(issuer_exp), issuer_exp)); + tlvdb_add(db, tlvdb_external(0x92, sizeof(issuer_rem), issuer_rem)); + tlvdb_add(db, tlvdb_external(0x5a, sizeof(pan), pan)); + + struct emv_pk *ipk = emv_pki_recover_issuer_cert(pk, db); + if (!ipk) { + fprintf(stderr, "Could not recover Issuer certificate!\n"); + tlvdb_free(db); + return 2; + } + + tlvdb_add(db, tlvdb_external(0x93, sizeof(ssad_cr), ssad_cr)); + + struct tlvdb *dacdb = emv_pki_recover_dac(ipk, db, &ssd1_tlv); + if (!dacdb) { + fprintf(stderr, "Could not recover DAC!\n"); + emv_pk_free(ipk); + tlvdb_free(db); + return 2; + } + + const struct tlv *dac = tlvdb_get(dacdb, 0x9f45, NULL); + if (!dac) { + fprintf(stderr, "DAC not found!\n"); + tlvdb_free(dacdb); + emv_pk_free(ipk); + tlvdb_free(db); + return 2; + } + + if (verbose) { + printf("dac:\n"); + dump_buffer(dac->value, dac->len, stdout, 0); + } + + tlvdb_free(dacdb); + emv_pk_free(ipk); + tlvdb_free(db); + + return 0; +} + +int exec_sda_test(bool verbose) +{ + int ret; + fprintf(stdout, "\n"); + + ret = sda_test_raw(verbose); + if (ret) { + fprintf(stderr, "SDA raw test: failed\n"); + return ret; + } + fprintf(stdout, "SDA raw test: passed\n"); + + ret = sda_test_pk(verbose); + if (ret) { + fprintf(stderr, "SDA test pk: failed\n"); + return ret; + } + fprintf(stdout, "SDA test pk: passed\n"); + + return 0; +} diff --git a/client/emv/test/sda_test.h b/client/emv/test/sda_test.h new file mode 100644 index 00000000..43a4c948 --- /dev/null +++ b/client/emv/test/sda_test.h @@ -0,0 +1,16 @@ +/* + * emv-tools - a set of tools to work with EMV family of smart cards + * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +extern int exec_sda_test(bool verbose); diff --git a/client/obj/emv/test/.dummy b/client/obj/emv/test/.dummy new file mode 100644 index 00000000..e69de29b diff --git a/client/scripting.c b/client/scripting.c index 13cb8cdf..3859fd48 100644 --- a/client/scripting.c +++ b/client/scripting.c @@ -23,8 +23,8 @@ #include "iso14443crc.h" #include "../common/crc16.h" #include "../common/crc64.h" -#include "../common/sha1.h" -#include "polarssl/aes.h" +#include "../common/polarssl/sha1.h" +#include "../common/polarssl/aes.h" #include "cmdcrc.h" /** * The following params expected: diff --git a/common/polarssl/aes.h b/common/polarssl/aes.h index 946bd87d..299cb4cd 100644 --- a/common/polarssl/aes.h +++ b/common/polarssl/aes.h @@ -125,7 +125,6 @@ int aes_crypt_ecb( aes_context *ctx, int mode, const unsigned char input[16], unsigned char output[16] ); - #if defined(POLARSSL_CIPHER_MODE_CBC) /** * \brief AES-CBC buffer encryption/decryption diff --git a/common/polarssl/bignum.c b/common/polarssl/bignum.c new file mode 100644 index 00000000..d22dd5c7 --- /dev/null +++ b/common/polarssl/bignum.c @@ -0,0 +1,2143 @@ +/* + * Multi-precision integer library + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * This MPI implementation is based on: + * + * http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf + * http://www.stillhq.com/extracted/gnupg-api/mpi/ + * http://math.libtomcrypt.com/files/tommath.pdf + */ + +#include "polarssl_config.h" + +#if defined(POLARSSL_BIGNUM_C) + +#include "bignum.h" +#include "bn_mul.h" + +#include + +#define ciL (sizeof(t_uint)) /* chars in limb */ +#define biL (ciL << 3) /* bits in limb */ +#define biH (ciL << 2) /* half limb size */ + +/* + * Convert between bits/chars and number of limbs + */ +#define BITS_TO_LIMBS(i) (((i) + biL - 1) / biL) +#define CHARS_TO_LIMBS(i) (((i) + ciL - 1) / ciL) + +/* + * Initialize one MPI + */ +void mpi_init( mpi *X ) +{ + if( X == NULL ) + return; + + X->s = 1; + X->n = 0; + X->p = NULL; +} + +/* + * Unallocate one MPI + */ +void mpi_free( mpi *X ) +{ + if( X == NULL ) + return; + + if( X->p != NULL ) + { + memset( X->p, 0, X->n * ciL ); + free( X->p ); + } + + X->s = 1; + X->n = 0; + X->p = NULL; +} + +/* + * Enlarge to the specified number of limbs + */ +int mpi_grow( mpi *X, size_t nblimbs ) +{ + t_uint *p; + + if( nblimbs > POLARSSL_MPI_MAX_LIMBS ) + return( POLARSSL_ERR_MPI_MALLOC_FAILED ); + + if( X->n < nblimbs ) + { + if( ( p = (t_uint *) malloc( nblimbs * ciL ) ) == NULL ) + return( POLARSSL_ERR_MPI_MALLOC_FAILED ); + + memset( p, 0, nblimbs * ciL ); + + if( X->p != NULL ) + { + memcpy( p, X->p, X->n * ciL ); + memset( X->p, 0, X->n * ciL ); + free( X->p ); + } + + X->n = nblimbs; + X->p = p; + } + + return( 0 ); +} + +/* + * Copy the contents of Y into X + */ +int mpi_copy( mpi *X, const mpi *Y ) +{ + int ret; + size_t i; + + if( X == Y ) + return( 0 ); + + for( i = Y->n - 1; i > 0; i-- ) + if( Y->p[i] != 0 ) + break; + i++; + + X->s = Y->s; + + MPI_CHK( mpi_grow( X, i ) ); + + memset( X->p, 0, X->n * ciL ); + memcpy( X->p, Y->p, i * ciL ); + +cleanup: + + return( ret ); +} + +/* + * Swap the contents of X and Y + */ +void mpi_swap( mpi *X, mpi *Y ) +{ + mpi T; + + memcpy( &T, X, sizeof( mpi ) ); + memcpy( X, Y, sizeof( mpi ) ); + memcpy( Y, &T, sizeof( mpi ) ); +} + +/* + * Set value from integer + */ +int mpi_lset( mpi *X, t_sint z ) +{ + int ret; + + MPI_CHK( mpi_grow( X, 1 ) ); + memset( X->p, 0, X->n * ciL ); + + X->p[0] = ( z < 0 ) ? -z : z; + X->s = ( z < 0 ) ? -1 : 1; + +cleanup: + + return( ret ); +} + +/* + * Get a specific bit + */ +int mpi_get_bit( const mpi *X, size_t pos ) +{ + if( X->n * biL <= pos ) + return( 0 ); + + return ( X->p[pos / biL] >> ( pos % biL ) ) & 0x01; +} + +/* + * Set a bit to a specific value of 0 or 1 + */ +int mpi_set_bit( mpi *X, size_t pos, unsigned char val ) +{ + int ret = 0; + size_t off = pos / biL; + size_t idx = pos % biL; + + if( val != 0 && val != 1 ) + return POLARSSL_ERR_MPI_BAD_INPUT_DATA; + + if( X->n * biL <= pos ) + { + if( val == 0 ) + return ( 0 ); + + MPI_CHK( mpi_grow( X, off + 1 ) ); + } + + X->p[off] = ( X->p[off] & ~( 0x01 << idx ) ) | ( val << idx ); + +cleanup: + + return( ret ); +} + +/* + * Return the number of least significant bits + */ +size_t mpi_lsb( const mpi *X ) +{ + size_t i, j, count = 0; + + for( i = 0; i < X->n; i++ ) + for( j = 0; j < biL; j++, count++ ) + if( ( ( X->p[i] >> j ) & 1 ) != 0 ) + return( count ); + + return( 0 ); +} + +/* + * Return the number of most significant bits + */ +size_t mpi_msb( const mpi *X ) +{ + size_t i, j; + + for( i = X->n - 1; i > 0; i-- ) + if( X->p[i] != 0 ) + break; + + for( j = biL; j > 0; j-- ) + if( ( ( X->p[i] >> ( j - 1 ) ) & 1 ) != 0 ) + break; + + return( ( i * biL ) + j ); +} + +/* + * Return the total size in bytes + */ +size_t mpi_size( const mpi *X ) +{ + return( ( mpi_msb( X ) + 7 ) >> 3 ); +} + +/* + * Convert an ASCII character to digit value + */ +static int mpi_get_digit( t_uint *d, int radix, char c ) +{ + *d = 255; + + if( c >= 0x30 && c <= 0x39 ) *d = c - 0x30; + if( c >= 0x41 && c <= 0x46 ) *d = c - 0x37; + if( c >= 0x61 && c <= 0x66 ) *d = c - 0x57; + + if( *d >= (t_uint) radix ) + return( POLARSSL_ERR_MPI_INVALID_CHARACTER ); + + return( 0 ); +} + +/* + * Import from an ASCII string + */ +int mpi_read_string( mpi *X, int radix, const char *s ) +{ + int ret; + size_t i, j, slen, n; + t_uint d; + mpi T; + + if( radix < 2 || radix > 16 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + mpi_init( &T ); + + slen = strlen( s ); + + if( radix == 16 ) + { + n = BITS_TO_LIMBS( slen << 2 ); + + MPI_CHK( mpi_grow( X, n ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + for( i = slen, j = 0; i > 0; i--, j++ ) + { + if( i == 1 && s[i - 1] == '-' ) + { + X->s = -1; + break; + } + + MPI_CHK( mpi_get_digit( &d, radix, s[i - 1] ) ); + X->p[j / (2 * ciL)] |= d << ( (j % (2 * ciL)) << 2 ); + } + } + else + { + MPI_CHK( mpi_lset( X, 0 ) ); + + for( i = 0; i < slen; i++ ) + { + if( i == 0 && s[i] == '-' ) + { + X->s = -1; + continue; + } + + MPI_CHK( mpi_get_digit( &d, radix, s[i] ) ); + MPI_CHK( mpi_mul_int( &T, X, radix ) ); + + if( X->s == 1 ) + { + MPI_CHK( mpi_add_int( X, &T, d ) ); + } + else + { + MPI_CHK( mpi_sub_int( X, &T, d ) ); + } + } + } + +cleanup: + + mpi_free( &T ); + + return( ret ); +} + +/* + * Helper to write the digits high-order first + */ +static int mpi_write_hlp( mpi *X, int radix, char **p ) +{ + int ret; + t_uint r; + + if( radix < 2 || radix > 16 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + MPI_CHK( mpi_mod_int( &r, X, radix ) ); + MPI_CHK( mpi_div_int( X, NULL, X, radix ) ); + + if( mpi_cmp_int( X, 0 ) != 0 ) + MPI_CHK( mpi_write_hlp( X, radix, p ) ); + + if( r < 10 ) + *(*p)++ = (char)( r + 0x30 ); + else + *(*p)++ = (char)( r + 0x37 ); + +cleanup: + + return( ret ); +} + +/* + * Export into an ASCII string + */ +int mpi_write_string( const mpi *X, int radix, char *s, size_t *slen ) +{ + int ret = 0; + size_t n; + char *p; + mpi T; + + if( radix < 2 || radix > 16 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + n = mpi_msb( X ); + if( radix >= 4 ) n >>= 1; + if( radix >= 16 ) n >>= 1; + n += 3; + + if( *slen < n ) + { + *slen = n; + return( POLARSSL_ERR_MPI_BUFFER_TOO_SMALL ); + } + + p = s; + mpi_init( &T ); + + if( X->s == -1 ) + *p++ = '-'; + + if( radix == 16 ) + { + int c; + size_t i, j, k; + + for( i = X->n, k = 0; i > 0; i-- ) + { + for( j = ciL; j > 0; j-- ) + { + c = ( X->p[i - 1] >> ( ( j - 1 ) << 3) ) & 0xFF; + + if( c == 0 && k == 0 && ( i + j + 3 ) != 0 ) + continue; + + *(p++) = "0123456789ABCDEF" [c / 16]; + *(p++) = "0123456789ABCDEF" [c % 16]; + k = 1; + } + } + } + else + { + MPI_CHK( mpi_copy( &T, X ) ); + + if( T.s == -1 ) + T.s = 1; + + MPI_CHK( mpi_write_hlp( &T, radix, &p ) ); + } + + *p++ = '\0'; + *slen = p - s; + +cleanup: + + mpi_free( &T ); + + return( ret ); +} + +#if defined(POLARSSL_FS_IO) +/* + * Read X from an opened file + */ +int mpi_read_file( mpi *X, int radix, FILE *fin ) +{ + t_uint d; + size_t slen; + char *p; + /* + * Buffer should have space for (short) label and decimal formatted MPI, + * newline characters and '\0' + */ + char s[ POLARSSL_MPI_RW_BUFFER_SIZE ]; + + memset( s, 0, sizeof( s ) ); + if( fgets( s, sizeof( s ) - 1, fin ) == NULL ) + return( POLARSSL_ERR_MPI_FILE_IO_ERROR ); + + slen = strlen( s ); + if( slen == sizeof( s ) - 2 ) + return( POLARSSL_ERR_MPI_BUFFER_TOO_SMALL ); + + if( s[slen - 1] == '\n' ) { slen--; s[slen] = '\0'; } + if( s[slen - 1] == '\r' ) { slen--; s[slen] = '\0'; } + + p = s + slen; + while( --p >= s ) + if( mpi_get_digit( &d, radix, *p ) != 0 ) + break; + + return( mpi_read_string( X, radix, p + 1 ) ); +} + +/* + * Write X into an opened file (or stdout if fout == NULL) + */ +int mpi_write_file( const char *p, const mpi *X, int radix, FILE *fout ) +{ + int ret; + size_t n, slen, plen; + /* + * Buffer should have space for (short) label and decimal formatted MPI, + * newline characters and '\0' + */ + char s[ POLARSSL_MPI_RW_BUFFER_SIZE ]; + + n = sizeof( s ); + memset( s, 0, n ); + n -= 2; + + MPI_CHK( mpi_write_string( X, radix, s, (size_t *) &n ) ); + + if( p == NULL ) p = ""; + + plen = strlen( p ); + slen = strlen( s ); + s[slen++] = '\r'; + s[slen++] = '\n'; + + if( fout != NULL ) + { + if( fwrite( p, 1, plen, fout ) != plen || + fwrite( s, 1, slen, fout ) != slen ) + return( POLARSSL_ERR_MPI_FILE_IO_ERROR ); + } + else + printf( "%s%s", p, s ); + +cleanup: + + return( ret ); +} +#endif /* POLARSSL_FS_IO */ + +/* + * Import X from unsigned binary data, big endian + */ +int mpi_read_binary( mpi *X, const unsigned char *buf, size_t buflen ) +{ + int ret; + size_t i, j, n; + + for( n = 0; n < buflen; n++ ) + if( buf[n] != 0 ) + break; + + MPI_CHK( mpi_grow( X, CHARS_TO_LIMBS( buflen - n ) ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + for( i = buflen, j = 0; i > n; i--, j++ ) + X->p[j / ciL] |= ((t_uint) buf[i - 1]) << ((j % ciL) << 3); + +cleanup: + + return( ret ); +} + +/* + * Export X into unsigned binary data, big endian + */ +int mpi_write_binary( const mpi *X, unsigned char *buf, size_t buflen ) +{ + size_t i, j, n; + + n = mpi_size( X ); + + if( buflen < n ) + return( POLARSSL_ERR_MPI_BUFFER_TOO_SMALL ); + + memset( buf, 0, buflen ); + + for( i = buflen - 1, j = 0; n > 0; i--, j++, n-- ) + buf[i] = (unsigned char)( X->p[j / ciL] >> ((j % ciL) << 3) ); + + return( 0 ); +} + +/* + * Left-shift: X <<= count + */ +int mpi_shift_l( mpi *X, size_t count ) +{ + int ret; + size_t i, v0, t1; + t_uint r0 = 0, r1; + + v0 = count / (biL ); + t1 = count & (biL - 1); + + i = mpi_msb( X ) + count; + + if( X->n * biL < i ) + MPI_CHK( mpi_grow( X, BITS_TO_LIMBS( i ) ) ); + + ret = 0; + + /* + * shift by count / limb_size + */ + if( v0 > 0 ) + { + for( i = X->n; i > v0; i-- ) + X->p[i - 1] = X->p[i - v0 - 1]; + + for( ; i > 0; i-- ) + X->p[i - 1] = 0; + } + + /* + * shift by count % limb_size + */ + if( t1 > 0 ) + { + for( i = v0; i < X->n; i++ ) + { + r1 = X->p[i] >> (biL - t1); + X->p[i] <<= t1; + X->p[i] |= r0; + r0 = r1; + } + } + +cleanup: + + return( ret ); +} + +/* + * Right-shift: X >>= count + */ +int mpi_shift_r( mpi *X, size_t count ) +{ + size_t i, v0, v1; + t_uint r0 = 0, r1; + + v0 = count / biL; + v1 = count & (biL - 1); + + if( v0 > X->n || ( v0 == X->n && v1 > 0 ) ) + return mpi_lset( X, 0 ); + + /* + * shift by count / limb_size + */ + if( v0 > 0 ) + { + for( i = 0; i < X->n - v0; i++ ) + X->p[i] = X->p[i + v0]; + + for( ; i < X->n; i++ ) + X->p[i] = 0; + } + + /* + * shift by count % limb_size + */ + if( v1 > 0 ) + { + for( i = X->n; i > 0; i-- ) + { + r1 = X->p[i - 1] << (biL - v1); + X->p[i - 1] >>= v1; + X->p[i - 1] |= r0; + r0 = r1; + } + } + + return( 0 ); +} + +/* + * Compare unsigned values + */ +int mpi_cmp_abs( const mpi *X, const mpi *Y ) +{ + size_t i, j; + + for( i = X->n; i > 0; i-- ) + if( X->p[i - 1] != 0 ) + break; + + for( j = Y->n; j > 0; j-- ) + if( Y->p[j - 1] != 0 ) + break; + + if( i == 0 && j == 0 ) + return( 0 ); + + if( i > j ) return( 1 ); + if( j > i ) return( -1 ); + + for( ; i > 0; i-- ) + { + if( X->p[i - 1] > Y->p[i - 1] ) return( 1 ); + if( X->p[i - 1] < Y->p[i - 1] ) return( -1 ); + } + + return( 0 ); +} + +/* + * Compare signed values + */ +int mpi_cmp_mpi( const mpi *X, const mpi *Y ) +{ + size_t i, j; + + for( i = X->n; i > 0; i-- ) + if( X->p[i - 1] != 0 ) + break; + + for( j = Y->n; j > 0; j-- ) + if( Y->p[j - 1] != 0 ) + break; + + if( i == 0 && j == 0 ) + return( 0 ); + + if( i > j ) return( X->s ); + if( j > i ) return( -Y->s ); + + if( X->s > 0 && Y->s < 0 ) return( 1 ); + if( Y->s > 0 && X->s < 0 ) return( -1 ); + + for( ; i > 0; i-- ) + { + if( X->p[i - 1] > Y->p[i - 1] ) return( X->s ); + if( X->p[i - 1] < Y->p[i - 1] ) return( -X->s ); + } + + return( 0 ); +} + +/* + * Compare signed values + */ +int mpi_cmp_int( const mpi *X, t_sint z ) +{ + mpi Y; + t_uint p[1]; + + *p = ( z < 0 ) ? -z : z; + Y.s = ( z < 0 ) ? -1 : 1; + Y.n = 1; + Y.p = p; + + return( mpi_cmp_mpi( X, &Y ) ); +} + +/* + * Unsigned addition: X = |A| + |B| (HAC 14.7) + */ +int mpi_add_abs( mpi *X, const mpi *A, const mpi *B ) +{ + int ret; + size_t i, j; + t_uint *o, *p, c; + + if( X == B ) + { + const mpi *T = A; A = X; B = T; + } + + if( X != A ) + MPI_CHK( mpi_copy( X, A ) ); + + /* + * X should always be positive as a result of unsigned additions. + */ + X->s = 1; + + for( j = B->n; j > 0; j-- ) + if( B->p[j - 1] != 0 ) + break; + + MPI_CHK( mpi_grow( X, j ) ); + + o = B->p; p = X->p; c = 0; + + for( i = 0; i < j; i++, o++, p++ ) + { + *p += c; c = ( *p < c ); + *p += *o; c += ( *p < *o ); + } + + while( c != 0 ) + { + if( i >= X->n ) + { + MPI_CHK( mpi_grow( X, i + 1 ) ); + p = X->p + i; + } + + *p += c; c = ( *p < c ); i++; p++; + } + +cleanup: + + return( ret ); +} + +/* + * Helper for mpi substraction + */ +static void mpi_sub_hlp( size_t n, t_uint *s, t_uint *d ) +{ + size_t i; + t_uint c, z; + + for( i = c = 0; i < n; i++, s++, d++ ) + { + z = ( *d < c ); *d -= c; + c = ( *d < *s ) + z; *d -= *s; + } + + while( c != 0 ) + { + z = ( *d < c ); *d -= c; + c = z; i++; d++; + } +} + +/* + * Unsigned substraction: X = |A| - |B| (HAC 14.9) + */ +int mpi_sub_abs( mpi *X, const mpi *A, const mpi *B ) +{ + mpi TB; + int ret; + size_t n; + + if( mpi_cmp_abs( A, B ) < 0 ) + return( POLARSSL_ERR_MPI_NEGATIVE_VALUE ); + + mpi_init( &TB ); + + if( X == B ) + { + MPI_CHK( mpi_copy( &TB, B ) ); + B = &TB; + } + + if( X != A ) + MPI_CHK( mpi_copy( X, A ) ); + + /* + * X should always be positive as a result of unsigned substractions. + */ + X->s = 1; + + ret = 0; + + for( n = B->n; n > 0; n-- ) + if( B->p[n - 1] != 0 ) + break; + + mpi_sub_hlp( n, B->p, X->p ); + +cleanup: + + mpi_free( &TB ); + + return( ret ); +} + +/* + * Signed addition: X = A + B + */ +int mpi_add_mpi( mpi *X, const mpi *A, const mpi *B ) +{ + int ret, s = A->s; + + if( A->s * B->s < 0 ) + { + if( mpi_cmp_abs( A, B ) >= 0 ) + { + MPI_CHK( mpi_sub_abs( X, A, B ) ); + X->s = s; + } + else + { + MPI_CHK( mpi_sub_abs( X, B, A ) ); + X->s = -s; + } + } + else + { + MPI_CHK( mpi_add_abs( X, A, B ) ); + X->s = s; + } + +cleanup: + + return( ret ); +} + +/* + * Signed substraction: X = A - B + */ +int mpi_sub_mpi( mpi *X, const mpi *A, const mpi *B ) +{ + int ret, s = A->s; + + if( A->s * B->s > 0 ) + { + if( mpi_cmp_abs( A, B ) >= 0 ) + { + MPI_CHK( mpi_sub_abs( X, A, B ) ); + X->s = s; + } + else + { + MPI_CHK( mpi_sub_abs( X, B, A ) ); + X->s = -s; + } + } + else + { + MPI_CHK( mpi_add_abs( X, A, B ) ); + X->s = s; + } + +cleanup: + + return( ret ); +} + +/* + * Signed addition: X = A + b + */ +int mpi_add_int( mpi *X, const mpi *A, t_sint b ) +{ + mpi _B; + t_uint p[1]; + + p[0] = ( b < 0 ) ? -b : b; + _B.s = ( b < 0 ) ? -1 : 1; + _B.n = 1; + _B.p = p; + + return( mpi_add_mpi( X, A, &_B ) ); +} + +/* + * Signed substraction: X = A - b + */ +int mpi_sub_int( mpi *X, const mpi *A, t_sint b ) +{ + mpi _B; + t_uint p[1]; + + p[0] = ( b < 0 ) ? -b : b; + _B.s = ( b < 0 ) ? -1 : 1; + _B.n = 1; + _B.p = p; + + return( mpi_sub_mpi( X, A, &_B ) ); +} + +/* + * Helper for mpi multiplication + */ +static +#if defined(__APPLE__) && defined(__arm__) +/* + * Apple LLVM version 4.2 (clang-425.0.24) (based on LLVM 3.2svn) + * appears to need this to prevent bad ARM code generation at -O3. + */ +__attribute__ ((noinline)) +#endif +void mpi_mul_hlp( size_t i, t_uint *s, t_uint *d, t_uint b ) +{ + t_uint c = 0, t = 0; + +#if defined(MULADDC_HUIT) + for( ; i >= 8; i -= 8 ) + { + MULADDC_INIT + MULADDC_HUIT + MULADDC_STOP + } + + for( ; i > 0; i-- ) + { + MULADDC_INIT + MULADDC_CORE + MULADDC_STOP + } +#else + for( ; i >= 16; i -= 16 ) + { + MULADDC_INIT + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_STOP + } + + for( ; i >= 8; i -= 8 ) + { + MULADDC_INIT + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_STOP + } + + for( ; i > 0; i-- ) + { + MULADDC_INIT + MULADDC_CORE + MULADDC_STOP + } +#endif + + t++; + + do { + *d += c; c = ( *d < c ); d++; + } + while( c != 0 ); +} + +/* + * Baseline multiplication: X = A * B (HAC 14.12) + */ +int mpi_mul_mpi( mpi *X, const mpi *A, const mpi *B ) +{ + int ret; + size_t i, j; + mpi TA, TB; + + mpi_init( &TA ); mpi_init( &TB ); + + if( X == A ) { MPI_CHK( mpi_copy( &TA, A ) ); A = &TA; } + if( X == B ) { MPI_CHK( mpi_copy( &TB, B ) ); B = &TB; } + + for( i = A->n; i > 0; i-- ) + if( A->p[i - 1] != 0 ) + break; + + for( j = B->n; j > 0; j-- ) + if( B->p[j - 1] != 0 ) + break; + + MPI_CHK( mpi_grow( X, i + j ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + for( i++; j > 0; j-- ) + mpi_mul_hlp( i - 1, A->p, X->p + j - 1, B->p[j - 1] ); + + X->s = A->s * B->s; + +cleanup: + + mpi_free( &TB ); mpi_free( &TA ); + + return( ret ); +} + +/* + * Baseline multiplication: X = A * b + */ +int mpi_mul_int( mpi *X, const mpi *A, t_sint b ) +{ + mpi _B; + t_uint p[1]; + + _B.s = 1; + _B.n = 1; + _B.p = p; + p[0] = b; + + return( mpi_mul_mpi( X, A, &_B ) ); +} + +/* + * Division by mpi: A = Q * B + R (HAC 14.20) + */ +int mpi_div_mpi( mpi *Q, mpi *R, const mpi *A, const mpi *B ) +{ + int ret; + size_t i, n, t, k; + mpi X, Y, Z, T1, T2; + + if( mpi_cmp_int( B, 0 ) == 0 ) + return( POLARSSL_ERR_MPI_DIVISION_BY_ZERO ); + + mpi_init( &X ); mpi_init( &Y ); mpi_init( &Z ); + mpi_init( &T1 ); mpi_init( &T2 ); + + if( mpi_cmp_abs( A, B ) < 0 ) + { + if( Q != NULL ) MPI_CHK( mpi_lset( Q, 0 ) ); + if( R != NULL ) MPI_CHK( mpi_copy( R, A ) ); + return( 0 ); + } + + MPI_CHK( mpi_copy( &X, A ) ); + MPI_CHK( mpi_copy( &Y, B ) ); + X.s = Y.s = 1; + + MPI_CHK( mpi_grow( &Z, A->n + 2 ) ); + MPI_CHK( mpi_lset( &Z, 0 ) ); + MPI_CHK( mpi_grow( &T1, 2 ) ); + MPI_CHK( mpi_grow( &T2, 3 ) ); + + k = mpi_msb( &Y ) % biL; + if( k < biL - 1 ) + { + k = biL - 1 - k; + MPI_CHK( mpi_shift_l( &X, k ) ); + MPI_CHK( mpi_shift_l( &Y, k ) ); + } + else k = 0; + + n = X.n - 1; + t = Y.n - 1; + MPI_CHK( mpi_shift_l( &Y, biL * (n - t) ) ); + + while( mpi_cmp_mpi( &X, &Y ) >= 0 ) + { + Z.p[n - t]++; + mpi_sub_mpi( &X, &X, &Y ); + } + mpi_shift_r( &Y, biL * (n - t) ); + + for( i = n; i > t ; i-- ) + { + if( X.p[i] >= Y.p[t] ) + Z.p[i - t - 1] = ~0; + else + { +#if defined(POLARSSL_HAVE_UDBL) + t_udbl r; + + r = (t_udbl) X.p[i] << biL; + r |= (t_udbl) X.p[i - 1]; + r /= Y.p[t]; + if( r > ((t_udbl) 1 << biL) - 1) + r = ((t_udbl) 1 << biL) - 1; + + Z.p[i - t - 1] = (t_uint) r; +#else + /* + * __udiv_qrnnd_c, from gmp/longlong.h + */ + t_uint q0, q1, r0, r1; + t_uint d0, d1, d, m; + + d = Y.p[t]; + d0 = ( d << biH ) >> biH; + d1 = ( d >> biH ); + + q1 = X.p[i] / d1; + r1 = X.p[i] - d1 * q1; + r1 <<= biH; + r1 |= ( X.p[i - 1] >> biH ); + + m = q1 * d0; + if( r1 < m ) + { + q1--, r1 += d; + while( r1 >= d && r1 < m ) + q1--, r1 += d; + } + r1 -= m; + + q0 = r1 / d1; + r0 = r1 - d1 * q0; + r0 <<= biH; + r0 |= ( X.p[i - 1] << biH ) >> biH; + + m = q0 * d0; + if( r0 < m ) + { + q0--, r0 += d; + while( r0 >= d && r0 < m ) + q0--, r0 += d; + } + r0 -= m; + + Z.p[i - t - 1] = ( q1 << biH ) | q0; +#endif + } + + Z.p[i - t - 1]++; + do + { + Z.p[i - t - 1]--; + + MPI_CHK( mpi_lset( &T1, 0 ) ); + T1.p[0] = (t < 1) ? 0 : Y.p[t - 1]; + T1.p[1] = Y.p[t]; + MPI_CHK( mpi_mul_int( &T1, &T1, Z.p[i - t - 1] ) ); + + MPI_CHK( mpi_lset( &T2, 0 ) ); + T2.p[0] = (i < 2) ? 0 : X.p[i - 2]; + T2.p[1] = (i < 1) ? 0 : X.p[i - 1]; + T2.p[2] = X.p[i]; + } + while( mpi_cmp_mpi( &T1, &T2 ) > 0 ); + + MPI_CHK( mpi_mul_int( &T1, &Y, Z.p[i - t - 1] ) ); + MPI_CHK( mpi_shift_l( &T1, biL * (i - t - 1) ) ); + MPI_CHK( mpi_sub_mpi( &X, &X, &T1 ) ); + + if( mpi_cmp_int( &X, 0 ) < 0 ) + { + MPI_CHK( mpi_copy( &T1, &Y ) ); + MPI_CHK( mpi_shift_l( &T1, biL * (i - t - 1) ) ); + MPI_CHK( mpi_add_mpi( &X, &X, &T1 ) ); + Z.p[i - t - 1]--; + } + } + + if( Q != NULL ) + { + mpi_copy( Q, &Z ); + Q->s = A->s * B->s; + } + + if( R != NULL ) + { + mpi_shift_r( &X, k ); + X.s = A->s; + mpi_copy( R, &X ); + + if( mpi_cmp_int( R, 0 ) == 0 ) + R->s = 1; + } + +cleanup: + + mpi_free( &X ); mpi_free( &Y ); mpi_free( &Z ); + mpi_free( &T1 ); mpi_free( &T2 ); + + return( ret ); +} + +/* + * Division by int: A = Q * b + R + */ +int mpi_div_int( mpi *Q, mpi *R, const mpi *A, t_sint b ) +{ + mpi _B; + t_uint p[1]; + + p[0] = ( b < 0 ) ? -b : b; + _B.s = ( b < 0 ) ? -1 : 1; + _B.n = 1; + _B.p = p; + + return( mpi_div_mpi( Q, R, A, &_B ) ); +} + +/* + * Modulo: R = A mod B + */ +int mpi_mod_mpi( mpi *R, const mpi *A, const mpi *B ) +{ + int ret; + + if( mpi_cmp_int( B, 0 ) < 0 ) + return POLARSSL_ERR_MPI_NEGATIVE_VALUE; + + MPI_CHK( mpi_div_mpi( NULL, R, A, B ) ); + + while( mpi_cmp_int( R, 0 ) < 0 ) + MPI_CHK( mpi_add_mpi( R, R, B ) ); + + while( mpi_cmp_mpi( R, B ) >= 0 ) + MPI_CHK( mpi_sub_mpi( R, R, B ) ); + +cleanup: + + return( ret ); +} + +/* + * Modulo: r = A mod b + */ +int mpi_mod_int( t_uint *r, const mpi *A, t_sint b ) +{ + size_t i; + t_uint x, y, z; + + if( b == 0 ) + return( POLARSSL_ERR_MPI_DIVISION_BY_ZERO ); + + if( b < 0 ) + return POLARSSL_ERR_MPI_NEGATIVE_VALUE; + + /* + * handle trivial cases + */ + if( b == 1 ) + { + *r = 0; + return( 0 ); + } + + if( b == 2 ) + { + *r = A->p[0] & 1; + return( 0 ); + } + + /* + * general case + */ + for( i = A->n, y = 0; i > 0; i-- ) + { + x = A->p[i - 1]; + y = ( y << biH ) | ( x >> biH ); + z = y / b; + y -= z * b; + + x <<= biH; + y = ( y << biH ) | ( x >> biH ); + z = y / b; + y -= z * b; + } + + /* + * If A is negative, then the current y represents a negative value. + * Flipping it to the positive side. + */ + if( A->s < 0 && y != 0 ) + y = b - y; + + *r = y; + + return( 0 ); +} + +/* + * Fast Montgomery initialization (thanks to Tom St Denis) + */ +static void mpi_montg_init( t_uint *mm, const mpi *N ) +{ + t_uint x, m0 = N->p[0]; + + x = m0; + x += ( ( m0 + 2 ) & 4 ) << 1; + x *= ( 2 - ( m0 * x ) ); + + if( biL >= 16 ) x *= ( 2 - ( m0 * x ) ); + if( biL >= 32 ) x *= ( 2 - ( m0 * x ) ); + if( biL >= 64 ) x *= ( 2 - ( m0 * x ) ); + + *mm = ~x + 1; +} + +/* + * Montgomery multiplication: A = A * B * R^-1 mod N (HAC 14.36) + */ +static void mpi_montmul( mpi *A, const mpi *B, const mpi *N, t_uint mm, const mpi *T ) +{ + size_t i, n, m; + t_uint u0, u1, *d; + + memset( T->p, 0, T->n * ciL ); + + d = T->p; + n = N->n; + m = ( B->n < n ) ? B->n : n; + + for( i = 0; i < n; i++ ) + { + /* + * T = (T + u0*B + u1*N) / 2^biL + */ + u0 = A->p[i]; + u1 = ( d[0] + u0 * B->p[0] ) * mm; + + mpi_mul_hlp( m, B->p, d, u0 ); + mpi_mul_hlp( n, N->p, d, u1 ); + + *d++ = u0; d[n + 1] = 0; + } + + memcpy( A->p, d, (n + 1) * ciL ); + + if( mpi_cmp_abs( A, N ) >= 0 ) + mpi_sub_hlp( n, N->p, A->p ); + else + /* prevent timing attacks */ + mpi_sub_hlp( n, A->p, T->p ); +} + +/* + * Montgomery reduction: A = A * R^-1 mod N + */ +static void mpi_montred( mpi *A, const mpi *N, t_uint mm, const mpi *T ) +{ + t_uint z = 1; + mpi U; + + U.n = U.s = (int) z; + U.p = &z; + + mpi_montmul( A, &U, N, mm, T ); +} + +/* + * Sliding-window exponentiation: X = A^E mod N (HAC 14.85) + */ +int mpi_exp_mod( mpi *X, const mpi *A, const mpi *E, const mpi *N, mpi *_RR ) +{ + int ret; + size_t wbits, wsize, one = 1; + size_t i, j, nblimbs; + size_t bufsize, nbits; + t_uint ei, mm, state; + mpi RR, T, W[ 2 << POLARSSL_MPI_WINDOW_SIZE ], Apos; + int neg; + + if( mpi_cmp_int( N, 0 ) < 0 || ( N->p[0] & 1 ) == 0 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + if( mpi_cmp_int( E, 0 ) < 0 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + /* + * Init temps and window size + */ + mpi_montg_init( &mm, N ); + mpi_init( &RR ); mpi_init( &T ); + memset( W, 0, sizeof( W ) ); + + i = mpi_msb( E ); + + wsize = ( i > 671 ) ? 6 : ( i > 239 ) ? 5 : + ( i > 79 ) ? 4 : ( i > 23 ) ? 3 : 1; + + if( wsize > POLARSSL_MPI_WINDOW_SIZE ) + wsize = POLARSSL_MPI_WINDOW_SIZE; + + j = N->n + 1; + MPI_CHK( mpi_grow( X, j ) ); + MPI_CHK( mpi_grow( &W[1], j ) ); + MPI_CHK( mpi_grow( &T, j * 2 ) ); + + /* + * Compensate for negative A (and correct at the end) + */ + neg = ( A->s == -1 ); + + mpi_init( &Apos ); + if( neg ) + { + MPI_CHK( mpi_copy( &Apos, A ) ); + Apos.s = 1; + A = &Apos; + } + + /* + * If 1st call, pre-compute R^2 mod N + */ + if( _RR == NULL || _RR->p == NULL ) + { + MPI_CHK( mpi_lset( &RR, 1 ) ); + MPI_CHK( mpi_shift_l( &RR, N->n * 2 * biL ) ); + MPI_CHK( mpi_mod_mpi( &RR, &RR, N ) ); + + if( _RR != NULL ) + memcpy( _RR, &RR, sizeof( mpi ) ); + } + else + memcpy( &RR, _RR, sizeof( mpi ) ); + + /* + * W[1] = A * R^2 * R^-1 mod N = A * R mod N + */ + if( mpi_cmp_mpi( A, N ) >= 0 ) + mpi_mod_mpi( &W[1], A, N ); + else mpi_copy( &W[1], A ); + + mpi_montmul( &W[1], &RR, N, mm, &T ); + + /* + * X = R^2 * R^-1 mod N = R mod N + */ + MPI_CHK( mpi_copy( X, &RR ) ); + mpi_montred( X, N, mm, &T ); + + if( wsize > 1 ) + { + /* + * W[1 << (wsize - 1)] = W[1] ^ (wsize - 1) + */ + j = one << (wsize - 1); + + MPI_CHK( mpi_grow( &W[j], N->n + 1 ) ); + MPI_CHK( mpi_copy( &W[j], &W[1] ) ); + + for( i = 0; i < wsize - 1; i++ ) + mpi_montmul( &W[j], &W[j], N, mm, &T ); + + /* + * W[i] = W[i - 1] * W[1] + */ + for( i = j + 1; i < (one << wsize); i++ ) + { + MPI_CHK( mpi_grow( &W[i], N->n + 1 ) ); + MPI_CHK( mpi_copy( &W[i], &W[i - 1] ) ); + + mpi_montmul( &W[i], &W[1], N, mm, &T ); + } + } + + nblimbs = E->n; + bufsize = 0; + nbits = 0; + wbits = 0; + state = 0; + + while( 1 ) + { + if( bufsize == 0 ) + { + if( nblimbs-- == 0 ) + break; + + bufsize = sizeof( t_uint ) << 3; + } + + bufsize--; + + ei = (E->p[nblimbs] >> bufsize) & 1; + + /* + * skip leading 0s + */ + if( ei == 0 && state == 0 ) + continue; + + if( ei == 0 && state == 1 ) + { + /* + * out of window, square X + */ + mpi_montmul( X, X, N, mm, &T ); + continue; + } + + /* + * add ei to current window + */ + state = 2; + + nbits++; + wbits |= (ei << (wsize - nbits)); + + if( nbits == wsize ) + { + /* + * X = X^wsize R^-1 mod N + */ + for( i = 0; i < wsize; i++ ) + mpi_montmul( X, X, N, mm, &T ); + + /* + * X = X * W[wbits] R^-1 mod N + */ + mpi_montmul( X, &W[wbits], N, mm, &T ); + + state--; + nbits = 0; + wbits = 0; + } + } + + /* + * process the remaining bits + */ + for( i = 0; i < nbits; i++ ) + { + mpi_montmul( X, X, N, mm, &T ); + + wbits <<= 1; + + if( (wbits & (one << wsize)) != 0 ) + mpi_montmul( X, &W[1], N, mm, &T ); + } + + /* + * X = A^E * R * R^-1 mod N = A^E mod N + */ + mpi_montred( X, N, mm, &T ); + + if( neg ) + { + X->s = -1; + mpi_add_mpi( X, N, X ); + } + +cleanup: + + for( i = (one << (wsize - 1)); i < (one << wsize); i++ ) + mpi_free( &W[i] ); + + mpi_free( &W[1] ); mpi_free( &T ); mpi_free( &Apos ); + + if( _RR == NULL ) + mpi_free( &RR ); + + return( ret ); +} + +/* + * Greatest common divisor: G = gcd(A, B) (HAC 14.54) + */ +int mpi_gcd( mpi *G, const mpi *A, const mpi *B ) +{ + int ret; + size_t lz, lzt; + mpi TG, TA, TB; + + mpi_init( &TG ); mpi_init( &TA ); mpi_init( &TB ); + + MPI_CHK( mpi_copy( &TA, A ) ); + MPI_CHK( mpi_copy( &TB, B ) ); + + lz = mpi_lsb( &TA ); + lzt = mpi_lsb( &TB ); + + if ( lzt < lz ) + lz = lzt; + + MPI_CHK( mpi_shift_r( &TA, lz ) ); + MPI_CHK( mpi_shift_r( &TB, lz ) ); + + TA.s = TB.s = 1; + + while( mpi_cmp_int( &TA, 0 ) != 0 ) + { + MPI_CHK( mpi_shift_r( &TA, mpi_lsb( &TA ) ) ); + MPI_CHK( mpi_shift_r( &TB, mpi_lsb( &TB ) ) ); + + if( mpi_cmp_mpi( &TA, &TB ) >= 0 ) + { + MPI_CHK( mpi_sub_abs( &TA, &TA, &TB ) ); + MPI_CHK( mpi_shift_r( &TA, 1 ) ); + } + else + { + MPI_CHK( mpi_sub_abs( &TB, &TB, &TA ) ); + MPI_CHK( mpi_shift_r( &TB, 1 ) ); + } + } + + MPI_CHK( mpi_shift_l( &TB, lz ) ); + MPI_CHK( mpi_copy( G, &TB ) ); + +cleanup: + + mpi_free( &TG ); mpi_free( &TA ); mpi_free( &TB ); + + return( ret ); +} + +int mpi_fill_random( mpi *X, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + + MPI_CHK( mpi_grow( X, CHARS_TO_LIMBS( size ) ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + MPI_CHK( f_rng( p_rng, (unsigned char *) X->p, size ) ); + +cleanup: + return( ret ); +} + +/* + * Modular inverse: X = A^-1 mod N (HAC 14.61 / 14.64) + */ +int mpi_inv_mod( mpi *X, const mpi *A, const mpi *N ) +{ + int ret; + mpi G, TA, TU, U1, U2, TB, TV, V1, V2; + + if( mpi_cmp_int( N, 0 ) <= 0 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + mpi_init( &TA ); mpi_init( &TU ); mpi_init( &U1 ); mpi_init( &U2 ); + mpi_init( &G ); mpi_init( &TB ); mpi_init( &TV ); + mpi_init( &V1 ); mpi_init( &V2 ); + + MPI_CHK( mpi_gcd( &G, A, N ) ); + + if( mpi_cmp_int( &G, 1 ) != 0 ) + { + ret = POLARSSL_ERR_MPI_NOT_ACCEPTABLE; + goto cleanup; + } + + MPI_CHK( mpi_mod_mpi( &TA, A, N ) ); + MPI_CHK( mpi_copy( &TU, &TA ) ); + MPI_CHK( mpi_copy( &TB, N ) ); + MPI_CHK( mpi_copy( &TV, N ) ); + + MPI_CHK( mpi_lset( &U1, 1 ) ); + MPI_CHK( mpi_lset( &U2, 0 ) ); + MPI_CHK( mpi_lset( &V1, 0 ) ); + MPI_CHK( mpi_lset( &V2, 1 ) ); + + do + { + while( ( TU.p[0] & 1 ) == 0 ) + { + MPI_CHK( mpi_shift_r( &TU, 1 ) ); + + if( ( U1.p[0] & 1 ) != 0 || ( U2.p[0] & 1 ) != 0 ) + { + MPI_CHK( mpi_add_mpi( &U1, &U1, &TB ) ); + MPI_CHK( mpi_sub_mpi( &U2, &U2, &TA ) ); + } + + MPI_CHK( mpi_shift_r( &U1, 1 ) ); + MPI_CHK( mpi_shift_r( &U2, 1 ) ); + } + + while( ( TV.p[0] & 1 ) == 0 ) + { + MPI_CHK( mpi_shift_r( &TV, 1 ) ); + + if( ( V1.p[0] & 1 ) != 0 || ( V2.p[0] & 1 ) != 0 ) + { + MPI_CHK( mpi_add_mpi( &V1, &V1, &TB ) ); + MPI_CHK( mpi_sub_mpi( &V2, &V2, &TA ) ); + } + + MPI_CHK( mpi_shift_r( &V1, 1 ) ); + MPI_CHK( mpi_shift_r( &V2, 1 ) ); + } + + if( mpi_cmp_mpi( &TU, &TV ) >= 0 ) + { + MPI_CHK( mpi_sub_mpi( &TU, &TU, &TV ) ); + MPI_CHK( mpi_sub_mpi( &U1, &U1, &V1 ) ); + MPI_CHK( mpi_sub_mpi( &U2, &U2, &V2 ) ); + } + else + { + MPI_CHK( mpi_sub_mpi( &TV, &TV, &TU ) ); + MPI_CHK( mpi_sub_mpi( &V1, &V1, &U1 ) ); + MPI_CHK( mpi_sub_mpi( &V2, &V2, &U2 ) ); + } + } + while( mpi_cmp_int( &TU, 0 ) != 0 ); + + while( mpi_cmp_int( &V1, 0 ) < 0 ) + MPI_CHK( mpi_add_mpi( &V1, &V1, N ) ); + + while( mpi_cmp_mpi( &V1, N ) >= 0 ) + MPI_CHK( mpi_sub_mpi( &V1, &V1, N ) ); + + MPI_CHK( mpi_copy( X, &V1 ) ); + +cleanup: + + mpi_free( &TA ); mpi_free( &TU ); mpi_free( &U1 ); mpi_free( &U2 ); + mpi_free( &G ); mpi_free( &TB ); mpi_free( &TV ); + mpi_free( &V1 ); mpi_free( &V2 ); + + return( ret ); +} + +#if defined(POLARSSL_GENPRIME) + +static const int small_prime[] = +{ + 3, 5, 7, 11, 13, 17, 19, 23, + 29, 31, 37, 41, 43, 47, 53, 59, + 61, 67, 71, 73, 79, 83, 89, 97, + 101, 103, 107, 109, 113, 127, 131, 137, + 139, 149, 151, 157, 163, 167, 173, 179, + 181, 191, 193, 197, 199, 211, 223, 227, + 229, 233, 239, 241, 251, 257, 263, 269, + 271, 277, 281, 283, 293, 307, 311, 313, + 317, 331, 337, 347, 349, 353, 359, 367, + 373, 379, 383, 389, 397, 401, 409, 419, + 421, 431, 433, 439, 443, 449, 457, 461, + 463, 467, 479, 487, 491, 499, 503, 509, + 521, 523, 541, 547, 557, 563, 569, 571, + 577, 587, 593, 599, 601, 607, 613, 617, + 619, 631, 641, 643, 647, 653, 659, 661, + 673, 677, 683, 691, 701, 709, 719, 727, + 733, 739, 743, 751, 757, 761, 769, 773, + 787, 797, 809, 811, 821, 823, 827, 829, + 839, 853, 857, 859, 863, 877, 881, 883, + 887, 907, 911, 919, 929, 937, 941, 947, + 953, 967, 971, 977, 983, 991, 997, -103 +}; + +/* + * Miller-Rabin primality test (HAC 4.24) + */ +int mpi_is_prime( mpi *X, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret, xs; + size_t i, j, n, s; + mpi W, R, T, A, RR; + + if( mpi_cmp_int( X, 0 ) == 0 || + mpi_cmp_int( X, 1 ) == 0 ) + return( POLARSSL_ERR_MPI_NOT_ACCEPTABLE ); + + if( mpi_cmp_int( X, 2 ) == 0 ) + return( 0 ); + + mpi_init( &W ); mpi_init( &R ); mpi_init( &T ); mpi_init( &A ); + mpi_init( &RR ); + + xs = X->s; X->s = 1; + + /* + * test trivial factors first + */ + if( ( X->p[0] & 1 ) == 0 ) + return( POLARSSL_ERR_MPI_NOT_ACCEPTABLE ); + + for( i = 0; small_prime[i] > 0; i++ ) + { + t_uint r; + + if( mpi_cmp_int( X, small_prime[i] ) <= 0 ) + return( 0 ); + + MPI_CHK( mpi_mod_int( &r, X, small_prime[i] ) ); + + if( r == 0 ) + return( POLARSSL_ERR_MPI_NOT_ACCEPTABLE ); + } + + /* + * W = |X| - 1 + * R = W >> lsb( W ) + */ + MPI_CHK( mpi_sub_int( &W, X, 1 ) ); + s = mpi_lsb( &W ); + MPI_CHK( mpi_copy( &R, &W ) ); + MPI_CHK( mpi_shift_r( &R, s ) ); + + i = mpi_msb( X ); + /* + * HAC, table 4.4 + */ + n = ( ( i >= 1300 ) ? 2 : ( i >= 850 ) ? 3 : + ( i >= 650 ) ? 4 : ( i >= 350 ) ? 8 : + ( i >= 250 ) ? 12 : ( i >= 150 ) ? 18 : 27 ); + + for( i = 0; i < n; i++ ) + { + /* + * pick a random A, 1 < A < |X| - 1 + */ + MPI_CHK( mpi_fill_random( &A, X->n * ciL, f_rng, p_rng ) ); + + if( mpi_cmp_mpi( &A, &W ) >= 0 ) + { + j = mpi_msb( &A ) - mpi_msb( &W ); + MPI_CHK( mpi_shift_r( &A, j + 1 ) ); + } + A.p[0] |= 3; + + /* + * A = A^R mod |X| + */ + MPI_CHK( mpi_exp_mod( &A, &A, &R, X, &RR ) ); + + if( mpi_cmp_mpi( &A, &W ) == 0 || + mpi_cmp_int( &A, 1 ) == 0 ) + continue; + + j = 1; + while( j < s && mpi_cmp_mpi( &A, &W ) != 0 ) + { + /* + * A = A * A mod |X| + */ + MPI_CHK( mpi_mul_mpi( &T, &A, &A ) ); + MPI_CHK( mpi_mod_mpi( &A, &T, X ) ); + + if( mpi_cmp_int( &A, 1 ) == 0 ) + break; + + j++; + } + + /* + * not prime if A != |X| - 1 or A == 1 + */ + if( mpi_cmp_mpi( &A, &W ) != 0 || + mpi_cmp_int( &A, 1 ) == 0 ) + { + ret = POLARSSL_ERR_MPI_NOT_ACCEPTABLE; + break; + } + } + +cleanup: + + X->s = xs; + + mpi_free( &W ); mpi_free( &R ); mpi_free( &T ); mpi_free( &A ); + mpi_free( &RR ); + + return( ret ); +} + +/* + * Prime number generation + */ +int mpi_gen_prime( mpi *X, size_t nbits, int dh_flag, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + size_t k, n; + mpi Y; + + if( nbits < 3 || nbits > POLARSSL_MPI_MAX_BITS ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + mpi_init( &Y ); + + n = BITS_TO_LIMBS( nbits ); + + MPI_CHK( mpi_fill_random( X, n * ciL, f_rng, p_rng ) ); + + k = mpi_msb( X ); + if( k < nbits ) MPI_CHK( mpi_shift_l( X, nbits - k ) ); + if( k > nbits ) MPI_CHK( mpi_shift_r( X, k - nbits ) ); + + X->p[0] |= 3; + + if( dh_flag == 0 ) + { + while( ( ret = mpi_is_prime( X, f_rng, p_rng ) ) != 0 ) + { + if( ret != POLARSSL_ERR_MPI_NOT_ACCEPTABLE ) + goto cleanup; + + MPI_CHK( mpi_add_int( X, X, 2 ) ); + } + } + else + { + MPI_CHK( mpi_sub_int( &Y, X, 1 ) ); + MPI_CHK( mpi_shift_r( &Y, 1 ) ); + + while( 1 ) + { + if( ( ret = mpi_is_prime( X, f_rng, p_rng ) ) == 0 ) + { + if( ( ret = mpi_is_prime( &Y, f_rng, p_rng ) ) == 0 ) + break; + + if( ret != POLARSSL_ERR_MPI_NOT_ACCEPTABLE ) + goto cleanup; + } + + if( ret != POLARSSL_ERR_MPI_NOT_ACCEPTABLE ) + goto cleanup; + + MPI_CHK( mpi_add_int( &Y, X, 1 ) ); + MPI_CHK( mpi_add_int( X, X, 2 ) ); + MPI_CHK( mpi_shift_r( &Y, 1 ) ); + } + } + +cleanup: + + mpi_free( &Y ); + + return( ret ); +} + +#endif + +#if defined(POLARSSL_SELF_TEST) + +#define GCD_PAIR_COUNT 3 + +static const int gcd_pairs[GCD_PAIR_COUNT][3] = +{ + { 693, 609, 21 }, + { 1764, 868, 28 }, + { 768454923, 542167814, 1 } +}; + +/* + * Checkup routine + */ +int mpi_self_test( int verbose ) +{ + int ret, i; + mpi A, E, N, X, Y, U, V; + + mpi_init( &A ); mpi_init( &E ); mpi_init( &N ); mpi_init( &X ); + mpi_init( &Y ); mpi_init( &U ); mpi_init( &V ); + + MPI_CHK( mpi_read_string( &A, 16, + "EFE021C2645FD1DC586E69184AF4A31E" \ + "D5F53E93B5F123FA41680867BA110131" \ + "944FE7952E2517337780CB0DB80E61AA" \ + "E7C8DDC6C5C6AADEB34EB38A2F40D5E6" ) ); + + MPI_CHK( mpi_read_string( &E, 16, + "B2E7EFD37075B9F03FF989C7C5051C20" \ + "34D2A323810251127E7BF8625A4F49A5" \ + "F3E27F4DA8BD59C47D6DAABA4C8127BD" \ + "5B5C25763222FEFCCFC38B832366C29E" ) ); + + MPI_CHK( mpi_read_string( &N, 16, + "0066A198186C18C10B2F5ED9B522752A" \ + "9830B69916E535C8F047518A889A43A5" \ + "94B6BED27A168D31D4A52F88925AA8F5" ) ); + + MPI_CHK( mpi_mul_mpi( &X, &A, &N ) ); + + MPI_CHK( mpi_read_string( &U, 16, + "602AB7ECA597A3D6B56FF9829A5E8B85" \ + "9E857EA95A03512E2BAE7391688D264A" \ + "A5663B0341DB9CCFD2C4C5F421FEC814" \ + "8001B72E848A38CAE1C65F78E56ABDEF" \ + "E12D3C039B8A02D6BE593F0BBBDA56F1" \ + "ECF677152EF804370C1A305CAF3B5BF1" \ + "30879B56C61DE584A0F53A2447A51E" ) ); + + if( verbose != 0 ) + printf( " MPI test #1 (mul_mpi): " ); + + if( mpi_cmp_mpi( &X, &U ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); + + MPI_CHK( mpi_div_mpi( &X, &Y, &A, &N ) ); + + MPI_CHK( mpi_read_string( &U, 16, + "256567336059E52CAE22925474705F39A94" ) ); + + MPI_CHK( mpi_read_string( &V, 16, + "6613F26162223DF488E9CD48CC132C7A" \ + "0AC93C701B001B092E4E5B9F73BCD27B" \ + "9EE50D0657C77F374E903CDFA4C642" ) ); + + if( verbose != 0 ) + printf( " MPI test #2 (div_mpi): " ); + + if( mpi_cmp_mpi( &X, &U ) != 0 || + mpi_cmp_mpi( &Y, &V ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); + + MPI_CHK( mpi_exp_mod( &X, &A, &E, &N, NULL ) ); + + MPI_CHK( mpi_read_string( &U, 16, + "36E139AEA55215609D2816998ED020BB" \ + "BD96C37890F65171D948E9BC7CBAA4D9" \ + "325D24D6A3C12710F10A09FA08AB87" ) ); + + if( verbose != 0 ) + printf( " MPI test #3 (exp_mod): " ); + + if( mpi_cmp_mpi( &X, &U ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); + +#if defined(POLARSSL_GENPRIME) + MPI_CHK( mpi_inv_mod( &X, &A, &N ) ); + + MPI_CHK( mpi_read_string( &U, 16, + "003A0AAEDD7E784FC07D8F9EC6E3BFD5" \ + "C3DBA76456363A10869622EAC2DD84EC" \ + "C5B8A74DAC4D09E03B5E0BE779F2DF61" ) ); + + if( verbose != 0 ) + printf( " MPI test #4 (inv_mod): " ); + + if( mpi_cmp_mpi( &X, &U ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); +#endif + + if( verbose != 0 ) + printf( " MPI test #5 (simple gcd): " ); + + for ( i = 0; i < GCD_PAIR_COUNT; i++) + { + MPI_CHK( mpi_lset( &X, gcd_pairs[i][0] ) ); + MPI_CHK( mpi_lset( &Y, gcd_pairs[i][1] ) ); + + MPI_CHK( mpi_gcd( &A, &X, &Y ) ); + + if( mpi_cmp_int( &A, gcd_pairs[i][2] ) != 0 ) + { + if( verbose != 0 ) + printf( "failed at %d\n", i ); + + return( 1 ); + } + } + + if( verbose != 0 ) + printf( "passed\n" ); + +cleanup: + + if( ret != 0 && verbose != 0 ) + printf( "Unexpected error, return code = %08X\n", ret ); + + mpi_free( &A ); mpi_free( &E ); mpi_free( &N ); mpi_free( &X ); + mpi_free( &Y ); mpi_free( &U ); mpi_free( &V ); + + if( verbose != 0 ) + printf( "\n" ); + + return( ret ); +} + +#endif + +#endif diff --git a/common/polarssl/bignum.h b/common/polarssl/bignum.h new file mode 100644 index 00000000..5eaf1a57 --- /dev/null +++ b/common/polarssl/bignum.h @@ -0,0 +1,685 @@ +/** + * \file bignum.h + * + * \brief Multi-precision integer library + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_BIGNUM_H +#define POLARSSL_BIGNUM_H + +#include +#include + +#include "polarssl_config.h" + +#ifdef _MSC_VER +#include +#if (_MSC_VER <= 1200) +typedef signed short int16_t; +typedef unsigned short uint16_t; +#else +typedef INT16 int16_t; +typedef UINT16 uint16_t; +#endif +typedef INT32 int32_t; +typedef INT64 int64_t; +typedef UINT32 uint32_t; +typedef UINT64 uint64_t; +#else +#include +#endif + +#define POLARSSL_ERR_MPI_FILE_IO_ERROR -0x0002 /**< An error occurred while reading from or writing to a file. */ +#define POLARSSL_ERR_MPI_BAD_INPUT_DATA -0x0004 /**< Bad input parameters to function. */ +#define POLARSSL_ERR_MPI_INVALID_CHARACTER -0x0006 /**< There is an invalid character in the digit string. */ +#define POLARSSL_ERR_MPI_BUFFER_TOO_SMALL -0x0008 /**< The buffer is too small to write to. */ +#define POLARSSL_ERR_MPI_NEGATIVE_VALUE -0x000A /**< The input arguments are negative or result in illegal output. */ +#define POLARSSL_ERR_MPI_DIVISION_BY_ZERO -0x000C /**< The input argument for division is zero, which is not allowed. */ +#define POLARSSL_ERR_MPI_NOT_ACCEPTABLE -0x000E /**< The input arguments are not acceptable. */ +#define POLARSSL_ERR_MPI_MALLOC_FAILED -0x0010 /**< Memory allocation failed. */ + +#define MPI_CHK(f) if( ( ret = f ) != 0 ) goto cleanup + +/* + * Maximum size MPIs are allowed to grow to in number of limbs. + */ +#define POLARSSL_MPI_MAX_LIMBS 10000 + +#if !defined(POLARSSL_CONFIG_OPTIONS) +/* + * Maximum window size used for modular exponentiation. Default: 6 + * Minimum value: 1. Maximum value: 6. + * + * Result is an array of ( 2 << POLARSSL_MPI_WINDOW_SIZE ) MPIs used + * for the sliding window calculation. (So 64 by default) + * + * Reduction in size, reduces speed. + */ +#define POLARSSL_MPI_WINDOW_SIZE 6 /**< Maximum windows size used. */ + +/* + * Maximum size of MPIs allowed in bits and bytes for user-MPIs. + * ( Default: 512 bytes => 4096 bits, Maximum tested: 2048 bytes => 16384 bits ) + * + * Note: Calculations can results temporarily in larger MPIs. So the number + * of limbs required (POLARSSL_MPI_MAX_LIMBS) is higher. + */ +#define POLARSSL_MPI_MAX_SIZE 512 /**< Maximum number of bytes for usable MPIs. */ + +#endif /* !POLARSSL_CONFIG_OPTIONS */ + +#define POLARSSL_MPI_MAX_BITS ( 8 * POLARSSL_MPI_MAX_SIZE ) /**< Maximum number of bits for usable MPIs. */ + +/* + * When reading from files with mpi_read_file() and writing to files with + * mpi_write_file() the buffer should have space + * for a (short) label, the MPI (in the provided radix), the newline + * characters and the '\0'. + * + * By default we assume at least a 10 char label, a minimum radix of 10 + * (decimal) and a maximum of 4096 bit numbers (1234 decimal chars). + * Autosized at compile time for at least a 10 char label, a minimum radix + * of 10 (decimal) for a number of POLARSSL_MPI_MAX_BITS size. + * + * This used to be statically sized to 1250 for a maximum of 4096 bit + * numbers (1234 decimal chars). + * + * Calculate using the formula: + * POLARSSL_MPI_RW_BUFFER_SIZE = ceil(POLARSSL_MPI_MAX_BITS / ln(10) * ln(2)) + + * LabelSize + 6 + */ +#define POLARSSL_MPI_MAX_BITS_SCALE100 ( 100 * POLARSSL_MPI_MAX_BITS ) +#define LN_2_DIV_LN_10_SCALE100 332 +#define POLARSSL_MPI_RW_BUFFER_SIZE ( ((POLARSSL_MPI_MAX_BITS_SCALE100 + LN_2_DIV_LN_10_SCALE100 - 1) / LN_2_DIV_LN_10_SCALE100) + 10 + 6 ) + +/* + * Define the base integer type, architecture-wise + */ +#if defined(POLARSSL_HAVE_INT8) +typedef signed char t_sint; +typedef unsigned char t_uint; +typedef uint16_t t_udbl; +#define POLARSSL_HAVE_UDBL +#else +#if defined(POLARSSL_HAVE_INT16) +typedef int16_t t_sint; +typedef uint16_t t_uint; +typedef uint32_t t_udbl; +#define POLARSSL_HAVE_UDBL +#else + #if ( defined(_MSC_VER) && defined(_M_AMD64) ) + typedef int64_t t_sint; + typedef uint64_t t_uint; + #else + #if ( defined(__GNUC__) && ( \ + defined(__amd64__) || defined(__x86_64__) || \ + defined(__ppc64__) || defined(__powerpc64__) || \ + defined(__ia64__) || defined(__alpha__) || \ + (defined(__sparc__) && defined(__arch64__)) || \ + defined(__s390x__) ) ) + typedef int64_t t_sint; + typedef uint64_t t_uint; + typedef unsigned int t_udbl __attribute__((mode(TI))); + #define POLARSSL_HAVE_UDBL + #else + typedef int32_t t_sint; + typedef uint32_t t_uint; + #if ( defined(_MSC_VER) && defined(_M_IX86) ) + typedef uint64_t t_udbl; + #define POLARSSL_HAVE_UDBL + #else + #if defined( POLARSSL_HAVE_LONGLONG ) + typedef unsigned long long t_udbl; + #define POLARSSL_HAVE_UDBL + #endif + #endif + #endif + #endif +#endif /* POLARSSL_HAVE_INT16 */ +#endif /* POLARSSL_HAVE_INT8 */ + +/** + * \brief MPI structure + */ +typedef struct +{ + int s; /*!< integer sign */ + size_t n; /*!< total # of limbs */ + t_uint *p; /*!< pointer to limbs */ +} +mpi; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Initialize one MPI + * + * \param X One MPI to initialize. + */ +void mpi_init( mpi *X ); + +/** + * \brief Unallocate one MPI + * + * \param X One MPI to unallocate. + */ +void mpi_free( mpi *X ); + +/** + * \brief Enlarge to the specified number of limbs + * + * \param X MPI to grow + * \param nblimbs The target number of limbs + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_grow( mpi *X, size_t nblimbs ); + +/** + * \brief Copy the contents of Y into X + * + * \param X Destination MPI + * \param Y Source MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_copy( mpi *X, const mpi *Y ); + +/** + * \brief Swap the contents of X and Y + * + * \param X First MPI value + * \param Y Second MPI value + */ +void mpi_swap( mpi *X, mpi *Y ); + +/** + * \brief Set value from integer + * + * \param X MPI to set + * \param z Value to use + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_lset( mpi *X, t_sint z ); + +/** + * \brief Get a specific bit from X + * + * \param X MPI to use + * \param pos Zero-based index of the bit in X + * + * \return Either a 0 or a 1 + */ +int mpi_get_bit( const mpi *X, size_t pos ); + +/** + * \brief Set a bit of X to a specific value of 0 or 1 + * + * \note Will grow X if necessary to set a bit to 1 in a not yet + * existing limb. Will not grow if bit should be set to 0 + * + * \param X MPI to use + * \param pos Zero-based index of the bit in X + * \param val The value to set the bit to (0 or 1) + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_BAD_INPUT_DATA if val is not 0 or 1 + */ +int mpi_set_bit( mpi *X, size_t pos, unsigned char val ); + +/** + * \brief Return the number of zero-bits before the least significant + * '1' bit + * + * Note: Thus also the zero-based index of the least significant '1' bit + * + * \param X MPI to use + */ +size_t mpi_lsb( const mpi *X ); + +/** + * \brief Return the number of bits up to and including the most + * significant '1' bit' + * + * Note: Thus also the one-based index of the most significant '1' bit + * + * \param X MPI to use + */ +size_t mpi_msb( const mpi *X ); + +/** + * \brief Return the total size in bytes + * + * \param X MPI to use + */ +size_t mpi_size( const mpi *X ); + +/** + * \brief Import from an ASCII string + * + * \param X Destination MPI + * \param radix Input numeric base + * \param s Null-terminated string buffer + * + * \return 0 if successful, or a POLARSSL_ERR_MPI_XXX error code + */ +int mpi_read_string( mpi *X, int radix, const char *s ); + +/** + * \brief Export into an ASCII string + * + * \param X Source MPI + * \param radix Output numeric base + * \param s String buffer + * \param slen String buffer size + * + * \return 0 if successful, or a POLARSSL_ERR_MPI_XXX error code. + * *slen is always updated to reflect the amount + * of data that has (or would have) been written. + * + * \note Call this function with *slen = 0 to obtain the + * minimum required buffer size in *slen. + */ +int mpi_write_string( const mpi *X, int radix, char *s, size_t *slen ); + +#if defined(POLARSSL_FS_IO) +/** + * \brief Read X from an opened file + * + * \param X Destination MPI + * \param radix Input numeric base + * \param fin Input file handle + * + * \return 0 if successful, POLARSSL_ERR_MPI_BUFFER_TOO_SMALL if + * the file read buffer is too small or a + * POLARSSL_ERR_MPI_XXX error code + */ +int mpi_read_file( mpi *X, int radix, FILE *fin ); + +/** + * \brief Write X into an opened file, or stdout if fout is NULL + * + * \param p Prefix, can be NULL + * \param X Source MPI + * \param radix Output numeric base + * \param fout Output file handle (can be NULL) + * + * \return 0 if successful, or a POLARSSL_ERR_MPI_XXX error code + * + * \note Set fout == NULL to print X on the console. + */ +int mpi_write_file( const char *p, const mpi *X, int radix, FILE *fout ); +#endif /* POLARSSL_FS_IO */ + +/** + * \brief Import X from unsigned binary data, big endian + * + * \param X Destination MPI + * \param buf Input buffer + * \param buflen Input buffer size + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_read_binary( mpi *X, const unsigned char *buf, size_t buflen ); + +/** + * \brief Export X into unsigned binary data, big endian + * + * \param X Source MPI + * \param buf Output buffer + * \param buflen Output buffer size + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_BUFFER_TOO_SMALL if buf isn't large enough + */ +int mpi_write_binary( const mpi *X, unsigned char *buf, size_t buflen ); + +/** + * \brief Left-shift: X <<= count + * + * \param X MPI to shift + * \param count Amount to shift + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_shift_l( mpi *X, size_t count ); + +/** + * \brief Right-shift: X >>= count + * + * \param X MPI to shift + * \param count Amount to shift + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_shift_r( mpi *X, size_t count ); + +/** + * \brief Compare unsigned values + * + * \param X Left-hand MPI + * \param Y Right-hand MPI + * + * \return 1 if |X| is greater than |Y|, + * -1 if |X| is lesser than |Y| or + * 0 if |X| is equal to |Y| + */ +int mpi_cmp_abs( const mpi *X, const mpi *Y ); + +/** + * \brief Compare signed values + * + * \param X Left-hand MPI + * \param Y Right-hand MPI + * + * \return 1 if X is greater than Y, + * -1 if X is lesser than Y or + * 0 if X is equal to Y + */ +int mpi_cmp_mpi( const mpi *X, const mpi *Y ); + +/** + * \brief Compare signed values + * + * \param X Left-hand MPI + * \param z The integer value to compare to + * + * \return 1 if X is greater than z, + * -1 if X is lesser than z or + * 0 if X is equal to z + */ +int mpi_cmp_int( const mpi *X, t_sint z ); + +/** + * \brief Unsigned addition: X = |A| + |B| + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_add_abs( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Unsigned substraction: X = |A| - |B| + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_NEGATIVE_VALUE if B is greater than A + */ +int mpi_sub_abs( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Signed addition: X = A + B + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_add_mpi( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Signed substraction: X = A - B + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_sub_mpi( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Signed addition: X = A + b + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param b The integer value to add + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_add_int( mpi *X, const mpi *A, t_sint b ); + +/** + * \brief Signed substraction: X = A - b + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param b The integer value to subtract + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_sub_int( mpi *X, const mpi *A, t_sint b ); + +/** + * \brief Baseline multiplication: X = A * B + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_mul_mpi( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Baseline multiplication: X = A * b + * Note: b is an unsigned integer type, thus + * Negative values of b are ignored. + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param b The integer value to multiply with + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_mul_int( mpi *X, const mpi *A, t_sint b ); + +/** + * \brief Division by mpi: A = Q * B + R + * + * \param Q Destination MPI for the quotient + * \param R Destination MPI for the rest value + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if B == 0 + * + * \note Either Q or R can be NULL. + */ +int mpi_div_mpi( mpi *Q, mpi *R, const mpi *A, const mpi *B ); + +/** + * \brief Division by int: A = Q * b + R + * + * \param Q Destination MPI for the quotient + * \param R Destination MPI for the rest value + * \param A Left-hand MPI + * \param b Integer to divide by + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if b == 0 + * + * \note Either Q or R can be NULL. + */ +int mpi_div_int( mpi *Q, mpi *R, const mpi *A, t_sint b ); + +/** + * \brief Modulo: R = A mod B + * + * \param R Destination MPI for the rest value + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if B == 0, + * POLARSSL_ERR_MPI_NEGATIVE_VALUE if B < 0 + */ +int mpi_mod_mpi( mpi *R, const mpi *A, const mpi *B ); + +/** + * \brief Modulo: r = A mod b + * + * \param r Destination t_uint + * \param A Left-hand MPI + * \param b Integer to divide by + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if b == 0, + * POLARSSL_ERR_MPI_NEGATIVE_VALUE if b < 0 + */ +int mpi_mod_int( t_uint *r, const mpi *A, t_sint b ); + +/** + * \brief Sliding-window exponentiation: X = A^E mod N + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param E Exponent MPI + * \param N Modular MPI + * \param _RR Speed-up MPI used for recalculations + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_BAD_INPUT_DATA if N is negative or even or if + * E is negative + * + * \note _RR is used to avoid re-computing R*R mod N across + * multiple calls, which speeds up things a bit. It can + * be set to NULL if the extra performance is unneeded. + */ +int mpi_exp_mod( mpi *X, const mpi *A, const mpi *E, const mpi *N, mpi *_RR ); + +/** + * \brief Fill an MPI X with size bytes of random + * + * \param X Destination MPI + * \param size Size in bytes + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_fill_random( mpi *X, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief Greatest common divisor: G = gcd(A, B) + * + * \param G Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_gcd( mpi *G, const mpi *A, const mpi *B ); + +/** + * \brief Modular inverse: X = A^-1 mod N + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param N Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_BAD_INPUT_DATA if N is negative or nil + POLARSSL_ERR_MPI_NOT_ACCEPTABLE if A has no inverse mod N + */ +int mpi_inv_mod( mpi *X, const mpi *A, const mpi *N ); + +/** + * \brief Miller-Rabin primality test + * + * \param X MPI to check + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \return 0 if successful (probably prime), + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_NOT_ACCEPTABLE if X is not prime + */ +int mpi_is_prime( mpi *X, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief Prime number generation + * + * \param X Destination MPI + * \param nbits Required size of X in bits ( 3 <= nbits <= POLARSSL_MPI_MAX_BITS ) + * \param dh_flag If 1, then (X-1)/2 will be prime too + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \return 0 if successful (probably prime), + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_BAD_INPUT_DATA if nbits is < 3 + */ +int mpi_gen_prime( mpi *X, size_t nbits, int dh_flag, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int mpi_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* bignum.h */ diff --git a/common/polarssl/bn_mul.h b/common/polarssl/bn_mul.h new file mode 100644 index 00000000..1c2da136 --- /dev/null +++ b/common/polarssl/bn_mul.h @@ -0,0 +1,864 @@ +/** + * \file bn_mul.h + * + * \brief Multi-precision integer library + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * Multiply source vector [s] with b, add result + * to destination vector [d] and set carry c. + * + * Currently supports: + * + * . IA-32 (386+) . AMD64 / EM64T + * . IA-32 (SSE2) . Motorola 68000 + * . PowerPC, 32-bit . MicroBlaze + * . PowerPC, 64-bit . TriCore + * . SPARC v8 . ARM v3+ + * . Alpha . MIPS32 + * . C, longlong . C, generic + */ +#ifndef POLARSSL_BN_MUL_H +#define POLARSSL_BN_MUL_H + +#include "bignum.h" + +#if defined(POLARSSL_HAVE_ASM) + +#if defined(__GNUC__) +#if defined(__i386__) + +#define MULADDC_INIT \ + asm( " \ + movl %%ebx, %0; \ + movl %5, %%esi; \ + movl %6, %%edi; \ + movl %7, %%ecx; \ + movl %8, %%ebx; \ + " + +#define MULADDC_CORE \ + " \ + lodsl; \ + mull %%ebx; \ + addl %%ecx, %%eax; \ + adcl $0, %%edx; \ + addl (%%edi), %%eax; \ + adcl $0, %%edx; \ + movl %%edx, %%ecx; \ + stosl; \ + " + +#if defined(POLARSSL_HAVE_SSE2) + +#define MULADDC_HUIT \ + " \ + movd %%ecx, %%mm1; \ + movd %%ebx, %%mm0; \ + movd (%%edi), %%mm3; \ + paddq %%mm3, %%mm1; \ + movd (%%esi), %%mm2; \ + pmuludq %%mm0, %%mm2; \ + movd 4(%%esi), %%mm4; \ + pmuludq %%mm0, %%mm4; \ + movd 8(%%esi), %%mm6; \ + pmuludq %%mm0, %%mm6; \ + movd 12(%%esi), %%mm7; \ + pmuludq %%mm0, %%mm7; \ + paddq %%mm2, %%mm1; \ + movd 4(%%edi), %%mm3; \ + paddq %%mm4, %%mm3; \ + movd 8(%%edi), %%mm5; \ + paddq %%mm6, %%mm5; \ + movd 12(%%edi), %%mm4; \ + paddq %%mm4, %%mm7; \ + movd %%mm1, (%%edi); \ + movd 16(%%esi), %%mm2; \ + pmuludq %%mm0, %%mm2; \ + psrlq $32, %%mm1; \ + movd 20(%%esi), %%mm4; \ + pmuludq %%mm0, %%mm4; \ + paddq %%mm3, %%mm1; \ + movd 24(%%esi), %%mm6; \ + pmuludq %%mm0, %%mm6; \ + movd %%mm1, 4(%%edi); \ + psrlq $32, %%mm1; \ + movd 28(%%esi), %%mm3; \ + pmuludq %%mm0, %%mm3; \ + paddq %%mm5, %%mm1; \ + movd 16(%%edi), %%mm5; \ + paddq %%mm5, %%mm2; \ + movd %%mm1, 8(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm7, %%mm1; \ + movd 20(%%edi), %%mm5; \ + paddq %%mm5, %%mm4; \ + movd %%mm1, 12(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm2, %%mm1; \ + movd 24(%%edi), %%mm5; \ + paddq %%mm5, %%mm6; \ + movd %%mm1, 16(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm4, %%mm1; \ + movd 28(%%edi), %%mm5; \ + paddq %%mm5, %%mm3; \ + movd %%mm1, 20(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm6, %%mm1; \ + movd %%mm1, 24(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm3, %%mm1; \ + movd %%mm1, 28(%%edi); \ + addl $32, %%edi; \ + addl $32, %%esi; \ + psrlq $32, %%mm1; \ + movd %%mm1, %%ecx; \ + " + +#define MULADDC_STOP \ + " \ + emms; \ + movl %4, %%ebx; \ + movl %%ecx, %1; \ + movl %%edi, %2; \ + movl %%esi, %3; \ + " \ + : "=m" (t), "=m" (c), "=m" (d), "=m" (s) \ + : "m" (t), "m" (s), "m" (d), "m" (c), "m" (b) \ + : "eax", "ecx", "edx", "esi", "edi" \ + ); + +#else + +#define MULADDC_STOP \ + " \ + movl %4, %%ebx; \ + movl %%ecx, %1; \ + movl %%edi, %2; \ + movl %%esi, %3; \ + " \ + : "=m" (t), "=m" (c), "=m" (d), "=m" (s) \ + : "m" (t), "m" (s), "m" (d), "m" (c), "m" (b) \ + : "eax", "ecx", "edx", "esi", "edi" \ + ); +#endif /* SSE2 */ +#endif /* i386 */ + +#if defined(__amd64__) || defined (__x86_64__) + +#define MULADDC_INIT \ + asm( "movq %0, %%rsi " :: "m" (s)); \ + asm( "movq %0, %%rdi " :: "m" (d)); \ + asm( "movq %0, %%rcx " :: "m" (c)); \ + asm( "movq %0, %%rbx " :: "m" (b)); \ + asm( "xorq %r8, %r8 " ); + +#define MULADDC_CORE \ + asm( "movq (%rsi),%rax " ); \ + asm( "mulq %rbx " ); \ + asm( "addq $8, %rsi " ); \ + asm( "addq %rcx, %rax " ); \ + asm( "movq %r8, %rcx " ); \ + asm( "adcq $0, %rdx " ); \ + asm( "nop " ); \ + asm( "addq %rax, (%rdi) " ); \ + asm( "adcq %rdx, %rcx " ); \ + asm( "addq $8, %rdi " ); + +#define MULADDC_STOP \ + asm( "movq %%rcx, %0 " : "=m" (c)); \ + asm( "movq %%rdi, %0 " : "=m" (d)); \ + asm( "movq %%rsi, %0 " : "=m" (s) :: \ + "rax", "rcx", "rdx", "rbx", "rsi", "rdi", "r8" ); + +#endif /* AMD64 */ + +#if defined(__mc68020__) || defined(__mcpu32__) + +#define MULADDC_INIT \ + asm( "movl %0, %%a2 " :: "m" (s)); \ + asm( "movl %0, %%a3 " :: "m" (d)); \ + asm( "movl %0, %%d3 " :: "m" (c)); \ + asm( "movl %0, %%d2 " :: "m" (b)); \ + asm( "moveq #0, %d0 " ); + +#define MULADDC_CORE \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "moveq #0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "addxl %d4, %d3 " ); + +#define MULADDC_STOP \ + asm( "movl %%d3, %0 " : "=m" (c)); \ + asm( "movl %%a3, %0 " : "=m" (d)); \ + asm( "movl %%a2, %0 " : "=m" (s) :: \ + "d0", "d1", "d2", "d3", "d4", "a2", "a3" ); + +#define MULADDC_HUIT \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "addxl %d0, %d3 " ); + +#endif /* MC68000 */ + +#if defined(__powerpc__) || defined(__ppc__) +#if defined(__powerpc64__) || defined(__ppc64__) + +#if defined(__MACH__) && defined(__APPLE__) + +#define MULADDC_INIT \ + asm( "ld r3, %0 " :: "m" (s)); \ + asm( "ld r4, %0 " :: "m" (d)); \ + asm( "ld r5, %0 " :: "m" (c)); \ + asm( "ld r6, %0 " :: "m" (b)); \ + asm( "addi r3, r3, -8 " ); \ + asm( "addi r4, r4, -8 " ); \ + asm( "addic r5, r5, 0 " ); + +#define MULADDC_CORE \ + asm( "ldu r7, 8(r3) " ); \ + asm( "mulld r8, r7, r6 " ); \ + asm( "mulhdu r9, r7, r6 " ); \ + asm( "adde r8, r8, r5 " ); \ + asm( "ld r7, 8(r4) " ); \ + asm( "addze r5, r9 " ); \ + asm( "addc r8, r8, r7 " ); \ + asm( "stdu r8, 8(r4) " ); + +#define MULADDC_STOP \ + asm( "addze r5, r5 " ); \ + asm( "addi r4, r4, 8 " ); \ + asm( "addi r3, r3, 8 " ); \ + asm( "std r5, %0 " : "=m" (c)); \ + asm( "std r4, %0 " : "=m" (d)); \ + asm( "std r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#else + +#define MULADDC_INIT \ + asm( "ld %%r3, %0 " :: "m" (s)); \ + asm( "ld %%r4, %0 " :: "m" (d)); \ + asm( "ld %%r5, %0 " :: "m" (c)); \ + asm( "ld %%r6, %0 " :: "m" (b)); \ + asm( "addi %r3, %r3, -8 " ); \ + asm( "addi %r4, %r4, -8 " ); \ + asm( "addic %r5, %r5, 0 " ); + +#define MULADDC_CORE \ + asm( "ldu %r7, 8(%r3) " ); \ + asm( "mulld %r8, %r7, %r6 " ); \ + asm( "mulhdu %r9, %r7, %r6 " ); \ + asm( "adde %r8, %r8, %r5 " ); \ + asm( "ld %r7, 8(%r4) " ); \ + asm( "addze %r5, %r9 " ); \ + asm( "addc %r8, %r8, %r7 " ); \ + asm( "stdu %r8, 8(%r4) " ); + +#define MULADDC_STOP \ + asm( "addze %r5, %r5 " ); \ + asm( "addi %r4, %r4, 8 " ); \ + asm( "addi %r3, %r3, 8 " ); \ + asm( "std %%r5, %0 " : "=m" (c)); \ + asm( "std %%r4, %0 " : "=m" (d)); \ + asm( "std %%r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#endif + +#else /* PPC32 */ + +#if defined(__MACH__) && defined(__APPLE__) + +#define MULADDC_INIT \ + asm( "lwz r3, %0 " :: "m" (s)); \ + asm( "lwz r4, %0 " :: "m" (d)); \ + asm( "lwz r5, %0 " :: "m" (c)); \ + asm( "lwz r6, %0 " :: "m" (b)); \ + asm( "addi r3, r3, -4 " ); \ + asm( "addi r4, r4, -4 " ); \ + asm( "addic r5, r5, 0 " ); + +#define MULADDC_CORE \ + asm( "lwzu r7, 4(r3) " ); \ + asm( "mullw r8, r7, r6 " ); \ + asm( "mulhwu r9, r7, r6 " ); \ + asm( "adde r8, r8, r5 " ); \ + asm( "lwz r7, 4(r4) " ); \ + asm( "addze r5, r9 " ); \ + asm( "addc r8, r8, r7 " ); \ + asm( "stwu r8, 4(r4) " ); + +#define MULADDC_STOP \ + asm( "addze r5, r5 " ); \ + asm( "addi r4, r4, 4 " ); \ + asm( "addi r3, r3, 4 " ); \ + asm( "stw r5, %0 " : "=m" (c)); \ + asm( "stw r4, %0 " : "=m" (d)); \ + asm( "stw r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#else + +#define MULADDC_INIT \ + asm( "lwz %%r3, %0 " :: "m" (s)); \ + asm( "lwz %%r4, %0 " :: "m" (d)); \ + asm( "lwz %%r5, %0 " :: "m" (c)); \ + asm( "lwz %%r6, %0 " :: "m" (b)); \ + asm( "addi %r3, %r3, -4 " ); \ + asm( "addi %r4, %r4, -4 " ); \ + asm( "addic %r5, %r5, 0 " ); + +#define MULADDC_CORE \ + asm( "lwzu %r7, 4(%r3) " ); \ + asm( "mullw %r8, %r7, %r6 " ); \ + asm( "mulhwu %r9, %r7, %r6 " ); \ + asm( "adde %r8, %r8, %r5 " ); \ + asm( "lwz %r7, 4(%r4) " ); \ + asm( "addze %r5, %r9 " ); \ + asm( "addc %r8, %r8, %r7 " ); \ + asm( "stwu %r8, 4(%r4) " ); + +#define MULADDC_STOP \ + asm( "addze %r5, %r5 " ); \ + asm( "addi %r4, %r4, 4 " ); \ + asm( "addi %r3, %r3, 4 " ); \ + asm( "stw %%r5, %0 " : "=m" (c)); \ + asm( "stw %%r4, %0 " : "=m" (d)); \ + asm( "stw %%r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#endif + +#endif /* PPC32 */ +#endif /* PPC64 */ + +#if defined(__sparc__) && defined(__sparc64__) + +#define MULADDC_INIT \ + asm( \ + " \ + ldx %3, %%o0; \ + ldx %4, %%o1; \ + ld %5, %%o2; \ + ld %6, %%o3; \ + " + +#define MULADDC_CORE \ + " \ + ld [%%o0], %%o4; \ + inc 4, %%o0; \ + ld [%%o1], %%o5; \ + umul %%o3, %%o4, %%o4; \ + addcc %%o4, %%o2, %%o4; \ + rd %%y, %%g1; \ + addx %%g1, 0, %%g1; \ + addcc %%o4, %%o5, %%o4; \ + st %%o4, [%%o1]; \ + addx %%g1, 0, %%o2; \ + inc 4, %%o1; \ + " + +#define MULADDC_STOP \ + " \ + st %%o2, %0; \ + stx %%o1, %1; \ + stx %%o0, %2; \ + " \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "g1", "o0", "o1", "o2", "o3", "o4", \ + "o5" \ + ); +#endif /* SPARCv9 */ + +#if defined(__sparc__) && !defined(__sparc64__) + +#define MULADDC_INIT \ + asm( \ + " \ + ld %3, %%o0; \ + ld %4, %%o1; \ + ld %5, %%o2; \ + ld %6, %%o3; \ + " + +#define MULADDC_CORE \ + " \ + ld [%%o0], %%o4; \ + inc 4, %%o0; \ + ld [%%o1], %%o5; \ + umul %%o3, %%o4, %%o4; \ + addcc %%o4, %%o2, %%o4; \ + rd %%y, %%g1; \ + addx %%g1, 0, %%g1; \ + addcc %%o4, %%o5, %%o4; \ + st %%o4, [%%o1]; \ + addx %%g1, 0, %%o2; \ + inc 4, %%o1; \ + " + +#define MULADDC_STOP \ + " \ + st %%o2, %0; \ + st %%o1, %1; \ + st %%o0, %2; \ + " \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "g1", "o0", "o1", "o2", "o3", "o4", \ + "o5" \ + ); + +#endif /* SPARCv8 */ + +#if defined(__microblaze__) || defined(microblaze) + +#define MULADDC_INIT \ + asm( "lwi r3, %0 " :: "m" (s)); \ + asm( "lwi r4, %0 " :: "m" (d)); \ + asm( "lwi r5, %0 " :: "m" (c)); \ + asm( "lwi r6, %0 " :: "m" (b)); \ + asm( "andi r7, r6, 0xffff" ); \ + asm( "bsrli r6, r6, 16 " ); + +#define MULADDC_CORE \ + asm( "lhui r8, r3, 0 " ); \ + asm( "addi r3, r3, 2 " ); \ + asm( "lhui r9, r3, 0 " ); \ + asm( "addi r3, r3, 2 " ); \ + asm( "mul r10, r9, r6 " ); \ + asm( "mul r11, r8, r7 " ); \ + asm( "mul r12, r9, r7 " ); \ + asm( "mul r13, r8, r6 " ); \ + asm( "bsrli r8, r10, 16 " ); \ + asm( "bsrli r9, r11, 16 " ); \ + asm( "add r13, r13, r8 " ); \ + asm( "add r13, r13, r9 " ); \ + asm( "bslli r10, r10, 16 " ); \ + asm( "bslli r11, r11, 16 " ); \ + asm( "add r12, r12, r10 " ); \ + asm( "addc r13, r13, r0 " ); \ + asm( "add r12, r12, r11 " ); \ + asm( "addc r13, r13, r0 " ); \ + asm( "lwi r10, r4, 0 " ); \ + asm( "add r12, r12, r10 " ); \ + asm( "addc r13, r13, r0 " ); \ + asm( "add r12, r12, r5 " ); \ + asm( "addc r5, r13, r0 " ); \ + asm( "swi r12, r4, 0 " ); \ + asm( "addi r4, r4, 4 " ); + +#define MULADDC_STOP \ + asm( "swi r5, %0 " : "=m" (c)); \ + asm( "swi r4, %0 " : "=m" (d)); \ + asm( "swi r3, %0 " : "=m" (s) :: \ + "r3", "r4" , "r5" , "r6" , "r7" , "r8" , \ + "r9", "r10", "r11", "r12", "r13" ); + +#endif /* MicroBlaze */ + +#if defined(__tricore__) + +#define MULADDC_INIT \ + asm( "ld.a %%a2, %0 " :: "m" (s)); \ + asm( "ld.a %%a3, %0 " :: "m" (d)); \ + asm( "ld.w %%d4, %0 " :: "m" (c)); \ + asm( "ld.w %%d1, %0 " :: "m" (b)); \ + asm( "xor %d5, %d5 " ); + +#define MULADDC_CORE \ + asm( "ld.w %d0, [%a2+] " ); \ + asm( "madd.u %e2, %e4, %d0, %d1 " ); \ + asm( "ld.w %d0, [%a3] " ); \ + asm( "addx %d2, %d2, %d0 " ); \ + asm( "addc %d3, %d3, 0 " ); \ + asm( "mov %d4, %d3 " ); \ + asm( "st.w [%a3+], %d2 " ); + +#define MULADDC_STOP \ + asm( "st.w %0, %%d4 " : "=m" (c)); \ + asm( "st.a %0, %%a3 " : "=m" (d)); \ + asm( "st.a %0, %%a2 " : "=m" (s) :: \ + "d0", "d1", "e2", "d4", "a2", "a3" ); + +#endif /* TriCore */ + +#if defined(__arm__) + +#if defined(__thumb__) && !defined(__thumb2__) + +#define MULADDC_INIT \ + asm( \ + " \ + ldr r0, %3; \ + ldr r1, %4; \ + ldr r2, %5; \ + ldr r3, %6; \ + lsr r7, r3, #16; \ + mov r9, r7; \ + lsl r7, r3, #16; \ + lsr r7, r7, #16; \ + mov r8, r7; \ + " + +#define MULADDC_CORE \ + " \ + ldmia r0!, {r6}; \ + lsr r7, r6, #16; \ + lsl r6, r6, #16; \ + lsr r6, r6, #16; \ + mov r4, r8; \ + mul r4, r6; \ + mov r3, r9; \ + mul r6, r3; \ + mov r5, r9; \ + mul r5, r7; \ + mov r3, r8; \ + mul r7, r3; \ + lsr r3, r6, #16; \ + add r5, r5, r3; \ + lsr r3, r7, #16; \ + add r5, r5, r3; \ + add r4, r4, r2; \ + mov r2, #0; \ + adc r5, r2; \ + lsl r3, r6, #16; \ + add r4, r4, r3; \ + adc r5, r2; \ + lsl r3, r7, #16; \ + add r4, r4, r3; \ + adc r5, r2; \ + ldr r3, [r1]; \ + add r4, r4, r3; \ + adc r2, r5; \ + stmia r1!, {r4}; \ + " + +#define MULADDC_STOP \ + " \ + str r2, %0; \ + str r1, %1; \ + str r0, %2; \ + " \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "r0", "r1", "r2", "r3", "r4", "r5", \ + "r6", "r7", "r8", "r9", "cc" \ + ); + +#else + +#define MULADDC_INIT \ + asm( \ + " \ + ldr r0, %3; \ + ldr r1, %4; \ + ldr r2, %5; \ + ldr r3, %6; \ + " + +#define MULADDC_CORE \ + " \ + ldr r4, [r0], #4; \ + mov r5, #0; \ + ldr r6, [r1]; \ + umlal r2, r5, r3, r4; \ + adds r7, r6, r2; \ + adc r2, r5, #0; \ + str r7, [r1], #4; \ + " + +#define MULADDC_STOP \ + " \ + str r2, %0; \ + str r1, %1; \ + str r0, %2; \ + " \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "r0", "r1", "r2", "r3", "r4", "r5", \ + "r6", "r7", "cc" \ + ); + +#endif /* Thumb */ + +#endif /* ARMv3 */ + +#if defined(__alpha__) + +#define MULADDC_INIT \ + asm( "ldq $1, %0 " :: "m" (s)); \ + asm( "ldq $2, %0 " :: "m" (d)); \ + asm( "ldq $3, %0 " :: "m" (c)); \ + asm( "ldq $4, %0 " :: "m" (b)); + +#define MULADDC_CORE \ + asm( "ldq $6, 0($1) " ); \ + asm( "addq $1, 8, $1 " ); \ + asm( "mulq $6, $4, $7 " ); \ + asm( "umulh $6, $4, $6 " ); \ + asm( "addq $7, $3, $7 " ); \ + asm( "cmpult $7, $3, $3 " ); \ + asm( "ldq $5, 0($2) " ); \ + asm( "addq $7, $5, $7 " ); \ + asm( "cmpult $7, $5, $5 " ); \ + asm( "stq $7, 0($2) " ); \ + asm( "addq $2, 8, $2 " ); \ + asm( "addq $6, $3, $3 " ); \ + asm( "addq $5, $3, $3 " ); + +#define MULADDC_STOP \ + asm( "stq $3, %0 " : "=m" (c)); \ + asm( "stq $2, %0 " : "=m" (d)); \ + asm( "stq $1, %0 " : "=m" (s) :: \ + "$1", "$2", "$3", "$4", "$5", "$6", "$7" ); + +#endif /* Alpha */ + +#if defined(__mips__) + +#define MULADDC_INIT \ + asm( "lw $10, %0 " :: "m" (s)); \ + asm( "lw $11, %0 " :: "m" (d)); \ + asm( "lw $12, %0 " :: "m" (c)); \ + asm( "lw $13, %0 " :: "m" (b)); + +#define MULADDC_CORE \ + asm( "lw $14, 0($10) " ); \ + asm( "multu $13, $14 " ); \ + asm( "addi $10, $10, 4 " ); \ + asm( "mflo $14 " ); \ + asm( "mfhi $9 " ); \ + asm( "addu $14, $12, $14 " ); \ + asm( "lw $15, 0($11) " ); \ + asm( "sltu $12, $14, $12 " ); \ + asm( "addu $15, $14, $15 " ); \ + asm( "sltu $14, $15, $14 " ); \ + asm( "addu $12, $12, $9 " ); \ + asm( "sw $15, 0($11) " ); \ + asm( "addu $12, $12, $14 " ); \ + asm( "addi $11, $11, 4 " ); + +#define MULADDC_STOP \ + asm( "sw $12, %0 " : "=m" (c)); \ + asm( "sw $11, %0 " : "=m" (d)); \ + asm( "sw $10, %0 " : "=m" (s) :: \ + "$9", "$10", "$11", "$12", "$13", "$14", "$15" ); + +#endif /* MIPS */ +#endif /* GNUC */ + +#if (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__) + +#define MULADDC_INIT \ + __asm mov esi, s \ + __asm mov edi, d \ + __asm mov ecx, c \ + __asm mov ebx, b + +#define MULADDC_CORE \ + __asm lodsd \ + __asm mul ebx \ + __asm add eax, ecx \ + __asm adc edx, 0 \ + __asm add eax, [edi] \ + __asm adc edx, 0 \ + __asm mov ecx, edx \ + __asm stosd + +#if defined(POLARSSL_HAVE_SSE2) + +#define EMIT __asm _emit + +#define MULADDC_HUIT \ + EMIT 0x0F EMIT 0x6E EMIT 0xC9 \ + EMIT 0x0F EMIT 0x6E EMIT 0xC3 \ + EMIT 0x0F EMIT 0x6E EMIT 0x1F \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x6E EMIT 0x16 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x66 EMIT 0x04 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xE0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x76 EMIT 0x08 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x7E EMIT 0x0C \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF8 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCA \ + EMIT 0x0F EMIT 0x6E EMIT 0x5F EMIT 0x04 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xDC \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x08 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xEE \ + EMIT 0x0F EMIT 0x6E EMIT 0x67 EMIT 0x0C \ + EMIT 0x0F EMIT 0xD4 EMIT 0xFC \ + EMIT 0x0F EMIT 0x7E EMIT 0x0F \ + EMIT 0x0F EMIT 0x6E EMIT 0x56 EMIT 0x10 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD0 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x6E EMIT 0x66 EMIT 0x14 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xE0 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x6E EMIT 0x76 EMIT 0x18 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF0 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x04 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x6E EMIT 0x5E EMIT 0x1C \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD8 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCD \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x10 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xD5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x08 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCF \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x14 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xE5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x0C \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCA \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x18 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xF5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x10 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCC \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x1C \ + EMIT 0x0F EMIT 0xD4 EMIT 0xDD \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x14 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCE \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x18 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x1C \ + EMIT 0x83 EMIT 0xC7 EMIT 0x20 \ + EMIT 0x83 EMIT 0xC6 EMIT 0x20 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x7E EMIT 0xC9 + +#define MULADDC_STOP \ + EMIT 0x0F EMIT 0x77 \ + __asm mov c, ecx \ + __asm mov d, edi \ + __asm mov s, esi \ + +#else + +#define MULADDC_STOP \ + __asm mov c, ecx \ + __asm mov d, edi \ + __asm mov s, esi \ + +#endif /* SSE2 */ +#endif /* MSVC */ + +#endif /* POLARSSL_HAVE_ASM */ + +#if !defined(MULADDC_CORE) +#if defined(POLARSSL_HAVE_UDBL) + +#define MULADDC_INIT \ +{ \ + t_udbl r; \ + t_uint r0, r1; + +#define MULADDC_CORE \ + r = *(s++) * (t_udbl) b; \ + r0 = r; \ + r1 = r >> biL; \ + r0 += c; r1 += (r0 < c); \ + r0 += *d; r1 += (r0 < *d); \ + c = r1; *(d++) = r0; + +#define MULADDC_STOP \ +} + +#else +#define MULADDC_INIT \ +{ \ + t_uint s0, s1, b0, b1; \ + t_uint r0, r1, rx, ry; \ + b0 = ( b << biH ) >> biH; \ + b1 = ( b >> biH ); + +#define MULADDC_CORE \ + s0 = ( *s << biH ) >> biH; \ + s1 = ( *s >> biH ); s++; \ + rx = s0 * b1; r0 = s0 * b0; \ + ry = s1 * b0; r1 = s1 * b1; \ + r1 += ( rx >> biH ); \ + r1 += ( ry >> biH ); \ + rx <<= biH; ry <<= biH; \ + r0 += rx; r1 += (r0 < rx); \ + r0 += ry; r1 += (r0 < ry); \ + r0 += c; r1 += (r0 < c); \ + r0 += *d; r1 += (r0 < *d); \ + c = r1; *(d++) = r0; + +#define MULADDC_STOP \ +} + +#endif /* C (generic) */ +#endif /* C (longlong) */ + +#endif /* bn_mul.h */ diff --git a/common/polarssl/des.c b/common/polarssl/des.c index 746752d7..b33deb6c 100644 --- a/common/polarssl/des.c +++ b/common/polarssl/des.c @@ -29,7 +29,7 @@ * http://csrc.nist.gov/publications/fips/fips46-3/fips46-3.pdf */ -//#include "polarssl/config.h" +#include "polarssl_config.h" #define POLARSSL_DES_C #if defined(POLARSSL_DES_C) diff --git a/common/polarssl/polarssl_config.h b/common/polarssl/polarssl_config.h index 5bd88fc0..3d9a2f67 100644 --- a/common/polarssl/polarssl_config.h +++ b/common/polarssl/polarssl_config.h @@ -82,7 +82,7 @@ * * Comment to disable the use of assembly code. */ -#define POLARSSL_HAVE_ASM +//#define POLARSSL_HAVE_ASM /** * \def POLARSSL_HAVE_SSE2 @@ -762,7 +762,7 @@ * * This enables support for RSAES-OAEP and RSASSA-PSS operations. */ -#define POLARSSL_PKCS1_V21 +//#define POLARSSL_PKCS1_V21 /** * \def POLARSSL_RSA_NO_CRT diff --git a/common/polarssl/rsa.c b/common/polarssl/rsa.c new file mode 100644 index 00000000..9872274b --- /dev/null +++ b/common/polarssl/rsa.c @@ -0,0 +1,1466 @@ +/* + * The RSA public-key cryptosystem + * + * Copyright (C) 2006-2011, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * RSA was designed by Ron Rivest, Adi Shamir and Len Adleman. + * + * http://theory.lcs.mit.edu/~rivest/rsapaper.pdf + * http://www.cacr.math.uwaterloo.ca/hac/about/chap8.pdf + */ + +#include "polarssl_config.h" + +#if defined(POLARSSL_RSA_C) + +#include "rsa.h" + +#if defined(POLARSSL_PKCS1_V21) +#include "md.h" +#endif + +#include +#include + +/* + * Initialize an RSA context + */ +void rsa_init( rsa_context *ctx, + int padding, + int hash_id ) +{ + memset( ctx, 0, sizeof( rsa_context ) ); + + ctx->padding = padding; + ctx->hash_id = hash_id; +} + +#if defined(POLARSSL_GENPRIME) + +/* + * Generate an RSA keypair + */ +int rsa_gen_key( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + unsigned int nbits, int exponent ) +{ + int ret; + mpi P1, Q1, H, G; + + if( f_rng == NULL || nbits < 128 || exponent < 3 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + mpi_init( &P1 ); mpi_init( &Q1 ); mpi_init( &H ); mpi_init( &G ); + + /* + * find primes P and Q with Q < P so that: + * GCD( E, (P-1)*(Q-1) ) == 1 + */ + MPI_CHK( mpi_lset( &ctx->E, exponent ) ); + + do + { + MPI_CHK( mpi_gen_prime( &ctx->P, ( nbits + 1 ) >> 1, 0, + f_rng, p_rng ) ); + + MPI_CHK( mpi_gen_prime( &ctx->Q, ( nbits + 1 ) >> 1, 0, + f_rng, p_rng ) ); + + if( mpi_cmp_mpi( &ctx->P, &ctx->Q ) < 0 ) + mpi_swap( &ctx->P, &ctx->Q ); + + if( mpi_cmp_mpi( &ctx->P, &ctx->Q ) == 0 ) + continue; + + MPI_CHK( mpi_mul_mpi( &ctx->N, &ctx->P, &ctx->Q ) ); + if( mpi_msb( &ctx->N ) != nbits ) + continue; + + MPI_CHK( mpi_sub_int( &P1, &ctx->P, 1 ) ); + MPI_CHK( mpi_sub_int( &Q1, &ctx->Q, 1 ) ); + MPI_CHK( mpi_mul_mpi( &H, &P1, &Q1 ) ); + MPI_CHK( mpi_gcd( &G, &ctx->E, &H ) ); + } + while( mpi_cmp_int( &G, 1 ) != 0 ); + + /* + * D = E^-1 mod ((P-1)*(Q-1)) + * DP = D mod (P - 1) + * DQ = D mod (Q - 1) + * QP = Q^-1 mod P + */ + MPI_CHK( mpi_inv_mod( &ctx->D , &ctx->E, &H ) ); + MPI_CHK( mpi_mod_mpi( &ctx->DP, &ctx->D, &P1 ) ); + MPI_CHK( mpi_mod_mpi( &ctx->DQ, &ctx->D, &Q1 ) ); + MPI_CHK( mpi_inv_mod( &ctx->QP, &ctx->Q, &ctx->P ) ); + + ctx->len = ( mpi_msb( &ctx->N ) + 7 ) >> 3; + +cleanup: + + mpi_free( &P1 ); mpi_free( &Q1 ); mpi_free( &H ); mpi_free( &G ); + + if( ret != 0 ) + { + rsa_free( ctx ); + return( POLARSSL_ERR_RSA_KEY_GEN_FAILED + ret ); + } + + return( 0 ); +} + +#endif + +/* + * Check a public RSA key + */ +int rsa_check_pubkey( const rsa_context *ctx ) +{ + if( !ctx->N.p || !ctx->E.p ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + if( ( ctx->N.p[0] & 1 ) == 0 || + ( ctx->E.p[0] & 1 ) == 0 ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + if( mpi_msb( &ctx->N ) < 128 || + mpi_msb( &ctx->N ) > POLARSSL_MPI_MAX_BITS ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + if( mpi_msb( &ctx->E ) < 2 || + mpi_msb( &ctx->E ) > 64 ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + return( 0 ); +} + +/* + * Check a private RSA key + */ +int rsa_check_privkey( const rsa_context *ctx ) +{ + int ret; + mpi PQ, DE, P1, Q1, H, I, G, G2, L1, L2, DP, DQ, QP; + + if( ( ret = rsa_check_pubkey( ctx ) ) != 0 ) + return( ret ); + + if( !ctx->P.p || !ctx->Q.p || !ctx->D.p ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + mpi_init( &PQ ); mpi_init( &DE ); mpi_init( &P1 ); mpi_init( &Q1 ); + mpi_init( &H ); mpi_init( &I ); mpi_init( &G ); mpi_init( &G2 ); + mpi_init( &L1 ); mpi_init( &L2 ); mpi_init( &DP ); mpi_init( &DQ ); + mpi_init( &QP ); + + MPI_CHK( mpi_mul_mpi( &PQ, &ctx->P, &ctx->Q ) ); + MPI_CHK( mpi_mul_mpi( &DE, &ctx->D, &ctx->E ) ); + MPI_CHK( mpi_sub_int( &P1, &ctx->P, 1 ) ); + MPI_CHK( mpi_sub_int( &Q1, &ctx->Q, 1 ) ); + MPI_CHK( mpi_mul_mpi( &H, &P1, &Q1 ) ); + MPI_CHK( mpi_gcd( &G, &ctx->E, &H ) ); + + MPI_CHK( mpi_gcd( &G2, &P1, &Q1 ) ); + MPI_CHK( mpi_div_mpi( &L1, &L2, &H, &G2 ) ); + MPI_CHK( mpi_mod_mpi( &I, &DE, &L1 ) ); + + MPI_CHK( mpi_mod_mpi( &DP, &ctx->D, &P1 ) ); + MPI_CHK( mpi_mod_mpi( &DQ, &ctx->D, &Q1 ) ); + MPI_CHK( mpi_inv_mod( &QP, &ctx->Q, &ctx->P ) ); + /* + * Check for a valid PKCS1v2 private key + */ + if( mpi_cmp_mpi( &PQ, &ctx->N ) != 0 || + mpi_cmp_mpi( &DP, &ctx->DP ) != 0 || + mpi_cmp_mpi( &DQ, &ctx->DQ ) != 0 || + mpi_cmp_mpi( &QP, &ctx->QP ) != 0 || + mpi_cmp_int( &L2, 0 ) != 0 || + mpi_cmp_int( &I, 1 ) != 0 || + mpi_cmp_int( &G, 1 ) != 0 ) + { + ret = POLARSSL_ERR_RSA_KEY_CHECK_FAILED; + } + +cleanup: + mpi_free( &PQ ); mpi_free( &DE ); mpi_free( &P1 ); mpi_free( &Q1 ); + mpi_free( &H ); mpi_free( &I ); mpi_free( &G ); mpi_free( &G2 ); + mpi_free( &L1 ); mpi_free( &L2 ); mpi_free( &DP ); mpi_free( &DQ ); + mpi_free( &QP ); + + if( ret == POLARSSL_ERR_RSA_KEY_CHECK_FAILED ) + return( ret ); + + if( ret != 0 ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED + ret ); + + return( 0 ); +} + +/* + * Do an RSA public key operation + */ +int rsa_public( rsa_context *ctx, + const unsigned char *input, + unsigned char *output ) +{ + int ret; + size_t olen; + mpi T; + + mpi_init( &T ); + + MPI_CHK( mpi_read_binary( &T, input, ctx->len ) ); + + if( mpi_cmp_mpi( &T, &ctx->N ) >= 0 ) + { + mpi_free( &T ); + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + olen = ctx->len; + MPI_CHK( mpi_exp_mod( &T, &T, &ctx->E, &ctx->N, &ctx->RN ) ); + MPI_CHK( mpi_write_binary( &T, output, olen ) ); + +cleanup: + + mpi_free( &T ); + + if( ret != 0 ) + return( POLARSSL_ERR_RSA_PUBLIC_FAILED + ret ); + + return( 0 ); +} + +/* + * Do an RSA private key operation + */ +int rsa_private( rsa_context *ctx, + const unsigned char *input, + unsigned char *output ) +{ + int ret; + size_t olen; + mpi T, T1, T2; + + mpi_init( &T ); mpi_init( &T1 ); mpi_init( &T2 ); + + MPI_CHK( mpi_read_binary( &T, input, ctx->len ) ); + + if( mpi_cmp_mpi( &T, &ctx->N ) >= 0 ) + { + mpi_free( &T ); + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + +#if defined(POLARSSL_RSA_NO_CRT) + MPI_CHK( mpi_exp_mod( &T, &T, &ctx->D, &ctx->N, &ctx->RN ) ); +#else + /* + * faster decryption using the CRT + * + * T1 = input ^ dP mod P + * T2 = input ^ dQ mod Q + */ + MPI_CHK( mpi_exp_mod( &T1, &T, &ctx->DP, &ctx->P, &ctx->RP ) ); + MPI_CHK( mpi_exp_mod( &T2, &T, &ctx->DQ, &ctx->Q, &ctx->RQ ) ); + + /* + * T = (T1 - T2) * (Q^-1 mod P) mod P + */ + MPI_CHK( mpi_sub_mpi( &T, &T1, &T2 ) ); + MPI_CHK( mpi_mul_mpi( &T1, &T, &ctx->QP ) ); + MPI_CHK( mpi_mod_mpi( &T, &T1, &ctx->P ) ); + + /* + * output = T2 + T * Q + */ + MPI_CHK( mpi_mul_mpi( &T1, &T, &ctx->Q ) ); + MPI_CHK( mpi_add_mpi( &T, &T2, &T1 ) ); +#endif + + olen = ctx->len; + MPI_CHK( mpi_write_binary( &T, output, olen ) ); + +cleanup: + + mpi_free( &T ); mpi_free( &T1 ); mpi_free( &T2 ); + + if( ret != 0 ) + return( POLARSSL_ERR_RSA_PRIVATE_FAILED + ret ); + + return( 0 ); +} + +#if defined(POLARSSL_PKCS1_V21) +/** + * Generate and apply the MGF1 operation (from PKCS#1 v2.1) to a buffer. + * + * \param dst buffer to mask + * \param dlen length of destination buffer + * \param src source of the mask generation + * \param slen length of the source buffer + * \param md_ctx message digest context to use + */ +static void mgf_mask( unsigned char *dst, size_t dlen, unsigned char *src, size_t slen, + md_context_t *md_ctx ) +{ + unsigned char mask[POLARSSL_MD_MAX_SIZE]; + unsigned char counter[4]; + unsigned char *p; + unsigned int hlen; + size_t i, use_len; + + memset( mask, 0, POLARSSL_MD_MAX_SIZE ); + memset( counter, 0, 4 ); + + hlen = md_ctx->md_info->size; + + // Generate and apply dbMask + // + p = dst; + + while( dlen > 0 ) + { + use_len = hlen; + if( dlen < hlen ) + use_len = dlen; + + md_starts( md_ctx ); + md_update( md_ctx, src, slen ); + md_update( md_ctx, counter, 4 ); + md_finish( md_ctx, mask ); + + for( i = 0; i < use_len; ++i ) + *p++ ^= mask[i]; + + counter[3]++; + + dlen -= use_len; + } +} +#endif + +#if defined(POLARSSL_PKCS1_V21) +/* + * Implementation of the PKCS#1 v2.1 RSAES-OAEP-ENCRYPT function + */ +int rsa_rsaes_oaep_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + const unsigned char *label, size_t label_len, + size_t ilen, + const unsigned char *input, + unsigned char *output ) +{ + size_t olen; + int ret; + unsigned char *p = output; + unsigned int hlen; + const md_info_t *md_info; + md_context_t md_ctx; + + if( ctx->padding != RSA_PKCS_V21 || f_rng == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + md_info = md_info_from_type( ctx->hash_id ); + + if( md_info == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + olen = ctx->len; + hlen = md_get_size( md_info ); + + if( olen < ilen + 2 * hlen + 2 || f_rng == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + memset( output, 0, olen ); + + *p++ = 0; + + // Generate a random octet string seed + // + if( ( ret = f_rng( p_rng, p, hlen ) ) != 0 ) + return( POLARSSL_ERR_RSA_RNG_FAILED + ret ); + + p += hlen; + + // Construct DB + // + md( md_info, label, label_len, p ); + p += hlen; + p += olen - 2 * hlen - 2 - ilen; + *p++ = 1; + memcpy( p, input, ilen ); + + md_init_ctx( &md_ctx, md_info ); + + // maskedDB: Apply dbMask to DB + // + mgf_mask( output + hlen + 1, olen - hlen - 1, output + 1, hlen, + &md_ctx ); + + // maskedSeed: Apply seedMask to seed + // + mgf_mask( output + 1, hlen, output + hlen + 1, olen - hlen - 1, + &md_ctx ); + + md_free_ctx( &md_ctx ); + + return( ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, output, output ) + : rsa_private( ctx, output, output ) ); +} +#endif /* POLARSSL_PKCS1_V21 */ + +/* + * Implementation of the PKCS#1 v2.1 RSAES-PKCS1-V1_5-ENCRYPT function + */ +int rsa_rsaes_pkcs1_v15_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t ilen, + const unsigned char *input, + unsigned char *output ) +{ + size_t nb_pad, olen; + int ret; + unsigned char *p = output; + + if( ctx->padding != RSA_PKCS_V15 || f_rng == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + olen = ctx->len; + + if( olen < ilen + 11 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + nb_pad = olen - 3 - ilen; + + *p++ = 0; + if( mode == RSA_PUBLIC ) + { + *p++ = RSA_CRYPT; + + while( nb_pad-- > 0 ) + { + int rng_dl = 100; + + do { + ret = f_rng( p_rng, p, 1 ); + } while( *p == 0 && --rng_dl && ret == 0 ); + + // Check if RNG failed to generate data + // + if( rng_dl == 0 || ret != 0) + return POLARSSL_ERR_RSA_RNG_FAILED + ret; + + p++; + } + } + else + { + *p++ = RSA_SIGN; + + while( nb_pad-- > 0 ) + *p++ = 0xFF; + } + + *p++ = 0; + memcpy( p, input, ilen ); + + return( ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, output, output ) + : rsa_private( ctx, output, output ) ); +} + +/* + * Add the message padding, then do an RSA operation + */ +int rsa_pkcs1_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t ilen, + const unsigned char *input, + unsigned char *output ) +{ + switch( ctx->padding ) + { + case RSA_PKCS_V15: + return rsa_rsaes_pkcs1_v15_encrypt( ctx, f_rng, p_rng, mode, ilen, + input, output ); + +#if defined(POLARSSL_PKCS1_V21) + case RSA_PKCS_V21: + return rsa_rsaes_oaep_encrypt( ctx, f_rng, p_rng, mode, NULL, 0, + ilen, input, output ); +#endif + + default: + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } +} + +#if defined(POLARSSL_PKCS1_V21) +/* + * Implementation of the PKCS#1 v2.1 RSAES-OAEP-DECRYPT function + */ +int rsa_rsaes_oaep_decrypt( rsa_context *ctx, + int mode, + const unsigned char *label, size_t label_len, + size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len ) +{ + int ret; + size_t ilen; + unsigned char *p; + unsigned char buf[POLARSSL_MPI_MAX_SIZE]; + unsigned char lhash[POLARSSL_MD_MAX_SIZE]; + unsigned int hlen; + const md_info_t *md_info; + md_context_t md_ctx; + + if( ctx->padding != RSA_PKCS_V21 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ilen = ctx->len; + + if( ilen < 16 || ilen > sizeof( buf ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ret = ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, input, buf ) + : rsa_private( ctx, input, buf ); + + if( ret != 0 ) + return( ret ); + + p = buf; + + if( *p++ != 0 ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + md_info = md_info_from_type( ctx->hash_id ); + if( md_info == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + hlen = md_get_size( md_info ); + + md_init_ctx( &md_ctx, md_info ); + + // Generate lHash + // + md( md_info, label, label_len, lhash ); + + // seed: Apply seedMask to maskedSeed + // + mgf_mask( buf + 1, hlen, buf + hlen + 1, ilen - hlen - 1, + &md_ctx ); + + // DB: Apply dbMask to maskedDB + // + mgf_mask( buf + hlen + 1, ilen - hlen - 1, buf + 1, hlen, + &md_ctx ); + + p += hlen; + md_free_ctx( &md_ctx ); + + // Check validity + // + if( memcmp( lhash, p, hlen ) != 0 ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + p += hlen; + + while( *p == 0 && p < buf + ilen ) + p++; + + if( p == buf + ilen ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + if( *p++ != 0x01 ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + if (ilen - (p - buf) > output_max_len) + return( POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE ); + + *olen = ilen - (p - buf); + memcpy( output, p, *olen ); + + return( 0 ); +} +#endif /* POLARSSL_PKCS1_V21 */ + +/* + * Implementation of the PKCS#1 v2.1 RSAES-PKCS1-V1_5-DECRYPT function + */ +int rsa_rsaes_pkcs1_v15_decrypt( rsa_context *ctx, + int mode, size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len) +{ + int ret, correct = 1; + size_t ilen, pad_count = 0; + unsigned char *p, *q; + unsigned char bt; + unsigned char buf[POLARSSL_MPI_MAX_SIZE]; + + if( ctx->padding != RSA_PKCS_V15 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ilen = ctx->len; + + if( ilen < 16 || ilen > sizeof( buf ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ret = ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, input, buf ) + : rsa_private( ctx, input, buf ); + + if( ret != 0 ) + return( ret ); + + p = buf; + + if( *p++ != 0 ) + correct = 0; + + bt = *p++; + if( ( bt != RSA_CRYPT && mode == RSA_PRIVATE ) || + ( bt != RSA_SIGN && mode == RSA_PUBLIC ) ) + { + correct = 0; + } + + if( bt == RSA_CRYPT ) + { + while( *p != 0 && p < buf + ilen - 1 ) + pad_count += ( *p++ != 0 ); + + correct &= ( *p == 0 && p < buf + ilen - 1 ); + + q = p; + + // Also pass over all other bytes to reduce timing differences + // + while ( q < buf + ilen - 1 ) + pad_count += ( *q++ != 0 ); + + // Prevent compiler optimization of pad_count + // + correct |= pad_count & 0x100000; /* Always 0 unless 1M bit keys */ + p++; + } + else + { + while( *p == 0xFF && p < buf + ilen - 1 ) + pad_count += ( *p++ == 0xFF ); + + correct &= ( *p == 0 && p < buf + ilen - 1 ); + + q = p; + + // Also pass over all other bytes to reduce timing differences + // + while ( q < buf + ilen - 1 ) + pad_count += ( *q++ != 0 ); + + // Prevent compiler optimization of pad_count + // + correct |= pad_count & 0x100000; /* Always 0 unless 1M bit keys */ + p++; + } + + if( correct == 0 ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + if (ilen - (p - buf) > output_max_len) + return( POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE ); + + *olen = ilen - (p - buf); + memcpy( output, p, *olen ); + + return( 0 ); +} + +/* + * Do an RSA operation, then remove the message padding + */ +int rsa_pkcs1_decrypt( rsa_context *ctx, + int mode, size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len) +{ + switch( ctx->padding ) + { + case RSA_PKCS_V15: + return rsa_rsaes_pkcs1_v15_decrypt( ctx, mode, olen, input, output, + output_max_len ); + +#if defined(POLARSSL_PKCS1_V21) + case RSA_PKCS_V21: + return rsa_rsaes_oaep_decrypt( ctx, mode, NULL, 0, olen, input, + output, output_max_len ); +#endif + + default: + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } +} + +#if defined(POLARSSL_PKCS1_V21) +/* + * Implementation of the PKCS#1 v2.1 RSASSA-PSS-SIGN function + */ +int rsa_rsassa_pss_sign( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ) +{ + size_t olen; + unsigned char *p = sig; + unsigned char salt[POLARSSL_MD_MAX_SIZE]; + unsigned int slen, hlen, offset = 0; + int ret; + size_t msb; + const md_info_t *md_info; + md_context_t md_ctx; + + if( ctx->padding != RSA_PKCS_V21 || f_rng == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + olen = ctx->len; + + switch( hash_id ) + { + case SIG_RSA_MD2: + case SIG_RSA_MD4: + case SIG_RSA_MD5: + hashlen = 16; + break; + + case SIG_RSA_SHA1: + hashlen = 20; + break; + + case SIG_RSA_SHA224: + hashlen = 28; + break; + + case SIG_RSA_SHA256: + hashlen = 32; + break; + + case SIG_RSA_SHA384: + hashlen = 48; + break; + + case SIG_RSA_SHA512: + hashlen = 64; + break; + + default: + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + md_info = md_info_from_type( ctx->hash_id ); + if( md_info == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + hlen = md_get_size( md_info ); + slen = hlen; + + if( olen < hlen + slen + 2 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + memset( sig, 0, olen ); + + msb = mpi_msb( &ctx->N ) - 1; + + // Generate salt of length slen + // + if( ( ret = f_rng( p_rng, salt, slen ) ) != 0 ) + return( POLARSSL_ERR_RSA_RNG_FAILED + ret ); + + // Note: EMSA-PSS encoding is over the length of N - 1 bits + // + msb = mpi_msb( &ctx->N ) - 1; + p += olen - hlen * 2 - 2; + *p++ = 0x01; + memcpy( p, salt, slen ); + p += slen; + + md_init_ctx( &md_ctx, md_info ); + + // Generate H = Hash( M' ) + // + md_starts( &md_ctx ); + md_update( &md_ctx, p, 8 ); + md_update( &md_ctx, hash, hashlen ); + md_update( &md_ctx, salt, slen ); + md_finish( &md_ctx, p ); + + // Compensate for boundary condition when applying mask + // + if( msb % 8 == 0 ) + offset = 1; + + // maskedDB: Apply dbMask to DB + // + mgf_mask( sig + offset, olen - hlen - 1 - offset, p, hlen, &md_ctx ); + + md_free_ctx( &md_ctx ); + + msb = mpi_msb( &ctx->N ) - 1; + sig[0] &= 0xFF >> ( olen * 8 - msb ); + + p += hlen; + *p++ = 0xBC; + + return( ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, sig, sig ) + : rsa_private( ctx, sig, sig ) ); +} +#endif /* POLARSSL_PKCS1_V21 */ + +/* + * Implementation of the PKCS#1 v2.1 RSASSA-PKCS1-V1_5-SIGN function + */ +/* + * Do an RSA operation to sign the message digest + */ +int rsa_rsassa_pkcs1_v15_sign( rsa_context *ctx, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ) +{ + size_t nb_pad, olen; + unsigned char *p = sig; + + if( ctx->padding != RSA_PKCS_V15 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + olen = ctx->len; + + switch( hash_id ) + { + case SIG_RSA_RAW: + nb_pad = olen - 3 - hashlen; + break; + + case SIG_RSA_MD2: + case SIG_RSA_MD4: + case SIG_RSA_MD5: + nb_pad = olen - 3 - 34; + break; + + case SIG_RSA_SHA1: + nb_pad = olen - 3 - 35; + break; + + case SIG_RSA_SHA224: + nb_pad = olen - 3 - 47; + break; + + case SIG_RSA_SHA256: + nb_pad = olen - 3 - 51; + break; + + case SIG_RSA_SHA384: + nb_pad = olen - 3 - 67; + break; + + case SIG_RSA_SHA512: + nb_pad = olen - 3 - 83; + break; + + + default: + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + if( ( nb_pad < 8 ) || ( nb_pad > olen ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + *p++ = 0; + *p++ = RSA_SIGN; + memset( p, 0xFF, nb_pad ); + p += nb_pad; + *p++ = 0; + + switch( hash_id ) + { + case SIG_RSA_RAW: + memcpy( p, hash, hashlen ); + break; + + case SIG_RSA_MD2: + memcpy( p, ASN1_HASH_MDX, 18 ); + memcpy( p + 18, hash, 16 ); + p[13] = 2; break; + + case SIG_RSA_MD4: + memcpy( p, ASN1_HASH_MDX, 18 ); + memcpy( p + 18, hash, 16 ); + p[13] = 4; break; + + case SIG_RSA_MD5: + memcpy( p, ASN1_HASH_MDX, 18 ); + memcpy( p + 18, hash, 16 ); + p[13] = 5; break; + + case SIG_RSA_SHA1: + memcpy( p, ASN1_HASH_SHA1, 15 ); + memcpy( p + 15, hash, 20 ); + break; + + case SIG_RSA_SHA224: + memcpy( p, ASN1_HASH_SHA2X, 19 ); + memcpy( p + 19, hash, 28 ); + p[1] += 28; p[14] = 4; p[18] += 28; break; + + case SIG_RSA_SHA256: + memcpy( p, ASN1_HASH_SHA2X, 19 ); + memcpy( p + 19, hash, 32 ); + p[1] += 32; p[14] = 1; p[18] += 32; break; + + case SIG_RSA_SHA384: + memcpy( p, ASN1_HASH_SHA2X, 19 ); + memcpy( p + 19, hash, 48 ); + p[1] += 48; p[14] = 2; p[18] += 48; break; + + case SIG_RSA_SHA512: + memcpy( p, ASN1_HASH_SHA2X, 19 ); + memcpy( p + 19, hash, 64 ); + p[1] += 64; p[14] = 3; p[18] += 64; break; + + default: + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + return( ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, sig, sig ) + : rsa_private( ctx, sig, sig ) ); +} + +/* + * Do an RSA operation to sign the message digest + */ +int rsa_pkcs1_sign( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ) +{ + switch( ctx->padding ) + { + case RSA_PKCS_V15: + return rsa_rsassa_pkcs1_v15_sign( ctx, mode, hash_id, + hashlen, hash, sig ); + +#if defined(POLARSSL_PKCS1_V21) + case RSA_PKCS_V21: + return rsa_rsassa_pss_sign( ctx, f_rng, p_rng, mode, hash_id, + hashlen, hash, sig ); +#endif + + default: + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } +} + +#if defined(POLARSSL_PKCS1_V21) +/* + * Implementation of the PKCS#1 v2.1 RSASSA-PSS-VERIFY function + */ +int rsa_rsassa_pss_verify( rsa_context *ctx, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ) +{ + int ret; + size_t siglen; + unsigned char *p; + unsigned char buf[POLARSSL_MPI_MAX_SIZE]; + unsigned char result[POLARSSL_MD_MAX_SIZE]; + unsigned char zeros[8]; + unsigned int hlen; + size_t slen, msb; + const md_info_t *md_info; + md_context_t md_ctx; + + if( ctx->padding != RSA_PKCS_V21 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + siglen = ctx->len; + + if( siglen < 16 || siglen > sizeof( buf ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ret = ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, sig, buf ) + : rsa_private( ctx, sig, buf ); + + if( ret != 0 ) + return( ret ); + + p = buf; + + if( buf[siglen - 1] != 0xBC ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + switch( hash_id ) + { + case SIG_RSA_MD2: + case SIG_RSA_MD4: + case SIG_RSA_MD5: + hashlen = 16; + break; + + case SIG_RSA_SHA1: + hashlen = 20; + break; + + case SIG_RSA_SHA224: + hashlen = 28; + break; + + case SIG_RSA_SHA256: + hashlen = 32; + break; + + case SIG_RSA_SHA384: + hashlen = 48; + break; + + case SIG_RSA_SHA512: + hashlen = 64; + break; + + default: + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + md_info = md_info_from_type( ctx->hash_id ); + if( md_info == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + hlen = md_get_size( md_info ); + slen = siglen - hlen - 1; + + memset( zeros, 0, 8 ); + + // Note: EMSA-PSS verification is over the length of N - 1 bits + // + msb = mpi_msb( &ctx->N ) - 1; + + // Compensate for boundary condition when applying mask + // + if( msb % 8 == 0 ) + { + p++; + siglen -= 1; + } + if( buf[0] >> ( 8 - siglen * 8 + msb ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + md_init_ctx( &md_ctx, md_info ); + + mgf_mask( p, siglen - hlen - 1, p + siglen - hlen - 1, hlen, &md_ctx ); + + buf[0] &= 0xFF >> ( siglen * 8 - msb ); + + while( *p == 0 && p < buf + siglen ) + p++; + + if( p == buf + siglen || + *p++ != 0x01 ) + { + md_free_ctx( &md_ctx ); + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } + + slen -= p - buf; + + // Generate H = Hash( M' ) + // + md_starts( &md_ctx ); + md_update( &md_ctx, zeros, 8 ); + md_update( &md_ctx, hash, hashlen ); + md_update( &md_ctx, p, slen ); + md_finish( &md_ctx, result ); + + md_free_ctx( &md_ctx ); + + if( memcmp( p + slen, result, hlen ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); +} +#endif /* POLARSSL_PKCS1_V21 */ + +/* + * Implementation of the PKCS#1 v2.1 RSASSA-PKCS1-v1_5-VERIFY function + */ +int rsa_rsassa_pkcs1_v15_verify( rsa_context *ctx, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ) +{ + int ret; + size_t len, siglen; + unsigned char *p, c; + unsigned char buf[POLARSSL_MPI_MAX_SIZE]; + + if( ctx->padding != RSA_PKCS_V15 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + siglen = ctx->len; + + if( siglen < 16 || siglen > sizeof( buf ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ret = ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, sig, buf ) + : rsa_private( ctx, sig, buf ); + + if( ret != 0 ) + return( ret ); + + p = buf; + + if( *p++ != 0 || *p++ != RSA_SIGN ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + while( *p != 0 ) + { + if( p >= buf + siglen - 1 || *p != 0xFF ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + p++; + } + p++; + + len = siglen - ( p - buf ); + + if( len == 33 && hash_id == SIG_RSA_SHA1 ) + { + if( memcmp( p, ASN1_HASH_SHA1_ALT, 13 ) == 0 && + memcmp( p + 13, hash, 20 ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + if( len == 34 ) + { + c = p[13]; + p[13] = 0; + + if( memcmp( p, ASN1_HASH_MDX, 18 ) != 0 ) + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + + if( ( c == 2 && hash_id == SIG_RSA_MD2 ) || + ( c == 4 && hash_id == SIG_RSA_MD4 ) || + ( c == 5 && hash_id == SIG_RSA_MD5 ) ) + { + if( memcmp( p + 18, hash, 16 ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + } + + if( len == 35 && hash_id == SIG_RSA_SHA1 ) + { + if( memcmp( p, ASN1_HASH_SHA1, 15 ) == 0 && + memcmp( p + 15, hash, 20 ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + if( ( len == 19 + 28 && p[14] == 4 && hash_id == SIG_RSA_SHA224 ) || + ( len == 19 + 32 && p[14] == 1 && hash_id == SIG_RSA_SHA256 ) || + ( len == 19 + 48 && p[14] == 2 && hash_id == SIG_RSA_SHA384 ) || + ( len == 19 + 64 && p[14] == 3 && hash_id == SIG_RSA_SHA512 ) ) + { + c = p[1] - 17; + p[1] = 17; + p[14] = 0; + + if( p[18] == c && + memcmp( p, ASN1_HASH_SHA2X, 18 ) == 0 && + memcmp( p + 19, hash, c ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + + if( len == hashlen && hash_id == SIG_RSA_RAW ) + { + if( memcmp( p, hash, hashlen ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + + return( POLARSSL_ERR_RSA_INVALID_PADDING ); +} + +/* + * Do an RSA operation and check the message digest + */ +int rsa_pkcs1_verify( rsa_context *ctx, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ) +{ + switch( ctx->padding ) + { + case RSA_PKCS_V15: + return rsa_rsassa_pkcs1_v15_verify( ctx, mode, hash_id, + hashlen, hash, sig ); + +#if defined(POLARSSL_PKCS1_V21) + case RSA_PKCS_V21: + return rsa_rsassa_pss_verify( ctx, mode, hash_id, + hashlen, hash, sig ); +#endif + + default: + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } +} + +/* + * Free the components of an RSA key + */ +void rsa_free( rsa_context *ctx ) +{ + mpi_free( &ctx->RQ ); mpi_free( &ctx->RP ); mpi_free( &ctx->RN ); + mpi_free( &ctx->QP ); mpi_free( &ctx->DQ ); mpi_free( &ctx->DP ); + mpi_free( &ctx->Q ); mpi_free( &ctx->P ); mpi_free( &ctx->D ); + mpi_free( &ctx->E ); mpi_free( &ctx->N ); +} + +#if defined(POLARSSL_SELF_TEST) + +#include "polarssl/sha1.h" + +/* + * Example RSA-1024 keypair, for test purposes + */ +#define KEY_LEN 128 + +#define RSA_N "9292758453063D803DD603D5E777D788" \ + "8ED1D5BF35786190FA2F23EBC0848AEA" \ + "DDA92CA6C3D80B32C4D109BE0F36D6AE" \ + "7130B9CED7ACDF54CFC7555AC14EEBAB" \ + "93A89813FBF3C4F8066D2D800F7C38A8" \ + "1AE31942917403FF4946B0A83D3D3E05" \ + "EE57C6F5F5606FB5D4BC6CD34EE0801A" \ + "5E94BB77B07507233A0BC7BAC8F90F79" + +#define RSA_E "10001" + +#define RSA_D "24BF6185468786FDD303083D25E64EFC" \ + "66CA472BC44D253102F8B4A9D3BFA750" \ + "91386C0077937FE33FA3252D28855837" \ + "AE1B484A8A9A45F7EE8C0C634F99E8CD" \ + "DF79C5CE07EE72C7F123142198164234" \ + "CABB724CF78B8173B9F880FC86322407" \ + "AF1FEDFDDE2BEB674CA15F3E81A1521E" \ + "071513A1E85B5DFA031F21ECAE91A34D" + +#define RSA_P "C36D0EB7FCD285223CFB5AABA5BDA3D8" \ + "2C01CAD19EA484A87EA4377637E75500" \ + "FCB2005C5C7DD6EC4AC023CDA285D796" \ + "C3D9E75E1EFC42488BB4F1D13AC30A57" + +#define RSA_Q "C000DF51A7C77AE8D7C7370C1FF55B69" \ + "E211C2B9E5DB1ED0BF61D0D9899620F4" \ + "910E4168387E3C30AA1E00C339A79508" \ + "8452DD96A9A5EA5D9DCA68DA636032AF" + +#define RSA_DP "C1ACF567564274FB07A0BBAD5D26E298" \ + "3C94D22288ACD763FD8E5600ED4A702D" \ + "F84198A5F06C2E72236AE490C93F07F8" \ + "3CC559CD27BC2D1CA488811730BB5725" + +#define RSA_DQ "4959CBF6F8FEF750AEE6977C155579C7" \ + "D8AAEA56749EA28623272E4F7D0592AF" \ + "7C1F1313CAC9471B5C523BFE592F517B" \ + "407A1BD76C164B93DA2D32A383E58357" + +#define RSA_QP "9AE7FBC99546432DF71896FC239EADAE" \ + "F38D18D2B2F0E2DD275AA977E2BF4411" \ + "F5A3B2A5D33605AEBBCCBA7FEB9F2D2F" \ + "A74206CEC169D74BF5A8C50D6F48EA08" + +#define PT_LEN 24 +#define RSA_PT "\xAA\xBB\xCC\x03\x02\x01\x00\xFF\xFF\xFF\xFF\xFF" \ + "\x11\x22\x33\x0A\x0B\x0C\xCC\xDD\xDD\xDD\xDD\xDD" + +static int myrand( void *rng_state, unsigned char *output, size_t len ) +{ + size_t i; + + if( rng_state != NULL ) + rng_state = NULL; + + for( i = 0; i < len; ++i ) + output[i] = rand(); + + return( 0 ); +} + +/* + * Checkup routine + */ +int rsa_self_test( int verbose ) +{ + size_t len; + rsa_context rsa; + unsigned char rsa_plaintext[PT_LEN]; + unsigned char rsa_decrypted[PT_LEN]; + unsigned char rsa_ciphertext[KEY_LEN]; +#if defined(POLARSSL_SHA1_C) + unsigned char sha1sum[20]; +#endif + + rsa_init( &rsa, RSA_PKCS_V15, 0 ); + + rsa.len = KEY_LEN; + mpi_read_string( &rsa.N , 16, RSA_N ); + mpi_read_string( &rsa.E , 16, RSA_E ); + mpi_read_string( &rsa.D , 16, RSA_D ); + mpi_read_string( &rsa.P , 16, RSA_P ); + mpi_read_string( &rsa.Q , 16, RSA_Q ); + mpi_read_string( &rsa.DP, 16, RSA_DP ); + mpi_read_string( &rsa.DQ, 16, RSA_DQ ); + mpi_read_string( &rsa.QP, 16, RSA_QP ); + + if( verbose != 0 ) + printf( " RSA key validation: " ); + + if( rsa_check_pubkey( &rsa ) != 0 || + rsa_check_privkey( &rsa ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n PKCS#1 encryption : " ); + + memcpy( rsa_plaintext, RSA_PT, PT_LEN ); + + if( rsa_pkcs1_encrypt( &rsa, &myrand, NULL, RSA_PUBLIC, PT_LEN, + rsa_plaintext, rsa_ciphertext ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n PKCS#1 decryption : " ); + + if( rsa_pkcs1_decrypt( &rsa, RSA_PRIVATE, &len, + rsa_ciphertext, rsa_decrypted, + sizeof(rsa_decrypted) ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( memcmp( rsa_decrypted, rsa_plaintext, len ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + +#if defined(POLARSSL_SHA1_C) + if( verbose != 0 ) + printf( "passed\n PKCS#1 data sign : " ); + + sha1( rsa_plaintext, PT_LEN, sha1sum ); + + if( rsa_pkcs1_sign( &rsa, NULL, NULL, RSA_PRIVATE, SIG_RSA_SHA1, 20, + sha1sum, rsa_ciphertext ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n PKCS#1 sig. verify: " ); + + if( rsa_pkcs1_verify( &rsa, RSA_PUBLIC, SIG_RSA_SHA1, 20, + sha1sum, rsa_ciphertext ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n\n" ); +#endif /* POLARSSL_SHA1_C */ + + rsa_free( &rsa ); + + return( 0 ); +} + +#endif + +#endif diff --git a/common/polarssl/rsa.h b/common/polarssl/rsa.h new file mode 100644 index 00000000..f9a02202 --- /dev/null +++ b/common/polarssl/rsa.h @@ -0,0 +1,597 @@ +/** + * \file rsa.h + * + * \brief The RSA public-key cryptosystem + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_RSA_H +#define POLARSSL_RSA_H + +#include "bignum.h" + +/* + * RSA Error codes + */ +#define POLARSSL_ERR_RSA_BAD_INPUT_DATA -0x4080 /**< Bad input parameters to function. */ +#define POLARSSL_ERR_RSA_INVALID_PADDING -0x4100 /**< Input data contains invalid padding and is rejected. */ +#define POLARSSL_ERR_RSA_KEY_GEN_FAILED -0x4180 /**< Something failed during generation of a key. */ +#define POLARSSL_ERR_RSA_KEY_CHECK_FAILED -0x4200 /**< Key failed to pass the libraries validity check. */ +#define POLARSSL_ERR_RSA_PUBLIC_FAILED -0x4280 /**< The public key operation failed. */ +#define POLARSSL_ERR_RSA_PRIVATE_FAILED -0x4300 /**< The private key operation failed. */ +#define POLARSSL_ERR_RSA_VERIFY_FAILED -0x4380 /**< The PKCS#1 verification failed. */ +#define POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE -0x4400 /**< The output buffer for decryption is not large enough. */ +#define POLARSSL_ERR_RSA_RNG_FAILED -0x4480 /**< The random generator failed to generate non-zeros. */ + +/* + * PKCS#1 constants + */ +#define SIG_RSA_RAW 0 +#define SIG_RSA_MD2 2 +#define SIG_RSA_MD4 3 +#define SIG_RSA_MD5 4 +#define SIG_RSA_SHA1 5 +#define SIG_RSA_SHA224 14 +#define SIG_RSA_SHA256 11 +#define SIG_RSA_SHA384 12 +#define SIG_RSA_SHA512 13 + +#define RSA_PUBLIC 0 +#define RSA_PRIVATE 1 + +#define RSA_PKCS_V15 0 +#define RSA_PKCS_V21 1 + +#define RSA_SIGN 1 +#define RSA_CRYPT 2 + +#define ASN1_STR_CONSTRUCTED_SEQUENCE "\x30" +#define ASN1_STR_NULL "\x05" +#define ASN1_STR_OID "\x06" +#define ASN1_STR_OCTET_STRING "\x04" + +#define OID_DIGEST_ALG_MDX "\x2A\x86\x48\x86\xF7\x0D\x02\x00" +#define OID_HASH_ALG_SHA1 "\x2b\x0e\x03\x02\x1a" +#define OID_HASH_ALG_SHA2X "\x60\x86\x48\x01\x65\x03\x04\x02\x00" + +#define OID_ISO_MEMBER_BODIES "\x2a" +#define OID_ISO_IDENTIFIED_ORG "\x2b" + +/* + * ISO Member bodies OID parts + */ +#define OID_COUNTRY_US "\x86\x48" +#define OID_RSA_DATA_SECURITY "\x86\xf7\x0d" + +/* + * ISO Identified organization OID parts + */ +#define OID_OIW_SECSIG_SHA1 "\x0e\x03\x02\x1a" + +/* + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithmIdentifier, + * digest Digest } + * + * DigestAlgorithmIdentifier ::= AlgorithmIdentifier + * + * Digest ::= OCTET STRING + */ +#define ASN1_HASH_MDX \ +( \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x20" \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x0C" \ + ASN1_STR_OID "\x08" \ + OID_DIGEST_ALG_MDX \ + ASN1_STR_NULL "\x00" \ + ASN1_STR_OCTET_STRING "\x10" \ +) + +#define ASN1_HASH_SHA1 \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x21" \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x09" \ + ASN1_STR_OID "\x05" \ + OID_HASH_ALG_SHA1 \ + ASN1_STR_NULL "\x00" \ + ASN1_STR_OCTET_STRING "\x14" + +#define ASN1_HASH_SHA1_ALT \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x1F" \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x07" \ + ASN1_STR_OID "\x05" \ + OID_HASH_ALG_SHA1 \ + ASN1_STR_OCTET_STRING "\x14" + +#define ASN1_HASH_SHA2X \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x11" \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x0d" \ + ASN1_STR_OID "\x09" \ + OID_HASH_ALG_SHA2X \ + ASN1_STR_NULL "\x00" \ + ASN1_STR_OCTET_STRING "\x00" + +/** + * \brief RSA context structure + */ +typedef struct +{ + int ver; /*!< always 0 */ + size_t len; /*!< size(N) in chars */ + + mpi N; /*!< public modulus */ + mpi E; /*!< public exponent */ + + mpi D; /*!< private exponent */ + mpi P; /*!< 1st prime factor */ + mpi Q; /*!< 2nd prime factor */ + mpi DP; /*!< D % (P - 1) */ + mpi DQ; /*!< D % (Q - 1) */ + mpi QP; /*!< 1 / (Q % P) */ + + mpi RN; /*!< cached R^2 mod N */ + mpi RP; /*!< cached R^2 mod P */ + mpi RQ; /*!< cached R^2 mod Q */ + + int padding; /*!< RSA_PKCS_V15 for 1.5 padding and + RSA_PKCS_v21 for OAEP/PSS */ + int hash_id; /*!< Hash identifier of md_type_t as + specified in the md.h header file + for the EME-OAEP and EMSA-PSS + encoding */ +} +rsa_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Initialize an RSA context + * + * Note: Set padding to RSA_PKCS_V21 for the RSAES-OAEP + * encryption scheme and the RSASSA-PSS signature scheme. + * + * \param ctx RSA context to be initialized + * \param padding RSA_PKCS_V15 or RSA_PKCS_V21 + * \param hash_id RSA_PKCS_V21 hash identifier + * + * \note The hash_id parameter is actually ignored + * when using RSA_PKCS_V15 padding. + */ +void rsa_init( rsa_context *ctx, + int padding, + int hash_id); + +/** + * \brief Generate an RSA keypair + * + * \param ctx RSA context that will hold the key + * \param f_rng RNG function + * \param p_rng RNG parameter + * \param nbits size of the public key in bits + * \param exponent public exponent (e.g., 65537) + * + * \note rsa_init() must be called beforehand to setup + * the RSA context. + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + */ +int rsa_gen_key( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + unsigned int nbits, int exponent ); + +/** + * \brief Check a public RSA key + * + * \param ctx RSA context to be checked + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + */ +int rsa_check_pubkey( const rsa_context *ctx ); + +/** + * \brief Check a private RSA key + * + * \param ctx RSA context to be checked + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + */ +int rsa_check_privkey( const rsa_context *ctx ); + +/** + * \brief Do an RSA public key operation + * + * \param ctx RSA context + * \param input input buffer + * \param output output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note This function does NOT take care of message + * padding. Also, be sure to set input[0] = 0 or assure that + * input is smaller than N. + * + * \note The input and output buffers must be large + * enough (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_public( rsa_context *ctx, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Do an RSA private key operation + * + * \param ctx RSA context + * \param input input buffer + * \param output output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The input and output buffers must be large + * enough (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_private( rsa_context *ctx, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Generic wrapper to perform a PKCS#1 encryption using the + * mode from the context. Add the message padding, then do an + * RSA operation. + * + * \param ctx RSA context + * \param f_rng RNG function (Needed for padding and PKCS#1 v2.1 encoding) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param ilen contains the plaintext length + * \param input buffer holding the data to be encrypted + * \param output buffer that will hold the ciphertext + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_pkcs1_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t ilen, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Perform a PKCS#1 v1.5 encryption (RSAES-PKCS1-v1_5-ENCRYPT) + * + * \param ctx RSA context + * \param f_rng RNG function (Needed for padding) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param ilen contains the plaintext length + * \param input buffer holding the data to be encrypted + * \param output buffer that will hold the ciphertext + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_rsaes_pkcs1_v15_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t ilen, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Perform a PKCS#1 v2.1 OAEP encryption (RSAES-OAEP-ENCRYPT) + * + * \param ctx RSA context + * \param f_rng RNG function (Needed for padding and PKCS#1 v2.1 encoding) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param label buffer holding the custom label to use + * \param label_len contains the label length + * \param ilen contains the plaintext length + * \param input buffer holding the data to be encrypted + * \param output buffer that will hold the ciphertext + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_rsaes_oaep_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + const unsigned char *label, size_t label_len, + size_t ilen, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Generic wrapper to perform a PKCS#1 decryption using the + * mode from the context. Do an RSA operation, then remove + * the message padding + * + * \param ctx RSA context + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param olen will contain the plaintext length + * \param input buffer holding the encrypted data + * \param output buffer that will hold the plaintext + * \param output_max_len maximum length of the output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used) otherwise + * an error is thrown. + */ +int rsa_pkcs1_decrypt( rsa_context *ctx, + int mode, size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len ); + +/** + * \brief Perform a PKCS#1 v1.5 decryption (RSAES-PKCS1-v1_5-DECRYPT) + * + * \param ctx RSA context + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param olen will contain the plaintext length + * \param input buffer holding the encrypted data + * \param output buffer that will hold the plaintext + * \param output_max_len maximum length of the output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used) otherwise + * an error is thrown. + */ +int rsa_rsaes_pkcs1_v15_decrypt( rsa_context *ctx, + int mode, size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len ); + +/** + * \brief Perform a PKCS#1 v2.1 OAEP decryption (RSAES-OAEP-DECRYPT) + * + * \param ctx RSA context + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param label buffer holding the custom label to use + * \param label_len contains the label length + * \param olen will contain the plaintext length + * \param input buffer holding the encrypted data + * \param output buffer that will hold the plaintext + * \param output_max_len maximum length of the output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used) otherwise + * an error is thrown. + */ +int rsa_rsaes_oaep_decrypt( rsa_context *ctx, + int mode, + const unsigned char *label, size_t label_len, + size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len ); + +/** + * \brief Generic wrapper to perform a PKCS#1 signature using the + * mode from the context. Do a private RSA operation to sign + * a message digest + * + * \param ctx RSA context + * \param f_rng RNG function (Needed for PKCS#1 v2.1 encoding) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param hash_id SIG_RSA_RAW, SIG_RSA_MD{2,4,5} or SIG_RSA_SHA{1,224,256,384,512} + * \param hashlen message digest length (for SIG_RSA_RAW only) + * \param hash buffer holding the message digest + * \param sig buffer that will hold the ciphertext + * + * \return 0 if the signing operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + * + * \note In case of PKCS#1 v2.1 encoding keep in mind that + * the hash_id in the RSA context is the one used for the + * encoding. hash_id in the function call is the type of hash + * that is encoded. According to RFC 3447 it is advised to + * keep both hashes the same. + */ +int rsa_pkcs1_sign( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * \brief Perform a PKCS#1 v1.5 signature (RSASSA-PKCS1-v1_5-SIGN) + * + * \param ctx RSA context + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param hash_id SIG_RSA_RAW, SIG_RSA_MD{2,4,5} or SIG_RSA_SHA{1,224,256,384,512} + * \param hashlen message digest length (for SIG_RSA_RAW only) + * \param hash buffer holding the message digest + * \param sig buffer that will hold the ciphertext + * + * \return 0 if the signing operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_rsassa_pkcs1_v15_sign( rsa_context *ctx, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * \brief Perform a PKCS#1 v2.1 PSS signature (RSASSA-PSS-SIGN) + * + * \param ctx RSA context + * \param f_rng RNG function (Needed for PKCS#1 v2.1 encoding) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param hash_id SIG_RSA_RAW, SIG_RSA_MD{2,4,5} or SIG_RSA_SHA{1,224,256,384,512} + * \param hashlen message digest length (for SIG_RSA_RAW only) + * \param hash buffer holding the message digest + * \param sig buffer that will hold the ciphertext + * + * \return 0 if the signing operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + * + * \note In case of PKCS#1 v2.1 encoding keep in mind that + * the hash_id in the RSA context is the one used for the + * encoding. hash_id in the function call is the type of hash + * that is encoded. According to RFC 3447 it is advised to + * keep both hashes the same. + */ +int rsa_rsassa_pss_sign( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * \brief Generic wrapper to perform a PKCS#1 verification using the + * mode from the context. Do a public RSA operation and check + * the message digest + * + * \param ctx points to an RSA public key + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param hash_id SIG_RSA_RAW, SIG_RSA_MD{2,4,5} or SIG_RSA_SHA{1,224,256,384,512} + * \param hashlen message digest length (for SIG_RSA_RAW only) + * \param hash buffer holding the message digest + * \param sig buffer holding the ciphertext + * + * \return 0 if the verify operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + * + * \note In case of PKCS#1 v2.1 encoding keep in mind that + * the hash_id in the RSA context is the one used for the + * verification. hash_id in the function call is the type of hash + * that is verified. According to RFC 3447 it is advised to + * keep both hashes the same. + */ +int rsa_pkcs1_verify( rsa_context *ctx, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * \brief Perform a PKCS#1 v1.5 verification (RSASSA-PKCS1-v1_5-VERIFY) + * + * \param ctx points to an RSA public key + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param hash_id SIG_RSA_RAW, SIG_RSA_MD{2,4,5} or SIG_RSA_SHA{1,224,256,384,512} + * \param hashlen message digest length (for SIG_RSA_RAW only) + * \param hash buffer holding the message digest + * \param sig buffer holding the ciphertext + * + * \return 0 if the verify operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_rsassa_pkcs1_v15_verify( rsa_context *ctx, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * \brief Perform a PKCS#1 v2.1 PSS verification (RSASSA-PSS-VERIFY) + * \brief Do a public RSA and check the message digest + * + * \param ctx points to an RSA public key + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param hash_id SIG_RSA_RAW, SIG_RSA_MD{2,4,5} or SIG_RSA_SHA{1,224,256,384,512} + * \param hashlen message digest length (for SIG_RSA_RAW only) + * \param hash buffer holding the message digest + * \param sig buffer holding the ciphertext + * + * \return 0 if the verify operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + * + * \note In case of PKCS#1 v2.1 encoding keep in mind that + * the hash_id in the RSA context is the one used for the + * verification. hash_id in the function call is the type of hash + * that is verified. According to RFC 3447 it is advised to + * keep both hashes the same. + */ +int rsa_rsassa_pss_verify( rsa_context *ctx, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * \brief Free the components of an RSA key + * + * \param ctx RSA Context to free + */ +void rsa_free( rsa_context *ctx ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int rsa_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* rsa.h */ diff --git a/common/sha1.c b/common/polarssl/sha1.c similarity index 87% rename from common/sha1.c rename to common/polarssl/sha1.c index d20c54a4..28bcd4e3 100644 --- a/common/sha1.c +++ b/common/polarssl/sha1.c @@ -1,8 +1,12 @@ /* * FIPS-180-1 compliant SHA-1 implementation * - * Copyright (C) 2006-2014, ARM Limited, All Rights Reserved - * This file is part of mbed TLS (https://tls.mbed.org) + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,38 +28,16 @@ * http://www.itl.nist.gov/fipspubs/fip180-1.htm */ -#if !defined(POLARSSL_CONFIG_FILE) -//#include "polarssl/config.h" -#define POLARSSL_SHA1_C - -#else -#include POLARSSL_CONFIG_FILE -#endif +#include "polarssl_config.h" #if defined(POLARSSL_SHA1_C) #include "sha1.h" -#include - -#if defined(POLARSSL_FS_IO) +#if defined(POLARSSL_FS_IO) || defined(POLARSSL_SELF_TEST) #include #endif -#if defined(POLARSSL_SELF_TEST) -#if defined(POLARSSL_PLATFORM_C) -#include "polarssl/platform.h" -#else -#include -#define polarssl_printf printf -#endif /* POLARSSL_PLATFORM_C */ -#endif /* POLARSSL_SELF_TEST */ - -/* Implementation that should never be optimized out by the compiler */ -static void polarssl_zeroize( void *v, size_t n ) { - volatile unsigned char *p = v; while( n-- ) *p++ = 0; -} - #if !defined(POLARSSL_SHA1_ALT) /* @@ -81,19 +63,6 @@ static void polarssl_zeroize( void *v, size_t n ) { } #endif -void sha1_init( sha1_context *ctx ) -{ - memset( ctx, 0, sizeof( sha1_context ) ); -} - -void sha1_free( sha1_context *ctx ) -{ - if( ctx == NULL ) - return; - - polarssl_zeroize( ctx, sizeof( sha1_context ) ); -} - /* * SHA-1 context setup */ @@ -134,8 +103,8 @@ void sha1_process( sha1_context *ctx, const unsigned char data[64] ) #define R(t) \ ( \ - temp = W[( t - 3 ) & 0x0F] ^ W[( t - 8 ) & 0x0F] ^ \ - W[( t - 14 ) & 0x0F] ^ W[ t & 0x0F], \ + temp = W[(t - 3) & 0x0F] ^ W[(t - 8) & 0x0F] ^ \ + W[(t - 14) & 0x0F] ^ W[ t & 0x0F], \ ( W[t & 0x0F] = S(temp,1) ) \ ) @@ -273,7 +242,7 @@ void sha1_update( sha1_context *ctx, const unsigned char *input, size_t ilen ) size_t fill; uint32_t left; - if( ilen == 0 ) + if( ilen <= 0 ) return; left = ctx->total[0] & 0x3F; @@ -351,11 +320,11 @@ void sha1( const unsigned char *input, size_t ilen, unsigned char output[20] ) { sha1_context ctx; - sha1_init( &ctx ); sha1_starts( &ctx ); sha1_update( &ctx, input, ilen ); sha1_finish( &ctx, output ); - sha1_free( &ctx ); + + memset( &ctx, 0, sizeof( sha1_context ) ); } #if defined(POLARSSL_FS_IO) @@ -372,14 +341,14 @@ int sha1_file( const char *path, unsigned char output[20] ) if( ( f = fopen( path, "rb" ) ) == NULL ) return( POLARSSL_ERR_SHA1_FILE_IO_ERROR ); - sha1_init( &ctx ); sha1_starts( &ctx ); while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) sha1_update( &ctx, buf, n ); sha1_finish( &ctx, output ); - sha1_free( &ctx ); + + memset( &ctx, 0, sizeof( sha1_context ) ); if( ferror( f ) != 0 ) { @@ -395,8 +364,7 @@ int sha1_file( const char *path, unsigned char output[20] ) /* * SHA-1 HMAC context setup */ -void sha1_hmac_starts( sha1_context *ctx, const unsigned char *key, - size_t keylen ) +void sha1_hmac_starts( sha1_context *ctx, const unsigned char *key, size_t keylen ) { size_t i; unsigned char sum[20]; @@ -420,14 +388,13 @@ void sha1_hmac_starts( sha1_context *ctx, const unsigned char *key, sha1_starts( ctx ); sha1_update( ctx, ctx->ipad, 64 ); - polarssl_zeroize( sum, sizeof( sum ) ); + memset( sum, 0, sizeof( sum ) ); } /* * SHA-1 HMAC process buffer */ -void sha1_hmac_update( sha1_context *ctx, const unsigned char *input, - size_t ilen ) +void sha1_hmac_update( sha1_context *ctx, const unsigned char *input, size_t ilen ) { sha1_update( ctx, input, ilen ); } @@ -445,7 +412,7 @@ void sha1_hmac_finish( sha1_context *ctx, unsigned char output[20] ) sha1_update( ctx, tmpbuf, 20 ); sha1_finish( ctx, output ); - polarssl_zeroize( tmpbuf, sizeof( tmpbuf ) ); + memset( tmpbuf, 0, sizeof( tmpbuf ) ); } /* @@ -466,18 +433,18 @@ void sha1_hmac( const unsigned char *key, size_t keylen, { sha1_context ctx; - sha1_init( &ctx ); sha1_hmac_starts( &ctx, key, keylen ); sha1_hmac_update( &ctx, input, ilen ); sha1_hmac_finish( &ctx, output ); - sha1_free( &ctx ); + + memset( &ctx, 0, sizeof( sha1_context ) ); } #if defined(POLARSSL_SELF_TEST) /* * FIPS-180-1 test vectors */ -static const unsigned char sha1_test_buf[3][57] = +static unsigned char sha1_test_buf[3][57] = { { "abc" }, { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" }, @@ -502,7 +469,7 @@ static const unsigned char sha1_test_sum[3][20] = /* * RFC 2202 test vectors */ -static const unsigned char sha1_hmac_test_key[7][26] = +static unsigned char sha1_hmac_test_key[7][26] = { { "\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B" "\x0B\x0B\x0B\x0B" }, @@ -522,7 +489,7 @@ static const int sha1_hmac_test_keylen[7] = 20, 4, 20, 25, 20, 80, 80 }; -static const unsigned char sha1_hmac_test_buf[7][74] = +static unsigned char sha1_hmac_test_buf[7][74] = { { "Hi There" }, { "what do ya want for nothing?" }, @@ -570,20 +537,18 @@ static const unsigned char sha1_hmac_test_sum[7][20] = */ int sha1_self_test( int verbose ) { - int i, j, buflen, ret = 0; + int i, j, buflen; unsigned char buf[1024]; unsigned char sha1sum[20]; sha1_context ctx; - sha1_init( &ctx ); - /* * SHA-1 */ for( i = 0; i < 3; i++ ) { if( verbose != 0 ) - polarssl_printf( " SHA-1 test #%d: ", i + 1 ); + printf( " SHA-1 test #%d: ", i + 1 ); sha1_starts( &ctx ); @@ -603,27 +568,26 @@ int sha1_self_test( int verbose ) if( memcmp( sha1sum, sha1_test_sum[i], 20 ) != 0 ) { if( verbose != 0 ) - polarssl_printf( "failed\n" ); + printf( "failed\n" ); - ret = 1; - goto exit; + return( 1 ); } if( verbose != 0 ) - polarssl_printf( "passed\n" ); + printf( "passed\n" ); } if( verbose != 0 ) - polarssl_printf( "\n" ); + printf( "\n" ); for( i = 0; i < 7; i++ ) { if( verbose != 0 ) - polarssl_printf( " HMAC-SHA-1 test #%d: ", i + 1 ); + printf( " HMAC-SHA-1 test #%d: ", i + 1 ); if( i == 5 || i == 6 ) { - memset( buf, 0xAA, buflen = 80 ); + memset( buf, '\xAA', buflen = 80 ); sha1_hmac_starts( &ctx, buf, buflen ); } else @@ -640,26 +604,21 @@ int sha1_self_test( int verbose ) if( memcmp( sha1sum, sha1_hmac_test_sum[i], buflen ) != 0 ) { if( verbose != 0 ) - polarssl_printf( "failed\n" ); + printf( "failed\n" ); - ret = 1; - goto exit; + return( 1 ); } if( verbose != 0 ) - polarssl_printf( "passed\n" ); + printf( "passed\n" ); } if( verbose != 0 ) - polarssl_printf( "\n" ); + printf( "\n" ); -exit: - sha1_free( &ctx ); - - return( ret ); + return( 0 ); } -#endif /* POLARSSL_SELF_TEST */ - -#endif /* POLARSSL_SHA1_C */ +#endif +#endif diff --git a/common/sha1.h b/common/polarssl/sha1.h similarity index 81% rename from common/sha1.h rename to common/polarssl/sha1.h index 056bba7e..01cb69b7 100644 --- a/common/sha1.h +++ b/common/polarssl/sha1.h @@ -3,9 +3,12 @@ * * \brief SHA-1 cryptographic hash function * - * Copyright (C) 2006-2014, ARM Limited, All Rights Reserved + * Copyright (C) 2006-2013, Brainspark B.V. * - * This file is part of mbed TLS (https://tls.mbed.org) + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,31 +27,11 @@ #ifndef POLARSSL_SHA1_H #define POLARSSL_SHA1_H -#if !defined(POLARSSL_CONFIG_FILE) -//#include "config.h" -/** - * \def POLARSSL_SHA1_C - * - * Enable the SHA1 cryptographic hash algorithm. - * - * Module: library/sha1.c - * Caller: library/md.c - * library/ssl_cli.c - * library/ssl_srv.c - * library/ssl_tls.c - * library/x509write_crt.c - * - * This module is required for SSL/TLS and SHA1-signed certificates. - */ -#define POLARSSL_SHA1_C +#include "polarssl_config.h" -#else -#include POLARSSL_CONFIG_FILE -#endif +#include -#include - -#if defined(_MSC_VER) && !defined(EFIX64) && !defined(EFI32) +#ifdef _MSC_VER #include typedef UINT32 uint32_t; #else @@ -61,10 +44,6 @@ typedef UINT32 uint32_t; // Regular implementation // -#ifdef __cplusplus -extern "C" { -#endif - /** * \brief SHA-1 context structure */ @@ -79,19 +58,9 @@ typedef struct } sha1_context; -/** - * \brief Initialize SHA-1 context - * - * \param ctx SHA-1 context to be initialized - */ -void sha1_init( sha1_context *ctx ); - -/** - * \brief Clear SHA-1 context - * - * \param ctx SHA-1 context to be cleared - */ -void sha1_free( sha1_context *ctx ); +#ifdef __cplusplus +extern "C" { +#endif /** * \brief SHA-1 context setup @@ -158,8 +127,7 @@ int sha1_file( const char *path, unsigned char output[20] ); * \param key HMAC secret key * \param keylen length of the HMAC key */ -void sha1_hmac_starts( sha1_context *ctx, const unsigned char *key, - size_t keylen ); +void sha1_hmac_starts( sha1_context *ctx, const unsigned char *key, size_t keylen ); /** * \brief SHA-1 HMAC process buffer @@ -168,8 +136,7 @@ void sha1_hmac_starts( sha1_context *ctx, const unsigned char *key, * \param input buffer holding the data * \param ilen length of the input data */ -void sha1_hmac_update( sha1_context *ctx, const unsigned char *input, - size_t ilen ); +void sha1_hmac_update( sha1_context *ctx, const unsigned char *input, size_t ilen ); /** * \brief SHA-1 HMAC final digest From 097d265091747a92c2885bc7a2477486d03cf16f Mon Sep 17 00:00:00 2001 From: Iceman Date: Sat, 23 Dec 2017 22:14:04 +0100 Subject: [PATCH 117/310] Update cmdmain.c cherry picking at fix for LF SNOOP, --- client/cmdmain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/cmdmain.c b/client/cmdmain.c index 739d68e1..ae059ea2 100644 --- a/client/cmdmain.c +++ b/client/cmdmain.c @@ -172,7 +172,7 @@ bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeo if (msclock() - start_time > 2000 && show_warning) { PrintAndLog("Waiting for a response from the proxmark..."); PrintAndLog("Don't forget to cancel its operation first by pressing on the button"); - break; + show_warning = false; } } return false; From 546ae21fa12ddcf75a0bcb422d8bd7c82609b9d7 Mon Sep 17 00:00:00 2001 From: merlokk Date: Sat, 23 Dec 2017 23:50:51 +0200 Subject: [PATCH 118/310] add `hf emv test` to appveyor config --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index eb22114e..f60c8e40 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -277,6 +277,7 @@ test_script: ExecTest "hf mf hardnested" "hf mf hardnested" {bash -lc "cd ~/client;proxmark3 comx -c 'hf mf hardnested t 1 000000000000'"} "found:" + ExecTest "hf emv test" "hf emv test" {bash -lc "cd ~/client;proxmark3 comx -c 'hf emv test'"} "[OK]" if ($global:TestsPassed) { Write-Host "Tests [ OK ]" -ForegroundColor Green From 828279508a09b40013255a0e7cd19e0a6e8f3eac Mon Sep 17 00:00:00 2001 From: merlokk Date: Sat, 23 Dec 2017 23:59:47 +0200 Subject: [PATCH 119/310] inc timeout --- appveyor.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index f60c8e40..b212318e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -232,8 +232,8 @@ test_script: #--- end Job [bool]$res=$false - # Wait 40 sec timeout for Job - if(Wait-Job $Job -Timeout 40){ + # Wait 120 sec timeout for Job + if(Wait-Job $Job -Timeout 120){ $Results = $Job | Receive-Job if($Results -like "true"){ $res=$true @@ -277,6 +277,8 @@ test_script: ExecTest "hf mf hardnested" "hf mf hardnested" {bash -lc "cd ~/client;proxmark3 comx -c 'hf mf hardnested t 1 000000000000'"} "found:" + #proxmark crypto tests + ExecTest "hf emv test" "hf emv test" {bash -lc "cd ~/client;proxmark3 comx -c 'hf emv test'"} "[OK]" if ($global:TestsPassed) { From b1c0f020988313a286f45669dcc505e66533640c Mon Sep 17 00:00:00 2001 From: merlokk Date: Sun, 24 Dec 2017 00:28:38 +0200 Subject: [PATCH 120/310] added xcode 9.2 and some logging --- .travis.yml | 7 ++++--- travis_test_commands.scr | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 918240c7..8e9289b1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,9 +12,9 @@ matrix: - os: osx osx_image: xcode8.3 # OS X 10.12 - os: osx - osx_image: xcode9 # OS X 10.13 + osx_image: xcode9 # OS X 10.12 - os: osx - osx_image: xcode9.1 # OS X 10.13.1 + osx_image: xcode9.2 # OS X 10.12 - os: linux dist: trusty sudo: required @@ -23,6 +23,7 @@ before_install: ## Install ARM toolchain on Linux. ## add our homebrew tap for MacOS ## Note: all dependencies on MacOS should be resolved by the brew install command + echo $REPOSITORY_EP; if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get update -qq; sudo apt-get install -y gcc-arm-none-eabi; @@ -31,7 +32,7 @@ before_install: if [[ "$REPOSITORY_EP" == "" ]]; then brew tap proxmark/proxmark3; else - brew tap "$REPOSITORY_EP"; + brew tap "$REPOSITORY_EP" --env=std; fi fi diff --git a/travis_test_commands.scr b/travis_test_commands.scr index 0dba3526..4f5b025c 100644 --- a/travis_test_commands.scr +++ b/travis_test_commands.scr @@ -1,2 +1,3 @@ hf mf hardnested t 1 000000000000 +hf emv test exit From e79209e1f4979e794c1d64289d560a2fd108d12d Mon Sep 17 00:00:00 2001 From: merlokk Date: Sun, 24 Dec 2017 01:11:43 +0200 Subject: [PATCH 121/310] fixed condition --- appveyor.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index b212318e..f5ae2c70 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -277,9 +277,11 @@ test_script: ExecTest "hf mf hardnested" "hf mf hardnested" {bash -lc "cd ~/client;proxmark3 comx -c 'hf mf hardnested t 1 000000000000'"} "found:" + #proxmark crypto tests - ExecTest "hf emv test" "hf emv test" {bash -lc "cd ~/client;proxmark3 comx -c 'hf emv test'"} "[OK]" + ExecTest "hf emv test" "hf emv test" {bash -lc "cd ~/client;proxmark3 comx -c 'hf emv test'"} "Tests ?OK" + if ($global:TestsPassed) { Write-Host "Tests [ OK ]" -ForegroundColor Green From 26f202e22feecfe6239c152d0783bf952e64ea35 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Tue, 26 Dec 2017 20:42:44 +0100 Subject: [PATCH 122/310] Change copyright to allow GPLV3, for https://github.com/Proxmark/proxmark3/issues/527 --- client/loclass/cipher.c | 3 +-- client/loclass/cipher.h | 3 +-- client/loclass/cipherutils.c | 3 +-- client/loclass/cipherutils.h | 3 +-- client/loclass/elite_crack.c | 3 +-- client/loclass/elite_crack.h | 3 +-- client/loclass/fileutils.c | 3 +-- client/loclass/fileutils.h | 3 +-- client/loclass/ikeys.c | 3 +-- client/loclass/ikeys.h | 3 +-- client/loclass/main.c | 3 +-- 11 files changed, 11 insertions(+), 22 deletions(-) diff --git a/client/loclass/cipher.c b/client/loclass/cipher.c index 57bc831f..3b146b10 100644 --- a/client/loclass/cipher.c +++ b/client/loclass/cipher.c @@ -22,7 +22,7 @@ * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. + * by the Free Software Foundation, or, at your option, any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -33,7 +33,6 @@ * along with loclass. If not, see . * * - * ****************************************************************************/ diff --git a/client/loclass/cipher.h b/client/loclass/cipher.h index 7afdb524..632910a9 100644 --- a/client/loclass/cipher.h +++ b/client/loclass/cipher.h @@ -22,7 +22,7 @@ * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. + * by the Free Software Foundation, or, at your option, any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -33,7 +33,6 @@ * along with loclass. If not, see . * * - * ****************************************************************************/ diff --git a/client/loclass/cipherutils.c b/client/loclass/cipherutils.c index 9a8256bb..b5e365bf 100644 --- a/client/loclass/cipherutils.c +++ b/client/loclass/cipherutils.c @@ -22,7 +22,7 @@ * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. + * by the Free Software Foundation, or, at your option, any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -33,7 +33,6 @@ * along with loclass. If not, see . * * - * ****************************************************************************/ #include diff --git a/client/loclass/cipherutils.h b/client/loclass/cipherutils.h index 3cdbf5fa..56867179 100644 --- a/client/loclass/cipherutils.h +++ b/client/loclass/cipherutils.h @@ -22,7 +22,7 @@ * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. + * by the Free Software Foundation, or, at your option, any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -33,7 +33,6 @@ * along with loclass. If not, see . * * - * ****************************************************************************/ diff --git a/client/loclass/elite_crack.c b/client/loclass/elite_crack.c index 777efe08..fe9bf7d1 100644 --- a/client/loclass/elite_crack.c +++ b/client/loclass/elite_crack.c @@ -22,7 +22,7 @@ * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. + * by the Free Software Foundation, or, at your option, any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -33,7 +33,6 @@ * along with loclass. If not, see . * * - * ****************************************************************************/ #include diff --git a/client/loclass/elite_crack.h b/client/loclass/elite_crack.h index fb27355f..46791fb5 100644 --- a/client/loclass/elite_crack.h +++ b/client/loclass/elite_crack.h @@ -22,7 +22,7 @@ * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. + * by the Free Software Foundation, or, at your option, any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -33,7 +33,6 @@ * along with loclass. If not, see . * * - * ****************************************************************************/ diff --git a/client/loclass/fileutils.c b/client/loclass/fileutils.c index e5e5c5b0..90857e7a 100644 --- a/client/loclass/fileutils.c +++ b/client/loclass/fileutils.c @@ -22,7 +22,7 @@ * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. + * by the Free Software Foundation, or, at your option, any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -33,7 +33,6 @@ * along with loclass. If not, see . * * - * ****************************************************************************/ #ifndef ON_DEVICE diff --git a/client/loclass/fileutils.h b/client/loclass/fileutils.h index 10720f76..79dbe566 100644 --- a/client/loclass/fileutils.h +++ b/client/loclass/fileutils.h @@ -22,7 +22,7 @@ * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. + * by the Free Software Foundation, or, at your option, any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -33,7 +33,6 @@ * along with loclass. If not, see . * * - * ****************************************************************************/ #ifndef FILEUTILS_H diff --git a/client/loclass/ikeys.c b/client/loclass/ikeys.c index 8892b4a3..72acf690 100644 --- a/client/loclass/ikeys.c +++ b/client/loclass/ikeys.c @@ -22,7 +22,7 @@ * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. + * by the Free Software Foundation, or, at your option, any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -33,7 +33,6 @@ * along with loclass. If not, see . * * - * ****************************************************************************/ /** diff --git a/client/loclass/ikeys.h b/client/loclass/ikeys.h index 13096194..5b44d179 100644 --- a/client/loclass/ikeys.h +++ b/client/loclass/ikeys.h @@ -22,7 +22,7 @@ * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. + * by the Free Software Foundation, or, at your option, any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -33,7 +33,6 @@ * along with loclass. If not, see . * * - * ****************************************************************************/ #ifndef IKEYS_H diff --git a/client/loclass/main.c b/client/loclass/main.c index 95112f7d..fa9886ee 100644 --- a/client/loclass/main.c +++ b/client/loclass/main.c @@ -22,7 +22,7 @@ * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. + * by the Free Software Foundation, or, at your option, any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -33,7 +33,6 @@ * along with loclass. If not, see . * * - * ****************************************************************************/ From 0f112d6f19ecf6e7d443eaf3464ebf916048be59 Mon Sep 17 00:00:00 2001 From: Pierre Pronchery Date: Thu, 28 Dec 2017 08:37:09 +0100 Subject: [PATCH 123/310] Avoid a crash in "script list" (#521) This command crashes if the path to the executable directory could not be found. --- client/cmdscript.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/client/cmdscript.c b/client/cmdscript.c index 23163aa9..0d19f496 100644 --- a/client/cmdscript.c +++ b/client/cmdscript.c @@ -76,8 +76,11 @@ int CmdList(const char *Cmd) { DIR *dp; struct dirent *ep; - char script_directory_path[strlen(get_my_executable_directory()) + strlen(LUA_SCRIPTS_DIRECTORY) + 1]; - strcpy(script_directory_path, get_my_executable_directory()); + char const * exedir = get_my_executable_directory(); + if (exedir == NULL) + return 0; + char script_directory_path[strlen(exedir) + strlen(LUA_SCRIPTS_DIRECTORY) + 1]; + strcpy(script_directory_path, exedir); strcat(script_directory_path, LUA_SCRIPTS_DIRECTORY); dp = opendir(script_directory_path); From b838c4ff272c2e48cc24b73dba35eda7e810d0b8 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Thu, 28 Dec 2017 08:49:07 +0100 Subject: [PATCH 124/310] Minor fixes * fix compiler warnings on Ubuntu * cmdhf14a.c: make manufactureMapping static * cmdhf14a.c: fix format strings --- client/cmdhf14a.c | 14 ++++++++++---- client/cmdhf14a.h | 6 ------ client/emv/crypto_polarssl.c | 4 ++-- client/emv/emv_pki.c | 4 ++-- client/emv/test/crypto_test.c | 4 ++-- client/util.c | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 8064724c..856d37b3 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "util.h" #include "util_posix.h" #include "iso14443crc.h" @@ -34,8 +35,13 @@ static int CmdHelp(const char *Cmd); static int waitCmd(uint8_t iLen); +// structure and database for uid -> tagtype lookups +typedef struct { + uint8_t uid; + char* desc; +} manufactureName; -const manufactureName manufactureMapping[] = { +static const manufactureName manufactureMapping[] = { // ID, "Vendor Country" { 0x01, "Motorola UK" }, { 0x02, "ST Microelectronics SA France" }, @@ -153,7 +159,7 @@ int CmdHF14AReader(const char *Cmd) { break; case 'x': case 'X': - cm = cm - ISO14A_CONNECT; + cm &= ~ISO14A_CONNECT; break; default: PrintAndLog("Unknown command."); @@ -191,7 +197,7 @@ int CmdHF14AReader(const char *Cmd) { PrintAndLog(" UID : %s", sprint_hex(card.uid, card.uidlen)); PrintAndLog("ATQA : %02x %02x", card.atqa[1], card.atqa[0]); - PrintAndLog(" SAK : %02x [%d]", card.sak, resp.arg[0]); + PrintAndLog(" SAK : %02x [%" PRIu64 "]", card.sak, resp.arg[0]); if(card.ats_len >= 3) { // a valid ATS consists of at least the length byte (TL) and 2 CRC bytes PrintAndLog(" ATS : %s", sprint_hex(card.ats, card.ats_len)); } @@ -243,7 +249,7 @@ int CmdHF14AInfo(const char *Cmd) PrintAndLog(" UID : %s", sprint_hex(card.uid, card.uidlen)); PrintAndLog("ATQA : %02x %02x", card.atqa[1], card.atqa[0]); - PrintAndLog(" SAK : %02x [%d]", card.sak, resp.arg[0]); + PrintAndLog(" SAK : %02x [%" PRIu64 "]", card.sak, resp.arg[0]); bool isMifareClassic = true; switch (card.sak) { diff --git a/client/cmdhf14a.h b/client/cmdhf14a.h index 401cead0..71007f95 100644 --- a/client/cmdhf14a.h +++ b/client/cmdhf14a.h @@ -15,12 +15,6 @@ #include #include -// structure and database for uid -> tagtype lookups -typedef struct { - uint8_t uid; - char* desc; -} manufactureName; - int CmdHF14A(const char *Cmd); int CmdHF14AList(const char *Cmd); int CmdHF14AMifare(const char *Cmd); diff --git a/client/emv/crypto_polarssl.c b/client/emv/crypto_polarssl.c index 760395c4..3d11afe5 100644 --- a/client/emv/crypto_polarssl.c +++ b/client/emv/crypto_polarssl.c @@ -215,7 +215,7 @@ static unsigned char *crypto_pk_polarssl_encrypt(const struct crypto_pk *_cp, co res = rsa_public(&cp->ctx, buf, result); if(res) { - printf("RSA encrypt failed. Error: %x data len: %d key len: %d\n", res * -1, len, keylen); + printf("RSA encrypt failed. Error: %x data len: %zd key len: %zd\n", res * -1, len, keylen); return NULL; } @@ -241,7 +241,7 @@ static unsigned char *crypto_pk_polarssl_decrypt(const struct crypto_pk *_cp, co res = rsa_private(&cp->ctx, buf, result); // CHECK??? if(res) { - printf("RSA decrypt failed. Error: %x data len: %d key len: %d\n", res * -1, len, keylen); + printf("RSA decrypt failed. Error: %x data len: %zd key len: %zd\n", res * -1, len, keylen); return NULL; } diff --git a/client/emv/emv_pki.c b/client/emv/emv_pki.c index 7803060e..f79e3045 100644 --- a/client/emv/emv_pki.c +++ b/client/emv/emv_pki.c @@ -53,7 +53,7 @@ static unsigned char *emv_pki_decode_message(const struct emv_pk *enc_pk, } if (cert_tlv->len != enc_pk->mlen) { - printf("ERROR: Certificate length (%d) not equal key length (%d)\n", cert_tlv->len, enc_pk->mlen); + printf("ERROR: Certificate length (%zd) not equal key length (%zd)\n", cert_tlv->len, enc_pk->mlen); return NULL; } kcp = crypto_pk_open(enc_pk->pk_algo, @@ -451,7 +451,7 @@ struct tlvdb *emv_pki_perform_cda_ex(const struct emv_pk *enc_pk, const struct t un_tlv, NULL); if (!data || data_len < 3) { - printf("ERROR: can't decode message. len %d\n", data_len); + printf("ERROR: can't decode message. len %zd\n", data_len); return NULL; } diff --git a/client/emv/test/crypto_test.c b/client/emv/test/crypto_test.c index ff18b9da..352f48b4 100644 --- a/client/emv/test/crypto_test.c +++ b/client/emv/test/crypto_test.c @@ -219,7 +219,7 @@ static int test_pk(bool verbose) tmp = crypto_pk_get_parameter(pubk, 0, &tmp_len); if (tmp_len != sizeof(pk_N) || memcmp(tmp, pk_N, tmp_len)) { - fprintf(stderr, "ERROR: crypto_pk_get_parameter(0) Modulus. param len %d len %d\n", tmp_len, sizeof(pk_N)); + fprintf(stderr, "ERROR: crypto_pk_get_parameter(0) Modulus. param len %zd len %zd\n", tmp_len, sizeof(pk_N)); free(tmp); goto close_pub; } @@ -256,7 +256,7 @@ static int test_pk(bool verbose) tmp = crypto_pk_get_parameter(privk, 0, &tmp_len); if (tmp_len != sizeof(pk_N) || memcmp(tmp, pk_N, tmp_len)) { - fprintf(stderr, "ERROR: crypto_pk_get_parameter(0) Modulus. param len %d len %d\n", tmp_len, sizeof(pk_N)); + fprintf(stderr, "ERROR: crypto_pk_get_parameter(0) Modulus. param len %zd len %zd\n", tmp_len, sizeof(pk_N)); free(tmp); goto close; } diff --git a/client/util.c b/client/util.c index b7f07bde..568992ab 100644 --- a/client/util.c +++ b/client/util.c @@ -620,7 +620,7 @@ int hextobinarray(char *target, char *source) else if (x >= 'A' && x <= 'F') x -= 'A' - 10; else { - printf("Discovered unknown character %c %d at idx %d of %s\n", x, x, source - start, start); + printf("Discovered unknown character %c %d at idx %d of %s\n", x, x, (unsigned int)(source - start), start); return 0; } // output From 55b700a0ac90f85aef1ea73a5edc153c32f8b42f Mon Sep 17 00:00:00 2001 From: merlokk Date: Thu, 28 Dec 2017 13:29:30 +0200 Subject: [PATCH 125/310] fix #532. looks at length of parameters. --- client/cmdhfmf.c | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index d9578af2..cca4da88 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -987,6 +987,7 @@ int CmdHF14AMfChk(const char *Cmd) int i, res; int keycnt = 0; char ctmp = 0x00; + int clen = 0; char ctmp3[3] = {0x00}; uint8_t blockNo = 0; uint8_t SectorsCnt = 0; @@ -1015,32 +1016,36 @@ int CmdHF14AMfChk(const char *Cmd) blockNo = param_get8(Cmd, 0); ctmp = param_getchar(Cmd, 1); - switch (ctmp) { - case 'a': case 'A': - keyType = 0; - break; - case 'b': case 'B': - keyType = 1; - break; - case '?': - keyType = 2; - break; - default: - PrintAndLog("Key type must be A , B or ?"); - free(keyBlock); - return 1; - }; + clen = param_getlength(Cmd, 1); + if (clen == 1) { + switch (ctmp) { + case 'a': case 'A': + keyType = 0; + break; + case 'b': case 'B': + keyType = 1; + break; + case '?': + keyType = 2; + break; + default: + PrintAndLog("Key type must be A , B or ?"); + free(keyBlock); + return 1; + }; + } // transfer to emulator & create dump file ctmp = param_getchar(Cmd, 2); - if (ctmp == 't' || ctmp == 'T') transferToEml = 1; - if (ctmp == 'd' || ctmp == 'D') createDumpFile = 1; + clen = param_getlength(Cmd, 2); + if (clen == 1 && (ctmp == 't' || ctmp == 'T')) transferToEml = 1; + if (clen == 1 && (ctmp == 'd' || ctmp == 'D')) createDumpFile = 1; param3InUse = transferToEml | createDumpFile; timeout14a = 500; // fast by default // double parameters - ts, ds - int clen = param_getlength(Cmd, 2); + clen = param_getlength(Cmd, 2); if (clen == 2 || clen == 3){ param_getstr(Cmd, 2, ctmp3, sizeof(ctmp3)); ctmp = ctmp3[1]; From 3ded0f97d32f91766a5815b6c74ca7c6ebade8bf Mon Sep 17 00:00:00 2001 From: Pierre Pronchery Date: Thu, 28 Dec 2017 17:56:18 +0100 Subject: [PATCH 126/310] Khorben/warnings (#519) * Fix warnings and missing #include for * Avoid a warning in client/util.c --- client/cmdcrc.c | 4 ++-- client/cmdhf14a.c | 3 ++- client/cmdhfmf.c | 2 +- client/cmdlft55xx.c | 2 +- client/emv/cmdemv.c | 3 ++- client/pm3_binlib.c | 2 +- client/reveng/model.c | 2 +- client/util.c | 8 ++++---- 8 files changed, 14 insertions(+), 12 deletions(-) diff --git a/client/cmdcrc.c b/client/cmdcrc.c index 27d081b9..0ca2b8b1 100644 --- a/client/cmdcrc.c +++ b/client/cmdcrc.c @@ -40,14 +40,14 @@ int split(char *str, char *arr[MAX_ARGS]){ int wordCnt = 0; while(1){ - while(isspace(str[beginIndex])){ + while(isspace((unsigned char)str[beginIndex])){ ++beginIndex; } if(str[beginIndex] == '\0') { break; } endIndex = beginIndex; - while (str[endIndex] && !isspace(str[endIndex])){ + while (str[endIndex] && !isspace((unsigned char)str[endIndex])){ ++endIndex; } int len = endIndex - beginIndex; diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 8064724c..e4b245c3 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "util.h" #include "util_posix.h" #include "iso14443crc.h" @@ -764,7 +765,7 @@ int CmdHF14AAPDU(const char *cmd) { return 1; } - if (isxdigit(c)) { + if (isxdigit((unsigned char)c)) { // len = data + PCB(1b) + CRC(2b) switch(param_gethex_to_eol(cmd, cmdp, data, sizeof(data) - 1 - 2, &datalen)) { case 1: diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index d9578af2..5ebf8144 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -1089,7 +1089,7 @@ int CmdHF14AMfChk(const char *Cmd) if( buf[0]=='#' ) continue; //The line start with # is comment, skip - if (!isxdigit(buf[0])){ + if (!isxdigit((unsigned char)buf[0])){ PrintAndLog("File content error. '%s' must include 12 HEX symbols",buf); continue; } diff --git a/client/cmdlft55xx.c b/client/cmdlft55xx.c index 92a00bce..c5a6dd3f 100644 --- a/client/cmdlft55xx.c +++ b/client/cmdlft55xx.c @@ -1433,7 +1433,7 @@ int CmdT55xxBruteForce(const char *Cmd) { //The line start with # is comment, skip if( buf[0]=='#' ) continue; - if (!isxdigit(buf[0])) { + if (!isxdigit((unsigned char)buf[0])) { PrintAndLog("File content error. '%s' must include 8 HEX symbols", buf); continue; } diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index 42c8524a..c53b02af 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -8,6 +8,7 @@ // EMV commands //----------------------------------------------------------------------------- +#include #include "cmdemv.h" #include "test/cryptotest.h" @@ -69,7 +70,7 @@ int CmdHFEMVSelect(const char *cmd) { return 1; } - if (isxdigit(c)) { + if (isxdigit((unsigned char)c)) { switch(param_gethex_to_eol(cmd, cmdp, data, sizeof(data), &datalen)) { case 1: PrintAndLog("Invalid HEX value."); diff --git a/client/pm3_binlib.c b/client/pm3_binlib.c index ed46c8e8..cffbca6c 100644 --- a/client/pm3_binlib.c +++ b/client/pm3_binlib.c @@ -306,7 +306,7 @@ static int l_pack(lua_State *L) /** pack(f,...) */ sbyte = 0; odd = 0; } - } else if (isspace(a[ii])) { + } else if (isspace((unsigned char)a[ii])) { /* ignore */ } else { /* err ... ignore too*/ diff --git a/client/reveng/model.c b/client/reveng/model.c index 2d45b2fe..5a9b6580 100644 --- a/client/reveng/model.c +++ b/client/reveng/model.c @@ -596,7 +596,7 @@ mbynam(model_t *dest, const char *key) { uerror("cannot allocate memory for comparison string"); akey.name = uptr = ukey; do - *uptr++ = toupper(*key); + *uptr++ = toupper((unsigned char)*key); while(*key++); aptr = bsearch(&akey, aliases, NALIASES, sizeof(struct malias), (int (*)(const void *, const void *)) &acmp); diff --git a/client/util.c b/client/util.c index b7f07bde..7e6b4074 100644 --- a/client/util.c +++ b/client/util.c @@ -496,7 +496,7 @@ int param_gethex(const char *line, int paramnum, uint8_t * data, int hexcnt) return 1; for(i = 0; i < hexcnt; i += 2) { - if (!(isxdigit(line[bg + i]) && isxdigit(line[bg + i + 1])) ) return 1; + if (!(isxdigit((unsigned char)line[bg + i]) && isxdigit((unsigned char)line[bg + i + 1])) ) return 1; sscanf((char[]){line[bg + i], line[bg + i + 1], 0}, "%X", &temp); data[i / 2] = temp & 0xff; @@ -518,7 +518,7 @@ int param_gethex_ex(const char *line, int paramnum, uint8_t * data, int *hexcnt) return 1; for(i = 0; i < *hexcnt; i += 2) { - if (!(isxdigit(line[bg + i]) && isxdigit(line[bg + i + 1])) ) return 1; + if (!(isxdigit((unsigned char)line[bg + i]) && isxdigit((unsigned char)line[bg + i + 1])) ) return 1; sscanf((char[]){line[bg + i], line[bg + i + 1], 0}, "%X", &temp); data[i / 2] = temp & 0xff; @@ -543,7 +543,7 @@ int param_gethex_to_eol(const char *line, int paramnum, uint8_t * data, int maxd continue; } - if (isxdigit(line[indx])) { + if (isxdigit((unsigned char)line[indx])) { buf[strlen(buf) + 1] = 0x00; buf[strlen(buf)] = line[indx]; } else { @@ -620,7 +620,7 @@ int hextobinarray(char *target, char *source) else if (x >= 'A' && x <= 'F') x -= 'A' - 10; else { - printf("Discovered unknown character %c %d at idx %d of %s\n", x, x, source - start, start); + printf("Discovered unknown character %c %d at idx %tu of %s\n", x, x, source - start, start); return 0; } // output From 3775e9e8651ee9d11c11efca550442128f468ee5 Mon Sep 17 00:00:00 2001 From: Michael Farrell Date: Fri, 29 Dec 2017 03:27:39 +1000 Subject: [PATCH 127/310] Allow externalisation of PrintAndLog (#506) --- client/ui.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/client/ui.c b/client/ui.c index df2c3ce3..8faed6e8 100644 --- a/client/ui.c +++ b/client/ui.c @@ -9,12 +9,14 @@ // UI utilities //----------------------------------------------------------------------------- -#include +#include +#ifndef EXTERNAL_PRINTANDLOG #include #include -#include +#include #include #include +#endif #include "ui.h" @@ -26,10 +28,12 @@ int GridOffset = 0; bool GridLocked = false; bool showDemod = true; -extern pthread_mutex_t print_lock; - static char *logfilename = "proxmark3.log"; +#ifndef EXTERNAL_PRINTANDLOG +// Declared in proxmark3.c +extern pthread_mutex_t print_lock; + void PrintAndLog(char *fmt, ...) { char *saved_line; @@ -94,7 +98,7 @@ void PrintAndLog(char *fmt, ...) //release lock pthread_mutex_unlock(&print_lock); } - +#endif void SetLogFilename(char *fn) { From 4a768458d2335fc3594f7a7f09bb4066442bed3f Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 2 Jan 2018 19:05:12 +0200 Subject: [PATCH 128/310] hardnested SIMD select --- client/cmdhfmfhard.c | 40 ++++++---- client/hardnested/hardnested_bf_core.c | 106 ++++++++++++++++++------- client/hardnested/hardnested_bf_core.h | 12 +++ 3 files changed, 112 insertions(+), 46 deletions(-) diff --git a/client/cmdhfmfhard.c b/client/cmdhfmfhard.c index 0153541e..cbdf3f60 100644 --- a/client/cmdhfmfhard.c +++ b/client/cmdhfmfhard.c @@ -32,6 +32,7 @@ #include "crapto1/crapto1.h" #include "parity.h" #include "hardnested/hardnested_bruteforce.h" +#include "hardnested/hardnested_bf_core.h" #include "hardnested/hardnested_bitarray_core.h" #include "zlib.h" @@ -71,27 +72,32 @@ static float brute_force_per_second; static void get_SIMD_instruction_set(char* instruction_set) { -#if defined (__i386__) || defined (__x86_64__) - #if !defined(__APPLE__) || (defined(__APPLE__) && (__clang_major__ > 8 || __clang_major__ == 8 && __clang_minor__ >= 1)) - #if (__GNUC__ >= 5) && (__GNUC__ > 5 || __GNUC_MINOR__ > 2) - if (__builtin_cpu_supports("avx512f")) strcpy(instruction_set, "AVX512F"); - else if (__builtin_cpu_supports("avx2")) strcpy(instruction_set, "AVX2"); - #else - if (__builtin_cpu_supports("avx2")) strcpy(instruction_set, "AVX2"); - #endif - else if (__builtin_cpu_supports("avx")) strcpy(instruction_set, "AVX"); - else if (__builtin_cpu_supports("sse2")) strcpy(instruction_set, "SSE2"); - else if (__builtin_cpu_supports("mmx")) strcpy(instruction_set, "MMX"); - else - #endif -#endif - strcpy(instruction_set, "no"); + switch(GetSIMDInstrAuto()) { + case SIMD_AVX512: + strcpy(instruction_set, "AVX512F"); + break; + case SIMD_AVX2: + strcpy(instruction_set, "AVX2"); + break; + case SIMD_AVX: + strcpy(instruction_set, "AVX"); + break; + case SIMD_SSE2: + strcpy(instruction_set, "SSE2"); + break; + case SIMD_MMX: + strcpy(instruction_set, "MMX"); + break; + default: + strcpy(instruction_set, "no"); + break; + } } static void print_progress_header(void) { char progress_text[80]; - char instr_set[12] = ""; + char instr_set[12] = {0}; get_SIMD_instruction_set(instr_set); sprintf(progress_text, "Start using %d threads and %s SIMD core", num_CPUs(), instr_set); PrintAndLog("\n\n"); @@ -2528,6 +2534,8 @@ static void set_test_state(uint8_t byte) int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *trgkey, bool nonce_file_read, bool nonce_file_write, bool slow, int tests) { char progress_text[80]; + + SetSIMDInstr(SIMD_NONE); srand((unsigned) time(NULL)); brute_force_per_second = brute_force_benchmark(); diff --git a/client/hardnested/hardnested_bf_core.c b/client/hardnested/hardnested_bf_core.c index 3c0c044f..d716f702 100644 --- a/client/hardnested/hardnested_bf_core.c +++ b/client/hardnested/hardnested_bf_core.c @@ -544,48 +544,94 @@ out: #ifndef __MMX__ +static SIMDExecInstr intSIMDInstr = SIMD_AUTO; + +void SetSIMDInstr(SIMDExecInstr instr) { + intSIMDInstr = instr; +} + +SIMDExecInstr GetSIMDInstr() { + SIMDExecInstr instr = SIMD_NONE; + +#if defined (__i386__) || defined (__x86_64__) + #if !defined(__APPLE__) || (defined(__APPLE__) && (__clang_major__ > 8 || __clang_major__ == 8 && __clang_minor__ >= 1)) + #if (__GNUC__ >= 5) && (__GNUC__ > 5 || __GNUC_MINOR__ > 2) + if (__builtin_cpu_supports("avx512f")) instr = SIMD_AVX512; + else if (__builtin_cpu_supports("avx2")) instr = SIMD_AVX2; + #else + if (__builtin_cpu_supports("avx2")) instr = SIMD_AVX2; + #endif + else if (__builtin_cpu_supports("avx")) instr = SIMD_AVX; + else if (__builtin_cpu_supports("sse2")) instr = SIMD_SSE2; + else if (__builtin_cpu_supports("mmx")) instr = SIMD_MMX; + else + #endif +#endif + instr = SIMD_NONE; + + return instr; +} + +SIMDExecInstr GetSIMDInstrAuto() { + SIMDExecInstr instr = intSIMDInstr; + if (instr == SIMD_AUTO) + return GetSIMDInstr(); + + return instr; +} + // pointers to functions: crack_states_bitsliced_t *crack_states_bitsliced_function_p = &crack_states_bitsliced_dispatch; bitslice_test_nonces_t *bitslice_test_nonces_function_p = &bitslice_test_nonces_dispatch; // determine the available instruction set at runtime and call the correct function const uint64_t crack_states_bitsliced_dispatch(uint32_t cuid, uint8_t *best_first_bytes, statelist_t *p, uint32_t *keys_found, uint64_t *num_keys_tested, uint32_t nonces_to_bruteforce, uint8_t *bf_test_nonce_2nd_byte, noncelist_t *nonces) { -#if defined (__i386__) || defined (__x86_64__) - #if !defined(__APPLE__) || (defined(__APPLE__) && (__clang_major__ > 8 || __clang_major__ == 8 && __clang_minor__ >= 1)) - #if (__GNUC__ >= 5) && (__GNUC__ > 5 || __GNUC_MINOR__ > 2) - if (__builtin_cpu_supports("avx512f")) crack_states_bitsliced_function_p = &crack_states_bitsliced_AVX512; - else if (__builtin_cpu_supports("avx2")) crack_states_bitsliced_function_p = &crack_states_bitsliced_AVX2; - #else - if (__builtin_cpu_supports("avx2")) crack_states_bitsliced_function_p = &crack_states_bitsliced_AVX2; - #endif - else if (__builtin_cpu_supports("avx")) crack_states_bitsliced_function_p = &crack_states_bitsliced_AVX; - else if (__builtin_cpu_supports("sse2")) crack_states_bitsliced_function_p = &crack_states_bitsliced_SSE2; - else if (__builtin_cpu_supports("mmx")) crack_states_bitsliced_function_p = &crack_states_bitsliced_MMX; - else - #endif -#endif - crack_states_bitsliced_function_p = &crack_states_bitsliced_NOSIMD; + switch(GetSIMDInstrAuto()) { + case SIMD_AVX512: + crack_states_bitsliced_function_p = &crack_states_bitsliced_AVX512; + break; + case SIMD_AVX2: + crack_states_bitsliced_function_p = &crack_states_bitsliced_AVX2; + break; + case SIMD_AVX: + crack_states_bitsliced_function_p = &crack_states_bitsliced_AVX; + break; + case SIMD_SSE2: + crack_states_bitsliced_function_p = &crack_states_bitsliced_SSE2; + break; + case SIMD_MMX: + crack_states_bitsliced_function_p = &crack_states_bitsliced_MMX; + break; + default: + crack_states_bitsliced_function_p = &crack_states_bitsliced_NOSIMD; + break; + } // call the most optimized function for this CPU return (*crack_states_bitsliced_function_p)(cuid, best_first_bytes, p, keys_found, num_keys_tested, nonces_to_bruteforce, bf_test_nonce_2nd_byte, nonces); } void bitslice_test_nonces_dispatch(uint32_t nonces_to_bruteforce, uint32_t *bf_test_nonce, uint8_t *bf_test_nonce_par) { -#if defined (__i386__) || defined (__x86_64__) - #if !defined(__APPLE__) || (defined(__APPLE__) && (__clang_major__ > 8 || __clang_major__ == 8 && __clang_minor__ >= 1)) - #if (__GNUC__ >= 5) && (__GNUC__ > 5 || __GNUC_MINOR__ > 2) - if (__builtin_cpu_supports("avx512f")) bitslice_test_nonces_function_p = &bitslice_test_nonces_AVX512; - else if (__builtin_cpu_supports("avx2")) bitslice_test_nonces_function_p = &bitslice_test_nonces_AVX2; - #else - if (__builtin_cpu_supports("avx2")) bitslice_test_nonces_function_p = &bitslice_test_nonces_AVX2; - #endif - else if (__builtin_cpu_supports("avx")) bitslice_test_nonces_function_p = &bitslice_test_nonces_AVX; - else if (__builtin_cpu_supports("sse2")) bitslice_test_nonces_function_p = &bitslice_test_nonces_SSE2; - else if (__builtin_cpu_supports("mmx")) bitslice_test_nonces_function_p = &bitslice_test_nonces_MMX; - else - #endif -#endif - bitslice_test_nonces_function_p = &bitslice_test_nonces_NOSIMD; + switch(GetSIMDInstrAuto()) { + case SIMD_AVX512: + bitslice_test_nonces_function_p = &bitslice_test_nonces_AVX512; + break; + case SIMD_AVX2: + bitslice_test_nonces_function_p = &bitslice_test_nonces_AVX2; + break; + case SIMD_AVX: + bitslice_test_nonces_function_p = &bitslice_test_nonces_AVX; + break; + case SIMD_SSE2: + bitslice_test_nonces_function_p = &bitslice_test_nonces_SSE2; + break; + case SIMD_MMX: + bitslice_test_nonces_function_p = &bitslice_test_nonces_MMX; + break; + default: + bitslice_test_nonces_function_p = &bitslice_test_nonces_NOSIMD; + break; + } // call the most optimized function for this CPU (*bitslice_test_nonces_function_p)(nonces_to_bruteforce, bf_test_nonce, bf_test_nonce_par); diff --git a/client/hardnested/hardnested_bf_core.h b/client/hardnested/hardnested_bf_core.h index 7a445993..b3df0547 100644 --- a/client/hardnested/hardnested_bf_core.h +++ b/client/hardnested/hardnested_bf_core.h @@ -52,6 +52,18 @@ THE SOFTWARE. #include "hardnested_bruteforce.h" // statelist_t +typedef enum { + SIMD_AUTO, + SIMD_AVX512, + SIMD_AVX2, + SIMD_AVX, + SIMD_SSE2, + SIMD_MMX, + SIMD_NONE, +} SIMDExecInstr; +extern void SetSIMDInstr(SIMDExecInstr instr); +extern SIMDExecInstr GetSIMDInstrAuto(); + extern const uint64_t crack_states_bitsliced(uint32_t cuid, uint8_t *best_first_bytes, statelist_t *p, uint32_t *keys_found, uint64_t *num_keys_tested, uint32_t nonces_to_bruteforce, uint8_t *bf_test_nonces_2nd_byte, noncelist_t *nonces); extern void bitslice_test_nonces(uint32_t nonces_to_bruteforce, uint32_t *bf_test_nonces, uint8_t *bf_test_nonce_par); From eaecd7f541bf64c9ae0681d1f9c4b6bac2c7322b Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 2 Jan 2018 19:11:26 +0200 Subject: [PATCH 129/310] add print SIMD core at the beginning --- client/cmdhfmfhard.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/cmdhfmfhard.c b/client/cmdhfmfhard.c index cbdf3f60..652a3cad 100644 --- a/client/cmdhfmfhard.c +++ b/client/cmdhfmfhard.c @@ -2536,6 +2536,9 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc char progress_text[80]; SetSIMDInstr(SIMD_NONE); + char instr_set[12] = {0}; + get_SIMD_instruction_set(instr_set); + PrintAndLog("Using %s SIMD core.", instr_set); srand((unsigned) time(NULL)); brute_force_per_second = brute_force_benchmark(); From 362d20390902245e943e307500c9ef02986160e2 Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 2 Jan 2018 22:28:13 +0200 Subject: [PATCH 130/310] works --- client/cmdhfmf.c | 50 +++++++++++++++++++++++++- client/cmdhfmfhard.c | 1 - client/hardnested/hardnested_bf_core.c | 11 +++--- 3 files changed, 56 insertions(+), 6 deletions(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index d9578af2..ed08917a 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -25,6 +25,7 @@ #include "mifarehost.h" #include "mifare.h" #include "mfkey.h" +#include "hardnested/hardnested_bf_core.h" #define NESTED_SECTOR_RETRY 10 // how often we try mfested() until we give up @@ -862,6 +863,13 @@ int CmdHF14AMfNestedHard(const char *Cmd) PrintAndLog(" w: Acquire nonces and write them to binary file nonces.bin"); PrintAndLog(" s: Slower acquisition (required by some non standard cards)"); PrintAndLog(" r: Read nonces.bin and start attack"); + PrintAndLog(" iX: set type of SIMD instructions. Without this flag programs autodetect it."); + PrintAndLog(" i5: AVX512"); + PrintAndLog(" i2: AVX2"); + PrintAndLog(" ia: AVX"); + PrintAndLog(" is: SSE2"); + PrintAndLog(" im: MMX"); + PrintAndLog(" in: none (use CPU regular instruction set)"); PrintAndLog(" "); PrintAndLog(" sample1: hf mf hardnested 0 A FFFFFFFFFFFF 4 A"); PrintAndLog(" sample2: hf mf hardnested 0 A FFFFFFFFFFFF 4 A w"); @@ -880,15 +888,20 @@ int CmdHF14AMfNestedHard(const char *Cmd) int tests = 0; + uint16_t iindx = 0; if (ctmp == 'R' || ctmp == 'r') { nonce_file_read = true; + iindx = 1; if (!param_gethex(Cmd, 1, trgkey, 12)) { know_target_key = true; + iindx = 2; } } else if (ctmp == 'T' || ctmp == 't') { tests = param_get32ex(Cmd, 1, 100, 10); + iindx = 2; if (!param_gethex(Cmd, 2, trgkey, 12)) { know_target_key = true; + iindx = 3; } } else { blockNo = param_get8(Cmd, 0); @@ -922,19 +935,54 @@ int CmdHF14AMfNestedHard(const char *Cmd) know_target_key = true; i++; } + iindx = i; while ((ctmp = param_getchar(Cmd, i))) { if (ctmp == 's' || ctmp == 'S') { slow = true; } else if (ctmp == 'w' || ctmp == 'W') { nonce_file_write = true; + } else if (param_getlength(Cmd, i) == 2 && ctmp == 'i') { + iindx = i; } else { - PrintAndLog("Possible options are w and/or s"); + PrintAndLog("Possible options are w , s and/or iX"); return 1; } i++; } } + + SetSIMDInstr(SIMD_AUTO); + if (iindx > 0) { + while ((ctmp = param_getchar(Cmd, iindx))) { + if (param_getlength(Cmd, iindx) == 2 && ctmp == 'i') { + switch(param_getchar_indx(Cmd, 1, iindx)) { + case '5': + SetSIMDInstr(SIMD_AVX512); + break; + case '2': + SetSIMDInstr(SIMD_AVX2); + break; + case 'a': + SetSIMDInstr(SIMD_AVX); + break; + case 's': + SetSIMDInstr(SIMD_SSE2); + break; + case 'm': + SetSIMDInstr(SIMD_MMX); + break; + case 'n': + SetSIMDInstr(SIMD_NONE); + break; + default: + PrintAndLog("Unknown SIMD type. %c", param_getchar_indx(Cmd, 1, iindx)); + return 1; + } + } + iindx++; + } + } PrintAndLog("--target block no:%3d, target key type:%c, known target key: 0x%02x%02x%02x%02x%02x%02x%s, file action: %s, Slow: %s, Tests: %d ", trgBlockNo, diff --git a/client/cmdhfmfhard.c b/client/cmdhfmfhard.c index 652a3cad..96c3a989 100644 --- a/client/cmdhfmfhard.c +++ b/client/cmdhfmfhard.c @@ -2535,7 +2535,6 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc { char progress_text[80]; - SetSIMDInstr(SIMD_NONE); char instr_set[12] = {0}; get_SIMD_instruction_set(instr_set); PrintAndLog("Using %s SIMD core.", instr_set); diff --git a/client/hardnested/hardnested_bf_core.c b/client/hardnested/hardnested_bf_core.c index d716f702..78384bbc 100644 --- a/client/hardnested/hardnested_bf_core.c +++ b/client/hardnested/hardnested_bf_core.c @@ -544,10 +544,17 @@ out: #ifndef __MMX__ +// pointers to functions: +crack_states_bitsliced_t *crack_states_bitsliced_function_p = &crack_states_bitsliced_dispatch; +bitslice_test_nonces_t *bitslice_test_nonces_function_p = &bitslice_test_nonces_dispatch; + static SIMDExecInstr intSIMDInstr = SIMD_AUTO; void SetSIMDInstr(SIMDExecInstr instr) { intSIMDInstr = instr; + + crack_states_bitsliced_function_p = &crack_states_bitsliced_dispatch; + bitslice_test_nonces_function_p = &bitslice_test_nonces_dispatch; } SIMDExecInstr GetSIMDInstr() { @@ -580,10 +587,6 @@ SIMDExecInstr GetSIMDInstrAuto() { return instr; } -// pointers to functions: -crack_states_bitsliced_t *crack_states_bitsliced_function_p = &crack_states_bitsliced_dispatch; -bitslice_test_nonces_t *bitslice_test_nonces_function_p = &bitslice_test_nonces_dispatch; - // determine the available instruction set at runtime and call the correct function const uint64_t crack_states_bitsliced_dispatch(uint32_t cuid, uint8_t *best_first_bytes, statelist_t *p, uint32_t *keys_found, uint64_t *num_keys_tested, uint32_t nonces_to_bruteforce, uint8_t *bf_test_nonce_2nd_byte, noncelist_t *nonces) { switch(GetSIMDInstrAuto()) { From 1c313691e6fbd76c48122c8c63844e240a6fb0d1 Mon Sep 17 00:00:00 2001 From: Iceman Date: Wed, 3 Jan 2018 11:15:46 +0100 Subject: [PATCH 131/310] Update ikeys.c (#537) FIX: 'hf iclass' - sneaky fread bug. It should return same number of bytes as read if ok. Thanks to @bettse who found it. --- client/loclass/ikeys.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/client/loclass/ikeys.c b/client/loclass/ikeys.c index 72acf690..2a6a0010 100644 --- a/client/loclass/ikeys.c +++ b/client/loclass/ikeys.c @@ -739,18 +739,16 @@ int readKeyFile(uint8_t key[8]) FILE *f; int retval = 1; f = fopen("iclass_key.bin", "rb"); - if (f) - { - if(fread(key, sizeof(uint8_t), 8, f) == 1) - { - retval = 0; - } - fclose(f); + if (!f) + return retval; + + if (fread(key, sizeof(uint8_t), 8, f) == 8) { + retval = 0; } + fclose(f); return retval; } - int doKeyTests(uint8_t debuglevel) { debug_print = debuglevel; From 58f0ae45c1f3bbe6d962aece3694c61f78475da3 Mon Sep 17 00:00:00 2001 From: merlokk Date: Fri, 5 Jan 2018 11:51:51 +0200 Subject: [PATCH 132/310] move travis files --- .travis.yml => CI/.travis.yml | 0 CI/readme.md | 19 +++++++++++++++++++ .../travis_test_commands.scr | 0 3 files changed, 19 insertions(+) rename .travis.yml => CI/.travis.yml (100%) create mode 100644 CI/readme.md rename travis_test_commands.scr => CI/travis_test_commands.scr (100%) diff --git a/.travis.yml b/CI/.travis.yml similarity index 100% rename from .travis.yml rename to CI/.travis.yml diff --git a/CI/readme.md b/CI/readme.md new file mode 100644 index 00000000..bcfc4542 --- /dev/null +++ b/CI/readme.md @@ -0,0 +1,19 @@ +# How to configure continuous integration + +Here 2 CI configuration files: + +1. for [travis](travis-ci.org) +2. for [appveyor](appveyor.com) + +It needs to put files from this directory to repository root and then configure CI from appropriate WEB portal. + +## travis + +- Copy .travis.yml and travis_test_commands.scr files to repository root +- Configure CI from http://travis-ci.org +- It needs to clone https://github.com/Proxmark/homebrew-proxmark3 from your proxmark repository home + + +## appveyor + +- Just copy appveyor.yml file to root and configure it from http://appveyor.com diff --git a/travis_test_commands.scr b/CI/travis_test_commands.scr similarity index 100% rename from travis_test_commands.scr rename to CI/travis_test_commands.scr From 7421ab0b43ba71f214f3cf24e28aa32d4e2ed844 Mon Sep 17 00:00:00 2001 From: merlokk Date: Fri, 5 Jan 2018 13:04:17 +0200 Subject: [PATCH 133/310] fix readme --- CI/readme.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CI/readme.md b/CI/readme.md index bcfc4542..d4159290 100644 --- a/CI/readme.md +++ b/CI/readme.md @@ -11,7 +11,8 @@ It needs to put files from this directory to repository root and then configure - Copy .travis.yml and travis_test_commands.scr files to repository root - Configure CI from http://travis-ci.org -- It needs to clone https://github.com/Proxmark/homebrew-proxmark3 from your proxmark repository home +- It needs to fork https://github.com/Proxmark/homebrew-proxmark3 from your proxmark repository home +- Put to file `proxmark3.rb` in line `head "https://github.com/proxmark/proxmark3.git"` your repository link. As sample: `head "https://github.com/merlokk/proxmark3.git"` ## appveyor From 4ed45d444b68b9a9e521618ae412d90c51695b31 Mon Sep 17 00:00:00 2001 From: merlokk Date: Fri, 5 Jan 2018 18:36:00 +0200 Subject: [PATCH 134/310] fix #404 --- client/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/Makefile b/client/Makefile index 52a106bd..0cda2b18 100644 --- a/client/Makefile +++ b/client/Makefile @@ -207,7 +207,7 @@ MULTIARCHOBJS = $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_NOSIMD.o) \ SUPPORTS_AVX512 := $(shell echo | gcc -E -mavx512f - > /dev/null 2>&1 && echo "True" ) HARD_SWITCH_NOSIMD = -mno-mmx -mno-sse2 -mno-avx -mno-avx2 HARD_SWITCH_MMX = -mmmx -mno-sse2 -mno-avx -mno-avx2 -HARD_SWITCH_SSE2 = -mmmx -msse2 -mno-avx -mno-avx2 +HARD_SWITCH_SSE2 = -mmmx -msse2 -mno-avx -mno-avx2 -mstackrealign HARD_SWITCH_AVX = -mmmx -msse2 -mavx -mno-avx2 HARD_SWITCH_AVX2 = -mmmx -msse2 -mavx -mavx2 HARD_SWITCH_AVX512 = -mmmx -msse2 -mavx -mavx2 -mavx512f From ab0582d672e1c2cbb1a49652cc8358218f672244 Mon Sep 17 00:00:00 2001 From: merlokk Date: Sat, 6 Jan 2018 19:14:34 +0200 Subject: [PATCH 135/310] fix another way --- client/Makefile | 2 +- client/hardnested/hardnested_bruteforce.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/Makefile b/client/Makefile index 0cda2b18..52a106bd 100644 --- a/client/Makefile +++ b/client/Makefile @@ -207,7 +207,7 @@ MULTIARCHOBJS = $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_NOSIMD.o) \ SUPPORTS_AVX512 := $(shell echo | gcc -E -mavx512f - > /dev/null 2>&1 && echo "True" ) HARD_SWITCH_NOSIMD = -mno-mmx -mno-sse2 -mno-avx -mno-avx2 HARD_SWITCH_MMX = -mmmx -mno-sse2 -mno-avx -mno-avx2 -HARD_SWITCH_SSE2 = -mmmx -msse2 -mno-avx -mno-avx2 -mstackrealign +HARD_SWITCH_SSE2 = -mmmx -msse2 -mno-avx -mno-avx2 HARD_SWITCH_AVX = -mmmx -msse2 -mavx -mno-avx2 HARD_SWITCH_AVX2 = -mmmx -msse2 -mavx -mavx2 HARD_SWITCH_AVX512 = -mmmx -msse2 -mavx -mavx2 -mavx512f diff --git a/client/hardnested/hardnested_bruteforce.c b/client/hardnested/hardnested_bruteforce.c index 718b7c5d..3072fc52 100644 --- a/client/hardnested/hardnested_bruteforce.c +++ b/client/hardnested/hardnested_bruteforce.c @@ -140,7 +140,7 @@ bool verify_key(uint32_t cuid, noncelist_t *nonces, uint8_t *best_first_bytes, u } -static void* crack_states_thread(void* x){ +static void* __attribute__((force_align_arg_pointer)) crack_states_thread(void* x){ struct arg { bool silent; From 84e86ed9e3c435bd386370ea4c18887decfb48c6 Mon Sep 17 00:00:00 2001 From: merlokk Date: Sun, 7 Jan 2018 23:23:35 +0200 Subject: [PATCH 136/310] move appveyor.yml --- appveyor.yml => CI/appveyor.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename appveyor.yml => CI/appveyor.yml (100%) diff --git a/appveyor.yml b/CI/appveyor.yml similarity index 100% rename from appveyor.yml rename to CI/appveyor.yml From 2d51da38f380302722bc91264c620190ce76a73f Mon Sep 17 00:00:00 2001 From: Iceman Date: Wed, 10 Jan 2018 13:56:27 +0100 Subject: [PATCH 137/310] HW TUNE adapting for powerful antennas. (#540) CHG: 'hw tune' adapting LF measuring from ( >> 8) max 65v, to ( >>9 ) max 130v in the graph data. Max LF voltage is 140.8v to measure. Added a delay for antenna coil and caps to power up before measuring, removed a variable in ReadAdc --- armsrc/appmain.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index e292483b..65fc6de6 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -135,15 +135,7 @@ void Dbhexdump(int len, uint8_t *d, bool bAsci) { // return that. //----------------------------------------------------------------------------- static int ReadAdc(int ch) -{ - uint32_t d; - - AT91C_BASE_ADC->ADC_CR = AT91C_ADC_SWRST; - AT91C_BASE_ADC->ADC_MR = - ADC_MODE_PRESCALE(63 /* was 32 */) | // ADC_CLK = MCK / ((63+1) * 2) = 48MHz / 128 = 375kHz - ADC_MODE_STARTUP_TIME(1 /* was 16 */) | // Startup Time = (1+1) * 8 / ADC_CLK = 16 / 375kHz = 42,7us Note: must be > 20us - ADC_MODE_SAMPLE_HOLD_TIME(15 /* was 8 */); // Sample & Hold Time SHTIM = 15 / ADC_CLK = 15 / 375kHz = 40us - +{ // Note: ADC_MODE_PRESCALE and ADC_MODE_SAMPLE_HOLD_TIME are set to the maximum allowed value. // Both AMPL_LO and AMPL_HI are very high impedance (10MOhm) outputs, the input capacitance of the ADC is 12pF (typical). This results in a time constant // of RC = 10MOhm * 12pF = 120us. Even after the maximum configurable sample&hold time of 40us the input capacitor will not be fully charged. @@ -154,16 +146,19 @@ static int ReadAdc(int ch) // v_cap = v_in * (1 - exp(-RC/SHTIM)) = v_in * (1 - exp(-3)) = v_in * 0,95 (i.e. an error of 5%) // // Note: with the "historic" values in the comments above, the error was 34% !!! - - AT91C_BASE_ADC->ADC_CHER = ADC_CHANNEL(ch); + AT91C_BASE_ADC->ADC_CR = AT91C_ADC_SWRST; + AT91C_BASE_ADC->ADC_MR = + ADC_MODE_PRESCALE(63 /* was 32 */) | // ADC_CLK = MCK / ((63+1) * 2) = 48MHz / 128 = 375kHz + ADC_MODE_STARTUP_TIME(1 /* was 16 */) | // Startup Time = (1+1) * 8 / ADC_CLK = 16 / 375kHz = 42,7us Note: must be > 20us + ADC_MODE_SAMPLE_HOLD_TIME(15 /* was 8 */); // Sample & Hold Time SHTIM = 15 / ADC_CLK = 15 / 375kHz = 40us + + AT91C_BASE_ADC->ADC_CHER = ADC_CHANNEL(ch); AT91C_BASE_ADC->ADC_CR = AT91C_ADC_START; - while(!(AT91C_BASE_ADC->ADC_SR & ADC_END_OF_CONVERSION(ch))) - ; - d = AT91C_BASE_ADC->ADC_CDR[ch]; - - return d; + while(!(AT91C_BASE_ADC->ADC_SR & ADC_END_OF_CONVERSION(ch))) {}; + + return AT91C_BASE_ADC->ADC_CDR[ch]; } int AvgAdc(int ch) // was static - merlok @@ -180,7 +175,8 @@ int AvgAdc(int ch) // was static - merlok void MeasureAntennaTuningLfOnly(int *vLf125, int *vLf134, int *peakf, int *peakv, uint8_t LF_Results[]) { - int i, adcval = 0, peak = 0; + uint8_t i; + int adcval = 0, peak = 0; /* * Sweeps the useful LF range of the proxmark from @@ -193,6 +189,8 @@ void MeasureAntennaTuningLfOnly(int *vLf125, int *vLf134, int *peakf, int *peakv FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); + SpinDelay(50); + for (i=255; i>=19; i--) { WDT_HIT(); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, i); @@ -201,7 +199,7 @@ void MeasureAntennaTuningLfOnly(int *vLf125, int *vLf134, int *peakf, int *peakv if (i==95) *vLf125 = adcval; // voltage at 125Khz if (i==89) *vLf134 = adcval; // voltage at 134Khz - LF_Results[i] = adcval>>8; // scale int to fit in byte for graphing purposes + LF_Results[i] = adcval >> 9; // scale int to fit in byte for graphing purposes if(LF_Results[i] > peak) { *peakv = adcval; peak = LF_Results[i]; From 9b32625c2c1d7ef36e4788c9a5f827470078f81d Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Thu, 11 Jan 2018 08:32:01 +0100 Subject: [PATCH 138/310] hw tune minor fix, comments updated --- armsrc/appmain.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 65fc6de6..1c3d9398 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -137,21 +137,19 @@ void Dbhexdump(int len, uint8_t *d, bool bAsci) { static int ReadAdc(int ch) { // Note: ADC_MODE_PRESCALE and ADC_MODE_SAMPLE_HOLD_TIME are set to the maximum allowed value. - // Both AMPL_LO and AMPL_HI are very high impedance (10MOhm) outputs, the input capacitance of the ADC is 12pF (typical). This results in a time constant - // of RC = 10MOhm * 12pF = 120us. Even after the maximum configurable sample&hold time of 40us the input capacitor will not be fully charged. + // AMPL_HI is are high impedance (10MOhm || 1MOhm) output, the input capacitance of the ADC is 12pF (typical). This results in a time constant + // of RC = (0.91MOhm) * 12pF = 10.9us. Even after the maximum configurable sample&hold time of 40us the input capacitor will not be fully charged. // // The maths are: // If there is a voltage v_in at the input, the voltage v_cap at the capacitor (this is what we are measuring) will be // - // v_cap = v_in * (1 - exp(-RC/SHTIM)) = v_in * (1 - exp(-3)) = v_in * 0,95 (i.e. an error of 5%) - // - // Note: with the "historic" values in the comments above, the error was 34% !!! + // v_cap = v_in * (1 - exp(-SHTIM/RC)) = v_in * (1 - exp(-40us/10.9us)) = v_in * 0,97 (i.e. an error of 3%) AT91C_BASE_ADC->ADC_CR = AT91C_ADC_SWRST; AT91C_BASE_ADC->ADC_MR = - ADC_MODE_PRESCALE(63 /* was 32 */) | // ADC_CLK = MCK / ((63+1) * 2) = 48MHz / 128 = 375kHz - ADC_MODE_STARTUP_TIME(1 /* was 16 */) | // Startup Time = (1+1) * 8 / ADC_CLK = 16 / 375kHz = 42,7us Note: must be > 20us - ADC_MODE_SAMPLE_HOLD_TIME(15 /* was 8 */); // Sample & Hold Time SHTIM = 15 / ADC_CLK = 15 / 375kHz = 40us + ADC_MODE_PRESCALE(63) | // ADC_CLK = MCK / ((63+1) * 2) = 48MHz / 128 = 375kHz + ADC_MODE_STARTUP_TIME(1) | // Startup Time = (1+1) * 8 / ADC_CLK = 16 / 375kHz = 42,7us Note: must be > 20us + ADC_MODE_SAMPLE_HOLD_TIME(15); // Sample & Hold Time SHTIM = 15 / ADC_CLK = 15 / 375kHz = 40us AT91C_BASE_ADC->ADC_CHER = ADC_CHANNEL(ch); AT91C_BASE_ADC->ADC_CR = AT91C_ADC_START; @@ -175,8 +173,7 @@ int AvgAdc(int ch) // was static - merlok void MeasureAntennaTuningLfOnly(int *vLf125, int *vLf134, int *peakf, int *peakv, uint8_t LF_Results[]) { - uint8_t i; - int adcval = 0, peak = 0; + int i, adcval = 0, peak = 0; /* * Sweeps the useful LF range of the proxmark from From d040cb90240062a2c164800edce6a93e440ff693 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Fri, 12 Jan 2018 21:48:42 +0100 Subject: [PATCH 139/310] hw tune: * scale results to allow higher LF voltages * remove historic comments --- armsrc/appmain.c | 2 +- client/cmddata.c | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 1c3d9398..64768922 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -244,7 +244,7 @@ void MeasureAntennaTuning(int mode) } } - cmd_send(CMD_MEASURED_ANTENNA_TUNING, vLf125 | (vLf134<<16), vHf, peakf | (peakv<<16), LF_Results, 256); + cmd_send(CMD_MEASURED_ANTENNA_TUNING, vLf125>>1 | (vLf134>>1<<16), vHf, peakf | (peakv>>1<<16), LF_Results, 256); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_B_OFF(); return; diff --git a/client/cmddata.c b/client/cmddata.c index 1f548284..9b960de9 100644 --- a/client/cmddata.c +++ b/client/cmddata.c @@ -1281,26 +1281,26 @@ int CmdTuneSamples(const char *Cmd) peakf = resp.arg[2] & 0xffff; peakv = resp.arg[2] >> 16; PrintAndLog(""); - PrintAndLog("# LF antenna: %5.2f V @ 125.00 kHz", vLf125/1000.0); - PrintAndLog("# LF antenna: %5.2f V @ 134.00 kHz", vLf134/1000.0); - PrintAndLog("# LF optimal: %5.2f V @%9.2f kHz", peakv/1000.0, 12000.0/(peakf+1)); + PrintAndLog("# LF antenna: %5.2f V @ 125.00 kHz", vLf125/500.0); + PrintAndLog("# LF antenna: %5.2f V @ 134.00 kHz", vLf134/500.0); + PrintAndLog("# LF optimal: %5.2f V @%9.2f kHz", peakv/500.0, 12000.0/(peakf+1)); PrintAndLog("# HF antenna: %5.2f V @ 13.56 MHz", vHf/1000.0); - #define LF_UNUSABLE_V 2948 // was 2000. Changed due to bugfix in voltage measurements. LF results are now 47% higher. - #define LF_MARGINAL_V 14739 // was 10000. Changed due to bugfix bug in voltage measurements. LF results are now 47% higher. - #define HF_UNUSABLE_V 3167 // was 2000. Changed due to bugfix in voltage measurements. HF results are now 58% higher. - #define HF_MARGINAL_V 7917 // was 5000. Changed due to bugfix in voltage measurements. HF results are now 58% higher. + #define LF_UNUSABLE_V 3000 + #define LF_MARGINAL_V 15000 + #define HF_UNUSABLE_V 3200 + #define HF_MARGINAL_V 8000 - if (peakv < LF_UNUSABLE_V) + if (peakv<<1 < LF_UNUSABLE_V) PrintAndLog("# Your LF antenna is unusable."); - else if (peakv < LF_MARGINAL_V) + else if (peakv<<1 < LF_MARGINAL_V) PrintAndLog("# Your LF antenna is marginal."); if (vHf < HF_UNUSABLE_V) PrintAndLog("# Your HF antenna is unusable."); else if (vHf < HF_MARGINAL_V) PrintAndLog("# Your HF antenna is marginal."); - if (peakv >= LF_UNUSABLE_V) { + if (peakv<<1 >= LF_UNUSABLE_V) { for (int i = 0; i < 256; i++) { GraphBuffer[i] = resp.d.asBytes[i] - 128; } From 078e2bd238dfddb39c33de775f5e0a68657c4504 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Fri, 12 Jan 2018 23:04:16 +0200 Subject: [PATCH 140/310] fix: SIMD instruction set detection on non-x86 hardware should fix issue #542 --- client/hardnested/hardnested_bf_core.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/client/hardnested/hardnested_bf_core.c b/client/hardnested/hardnested_bf_core.c index 78384bbc..d02209e9 100644 --- a/client/hardnested/hardnested_bf_core.c +++ b/client/hardnested/hardnested_bf_core.c @@ -590,9 +590,13 @@ SIMDExecInstr GetSIMDInstrAuto() { // determine the available instruction set at runtime and call the correct function const uint64_t crack_states_bitsliced_dispatch(uint32_t cuid, uint8_t *best_first_bytes, statelist_t *p, uint32_t *keys_found, uint64_t *num_keys_tested, uint32_t nonces_to_bruteforce, uint8_t *bf_test_nonce_2nd_byte, noncelist_t *nonces) { switch(GetSIMDInstrAuto()) { +#if defined (__i386__) || defined (__x86_64__) +#if !defined(__APPLE__) || (defined(__APPLE__) && (__clang_major__ > 8 || __clang_major__ == 8 && __clang_minor__ >= 1)) +#if (__GNUC__ >= 5) && (__GNUC__ > 5 || __GNUC_MINOR__ > 2) case SIMD_AVX512: crack_states_bitsliced_function_p = &crack_states_bitsliced_AVX512; break; +#endif case SIMD_AVX2: crack_states_bitsliced_function_p = &crack_states_bitsliced_AVX2; break; @@ -605,6 +609,8 @@ const uint64_t crack_states_bitsliced_dispatch(uint32_t cuid, uint8_t *best_firs case SIMD_MMX: crack_states_bitsliced_function_p = &crack_states_bitsliced_MMX; break; +#endif +#endif default: crack_states_bitsliced_function_p = &crack_states_bitsliced_NOSIMD; break; @@ -616,9 +622,13 @@ const uint64_t crack_states_bitsliced_dispatch(uint32_t cuid, uint8_t *best_firs void bitslice_test_nonces_dispatch(uint32_t nonces_to_bruteforce, uint32_t *bf_test_nonce, uint8_t *bf_test_nonce_par) { switch(GetSIMDInstrAuto()) { +#if defined (__i386__) || defined (__x86_64__) +#if !defined(__APPLE__) || (defined(__APPLE__) && (__clang_major__ > 8 || __clang_major__ == 8 && __clang_minor__ >= 1)) +#if (__GNUC__ >= 5) && (__GNUC__ > 5 || __GNUC_MINOR__ > 2) case SIMD_AVX512: bitslice_test_nonces_function_p = &bitslice_test_nonces_AVX512; break; +#endif case SIMD_AVX2: bitslice_test_nonces_function_p = &bitslice_test_nonces_AVX2; break; @@ -631,6 +641,8 @@ void bitslice_test_nonces_dispatch(uint32_t nonces_to_bruteforce, uint32_t *bf_t case SIMD_MMX: bitslice_test_nonces_function_p = &bitslice_test_nonces_MMX; break; +#endif +#endif default: bitslice_test_nonces_function_p = &bitslice_test_nonces_NOSIMD; break; From 8ec06f5ef3c5c63a420b8292dd3ea5d0a5d6974e Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Fri, 12 Jan 2018 23:37:55 +0200 Subject: [PATCH 141/310] hf mf sniff: small fixes --- armsrc/iso14443a.c | 4 ++-- client/cmdhfmf.c | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 94ca52f5..7d589f0e 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -2482,7 +2482,7 @@ void RAMFUNC SniffMifare(uint8_t param) { for(uint32_t sniffCounter = 0; true; ) { if(BUTTON_PRESS()) { - DbpString("cancelled by button"); + DbpString("Canceled by button."); break; } @@ -2576,7 +2576,7 @@ void RAMFUNC SniffMifare(uint8_t param) { } // main cycle - DbpString("COMMAND FINISHED"); + DbpString("COMMAND FINISHED."); FpgaDisableSscDma(); MfSniffEnd(); diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 4956df8c..b653cf30 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -2527,14 +2527,13 @@ int CmdHF14AMfSniff(const char *Cmd){ } UsbCommand resp; - if (WaitForResponseTimeout(CMD_ACK,&resp,2000)) { + if (WaitForResponseTimeoutW(CMD_ACK, &resp, 2000, false)) { res = resp.arg[0] & 0xff; uint16_t traceLen = resp.arg[1]; len = resp.arg[2]; if (res == 0) { // we are done - free(buf); - return 0; + break; } if (res == 1) { // there is (more) data to be transferred @@ -2610,6 +2609,9 @@ int CmdHF14AMfSniff(const char *Cmd){ } // while (true) free(buf); + + msleep(300); // wait for exiting arm side. + PrintAndLog("Done."); return 0; } From d1f9ec06aec73db05a48df42fc1eb51a399754ba Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Mon, 15 Jan 2018 15:29:14 +0100 Subject: [PATCH 142/310] Revert changes in hardnested nonces collection. Sacrifice some speed in favor of stability for some card types (#547) --- armsrc/mifarecmd.c | 16 ++++++++++++---- armsrc/mifareutil.h | 2 -- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index fcfd7e8f..3854b589 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -20,6 +20,9 @@ #include "parity.h" #include "crc.h" +#define HARDNESTED_AUTHENTICATION_TIMEOUT 848 // card times out 1ms after wrong authentication (according to NXP documentation) +#define HARDNESTED_PRE_AUTHENTICATION_LEADTIME 400 // some (non standard) cards need a pause after select before they are ready for first authentication + // the block number for the ISO14443-4 PCB static uint8_t pcb_blocknum = 0; // Deselect card by sending a s-block. the crc is precalced for speed @@ -677,7 +680,7 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, } if (slow) { - timeout = GetCountSspClk() + PRE_AUTHENTICATION_LEADTIME; + timeout = GetCountSspClk() + HARDNESTED_PRE_AUTHENTICATION_LEADTIME; while(GetCountSspClk() < timeout); } @@ -694,10 +697,12 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, continue; } - // send a dummy response in order to trigger the cards authentication failure timeout - uint8_t dummy_answer[8] = {0}; - ReaderTransmit(dummy_answer, 8, NULL); + // send an incomplete dummy response in order to trigger the card's authentication failure timeout + uint8_t dummy_answer[1] = {0}; + ReaderTransmit(dummy_answer, 1, NULL); + timeout = GetCountSspClk() + HARDNESTED_AUTHENTICATION_TIMEOUT; + num_nonces++; if (num_nonces % 2) { memcpy(buf+i, receivedAnswer, 4); @@ -709,6 +714,9 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, i += 9; } + // wait for the card to become ready again + while(GetCountSspClk() < timeout); + } LED_C_OFF(); diff --git a/armsrc/mifareutil.h b/armsrc/mifareutil.h index c34dc8f4..b2912895 100644 --- a/armsrc/mifareutil.h +++ b/armsrc/mifareutil.h @@ -24,8 +24,6 @@ #define CRYPT_REQUEST 2 #define AUTH_FIRST 0 #define AUTH_NESTED 2 -#define AUTHENTICATION_TIMEOUT 848 // card times out 1ms after wrong authentication (according to NXP documentation) -#define PRE_AUTHENTICATION_LEADTIME 400 // some (non standard) cards need a pause after select before they are ready for first authentication // mifare 4bit card answers #define CARD_ACK 0x0A // 1010 - ACK From feb328c90ab6287c691b578d6a41fe5cf50a987d Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 16 Jan 2018 14:34:04 +0200 Subject: [PATCH 143/310] `hf mf sniff` small fix --- armsrc/iso14443a.c | 9 +++++++-- armsrc/mifaresniff.c | 12 +++--------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 7d589f0e..bd37fe9a 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -2539,7 +2539,9 @@ void RAMFUNC SniffMifare(uint8_t param) { if(!TagIsActive) { // no need to try decoding tag data if the reader is sending uint8_t readerdata = (previous_data & 0xF0) | (*data >> 4); if(MillerDecoding(readerdata, (sniffCounter-1)*4)) { - LED_C_INV(); + LED_B_ON(); + LED_C_OFF(); + if (MfSniffLogic(receivedCmd, Uart.len, Uart.parity, Uart.bitCount, true)) break; /* And ready to receive another command. */ @@ -2549,12 +2551,14 @@ void RAMFUNC SniffMifare(uint8_t param) { DemodReset(); } ReaderIsActive = (Uart.state != STATE_UNSYNCD); + TagIsActive = !ReaderIsActive; } if(!ReaderIsActive) { // no need to try decoding tag data if the reader is sending uint8_t tagdata = (previous_data << 4) | (*data & 0x0F); if(ManchesterDecoding(tagdata, 0, (sniffCounter-1)*4)) { - LED_C_INV(); + LED_B_OFF(); + LED_C_ON(); if (MfSniffLogic(receivedResponse, Demod.len, Demod.parity, Demod.bitCount, false)) break; @@ -2564,6 +2568,7 @@ void RAMFUNC SniffMifare(uint8_t param) { UartInit(receivedCmd, receivedCmdPar); } TagIsActive = (Demod.state != DEMOD_UNSYNCD); + ReaderIsActive = !TagIsActive; } } diff --git a/armsrc/mifaresniff.c b/armsrc/mifaresniff.c index 7f94b0fe..b9ab3df4 100644 --- a/armsrc/mifaresniff.c +++ b/armsrc/mifaresniff.c @@ -124,16 +124,10 @@ bool RAMFUNC MfSniffLogic(const uint8_t *data, uint16_t len, uint8_t *parity, ui sniffBuf[12] = 0xFF; sniffBuf[13] = 0xFF; LogTrace(sniffBuf, 14, 0, 0, NULL, TRUE); - } // intentionally no break; - case SNF_CARD_CMD:{ - LogTrace(data, len, 0, 0, NULL, TRUE); - sniffState = SNF_CARD_RESP; - timerData = GetTickCount(); - break; - } - case SNF_CARD_RESP:{ - LogTrace(data, len, 0, 0, NULL, FALSE); sniffState = SNF_CARD_CMD; + } // intentionally no break; + case SNF_CARD_CMD:{ + LogTrace(data, len, 0, 0, NULL, reader); timerData = GetTickCount(); break; } From 3544b99715671fa9809ad2e2565a436cf79316f8 Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 16 Jan 2018 17:54:54 +0200 Subject: [PATCH 144/310] fixed #541 --- armsrc/mifaresniff.c | 341 ++++++++++++++++++++++--------------------- 1 file changed, 174 insertions(+), 167 deletions(-) diff --git a/armsrc/mifaresniff.c b/armsrc/mifaresniff.c index b9ab3df4..08fb9f23 100644 --- a/armsrc/mifaresniff.c +++ b/armsrc/mifaresniff.c @@ -1,177 +1,184 @@ -//----------------------------------------------------------------------------- -// Merlok - 2012 -// -// 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. -//----------------------------------------------------------------------------- -// Routines to support mifare classic sniffer. -//----------------------------------------------------------------------------- - -#include "mifaresniff.h" -#include "apps.h" -#include "proxmark3.h" -#include "util.h" -#include "string.h" -#include "iso14443crc.h" -#include "iso14443a.h" -#include "crapto1/crapto1.h" -#include "mifareutil.h" -#include "common.h" - - -static int sniffState = SNF_INIT; -static uint8_t sniffUIDType; -static uint8_t sniffUID[8] = {0x00}; -static uint8_t sniffATQA[2] = {0x00}; -static uint8_t sniffSAK; -static uint8_t sniffBuf[16] = {0x00}; -static uint32_t timerData = 0; - - -bool MfSniffInit(void){ - memset(sniffUID, 0x00, 8); - memset(sniffATQA, 0x00, 2); - sniffSAK = 0; - sniffUIDType = SNF_UID_4; - - return FALSE; -} - -bool MfSniffEnd(void){ - LED_B_ON(); - cmd_send(CMD_ACK,0,0,0,0,0); - LED_B_OFF(); - - return FALSE; -} - -bool RAMFUNC MfSniffLogic(const uint8_t *data, uint16_t len, uint8_t *parity, uint16_t bitCnt, bool reader) { - - if (reader && (len == 1) && (bitCnt == 7)) { // reset on 7-Bit commands from reader - sniffState = SNF_INIT; - } - - switch (sniffState) { - case SNF_INIT:{ - if ((len == 1) && (reader) && (bitCnt == 7) ) { // REQA or WUPA from reader - sniffUIDType = SNF_UID_4; - memset(sniffUID, 0x00, 8); - memset(sniffATQA, 0x00, 2); - sniffSAK = 0; - sniffState = SNF_WUPREQ; - } - break; - } - case SNF_WUPREQ:{ - if ((!reader) && (len == 2)) { // ATQA from tag - memcpy(sniffATQA, data, 2); - sniffState = SNF_ATQA; - } - break; - } - case SNF_ATQA:{ - if ((reader) && (len == 2) && (data[0] == 0x93) && (data[1] == 0x20)) { // Select ALL from reader - sniffState = SNF_ANTICOL1; - } - break; - } - case SNF_ANTICOL1:{ - if ((!reader) && (len == 5) && ((data[0] ^ data[1] ^ data[2] ^ data[3]) == data[4])) { // UID from tag (CL1) - memcpy(sniffUID + 3, data, 4); - sniffState = SNF_UID1; - } - break; - } +//----------------------------------------------------------------------------- +// Merlok - 2012 +// +// 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. +//----------------------------------------------------------------------------- +// Routines to support mifare classic sniffer. +//----------------------------------------------------------------------------- + +#include "mifaresniff.h" +#include "apps.h" +#include "proxmark3.h" +#include "util.h" +#include "string.h" +#include "iso14443crc.h" +#include "iso14443a.h" +#include "crapto1/crapto1.h" +#include "mifareutil.h" +#include "common.h" + + +static int sniffState = SNF_INIT; +static uint8_t sniffUIDType; +static uint8_t sniffUID[8] = {0x00}; +static uint8_t sniffATQA[2] = {0x00}; +static uint8_t sniffSAK; +static uint8_t sniffBuf[16] = {0x00}; +static uint32_t timerData = 0; + + +bool MfSniffInit(void){ + memset(sniffUID, 0x00, 8); + memset(sniffATQA, 0x00, 2); + sniffSAK = 0; + sniffUIDType = SNF_UID_4; + + return FALSE; +} + +bool MfSniffEnd(void){ + LED_B_ON(); + cmd_send(CMD_ACK,0,0,0,0,0); + LED_B_OFF(); + + return FALSE; +} + +bool RAMFUNC MfSniffLogic(const uint8_t *data, uint16_t len, uint8_t *parity, uint16_t bitCnt, bool reader) { + + if (reader && (len == 1) && (bitCnt == 7)) { // reset on 7-Bit commands from reader + sniffState = SNF_INIT; + } + + switch (sniffState) { + case SNF_INIT:{ + if ((len == 1) && (reader) && (bitCnt == 7) ) { // REQA or WUPA from reader + sniffUIDType = SNF_UID_4; + memset(sniffUID, 0x00, 8); + memset(sniffATQA, 0x00, 2); + sniffSAK = 0; + sniffState = SNF_WUPREQ; + } + break; + } + case SNF_WUPREQ:{ + if ((!reader) && (len == 2)) { // ATQA from tag + memcpy(sniffATQA, data, 2); + sniffState = SNF_ATQA; + } + break; + } + case SNF_ATQA: case SNF_UID1:{ + // SNF_ATQA + if ((reader) && (len == 2) && (data[0] == 0x93) && (data[1] == 0x20)) { // Select ALL from reader + sniffState = SNF_ANTICOL1; + } + + // SNF_UID1 if ((reader) && (len == 9) && (data[0] == 0x93) && (data[1] == 0x70) && (CheckCrc14443(CRC_14443_A, data, 9))) { // Select 4 Byte UID from reader + memcpy(sniffUID + 3, &data[2], 4); sniffState = SNF_SAK; } - break; - } - case SNF_SAK:{ - if ((!reader) && (len == 3) && (CheckCrc14443(CRC_14443_A, data, 3))) { // SAK from card? - sniffSAK = data[0]; - if (sniffUID[3] == 0x88) { // CL2 UID part to be expected - sniffState = SNF_ANTICOL2; - } else { // select completed - sniffState = SNF_CARD_IDLE; - } - } - break; - } - case SNF_ANTICOL2:{ - if ((!reader) && (len == 5) && ((data[0] ^ data[1] ^ data[2] ^ data[3]) == data[4])) { // CL2 UID - memcpy(sniffUID, sniffUID+4, 3); - memcpy(sniffUID+3, data, 4); - sniffUIDType = SNF_UID_7; - sniffState = SNF_UID2; - } - break; + break; } + case SNF_ANTICOL1:{ + if ((!reader) && (len == 5) && ((data[0] ^ data[1] ^ data[2] ^ data[3]) == data[4])) { // UID from tag (CL1) + memcpy(sniffUID + 3, data, 4); + sniffState = SNF_UID1; + } + break; + } + case SNF_SAK:{ + if ((!reader) && (len == 3) && (CheckCrc14443(CRC_14443_A, data, 3))) { // SAK from card? + sniffSAK = data[0]; + if ((sniffUID[3] == 0x88) && (sniffUIDType == SNF_UID_4)) { // CL2 UID part to be expected + sniffUIDType = SNF_UID_7; + memcpy(sniffUID, sniffUID + 4, 3); + sniffState = SNF_UID2; + } else { // select completed + sniffState = SNF_CARD_IDLE; + } + } + break; + } + case SNF_ANTICOL2:{ + if ((!reader) && (len == 5) && ((data[0] ^ data[1] ^ data[2] ^ data[3]) == data[4])) { // CL2 UID + memcpy(sniffUID + 3, data, 4); + sniffState = SNF_UID2; + } + break; + } case SNF_UID2:{ - if ((reader) && (len == 9) && (data[0] == 0x95) && (data[1] == 0x70) && (CheckCrc14443(CRC_14443_A, data, 9))) { // Select 2nd part of 7 Byte UID + if ((reader) && (len == 2) && (data[0] == 0x95) && (data[1] == 0x20)) { + sniffState = SNF_ANTICOL2; + } + + if ((reader) && (len == 9) && (data[0] == 0x95) && (data[1] == 0x70) && (CheckCrc14443(CRC_14443_A, data, 9))) { + memcpy(sniffUID + 3, &data[2], 4); sniffState = SNF_SAK; } break; } - case SNF_CARD_IDLE:{ // trace the card select sequence - sniffBuf[0] = 0xFF; - sniffBuf[1] = 0xFF; - memcpy(sniffBuf + 2, sniffUID, 7); - memcpy(sniffBuf + 9, sniffATQA, 2); - sniffBuf[11] = sniffSAK; - sniffBuf[12] = 0xFF; - sniffBuf[13] = 0xFF; - LogTrace(sniffBuf, 14, 0, 0, NULL, TRUE); - sniffState = SNF_CARD_CMD; - } // intentionally no break; - case SNF_CARD_CMD:{ - LogTrace(data, len, 0, 0, NULL, reader); - timerData = GetTickCount(); - break; - } - - default: - sniffState = SNF_INIT; - break; - } - - - return FALSE; -} - -bool RAMFUNC MfSniffSend(uint16_t maxTimeoutMs) { - if (BigBuf_get_traceLen() && (GetTickCount() > timerData + maxTimeoutMs)) { - return intMfSniffSend(); - } - return FALSE; -} - -// internal sending function. not a RAMFUNC. -bool intMfSniffSend() { - - int pckSize = 0; - int pckLen = BigBuf_get_traceLen(); - int pckNum = 0; - uint8_t *trace = BigBuf_get_addr(); - - FpgaDisableSscDma(); - while (pckLen > 0) { - pckSize = MIN(USB_CMD_DATA_SIZE, pckLen); - LED_B_ON(); - cmd_send(CMD_ACK, 1, BigBuf_get_traceLen(), pckSize, trace + BigBuf_get_traceLen() - pckLen, pckSize); - LED_B_OFF(); - - pckLen -= pckSize; - pckNum++; - } - - LED_B_ON(); - cmd_send(CMD_ACK,2,0,0,0,0); - LED_B_OFF(); - - clear_trace(); - - return TRUE; -} + case SNF_CARD_IDLE:{ // trace the card select sequence + sniffBuf[0] = 0xFF; + sniffBuf[1] = 0xFF; + memcpy(sniffBuf + 2, sniffUID, 7); + memcpy(sniffBuf + 9, sniffATQA, 2); + sniffBuf[11] = sniffSAK; + sniffBuf[12] = 0xFF; + sniffBuf[13] = 0xFF; + LogTrace(sniffBuf, 14, 0, 0, NULL, TRUE); + sniffState = SNF_CARD_CMD; + } // intentionally no break; + case SNF_CARD_CMD:{ + LogTrace(data, len, 0, 0, NULL, reader); + timerData = GetTickCount(); + break; + } + + default: + sniffState = SNF_INIT; + break; + } + + + return FALSE; +} + +bool RAMFUNC MfSniffSend(uint16_t maxTimeoutMs) { + if (BigBuf_get_traceLen() && (GetTickCount() > timerData + maxTimeoutMs)) { + return intMfSniffSend(); + } + return FALSE; +} + +// internal sending function. not a RAMFUNC. +bool intMfSniffSend() { + + int pckSize = 0; + int pckLen = BigBuf_get_traceLen(); + int pckNum = 0; + uint8_t *trace = BigBuf_get_addr(); + + FpgaDisableSscDma(); + while (pckLen > 0) { + pckSize = MIN(USB_CMD_DATA_SIZE, pckLen); + LED_B_ON(); + cmd_send(CMD_ACK, 1, BigBuf_get_traceLen(), pckSize, trace + BigBuf_get_traceLen() - pckLen, pckSize); + LED_B_OFF(); + + pckLen -= pckSize; + pckNum++; + } + + LED_B_ON(); + cmd_send(CMD_ACK,2,0,0,0,0); + LED_B_OFF(); + + clear_trace(); + + return TRUE; +} From 35cc71b0752a8595c96f127eced98112fe2a5a0b Mon Sep 17 00:00:00 2001 From: merlokk Date: Wed, 17 Jan 2018 14:10:26 +0200 Subject: [PATCH 145/310] delete opposite states fix --- armsrc/iso14443a.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index bd37fe9a..6703cc65 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -2551,7 +2551,6 @@ void RAMFUNC SniffMifare(uint8_t param) { DemodReset(); } ReaderIsActive = (Uart.state != STATE_UNSYNCD); - TagIsActive = !ReaderIsActive; } if(!ReaderIsActive) { // no need to try decoding tag data if the reader is sending @@ -2568,7 +2567,6 @@ void RAMFUNC SniffMifare(uint8_t param) { UartInit(receivedCmd, receivedCmdPar); } TagIsActive = (Demod.state != DEMOD_UNSYNCD); - ReaderIsActive = !TagIsActive; } } From 968cb35498c6d6331a4c65b1f23dad2bbeb79f6f Mon Sep 17 00:00:00 2001 From: merlokk Date: Wed, 17 Jan 2018 14:14:36 +0200 Subject: [PATCH 146/310] deleted save UID from card --- armsrc/mifaresniff.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/armsrc/mifaresniff.c b/armsrc/mifaresniff.c index 08fb9f23..f4879329 100644 --- a/armsrc/mifaresniff.c +++ b/armsrc/mifaresniff.c @@ -86,7 +86,6 @@ bool RAMFUNC MfSniffLogic(const uint8_t *data, uint16_t len, uint8_t *parity, ui } case SNF_ANTICOL1:{ if ((!reader) && (len == 5) && ((data[0] ^ data[1] ^ data[2] ^ data[3]) == data[4])) { // UID from tag (CL1) - memcpy(sniffUID + 3, data, 4); sniffState = SNF_UID1; } break; @@ -106,7 +105,6 @@ bool RAMFUNC MfSniffLogic(const uint8_t *data, uint16_t len, uint8_t *parity, ui } case SNF_ANTICOL2:{ if ((!reader) && (len == 5) && ((data[0] ^ data[1] ^ data[2] ^ data[3]) == data[4])) { // CL2 UID - memcpy(sniffUID + 3, data, 4); sniffState = SNF_UID2; } break; From 543a6ed3c99fb1f1324b25d7d2132133dce904dc Mon Sep 17 00:00:00 2001 From: merlokk Date: Wed, 17 Jan 2018 15:48:01 +0200 Subject: [PATCH 147/310] SNF_WUPREQ and SNF_SAK harmonized) --- armsrc/mifaresniff.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/armsrc/mifaresniff.c b/armsrc/mifaresniff.c index f4879329..3929561e 100644 --- a/armsrc/mifaresniff.c +++ b/armsrc/mifaresniff.c @@ -59,25 +59,22 @@ bool RAMFUNC MfSniffLogic(const uint8_t *data, uint16_t len, uint8_t *parity, ui memset(sniffUID, 0x00, 8); memset(sniffATQA, 0x00, 2); sniffSAK = 0; - sniffState = SNF_WUPREQ; - } - break; - } - case SNF_WUPREQ:{ - if ((!reader) && (len == 2)) { // ATQA from tag - memcpy(sniffATQA, data, 2); sniffState = SNF_ATQA; } break; } - case SNF_ATQA: + case SNF_ATQA:{ + if ((!reader) && (len == 2)) { // ATQA from tag + memcpy(sniffATQA, data, 2); + sniffState = SNF_UID1; + } + break; + } case SNF_UID1:{ - // SNF_ATQA if ((reader) && (len == 2) && (data[0] == 0x93) && (data[1] == 0x20)) { // Select ALL from reader sniffState = SNF_ANTICOL1; } - // SNF_UID1 if ((reader) && (len == 9) && (data[0] == 0x93) && (data[1] == 0x70) && (CheckCrc14443(CRC_14443_A, data, 9))) { // Select 4 Byte UID from reader memcpy(sniffUID + 3, &data[2], 4); sniffState = SNF_SAK; From 3e77c7a7b4859c18345dac5652c70c280eb1ca10 Mon Sep 17 00:00:00 2001 From: merlokk Date: Wed, 17 Jan 2018 15:57:24 +0200 Subject: [PATCH 148/310] ignore SELECT ALL --- armsrc/mifaresniff.c | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/armsrc/mifaresniff.c b/armsrc/mifaresniff.c index 3929561e..20b54e82 100644 --- a/armsrc/mifaresniff.c +++ b/armsrc/mifaresniff.c @@ -71,22 +71,12 @@ bool RAMFUNC MfSniffLogic(const uint8_t *data, uint16_t len, uint8_t *parity, ui break; } case SNF_UID1:{ - if ((reader) && (len == 2) && (data[0] == 0x93) && (data[1] == 0x20)) { // Select ALL from reader - sniffState = SNF_ANTICOL1; - } - if ((reader) && (len == 9) && (data[0] == 0x93) && (data[1] == 0x70) && (CheckCrc14443(CRC_14443_A, data, 9))) { // Select 4 Byte UID from reader memcpy(sniffUID + 3, &data[2], 4); sniffState = SNF_SAK; } break; } - case SNF_ANTICOL1:{ - if ((!reader) && (len == 5) && ((data[0] ^ data[1] ^ data[2] ^ data[3]) == data[4])) { // UID from tag (CL1) - sniffState = SNF_UID1; - } - break; - } case SNF_SAK:{ if ((!reader) && (len == 3) && (CheckCrc14443(CRC_14443_A, data, 3))) { // SAK from card? sniffSAK = data[0]; @@ -100,17 +90,7 @@ bool RAMFUNC MfSniffLogic(const uint8_t *data, uint16_t len, uint8_t *parity, ui } break; } - case SNF_ANTICOL2:{ - if ((!reader) && (len == 5) && ((data[0] ^ data[1] ^ data[2] ^ data[3]) == data[4])) { // CL2 UID - sniffState = SNF_UID2; - } - break; - } case SNF_UID2:{ - if ((reader) && (len == 2) && (data[0] == 0x95) && (data[1] == 0x20)) { - sniffState = SNF_ANTICOL2; - } - if ((reader) && (len == 9) && (data[0] == 0x95) && (data[1] == 0x70) && (CheckCrc14443(CRC_14443_A, data, 9))) { memcpy(sniffUID + 3, &data[2], 4); sniffState = SNF_SAK; From f7887fa82efb7a30b9a572bb2f379b90e62fde6a Mon Sep 17 00:00:00 2001 From: merlokk Date: Wed, 17 Jan 2018 16:01:56 +0200 Subject: [PATCH 149/310] added magic cards --- armsrc/mifaresniff.c | 7 +++++++ armsrc/mifaresniff.h | 1 + 2 files changed, 8 insertions(+) diff --git a/armsrc/mifaresniff.c b/armsrc/mifaresniff.c index 20b54e82..4e573be7 100644 --- a/armsrc/mifaresniff.c +++ b/armsrc/mifaresniff.c @@ -60,9 +60,16 @@ bool RAMFUNC MfSniffLogic(const uint8_t *data, uint16_t len, uint8_t *parity, ui memset(sniffATQA, 0x00, 2); sniffSAK = 0; sniffState = SNF_ATQA; + if (data[0] == 0x40) + sniffState = SNF_MAGIC_WUPC2; } break; } + case SNF_MAGIC_WUPC2: + if ((len == 1) && (reader) && (data[0] == 0x43) ) { + sniffState = SNF_CARD_IDLE; + } + break; case SNF_ATQA:{ if ((!reader) && (len == 2)) { // ATQA from tag memcpy(sniffATQA, data, 2); diff --git a/armsrc/mifaresniff.h b/armsrc/mifaresniff.h index 8a8e31a9..b181f982 100644 --- a/armsrc/mifaresniff.h +++ b/armsrc/mifaresniff.h @@ -27,6 +27,7 @@ #define SNF_CARD_IDLE 9 #define SNF_CARD_CMD 10 #define SNF_CARD_RESP 11 +#define SNF_MAGIC_WUPC2 12 #define SNF_UID_4 0 #define SNF_UID_7 0 From a0349483636de9ceb91fdecde41fbbf56d6fc79b Mon Sep 17 00:00:00 2001 From: merlokk Date: Wed, 17 Jan 2018 16:12:50 +0200 Subject: [PATCH 150/310] fix eml file format --- client/mifarehost.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/mifarehost.c b/client/mifarehost.c index 67277b59..398e4f15 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -609,7 +609,8 @@ int saveTraceCard(void) { for (int i = 0; i < 64; i++) { // blocks for (int j = 0; j < 16; j++) // bytes fprintf(f, "%02x", *(traceCard + i * 16 + j)); - fprintf(f,"\n"); + if (i < 63) + fprintf(f,"\n"); } fclose(f); return 0; From 2ba37081ec005c623baf69accca493662cbaa4c3 Mon Sep 17 00:00:00 2001 From: merlokk Date: Wed, 17 Jan 2018 18:16:37 +0200 Subject: [PATCH 151/310] added nested authentication "not implemented" warning --- client/mifarehost.c | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/client/mifarehost.c b/client/mifarehost.c index 398e4f15..d204bf12 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -827,20 +827,30 @@ int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile) { if (len ==4) { traceState = TRACE_IDLE; - at_enc = bytes_to_num(data, 4); + if (!traceCrypto1) { + at_enc = bytes_to_num(data, 4); - // decode key here) - ks2 = ar_enc ^ prng_successor(nt, 64); - ks3 = at_enc ^ prng_successor(nt, 96); - revstate = lfsr_recovery64(ks2, ks3); - lfsr_rollback_word(revstate, 0, 0); - lfsr_rollback_word(revstate, 0, 0); - lfsr_rollback_word(revstate, nr_enc, 1); - lfsr_rollback_word(revstate, uid ^ nt, 0); + // decode key here) + ks2 = ar_enc ^ prng_successor(nt, 64); + ks3 = at_enc ^ prng_successor(nt, 96); + revstate = lfsr_recovery64(ks2, ks3); + lfsr_rollback_word(revstate, 0, 0); + lfsr_rollback_word(revstate, 0, 0); + lfsr_rollback_word(revstate, nr_enc, 1); + lfsr_rollback_word(revstate, uid ^ nt, 0); - crypto1_get_lfsr(revstate, &lfsr); - printf("key> %x%x\n", (unsigned int)((lfsr & 0xFFFFFFFF00000000) >> 32), (unsigned int)(lfsr & 0xFFFFFFFF)); - AddLogUint64(logHexFileName, "key> ", lfsr); + crypto1_get_lfsr(revstate, &lfsr); + printf("key> %x%x\n", (unsigned int)((lfsr & 0xFFFFFFFF00000000) >> 32), (unsigned int)(lfsr & 0xFFFFFFFF)); + AddLogUint64(logHexFileName, "key> ", lfsr); + } else { + printf("key> nested not implemented!\n"); + at_enc = bytes_to_num(data, 4); + + crypto1_destroy(traceCrypto1); + + // not implemented + traceState = TRACE_ERROR; + } int blockShift = ((traceCurBlock & 0xFC) + 3) * 16; if (isBlockEmpty((traceCurBlock & 0xFC) + 3)) memcpy(traceCard + blockShift + 6, trailerAccessBytes, 4); @@ -858,15 +868,6 @@ int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile) { // set cryptosystem state traceCrypto1 = lfsr_recovery64(ks2, ks3); - -// nt = crypto1_word(traceCrypto1, nt ^ uid, 1) ^ nt; - - /* traceCrypto1 = crypto1_create(lfsr); // key in lfsr - crypto1_word(traceCrypto1, nt ^ uid, 0); - crypto1_word(traceCrypto1, ar, 1); - crypto1_word(traceCrypto1, 0, 0); - crypto1_word(traceCrypto1, 0, 0);*/ - return 0; } else { traceState = TRACE_ERROR; From fc175230f431e7e33c7628c6b31064d4f7d1e1ad Mon Sep 17 00:00:00 2001 From: rkblackfire Date: Mon, 22 Jan 2018 18:54:29 +0100 Subject: [PATCH 152/310] dbg: Watchdog resets while 'hw tune lf' loop does not finish, u_int is always '>=0' --- armsrc/appmain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 64768922..3aeb4c8a 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -205,7 +205,7 @@ void MeasureAntennaTuningLfOnly(int *vLf125, int *vLf134, int *peakf, int *peakv } } - for (i=18; i >= 0; i--) LF_Results[i] = 0; + for (i=0; i <= 18; i++) LF_Results[i] = 0; return; } From 095173f72d354f7fee70cdc84115302e633f3cb0 Mon Sep 17 00:00:00 2001 From: rkblackfire Date: Mon, 22 Jan 2018 18:59:48 +0100 Subject: [PATCH 153/310] chg: print only valid tuning data only print the ranges which were tuned, thus not showing an error for 'lf antenna' when called 'hw tune hf' and vice versa --- client/cmddata.c | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/client/cmddata.c b/client/cmddata.c index 9b960de9..9bfe58f9 100644 --- a/client/cmddata.c +++ b/client/cmddata.c @@ -1281,24 +1281,34 @@ int CmdTuneSamples(const char *Cmd) peakf = resp.arg[2] & 0xffff; peakv = resp.arg[2] >> 16; PrintAndLog(""); - PrintAndLog("# LF antenna: %5.2f V @ 125.00 kHz", vLf125/500.0); - PrintAndLog("# LF antenna: %5.2f V @ 134.00 kHz", vLf134/500.0); - PrintAndLog("# LF optimal: %5.2f V @%9.2f kHz", peakv/500.0, 12000.0/(peakf+1)); - PrintAndLog("# HF antenna: %5.2f V @ 13.56 MHz", vHf/1000.0); + if (arg & FLAG_TUNE_LF) + { + PrintAndLog("# LF antenna: %5.2f V @ 125.00 kHz", vLf125/500.0); + PrintAndLog("# LF antenna: %5.2f V @ 134.00 kHz", vLf134/500.0); + PrintAndLog("# LF optimal: %5.2f V @%9.2f kHz", peakv/500.0, 12000.0/(peakf+1)); + } + if (arg & FLAG_TUNE_HF) + PrintAndLog("# HF antenna: %5.2f V @ 13.56 MHz", vHf/1000.0); #define LF_UNUSABLE_V 3000 #define LF_MARGINAL_V 15000 #define HF_UNUSABLE_V 3200 #define HF_MARGINAL_V 8000 - if (peakv<<1 < LF_UNUSABLE_V) - PrintAndLog("# Your LF antenna is unusable."); - else if (peakv<<1 < LF_MARGINAL_V) - PrintAndLog("# Your LF antenna is marginal."); - if (vHf < HF_UNUSABLE_V) - PrintAndLog("# Your HF antenna is unusable."); - else if (vHf < HF_MARGINAL_V) - PrintAndLog("# Your HF antenna is marginal."); + if (arg & FLAG_TUNE_LF) + { + if (peakv<<1 < LF_UNUSABLE_V) + PrintAndLog("# Your LF antenna is unusable."); + else if (peakv<<1 < LF_MARGINAL_V) + PrintAndLog("# Your LF antenna is marginal."); + } + if (arg & FLAG_TUNE_HF) + { + if (vHf < HF_UNUSABLE_V) + PrintAndLog("# Your HF antenna is unusable."); + else if (vHf < HF_MARGINAL_V) + PrintAndLog("# Your HF antenna is marginal."); + } if (peakv<<1 >= LF_UNUSABLE_V) { for (int i = 0; i < 256; i++) { From f3ffd5a24f6e4488505ff1c98bc6512dff5a69ed Mon Sep 17 00:00:00 2001 From: rkblackfire Date: Mon, 22 Jan 2018 19:25:22 +0100 Subject: [PATCH 154/310] Revert "dbg: Watchdog resets while 'hw tune lf'" This reverts commit fc175230f431e7e33c7628c6b31064d4f7d1e1ad. --- armsrc/appmain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 3aeb4c8a..64768922 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -205,7 +205,7 @@ void MeasureAntennaTuningLfOnly(int *vLf125, int *vLf134, int *peakf, int *peakv } } - for (i=0; i <= 18; i++) LF_Results[i] = 0; + for (i=18; i >= 0; i--) LF_Results[i] = 0; return; } From ec0872181b882afe69213c2443f074426ccaa94f Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Tue, 23 Jan 2018 08:27:58 +0100 Subject: [PATCH 155/310] fix clang compiler warnings identified in http://www.proxmark.org/forum/viewtopic.php?id=5388 --- client/cmdhfmfhard.c | 40 ----------------------- client/emv/apduinfo.c | 2 +- client/fpga_compress.c | 4 +-- client/hardnested/hardnested_bruteforce.c | 9 +++-- client/util_posix.c | 4 +++ 5 files changed, 14 insertions(+), 45 deletions(-) diff --git a/client/cmdhfmfhard.c b/client/cmdhfmfhard.c index 96c3a989..69ad898d 100644 --- a/client/cmdhfmfhard.c +++ b/client/cmdhfmfhard.c @@ -150,12 +150,6 @@ static inline void set_bit24(uint32_t *bitarray, uint32_t index) } -static inline void clear_bit24(uint32_t *bitarray, uint32_t index) -{ - bitarray[index>>5] &= ~(0x80000000>>(index&0x0000001f)); -} - - static inline uint32_t test_bit24(uint32_t *bitarray, uint32_t index) { return bitarray[index>>5] & (0x80000000>>(index&0x0000001f)); @@ -196,40 +190,6 @@ static inline uint32_t next_state(uint32_t *bitarray, uint32_t state) } -static inline uint32_t next_not_state(uint32_t *bitarray, uint32_t state) -{ - if (++state == 1<<24) return 1<<24; - uint32_t index = state >> 5; - uint_fast8_t bit = state & 0x1f; - uint32_t line = bitarray[index] << bit; - while (bit <= 0x1f) { - if ((line & 0x80000000) == 0) return state; - state++; - bit++; - line <<= 1; - } - index++; - while (bitarray[index] == 0xffffffff && state < 1<<24) { - index++; - state += 0x20; - } - if (state >= 1<<24) return 1<<24; -#if defined __GNUC__ - return state + __builtin_clz(~bitarray[index]); -#else - bit = 0x00; - line = bitarray[index]; - while (bit <= 0x1f) { - if ((line & 0x80000000) == 0) return state; - state++; - bit++; - line <<= 1; - } - return 1<<24; -#endif -} - - #define BITFLIP_2ND_BYTE 0x0200 diff --git a/client/emv/apduinfo.c b/client/emv/apduinfo.c index ec7a64a1..a631c614 100644 --- a/client/emv/apduinfo.c +++ b/client/emv/apduinfo.c @@ -132,7 +132,7 @@ const APDUCode APDUCodeTable[] = { {"6EXX", APDUCODE_TYPE_ERROR, "Instruction class not supported (procedure byte), (ISO 7816-3)"}, {"6F--", APDUCODE_TYPE_ERROR, "Internal exception"}, {"6F00", APDUCODE_TYPE_ERROR, "Command aborted - more exact diagnosis not possible (e.g., operating system error)."}, - {"6FFF", APDUCODE_TYPE_ERROR, "Card dead (overuse, …)"}, + {"6FFF", APDUCODE_TYPE_ERROR, "Card dead (overuse, ...)"}, {"6FXX", APDUCODE_TYPE_ERROR, "No precise diagnosis (procedure byte), (ISO 7816-3)"}, {"9---", APDUCODE_TYPE_NONE, ""}, {"9000", APDUCODE_TYPE_INFO, "Command successfully executed (OK)."}, diff --git a/client/fpga_compress.c b/client/fpga_compress.c index a672ab58..fc298a8b 100644 --- a/client/fpga_compress.c +++ b/client/fpga_compress.c @@ -297,9 +297,9 @@ int main(int argc, char **argv) } infiles = calloc(num_input_files, sizeof(FILE*)); for (uint16_t i = 0; i < num_input_files; i++) { - infiles[i] = fopen(argv[i+hardnested_mode?2:1], "rb"); + infiles[i] = fopen(argv[i+(hardnested_mode?2:1)], "rb"); if (infiles[i] == NULL) { - fprintf(stderr, "Error. Cannot open input file %s", argv[i+hardnested_mode?2:1]); + fprintf(stderr, "Error. Cannot open input file %s", argv[i+(hardnested_mode?2:1)]); return(EXIT_FAILURE); } } diff --git a/client/hardnested/hardnested_bruteforce.c b/client/hardnested/hardnested_bruteforce.c index 3072fc52..deea69d8 100644 --- a/client/hardnested/hardnested_bruteforce.c +++ b/client/hardnested/hardnested_bruteforce.c @@ -139,8 +139,13 @@ bool verify_key(uint32_t cuid, noncelist_t *nonces, uint8_t *best_first_bytes, u return true; } - -static void* __attribute__((force_align_arg_pointer)) crack_states_thread(void* x){ +static void* +#ifdef __has_attribute +#if __has_attribute(force_align_arg_pointer) +__attribute__((force_align_arg_pointer)) +#endif +#endif +crack_states_thread(void* x){ struct arg { bool silent; diff --git a/client/util_posix.c b/client/util_posix.c index 8f3ed46b..32158ada 100644 --- a/client/util_posix.c +++ b/client/util_posix.c @@ -37,8 +37,12 @@ void msleep(uint32_t n) { #ifdef __APPLE__ +#ifndef CLOCK_MONOTONIC #define CLOCK_MONOTONIC (1) +#endif +#ifndef CLOCK_REALTIME #define CLOCK_REALTIME (2) +#endif #include #include From 757221f5d6db8f1d9447d59c71a9807c6a9f522f Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Mon, 8 Jan 2018 07:48:13 +0100 Subject: [PATCH 156/310] Remove reveng (its license was not compatible with PM3 license, see issue #527) --- client/Makefile | 9 +- client/cmdcrc.c | 547 ----------------- client/cmdcrc.h | 20 - client/cmdmain.c | 9 - client/obj/reveng/.dummy | 0 client/reveng/bmpbit.c | 86 --- client/reveng/cli.c | 628 -------------------- client/reveng/config.h | 96 --- client/reveng/getopt.c | 81 --- client/reveng/getopt.h | 25 - client/reveng/model.c | 823 -------------------------- client/reveng/poly.c | 1196 -------------------------------------- client/reveng/reveng.c | 489 ---------------- client/reveng/reveng.h | 214 ------- client/scripting.c | 60 +- client/scripts/e.lua | 72 --- 16 files changed, 2 insertions(+), 4353 deletions(-) delete mode 100644 client/cmdcrc.c delete mode 100644 client/cmdcrc.h delete mode 100644 client/obj/reveng/.dummy delete mode 100644 client/reveng/bmpbit.c delete mode 100644 client/reveng/cli.c delete mode 100644 client/reveng/config.h delete mode 100644 client/reveng/getopt.c delete mode 100644 client/reveng/getopt.h delete mode 100644 client/reveng/model.c delete mode 100644 client/reveng/poly.c delete mode 100644 client/reveng/reveng.c delete mode 100644 client/reveng/reveng.h delete mode 100644 client/scripts/e.lua diff --git a/client/Makefile b/client/Makefile index 52a106bd..bf642803 100644 --- a/client/Makefile +++ b/client/Makefile @@ -169,14 +169,7 @@ CMDSRCS = crapto1/crapto1.c\ cmdscript.c\ pm3_binlib.c\ pm3_bitlib.c\ - protocols.c\ - cmdcrc.c\ - reveng/reveng.c\ - reveng/cli.c\ - reveng/bmpbit.c\ - reveng/model.c\ - reveng/poly.c\ - reveng/getopt.c\ + protocols.c cpu_arch = $(shell uname -m) ifneq ($(findstring 86, $(cpu_arch)), ) diff --git a/client/cmdcrc.c b/client/cmdcrc.c deleted file mode 100644 index 0ca2b8b1..00000000 --- a/client/cmdcrc.c +++ /dev/null @@ -1,547 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (C) 2015 iceman -// -// 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. -//----------------------------------------------------------------------------- -// CRC Calculations from the software reveng commands -//----------------------------------------------------------------------------- - -#ifdef _WIN32 -# include -# include -# ifndef STDIN_FILENO -# define STDIN_FILENO 0 -# endif /* STDIN_FILENO */ -#endif /* _WIN32 */ - -#include -#include -#include -#include -#include "cmdmain.h" -#include "cmdcrc.h" -#include "reveng/reveng.h" -#include "ui.h" -#include "util.h" - -#define MAX_ARGS 20 - -int uerr(char *msg){ - PrintAndLog("%s",msg); - return 0; -} - -int split(char *str, char *arr[MAX_ARGS]){ - int beginIndex = 0; - int endIndex; - int maxWords = MAX_ARGS; - int wordCnt = 0; - - while(1){ - while(isspace((unsigned char)str[beginIndex])){ - ++beginIndex; - } - if(str[beginIndex] == '\0') { - break; - } - endIndex = beginIndex; - while (str[endIndex] && !isspace((unsigned char)str[endIndex])){ - ++endIndex; - } - int len = endIndex - beginIndex; - char *tmp = calloc(len + 1, sizeof(char)); - memcpy(tmp, &str[beginIndex], len); - arr[wordCnt++] = tmp; - //PrintAndLog("DEBUG cnt: %d, %s",wordCnt-1, arr[wordCnt-1]); - beginIndex = endIndex; - if (wordCnt == maxWords) - break; - } - return wordCnt; -} - -int CmdCrc(const char *Cmd) -{ - char name[] = {"reveng "}; - char Cmd2[50 + 7]; - memcpy(Cmd2, name, 7); - memcpy(Cmd2 + 7, Cmd, 50); - char *argv[MAX_ARGS]; - int argc = split(Cmd2, argv); - - if (argc == 3 && memcmp(argv[1],"-g",2)==0) { - CmdrevengSearch(argv[2]); - } else { - reveng_main(argc, argv); - } - //PrintAndLog("DEBUG argc: %d, %s %s Cmd: %s",argc, argv[0], Cmd2, Cmd); - for(int i = 0; i < argc; ++i){ - free(argv[i]); - } - - return 0; -} - -//returns array of model names and the count of models returning -// as well as a width array for the width of each model -int GetModels(char *Models[], int *count, uint8_t *width){ - /* default values */ - static model_t model = { - PZERO, /* no CRC polynomial, user must specify */ - PZERO, /* Init = 0 */ - P_BE, /* RefIn = false, RefOut = false, plus P_RTJUST setting in reveng.h */ - PZERO, /* XorOut = 0 */ - PZERO, /* check value unused */ - NULL /* no model name */ - }; - - int ibperhx = 8;//, obperhx = 8; - int rflags = 0, uflags = 0; /* search and UI flags */ - poly_t apoly, crc, qpoly = PZERO, *apolys = NULL, *pptr = NULL, *qptr = NULL; - model_t pset = model, *candmods, *mptr; - - /* stdin must be binary */ - #ifdef _WIN32 - _setmode(STDIN_FILENO, _O_BINARY); - #endif /* _WIN32 */ - - SETBMP(); - - int args = 0, psets, pass; - int Cnt = 0; - if (width[0] == 0) { //reveng -D - *count = mcount(); - if(!*count) - return uerr("no preset models available"); - - for(int mode = 0; mode < *count; ++mode) { - mbynum(&model, mode); - mcanon(&model); - size_t size = (model.name && *model.name) ? strlen(model.name) : 6; - char *tmp = calloc(size+1, sizeof(char)); - if (tmp==NULL) - return uerr("out of memory?"); - - memcpy(tmp, model.name, size); - Models[mode] = tmp; - width[mode] = plen(model.spoly); - } - mfree(&model); - } else { //reveng -s - - if(~model.flags & P_MULXN) - return uerr("cannot search for non-Williams compliant models"); - - praloc(&model.spoly, (unsigned long)width[0]); - praloc(&model.init, (unsigned long)width[0]); - praloc(&model.xorout, (unsigned long)width[0]); - if(!plen(model.spoly)) - palloc(&model.spoly, (unsigned long)width[0]); - else - width[0] = (uint8_t)plen(model.spoly); - - /* special case if qpoly is zero, search to end of range */ - if(!ptst(qpoly)) - rflags &= ~R_HAVEQ; - - - /* not going to be sending additional args at this time (maybe future?) - - // allocate argument array - args = argc - optind; - if(!(apolys = malloc(args * sizeof(poly_t)))) - return uerr("cannot allocate memory for argument list"); - - for(pptr = apolys; optind < argc; ++optind) { - if(uflags & C_INFILE) - *pptr++ = rdpoly(argv[optind], model.flags, ibperhx); - else - *pptr++ = strtop(argv[optind], model.flags, ibperhx); - } - // exit value of pptr is used hereafter! - - */ - - /* if endianness not specified, try - * little-endian then big-endian. - * NB: crossed-endian algorithms will not be - * searched. - */ - /* scan against preset models */ - if(~uflags & C_FORCE) { - pass = 0; - Cnt = 0; - do { - psets = mcount(); - //PrintAndLog("psets: %d",psets); - while(psets) { - mbynum(&pset, --psets); - - /* skip if different width, or refin or refout don't match */ - if(plen(pset.spoly) != width[0] || (model.flags ^ pset.flags) & (P_REFIN | P_REFOUT)) - continue; - /* skip if the preset doesn't match specified parameters */ - if(rflags & R_HAVEP && pcmp(&model.spoly, &pset.spoly)) - continue; - if(rflags & R_HAVEI && psncmp(&model.init, &pset.init)) - continue; - if(rflags & R_HAVEX && psncmp(&model.xorout, &pset.xorout)) - continue; - - //for additional args (not used yet, maybe future?) - apoly = pclone(pset.xorout); - if(pset.flags & P_REFOUT) - prev(&apoly); - - for(qptr = apolys; qptr < pptr; ++qptr) { - crc = pcrc(*qptr, pset.spoly, pset.init, apoly, 0); - if(ptst(crc)) { - pfree(&crc); - break; - } else - pfree(&crc); - } - pfree(&apoly); - if(qptr == pptr) { - - /* the selected model solved all arguments */ - - mcanon(&pset); - - size_t size = (pset.name && *pset.name) ? strlen(pset.name) : 6; - //PrintAndLog("Size: %d, %s, count: %d",size,pset.name, Cnt); - char *tmp = calloc(size+1, sizeof(char)); - if (tmp==NULL){ - PrintAndLog("out of memory?"); - return 0; - } - width[Cnt] = width[0]; - memcpy(tmp, pset.name, size); - Models[Cnt++] = tmp; - *count = Cnt; - uflags |= C_RESULT; - } - } - mfree(&pset); - - /* toggle refIn/refOut and reflect arguments */ - if(~rflags & R_HAVERI) { - model.flags ^= P_REFIN | P_REFOUT; - for(qptr = apolys; qptr < pptr; ++qptr) - prevch(qptr, ibperhx); - } - } while(~rflags & R_HAVERI && ++pass < 2); - } - //got everything now free the memory... - - if(uflags & C_RESULT) { - for(qptr = apolys; qptr < pptr; ++qptr) - pfree(qptr); - } - if(!(model.flags & P_REFIN) != !(model.flags & P_REFOUT)) - return uerr("cannot search for crossed-endian models"); - - pass = 0; - do { - mptr = candmods = reveng(&model, qpoly, rflags, args, apolys); - if(mptr && plen(mptr->spoly)) - uflags |= C_RESULT; - while(mptr && plen(mptr->spoly)) { - mfree(mptr++); - } - free(candmods); - if(~rflags & R_HAVERI) { - model.flags ^= P_REFIN | P_REFOUT; - for(qptr = apolys; qptr < pptr; ++qptr) - prevch(qptr, ibperhx); - } - } while(~rflags & R_HAVERI && ++pass < 2); - for(qptr = apolys; qptr < pptr; ++qptr) - pfree(qptr); - free(apolys); - if(~uflags & C_RESULT) - return uerr("no models found"); - mfree(&model); - } - return 1; -} - -//test call to GetModels -int CmdrevengTest(const char *Cmd){ - char *Models[80]; - int count = 0; - uint8_t widtharr[80] = {0}; - uint8_t width = 0; - width = param_get8(Cmd, 0); - //PrintAndLog("width: %d",width); - if (width > 89) - return uerr("Width cannot exceed 89"); - - widtharr[0] = width; - int ans = GetModels(Models, &count, widtharr); - if (!ans) return 0; - - PrintAndLog("Count: %d",count); - for (int i = 0; i < count; i++){ - PrintAndLog("Model %d: %s, width: %d",i,Models[i], widtharr[i]); - free(Models[i]); - } - return 1; -} - -//-c || -v -//inModel = valid model name string - CRC-8 -//inHexStr = input hex string to calculate crc on -//reverse = reverse calc option if true -//endian = {0 = calc default endian input and output, b = big endian input and output, B = big endian output, r = right justified -// l = little endian input and output, L = little endian output only, t = left justified} -//result = calculated crc hex string -int RunModel(char *inModel, char *inHexStr, bool reverse, char endian, char *result){ - /* default values */ - static model_t model = { - PZERO, // no CRC polynomial, user must specify - PZERO, // Init = 0 - P_BE, // RefIn = false, RefOut = false, plus P_RTJUST setting in reveng.h - PZERO, // XorOut = 0 - PZERO, // check value unused - NULL // no model name - }; - int ibperhx = 8, obperhx = 8; - int rflags = 0; // search flags - int c; - //unsigned long width; - poly_t apoly, crc; - - char *string; - - // stdin must be binary - #ifdef _WIN32 - _setmode(STDIN_FILENO, _O_BINARY); - #endif /* _WIN32 */ - - SETBMP(); - //set model - if(!(c = mbynam(&model, inModel))) { - fprintf(stderr,"error: preset model '%s' not found. Use reveng -D to list presets.\n", inModel); - return 0; - } - if(c < 0) - return uerr("no preset models available"); - - // must set width so that parameter to -ipx is not zeroed - //width = plen(model.spoly); - rflags |= R_HAVEP | R_HAVEI | R_HAVERI | R_HAVERO | R_HAVEX; - - //set flags - switch (endian) { - case 'b': /* b big-endian (RefIn = false, RefOut = false ) */ - model.flags &= ~P_REFIN; - rflags |= R_HAVERI; - /* fall through: */ - case 'B': /* B big-endian output (RefOut = false) */ - model.flags &= ~P_REFOUT; - rflags |= R_HAVERO; - mnovel(&model); - /* fall through: */ - case 'r': /* r right-justified */ - model.flags |= P_RTJUST; - break; - case 'l': /* l little-endian input and output */ - model.flags |= P_REFIN; - rflags |= R_HAVERI; - /* fall through: */ - case 'L': /* L little-endian output */ - model.flags |= P_REFOUT; - rflags |= R_HAVERO; - mnovel(&model); - /* fall through: */ - case 't': /* t left-justified */ - model.flags &= ~P_RTJUST; - break; - } - - mcanon(&model); - - if (reverse) { - // v calculate reversed CRC - /* Distinct from the -V switch as this causes - * the arguments and output to be reversed as well. - */ - // reciprocate Poly - prcp(&model.spoly); - - /* mrev() does: - * if(refout) prev(init); else prev(xorout); - * but here the entire argument polynomial is - * reflected, not just the characters, so RefIn - * and RefOut are not inverted as with -V. - * Consequently Init is the mirror image of the - * one resulting from -V, and so we have: - */ - if(~model.flags & P_REFOUT) { - prev(&model.init); - prev(&model.xorout); - } - - // swap init and xorout - apoly = model.init; - model.init = model.xorout; - model.xorout = apoly; - } - // c calculate CRC - - // validate inputs - /* if(plen(model.spoly) == 0) { - * fprintf(stderr,"%s: no polynomial specified for -%c (add -w WIDTH -p POLY)\n", myname, mode); - * exit(EXIT_FAILURE); - * } - */ - - /* in the Williams model, xorout is applied after the refout stage. - * as refout is part of ptostr(), we reverse xorout here. - */ - if(model.flags & P_REFOUT) - prev(&model.xorout); - - apoly = strtop(inHexStr, model.flags, ibperhx); - - if(reverse) - prev(&apoly); - - crc = pcrc(apoly, model.spoly, model.init, model.xorout, model.flags); - - if(reverse) - prev(&crc); - - string = ptostr(crc, model.flags, obperhx); - for (int i = 0; i < 50; i++){ - result[i] = string[i]; - if (result[i]==0) break; - } - free(string); - pfree(&crc); - pfree(&apoly); - return 1; -} - -//test call to RunModel -int CmdrevengTestC(const char *Cmd){ - int cmdp = 0; - char inModel[30] = {0x00}; - char inHexStr[30] = {0x00}; - char result[30]; - int dataLen; - char endian = 0; - dataLen = param_getstr(Cmd, cmdp++, inModel, sizeof(inModel)); - if (dataLen < 4) return 0; - dataLen = param_getstr(Cmd, cmdp++, inHexStr, sizeof(inHexStr)); - if (dataLen < 4) return 0; - bool reverse = (param_get8(Cmd, cmdp++)) ? true : false; - endian = param_getchar(Cmd, cmdp++); - - //PrintAndLog("mod: %s, hex: %s, rev %d", inModel, inHexStr, reverse); - int ans = RunModel(inModel, inHexStr, reverse, endian, result); - if (!ans) return 0; - - PrintAndLog("Result: %s",result); - return 1; -} - -//returns a calloced string (needs to be freed) -char *SwapEndianStr(const char *inStr, const size_t len, const uint8_t blockSize){ - char *tmp = calloc(len+1, sizeof(char)); - for (uint8_t block=0; block < (uint8_t)(len/blockSize); block++){ - for (size_t i = 0; i < blockSize; i+=2){ - tmp[i+(blockSize*block)] = inStr[(blockSize-1-i-1)+(blockSize*block)]; - tmp[i+(blockSize*block)+1] = inStr[(blockSize-1-i)+(blockSize*block)]; - } - } - return tmp; -} - -// takes hex string in and searches for a matching result (hex string must include checksum) -int CmdrevengSearch(const char *Cmd){ - char inHexStr[50] = {0x00}; - int dataLen = param_getstr(Cmd, 0, inHexStr, sizeof(inHexStr)); - if (dataLen < 4) return 0; - - char *Models[80]; - int count = 0; - uint8_t width[80]; - width[0] = 0; - uint8_t crcChars = 0; - char result[30]; - char revResult[30]; - int ans = GetModels(Models, &count, width); - bool found = false; - if (!ans) return 0; - - // try each model and get result - for (int i = 0; i < count; i++){ - /*if (found) { - free(Models[i]); - continue; - }*/ - // round up to # of characters in this model's crc - crcChars = ((width[i]+7)/8)*2; - // can't test a model that has more crc digits than our data - if (crcChars >= dataLen) - continue; - memset(result, 0, 30); - char *inCRC = calloc(crcChars+1, sizeof(char)); - memcpy(inCRC, inHexStr+(dataLen-crcChars), crcChars); - - char *outHex = calloc(dataLen-crcChars+1, sizeof(char)); - memcpy(outHex, inHexStr, dataLen-crcChars); - - //PrintAndLog("DEBUG: dataLen: %d, crcChars: %d, Model: %s, CRC: %s, width: %d, outHex: %s",dataLen, crcChars, Models[i], inCRC, width[i], outHex); - ans = RunModel(Models[i], outHex, false, 0, result); - if (ans) { - //test for match - if (memcmp(result, inCRC, crcChars)==0){ - PrintAndLog("\nFound a possible match!\nModel: %s\nValue: %s\n",Models[i], result); - //optional - stop searching if found... - found = true; - } else { - if (crcChars > 2){ - char *swapEndian = SwapEndianStr(result, crcChars, crcChars); - if (memcmp(swapEndian, inCRC, crcChars)==0){ - PrintAndLog("\nFound a possible match!\nModel: %s\nValue EndianSwapped: %s\n",Models[i], swapEndian); - //optional - stop searching if found... - found = true; - } - free(swapEndian); - } - } - } - - //if (!found){ - ans = RunModel(Models[i], outHex, true, 0, revResult); - if (ans) { - //test for match - if (memcmp(revResult, inCRC, crcChars)==0){ - PrintAndLog("\nFound a possible match!\nModel Reversed: %s\nValue: %s\n",Models[i], revResult); - //optional - stop searching if found... - found = true; - } else { - if (crcChars > 2){ - char *swapEndian = SwapEndianStr(revResult, crcChars, crcChars); - if (memcmp(swapEndian, inCRC, crcChars)==0){ - PrintAndLog("\nFound a possible match!\nModel Reversed: %s\nValue EndianSwapped: %s\n",Models[i], swapEndian); - //optional - stop searching if found... - found = true; - } - free(swapEndian); - } - } - } - //} - free(inCRC); - free(outHex); - free(Models[i]); - } - if (!found) PrintAndLog("\nNo matches found\n"); - return 1; -} diff --git a/client/cmdcrc.h b/client/cmdcrc.h deleted file mode 100644 index 5041554d..00000000 --- a/client/cmdcrc.h +++ /dev/null @@ -1,20 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (C) 2015 iceman -// -// 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. -//----------------------------------------------------------------------------- -// CRC Calculations from the software reveng commands -//----------------------------------------------------------------------------- - -#ifndef CMDCRC_H__ -#define CMDCRC_H__ - -int CmdCrc(const char *Cmd); -int CmdrevengTest(const char *Cmd); -int CmdrevengTestC(const char *Cmd); -int CmdrevengSearch(const char *Cmd); -int GetModels(char *Models[], int *count, uint8_t *width); -int RunModel(char *inModel, char *inHexStr, bool reverse, char endian, char *result); -#endif diff --git a/client/cmdmain.c b/client/cmdmain.c index ae059ea2..719617fd 100644 --- a/client/cmdmain.c +++ b/client/cmdmain.c @@ -27,14 +27,12 @@ #include "util.h" #include "util_posix.h" #include "cmdscript.h" -#include "cmdcrc.h" unsigned int current_command = CMD_UNKNOWN; static int CmdHelp(const char *Cmd); static int CmdQuit(const char *Cmd); -static int CmdRev(const char *Cmd); //For storing command that are received from the device #define CMD_BUFFER_SIZE 50 @@ -53,7 +51,6 @@ static command_t CommandTable[] = {"hf", CmdHF, 1, "{ High Frequency commands... }"}, {"hw", CmdHW, 1, "{ Hardware commands... }"}, {"lf", CmdLF, 1, "{ Low Frequency commands... }"}, - {"reveng",CmdRev, 1, "Crc calculations from the software reveng1-30"}, {"script",CmdScript,1, "{ Scripting commands }"}, {"quit", CmdQuit, 1, "Exit program"}, {"exit", CmdQuit, 1, "Exit program"}, @@ -75,12 +72,6 @@ int CmdQuit(const char *Cmd) return 99; } -int CmdRev(const char *Cmd) -{ - CmdCrc(Cmd); - return 0; -} - /** * @brief This method should be called when sending a new command to the pm3. In case any old * responses from previous commands are stored in the buffer, a call to this method should clear them. diff --git a/client/obj/reveng/.dummy b/client/obj/reveng/.dummy deleted file mode 100644 index e69de29b..00000000 diff --git a/client/reveng/bmpbit.c b/client/reveng/bmpbit.c deleted file mode 100644 index 39a29e61..00000000 --- a/client/reveng/bmpbit.c +++ /dev/null @@ -1,86 +0,0 @@ -/* bmpbit.c - * Greg Cook, 9/Apr/2015 - */ - -/* CRC RevEng, an arbitrary-precision CRC calculator and algorithm finder - * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015 Gregory Cook - * - * This file is part of CRC RevEng. - * - * CRC RevEng is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * CRC RevEng is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with CRC RevEng. If not, see . - */ - -#ifdef BMPTST -# include -# include -#else -# define FILE void -#endif -#include "reveng.h" - -#if (defined BMPTST) || (BMP_BIT < 32) -/* Size in bits of a bmp_t. Not necessarily a power of two. */ -int bmpbit; - -/* The highest power of two that is strictly less than BMP_BIT. - * Initialises the index of a binary search for set bits in a bmp_t. - * (Computed correctly for BMP_BIT >= 2) - */ -int bmpsub; - -void -setbmp(void) { - /* Initialise BMP_BIT and BMP_SUB for the local architecture. */ - bmp_t bmpmax = ~(bmp_t) 0; - - bmpbit = 0; bmpsub = 1; - - while(bmpmax) { - bmpmax <<= 1; - ++bmpbit; - } - - while((bmpsub | (bmpsub - 1)) < bmpbit - 1) - bmpsub <<= 1; -} -#endif - -#ifdef BMPTST -int -main(int argc, char *argv[]) { - /* check the compile-time bitmap width is correct, otherwise - * searches run forever. */ -# if BMP_BIT > 0 - setbmp(); - if(BMP_BIT != bmpbit || BMP_SUB != bmpsub) { - fprintf(stderr,"reveng: configuration fault. Update " - "config.h with these definitions and " - "recompile:\n" - "\t#define BMP_BIT %d\n" - "\t#define BMP_SUB %d\n", - bmpbit, bmpsub); - exit(EXIT_FAILURE); - } -# endif /* BMP_BIT > 0 */ - /* check the bitmap constant macro */ - if(~(bmp_t) 0 != ~BMP_C(0)) { - fprintf(stderr, "reveng: configuration fault. Edit " - "the definition of BMP_C() in config.h to " - "match BMP_T and recompile.\n"); - exit(EXIT_FAILURE); - } - exit(EXIT_SUCCESS); -} - -#endif /* BMPTST */ diff --git a/client/reveng/cli.c b/client/reveng/cli.c deleted file mode 100644 index 224eedd6..00000000 --- a/client/reveng/cli.c +++ /dev/null @@ -1,628 +0,0 @@ -/* cli.c - * Greg Cook, 9/Apr/2015 - */ - -/* CRC RevEng, an arbitrary-precision CRC calculator and algorithm finder - * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015 Gregory Cook - * - * This file is part of CRC RevEng. - * - * CRC RevEng is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * CRC RevEng is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with CRC RevEng. If not, see . - */ - -/* 2015-04-03: added -z - * 2013-09-16: do not search with -M - * 2013-06-11: uprog() suppresses first progress report - * 2013-04-22: uprog() prints poly same as mtostr() - * 2013-02-07: added -q, uprog(), removed -W, R_ODDLY - * 2012-05-24: -D dumps parameters of all models - * 2012-03-03: added code to test sort order of model table - * 2012-02-20: set stdin to binary (MinGW). offer -D if preset unknown. - * 2011-09-06: -s reads arguments once. stdin not closed. - * 2011-09-06: fixed bad argument-freeing loops. - * 2011-08-27: validates BMP_C() - * 2011-08-26: validates BMPBIT and BMPSUB - * 2011-08-25: fixed Init/Xorout reflection logic in -V and -v - * 2011-01-17: fixed ANSI C warnings - * 2011-01-15: added NOFORCE - * 2011-01-14: added -k, -P - * 2011-01-10: reorganised switches, added -V, -X - * 2010-12-26: renamed CRC RevEng - * 2010-12-18: implemented -c, -C - * 2010-12-14: added and implemented -d, -D, fixed -ipx entry - * 2010-12-11: implemented -e. first tests - * 2010-12-10: finalised option processing. started input validation - * 2010-12-07: started cli - */ - -#include -#include -#include "getopt.h" -#ifdef _WIN32 -# include -# include -# ifndef STDIN_FILENO -# define STDIN_FILENO 0 -# endif /* STDIN_FILENO */ -#endif /* _WIN32 */ - -#include "reveng.h" - -static FILE *oread(const char *); -static poly_t rdpoly(const char *, int, int); -static void usage(void); - -static const char *myname = "reveng"; /* name of our program */ - -int reveng_main(int argc, char *argv[]) { - /* Command-line interface for CRC RevEng. - * Process options and switches in the argument list and - * run the required function. - */ - - /* default values */ - model_t model = { - PZERO, /* no CRC polynomial, user must specify */ - PZERO, /* Init = 0 */ - P_BE, /* RefIn = false, RefOut = false, plus P_RTJUST setting in reveng.h */ - PZERO, /* XorOut = 0 */ - PZERO, /* check value unused */ - NULL /* no model name */ - }; - int ibperhx = 8, obperhx = 8; - int rflags = 0, uflags = 0; /* search and UI flags */ - - unsigned long width = 0UL; - int c, mode = 0, args, psets, pass; - poly_t apoly, crc, qpoly = PZERO, *apolys, *pptr = NULL, *qptr = NULL; - model_t pset = model, *candmods, *mptr; - char *string; - - myname = argv[0]; - - /* stdin must be binary */ -#ifdef _WIN32 - _setmode(STDIN_FILENO, _O_BINARY); -#endif /* _WIN32 */ - - SETBMP(); - - pos=0; - optind=1; - do { - c=getopt(argc, argv, "?A:BDFLMP:SVXa:bcdefhi:k:lm:p:q:rstuvw:x:yz"); - switch(c) { - case 'A': /* A: bits per output character */ - case 'a': /* a: bits per character */ - if((obperhx = atoi(optarg)) > BMP_BIT) { - fprintf(stderr,"%s: argument to -%c must be between 1 and %d\n", myname, c, BMP_BIT); - return 0; - //exit(EXIT_FAILURE); - } - if(c == 'a') ibperhx = obperhx; - break; - case 'b': /* b big-endian (RefIn = false, RefOut = false ) */ - model.flags &= ~P_REFIN; - rflags |= R_HAVERI; - /* fall through: */ - case 'B': /* B big-endian output (RefOut = false) */ - model.flags &= ~P_REFOUT; - rflags |= R_HAVERO; - mnovel(&model); - /* fall through: */ - case 'r': /* r right-justified */ - model.flags |= P_RTJUST; - break; - case 'c': /* c calculate CRC */ - case 'D': /* D list primary model names */ - case 'd': /* d dump CRC model */ - case 'e': /* e echo arguments */ - case 's': /* s search for algorithm */ - case 'v': /* v calculate reversed CRC */ - if(mode) { - fprintf(stderr,"%s: more than one mode switch specified. Use %s -h for help.\n", myname, myname); - return 0; - //exit(EXIT_FAILURE); - } - mode = c; - break; - case 'F': /* F force search */ -#ifndef NOFORCE - uflags |= C_FORCE; -#endif - break; - case 'f': /* f arguments are filenames */ - uflags |= C_INFILE; - break; - case 'h': /* h get help / usage */ - case 'u': /* u get help / usage */ - case '?': /* ? get help / usage */ - default: - usage(); - return 0; - //exit(EXIT_FAILURE); - break; - case 'i': /* i: Init value */ - pptr = &model.init; - rflags |= R_HAVEI; - goto ippx; - case 'k': /* k: polynomial in Koopman notation */ - pfree(&model.spoly); - model.spoly = strtop(optarg, 0, 4); - pkchop(&model.spoly); - width = plen(model.spoly); - rflags |= R_HAVEP; - mnovel(&model); - break; - case 'l': /* l little-endian input and output */ - model.flags |= P_REFIN; - rflags |= R_HAVERI; - /* fall through: */ - case 'L': /* L little-endian output */ - model.flags |= P_REFOUT; - rflags |= R_HAVERO; - mnovel(&model); - /* fall through: */ - case 't': /* t left-justified */ - model.flags &= ~P_RTJUST; - break; - case 'm': /* m: select preset CRC model */ - if(!(c = mbynam(&model, optarg))) { - fprintf(stderr,"%s: preset model '%s' not found. Use %s -D to list presets.\n", myname, optarg, myname); - return 0; - //exit(EXIT_FAILURE); - } - if(c < 0){ - uerror("no preset models available"); - return 0; - } - /* must set width so that parameter to -ipx is not zeroed */ - width = plen(model.spoly); - rflags |= R_HAVEP | R_HAVEI | R_HAVERI | R_HAVERO | R_HAVEX; - break; - case 'M': /* M non-augmenting algorithm */ - model.flags &= ~P_MULXN; - break; - case 'P': /* P: reversed polynomial */ - case 'p': /* p: polynomial */ - pptr = &model.spoly; - rflags &= ~R_HAVEQ; - rflags |= R_HAVEP; -ippx: - pfree(pptr); - *pptr = strtop(optarg, 0, 4); - pright(pptr, width); - if(c == 'P') - prev(pptr); - mnovel(&model); - break; - case 'q': /* q: range end polynomial */ - pptr = &qpoly; - rflags &= ~R_HAVEP; - rflags |= R_HAVEQ; - goto ippx; - case 'S': /* s space between output characters */ - model.flags |= P_SPACE; - break; - case 'V': /* v reverse algorithm */ - /* Distinct from the -v switch as the - * user will have to reverse his or her - * own arguments. The user cannot dump - * the model generated by -v either. - */ - mrev(&model); - break; - case 'w': /* w: CRC width = order - 1 */ - width = (unsigned long) atol(optarg); - break; - case 'X': /* X print uppercase hex */ - model.flags |= P_UPPER; - break; - case 'x': /* x: XorOut value */ - pptr = &model.xorout; - rflags |= R_HAVEX; - goto ippx; - case 'y': /* y little-endian byte order in files */ - model.flags |= P_LTLBYT; - break; - case 'z': /* z raw binary arguments */ - model.flags |= P_DIRECT; - break; - case -1: /* no more options, continue */ - ; - } - } while(c != -1); - - /* canonicalise the model, so the one we dump is the one we - * calculate with (not with -s, spoly may be blank which will - * normalise to zero and clear init and xorout.) - */ - if(mode != 's') - mcanon(&model); - - switch(mode) { - case 'v': /* v calculate reversed CRC */ - /* Distinct from the -V switch as this causes - * the arguments and output to be reversed as well. - */ - /* reciprocate Poly */ - prcp(&model.spoly); - - /* mrev() does: - * if(refout) prev(init); else prev(xorout); - * but here the entire argument polynomial is - * reflected, not just the characters, so RefIn - * and RefOut are not inverted as with -V. - * Consequently Init is the mirror image of the - * one resulting from -V, and so we have: - */ - if(~model.flags & P_REFOUT) { - prev(&model.init); - prev(&model.xorout); - } - - /* swap init and xorout */ - apoly = model.init; - model.init = model.xorout; - model.xorout = apoly; - - /* fall through: */ - case 'c': /* c calculate CRC */ - - /* validate inputs */ - /* if(plen(model.spoly) == 0) { - * fprintf(stderr,"%s: no polynomial specified for -%c (add -w WIDTH -p POLY)\n", myname, mode); - * exit(EXIT_FAILURE); - * } - */ - - /* in the Williams model, xorout is applied after the refout stage. - * as refout is part of ptostr(), we reverse xorout here. - */ - if(model.flags & P_REFOUT) - prev(&model.xorout); - - for(; optind < argc; ++optind) { - if(uflags & C_INFILE) - apoly = rdpoly(argv[optind], model.flags, ibperhx); - else - apoly = strtop(argv[optind], model.flags, ibperhx); - - if(mode == 'v') - prev(&apoly); - - crc = pcrc(apoly, model.spoly, model.init, model.xorout, model.flags); - - if(mode == 'v') - prev(&crc); - - string = ptostr(crc, model.flags, obperhx); - puts(string); - free(string); - pfree(&crc); - pfree(&apoly); - } - break; - case 'D': /* D dump all models */ - args = mcount(); - if(!args){ - uerror("no preset models available"); - return 0; - } - for(mode = 0; mode < args; ++mode) { - mbynum(&model, mode); - mcanon(&model); - ufound(&model); - } - break; - case 'd': /* d dump CRC model */ - /* maybe we don't want to do this: - * either attaching names to arbitrary models or forcing to a preset - * mmatch(&model, M_OVERWR); - */ - if(~model.flags & P_MULXN){ - uerror("not a Williams model compliant algorithm"); - return 0; - } - string = mtostr(&model); - puts(string); - free(string); - break; - case 'e': /* e echo arguments */ - for(; optind < argc; ++optind) { - if(uflags & C_INFILE) - apoly = rdpoly(argv[optind], model.flags, ibperhx); - else - apoly = strtop(argv[optind], model.flags, ibperhx); - - psum(&apoly, model.init, 0UL); - string = ptostr(apoly, model.flags, obperhx); - puts(string); - free(string); - pfree(&apoly); - } - break; - case 's': /* s search for algorithm */ - if(!width){ - uerror("must specify positive -k or -w before -s"); - return 0; - } - if(~model.flags & P_MULXN){ - uerror("cannot search for non-Williams compliant models"); - return 0; - } - praloc(&model.spoly, width); - praloc(&model.init, width); - praloc(&model.xorout, width); - if(!plen(model.spoly)) - palloc(&model.spoly, width); - else - width = plen(model.spoly); - - /* special case if qpoly is zero, search to end of range */ - if(!ptst(qpoly)) - rflags &= ~R_HAVEQ; - - /* allocate argument array */ - args = argc - optind; - if(!(apolys = malloc(args * sizeof(poly_t)))){ - uerror("cannot allocate memory for argument list"); - return 0; - } - - for(pptr = apolys; optind < argc; ++optind) { - if(uflags & C_INFILE) - *pptr++ = rdpoly(argv[optind], model.flags, ibperhx); - else - *pptr++ = strtop(argv[optind], model.flags, ibperhx); - } - /* exit value of pptr is used hereafter! */ - - /* if endianness not specified, try - * little-endian then big-endian. - * NB: crossed-endian algorithms will not be - * searched. - */ - - /* scan against preset models */ - if(~uflags & C_FORCE) { - pass = 0; - do { - psets = mcount(); - while(psets) { - mbynum(&pset, --psets); - /* skip if different width, or refin or refout don't match */ - if(plen(pset.spoly) != width || (model.flags ^ pset.flags) & (P_REFIN | P_REFOUT)) - continue; - /* skip if the preset doesn't match specified parameters */ - if(rflags & R_HAVEP && pcmp(&model.spoly, &pset.spoly)) - continue; - if(rflags & R_HAVEI && psncmp(&model.init, &pset.init)) - continue; - if(rflags & R_HAVEX && psncmp(&model.xorout, &pset.xorout)) - continue; - apoly = pclone(pset.xorout); - if(pset.flags & P_REFOUT) - prev(&apoly); - for(qptr = apolys; qptr < pptr; ++qptr) { - crc = pcrc(*qptr, pset.spoly, pset.init, apoly, 0); - if(ptst(crc)) { - pfree(&crc); - break; - } else - pfree(&crc); - } - pfree(&apoly); - if(qptr == pptr) { - /* the selected model solved all arguments */ - mcanon(&pset); - ufound(&pset); - uflags |= C_RESULT; - } - } - mfree(&pset); - - /* toggle refIn/refOut and reflect arguments */ - if(~rflags & R_HAVERI) { - model.flags ^= P_REFIN | P_REFOUT; - for(qptr = apolys; qptr < pptr; ++qptr) - prevch(qptr, ibperhx); - } - } while(~rflags & R_HAVERI && ++pass < 2); - } - if(uflags & C_RESULT) { - for(qptr = apolys; qptr < pptr; ++qptr) - pfree(qptr); - //return 1; - //exit(EXIT_SUCCESS); - } - if(!(model.flags & P_REFIN) != !(model.flags & P_REFOUT)){ - uerror("cannot search for crossed-endian models"); - return 0; - } - pass = 0; - do { - mptr = candmods = reveng(&model, qpoly, rflags, args, apolys); - if(mptr && plen(mptr->spoly)) - uflags |= C_RESULT; - while(mptr && plen(mptr->spoly)) { - /* results were printed by the callback - * string = mtostr(mptr); - * puts(string); - * free(string); - */ - mfree(mptr++); - } - free(candmods); - if(~rflags & R_HAVERI) { - model.flags ^= P_REFIN | P_REFOUT; - for(qptr = apolys; qptr < pptr; ++qptr) - prevch(qptr, ibperhx); - } - } while(~rflags & R_HAVERI && ++pass < 2); - for(qptr = apolys; qptr < pptr; ++qptr) - pfree(qptr); - free(apolys); - if(~uflags & C_RESULT) - uerror("no models found"); - break; - default: /* no mode specified */ - fprintf(stderr, "%s: no mode switch specified. Use %s -h for help.\n", myname, myname); - //exit(EXIT_FAILURE); - } - - return 1; - //exit(EXIT_SUCCESS); -} - -void -ufound(const model_t *model) { - /* Callback function to report each model found */ - char *string; - - if(!model) return; - /* generated models will be canonical */ - string = mtostr(model); - puts(string); - free(string); -} - -void -uerror(const char *msg) { - /* Callback function to report fatal errors */ - fprintf(stderr, "%s: %s\n", myname, msg); - return; - //exit(EXIT_FAILURE); -} - -void -uprog(const poly_t gpoly, int flags, unsigned long seq) { - /* Callback function to report search progress */ - char *string; - - /* Suppress first report in CLI */ - if(!seq) - return; - string = ptostr(gpoly, P_RTJUST, 4); - fprintf(stderr, "%s: searching: width=%ld poly=0x%s refin=%s refout=%s\n", - myname, plen(gpoly), string, - (flags & P_REFIN ? "true" : "false"), - (flags & P_REFOUT ? "true" : "false") - ); - free(string); -} - -static poly_t -rdpoly(const char *name, int flags, int bperhx) { - /* read poly from file in chunks and report errors */ - - poly_t apoly = PZERO, chunk = PZERO; - FILE *input; - - input = oread(name); - while(!feof(input) && !ferror(input)) { - chunk = filtop(input, BUFFER, flags, bperhx); - psum(&apoly, chunk, plen(apoly)); - pfree(&chunk); - } - if(ferror(input)) { - fprintf(stderr,"%s: error condition on file '%s'\n", myname, name); - exit(EXIT_FAILURE); - } - /* close file unless stdin */ - if(input == stdin) - /* reset EOF condition */ - clearerr(input); - else if(fclose(input)) { - fprintf(stderr,"%s: error closing file '%s'\n", myname, name); - exit(EXIT_FAILURE); - } - return(apoly); -} - -static FILE * -oread(const char *name) { - /* open file for reading and report errors */ - FILE *handle; - - /* recognise special name '-' as standard input */ - if(*name == '-' && name[1] == '\0') - return(stdin); - if(!(handle = fopen(name, "rb"))) { - fprintf(stderr, "%s: cannot open '%s' for reading\n", myname, name); - return 0; - //exit(EXIT_FAILURE); - } - return(handle); -} - -static void -usage(void) { - /* print usage if asked, or if syntax incorrect */ - fprintf(stderr, - "CRC RevEng, an arbitrary-precision CRC calculator and algorithm finder\n" - "Usage:\t"); - fputs(myname, stderr); - fprintf(stderr, - "\t-cdDesvhu? [-bBfFlLMrStVXyz]\n" - "\t\t[-a BITS] [-A OBITS] [-i INIT] [-k KPOLY] [-m MODEL]\n" - "\t\t[-p POLY] [-P RPOLY] [-q QPOLY] [-w WIDTH] [-x XOROUT]\n" - "\t\t[STRING...]\n" - "Options:\n" - "\t-a BITS\t\tbits per character (1 to %d)\n" - "\t-A OBITS\tbits per output character (1 to %d)\n" - "\t-i INIT\t\tinitial register value\n" - "\t-k KPOLY\tgenerator in Koopman notation (implies WIDTH)\n" - "\t-m MODEL\tpreset CRC algorithm\n" - "\t-p POLY\t\tgenerator or search range start polynomial\n" - "\t-P RPOLY\treversed generator polynomial\n", - BMP_BIT, BMP_BIT); - fprintf(stderr, - "\t-q QPOLY\tsearch range end polynomial\n" - "\t-w WIDTH\tregister size, in bits\n" - "\t-x XOROUT\tfinal register XOR value\n" - "Modifier switches:\n" - "\t-b big-endian CRC\t\t-B big-endian CRC output\n" - "\t-f read files named in STRINGs\t-F find presets less quickly\n" - "\t-l little-endian CRC\t\t-L little-endian CRC output\n" - "\t-M non-augmenting algorithm\t-r right-justified output\n" - "\t-S print spaces between chars\t-t left-justified output\n" - "\t-V reverse algorithm only\t-X print uppercase hex\n" - "\t-y low bytes first in files\t-z raw binary STRINGs\n"); - fprintf(stderr, - "Mode switches:\n" - "\t-c calculate CRCs\t\t-d dump algorithm parameters\n" - "\t-D list preset algorithms\t-e echo (and reformat) input\n" - "\t-s search for algorithm\t\t-v calculate reversed CRCs\n" - "\t-g search for alg given hex+crc\t-h | -u | -? show this help\n" - "Common Use Examples:\n" - "\t reveng -g 01020304e3\n" - "\t Searches for a known/common crc preset that computes the crc\n" - "\t on the end of the given hex string\n" - "\t reveng -w 8 -s 01020304e3 010204039d\n" - "\t Searches for any possible 8 bit width crc calc that computes\n" - "\t the crc on the end of the given hex string(s)\n" - "\t reveng -m CRC-8 -c 01020304\n" - "\t Calculates the crc-8 of the given hex string\n" - "\t reveng -D\n" - "\t Outputs a list of all known/common crc models with their\n" - "\t preset values\n" - "\n" - "Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015 Gregory Cook\n" - "This is free software; see the source for copying conditions. There is NO\n" - "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" - "Version " - VERSION - "\t\t\t\t \n"); -} diff --git a/client/reveng/config.h b/client/reveng/config.h deleted file mode 100644 index 2f5108f0..00000000 --- a/client/reveng/config.h +++ /dev/null @@ -1,96 +0,0 @@ -/* config.h - * Greg Cook, 9/Apr/2015 - */ - -/* CRC RevEng, an arbitrary-precision CRC calculator and algorithm finder - * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015 Gregory Cook - * - * This file is part of CRC RevEng. - * - * CRC RevEng is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * CRC RevEng is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with CRC RevEng. If not, see . - */ - -#ifndef CONFIG_H -#define CONFIG_H 1 - -/***************************************** - * * - * Start of user configuration options * - * * - *****************************************/ - -/* A type to contain polynomial coefficient bitmaps. - * Can be changed to 'unsigned long long' for some extended compilers. - * Adjust BMP_C(), BMP_BIT and BMP_SUB below if this is changed. - */ - -#define BMP_T unsigned long - -/* Creates an appropriate numeric constant for bmp_t. - * If the underlying type is 'unsigned long long', change UL to ULL. - */ - -#define BMP_C(n) (n##UL) - -/* Define BMPMACRO to turn the definitions of the size of a bmp_t into - * compile-time constants. This improves efficiency but makes the code - * platform-specific. - */ - -/* #define BMPMACRO 1 */ - -/* Some enterprise users may wish to disable the -F switch to minimise CPU - * usage. To do this, define the macro NOFORCE. - */ - -/* #define NOFORCE 1 */ - -/* Define PRESETS to compile CRC RevEng with the preset models from the - * CRC Catalogue. This implies BMPMACRO and so makes the code platform- - * specific. - */ - - -// PM3 NOTES: -// only PRESETS tested on windows -// PRESETS was commented out of original code -// but to enable preset models it must be enabled -// (marshmellow) -#define PRESETS 1 - - -/* Macros defining the size of a bmp_t. - * Their values only matter if PRESETS and/or BMPMACRO are defined, in - * which case edit the macros below to suit your architecture. - * Otherwise, BMP_BIT and BMP_SUB will be redefined as aliases of bmpbit - * and bmpsub, global objects initialised at run time. - */ - -/* Size in bits of a bmp_t. Not necessarily a power of two. */ - -#define BMP_BIT 32 - -/* The highest power of two that is strictly less than BMP_BIT. - * Initialises the index of a binary search for set bits in a bmp_t. - */ - -#define BMP_SUB 16 - -/***************************************** - * * - * End of user configuration options * - * * - *****************************************/ - -#endif /* CONFIG_H */ diff --git a/client/reveng/getopt.c b/client/reveng/getopt.c deleted file mode 100644 index abe99634..00000000 --- a/client/reveng/getopt.c +++ /dev/null @@ -1,81 +0,0 @@ -/*---------------------------------------------------------------------- - - Replacement for Unix "getopt()", for DOS/Windows/etc. - - getopt.c 1.3 2003/09/17 16:17:59 - - Copyright (C) 1998, 2003 by David A. Hinds -- All Rights Reserved - - This file is part of ASPEX. - - ASPEX is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - ASPEX is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with ASPEX; if not, write to the Free Software Foundation, - Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -----------------------------------------------------------------------*/ - -#include "string.h" -#include "stdio.h" -#include "getopt.h" - -char *optarg; -int optind = 1, opterr, optopt; -int pos = 0; -int getopt(int argc, char *argv[], const char *optstring) -{ - //static int pos = 0; - char *str; - - if (pos == 0) { - if ((optind >= argc) || (*argv[optind] != '-')) - return EOF; - pos = 1; - if (argv[optind][pos] == '\0') - return EOF; - } - - str = strchr(optstring, argv[optind][pos]); - if (str == NULL) { - optopt = argv[optind][pos]; - if (opterr) - fprintf(stderr, "%s: illegal option -- %c\n", argv[0], - optopt); - return '?'; - } - - if (str[1] == ':') { - if (argv[optind][pos+1] != '\0') { - optarg = &argv[optind][pos+1]; - return *str; - } - optind++; - if (optind >= argc) { - optopt = *str; - if (opterr) - fprintf(stderr, "%s: option requires an argument -- %c\n", - argv[0], optopt); - return '?'; - } - optarg = argv[optind]; - optind++; pos = 0; - return *str; - } - else { - pos++; - if (argv[optind][pos] == '\0') { - optind++; - pos = 0; - } - return *str; - } -} diff --git a/client/reveng/getopt.h b/client/reveng/getopt.h deleted file mode 100644 index bf66d9b1..00000000 --- a/client/reveng/getopt.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - getopt.h 1.2 2003/09/17 16:17:59 - - Copyright (C) 1998, 2003 by David A. Hinds -- All Rights Reserved - - This file is part of ASPEX. - - ASPEX is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - ASPEX is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with ASPEX; if not, write to the Free Software Foundation, - Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -extern char *optarg; -extern int optind, opterr, optopt, pos; -int getopt(int argc, char *argv[], const char *optstring); diff --git a/client/reveng/model.c b/client/reveng/model.c deleted file mode 100644 index 5a9b6580..00000000 --- a/client/reveng/model.c +++ /dev/null @@ -1,823 +0,0 @@ -/* model.c - * Greg Cook, 9/Apr/2015 - */ - -/* CRC RevEng, an arbitrary-precision CRC calculator and algorithm finder - * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015 Gregory Cook - * - * This file is part of CRC RevEng. - * - * CRC RevEng is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * CRC RevEng is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with CRC RevEng. If not, see . - */ - -/* 2014-01-14: added CRC-8/DVB-S2 - * 2014-01-11: corrected CRC-40/GSM, added alias CRC-8/AES - * 2013-10-14: added CRC-13/BBC and six cdma2000 algorithms - * 2013-06-11: ensure BMP_BIT is an integer constant to compile presets - * 2013-01-20: big polynomials autogenerated, corrected CRC-82/DARC - * 2012-07-19: added CRC-8/EBU - * 2012-07-16: added CRC-15/MPT1327 - * 2012-05-25: removed CRC-1/PARITY-EVEN, CRC-1/PARITY-ODD - * 2012-04-12: added model CRC-31/PHILIPS - * 2012-03-03: single-line Williams model string conversion - * 2012-02-20: corrected model CRC-6/DARC - * 2011-09-03: added mrev(), mnovel() - * 2011-08-28: added model CRC-64/XZ - * 2011-04-30: added models CRC-16/TMS37157 and CRC-A, and alias CRC-B - * 2011-02-10: made preset models ANSI C compliant - * 2011-01-17: fixed ANSI C warnings (except preset models) - * 2011-01-01: added mbynum(), mcount() - * 2010-12-26: renamed CRC RevEng - * 2010-12-18: minor change to mtostr() output format - * 2010-12-15: added mcmp(), mmatch() - * 2010-12-14: finished mbynam(), mnames(), mtostr() - * 2010-12-13: restarted with PCONST macros - * 2010-12-12: was having so much fun I didn't think to try compiling. :( - * 2010-12-12: started models.c - */ - -#include -#include -#include -#include -#include "reveng.h" - -/* Private declarations */ - -struct mpreset { - const unsigned long width; /* width of CRC algorithm */ - const bmp_t *const bspoly; /* polynomial with highest-order term removed. length determines CRC width */ - const bmp_t *const binit; /* initial register value. length == poly.length */ - const int flags; /* P_REFIN and P_REFOUT indicate reflected input/output */ - const bmp_t *const bxorout; /* final register XOR mask. length == poly.length */ - const bmp_t *const bcheck; /* optional check value, the CRC of the UTF-8 string "123456789" */ - const char *const name; /* optional canonical name of the model */ -}; - -struct malias { - const char *name; - const struct mpreset *model; - const int isprimry; -}; - -#ifdef PRESETS -# if BMP_BIT < 32 -# error config.h: BMP_BIT must be an integer constant macro to compile presets -# else /* BMP_BIT */ - -/* Big polynomial constants. */ - -/* Directives for relink.pl */ -/* CONSTANT b40 = (40, 0x0004820009) */ -/* CONSTANT b40a = (40, 0xffffffffff) */ -/* CONSTANT b40b = (40, 0xd4164fc646) */ -/* CONSTANT b64 = (64, 0x42f0e1eba9ea3693) */ -/* CONSTANT b64a = (64, 0x6c40df5f0b497347) */ -/* CONSTANT b64b = (64, 0xffffffffffffffff) */ -/* CONSTANT b64c = (64, 0x62ec59e3f1a4f00a) */ -/* CONSTANT b64d = (64, 0x995dc9bbdf1939fa) */ -/* CONSTANT b82 = (82, 0x0308c0111011401440411) */ -/* CONSTANT b82a = (82, 0x09ea83f625023801fd612) */ - -/* The next section was generated by relink.pl from the directives above. */ - -/* DO NOT EDIT the section below, INCLUDING the next comment. */ -/* BEGIN AUTO-GENERATED CONSTANTS */ -# if BMP_BIT >= 40 -static const bmp_t b40[] = { - BMP_C(0x0004820009) << (BMP_BIT - 40), -}; -static const bmp_t b40a[] = { - BMP_C(0xffffffffff) << (BMP_BIT - 40), -}; -static const bmp_t b40b[] = { - BMP_C(0xd4164fc646) << (BMP_BIT - 40), -}; -# else /* BMP_BIT */ -static const bmp_t b40[] = { - BMP_C(0x00048200) << (BMP_BIT - 32) | BMP_C(0x04) >> (39 - BMP_BIT), - BMP_C(0x09) << (BMP_BIT * 2 - 40), -}; -static const bmp_t b40a[] = { - BMP_C(0xffffffff) << (BMP_BIT - 32) | BMP_C(0x7f) >> (39 - BMP_BIT), - BMP_C(0xff) << (BMP_BIT * 2 - 40), -}; -static const bmp_t b40b[] = { - BMP_C(0xd4164fc6) << (BMP_BIT - 32) | BMP_C(0x23) >> (39 - BMP_BIT), - BMP_C(0x46) << (BMP_BIT * 2 - 40), -}; -# endif /* BMP_BIT */ - -# if BMP_BIT >= 64 -static const bmp_t b64[] = { - BMP_C(0x42f0e1eba9ea3693) << (BMP_BIT - 64), -}; -static const bmp_t b64a[] = { - BMP_C(0x6c40df5f0b497347) << (BMP_BIT - 64), -}; -static const bmp_t b64b[] = { - BMP_C(0xffffffffffffffff) << (BMP_BIT - 64), -}; -static const bmp_t b64c[] = { - BMP_C(0x62ec59e3f1a4f00a) << (BMP_BIT - 64), -}; -static const bmp_t b64d[] = { - BMP_C(0x995dc9bbdf1939fa) << (BMP_BIT - 64), -}; -# else /* BMP_BIT */ -static const bmp_t b64[] = { - BMP_C(0x42f0e1eb) << (BMP_BIT - 32) | BMP_C(0x54f51b49) >> (63 - BMP_BIT), - BMP_C(0xa9ea3693) << (BMP_BIT * 2 - 64), -}; -static const bmp_t b64a[] = { - BMP_C(0x6c40df5f) << (BMP_BIT - 32) | BMP_C(0x05a4b9a3) >> (63 - BMP_BIT), - BMP_C(0x0b497347) << (BMP_BIT * 2 - 64), -}; -static const bmp_t b64b[] = { - BMP_C(0xffffffff) << (BMP_BIT - 32) | BMP_C(0x7fffffff) >> (63 - BMP_BIT), - BMP_C(0xffffffff) << (BMP_BIT * 2 - 64), -}; -static const bmp_t b64c[] = { - BMP_C(0x62ec59e3) << (BMP_BIT - 32) | BMP_C(0x78d27805) >> (63 - BMP_BIT), - BMP_C(0xf1a4f00a) << (BMP_BIT * 2 - 64), -}; -static const bmp_t b64d[] = { - BMP_C(0x995dc9bb) << (BMP_BIT - 32) | BMP_C(0x6f8c9cfd) >> (63 - BMP_BIT), - BMP_C(0xdf1939fa) << (BMP_BIT * 2 - 64), -}; -# endif /* BMP_BIT */ - -# if BMP_BIT >= 82 -static const bmp_t b82[] = { - BMP_C(0x0308c0111011401440411) << (BMP_BIT - 82), -}; -static const bmp_t b82a[] = { - BMP_C(0x09ea83f625023801fd612) << (BMP_BIT - 82), -}; -# elif BMP_BIT >= 41 -static const bmp_t b82[] = { - BMP_C(0x01846008880) << (BMP_BIT - 41) | BMP_C(0x08a00a20208) >> (81 - BMP_BIT), - BMP_C(0x11401440411) << (BMP_BIT * 2 - 82), -}; -static const bmp_t b82a[] = { - BMP_C(0x04f541fb128) << (BMP_BIT - 41) | BMP_C(0x011c00feb09) >> (81 - BMP_BIT), - BMP_C(0x023801fd612) << (BMP_BIT * 2 - 82), -}; -# else /* BMP_BIT */ -static const bmp_t b82[] = { - BMP_C(0x0c230044) << (BMP_BIT - 32) | BMP_C(0x040) >> (40 - BMP_BIT), - BMP_C(0x40450051) << (BMP_BIT * 2 - 64) | BMP_C(0x00104) >> (80 - BMP_BIT * 2), - BMP_C(0x00411) << (BMP_BIT * 3 - 82), -}; -static const bmp_t b82a[] = { - BMP_C(0x27aa0fd8) << (BMP_BIT - 32) | BMP_C(0x094) >> (40 - BMP_BIT), - BMP_C(0x9408e007) << (BMP_BIT * 2 - 64) | BMP_C(0x0f584) >> (80 - BMP_BIT * 2), - BMP_C(0x3d612) << (BMP_BIT * 3 - 82), -}; -# endif /* BMP_BIT */ - -/* END AUTO-GENERATED CONSTANTS */ -/* DO NOT EDIT the section above, INCLUDING the previous comment. */ - -/* Array of the polynomial bitmaps used in the model table. */ -static const bmp_t b32[] = { - BMP_C(0x00000000) << (BMP_BIT - 32), /* 0 -- 5, 00 */ - BMP_C(0x000000af) << (BMP_BIT - 32), /* 1 -- 32,000000af */ - BMP_C(0x00010000) << (BMP_BIT - 32), /* 2 -- 16, 0001 */ - BMP_C(0x00020000) << (BMP_BIT - 32), /* 3 -- 15, 0001 */ - BMP_C(0x007e0000) << (BMP_BIT - 32), /* 4 -- 16, 007e */ - BMP_C(0x007f0000) << (BMP_BIT - 32), /* 5 -- 16, 007f */ - BMP_C(0x03400000) << (BMP_BIT - 32), /* 6 -- 11, 01a */ - BMP_C(0x0376e6e7) << (BMP_BIT - 32), /* 7 -- 32,0376e6e7 */ - BMP_C(0x04c11db7) << (BMP_BIT - 32), /* 8 -- 32,04c11db7 */ - BMP_C(0x05890000) << (BMP_BIT - 32), /* 9 -- 16, 0589 */ - BMP_C(0x07000000) << (BMP_BIT - 32), /* 10 -- 8, 07 */ - BMP_C(0x09823b6e) << (BMP_BIT - 32), /* 11 -- 31,04c11db7 */ - BMP_C(0x0b3c0000) << (BMP_BIT - 32), /* 12 -- 15, 059e */ - BMP_C(0x0c000000) << (BMP_BIT - 32), /* 13 -- 6, 03 */ - BMP_C(0x0fb30000) << (BMP_BIT - 32), /* 14 -- 16, 0fb3 */ - BMP_C(0x10210000) << (BMP_BIT - 32), /* 15 -- 16, 1021 */ - BMP_C(0x12000000) << (BMP_BIT - 32), /* 16 -- 7, 09 */ - BMP_C(0x15000000) << (BMP_BIT - 32), /* 17 -- 8, 15 */ - BMP_C(0x18000000) << (BMP_BIT - 32), /* 18 -- 6, 06 */ - BMP_C(0x19d3c8d8) << (BMP_BIT - 32), /* 19 -- 31,0ce9e46c */ - BMP_C(0x1c000000) << (BMP_BIT - 32), /* 20 -- 6, 07 */ - BMP_C(0x1d000000) << (BMP_BIT - 32), /* 21 -- 8, 1d */ - BMP_C(0x1d0f0000) << (BMP_BIT - 32), /* 22 -- 16, 1d0f */ - BMP_C(0x1edc6f41) << (BMP_BIT - 32), /* 23 -- 32,1edc6f41 */ - BMP_C(0x1f23b800) << (BMP_BIT - 32), /* 24 -- 24, 1f23b8 */ - BMP_C(0x20140000) << (BMP_BIT - 32), /* 25 -- 14, 0805 */ - BMP_C(0x20b40000) << (BMP_BIT - 32), /* 26 -- 14, 082d */ - BMP_C(0x21890000) << (BMP_BIT - 32), /* 27 -- 16, 2189 */ - BMP_C(0x21cf0200) << (BMP_BIT - 32), /* 28 -- 24, 21cf02 */ - BMP_C(0x25000000) << (BMP_BIT - 32), /* 29 -- 8, 25 */ - BMP_C(0x26b10000) << (BMP_BIT - 32), /* 30 -- 16, 26b1 */ - BMP_C(0x27d00000) << (BMP_BIT - 32), /* 31 -- 13, 04fa */ - BMP_C(0x28000000) << (BMP_BIT - 32), /* 32 -- 5, 05 */ - BMP_C(0x29b10000) << (BMP_BIT - 32), /* 33 -- 16, 29b1 */ - BMP_C(0x30000000) << (BMP_BIT - 32), /* 34 -- 4, 3 */ - BMP_C(0x3010bf7f) << (BMP_BIT - 32), /* 35 -- 32,3010bf7f */ - BMP_C(0x31000000) << (BMP_BIT - 32), /* 36 -- 8, 31 */ - BMP_C(0x31c30000) << (BMP_BIT - 32), /* 37 -- 16, 31c3 */ - BMP_C(0x34000000) << (BMP_BIT - 32), /* 38 -- 6, 0d */ - BMP_C(0x340bc6d9) << (BMP_BIT - 32), /* 39 -- 32,340bc6d9 */ - BMP_C(0x38000000) << (BMP_BIT - 32), /* 40 -- 5, 07 */ - BMP_C(0x39000000) << (BMP_BIT - 32), /* 41 -- 8, 39 */ - BMP_C(0x3d650000) << (BMP_BIT - 32), /* 42 -- 16, 3d65 */ - BMP_C(0x44c20000) << (BMP_BIT - 32), /* 43 -- 16, 44c2 */ - BMP_C(0x48000000) << (BMP_BIT - 32), /* 44 -- 5, 09 */ - BMP_C(0x4acc0000) << (BMP_BIT - 32), /* 45 -- 15, 2566 */ - BMP_C(0x4b370000) << (BMP_BIT - 32), /* 46 -- 16, 4b37 */ - BMP_C(0x4c060000) << (BMP_BIT - 32), /* 47 -- 16, 4c06 */ - BMP_C(0x55000000) << (BMP_BIT - 32), /* 48 -- 8, 55 */ - BMP_C(0x5d6dcb00) << (BMP_BIT - 32), /* 49 -- 24, 5d6dcb */ - BMP_C(0x60000000) << (BMP_BIT - 32), /* 50 -- 3, 3 */ - BMP_C(0x63d00000) << (BMP_BIT - 32), /* 51 -- 16, 63d0 */ - BMP_C(0x64000000) << (BMP_BIT - 32), /* 52 -- 6, 19 */ - BMP_C(0x66400000) << (BMP_BIT - 32), /* 53 -- 10, 199 */ - BMP_C(0x6f910000) << (BMP_BIT - 32), /* 54 -- 16, 6f91 */ - BMP_C(0x70000000) << (BMP_BIT - 32), /* 55 -- 4, 7 */ - BMP_C(0x70a00000) << (BMP_BIT - 32), /* 56 -- 11, 385 */ - BMP_C(0x765e7680) << (BMP_BIT - 32), /* 57 -- 32,765e7680 */ - BMP_C(0x7979bd00) << (BMP_BIT - 32), /* 58 -- 24, 7979bd */ - BMP_C(0x7e000000) << (BMP_BIT - 32), /* 59 -- 8, 7e */ - BMP_C(0x80050000) << (BMP_BIT - 32), /* 60 -- 16, 8005 */ - BMP_C(0x800d0000) << (BMP_BIT - 32), /* 61 -- 16, 800d */ - BMP_C(0x80f00000) << (BMP_BIT - 32), /* 62 -- 12, 80f */ - BMP_C(0x814141ab) << (BMP_BIT - 32), /* 63 -- 32,814141ab */ - BMP_C(0x864cfb00) << (BMP_BIT - 32), /* 64 -- 24, 864cfb */ - BMP_C(0x87315576) << (BMP_BIT - 32), /* 65 -- 32,87315576 */ - BMP_C(0x89ec0000) << (BMP_BIT - 32), /* 66 -- 16, 89ec */ - BMP_C(0x8b320000) << (BMP_BIT - 32), /* 67 -- 15, 4599 */ - BMP_C(0x8bb70000) << (BMP_BIT - 32), /* 68 -- 16, 8bb7 */ - BMP_C(0x8cc00000) << (BMP_BIT - 32), /* 69 -- 10, 233 */ - BMP_C(0x906e0000) << (BMP_BIT - 32), /* 70 -- 16, 906e */ - BMP_C(0x97000000) << (BMP_BIT - 32), /* 71 -- 8, 97 */ - BMP_C(0x98000000) << (BMP_BIT - 32), /* 72 -- 6, 26 */ - BMP_C(0x9b000000) << (BMP_BIT - 32), /* 73 -- 8, 9b */ - BMP_C(0x9c000000) << (BMP_BIT - 32), /* 74 -- 6, 27 */ - BMP_C(0x9e000000) << (BMP_BIT - 32), /* 75 -- 7, 4f */ - BMP_C(0x9ecf0000) << (BMP_BIT - 32), /* 76 -- 16, 9ecf */ - BMP_C(0xa0970000) << (BMP_BIT - 32), /* 77 -- 16, a097 */ - BMP_C(0xa1000000) << (BMP_BIT - 32), /* 78 -- 8, a1 */ - BMP_C(0xa6000000) << (BMP_BIT - 32), /* 79 -- 7, 53 */ - BMP_C(0xa8000000) << (BMP_BIT - 32), /* 80 -- 5, 15 */ - BMP_C(0xa833982b) << (BMP_BIT - 32), /* 81 -- 32,a833982b */ - BMP_C(0xabcdef00) << (BMP_BIT - 32), /* 82 -- 24, abcdef */ - BMP_C(0xb2aa0000) << (BMP_BIT - 32), /* 83 -- 16, b2aa */ - BMP_C(0xb4600000) << (BMP_BIT - 32), /* 84 -- 11, 5a3 */ - BMP_C(0xb4c80000) << (BMP_BIT - 32), /* 85 -- 16, b4c8 */ - BMP_C(0xb704ce00) << (BMP_BIT - 32), /* 86 -- 24, b704ce */ - BMP_C(0xbb3d0000) << (BMP_BIT - 32), /* 87 -- 16, bb3d */ - BMP_C(0xbc000000) << (BMP_BIT - 32), /* 88 -- 8, bc */ - BMP_C(0xbd0be338) << (BMP_BIT - 32), /* 89 -- 32,bd0be338 */ - BMP_C(0xbf050000) << (BMP_BIT - 32), /* 90 -- 16, bf05 */ - BMP_C(0xc0000000) << (BMP_BIT - 32), /* 91 -- 3, 6 */ - BMP_C(0xc2b70000) << (BMP_BIT - 32), /* 92 -- 16, c2b7 */ - BMP_C(0xc6c60000) << (BMP_BIT - 32), /* 93 -- 16, c6c6 */ - BMP_C(0xc8000000) << (BMP_BIT - 32), /* 94 -- 5, 19 */ - BMP_C(0xc8670000) << (BMP_BIT - 32), /* 95 -- 16, c867 */ - BMP_C(0xcbf43926) << (BMP_BIT - 32), /* 96 -- 32,cbf43926 */ - BMP_C(0xd0000000) << (BMP_BIT - 32), /* 97 -- 8, d0 */ - BMP_C(0xd02a0000) << (BMP_BIT - 32), /* 98 -- 15, 6815 */ - BMP_C(0xd0db0000) << (BMP_BIT - 32), /* 99 -- 16, d0db */ - BMP_C(0xd4d00000) << (BMP_BIT - 32), /* 100 -- 12, d4d */ - BMP_C(0xd5000000) << (BMP_BIT - 32), /* 101 -- 8, d5 */ - BMP_C(0xd64e0000) << (BMP_BIT - 32), /* 102 -- 16, d64e */ - BMP_C(0xda000000) << (BMP_BIT - 32), /* 103 -- 8, da */ - BMP_C(0xdaf00000) << (BMP_BIT - 32), /* 104 -- 12, daf */ - BMP_C(0xe0000000) << (BMP_BIT - 32), /* 105 -- 3, 7 */ - BMP_C(0xe3069283) << (BMP_BIT - 32), /* 106 -- 32,e3069283 */ - BMP_C(0xe5cc0000) << (BMP_BIT - 32), /* 107 -- 16, e5cc */ - BMP_C(0xe7a80000) << (BMP_BIT - 32), /* 108 -- 13, 1cf5 */ - BMP_C(0xea000000) << (BMP_BIT - 32), /* 109 -- 7, 75 */ - BMP_C(0xea820000) << (BMP_BIT - 32), /* 110 -- 16, ea82 */ - BMP_C(0xec000000) << (BMP_BIT - 32), /* 111 -- 6, 3b */ - BMP_C(0xf1300000) << (BMP_BIT - 32), /* 112 -- 12, f13 */ - BMP_C(0xf4000000) << (BMP_BIT - 32), /* 113 -- 8, f4 */ - BMP_C(0xf5b00000) << (BMP_BIT - 32), /* 114 -- 12, f5b */ - BMP_C(0xf6400000) << (BMP_BIT - 32), /* 115 -- 10, 3d9 */ - BMP_C(0xf8000000) << (BMP_BIT - 32), /* 116 -- 5, 1f */ - BMP_C(0xfc000000) << (BMP_BIT - 32), /* 117 -- 6, 3f */ - BMP_C(0xfc891918) << (BMP_BIT - 32), /* 118 -- 32,fc891918 */ - BMP_C(0xfd000000) << (BMP_BIT - 32), /* 119 -- 8, fd */ - BMP_C(0xfe000000) << (BMP_BIT - 32), /* 120 -- 7, 7f */ - BMP_C(0xfedcba00) << (BMP_BIT - 32), /* 121 -- 24, fedcba */ - BMP_C(0xfee80000) << (BMP_BIT - 32), /* 122 -- 16, fee8 */ - BMP_C(0xff000000) << (BMP_BIT - 32), /* 123 -- 8, ff */ - BMP_C(0xffc00000) << (BMP_BIT - 32), /* 124 -- 10, 3ff */ - BMP_C(0xfff00000) << (BMP_BIT - 32), /* 125 -- 12, fff */ - BMP_C(0xffff0000) << (BMP_BIT - 32), /* 126 -- 16, ffff */ - BMP_C(0xfffffffe) << (BMP_BIT - 32), /* 127 -- 31,7fffffff */ - BMP_C(0xffffffff) << (BMP_BIT - 32), /* 128 -- 32,ffffffff */ -}; - -/* Table of preset CRC models. - * Sorted by left-justified polynomial for bsearch(). - */ -static const struct mpreset models[] = { - {32UL, b32+ 1, 0, P_BE, 0, b32+ 89, "XFER" }, /* 0 */ - {40UL, b40, 0, P_BE, b40a, b40b, "CRC-40/GSM" }, /* 1 */ - {32UL, b32+ 8, 0, P_BE, b32+128, b32+ 57, "CRC-32/POSIX" }, /* 2 */ - {32UL, b32+ 8, b32+128, P_BE, 0, b32+ 7, "CRC-32/MPEG-2" }, /* 3 */ - {32UL, b32+ 8, b32+128, P_BE, b32+128, b32+118, "CRC-32/BZIP2" }, /* 4 */ - {32UL, b32+ 8, b32+128, P_LE, 0, b32+ 39, "JAMCRC" }, /* 5 */ - {32UL, b32+ 8, b32+128, P_LE, b32+128, b32+ 96, "CRC-32" }, /* 6 */ - {16UL, b32+ 9, 0, P_BE, 0, b32+ 5, "CRC-16/DECT-X" }, /* 7 */ - {16UL, b32+ 9, 0, P_BE, b32+ 2, b32+ 4, "CRC-16/DECT-R" }, /* 8 */ - { 8UL, b32+ 10, 0, P_BE, 0, b32+113, "CRC-8" }, /* 9 */ - { 8UL, b32+ 10, 0, P_BE, b32+ 48, b32+ 78, "CRC-8/ITU" }, /* 10 */ - { 8UL, b32+ 10, b32+123, P_LE, 0, b32+ 97, "CRC-8/ROHC" }, /* 11 */ - {31UL, b32+ 11, b32+127, P_BE, b32+127, b32+ 19, "CRC-31/PHILIPS" }, /* 12 */ - { 6UL, b32+ 13, 0, P_LE, 0, b32+ 18, "CRC-6/ITU" }, /* 13 */ - {82UL, b82, 0, P_LE, 0, b82a, "CRC-82/DARC" }, /* 14 */ - {16UL, b32+ 15, 0, P_BE, 0, b32+ 37, "XMODEM" }, /* 15 */ - {16UL, b32+ 15, 0, P_LE, 0, b32+ 27, "KERMIT" }, /* 16 */ - {16UL, b32+ 15, b32+ 22, P_BE, 0, b32+107, "CRC-16/AUG-CCITT" }, /* 17 */ - {16UL, b32+ 15, b32+ 66, P_LE, 0, b32+ 30, "CRC-16/TMS37157" }, /* 18 */ - {16UL, b32+ 15, b32+ 83, P_LE, 0, b32+ 51, "CRC-16/RIELLO" }, /* 19 */ - {16UL, b32+ 15, b32+ 93, P_LE, 0, b32+ 90, "CRC-A" }, /* 20 */ - {16UL, b32+ 15, b32+126, P_BE, 0, b32+ 33, "CRC-16/CCITT-FALSE"}, /* 21 */ - {16UL, b32+ 15, b32+126, P_BE, b32+126, b32+102, "CRC-16/GENIBUS" }, /* 22 */ - {16UL, b32+ 15, b32+126, P_LE, 0, b32+ 54, "CRC-16/MCRF4XX" }, /* 23 */ - {16UL, b32+ 15, b32+126, P_LE, b32+126, b32+ 70, "X-25" }, /* 24 */ - { 7UL, b32+ 16, 0, P_BE, 0, b32+109, "CRC-7" }, /* 25 */ - { 6UL, b32+ 20, b32+117, P_BE, 0, b32+111, "CRC-6/CDMA2000-B" }, /* 26 */ - { 8UL, b32+ 21, b32+119, P_BE, 0, b32+ 59, "CRC-8/I-CODE" }, /* 27 */ - { 8UL, b32+ 21, b32+123, P_LE, 0, b32+ 71, "CRC-8/EBU" }, /* 28 */ - {32UL, b32+ 23, b32+128, P_LE, b32+128, b32+106, "CRC-32C" }, /* 29 */ - {14UL, b32+ 25, 0, P_LE, 0, b32+ 26, "CRC-14/DARC" }, /* 30 */ - { 5UL, b32+ 32, b32+116, P_LE, b32+116, b32+ 94, "CRC-5/USB" }, /* 31 */ - { 4UL, b32+ 34, 0, P_LE, 0, b32+ 55, "CRC-4/ITU" }, /* 32 */ - { 8UL, b32+ 36, 0, P_LE, 0, b32+ 78, "CRC-8/MAXIM" }, /* 33 */ - { 8UL, b32+ 41, 0, P_LE, 0, b32+ 17, "CRC-8/DARC" }, /* 34 */ - {16UL, b32+ 42, 0, P_BE, b32+126, b32+ 92, "CRC-16/EN-13757" }, /* 35 */ - {16UL, b32+ 42, 0, P_LE, b32+126, b32+110, "CRC-16/DNP" }, /* 36 */ - {64UL, b64, 0, P_BE, 0, b64a, "CRC-64" }, /* 37 */ - {64UL, b64, b64b, P_BE, b64b, b64c, "CRC-64/WE" }, /* 38 */ - {64UL, b64, b64b, P_LE, b64b, b64d, "CRC-64/XZ" }, /* 39 */ - { 5UL, b32+ 44, b32+ 44, P_BE, 0, b32+ 0, "CRC-5/EPC" }, /* 40 */ - {24UL, b32+ 49, b32+ 82, P_BE, 0, b32+ 24, "CRC-24/FLEXRAY-B" }, /* 41 */ - {24UL, b32+ 49, b32+121, P_BE, 0, b32+ 58, "CRC-24/FLEXRAY-A" }, /* 42 */ - { 3UL, b32+ 50, b32+105, P_LE, 0, b32+ 91, "CRC-3/ROHC" }, /* 43 */ - { 6UL, b32+ 52, 0, P_LE, 0, b32+ 72, "CRC-6/DARC" }, /* 44 */ - {11UL, b32+ 56, b32+ 6, P_BE, 0, b32+ 84, "CRC-11" }, /* 45 */ - {16UL, b32+ 60, 0, P_BE, 0, b32+122, "CRC-16/BUYPASS" }, /* 46 */ - {16UL, b32+ 60, 0, P_LE, 0, b32+ 87, "ARC" }, /* 47 */ - {16UL, b32+ 60, 0, P_LE, b32+126, b32+ 43, "CRC-16/MAXIM" }, /* 48 */ - {16UL, b32+ 60, b32+ 61, P_BE, 0, b32+ 76, "CRC-16/DDS-110" }, /* 49 */ - {16UL, b32+ 60, b32+126, P_LE, 0, b32+ 46, "MODBUS" }, /* 50 */ - {16UL, b32+ 60, b32+126, P_LE, b32+126, b32+ 85, "CRC-16/USB" }, /* 51 */ - {12UL, b32+ 62, 0, P_BE, 0, b32+114, "CRC-12/DECT" }, /* 52 */ - {12UL, b32+ 62, 0, P_BELE, 0, b32+104, "CRC-12/3GPP" }, /* 53 */ - {32UL, b32+ 63, 0, P_BE, 0, b32+ 35, "CRC-32Q" }, /* 54 */ - {24UL, b32+ 64, b32+ 86, P_BE, 0, b32+ 28, "CRC-24" }, /* 55 */ - {15UL, b32+ 67, 0, P_BE, 0, b32+ 12, "CRC-15" }, /* 56 */ - {16UL, b32+ 68, 0, P_BE, 0, b32+ 99, "CRC-16/T10-DIF" }, /* 57 */ - {10UL, b32+ 69, 0, P_BE, 0, b32+ 53, "CRC-10" }, /* 58 */ - { 8UL, b32+ 73, 0, P_LE, 0, b32+ 29, "CRC-8/WCDMA" }, /* 59 */ - { 8UL, b32+ 73, b32+123, P_BE, 0, b32+103, "CRC-8/CDMA2000" }, /* 60 */ - { 6UL, b32+ 74, b32+117, P_BE, 0, b32+ 38, "CRC-6/CDMA2000-A" }, /* 61 */ - { 7UL, b32+ 75, b32+120, P_LE, 0, b32+ 79, "CRC-7/ROHC" }, /* 62 */ - {16UL, b32+ 77, 0, P_BE, 0, b32+ 14, "CRC-16/TELEDISK" }, /* 63 */ - { 5UL, b32+ 80, 0, P_LE, 0, b32+ 40, "CRC-5/ITU" }, /* 64 */ - {32UL, b32+ 81, b32+128, P_LE, b32+128, b32+ 65, "CRC-32D" }, /* 65 */ - {16UL, b32+ 95, b32+126, P_BE, 0, b32+ 47, "CRC-16/CDMA2000" }, /* 66 */ - {15UL, b32+ 98, 0, P_BE, b32+ 3, b32+ 45, "CRC-15/MPT1327" }, /* 67 */ - { 8UL, b32+101, 0, P_BE, 0, b32+ 88, "CRC-8/DVB-S2" }, /* 68 */ - {13UL, b32+108, 0, P_BE, 0, b32+ 31, "CRC-13/BBC" }, /* 69 */ - {12UL, b32+112, b32+125, P_BE, 0, b32+100, "CRC-12/CDMA2000" }, /* 70 */ - {10UL, b32+115, b32+124, P_BE, 0, b32+ 69, "CRC-10/CDMA2000" }, /* 71 */ -}; -# define NPRESETS 72 - -/* List of names with pointers to models, pre-sorted for use with bsearch() */ -static const struct malias aliases[] = { - {"ARC", models+47, 1}, /* 0 */ - {"B-CRC-32", models+ 4, 0}, /* 1 */ - {"CKSUM", models+ 2, 0}, /* 2 */ - {"CRC-10", models+58, 1}, /* 3 */ - {"CRC-10/CDMA2000", models+71, 1}, /* 4 */ - {"CRC-11", models+45, 1}, /* 5 */ - {"CRC-12/3GPP", models+53, 1}, /* 6 */ - {"CRC-12/CDMA2000", models+70, 1}, /* 7 */ - {"CRC-12/DECT", models+52, 1}, /* 8 */ - {"CRC-13/BBC", models+69, 1}, /* 9 */ - {"CRC-14/DARC", models+30, 1}, /* 10 */ - {"CRC-15", models+56, 1}, /* 11 */ - {"CRC-15/MPT1327", models+67, 1}, /* 12 */ - {"CRC-16", models+47, 0}, /* 13 */ - {"CRC-16/ACORN", models+15, 0}, /* 14 */ - {"CRC-16/ARC", models+47, 0}, /* 15 */ - {"CRC-16/AUG-CCITT", models+17, 1}, /* 16 */ - {"CRC-16/BUYPASS", models+46, 1}, /* 17 */ - {"CRC-16/CCITT", models+16, 0}, /* 18 */ - {"CRC-16/CCITT-FALSE", models+21, 1}, /* 19 */ - {"CRC-16/CCITT-TRUE", models+16, 0}, /* 20 */ - {"CRC-16/CDMA2000", models+66, 1}, /* 21 */ - {"CRC-16/DARC", models+22, 0}, /* 22 */ - {"CRC-16/DDS-110", models+49, 1}, /* 23 */ - {"CRC-16/DECT-R", models+ 8, 1}, /* 24 */ - {"CRC-16/DECT-X", models+ 7, 1}, /* 25 */ - {"CRC-16/DNP", models+36, 1}, /* 26 */ - {"CRC-16/EN-13757", models+35, 1}, /* 27 */ - {"CRC-16/EPC", models+22, 0}, /* 28 */ - {"CRC-16/GENIBUS", models+22, 1}, /* 29 */ - {"CRC-16/I-CODE", models+22, 0}, /* 30 */ - {"CRC-16/IBM-SDLC", models+24, 0}, /* 31 */ - {"CRC-16/ISO-HDLC", models+24, 0}, /* 32 */ - {"CRC-16/LHA", models+47, 0}, /* 33 */ - {"CRC-16/MAXIM", models+48, 1}, /* 34 */ - {"CRC-16/MCRF4XX", models+23, 1}, /* 35 */ - {"CRC-16/RIELLO", models+19, 1}, /* 36 */ - {"CRC-16/SPI-FUJITSU", models+17, 0}, /* 37 */ - {"CRC-16/T10-DIF", models+57, 1}, /* 38 */ - {"CRC-16/TELEDISK", models+63, 1}, /* 39 */ - {"CRC-16/TMS37157", models+18, 1}, /* 40 */ - {"CRC-16/USB", models+51, 1}, /* 41 */ - {"CRC-16/VERIFONE", models+46, 0}, /* 42 */ - {"CRC-24", models+55, 1}, /* 43 */ - {"CRC-24/FLEXRAY-A", models+42, 1}, /* 44 */ - {"CRC-24/FLEXRAY-B", models+41, 1}, /* 45 */ - {"CRC-24/OPENPGP", models+55, 0}, /* 46 */ - {"CRC-3/ROHC", models+43, 1}, /* 47 */ - {"CRC-31/PHILIPS", models+12, 1}, /* 48 */ - {"CRC-32", models+ 6, 1}, /* 49 */ - {"CRC-32/AAL5", models+ 4, 0}, /* 50 */ - {"CRC-32/ADCCP", models+ 6, 0}, /* 51 */ - {"CRC-32/BZIP2", models+ 4, 1}, /* 52 */ - {"CRC-32/CASTAGNOLI", models+29, 0}, /* 53 */ - {"CRC-32/DECT-B", models+ 4, 0}, /* 54 */ - {"CRC-32/ISCSI", models+29, 0}, /* 55 */ - {"CRC-32/MPEG-2", models+ 3, 1}, /* 56 */ - {"CRC-32/POSIX", models+ 2, 1}, /* 57 */ - {"CRC-32C", models+29, 1}, /* 58 */ - {"CRC-32D", models+65, 1}, /* 59 */ - {"CRC-32Q", models+54, 1}, /* 60 */ - {"CRC-4/ITU", models+32, 1}, /* 61 */ - {"CRC-40/GSM", models+ 1, 1}, /* 62 */ - {"CRC-5/EPC", models+40, 1}, /* 63 */ - {"CRC-5/ITU", models+64, 1}, /* 64 */ - {"CRC-5/USB", models+31, 1}, /* 65 */ - {"CRC-6/CDMA2000-A", models+61, 1}, /* 66 */ - {"CRC-6/CDMA2000-B", models+26, 1}, /* 67 */ - {"CRC-6/DARC", models+44, 1}, /* 68 */ - {"CRC-6/ITU", models+13, 1}, /* 69 */ - {"CRC-64", models+37, 1}, /* 70 */ - {"CRC-64/WE", models+38, 1}, /* 71 */ - {"CRC-64/XZ", models+39, 1}, /* 72 */ - {"CRC-7", models+25, 1}, /* 73 */ - {"CRC-7/ROHC", models+62, 1}, /* 74 */ - {"CRC-8", models+ 9, 1}, /* 75 */ - {"CRC-8/AES", models+28, 0}, /* 76 */ - {"CRC-8/CDMA2000", models+60, 1}, /* 77 */ - {"CRC-8/DARC", models+34, 1}, /* 78 */ - {"CRC-8/DVB-S2", models+68, 1}, /* 79 */ - {"CRC-8/EBU", models+28, 1}, /* 80 */ - {"CRC-8/I-CODE", models+27, 1}, /* 81 */ - {"CRC-8/ITU", models+10, 1}, /* 82 */ - {"CRC-8/MAXIM", models+33, 1}, /* 83 */ - {"CRC-8/ROHC", models+11, 1}, /* 84 */ - {"CRC-8/WCDMA", models+59, 1}, /* 85 */ - {"CRC-82/DARC", models+14, 1}, /* 86 */ - {"CRC-A", models+20, 1}, /* 87 */ - {"CRC-B", models+24, 0}, /* 88 */ - {"CRC-CCITT", models+16, 0}, /* 89 */ - {"CRC-IBM", models+47, 0}, /* 90 */ - {"DOW-CRC", models+33, 0}, /* 91 */ - {"JAMCRC", models+ 5, 1}, /* 92 */ - {"KERMIT", models+16, 1}, /* 93 */ - {"MODBUS", models+50, 1}, /* 94 */ - {"PKZIP", models+ 6, 0}, /* 95 */ - {"R-CRC-16", models+ 8, 0}, /* 96 */ - {"X-25", models+24, 1}, /* 97 */ - {"X-CRC-12", models+52, 0}, /* 98 */ - {"X-CRC-16", models+ 7, 0}, /* 99 */ - {"XFER", models+ 0, 1}, /* 100 */ - {"XMODEM", models+15, 1}, /* 101 */ - {"ZMODEM", models+15, 0}, /* 102 */ - {NULL, NULL, 0}, /* terminating entry */ -}; -# define NALIASES 103 - -# endif /* BMP_BIT */ -#else /* PRESETS */ - -static const struct mpreset models[] = { - { 0UL, 0, 0, P_BE, 0, 0, NULL }, /* terminating entry */ -}; -# define NPRESETS 0 - -static const struct malias aliases[] = { - {NULL, NULL, 0}, /* terminating entry */ -}; -# define NALIASES 0 - -#endif /* PRESETS */ - -static const poly_t pzero = PZERO; - -static int acmp(const struct malias *, const struct malias *); -static void munpack(model_t *, const struct mpreset *); - -/* copy a parameter of a preset into a model */ -#define MUNPACK(parm) \ - praloc(&dest->parm, (src->b##parm ? src->width : 0UL)); \ - for(iter=0UL, idx=0UL; iter < dest->parm.length; iter += BMP_BIT, ++idx)\ - dest->parm.bitmap[idx] = src->b##parm[idx]; - -/* Definitions */ - -void -mcpy(model_t *dest, const model_t *src) { - /* Copies the parameters of src to dest. - * dest must be an initialised model. - */ - if(!dest || !src) return; - pcpy(&dest->spoly, src->spoly); - pcpy(&dest->init, src->init); - pcpy(&dest->xorout, src->xorout); - pcpy(&dest->check, src->check); - dest->flags = src->flags; - /* link to the name as it is static */ - dest->name = src->name; -} - -void -mfree(model_t *model) { - /* Frees the parameters of model. */ - if(!model) return; - pfree(&model->spoly); - pfree(&model->init); - pfree(&model->xorout); - pfree(&model->check); - /* not name as it is static */ - /* not model either, it might point to an array! */ -} - -int -mcmp(const model_t *a, const model_t *b) { - /* Compares a and b for identical effect, i.e. disregarding - * trailing zeroes in parameter polys. - * Intended for bsearch() to find a matching model in models[]. - */ - int result; - if(!a || !b) return(!b - !a); - if((result = psncmp(&a->spoly, &b->spoly))) return(result); - if((result = psncmp(&a->init, &b->init))) return(result); - if((a->flags & P_REFIN) && (~b->flags & P_REFIN)) return(1); - if((~a->flags & P_REFIN) && (b->flags & P_REFIN)) return(-1); - if((a->flags & P_REFOUT) && (~b->flags & P_REFOUT)) return(1); - if((~a->flags & P_REFOUT) && (b->flags & P_REFOUT)) return(-1); - return(psncmp(&a->xorout, &b->xorout)); -} - -int -mbynam(model_t *dest, const char *key) { - /* Sets parameters in dest according to the model named by key. - */ - struct malias akey = {NULL, NULL, 0}, *aptr; - char *ukey, *uptr; - - if(!aliases->name) - return(-1); - if(!(ukey = malloc((size_t) 1 + strlen(key)))) - uerror("cannot allocate memory for comparison string"); - akey.name = uptr = ukey; - do - *uptr++ = toupper((unsigned char)*key); - while(*key++); - - aptr = bsearch(&akey, aliases, NALIASES, sizeof(struct malias), (int (*)(const void *, const void *)) &acmp); - free(ukey); - - if(aptr == NULL) - return(0); - munpack(dest, aptr->model); - return(1); -} - -void -mbynum(model_t *dest, int num) { - /* Sets parameters in dest according to the model indexed by num. */ - if(num > NPRESETS) - num = NPRESETS; - munpack(dest, models+num); -} - -int -mcount(void) { - /* Returns the number of preset models. */ - return(NPRESETS); -} - -char * -mnames(void) { - /* Returns a malloc()-ed string of the names of all preset - * models, separated by newlines and terminated by NULL. - * Aliases are not listed. - */ - size_t size = 0; - char *string, *sptr; - const struct malias *aptr = aliases; - - while(aptr->name) { - if(aptr->isprimry) - size += strlen(aptr->name) + 1; - ++aptr; - } - if(!size) return(NULL); - if((string = malloc(size))) { - aptr = aliases; - sptr = string; - while(aptr->name) { - if(aptr->isprimry) { - strcpy(sptr, aptr->name); - sptr += strlen(aptr->name); - *sptr++ = '\n'; - } - ++aptr; - } - *--sptr = '\0'; - } else - uerror("cannot allocate memory for list of models"); - - return(string); -} - -char * -mtostr(const model_t *model) { - /* Returns a malloc()-ed string containing a Williams model - * record representing the input model. - * mcanon() should be called on the argument before printing. - */ - size_t size; - char *polystr, *initstr, *xorotstr, *checkstr, strbuf[512], *string = NULL; - - if(!model) return(NULL); - polystr = ptostr(model->spoly, P_RTJUST, 4); - initstr = ptostr(model->init, P_RTJUST, 4); - xorotstr = ptostr(model->xorout, P_RTJUST, 4); - checkstr = ptostr(model->check, P_RTJUST, 4); - - sprintf(strbuf, "%lu", plen(model->spoly)); - size = - 70 - + (model->name && *model->name ? 2 + strlen(model->name) : 6) - + strlen(strbuf) - + (polystr && *polystr ? strlen(polystr) : 6) - + (initstr && *initstr ? strlen(initstr) : 6) - + (model->flags & P_REFIN ? 4 : 5) - + (model->flags & P_REFOUT ? 4 : 5) - + (xorotstr && *xorotstr ? strlen(xorotstr) : 6) - + (checkstr && *checkstr ? strlen(checkstr) : 6); - if((string = malloc(size))) { - sprintf(strbuf, "\"%s\"", model->name); - sprintf(string, - "width=%lu " - "poly=0x%s " - "init=0x%s " - "refin=%s " - "refout=%s " - "xorout=0x%s " - "check=0x%s " - "name=%s", - plen(model->spoly), - polystr && *polystr ? polystr : "(none)", - initstr && *initstr ? initstr : "(none)", - (model->flags & P_REFIN) ? "true" : "false", - (model->flags & P_REFOUT) ? "true" : "false", - xorotstr && *xorotstr ? xorotstr : "(none)", - checkstr && *checkstr ? checkstr : "(none)", - (model->name && *model->name) ? strbuf : "(none)"); - } - free(polystr); - free(initstr); - free(xorotstr); - free(checkstr); - if(!string) - uerror("cannot allocate memory for model description"); - return(string); -} - -void -mmatch(model_t *model, int flags) { - /* searches models[] for a model matching the argument, and links a name if found - * if flags & M_OVERWR, copies the found model onto the argument. */ - model_t *mptr; - if(!model) return; - - mptr = bsearch(model, models, NPRESETS, sizeof(model_t), (int (*)(const void *, const void *)) &mcmp); - if(mptr) { - model->name = mptr->name; - if(flags & M_OVERWR) - mcpy(model, mptr); - } -} - -void -mcanon(model_t *model) { - /* canonicalise a model */ - unsigned long dlen; - - if(!model) return; - - /* extending on the right here. This preserves the functionality - * of a presumed working model. - */ - psnorm(&model->spoly); - dlen = plen(model->spoly); - praloc(&model->init, dlen); - praloc(&model->xorout, dlen); - - if(!plen(model->check)) - mcheck(model); -} - -void -mcheck(model_t *model) { - /* calculate a check for the model */ - poly_t checkstr, check; - - /* generate the check string with the correct bit order */ - checkstr = strtop("313233343536373839", model->flags, 8); - check = pcrc(checkstr, model->spoly, model->init, pzero, model->flags); - if(model->flags & P_REFOUT) - prev(&check); - psum(&check, model->xorout, 0UL); - model->check = check; - pfree(&checkstr); -} - -void -mrev(model_t *model) { - /* reverse the model to calculate reversed CRCs */ - /* Here we invert RefIn and RefOut so that the user need only - * reverse the order of characters in the arguments, not the - * characters themselves. If RefOut=True, the mirror image of - * Init seen through RefOut becomes XorOut, and as RefOut - * becomes false, the XorOut value moved to Init stays upright. - * If RefOut=False, Init transfers to XorOut without reflection - * but the new Init must be reflected to present the same image, - * as RefOut becomes true. - */ - poly_t temp; - - prcp(&model->spoly); - if(model->flags & P_REFOUT) - prev(&model->init); - else - prev(&model->xorout); - - /* exchange init and xorout */ - temp = model->init; - model->init = model->xorout; - model->xorout = temp; - - /* invert refin and refout */ - model->flags ^= P_REFIN | P_REFOUT; - - mnovel(model); -} - -void -mnovel(model_t *model) { - /* remove name and check string from modified model */ - model->name = NULL; - pfree(&model->check); -} - -static int -acmp(const struct malias *a, const struct malias *b) { - /* compares two aliases, for use in bsearch */ - if(!a || !b) return(!b - !a); - if(!a->name || !b->name) return(!b->name - !a->name); - return(strcmp(a->name, b->name)); -} - -static void -munpack(model_t *dest, const struct mpreset *src) { - /* Copies the parameters of src to dest. - * dest must be an initialised model. - */ - unsigned long iter, idx; - if(!dest || !src) return; - MUNPACK(spoly); - MUNPACK(init); - MUNPACK(xorout); - MUNPACK(check); - dest->flags = src->flags; - /* link to the name as it is static */ - dest->name = src->name; -} diff --git a/client/reveng/poly.c b/client/reveng/poly.c deleted file mode 100644 index e4a8e8f9..00000000 --- a/client/reveng/poly.c +++ /dev/null @@ -1,1196 +0,0 @@ -/* poly.c - * Greg Cook, 9/Apr/2015 - */ - -/* CRC RevEng, an arbitrary-precision CRC calculator and algorithm finder - * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015 Gregory Cook - * - * This file is part of CRC RevEng. - * - * CRC RevEng is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * CRC RevEng is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with CRC RevEng. If not, see . - */ - -/* 2015-04-03: added direct mode to strtop() - * 2014-01-11: added LOFS(), RNDUP() - * 2013-09-16: SIZE(), IDX(), OFS() macros bitshift if BMP_POF2 - * 2013-02-07: conditional non-2^n fix, pmpar() return mask constant type - * 2013-01-17: fixed pfirst(), plast() for non-2^n BMP_BIT - * 2012-07-16: added pident() - * 2012-05-23: added pmpar() - * 2012-03-03: internal lookup tables stored better - * 2012-03-02: fixed full-width masking in filtop() - * 2011-09-06: added prevch() - * 2011-08-27: fixed zero test in piter() - * 2011-01-17: fixed ANSI C warnings, uses bmp_t type - * 2011-01-15: palloc() and praloc() gracefully handle lengths slightly - * less than ULONG_MAX - * 2011-01-15: strtop() error on invalid argument. pkchop() special case - * when argument all zeroes - * 2011-01-14: added pkchop() - * 2011-01-04: fixed bogus final length calculation in wide pcrc() - * 2011-01-02: faster, more robust prcp() - * 2011-01-01: commented functions, full const declarations, all-LUT rev() - * 2010-12-26: renamed CRC RevEng - * 2010-12-18: removed pmods(), finished pcrc(), added piter() - * 2010-12-17: roughed out pcrc(). difficult, etiam aberat musa heri :( - * 2010-12-15: added psnorm(), psncmp(); optimised pnorm(); fix to praloc() - * 2010-12-14: strtop() resets count between passes - * 2010-12-12: added pright() - * 2010-12-11: filtop won't read more than length bits - * 2010-12-10: finished filtop. 26 public functions - * 2010-12-05: finished strtop, pxsubs; unit tests - * 2010-12-02: project started - */ - -/* Note: WELL-FORMED poly_t objects have a valid bitmap pointer pointing - * to a malloc()-ed array of at least as many bits as stated in its - * length field. Any poly_t with a length of 0 is also a WELL-FORMED - * poly_t (whatever value the bitmap pointer has.) - * All poly_t objects passed to and from functions must be WELL-FORMED - * unless otherwise stated. - * - * CLEAN (or CANONICAL) poly_t objects are WELL-FORMED objects in which - * all spare bits in the bitmap word containing the last bit are zero. - * (Any excess allocated words will not be accessed.) - * - * SEMI-NORMALISED poly_t objects are CLEAN objects in which the last - * bit, at position (length - 1), is one. - * - * NORMALISED poly_t objects are SEMI-NORMALISED objects in which the - * first bit is one. - * - * pfree() should be called on every poly_t object (including - * those returned by functions) after its last use. - * As always, free() should be called on every malloc()-ed string after - * its last use. - */ - -#include -#include -#include -#include "reveng.h" - -static bmp_t getwrd(const poly_t poly, unsigned long iter); -static bmp_t rev(bmp_t accu, int bits); -static void prhex(char **spp, bmp_t bits, int flags, int bperhx); - -static const poly_t pzero = PZERO; - -/* word number (0..m-1) of var'th bit (0..n-1) */ -#if BMP_POF2 >= 5 -# define IDX(var) ((var) >> BMP_POF2) -#else -# define IDX(var) ((var) / BMP_BIT) -#endif - -/* size of polynomial with var bits */ -#if BMP_POF2 >= 5 -# define SIZE(var) ((BMP_BIT - 1UL + (var)) >> BMP_POF2) -#else -# define SIZE(var) ((BMP_BIT - 1UL + (var)) / BMP_BIT) -#endif - -/* polynomial length rounded up to BMP_BIT */ -#ifdef BMP_POF2 -# define RNDUP(var) (~(BMP_BIT - 1UL) & (BMP_BIT - 1UL + (var))) -#else -# define RNDUP(var) ((BMP_BIT - (var) % BMP_BIT) % BMP_BIT + (var)) -#endif - -/* bit offset (0..BMP_BIT-1, 0 = LSB) of var'th bit (0..n-1) */ -#ifdef BMP_POF2 -# define OFS(var) ((int) ((BMP_BIT - 1UL) & ~(var))) -#else -# define OFS(var) ((int) (BMP_BIT - 1UL - (var) % BMP_BIT)) -#endif - -/* bit offset (0..BMP_BIT-1, 0 = MSB) of var'th bit (0..n-1) */ -#ifdef BMP_POF2 -# define LOFS(var) ((int) ((BMP_BIT - 1UL) & (var))) -#else -# define LOFS(var) ((int) ((var) % BMP_BIT)) -#endif - -poly_t -filtop(FILE *input, unsigned long length, int flags, int bperhx) { - /* reads binary data from input into a poly_t until EOF or until - * length bits are read. Characters are read until - * ceil(bperhx / CHAR_BIT) bits are collected; if P_LTLBYT is - * set in flags then the first character contains the LSB, - * otherwise the last one does. The least significant bperhx - * bits are taken, reflected (if P_REFIN) and appended to the - * result, then more characters are read. The maximum number of - * characters read is - * floor(length / bperhx) * ceil(bperhx / * CHAR_BIT). - * The returned poly_t is CLEAN. - */ - - bmp_t accu = BMP_C(0); - bmp_t mask = bperhx == BMP_BIT ? ~BMP_C(0) : (BMP_C(1) << bperhx) - BMP_C(1); - unsigned long iter = 0UL, idx; - int cmask = ~(~0U << CHAR_BIT), c; - int count = 0, ofs; - poly_t poly = PZERO; - if(bperhx == 0) return(poly); - - length -= length % bperhx; - palloc(&poly, length); /* >= 0 */ - - while(iter < length && (c = fgetc(input)) != EOF) { - if(flags & P_LTLBYT) - accu |= (bmp_t) (c & cmask) << count; - else - accu = (accu << CHAR_BIT) | (bmp_t) (c & cmask); - count += CHAR_BIT; - if(count >= bperhx) { - /* the low bperhx bits of accu contain bits of the poly.*/ - iter += bperhx; - count = 0; - if(flags & P_REFIN) - accu = rev(accu, bperhx); - accu &= mask; - - /* iter >= bperhx > 0 */ - idx = IDX(iter - 1UL); - ofs = OFS(iter - 1UL); - poly.bitmap[idx] |= accu << ofs; - if(ofs + bperhx > BMP_BIT) { - poly.bitmap[idx-1] |= accu >> (BMP_BIT - ofs); - } - accu = BMP_C(0); /* only needed for P_LTLBYT */ - } - } - praloc(&poly, iter); - return(poly); -} - -poly_t -strtop(const char *string, int flags, int bperhx) { - /* Converts a hex or character string to a poly_t. - * Each character is converted to a hex nibble yielding 4 bits - * unless P_DIRECT, when each character yields CHAR_BIT bits. - * Nibbles and characters are accumulated left-to-right - * unless P_DIRECT && P_LTLBYT, when they are accumulated - * right-to-left without reflection. - * As soon as at least bperhx bits are accumulated, the - * rightmost bperhx bits are reflected (if P_REFIN) - * and appended to the poly. When !P_DIRECT: - * bperhx=8 reads hex nibbles in pairs - * bperhx=7 reads hex nibbles in pairs and discards - * b3 of first nibble - * bperhx=4 reads hex nibbles singly - * bperhx=3 reads octal - * bperhx=1 reads longhand binary - * in theory if !P_REFIN, bperhx can be any multiple of 4 - * with equal effect - * The returned poly_t is CLEAN. - */ - - /* make two passes, one to determine the poly size - * one to populate the bitmap - */ - unsigned long length = 1UL, idx; - bmp_t accu; - bmp_t mask = bperhx == BMP_BIT ? ~BMP_C(0) : (BMP_C(1) << bperhx) - BMP_C(1); - int pass, count, ofs; - int cmask = ~(~0U << CHAR_BIT), c; - const char *s; - - poly_t poly = PZERO; - if(bperhx > BMP_BIT || bperhx <= 0 || string == NULL || *string == '\0') - return(poly); - - for(pass=0; pass<2 && length > 0UL; ++pass) { - s = string; - length = 0UL; - count = 0; - accu = BMP_C(0); - while((c = *s++)) { - if(flags & P_DIRECT) { - if(flags & P_LTLBYT) - accu |= (bmp_t) (c & cmask) << count; - else - accu = (accu << CHAR_BIT) | (bmp_t) (c & cmask); - count += CHAR_BIT; - } else { - if(c == ' ' || c == '\t' || c == '\r' || c == '\n') continue; - accu <<= 4; - count += 4; - switch(c) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - accu |= (bmp_t) c - '0'; - break; - case 'A': - case 'a': - accu |= BMP_C(0xa); - break; - case 'B': - case 'b': - accu |= BMP_C(0xb); - break; - case 'C': - case 'c': - accu |= BMP_C(0xc); - break; - case 'D': - case 'd': - accu |= BMP_C(0xd); - break; - case 'E': - case 'e': - accu |= BMP_C(0xe); - break; - case 'F': - case 'f': - accu |= BMP_C(0xf); - break; - default: - uerror("invalid character in hexadecimal argument"); - } - } - - if(count >= bperhx) { - /* the low bperhx bits of accu contain bits of the poly. - * in pass 0, increment length by bperhx. - * in pass 1, put the low bits of accu into the bitmap. */ - length += bperhx; - count = 0; - if(pass == 1) { - if(flags & P_REFIN) - accu = rev(accu, bperhx); - accu &= mask; - - /* length >= bperhx > 0 */ - idx = IDX(length - 1); - ofs = OFS(length - 1); - poly.bitmap[idx] |= accu << ofs; - if(ofs + bperhx > BMP_BIT) - poly.bitmap[idx-1] |= accu >> (BMP_BIT - ofs); - accu = BMP_C(0); /* only needed for P_LTLBYT */ - } - } - } - if(pass == 0) palloc(&poly, length); - } - return(poly); -} - -char * -ptostr(const poly_t poly, int flags, int bperhx) { - /* Returns a malloc()-ed string containing a hexadecimal - * representation of poly. See phxsubs(). - */ - return(pxsubs(poly, flags, bperhx, 0UL, poly.length)); -} - -char * -pxsubs(const poly_t poly, int flags, int bperhx, unsigned long start, unsigned long end) { - /* Returns a malloc()-ed string containing a hexadecimal - * representation of a portion of poly, from bit offset start to - * (end - 1) inclusive. The output is grouped into words of - * bperhx bits each. If P_RTJUST then the first word is padded - * with zeroes at the MSB end to make a whole number of words, - * otherwise the last word is padded at the LSB end. After - * justification the bperhx bits of each word are reversed (if - * P_REFOUT) and printed as a hex sequence, with words - * optionally separated by spaces (P_SPACE). - * If end exceeds the length of poly then zero bits are appended - * to make up the difference, in which case poly must be CLEAN. - */ - char *string, *sptr; - unsigned long size, iter; - bmp_t accu; - bmp_t mask = bperhx == BMP_BIT ? ~BMP_C(0) : (BMP_C(1) << bperhx) - BMP_C(1); - int cperhx, part; - - if(bperhx <= 0 || bperhx > BMP_BIT) return(NULL); - - if(start > poly.length) start = poly.length; - if(end > poly.length) end = poly.length; - if(end < start) end = start; - - cperhx = (bperhx + 3) >> 2; - if(flags & P_SPACE) ++cperhx; - - size = (end - start + bperhx - 1UL) / bperhx; - size *= cperhx; - if(!size || ~flags & P_SPACE) ++size; /* for trailing null */ - - if(!(sptr = string = (char *) malloc(size))) - uerror("cannot allocate memory for string"); - - size = end - start; - part = (int) size % bperhx; - if(part && flags & P_RTJUST) { - iter = start + part; - accu = getwrd(poly, iter - 1UL) & ((BMP_C(1) << part) - BMP_C(1)); - if(flags & P_REFOUT) - /* best to reverse over bperhx rather than part, I think - * e.g. converting a 7-bit poly to 8-bit little-endian hex - */ - accu = rev(accu, bperhx); - prhex(&sptr, accu, flags, bperhx); - if(flags & P_SPACE && size > iter) *sptr++ = ' '; - } else { - iter = start; - } - - while((iter+=bperhx) <= end) { - accu = getwrd(poly, iter - 1UL) & mask; - if(flags & P_REFOUT) - accu = rev(accu, bperhx); - prhex(&sptr, accu, flags, bperhx); - if(flags & P_SPACE && size > iter) *sptr++ = ' '; - } - - if(part && ~flags & P_RTJUST) { - accu = getwrd(poly, end - 1UL); - if(flags & P_REFOUT) - accu = rev(accu, part); - else - accu = accu << (bperhx - part) & mask; - prhex(&sptr, accu, flags, bperhx); - } - *sptr = '\0'; - return(string); -} - -poly_t -pclone(const poly_t poly) { - /* Returns a freestanding copy of poly. Does not clean poly or - * the result. - */ - poly_t clone = PZERO; - - pcpy(&clone, poly); - return(clone); -} - -void -pcpy(poly_t *dest, const poly_t src) { - /* Assigns (copies) src into dest. Does not clean src or dest. - */ - unsigned long iter, idx; - - praloc(dest, src.length); - for(iter=0UL, idx=0UL; iter < src.length; iter += BMP_BIT, ++idx) - dest->bitmap[idx] = src.bitmap[idx]; -} - -void -pcanon(poly_t *poly) { - /* Converts poly into a CLEAN object by freeing unused bitmap words - * and clearing any bits in the last word beyond the last bit. - * The length field has absolute priority over the contents of the bitmap. - * Canonicalisation differs from normalisation in that leading and trailing - * zero terms are significant and preserved. - * poly may or may not be WELL-FORMED. - */ - praloc(poly, poly->length); -} - -void -pnorm(poly_t *poly) { - /* Converts poly into a NORMALISED object by removing leading - * and trailing zeroes, so that the polynomial starts and ends - * with significant terms. - * poly may or may not be WELL-FORMED. - */ - unsigned long first; - - /* call pcanon() here so pfirst() and plast() return the correct - * results - */ - pcanon(poly); - first = pfirst(*poly); - if(first) - pshift(poly, *poly, 0UL, first, plast(*poly), 0UL); - else - praloc(poly, plast(*poly)); -} - -void -psnorm(poly_t *poly) { - /* Converts poly into a SEMI-NORMALISED object by removing - * trailing zeroes, so that the polynomial ends with a - * significant term. - * poly may or may not be WELL-FORMED. - */ - - /* call pcanon() here so plast() returns the correct result */ - pcanon(poly); - praloc(poly, plast(*poly)); -} - -void -pchop(poly_t *poly) { - /* Normalise poly, then chop off the highest significant term - * (produces a SEMI-NORMALISED object). poly becomes a suitable - * divisor for pcrc(). - * poly may or may not be WELL-FORMED. - */ - - /* call pcanon() here so pfirst() and plast() return correct - * results - */ - pcanon(poly); - pshift(poly, *poly, 0UL, pfirst(*poly) + 1UL, plast(*poly), 0UL); -} - -void -pkchop(poly_t *poly) { - /* Convert poly from Koopman notation to chopped form (produces - * a SEMI-NORMALISED object). poly becomes a suitable divisor - * for pcrc(). - * poly may or may not be WELL-FORMED. - */ - unsigned long first; - - /* call pcanon() here so pfirst() returns the correct result */ - pcanon(poly); - first = pfirst(*poly); - if(first >= poly->length) { - pfree(poly); - return; - } - pshift(poly, *poly, 0UL, first + 1UL, poly->length, 1UL); - piter(poly); -} - -unsigned long -plen(const poly_t poly) { - /* Return length of polynomial. - * poly may or may not be WELL-FORMED. - */ - return(poly.length); -} - -int -pcmp(const poly_t *a, const poly_t *b) { - /* Compares poly_t objects for identical sizes and contents. - * a and b must be CLEAN. - * Defines a total order relation for sorting, etc. although - * mathematically, polynomials of equal degree are no greater or - * less than one another. - */ - unsigned long iter; - bmp_t *aptr, *bptr; - - if(!a || !b) return(!b - !a); - if(a->length < b->length) return(-1); - if(a->length > b->length) return(1); - aptr = a->bitmap; - bptr = b->bitmap; - for(iter=0UL; iter < a->length; iter += BMP_BIT) { - if(*aptr < *bptr) - return(-1); - if(*aptr++ > *bptr++) - return(1); - } - return(0); -} - -int -psncmp(const poly_t *a, const poly_t *b) { - /* Compares polys for identical effect, i.e. as though the - * shorter poly were padded with zeroes to the length of the - * longer. - * a and b must still be CLEAN, therefore psncmp() is *not* - * identical to pcmp() on semi-normalised polys as psnorm() - * clears the slack space. - */ - unsigned long length, iter, idx; - bmp_t aword, bword; - if(!a || !b) return(!b - !a); - length = (a->length > b->length) ? a->length : b->length; - for(iter = 0UL, idx = 0UL; iter < length; iter += BMP_BIT, ++idx) { - aword = (iter < a->length) ? a->bitmap[idx] : BMP_C(0); - bword = (iter < b->length) ? b->bitmap[idx] : BMP_C(0); - if(aword < bword) - return(-1); - if(aword > bword) - return(1); - } - return(0); -} - - -int -ptst(const poly_t poly) { - /* Tests whether a polynomial equals zero. Returns 0 if equal, - * a nonzero value otherwise. - * poly must be CLEAN. - */ - unsigned long iter; - bmp_t *bptr; - if(!poly.bitmap) return(0); - for(iter = 0UL, bptr = poly.bitmap; iter < poly.length; iter += BMP_BIT) - if(*bptr++) return(1); - return(0); -} - -unsigned long -pfirst(const poly_t poly) { - /* Returns the index of the first nonzero term in poly. If none - * is found, returns the length of poly. - * poly must be CLEAN. - */ - unsigned long idx = 0UL, size = SIZE(poly.length); - bmp_t accu = BMP_C(0); /* initialiser for Acorn C */ - unsigned int probe = BMP_SUB, ofs = 0; - - while(idx < size && !(accu = poly.bitmap[idx])) ++idx; - if(idx >= size) return(poly.length); - while(probe) { -#ifndef BMP_POF2 - while((ofs | probe) >= (unsigned int) BMP_BIT) probe >>= 1; -#endif - if(accu >> (ofs | probe)) ofs |= probe; - probe >>= 1; - } - - return(BMP_BIT - 1UL - ofs + idx * BMP_BIT); -} - -unsigned long -plast(const poly_t poly) { - /* Returns 1 plus the index of the last nonzero term in poly. - * If none is found, returns zero. - * poly must be CLEAN. - */ - unsigned long idx, size = SIZE(poly.length); - bmp_t accu; - unsigned int probe = BMP_SUB, ofs = 0; - - if(!poly.length) return(0UL); - idx = size - 1UL; - while(idx && !(accu = poly.bitmap[idx])) --idx; - if(!idx && !(accu = poly.bitmap[idx])) return(0UL); - /* now accu == poly.bitmap[idx] and contains last significant term */ - while(probe) { -#ifndef BMP_POF2 - while((ofs | probe) >= (unsigned int) BMP_BIT) probe >>= 1; -#endif - if(accu << (ofs | probe)) ofs |= probe; - probe >>= 1; - } - - return(idx * BMP_BIT + ofs + 1UL); -} - -poly_t -psubs(const poly_t src, unsigned long head, unsigned long start, unsigned long end, unsigned long tail) { - poly_t dest = PZERO; - pshift(&dest, src, head, start, end, tail); - return(dest); -} - -void -pright(poly_t *poly, unsigned long length) { - /* Trims or extends poly to length at the left edge, prepending - * zeroes if necessary. Analogous to praloc() except the - * rightmost terms of poly are preserved. - * On entry, poly may or may not be WELL-FORMED. - * On exit, poly is CLEAN. - */ - - if(length > poly->length) - pshift(poly, *poly, length - poly->length, 0UL, poly->length, 0UL); - else if(length < poly->length) - pshift(poly, *poly, 0UL, poly->length - length, poly->length, 0UL); - else - praloc(poly, poly->length); -} - -void -pshift(poly_t *dest, const poly_t src, unsigned long head, unsigned long start, unsigned long end, unsigned long tail) { - /* copies bits start to end-1 of src to dest, plus the number of leading and trailing zeroes given by head and tail. - * end may exceed the length of src in which case more zeroes are appended. - * dest may point to src, in which case the poly is edited in place. - * On exit, dest is CLEAN. - */ - - unsigned long length, fulllength, size, fullsize, iter, idx, datidx; - /* condition inputs; end, head and tail may be any value */ - if(end < start) end = start; - - length = end - start + head; - fulllength = length + tail; - if(fulllength > src.length) - praloc(dest, fulllength); - else - praloc(dest, src.length); - - /* number of words in new poly */ - size = SIZE(length); - fullsize = SIZE(fulllength); - /* array index of first word ending up with source material */ - datidx = IDX(head); - - if(head > start && end > start) { - /* shifting right, size > 0 */ - /* index of the source bit ending up in the LSB of the last word - * size * BMP_BIT >= length > head > 0 */ - iter = size * BMP_BIT - head - 1UL; - for(idx = size - 1UL; idx > datidx; iter -= BMP_BIT, --idx) - dest->bitmap[idx] = getwrd(src, iter); - dest->bitmap[idx] = getwrd(src, iter); - /* iter == size * BMP_BIT - head - 1 - BMP_BIT * (size - 1 - datidx) - * == BMP_BIT * (size - size + 1 + datidx) - head - 1 - * == BMP_BIT * (1 + head / BMP_BIT) - head - 1 - * == BMP_BIT + head - head % BMP_BIT - head - 1 - * == BMP_BIT - head % BMP_BIT - 1 - * >= 0 - */ - } else if(head <= start) { - /* shifting left or copying */ - /* index of the source bit ending up in the LSB of bitmap[idx] */ - iter = start - head + BMP_BIT - 1UL; - for(idx = datidx; idx < size; iter += BMP_BIT, ++idx) - dest->bitmap[idx] = getwrd(src, iter); - } - - /* clear head */ - for(idx = 0UL; idx < datidx; ++idx) - dest->bitmap[idx] = BMP_C(0); - if(size) - dest->bitmap[datidx] &= ~BMP_C(0) >> LOFS(head); - - /* clear tail */ - if(LOFS(length)) - dest->bitmap[size - 1UL] &= ~(~BMP_C(0) >> LOFS(length)); - for(idx = size; idx < fullsize; ++idx) - dest->bitmap[idx] = BMP_C(0); - - /* call praloc to shrink poly if required */ - if(dest->length > fulllength) - praloc(dest, fulllength); -} - -void -ppaste(poly_t *dest, const poly_t src, unsigned long skip, unsigned long seek, unsigned long end, unsigned long fulllength) { - /* pastes terms of src, starting from skip, to positions seek to end-1 of dest - * then sets length of dest to fulllength (>= end) - * to paste n terms of src, give end = seek + n - * to truncate dest at end of paste, set fulllength = end - * to avoid truncating, set fulllength = plen(*dest) - * dest may point to src, in which case the poly is edited in place. - * src must be CLEAN in the case that the end is overrun. - * On exit, dest is CLEAN. - */ - bmp_t mask; - unsigned long seekidx, endidx, iter; - int seekofs; - if(end < seek) end = seek; - if(fulllength < end) fulllength = end; - - /* expand dest if necessary. don't shrink as dest may be src */ - if(fulllength > dest->length) - praloc(dest, fulllength); - seekidx = IDX(seek); - endidx = IDX(end); - seekofs = OFS(seek); - /* index of the source bit ending up in the LSB of the first modified word */ - iter = skip + seekofs; - if(seekidx == endidx) { - /* paste affects one word (traps end = seek case) */ - mask = ((BMP_C(1) << seekofs) - (BMP_C(1) << OFS(end))) << 1; - dest->bitmap[seekidx] = (dest->bitmap[seekidx] & ~mask) | (getwrd(src, iter) & mask); - } else if(seek > skip) { - /* shifting right */ - /* index of the source bit ending up in the LSB of the last modified word */ - iter += (endidx - seekidx) * BMP_BIT; - mask = ~BMP_C(0) >> LOFS(end); - dest->bitmap[endidx] = (dest->bitmap[endidx] & mask) | (getwrd(src, iter) & ~mask); - for(iter -= BMP_BIT, --endidx; endidx > seekidx; iter -= BMP_BIT, --endidx) - dest->bitmap[endidx] = getwrd(src, iter); - mask = ~BMP_C(0) >> LOFS(seek); - dest->bitmap[endidx] = (dest->bitmap[endidx] & ~mask) | (getwrd(src, iter) & mask); - /* iter == skip + seekofs + (endidx - seekidx) * BMP_BIT - BMP_BIT * (endidx - seekidx) - * == skip + seekofs + BMP_BIT * (endidx - seekidx - endidx + seekidx) - * == skip + seekofs - * >= 0 - */ - } else { - /* shifting left or copying */ - mask = ~BMP_C(0) >> LOFS(seek); - dest->bitmap[seekidx] = (dest->bitmap[seekidx] & ~mask) | (getwrd(src, iter) & mask); - for(iter += BMP_BIT, ++seekidx; seekidx < endidx; iter += BMP_BIT, ++seekidx) - dest->bitmap[seekidx] = getwrd(src, iter); - mask = ~BMP_C(0) >> LOFS(end); - dest->bitmap[seekidx] = (dest->bitmap[seekidx] & mask) | (getwrd(src, iter) & ~mask); - } - /* shrink poly if required */ - if(dest->length > fulllength) - praloc(dest, fulllength); -} - -void -pdiff(poly_t *dest, const poly_t src, unsigned long ofs) { - /* Subtract src from dest (modulo 2) at offset ofs. - * In modulo 2 arithmetic, subtraction is equivalent to addition - * We include an alias for those who wish to retain the distinction - * src and dest must be CLEAN. - */ - psum(dest, src, ofs); -} - -void -psum(poly_t *dest, const poly_t src, unsigned long ofs) { - /* Adds src to dest (modulo 2) at offset ofs. - * When ofs == dest->length, catenates src on to dest. - * src and dest must be CLEAN. - */ - unsigned long fulllength, idx, iter, end; - - fulllength = ofs + src.length; - if(fulllength > dest->length) - praloc(dest, fulllength); - /* array index of first word in dest to be modified */ - idx = IDX(ofs); - /* index of bit in src to be added to LSB of dest->bitmap[idx] */ - iter = OFS(ofs); - /* stop value for iter */ - end = BMP_BIT - 1UL + src.length; - for(; iter < end; iter += BMP_BIT, ++idx) - dest->bitmap[idx] ^= getwrd(src, iter); -} - -void -prev(poly_t *poly) { - /* Reverse or reciprocate a polynomial. - * On exit, poly is CLEAN. - */ - unsigned long leftidx = 0UL, rightidx = SIZE(poly->length); - unsigned long ofs = LOFS(BMP_BIT - LOFS(poly->length)); - unsigned long fulllength = poly->length + ofs; - bmp_t accu; - - if(ofs) { - /* removable optimisation */ - if(poly->length < (unsigned long) BMP_BIT) { - *poly->bitmap = rev(*poly->bitmap >> ofs, (int) poly->length) << ofs; - return; - } - } - - /* claim remaining bits of last word (as we use public function pshift()) */ - poly->length = fulllength; - - /* reverse and swap words in the array, leaving it right-justified */ - while(leftidx < rightidx) { - /* rightidx > 0 */ - accu = rev(poly->bitmap[--rightidx], BMP_BIT); - poly->bitmap[rightidx] = rev(poly->bitmap[leftidx], BMP_BIT); - poly->bitmap[leftidx++] = accu; - } - /* shift polynomial to left edge if required */ - if(ofs) - pshift(poly, *poly, 0UL, ofs, fulllength, 0UL); -} - -void -prevch(poly_t *poly, int bperhx) { - /* Reverse each group of bperhx bits in a polynomial. - * Does not clean poly. - */ - unsigned long iter = 0, idx, ofs; - bmp_t mask, accu; - - if(bperhx < 2 || bperhx > BMP_BIT) - return; - if(poly->length % bperhx) - praloc(poly, bperhx - (poly->length % bperhx) + poly->length); - mask = ~BMP_C(0) >> (BMP_BIT - bperhx); - for(iter = (unsigned long) (bperhx - 1); iter < poly->length; iter += bperhx) { - accu = getwrd(*poly, iter) & mask; - accu ^= rev(accu, bperhx); - idx = IDX(iter); - ofs = OFS(iter); - poly->bitmap[idx] ^= accu << ofs; - if(ofs + bperhx > (unsigned int) BMP_BIT) - /* (BMP_BIT - 1UL - (iter) % BMP_BIT) + bperhx > BMP_BIT - * (-1UL - (iter) % BMP_BIT) + bperhx > 0 - * (- (iter % BMP_BIT)) + bperhx > 1 - * - (iter % BMP_BIT) > 1 - bperhx - * iter % BMP_BIT < bperhx - 1, iter >= bperhx - 1 - * iter >= BMP_BIT - * idx >= 1 - */ - poly->bitmap[idx-1] ^= accu >> (BMP_BIT - ofs); - } -} - -void -prcp(poly_t *poly) { - /* Reciprocate a chopped polynomial. Use prev() on whole - * polynomials. - * On exit, poly is SEMI-NORMALISED. - */ - unsigned long first; - - praloc(poly, RNDUP(poly->length)); - prev(poly); - first = pfirst(*poly); - if(first >= poly->length) { - pfree(poly); - return; - } - pshift(poly, *poly, 0UL, first + 1UL, poly->length, 1UL); - piter(poly); -} - -void -pinv(poly_t *poly) { - /* Invert a polynomial, i.e. add 1 (modulo 2) to the coefficient of each term - * on exit, poly is CLEAN. - */ - unsigned long idx, size = SIZE(poly->length); - - for(idx = 0UL; idxbitmap[idx] = ~poly->bitmap[idx]; - if(LOFS(poly->length)) - poly->bitmap[size - 1UL] &= ~(~BMP_C(0) >> LOFS(poly->length)); -} - -poly_t -pmod(const poly_t dividend, const poly_t divisor) { - /* Divide dividend by normalised divisor and return the remainder - * This function generates a temporary 'chopped' divisor for pcrc() - * If calling repeatedly with a constant divisor, produce a chopped copy - * with pchop() and call pcrc() directly for higher efficiency. - * dividend and divisor must be CLEAN. - */ - - /* perhaps generate an error if divisor is zero */ - poly_t subdivisor = psubs(divisor, 0UL, pfirst(divisor) + 1UL, plast(divisor), 0UL); - poly_t result = pcrc(dividend, subdivisor, pzero, pzero, 0); - pfree(&subdivisor); - return(result); -} - -poly_t -pcrc(const poly_t message, const poly_t divisor, const poly_t init, const poly_t xorout, int flags) { - /* Divide message by divisor and return the remainder. - * init is added to divisor, highest terms aligned, before - * division. - * xorout is added to the remainder, highest terms aligned. - * If P_MULXN is set in flags, message is multiplied by x^n - * (i.e. trailing zeroes equal to the CRC width are appended) - * before adding init and division. Set P_MULXN for most CRC - * calculations. - * All inputs must be CLEAN. - * If all inputs are CLEAN, the returned poly_t will be CLEAN. - */ - unsigned long max = 0UL, iter, ofs, resiter; - bmp_t probe, rem, dvsr, *rptr, *sptr; - const bmp_t *bptr, *eptr; - poly_t result = PZERO; - - if(flags & P_MULXN) - max = message.length; - else if(message.length > divisor.length) - max = message.length - divisor.length; - bptr=message.bitmap; - eptr=message.bitmap+SIZE(message.length); - probe=~(~BMP_C(0) >> 1); - if(divisor.length <= (unsigned long) BMP_BIT - && init.length <= (unsigned long) BMP_BIT) { - rem = init.length ? *init.bitmap : BMP_C(0); - dvsr = divisor.length ? *divisor.bitmap : BMP_C(0); - for(iter = 0UL, ofs = 0UL; iter < max; ++iter, --ofs) { - if(!ofs) { - ofs = BMP_BIT; - rem ^= *bptr++; - } - if(rem & probe) - rem = (rem << 1) ^ dvsr; - else - rem <<= 1; - } - if(bptr < eptr) - /* max < message.length */ - rem ^= *bptr >> OFS(BMP_BIT - 1UL + max); - if(init.length > max && init.length - max > divisor.length) { - palloc(&result, init.length - max); - *result.bitmap = rem; - } else if(divisor.length) { - palloc(&result, divisor.length); - *result.bitmap = rem; - } - } else { - /* allocate maximum size plus one word for shifted divisors and one word containing zero. - * This also ensures that result[1] exists - */ - palloc(&result, (init.length > divisor.length ? init.length : divisor.length) + (unsigned long) (BMP_BIT << 1)); - /*if there is content in init, there will be an extra word in result to clear it */ - psum(&result, init, 0UL); - if(max) - *result.bitmap ^= *bptr++; - for(iter = 0UL, ofs = 0UL; iter < max; ++iter, probe >>= 1) { - if(!probe) { - probe = ~(~BMP_C(0) >> 1); - ofs = 0UL; - sptr = rptr = result.bitmap; - ++sptr; - /* iter < max <= message.length, so bptr is valid - * shift result one word to the left, splicing in a message word - * and clearing the last active word - */ - *rptr++ = *sptr++ ^ *bptr++; - for(resiter = (unsigned long) (BMP_BIT << 1); resiter < result.length; resiter += BMP_BIT) - *rptr++ = *sptr++; - } - ++ofs; - if(*result.bitmap & probe) - psum(&result, divisor, ofs); - } - rptr = result.bitmap; - ++rptr; - while(bptr < eptr) - *rptr++ ^= *bptr++; - /* 0 <= ofs <= BMP_BIT, location of the first bit of the result */ - pshift(&result, result, 0UL, ofs, (init.length > max + divisor.length ? init.length - max - divisor.length : 0UL) + divisor.length + ofs, 0UL); - } - psum(&result, xorout, 0UL); - return(result); -} - -int -piter(poly_t *poly) { - /* Replace poly with the 'next' polynomial of equal length. - * Returns zero if the next polynomial is all zeroes, a nonzero - * value otherwise. - * Does not clean poly. - */ - bmp_t *bptr; - if(!poly->length) return(0); - - bptr = poly->bitmap + IDX(poly->length - 1UL); - *bptr += BMP_C(1) << OFS(poly->length - 1UL); - while(bptr != poly->bitmap && !*bptr) - ++(*--bptr); - return(*bptr != BMP_C(0)); -} - -void -palloc(poly_t *poly, unsigned long length) { - /* Replaces poly with a CLEAN object of the specified length, - * consisting of all zeroes. - * It is safe to call with length = 0, in which case the object - * is freed. - * poly may or may not be WELL-FORMED. - * On exit, poly is CLEAN. - */ - unsigned long size = SIZE(length); - - poly->length = 0UL; - free(poly->bitmap); - poly->bitmap = NULL; - if(!length) return; - if(!size) - size = IDX(length) + 1UL; - poly->bitmap = (bmp_t *) calloc(size, sizeof(bmp_t)); - if(poly->bitmap) { - poly->length = length; - } else - uerror("cannot allocate memory for poly"); -} - -void -pfree(poly_t *poly) { - /* Frees poly's bitmap storage and sets poly equal to the empty - * polynomial (PZERO). - * poly may or may not be WELL-FORMED. - * On exit, poly is CLEAN. - */ - - /* palloc(poly, 0UL); */ - - poly->length = 0UL; - free(poly->bitmap); - poly->bitmap = NULL; -} - -void -praloc(poly_t *poly, unsigned long length) { - /* Trims or extends poly to length at the right edge, appending - * zeroes if necessary. - * On entry, poly may or may not be WELL-FORMED. - * On exit, poly is CLEAN. - */ - unsigned long oldsize, size = SIZE(length); - if(!poly) return; - if(!length) { - poly->length = 0UL; - free(poly->bitmap); - poly->bitmap = NULL; - return; - } - if(!size) - size = IDX(length) + 1UL; - if(!poly->bitmap) - poly->length = 0UL; - oldsize = SIZE(poly->length); - if(oldsize != size) - /* reallocate if array pointer is null or array resized */ - poly->bitmap = (bmp_t *) realloc((void *)poly->bitmap, size * sizeof(bmp_t)); - if(poly->bitmap) { - if(poly->length < length) { - /* poly->length >= 0, length > 0, size > 0. - * poly expanded. clear old last word and all new words - */ - if(LOFS(poly->length)) - poly->bitmap[oldsize - 1UL] &= ~(~BMP_C(0) >> LOFS(poly->length)); - while(oldsize < size) - poly->bitmap[oldsize++] = BMP_C(0); - } else if(LOFS(length)) - /* poly->length >= length > 0. - * poly shrunk. clear new last word - */ - poly->bitmap[size - 1UL] &= ~(~BMP_C(0) >> LOFS(length)); - poly->length = length; - } else - uerror("cannot reallocate memory for poly"); -} - -int -pmpar(const poly_t poly, const poly_t mask) { - /* Return even parity of poly masked with mask. - * Poly and mask must be CLEAN. - */ - bmp_t res = BMP_C(0); - int i = BMP_SUB; - const bmp_t *pptr = poly.bitmap, *mptr = mask.bitmap; - const bmp_t *const pend = poly.bitmap + SIZE(poly.length); - const bmp_t *const mend = mask.bitmap + SIZE(mask.length); - - while(pptr < pend && mptr < mend) - res ^= *pptr++ & *mptr++; - do - res ^= res >> i; - while(i >>= 1); - - return((int) (res & BMP_C(1))); -} - -int -pident(const poly_t a, const poly_t b) { - /* Return nonzero if a and b have the same length - * and point to the same bitmap. - * a and b need not be CLEAN. - */ - return(a.length == b.length && a.bitmap == b.bitmap); -} - -/* Private functions */ - -static bmp_t -getwrd(const poly_t poly, unsigned long iter) { - /* Fetch unaligned word from poly where LSB of result is - * bit iter of the bitmap (counting from zero). If iter exceeds - * the length of poly then zeroes are appended as necessary. - * Factored from ptostr(). - * poly must be CLEAN. - */ - bmp_t accu = BMP_C(0); - unsigned long idx, size; - int ofs; - - idx = IDX(iter); - ofs = OFS(iter); - size = SIZE(poly.length); - - if(idx < size) - accu |= poly.bitmap[idx] >> ofs; - if(idx && idx <= size && ofs > 0) - accu |= poly.bitmap[idx - 1UL] << (BMP_BIT - ofs); - return(accu); -} - -static bmp_t -rev(bmp_t accu, int bits) { - /* Returns the bitmap word argument with the given number of - * least significant bits reversed and the rest cleared. - */ - static const unsigned char revtab[256] = { - 0x00,0x80,0x40,0xc0,0x20,0xa0,0x60,0xe0, - 0x10,0x90,0x50,0xd0,0x30,0xb0,0x70,0xf0, - 0x08,0x88,0x48,0xc8,0x28,0xa8,0x68,0xe8, - 0x18,0x98,0x58,0xd8,0x38,0xb8,0x78,0xf8, - 0x04,0x84,0x44,0xc4,0x24,0xa4,0x64,0xe4, - 0x14,0x94,0x54,0xd4,0x34,0xb4,0x74,0xf4, - 0x0c,0x8c,0x4c,0xcc,0x2c,0xac,0x6c,0xec, - 0x1c,0x9c,0x5c,0xdc,0x3c,0xbc,0x7c,0xfc, - 0x02,0x82,0x42,0xc2,0x22,0xa2,0x62,0xe2, - 0x12,0x92,0x52,0xd2,0x32,0xb2,0x72,0xf2, - 0x0a,0x8a,0x4a,0xca,0x2a,0xaa,0x6a,0xea, - 0x1a,0x9a,0x5a,0xda,0x3a,0xba,0x7a,0xfa, - 0x06,0x86,0x46,0xc6,0x26,0xa6,0x66,0xe6, - 0x16,0x96,0x56,0xd6,0x36,0xb6,0x76,0xf6, - 0x0e,0x8e,0x4e,0xce,0x2e,0xae,0x6e,0xee, - 0x1e,0x9e,0x5e,0xde,0x3e,0xbe,0x7e,0xfe, - 0x01,0x81,0x41,0xc1,0x21,0xa1,0x61,0xe1, - 0x11,0x91,0x51,0xd1,0x31,0xb1,0x71,0xf1, - 0x09,0x89,0x49,0xc9,0x29,0xa9,0x69,0xe9, - 0x19,0x99,0x59,0xd9,0x39,0xb9,0x79,0xf9, - 0x05,0x85,0x45,0xc5,0x25,0xa5,0x65,0xe5, - 0x15,0x95,0x55,0xd5,0x35,0xb5,0x75,0xf5, - 0x0d,0x8d,0x4d,0xcd,0x2d,0xad,0x6d,0xed, - 0x1d,0x9d,0x5d,0xdd,0x3d,0xbd,0x7d,0xfd, - 0x03,0x83,0x43,0xc3,0x23,0xa3,0x63,0xe3, - 0x13,0x93,0x53,0xd3,0x33,0xb3,0x73,0xf3, - 0x0b,0x8b,0x4b,0xcb,0x2b,0xab,0x6b,0xeb, - 0x1b,0x9b,0x5b,0xdb,0x3b,0xbb,0x7b,0xfb, - 0x07,0x87,0x47,0xc7,0x27,0xa7,0x67,0xe7, - 0x17,0x97,0x57,0xd7,0x37,0xb7,0x77,0xf7, - 0x0f,0x8f,0x4f,0xcf,0x2f,0xaf,0x6f,0xef, - 0x1f,0x9f,0x5f,0xdf,0x3f,0xbf,0x7f,0xff - }; - bmp_t result = BMP_C(0); - while(bits > 8) { - bits -= 8; - result = result << 8 | revtab[accu & 0xff]; - accu >>= 8; - } - result = result << bits | (bmp_t) (revtab[accu & 0xff] >> (8 - bits)); - return(result); -} - -static void -prhex(char **spp, bmp_t bits, int flags, int bperhx) { - /* Appends a hexadecimal string representing the bperhx least - * significant bits of bits to an external string. - * spp points to a character pointer that in turn points to the - * end of a hex string being built. prhex() advances this - * second pointer by the number of characters written. - * The unused MSBs of bits MUST be cleared. - * Set P_UPPER in flags to write A-F in uppercase. - */ - static const char hex[] = "0123456789abcdef0123456789ABCDEF"; - const int upper = (flags & P_UPPER ? 0x10 : 0); - while(bperhx > 0) { - bperhx -= ((bperhx + 3) & 3) + 1; - *(*spp)++ = hex[(bits >> bperhx & BMP_C(0xf)) | upper]; - } -} diff --git a/client/reveng/reveng.c b/client/reveng/reveng.c deleted file mode 100644 index dd50987c..00000000 --- a/client/reveng/reveng.c +++ /dev/null @@ -1,489 +0,0 @@ -/* reveng.c - * Greg Cook, 9/Apr/2015 - */ - -/* CRC RevEng, an arbitrary-precision CRC calculator and algorithm finder - * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015 Gregory Cook - * - * This file is part of CRC RevEng. - * - * CRC RevEng is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * CRC RevEng is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with CRC RevEng. If not, see . - */ - -/* 2013-09-16: calini(), calout() work on shortest argument - * 2013-06-11: added sequence number to uprog() calls - * 2013-02-08: added polynomial range search - * 2013-01-18: refactored model checking to pshres(); renamed chkres() - * 2012-05-24: efficiently build Init contribution string - * 2012-05-24: removed broken search for crossed-endian algorithms - * 2012-05-23: rewrote engini() after Ewing; removed modini() - * 2011-01-17: fixed ANSI C warnings - * 2011-01-08: fixed calini(), modini() caters for crossed-endian algos - * 2011-01-04: renamed functions, added calini(), factored pshres(); - * rewrote engini() and implemented quick Init search - * 2011-01-01: reveng() initialises terminating entry, addparms() - * initialises all fields - * 2010-12-26: renamed CRC RevEng. right results, rejects polys faster - * 2010-12-24: completed, first tests (unsuccessful) - * 2010-12-21: completed modulate(), partial sketch of reveng() - * 2010-12-19: started reveng - */ - -/* reveng() can in theory be modified to search for polynomials shorter - * than the full width as well, but this imposes a heavy time burden on - * the full width search, which is the primary use case, as well as - * complicating the search range function introduced in version 1.1.0. - * It is more effective to search for each shorter width directly. - */ - -#include - -#define FILE void -#include "reveng.h" - -static poly_t *modpol(const poly_t init, int rflags, int args, const poly_t *argpolys); -static void engini(int *resc, model_t **result, const poly_t divisor, int flags, int args, const poly_t *argpolys); -static void calout(int *resc, model_t **result, const poly_t divisor, const poly_t init, int flags, int args, const poly_t *argpolys); -static void calini(int *resc, model_t **result, const poly_t divisor, int flags, const poly_t xorout, int args, const poly_t *argpolys); -static void chkres(int *resc, model_t **result, const poly_t divisor, const poly_t init, int flags, const poly_t xorout, int args, const poly_t *argpolys); - -static const poly_t pzero = PZERO; - -model_t * -reveng(const model_t *guess, const poly_t qpoly, int rflags, int args, const poly_t *argpolys) { - /* Complete the parameters of a model by calculation or brute search. */ - poly_t *pworks, *wptr, rem, gpoly; - model_t *result = NULL, *rptr; - int resc = 0; - unsigned long spin = 0, seq = 0; - - if(~rflags & R_HAVEP) { - /* The poly is not known. - * Produce a list of differences between the arguments. - */ - pworks = modpol(guess->init, rflags, args, argpolys); - if(!pworks || !plen(*pworks)) { - free(pworks); - goto requit; - } - /* Initialise the guessed poly to the starting value. */ - gpoly = pclone(guess->spoly); - /* Clear the least significant term, to be set in the - * loop. qpoly does not need fixing as it is only - * compared with odd polys. - */ - if(plen(gpoly)) - pshift(&gpoly, gpoly, 0UL, 0UL, plen(gpoly) - 1UL, 1UL); - - while(piter(&gpoly) && (~rflags & R_HAVEQ || pcmp(&gpoly, &qpoly) < 0)) { - /* For each possible poly of this size, try - * dividing all the differences in the list. - */ - if(!(spin++ & R_SPMASK)) { - uprog(gpoly, guess->flags, seq++); - } - for(wptr = pworks; plen(*wptr); ++wptr) { - /* straight divide message by poly, don't multiply by x^n */ - rem = pcrc(*wptr, gpoly, pzero, pzero, 0); - if(ptst(rem)) { - pfree(&rem); - break; - } else - pfree(&rem); - } - /* If gpoly divides all the differences, it is a - * candidate. Search for an Init value for this - * poly or if Init is known, log the result. - */ - if(!plen(*wptr)) { - /* gpoly is a candidate poly */ - if(rflags & R_HAVEI && rflags & R_HAVEX) - chkres(&resc, &result, gpoly, guess->init, guess->flags, guess->xorout, args, argpolys); - else if(rflags & R_HAVEI) - calout(&resc, &result, gpoly, guess->init, guess->flags, args, argpolys); - else if(rflags & R_HAVEX) - calini(&resc, &result, gpoly, guess->flags, guess->xorout, args, argpolys); - else - engini(&resc, &result, gpoly, guess->flags, args, argpolys); - } - if(!piter(&gpoly)) - break; - } - /* Finished with gpoly and the differences list, free them. - */ - pfree(&gpoly); - for(wptr = pworks; plen(*wptr); ++wptr) - pfree(wptr); - free(pworks); - } - else if(rflags & R_HAVEI && rflags & R_HAVEX) - /* All parameters are known! Submit the result if we get here */ - chkres(&resc, &result, guess->spoly, guess->init, guess->flags, guess->xorout, args, argpolys); - else if(rflags & R_HAVEI) - /* Poly and Init are known, calculate XorOut */ - calout(&resc, &result, guess->spoly, guess->init, guess->flags, args, argpolys); - else if(rflags & R_HAVEX) - /* Poly and XorOut are known, calculate Init */ - calini(&resc, &result, guess->spoly, guess->flags, guess->xorout, args, argpolys); - else - /* Poly is known but not Init; search for Init. */ - engini(&resc, &result, guess->spoly, guess->flags, args, argpolys); - -requit: - if(!(result = realloc(result, ++resc * sizeof(model_t)))) - uerror("cannot reallocate result array"); - rptr = result + resc - 1; - rptr->spoly = pzero; - rptr->init = pzero; - rptr->flags = 0; - rptr->xorout = pzero; - rptr->check = pzero; - rptr->name = NULL; - - return(result); -} - -static poly_t * -modpol(const poly_t init, int rflags, int args, const poly_t *argpolys) { - /* Produce, in ascending length order, a list of differences - * between the arguments in the list by summing pairs of arguments. - * If R_HAVEI is not set in rflags, only pairs of equal length are - * summed. - * Otherwise, sums of right-aligned pairs are also returned, with - * the supplied init poly added to the leftmost terms of each - * poly of the pair. - */ - poly_t work, swap, *result, *rptr, *iptr; - const poly_t *aptr, *bptr, *eptr = argpolys + args; - unsigned long alen, blen; - - if(args < 2) return(NULL); - - if(!(result = malloc(((((args - 1) * args) >> 1) + 1) * sizeof(poly_t)))) - uerror("cannot allocate memory for codeword table"); - - rptr = result; - - for(aptr = argpolys; aptr < eptr; ++aptr) { - alen = plen(*aptr); - for(bptr = aptr + 1; bptr < eptr; ++bptr) { - blen = plen(*bptr); - if(alen == blen) { - work = pclone(*aptr); - psum(&work, *bptr, 0UL); - } else if(rflags & R_HAVEI && alen < blen) { - work = pclone(*bptr); - psum(&work, *aptr, blen - alen); - psum(&work, init, 0UL); - psum(&work, init, blen - alen); - } else if(rflags & R_HAVEI /* && alen > blen */) { - work = pclone(*aptr); - psum(&work, *bptr, alen - blen); - psum(&work, init, 0UL); - psum(&work, init, alen - blen); - } else - work = pzero; - - if(plen(work)) - pnorm(&work); - if((blen = plen(work))) { - /* insert work into result[] in ascending order of length */ - for(iptr = result; iptr < rptr; ++iptr) { - if(plen(work) < plen(*iptr)) { - swap = *iptr; - *iptr = work; - work = swap; - } - else if(plen(*iptr) == blen && !pcmp(&work, iptr)) { - pfree(&work); - work = *--rptr; - break; - } - } - *rptr++ = work; - } - } - } - *rptr = pzero; - return(result); -} - -static void -engini(int *resc, model_t **result, const poly_t divisor, int flags, int args, const poly_t *argpolys) { - /* Search for init values implied by the arguments. - * Method from: Ewing, Gregory C. (March 2010). - * "Reverse-Engineering a CRC Algorithm". Christchurch: - * University of Canterbury. - * - */ - poly_t apoly = PZERO, bpoly, pone = PZERO, *mat, *jptr; - const poly_t *aptr, *bptr, *iptr; - unsigned long alen, blen, dlen, ilen, i, j; - int cy; - - dlen = plen(divisor); - - /* Allocate the CRC matrix */ - if(!(mat = (poly_t *) malloc((dlen << 1) * sizeof(poly_t)))) - uerror("cannot allocate memory for CRC matrix"); - - /* Find arguments of the two shortest lengths */ - alen = blen = plen(*(aptr = bptr = iptr = argpolys)); - for(++iptr; iptr < argpolys + args; ++iptr) { - ilen = plen(*iptr); - if(ilen < alen) { - bptr = aptr; blen = alen; - aptr = iptr; alen = ilen; - } else if(ilen > alen && (aptr == bptr || ilen < blen)) { - bptr = iptr; blen = ilen; - } - } - if(aptr == bptr) { - /* if no arguments are suitable, calculate Init with an - * assumed XorOut of 0. Create a padded XorOut - */ - palloc(&apoly, dlen); - calini(resc, result, divisor, flags, apoly, args, argpolys); - pfree(&apoly); - free(mat); - return; - } - - /* Find the potential contribution of the bottom bit of Init */ - palloc(&pone, 1UL); - piter(&pone); - if(blen < (dlen << 1)) { - palloc(&apoly, dlen); /* >= 1 */ - psum(&apoly, pone, (dlen << 1) - 1UL - blen); /* >= 0 */ - psum(&apoly, pone, (dlen << 1) - 1UL - alen); /* >= 1 */ - } else { - palloc(&apoly, blen - dlen + 1UL); /* > dlen */ - psum(&apoly, pone, 0UL); - psum(&apoly, pone, blen - alen); /* >= 1 */ - } - if(plen(apoly) > dlen) { - mat[dlen] = pcrc(apoly, divisor, pzero, pzero, 0); - pfree(&apoly); - } else { - mat[dlen] = apoly; - } - - /* Find the actual contribution of Init */ - apoly = pcrc(*aptr, divisor, pzero, pzero, 0); - bpoly = pcrc(*bptr, divisor, pzero, apoly, 0); - - /* Populate the matrix */ - palloc(&apoly, 1UL); - for(jptr=mat; jptr j */ - j = pfirst(apoly); - } - if(j < dlen) - mat[j] = apoly; /* pident(mat[j], pzero) || pfirst(mat[j]) == j */ - else - pfree(&apoly); - } - palloc(&bpoly, dlen + 1UL); - psum(&bpoly, pone, dlen); - - /* Iterate through all solutions */ - do { - /* Solve the matrix by Gaussian elimination. - * The parity of the result, masked by each row, should be even. - */ - cy = 1; - apoly = pclone(bpoly); - jptr = mat + dlen; - for(i=0UL; ispoly = pclone(divisor); - rptr->init = pclone(init); - rptr->flags = flags; - rptr->xorout = pclone(xorout); - rptr->name = NULL; - - /* compute check value for this model */ - mcheck(rptr); - - /* callback to notify new model */ - ufound(rptr); -} diff --git a/client/reveng/reveng.h b/client/reveng/reveng.h deleted file mode 100644 index 48dcb31c..00000000 --- a/client/reveng/reveng.h +++ /dev/null @@ -1,214 +0,0 @@ -/* reveng.h - * Greg Cook, 9/Apr/2015 - */ - -/* CRC RevEng, an arbitrary-precision CRC calculator and algorithm finder - * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015 Gregory Cook - * - * This file is part of CRC RevEng. - * - * CRC RevEng is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * CRC RevEng is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with CRC RevEng. If not, see . - */ - -#ifndef REVENG_H -#define REVENG_H 1 - -/* Configuration options */ - -#include "config.h" - -#ifndef BMP_T -# error config.h: BMP_T must be defined as unsigned long or a longer unsigned type -#endif - -#ifndef BMP_C -# error config.h: BMP_C() must define a BMP_T constant -#endif - -#if !defined PRESETS && !defined BMPMACRO -# undef BMP_BIT -# undef BMP_SUB -#endif - -#undef BMP_POF2 - -#ifdef BMP_BIT -# ifndef BMP_SUB -# error config.h: BMP_SUB must be defined as the highest power of two that is strictly less than BMP_BIT -# elif BMP_BIT < 32 -# error config.h: BMP_BIT must be at least 32 -# elif BMP_SUB < 16 -# error config.h: BMP_SUB must be at least 16 -# elif (BMP_SUB >= BMP_BIT || BMP_SUB << 1 < BMP_BIT || BMP_SUB & (BMP_SUB - 1)) -# error config.h: BMP_SUB must be defined as the highest power of two that is strictly less than BMP_BIT -# else /* BMP_SUB */ -# define SETBMP() -# endif /* BMP_SUB */ -# if BMP_BIT == 32 -# define BMP_POF2 5 -# elif BMP_BIT == 64 -# define BMP_POF2 6 -# elif BMP_BIT == 128 -# define BMP_POF2 7 -# elif BMP_BIT == 256 -# define BMP_POF2 8 -# elif BMP_BIT == 512 -# define BMP_POF2 9 -# elif BMP_BIT == 1024 -# define BMP_POF2 10 -# elif BMP_BIT == 2048 -# define BMP_POF2 11 -# elif BMP_BIT == 4096 -# define BMP_POF2 12 -# elif BMP_BIT == 8192 -# define BMP_POF2 13 -# elif BMP_BIT == 16384 -# define BMP_POF2 14 -# elif BMP_BIT == 32768 -# define BMP_POF2 15 -# elif BMP_BIT == 65536 -# define BMP_POF2 16 -/* may extend list as required */ -# elif (BMP_BIT & (BMP_BIT - 1)) == 0 -# define BMP_POF2 1 -# endif -#else /* BMP_BIT */ -# define BMP_BIT bmpbit -# define BMP_SUB bmpsub -# define SETBMP() setbmp() -#endif /* BMP_BIT */ - -/* Global definitions */ - -/* CRC RevEng version string */ -#define VERSION "1.3.0" - -/* bmpbit.c */ -typedef BMP_T bmp_t; - -extern int bmpbit, bmpsub; -extern void setbmp(void); - -/* poly.c */ -#define P_REFIN 1 -#define P_REFOUT 2 -#define P_MULXN 4 -#define P_RTJUST 8 -#define P_UPPER 16 -#define P_SPACE 32 -#define P_LTLBYT 64 -#define P_DIRECT 128 - -/* default flags */ -#define P_BE (P_RTJUST | P_MULXN) -#define P_LE (P_REFIN | P_REFOUT | P_MULXN) -#define P_BELE (P_REFOUT | P_MULXN) -#define P_LEBE (P_REFIN | P_RTJUST | P_MULXN) - -/* A poly_t constant representing the polynomial 0. */ -#define PZERO {0UL, (bmp_t *) 0} - -typedef struct { - unsigned long length; /* number of significant bits */ - bmp_t *bitmap; /* bitmap, MSB first, */ - /* left-justified in each word */ -} poly_t; - -extern poly_t filtop(FILE *input, unsigned long length, int flags, int bperhx); -extern poly_t strtop(const char *string, int flags, int bperhx); -extern char *ptostr(const poly_t poly, int flags, int bperhx); -extern char *pxsubs(const poly_t poly, int flags, int bperhx, unsigned long start, unsigned long end); -extern poly_t pclone(const poly_t poly); -extern void pcpy(poly_t *dest, const poly_t src); -extern void pcanon(poly_t *poly); -extern void pnorm(poly_t *poly); -extern void psnorm(poly_t *poly); -extern void pchop(poly_t *poly); -extern void pkchop(poly_t *poly); -extern unsigned long plen(const poly_t poly); -extern int pcmp(const poly_t *a, const poly_t *b); -extern int psncmp(const poly_t *a, const poly_t *b); -extern int ptst(const poly_t poly); -extern unsigned long pfirst(const poly_t poly); -extern unsigned long plast(const poly_t poly); -extern poly_t psubs(const poly_t src, unsigned long head, unsigned long start, unsigned long end, unsigned long tail); -extern void pright(poly_t *poly, unsigned long length); -extern void pshift(poly_t *dest, const poly_t src, unsigned long head, unsigned long start, unsigned long end, unsigned long tail); -extern void ppaste(poly_t *dest, const poly_t src, unsigned long skip, unsigned long seek, unsigned long end, unsigned long fulllength); -extern void pdiff(poly_t *dest, const poly_t src, unsigned long ofs); -extern void psum(poly_t *dest, const poly_t src, unsigned long ofs); -extern void prev(poly_t *poly); -extern void prevch(poly_t *poly, int bperhx); -extern void prcp(poly_t *poly); -extern void pinv(poly_t *poly); -extern poly_t pmod(const poly_t dividend, const poly_t divisor); -extern poly_t pcrc(const poly_t message, const poly_t divisor, const poly_t init, const poly_t xorout, int flags); -extern int piter(poly_t *poly); -extern void palloc(poly_t *poly, unsigned long length); -extern void pfree(poly_t *poly); -extern void praloc(poly_t *poly, unsigned long length); -extern int pmpar(const poly_t poly, const poly_t mask); -extern int pident(const poly_t a, const poly_t b); - -/* model.c */ -#define M_OVERWR 256 - -typedef struct { - poly_t spoly; /* polynomial with highest-order term removed. length determines CRC width */ - poly_t init; /* initial register value. length == poly.length */ - int flags; /* P_REFIN and P_REFOUT indicate reflected input/output */ - poly_t xorout; /* final register XOR mask. length == poly.length */ - poly_t check; /* optional check value, the CRC of the UTF-8 string "123456789" */ - const char *name; /* optional canonical name of the model */ -} model_t; - -extern void mcpy(model_t *dest, const model_t *src); -extern void mfree(model_t *model); -extern int mcmp(const model_t *a, const model_t *b); -extern int mbynam(model_t *dest, const char *key); -extern void mbynum(model_t *dest, int num); -extern int mcount(void); -extern char *mnames(void); -extern char *mtostr(const model_t *model); -extern void mmatch(model_t *model, int flags); -extern void mcanon(model_t *model); -extern void mcheck(model_t *model); -extern void mrev(model_t *model); -extern void mnovel(model_t *model); - -/* reveng.c */ -#define R_HAVEP 512 -#define R_HAVEI 1024 -#define R_HAVERI 2048 -#define R_HAVERO 4096 -#define R_HAVEX 8192 -#define R_HAVEQ 16384 - -#define R_SPMASK 0x7FFFFFFUL - -extern model_t *reveng(const model_t *guess, const poly_t qpoly, int rflags, int args, const poly_t *argpolys); - -/* cli.c */ -#define C_INFILE 1 -#define C_FORCE 2 -#define C_RESULT 4 - -#define BUFFER 32768 - -extern int reveng_main(int argc, char *argv[]); -extern void ufound(const model_t *model); -extern void uerror(const char *msg); -extern void uprog(const poly_t gpoly, int flags, unsigned long seq); - -#endif /* REVENG_H */ diff --git a/client/scripting.c b/client/scripting.c index 3859fd48..0c761cb2 100644 --- a/client/scripting.c +++ b/client/scripting.c @@ -25,7 +25,7 @@ #include "../common/crc64.h" #include "../common/polarssl/sha1.h" #include "../common/polarssl/aes.h" -#include "cmdcrc.h" + /** * The following params expected: * UsbCommand c @@ -391,62 +391,6 @@ static int l_sha1(lua_State *L) return 1; } -static int l_reveng_models(lua_State *L){ - - char *models[80]; - int count = 0; - int in_width = luaL_checkinteger(L, 1); - - if( in_width > 89 ) return returnToLuaWithError(L,"Width cannot exceed 89, got %d", in_width); - - uint8_t width[80]; - width[0] = (uint8_t)in_width; - int ans = GetModels(models, &count, width); - if (!ans) return 0; - - lua_newtable(L); - - for (int i = 0; i < count; i++){ - lua_pushstring(L, (const char*)models[i]); - lua_rawseti(L,-2,i+1); - free(models[i]); - } - - return 1; -} - -//Called with 4 parameters. -// inModel ,string containing the crc model name: 'CRC-8' -// inHexStr ,string containing the hex representation of the data that will be used for CRC calculations. -// reverse ,int 0/1 (bool) if 1, calculate the reverse CRC -// endian ,char, 'B','b','L','l','t','r' describing if Big-Endian or Little-Endian should be used in different combinations. -// -// outputs: string with hex representation of the CRC result -static int l_reveng_RunModel(lua_State *L){ - //-c || -v - //inModel = valid model name string - CRC-8 - //inHexStr = input hex string to calculate crc on - //reverse = reverse calc option if true - //endian = {0 = calc default endian input and output, b = big endian input and output, B = big endian output, r = right justified - // l = little endian input and output, L = little endian output only, t = left justified} - //result = calculated crc hex string - char result[50]; - - const char *inModel = luaL_checkstring(L, 1); - const char *inHexStr = luaL_checkstring(L, 2); - bool reverse = lua_toboolean(L, 3); - const char endian = luaL_checkstring(L, 4)[0]; - - //PrintAndLog("mod: %s, hex: %s, rev %d", inModel, inHexStr, reverse); - //int RunModel(char *inModel, char *inHexStr, bool reverse, char endian, char *result) - int ans = RunModel( (char *)inModel, (char *)inHexStr, reverse, endian, result); - if (!ans) - return returnToLuaWithError(L,"Reveng failed"); - - lua_pushstring(L, (const char*)result); - return 1; -} - /** * @brief Sets the lua path to include "./lualibs/?.lua", in order for a script to be * able to do "require('foobar')" if foobar.lua is within lualibs folder. @@ -493,8 +437,6 @@ int set_pm3_libraries(lua_State *L) {"crc16", l_crc16}, {"crc64", l_crc64}, {"sha1", l_sha1}, - {"reveng_models", l_reveng_models}, - {"reveng_runmodel", l_reveng_RunModel}, {NULL, NULL} }; diff --git a/client/scripts/e.lua b/client/scripts/e.lua deleted file mode 100644 index a20b8e47..00000000 --- a/client/scripts/e.lua +++ /dev/null @@ -1,72 +0,0 @@ -local getopt = require('getopt') -local utils = require('utils') - -example = "script calculates many different checksums (CRC) over the provided hex input" -author = "Iceman" -desc = -[[ -This script calculates many checksums (CRC) over the provided hex input. - -Arguments: - -b data in hex - -w bitwidth of the CRC family of algorithm. defaults to all known CRC presets. -Examples : - script run e -b 010203040506070809 - script run e -b 010203040506070809 -w 16 -]] - ---- --- A debug printout-function -function dbg(args) - if DEBUG then - print("###", args) - end -end ---- --- This is only meant to be used when errors occur -function oops(err) - print("ERROR: ",err) - return nil,err -end ---- --- Usage help -function help() - print(desc) - print("Example usage") - print(example) -end ---- --- The main entry point -function main(args) - - local data - local width = 0 - - -- Read the parameters - for o, a in getopt.getopt(args, 'hb:w:') do - if o == "h" then return help() end - if o == "b" then data = a end - if o == "w" then width = a end - end - - data = data or '01020304' - - print( string.rep('-',60) ) - print('Bit width of CRC | '..width) - print('Bytes | '..data) - print('') - print( ('%-20s| %-16s| %s'):format('Model','CRC', 'CRC reverse')) - print( string.rep('-',60) ) - local lists = core.reveng_models(width) - for _,i in pairs(lists) do - local a1 = core.reveng_runmodel(i, data, false, '0') - local a2 = core.reveng_runmodel(i, data, true, '0') - local a3 = core.reveng_runmodel(i, data, false, 'b') - local a4 = core.reveng_runmodel(i, data, false, 'B') - local a5 = core.reveng_runmodel(i, data, false, 'l') - local a6 = core.reveng_runmodel(i, data, false, 'L') - print( ('%-20s| %-16s| %-16s| %-16s| %-16s| %-16s| %-16s'):format(i, a1:upper(), a2:upper(),a3:upper(),a4:upper(),a5:upper(),a6:upper() ) ) - end -end - -main(args) \ No newline at end of file From e98389b355020a7baa9a82aef200a2a25cc78f47 Mon Sep 17 00:00:00 2001 From: Arnie97 Date: Thu, 25 Jan 2018 13:43:01 +0800 Subject: [PATCH 157/310] Fix typo in lua scripts: 1443a -> 14443a. --- client/lualibs/read14a.lua | 40 +++++++++++++++---------------- client/scripts/14araw.lua | 2 +- client/scripts/didump.lua | 2 +- client/scripts/formatMifare.lua | 2 +- client/scripts/mfkeys.lua | 2 +- client/scripts/mifare_autopwn.lua | 2 +- client/scripts/tnp3clone.lua | 2 +- client/scripts/tnp3dump.lua | 2 +- 8 files changed, 27 insertions(+), 27 deletions(-) diff --git a/client/lualibs/read14a.lua b/client/lualibs/read14a.lua index ec227b17..60fc0e68 100644 --- a/client/lualibs/read14a.lua +++ b/client/lualibs/read14a.lua @@ -2,7 +2,7 @@ This is a library to read 14443a tags. It can be used something like this local reader = require('read14a') - result, err = reader.read1443a() + result, err = reader.read14443a() if not result then print(err) return @@ -26,7 +26,7 @@ local ISO14A_COMMAND = { ISO14A_NO_RATS = 0x200 } -local ISO14443a_TYPES = {} +local ISO14443a_TYPES = {} ISO14443a_TYPES[0x00] = "NXP MIFARE Ultralight | Ultralight C" ISO14443a_TYPES[0x01] = "NXP MIFARE TNP3xxx Activision Game Appliance" ISO14443a_TYPES[0x04] = "NXP MIFARE (various !DESFire !DESFire EV1)" @@ -43,14 +43,14 @@ ISO14443a_TYPES[0x88] = "Infineon MIFARE CLASSIC 1K" ISO14443a_TYPES[0x98] = "Gemplus MPCOS" -local function tostring_1443a(sak) +local function tostring_14443a(sak) return ISO14443a_TYPES[sak] or ("Unknown (SAK=%x)"):format(sak) end -local function parse1443a(data) +local function parse14443a(data) --[[ - Based on this struct : + Based on this struct : typedef struct { byte_t uid[10]; @@ -66,14 +66,14 @@ local function parse1443a(data) local count,uid,uidlen, atqa, sak, ats_len, ats= bin.unpack('H10CH2CC',data) uid = uid:sub(1,2*uidlen) --print("uid, atqa, sak: ",uid, atqa, sak) - --print("TYPE: ", tostring_1443a(sak)) - return { uid = uid, atqa = atqa, sak = sak, name = tostring_1443a(sak)} + --print("TYPE: ", tostring_14443a(sak)) + return { uid = uid, atqa = atqa, sak = sak, name = tostring_14443a(sak)} end --- Sends a USBpacket to the device -- @param command - the usb packet to send --- @param ignoreresponse - if set to true, we don't read the device answer packet --- which is usually recipe for fail. If not sent, the host will wait 2s for a +-- @param ignoreresponse - if set to true, we don't read the device answer packet +-- which is usually recipe for fail. If not sent, the host will wait 2s for a -- response of type CMD_ACK -- @return packet,nil if successfull -- nil, errormessage if unsuccessfull @@ -99,7 +99,7 @@ end local function read14443a(dont_disconnect, no_rats) local command, result, info, err, data - command = Command:new{cmd = cmds.CMD_READER_ISO_14443a, + command = Command:new{cmd = cmds.CMD_READER_ISO_14443a, arg1 = ISO14A_COMMAND.ISO14A_CONNECT} if dont_disconnect then command.arg1 = command.arg1 + ISO14A_COMMAND.ISO14A_NO_DISCONNECT @@ -110,24 +110,24 @@ local function read14443a(dont_disconnect, no_rats) local result,err = sendToDevice(command) if result then local count,cmd,arg0,arg1,arg2 = bin.unpack('LLLL',result) - if arg0 == 0 then + if arg0 == 0 then return nil, "iso14443a card select failed" end data = string.sub(result,count) - info, err = parse1443a(data) + info, err = parse14443a(data) else err ="No response from card" end - if err then - print(err) + if err then + print(err) return nil, err end return info end --- --- Waits for a mifare card to be placed within the vicinity of the reader. +-- Waits for a mifare card to be placed within the vicinity of the reader. -- @return if successfull: an table containing card info -- @return if unsuccessfull : nil, error local function waitFor14443a() @@ -139,14 +139,14 @@ local function waitFor14443a() end return nil, "Aborted by user" end + local library = { - - read1443a = read14443a, - read = read14443a, + read14443a = read14443a, + read = read14443a, waitFor14443a = waitFor14443a, - parse1443a = parse1443a, + parse14443a = parse14443a, sendToDevice = sendToDevice, ISO14A_COMMAND = ISO14A_COMMAND, } -return library \ No newline at end of file +return library diff --git a/client/scripts/14araw.lua b/client/scripts/14araw.lua index 94b3020c..e2530929 100644 --- a/client/scripts/14araw.lua +++ b/client/scripts/14araw.lua @@ -106,7 +106,7 @@ function main(args) dbg("doconnect") -- We reuse the connect functionality from a -- common library - info, err = lib14a.read1443a(true, no_rats) + info, err = lib14a.read14443a(true, no_rats) if err then return oops(err) end print(("Connected to card, uid = %s"):format(info.uid)) diff --git a/client/scripts/didump.lua b/client/scripts/didump.lua index 124c3dc3..71bfd99c 100644 --- a/client/scripts/didump.lua +++ b/client/scripts/didump.lua @@ -406,7 +406,7 @@ function main(args) -- GET TAG UID - result, err = lib14a.read1443a(false, true) + result, err = lib14a.read14443a(false, true) if not result then return oops(err) end diff --git a/client/scripts/formatMifare.lua b/client/scripts/formatMifare.lua index 56397acd..2861b542 100644 --- a/client/scripts/formatMifare.lua +++ b/client/scripts/formatMifare.lua @@ -71,7 +71,7 @@ end -- -- Read information from a card function GetCardInfo() - result, err = lib14a.read1443a(false, true) + result, err = lib14a.read14443a(false, true) if not result then print(err) return diff --git a/client/scripts/mfkeys.lua b/client/scripts/mfkeys.lua index 8e54f908..90aeafc2 100644 --- a/client/scripts/mfkeys.lua +++ b/client/scripts/mfkeys.lua @@ -170,7 +170,7 @@ local function main( args) print(desc); - result, err = reader.read1443a(false, true) + result, err = reader.read14443a(false, true) if not result then print(err) return diff --git a/client/scripts/mifare_autopwn.lua b/client/scripts/mifare_autopwn.lua index bede483b..d743f21e 100644 --- a/client/scripts/mifare_autopwn.lua +++ b/client/scripts/mifare_autopwn.lua @@ -56,7 +56,7 @@ end -- @return if unsuccessfull : nil, error function wait_for_mifare() while not core.ukbhit() do - res, err = reader.read1443a(false, true) + res, err = reader.read14443a(false, true) if res then return res end -- err means that there was no response from card end diff --git a/client/scripts/tnp3clone.lua b/client/scripts/tnp3clone.lua index 2a8d2763..9441559b 100644 --- a/client/scripts/tnp3clone.lua +++ b/client/scripts/tnp3clone.lua @@ -122,7 +122,7 @@ local function main(args) -- find tag - result, err = lib14a.read1443a(false, true) + result, err = lib14a.read14443a(false, true) if not result then return oops(err) end -- load keys diff --git a/client/scripts/tnp3dump.lua b/client/scripts/tnp3dump.lua index aca7d046..3955137e 100644 --- a/client/scripts/tnp3dump.lua +++ b/client/scripts/tnp3dump.lua @@ -127,7 +127,7 @@ local function main(args) local cmdSetDbgOff = "hf mf dbg 0" core.console( cmdSetDbgOff) - result, err = lib14a.read1443a(false, true) + result, err = lib14a.read14443a(false, true) if not result then return oops(err) end From 4e002980af3da9a88518082d36135ca0d65b6e9c Mon Sep 17 00:00:00 2001 From: Arnie97 Date: Thu, 25 Jan 2018 13:49:40 +0800 Subject: [PATCH 158/310] hf mf ecfill: Add the missing line break. --- client/cmdhfmf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index b653cf30..d0320148 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -1907,7 +1907,7 @@ int CmdHF14AMfECFill(const char *Cmd) default: numSectors = 16; } - printf("--params: numSectors: %d, keyType:%d", numSectors, keyType); + printf("--params: numSectors: %d, keyType:%d\n", numSectors, keyType); UsbCommand c = {CMD_MIFARE_EML_CARDLOAD, {numSectors, keyType, 0}}; SendCommand(&c); return 0; From 392301aaabe4622b3fece54c9cf8d07aa26f9a60 Mon Sep 17 00:00:00 2001 From: Arnie97 Date: Thu, 25 Jan 2018 14:10:41 +0800 Subject: [PATCH 159/310] mfkeys.lua: Remove the user input helpers (now in lualibs/utils.lua) --- client/scripts/mfkeys.lua | 42 ++++----------------------------------- 1 file changed, 4 insertions(+), 38 deletions(-) diff --git a/client/scripts/mfkeys.lua b/client/scripts/mfkeys.lua index 90aeafc2..ee1ee2d7 100644 --- a/client/scripts/mfkeys.lua +++ b/client/scripts/mfkeys.lua @@ -14,49 +14,15 @@ local cmds = require('commands') local keys = require('mf_default_keys') -- Ability to read what card is there local reader = require('read14a') +-- Asks the user for input +local utils = require('utils') -local desc = -("This script implements check keys. It utilises a large list of default keys (currently %d keys).\ +local desc = ("This script implements check keys. \ +It utilises a large list of default keys (currently %d keys).\ If you want to add more, just put them inside mf_default_keys.lua. "):format(#keys) local TIMEOUT = 10000 -- 10 seconds - ---[[This may be moved to a separate library at some point]] -local utils = -{ - --- - -- Asks the user for Yes or No - confirm = function(message, ...) - local answer - message = message .. " [y]/[n] ?" - repeat - io.write(message) - io.flush() - answer=io.read() - if answer == 'Y' or answer == "y" then - return true - elseif answer == 'N' or answer == 'n' then - return false - end - until false - end, - --- - -- Asks the user for input - input = function (message , default) - local answer - if default ~= nil then - message = message .. " (default: ".. default.. " )" - end - message = message .." \n > " - io.write(message) - io.flush() - answer=io.read() - if answer == '' then answer = default end - - return answer - end, -} local function checkCommand(command) From f88fa399bc3855f38676e68ba228e31302f7ce6a Mon Sep 17 00:00:00 2001 From: Arnie97 Date: Thu, 25 Jan 2018 14:48:12 +0800 Subject: [PATCH 160/310] mfkeys.lua: Align empty cells in the table. --- client/scripts/mfkeys.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/scripts/mfkeys.lua b/client/scripts/mfkeys.lua index ee1ee2d7..919dadb8 100644 --- a/client/scripts/mfkeys.lua +++ b/client/scripts/mfkeys.lua @@ -85,7 +85,7 @@ local function displayresults(results) for sector,_ in pairs(results) do blockNo, keyA, keyB = unpack(_) - print(("| %3d | %3d |%s|%s|"):format(sector, blockNo, keyA, keyB )) + print(("| %3d | %3d |%12s|%12s|"):format(sector, blockNo, keyA, keyB)) end print("|--------------------------------------|") From b5cf8b07e22e96309a066149344673dfc19d1960 Mon Sep 17 00:00:00 2001 From: Arnie97 Date: Thu, 25 Jan 2018 14:48:26 +0800 Subject: [PATCH 161/310] mfkeys.lua: Remove trailing whitespaces. --- client/scripts/mfkeys.lua | 59 ++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/client/scripts/mfkeys.lua b/client/scripts/mfkeys.lua index 919dadb8..33027d31 100644 --- a/client/scripts/mfkeys.lua +++ b/client/scripts/mfkeys.lua @@ -1,11 +1,11 @@ --[[ This is an example of Lua-scripting within proxmark3. This is a lua-side - implementation of hf mf chk + implementation of hf mf chk 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. - + Copyright (C) 2013 m h swende --]] -- Loads the commands-library @@ -49,7 +49,7 @@ end function checkBlock(blockNo, keys, keyType) - -- The command data is only 512 bytes, each key is 6 bytes, meaning that we can send max 85 keys in one go. + -- The command data is only 512 bytes, each key is 6 bytes, meaning that we can send max 85 keys in one go. -- If there's more, we need to split it up local start, remaining= 1, #keys local arg1 = bit32.bor(bit32.lshift(keyType, 8), blockNo) @@ -61,10 +61,10 @@ function checkBlock(blockNo, keys, keyType) --print("data",data) --print("data len", #data) print(("Testing block %d, keytype %d, with %d keys"):format(blockNo, keyType, n)) - local command = Command:new{cmd = cmds.CMD_MIFARE_CHKKEYS, - arg1 = arg1, - arg2 = 1, - arg3 = n, + local command = Command:new{cmd = cmds.CMD_MIFARE_CHKKEYS, + arg1 = arg1, + arg2 = 1, + arg3 = n, data = data} local status = checkCommand(command) if status then return status, blockNo end @@ -90,36 +90,38 @@ local function displayresults(results) print("|--------------------------------------|") end + -- A little helper to place an item first in the list local function placeFirst(akey, list) - akey = akey:lower() - if list[1] == akey then + akey = akey:lower() + if list[1] == akey then -- Already at pole position return list end local result = {akey} --print(("Putting '%s' first"):format(akey)) for i,v in ipairs(list) do - if v ~= akey then + if v ~= akey then result[#result+1] = v end end return result end + local function dumptofile(results) local sector, blockNo, keyA, keyB,_ - if utils.confirm("Do you wish to save the keys to dumpfile?") then + if utils.confirm("Do you wish to save the keys to dumpfile?") then local destination = utils.input("Select a filename to store to", "dumpkeys.bin") local file = io.open(destination, "w") - if file == nil then + if file == nil then print("Could not write to file ", destination) return end local key_a = "" local key_b = "" - + for sector,_ in pairs(results) do blockNo, keyA, keyB = unpack(_) key_a = key_a .. bin.pack("H",keyA); @@ -132,7 +134,7 @@ local function dumptofile(results) end -local function main( args) +local function main(args) print(desc); @@ -147,11 +149,11 @@ local function main( args) core.clearCommandBuffer() local blockNo local keyType = 0 -- A=0, B=1 - local numSectors = 16 + local numSectors = 16 - if 0x18 == result.sak then --NXP MIFARE Classic 4k | Plus 4k - -- IFARE Classic 4K offers 4096 bytes split into forty sectors, - -- of which 32 are same size as in the 1K with eight more that are quadruple size sectors. + if 0x18 == result.sak then -- NXP MIFARE Classic 4k | Plus 4k + -- IFARE Classic 4K offers 4096 bytes split into forty sectors, + -- of which 32 are same size as in the 1K with eight more that are quadruple size sectors. numSectors = 40 elseif 0x08 == result.sak then -- NXP MIFARE CLASSIC 1k | Plus 2k -- 1K offers 1024 bytes of data storage, split into 16 sector @@ -159,7 +161,7 @@ local function main( args) elseif 0x09 == result.sak then -- NXP MIFARE Mini 0.3k -- MIFARE Classic mini offers 320 bytes split into five sectors. numSectors = 5 - elseif 0x10 == result.sak then-- "NXP MIFARE Plus 2k" + elseif 0x10 == result.sak then -- NXP MIFARE Plus 2k numSectors = 32 else print("I don't know how many sectors there are on this type of card, defaulting to 16") @@ -169,25 +171,25 @@ local function main( args) for sector=1,numSectors,1 do --[[ - The mifare Classic 1k card has 16 sectors of 4 data blocks each. + The mifare Classic 1k card has 16 sectors of 4 data blocks each. The first 32 sectors of a mifare Classic 4k card consists of 4 data blocks and the remaining - 8 sectors consist of 16 data blocks. + 8 sectors consist of 16 data blocks. --]] - local blockNo = sector * 4 -1 - + local blockNo = sector * 4 - 1 + if sector > 32 then - blockNo = 32*4+ (sector-32)*16 -1 + blockNo = 32*4 + (sector-32)*16 - 1 end local keyA = checkBlock(blockNo, keys, 0) - if keyA then keys = placeFirst(keyA, keys) end + if keyA then keys = placeFirst(keyA, keys) end keyA = keyA or "" local keyB = checkBlock(blockNo, keys, 1) - if keyB then keys = placeFirst(keyB, keys) end + if keyB then keys = placeFirst(keyB, keys) end keyB = keyB or "" - result[sector] = {blockNo, keyA, keyB } + result[sector] = {blockNo, keyA, keyB} -- Check if user aborted if core.ukbhit() then @@ -199,5 +201,4 @@ local function main( args) dumptofile(result) end -main( args) - +main(args) From 36545f0a5617d71b06e6d5773802fcf33ed5525a Mon Sep 17 00:00:00 2001 From: Arnie97 Date: Sat, 27 Jan 2018 01:09:56 +0800 Subject: [PATCH 162/310] hf mf eset: reuse mfEmlSetMem(). --- client/cmdhfmf.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index d0320148..ddfb1c02 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -1685,10 +1685,7 @@ int CmdHF14AMfESet(const char *Cmd) } // 1 - blocks count - UsbCommand c = {CMD_MIFARE_EML_MEMSET, {blockNo, 1, 0}}; - memcpy(c.d.asBytes, memBlock, 16); - SendCommand(&c); - return 0; + return mfEmlSetMem(memBlock, blockNo, 1); } From 7b7416bd3b7e0b9a3003ec33da943bd26b505d82 Mon Sep 17 00:00:00 2001 From: Arnie97 Date: Sat, 27 Jan 2018 01:55:38 +0800 Subject: [PATCH 163/310] hf mf nested: transfer keys to the correct block in one sector mode. --- client/cmdhfmf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index ddfb1c02..dbb6d092 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -683,9 +683,9 @@ int CmdHF14AMfNested(const char *Cmd) if (transferToEml) { uint8_t sectortrailer; if (trgBlockNo < 32*4) { // 4 block sector - sectortrailer = (trgBlockNo & 0x03) + 3; + sectortrailer = (trgBlockNo & ~0x03) + 3; } else { // 16 block sector - sectortrailer = (trgBlockNo & 0x0f) + 15; + sectortrailer = (trgBlockNo & ~0x0f) + 15; } mfEmlGetMem(keyBlock, sectortrailer, 1); From 298e1a2d0da6ad10a5db57f1529496306e47e009 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Wed, 31 Jan 2018 08:14:56 +0100 Subject: [PATCH 164/310] update license conditions in armsrc/optimized_cipher.[ch] (#554) --- armsrc/optimized_cipher.c | 2 +- armsrc/optimized_cipher.h | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/armsrc/optimized_cipher.c b/armsrc/optimized_cipher.c index 005f473b..b1f33737 100644 --- a/armsrc/optimized_cipher.c +++ b/armsrc/optimized_cipher.c @@ -22,7 +22,7 @@ * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. + * by the Free Software Foundation, or, at your option, any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/armsrc/optimized_cipher.h b/armsrc/optimized_cipher.h index c10aea28..6a4e2641 100644 --- a/armsrc/optimized_cipher.h +++ b/armsrc/optimized_cipher.h @@ -1,4 +1,41 @@ -#ifndef OPTIMIZED_CIPHER_H +/***************************************************************************** + * WARNING + * + * THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. + * + * USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL + * PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, + * AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. + * + * THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. + * + ***************************************************************************** + * + * This file is part of loclass. It is a reconstructon of the cipher engine + * used in iClass, and RFID techology. + * + * The implementation is based on the work performed by + * Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and + * Milosch Meriac in the paper "Dismantling IClass". + * + * Copyright (C) 2014 Martin Holst Swende + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, or, at your option, any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with loclass. If not, see . + * + * + ****************************************************************************/ + + #ifndef OPTIMIZED_CIPHER_H #define OPTIMIZED_CIPHER_H #include From f921c113c91ed6940f030db837e83afe4dab9ca8 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Wed, 31 Jan 2018 18:31:51 +0100 Subject: [PATCH 165/310] add force_align_arg_pointer attribute to all callback functions * inspired by issue #404 and respective fix PR #538 * possible fix for mysterious crashes, e.g. issue #497 and http://www.proxmark.org/forum/viewtopic.php?id=5388 --- client/cmdhfmfhard.c | 16 ++++++++++++++-- client/mifarehost.c | 9 ++++++++- client/proxmark3.c | 17 +++++++++++++++-- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/client/cmdhfmfhard.c b/client/cmdhfmfhard.c index 69ad898d..cf19436c 100644 --- a/client/cmdhfmfhard.c +++ b/client/cmdhfmfhard.c @@ -1158,7 +1158,13 @@ static bool timeout(void) } -static void *check_for_BitFlipProperties_thread(void *args) +static void +#ifdef __has_attribute +#if __has_attribute(force_align_arg_pointer) +__attribute__((force_align_arg_pointer)) +#endif +#endif +*check_for_BitFlipProperties_thread(void *args) { uint8_t first_byte = ((uint8_t *)args)[0]; uint8_t last_byte = ((uint8_t *)args)[1]; @@ -1906,7 +1912,13 @@ static void init_book_of_work(void) } -static void *generate_candidates_worker_thread(void *args) +static void +#ifdef __has_attribute +#if __has_attribute(force_align_arg_pointer) +__attribute__((force_align_arg_pointer)) +#endif +#endif +*generate_candidates_worker_thread(void *args) { uint16_t *sum_args = (uint16_t *)args; uint16_t sum_a0 = sums[sum_args[0]]; diff --git a/client/mifarehost.c b/client/mifarehost.c index d204bf12..dd656ee1 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -295,7 +295,13 @@ typedef // wrapper function for multi-threaded lfsr_recovery32 -void* nested_worker_thread(void *arg) +void +#ifdef __has_attribute +#if __has_attribute(force_align_arg_pointer) +__attribute__((force_align_arg_pointer)) +#endif +#endif +*nested_worker_thread(void *arg) { struct Crypto1State *p1; StateList_t *statelist = arg; @@ -309,6 +315,7 @@ void* nested_worker_thread(void *arg) return statelist->head.slhead; } + int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *resultKey, bool calibrate) { uint16_t i; diff --git a/client/proxmark3.c b/client/proxmark3.c index 99ba9fba..95dd7cb9 100644 --- a/client/proxmark3.c +++ b/client/proxmark3.c @@ -67,7 +67,14 @@ struct receiver_arg { byte_t rx[sizeof(UsbCommand)]; byte_t* prx = rx; -static void *uart_receiver(void *targ) { + +static void +#ifdef __has_attribute +#if __has_attribute(force_align_arg_pointer) +__attribute__((force_align_arg_pointer)) +#endif +#endif +*uart_receiver(void *targ) { struct receiver_arg *arg = (struct receiver_arg*)targ; size_t rxlen; @@ -95,7 +102,13 @@ static void *uart_receiver(void *targ) { } -void main_loop(char *script_cmds_file, char *script_cmd, bool usb_present) { +void +#ifdef __has_attribute +#if __has_attribute(force_align_arg_pointer) +__attribute__((force_align_arg_pointer)) +#endif +#endif +main_loop(char *script_cmds_file, char *script_cmd, bool usb_present) { struct receiver_arg rarg; char *cmd = NULL; pthread_t reader_thread; From 32e6891a05f70ebdfae44671c9e5fde3035bb598 Mon Sep 17 00:00:00 2001 From: Arnie97 Date: Thu, 1 Feb 2018 15:09:14 +0800 Subject: [PATCH 166/310] hf mf nested: use bitwise or instead. (#556) --- client/cmdhfmf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index dbb6d092..f5d7a5be 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -683,9 +683,9 @@ int CmdHF14AMfNested(const char *Cmd) if (transferToEml) { uint8_t sectortrailer; if (trgBlockNo < 32*4) { // 4 block sector - sectortrailer = (trgBlockNo & ~0x03) + 3; + sectortrailer = trgBlockNo | 0x03; } else { // 16 block sector - sectortrailer = (trgBlockNo & ~0x0f) + 15; + sectortrailer = trgBlockNo | 0x0f; } mfEmlGetMem(keyBlock, sectortrailer, 1); From d86ee73da53a701b3e47de9eb2a047c6bf0b0bd1 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Sat, 3 Feb 2018 18:24:32 +0100 Subject: [PATCH 167/310] fix: prevent hf mf mifare from always running twice (issue #510) --- client/mifarehost.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/client/mifarehost.c b/client/mifarehost.c index d204bf12..b6eb60f0 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -178,12 +178,14 @@ int mfDarkside(uint64_t *key) continue; } - qsort(keylist, keycount, sizeof(*keylist), compare_uint64); - keycount = intersection(last_keylist, keylist); - if (keycount == 0) { - free(last_keylist); - last_keylist = keylist; - continue; + if (par_list == 0) { + qsort(keylist, keycount, sizeof(*keylist), compare_uint64); + keycount = intersection(last_keylist, keylist); + if (keycount == 0) { + free(last_keylist); + last_keylist = keylist; + continue; + } } if (keycount > 1) { @@ -198,10 +200,10 @@ int mfDarkside(uint64_t *key) for (int i = 0; i < keycount; i += max_keys) { int size = keycount - i > max_keys ? max_keys : keycount - i; for (int j = 0; j < size; j++) { - if (last_keylist == NULL) { - num_to_bytes(keylist[i*max_keys + j], 6, keyBlock); - } else { + if (par_list == 0) { num_to_bytes(last_keylist[i*max_keys + j], 6, keyBlock); + } else { + num_to_bytes(keylist[i*max_keys + j], 6, keyBlock); } } if (!mfCheckKeys(0, 0, false, size, keyBlock, key)) { From 948b49c4077946bc5aea7b8def79e5f856997197 Mon Sep 17 00:00:00 2001 From: Proxcloud Date: Wed, 7 Feb 2018 01:07:48 +0800 Subject: [PATCH 168/310] Add BOS USB descriptor. This allows non-root access on Android devices --- common/usb_cdc.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/common/usb_cdc.c b/common/usb_cdc.c index 348b97e9..36df2a86 100644 --- a/common/usb_cdc.c +++ b/common/usb_cdc.c @@ -156,6 +156,28 @@ static const char cfgDescriptor[] = { 0x00, 0x00 // bInterval }; +const char BOSDescriptor[] = { + // BOS descriptor header + 0x05, 0x0F, 0x39, 0x00, 0x02, + + // Microsoft OS 2.0 Platform Capability Descriptor + 0x1C, // Descriptor size (28 bytes) + 0x10, // Descriptor type (Device Capability) + 0x05, // Capability type (Platform) + 0x00, // Reserved + + // MS OS 2.0 Platform Capability ID (D8DD60DF-4589-4CC7-9CD2-659D9E648A9F) + 0xDF, 0x60, 0xDD, 0xD8, + 0x89, 0x45, + 0xC7, 0x4C, + 0x9C, 0xD2, + 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F, + + 0x00, 0x00, 0x03, 0x06, // Windows version (8.1) (0x06030000) + 0x1e, 0x00, + 252, // Vendor-assigned bMS_VendorCode + 0x00 // Doesn’t support alternate enumeration +}; static const char StrDescLanguageCodes[] = { 4, // Length @@ -550,6 +572,10 @@ void AT91F_CDC_Enumerate() { AT91F_USB_SendData(pUdp, devDescriptor, MIN(sizeof(devDescriptor), wLength)); else if (wValue == 0x200) // Return Configuration Descriptor AT91F_USB_SendData(pUdp, cfgDescriptor, MIN(sizeof(cfgDescriptor), wLength)); + else if ((wValue & 0xF00) == 0xF00) // Return BOS Descriptor + AT91F_USB_SendData(pUdp, BOSDescriptor, MIN(sizeof(BOSDescriptor), wLength)); + else if ((wValue & 0x300) == 0x300) // Return Manufacturer Descriptor - this is needed by Android + AT91F_USB_SendData(pUdp, StrDescManufacturer, MIN(sizeof(StrDescManufacturer), wLength)); else if ((wValue & 0xF00) == 0x300) { // Return String Descriptor const char *strDescriptor = getStringDescriptor(wValue & 0xff); if (strDescriptor != NULL) { From 02515642bc81dc44d08e47b2f3454e29609dd0fe Mon Sep 17 00:00:00 2001 From: Iceman Date: Thu, 8 Feb 2018 04:15:42 +0100 Subject: [PATCH 169/310] fix: hf mf mifare - populate keylist (#568) This will solve the long running of the attack, since all candidate keys will be tested. --- client/mifarehost.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/mifarehost.c b/client/mifarehost.c index a01d3b5f..2bd21077 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -201,9 +201,9 @@ int mfDarkside(uint64_t *key) int size = keycount - i > max_keys ? max_keys : keycount - i; for (int j = 0; j < size; j++) { if (par_list == 0) { - num_to_bytes(last_keylist[i*max_keys + j], 6, keyBlock); + num_to_bytes(last_keylist[i*max_keys + j], 6, keyBlock+(j*6)); } else { - num_to_bytes(keylist[i*max_keys + j], 6, keyBlock); + num_to_bytes(keylist[i*max_keys + j], 6, keyBlock+(j*6)); } } if (!mfCheckKeys(0, 0, false, size, keyBlock, key)) { From 8b9de94afe7436eb14bf6e6c801bd4054842abe2 Mon Sep 17 00:00:00 2001 From: Proxcloud Date: Thu, 8 Feb 2018 17:47:40 +0800 Subject: [PATCH 170/310] Remove BOS descriptor, leave just manufacturer descriptor for Android --- common/usb_cdc.c | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/common/usb_cdc.c b/common/usb_cdc.c index 36df2a86..3553d850 100644 --- a/common/usb_cdc.c +++ b/common/usb_cdc.c @@ -156,28 +156,6 @@ static const char cfgDescriptor[] = { 0x00, 0x00 // bInterval }; -const char BOSDescriptor[] = { - // BOS descriptor header - 0x05, 0x0F, 0x39, 0x00, 0x02, - - // Microsoft OS 2.0 Platform Capability Descriptor - 0x1C, // Descriptor size (28 bytes) - 0x10, // Descriptor type (Device Capability) - 0x05, // Capability type (Platform) - 0x00, // Reserved - - // MS OS 2.0 Platform Capability ID (D8DD60DF-4589-4CC7-9CD2-659D9E648A9F) - 0xDF, 0x60, 0xDD, 0xD8, - 0x89, 0x45, - 0xC7, 0x4C, - 0x9C, 0xD2, - 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F, - - 0x00, 0x00, 0x03, 0x06, // Windows version (8.1) (0x06030000) - 0x1e, 0x00, - 252, // Vendor-assigned bMS_VendorCode - 0x00 // Doesn’t support alternate enumeration -}; static const char StrDescLanguageCodes[] = { 4, // Length @@ -572,8 +550,6 @@ void AT91F_CDC_Enumerate() { AT91F_USB_SendData(pUdp, devDescriptor, MIN(sizeof(devDescriptor), wLength)); else if (wValue == 0x200) // Return Configuration Descriptor AT91F_USB_SendData(pUdp, cfgDescriptor, MIN(sizeof(cfgDescriptor), wLength)); - else if ((wValue & 0xF00) == 0xF00) // Return BOS Descriptor - AT91F_USB_SendData(pUdp, BOSDescriptor, MIN(sizeof(BOSDescriptor), wLength)); else if ((wValue & 0x300) == 0x300) // Return Manufacturer Descriptor - this is needed by Android AT91F_USB_SendData(pUdp, StrDescManufacturer, MIN(sizeof(StrDescManufacturer), wLength)); else if ((wValue & 0xF00) == 0x300) { // Return String Descriptor From c179e7b1f886f5ca0c2f924197349195a8925215 Mon Sep 17 00:00:00 2001 From: Proxcloud Date: Thu, 8 Feb 2018 19:37:55 +0800 Subject: [PATCH 171/310] remove old Manufacturer description code. fix Product description length --- common/usb_cdc.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/common/usb_cdc.c b/common/usb_cdc.c index 3553d850..d3f5cd0c 100644 --- a/common/usb_cdc.c +++ b/common/usb_cdc.c @@ -183,7 +183,7 @@ static const char StrDescManufacturer[] = { }; static const char StrDescProduct[] = { - 8, // Length + 4, // Length 0x03, // Type is string 'P', 0x00, 'M', 0x00, @@ -550,16 +550,15 @@ void AT91F_CDC_Enumerate() { AT91F_USB_SendData(pUdp, devDescriptor, MIN(sizeof(devDescriptor), wLength)); else if (wValue == 0x200) // Return Configuration Descriptor AT91F_USB_SendData(pUdp, cfgDescriptor, MIN(sizeof(cfgDescriptor), wLength)); - else if ((wValue & 0x300) == 0x300) // Return Manufacturer Descriptor - this is needed by Android - AT91F_USB_SendData(pUdp, StrDescManufacturer, MIN(sizeof(StrDescManufacturer), wLength)); else if ((wValue & 0xF00) == 0x300) { // Return String Descriptor const char *strDescriptor = getStringDescriptor(wValue & 0xff); if (strDescriptor != NULL) { AT91F_USB_SendData(pUdp, strDescriptor, MIN(strDescriptor[0], wLength)); } else { - AT91F_USB_SendStall(pUdp); + AT91F_USB_SendData(pUdp, StrDescManufacturer, MIN(sizeof(StrDescManufacturer), wLength)); } } + else AT91F_USB_SendStall(pUdp); break; From 13c25f892e003f1fa3639b7154782c3146aad4eb Mon Sep 17 00:00:00 2001 From: Proxcloud Date: Thu, 8 Feb 2018 19:43:03 +0800 Subject: [PATCH 172/310] remove debug change --- common/usb_cdc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/common/usb_cdc.c b/common/usb_cdc.c index d3f5cd0c..51dcacdb 100644 --- a/common/usb_cdc.c +++ b/common/usb_cdc.c @@ -555,10 +555,9 @@ void AT91F_CDC_Enumerate() { if (strDescriptor != NULL) { AT91F_USB_SendData(pUdp, strDescriptor, MIN(strDescriptor[0], wLength)); } else { - AT91F_USB_SendData(pUdp, StrDescManufacturer, MIN(sizeof(StrDescManufacturer), wLength)); + AT91F_USB_SendStall(pUdp); } } - else AT91F_USB_SendStall(pUdp); break; From d03a573eee1e2d9f87990524fee3502d5e730665 Mon Sep 17 00:00:00 2001 From: Proxcloud Date: Thu, 8 Feb 2018 20:40:57 +0800 Subject: [PATCH 173/310] change product desc to 9 and add extra null byte --- common/usb_cdc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/common/usb_cdc.c b/common/usb_cdc.c index 51dcacdb..82522623 100644 --- a/common/usb_cdc.c +++ b/common/usb_cdc.c @@ -183,11 +183,12 @@ static const char StrDescManufacturer[] = { }; static const char StrDescProduct[] = { - 4, // Length + 9, // Length 0x03, // Type is string 'P', 0x00, 'M', 0x00, - '3', 0x00 + '3', 0x00, + 0x00 }; const char* getStringDescriptor(uint8_t idx) From 664e132f882e04cf8aafe9a6d8fc137d57816ab8 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Thu, 8 Feb 2018 14:50:03 +0100 Subject: [PATCH 174/310] hf mf mifare: * prevent WDT crash on repeated tries * comes with faster execution as side effect * use uint8_t instead of byte_t * populate ar instead of (correctly) assuming that it is zero * remove the "will take a few seconds longer" message because it is no longer true --- armsrc/iso14443a.c | 28 +++++++++++++--------------- client/mifarehost.c | 17 ++++++++--------- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 6703cc65..a8273e5e 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -2151,9 +2151,7 @@ void ReaderMifare(bool first_try) uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE]; uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE]; - if (first_try) { - iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD); - } + iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD); // free eventually allocated BigBuf memory. We want all for tracing. BigBuf_free(); @@ -2161,9 +2159,9 @@ void ReaderMifare(bool first_try) clear_trace(); set_tracing(true); - byte_t nt_diff = 0; + uint8_t nt_diff = 0; uint8_t par[1] = {0}; // maximum 8 Bytes to be sent here, 1 byte parity is therefore enough - static byte_t par_low = 0; + static uint8_t par_low = 0; bool led_on = true; uint8_t uid[10] ={0}; uint32_t cuid; @@ -2171,11 +2169,11 @@ void ReaderMifare(bool first_try) uint32_t nt = 0; uint32_t previous_nt = 0; static uint32_t nt_attacked = 0; - byte_t par_list[8] = {0x00}; - byte_t ks_list[8] = {0x00}; + uint8_t par_list[8] = {0x00}; + uint8_t ks_list[8] = {0x00}; #define PRNG_SEQUENCE_LENGTH (1 << 16); - static uint32_t sync_time; + uint32_t sync_time = GetCountSspClk() & 0xfffffff8; static int32_t sync_cycles; int catch_up_cycles = 0; int last_catch_up = 0; @@ -2185,10 +2183,9 @@ void ReaderMifare(bool first_try) if (first_try) { mf_nr_ar3 = 0; - sync_time = GetCountSspClk() & 0xfffffff8; + par[0] = par_low = 0; sync_cycles = PRNG_SEQUENCE_LENGTH; // theory: Mifare Classic's random generator repeats every 2^16 cycles (and so do the tag nonces). nt_attacked = 0; - par[0] = 0; } else { // we were unsuccessful on a previous call. Try another READER nonce (first 3 parity bits remain the same) @@ -2204,6 +2201,7 @@ void ReaderMifare(bool first_try) #define MAX_UNEXPECTED_RANDOM 4 // maximum number of unexpected (i.e. real) random numbers when trying to sync. Then give up. #define MAX_SYNC_TRIES 32 + #define SYNC_TIME_BUFFER 16 // if there is only SYNC_TIME_BUFFER left before next planned sync, wait for next PRNG cycle #define NUM_DEBUG_INFOS 8 // per strategy #define MAX_STRATEGY 3 uint16_t unexpected_random = 0; @@ -2253,8 +2251,8 @@ void ReaderMifare(bool first_try) sync_time = (sync_time & 0xfffffff8) + sync_cycles + catch_up_cycles; catch_up_cycles = 0; - // if we missed the sync time already, advance to the next nonce repeat - while(GetCountSspClk() > sync_time) { + // if we missed the sync time already or are about to miss it, advance to the next nonce repeat + while(sync_time < GetCountSspClk() + SYNC_TIME_BUFFER) { elapsed_prng_sequences++; sync_time = (sync_time & 0xfffffff8) + sync_cycles; } @@ -2410,14 +2408,14 @@ void ReaderMifare(bool first_try) } } - byte_t buf[28]; + uint8_t buf[32]; memcpy(buf + 0, uid, 4); num_to_bytes(nt, 4, buf + 4); memcpy(buf + 8, par_list, 8); memcpy(buf + 16, ks_list, 8); - memcpy(buf + 24, mf_nr_ar, 4); + memcpy(buf + 24, mf_nr_ar, 8); - cmd_send(CMD_ACK, isOK, 0, 0, buf, 28); + cmd_send(CMD_ACK, isOK, 0, 0, buf, 32); // Thats it... FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); diff --git a/client/mifarehost.c b/client/mifarehost.c index 2bd21077..471fbc42 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -73,13 +73,12 @@ static uint32_t intersection(uint64_t *list1, uint64_t *list2) // Darkside attack (hf mf mifare) -static uint32_t nonce2key(uint32_t uid, uint32_t nt, uint32_t nr, uint64_t par_info, uint64_t ks_info, uint64_t **keys) { +static uint32_t nonce2key(uint32_t uid, uint32_t nt, uint32_t nr, uint32_t ar, uint64_t par_info, uint64_t ks_info, uint64_t **keys) { struct Crypto1State *states; - uint32_t i, pos, rr; //nr_diff; + uint32_t i, pos; uint8_t bt, ks3x[8], par[8][8]; uint64_t key_recovered; - static uint64_t *keylist; - rr = 0; + uint64_t *keylist; // Reset the last three significant bits of the reader nonce nr &= 0xffffff1f; @@ -92,7 +91,7 @@ static uint32_t nonce2key(uint32_t uid, uint32_t nt, uint32_t nr, uint64_t par_i } } - states = lfsr_common_prefix(nr, rr, ks3x, par, (par_info == 0)); + states = lfsr_common_prefix(nr, ar, ks3x, par, (par_info == 0)); if (states == NULL) { *keys = NULL; @@ -116,7 +115,7 @@ static uint32_t nonce2key(uint32_t uid, uint32_t nt, uint32_t nr, uint64_t par_i int mfDarkside(uint64_t *key) { uint32_t uid = 0; - uint32_t nt = 0, nr = 0; + uint32_t nt = 0, nr = 0, ar = 0; uint64_t par_list = 0, ks_list = 0; uint64_t *keylist = NULL, *last_keylist = NULL; uint32_t keycount = 0; @@ -159,18 +158,18 @@ int mfDarkside(uint64_t *key) nt = (uint32_t)bytes_to_num(resp.d.asBytes + 4, 4); par_list = bytes_to_num(resp.d.asBytes + 8, 8); ks_list = bytes_to_num(resp.d.asBytes + 16, 8); - nr = bytes_to_num(resp.d.asBytes + 24, 4); + nr = (uint32_t)bytes_to_num(resp.d.asBytes + 24, 4); + ar = (uint32_t)bytes_to_num(resp.d.asBytes + 28, 4); break; } } if (par_list == 0 && c.arg[0] == true) { PrintAndLog("Parity is all zero. Most likely this card sends NACK on every failed authentication."); - PrintAndLog("Attack will take a few seconds longer because we need two consecutive successful runs."); } c.arg[0] = false; - keycount = nonce2key(uid, nt, nr, par_list, ks_list, &keylist); + keycount = nonce2key(uid, nt, nr, ar, par_list, ks_list, &keylist); if (keycount == 0) { PrintAndLog("Key not found (lfsr_common_prefix list is null). Nt=%08x", nt); From f3dc6d59f45e25fd199cb20cdf4d3af270d93963 Mon Sep 17 00:00:00 2001 From: Proxcloud Date: Thu, 8 Feb 2018 22:32:08 +0800 Subject: [PATCH 175/310] make product string even length --- common/usb_cdc.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/common/usb_cdc.c b/common/usb_cdc.c index 82522623..d33bca7b 100644 --- a/common/usb_cdc.c +++ b/common/usb_cdc.c @@ -183,12 +183,17 @@ static const char StrDescManufacturer[] = { }; static const char StrDescProduct[] = { - 9, // Length + 20, // Length 0x03, // Type is string - 'P', 0x00, - 'M', 0x00, - '3', 0x00, - 0x00 + 'p', 0x00, + 'r', 0x00, + 'o', 0x00, + 'x', 0x00, + 'm', 0x00, + 'a', 0x00, + 'r', 0x00, + 'k', 0x00, + '3', 0x00 }; const char* getStringDescriptor(uint8_t idx) From a37725facfb70e0d699f423c6d986173ef890531 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Fri, 9 Feb 2018 16:50:55 +0200 Subject: [PATCH 176/310] add nested auth decoding to `hf mf sniff` --- armsrc/mifarecmd.h | 1 - armsrc/mifaresniff.c | 2 +- client/cmdhfmf.c | 17 +++++- client/mifarehost.c | 134 +++++++++++++++++++++++++++++++++++++------ client/mifarehost.h | 3 +- client/util.c | 17 ++++++ client/util.h | 1 + common/parity.h | 6 ++ 8 files changed, 159 insertions(+), 22 deletions(-) diff --git a/armsrc/mifarecmd.h b/armsrc/mifarecmd.h index 145e2989..e17fa998 100644 --- a/armsrc/mifarecmd.h +++ b/armsrc/mifarecmd.h @@ -16,7 +16,6 @@ #include "proxmark3.h" #include "apps.h" #include "util.h" -#include "string.h" #include "iso14443crc.h" #include "iso14443a.h" diff --git a/armsrc/mifaresniff.c b/armsrc/mifaresniff.c index 4e573be7..f20f2557 100644 --- a/armsrc/mifaresniff.c +++ b/armsrc/mifaresniff.c @@ -116,7 +116,7 @@ bool RAMFUNC MfSniffLogic(const uint8_t *data, uint16_t len, uint8_t *parity, ui sniffState = SNF_CARD_CMD; } // intentionally no break; case SNF_CARD_CMD:{ - LogTrace(data, len, 0, 0, NULL, reader); + LogTrace(data, len, 0, 0, parity, reader); timerData = GetTickCount(); break; } diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index f5d7a5be..a2da01c9 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -18,6 +18,7 @@ #include "proxmark3.h" #include "cmdmain.h" #include "cmdhfmfhard.h" +#include "parity.h" #include "util.h" #include "util_posix.h" #include "usb_cmd.h" @@ -2470,6 +2471,7 @@ int CmdHF14AMfSniff(const char *Cmd){ //var int res = 0; int len = 0; + int parlen = 0; int blockLen = 0; int pckNum = 0; int num = 0; @@ -2481,6 +2483,7 @@ int CmdHF14AMfSniff(const char *Cmd){ uint8_t *buf = NULL; uint16_t bufsize = 0; uint8_t *bufPtr = NULL; + uint8_t parity[16]; char ctmp = param_getchar(Cmd, 0); if ( ctmp == 'h' || ctmp == 'H' ) { @@ -2572,6 +2575,7 @@ int CmdHF14AMfSniff(const char *Cmd){ } else { isTag = false; } + parlen = (len - 1) / 8 + 1; bufPtr += 2; if ((len == 14) && (bufPtr[0] == 0xff) && (bufPtr[1] == 0xff) && (bufPtr[12] == 0xff) && (bufPtr[13] == 0xff)) { memcpy(uid, bufPtr + 2, 7); @@ -2590,15 +2594,22 @@ int CmdHF14AMfSniff(const char *Cmd){ if (wantDecrypt) mfTraceInit(uid, atqa, sak, wantSaveToEmlFile); } else { - PrintAndLog("%s(%d):%s", isTag ? "TAG":"RDR", num, sprint_hex(bufPtr, len)); + oddparitybuf(bufPtr, len, parity); + PrintAndLog("%s(%d):%s [%s] c[%s]%c", + isTag ? "TAG":"RDR", + num, + sprint_hex(bufPtr, len), + printBitsPar(bufPtr + len, len), + printBitsPar(parity, len), + memcmp(bufPtr + len, parity, len / 8 + 1) ? '!' : ' '); if (wantLogToFile) AddLogHex(logHexFileName, isTag ? "TAG: ":"RDR: ", bufPtr, len); if (wantDecrypt) - mfTraceDecode(bufPtr, len, wantSaveToEmlFile); + mfTraceDecode(bufPtr, len, bufPtr[len], wantSaveToEmlFile); num++; } bufPtr += len; - bufPtr += ((len-1)/8+1); // ignore parity + bufPtr += parlen; // ignore parity } pckNum = 0; } diff --git a/client/mifarehost.c b/client/mifarehost.c index 471fbc42..e1ced176 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -20,6 +20,7 @@ #include "usb_cmd.h" #include "cmdmain.h" #include "ui.h" +#include "parity.h" #include "util.h" #include "iso14443crc.h" @@ -582,14 +583,19 @@ struct Crypto1State *traceCrypto1 = NULL; struct Crypto1State *revstate; uint64_t lfsr; +uint64_t ui64Key; uint32_t ks2; uint32_t ks3; -uint32_t uid; // serial number -uint32_t nt; // tag challenge -uint32_t nr_enc; // encrypted reader challenge -uint32_t ar_enc; // encrypted reader response -uint32_t at_enc; // encrypted tag response +uint32_t uid; // serial number +uint32_t nt; // tag challenge +uint32_t nt_enc; // encrypted tag challenge +uint8_t nt_enc_par; // encrypted tag challenge parity +uint32_t nr_enc; // encrypted reader challenge +uint32_t ar_enc; // encrypted reader response +uint8_t ar_enc_par; // encrypted reader response parity +uint32_t at_enc; // encrypted tag response +uint8_t at_enc_par; // encrypted tag response parity int isTraceCardEmpty(void) { return ((traceCard[0] == 0) && (traceCard[1] == 0) && (traceCard[2] == 0) && (traceCard[3] == 0)); @@ -708,8 +714,36 @@ void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len, bool i return; } +bool NTParityCheck(uint32_t ntx) { + if ( + (oddparity8(ntx >> 8 & 0xff) ^ (ntx & 0x01) ^ ((nt_enc_par >> 5) & 0x01) ^ (nt_enc & 0x01)) || + (oddparity8(ntx >> 16 & 0xff) ^ (ntx >> 8 & 0x01) ^ ((nt_enc_par >> 6) & 0x01) ^ (nt_enc >> 8 & 0x01)) || + (oddparity8(ntx >> 24 & 0xff) ^ (ntx >> 16 & 0x01) ^ ((nt_enc_par >> 7) & 0x01) ^ (nt_enc >> 16 & 0x01)) + ) + return false; + + uint32_t ar = prng_successor(ntx, 64); + if ( + (oddparity8(ar >> 8 & 0xff) ^ (ar & 0x01) ^ ((ar_enc_par >> 5) & 0x01) ^ (ar_enc & 0x01)) || + (oddparity8(ar >> 16 & 0xff) ^ (ar >> 8 & 0x01) ^ ((ar_enc_par >> 6) & 0x01) ^ (ar_enc >> 8 & 0x01)) || + (oddparity8(ar >> 24 & 0xff) ^ (ar >> 16 & 0x01) ^ ((ar_enc_par >> 7) & 0x01) ^ (ar_enc >> 16 & 0x01)) + ) + return false; -int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile) { + uint32_t at = prng_successor(ntx, 96); + if ( + (oddparity8(ar & 0xff) ^ (at >> 24 & 0x01) ^ ((ar_enc_par >> 4) & 0x01) ^ (at_enc >> 24 & 0x01)) || + (oddparity8(at >> 8 & 0xff) ^ (at & 0x01) ^ ((at_enc_par >> 5) & 0x01) ^ (at_enc & 0x01)) || + (oddparity8(at >> 16 & 0xff) ^ (at >> 8 & 0x01) ^ ((at_enc_par >> 6) & 0x01) ^ (at_enc >> 8 & 0x01)) || + (oddparity8(at >> 24 & 0xff) ^ (at >> 16 & 0x01) ^ ((at_enc_par >> 7) & 0x01) ^ (at_enc >> 16 & 0x01)) + ) + return false; + + return true; +} + + +int mfTraceDecode(uint8_t *data_src, int len, uint8_t parity, bool wantSaveToEmlFile) { uint8_t data[64]; if (traceState == TRACE_ERROR) return 1; @@ -721,7 +755,9 @@ int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile) { memcpy(data, data_src, len); if ((traceCrypto1) && ((traceState == TRACE_IDLE) || (traceState > TRACE_AUTH_OK))) { mf_crypto1_decrypt(traceCrypto1, data, len, 0); - PrintAndLog("dec> %s", sprint_hex(data, len)); + uint8_t parity[16]; + oddparitybuf(data, len, parity); + PrintAndLog("dec> %s [%s]", sprint_hex(data, len), printBitsPar(parity, len)); AddLogHex(logHexFileName, "dec> ", data, len); } @@ -810,7 +846,12 @@ int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile) { case TRACE_AUTH1: if (len == 4) { traceState = TRACE_AUTH2; - nt = bytes_to_num(data, 4); + if (!traceCrypto1) { + nt = bytes_to_num(data, 4); + } else { + nt_enc = bytes_to_num(data, 4); + nt_enc_par = parity; + } return 0; } else { traceState = TRACE_ERROR; @@ -824,6 +865,7 @@ int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile) { nr_enc = bytes_to_num(data, 4); ar_enc = bytes_to_num(data + 4, 4); + ar_enc_par = parity << 4; return 0; } else { traceState = TRACE_ERROR; @@ -835,8 +877,9 @@ int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile) { if (len ==4) { traceState = TRACE_IDLE; + at_enc = bytes_to_num(data, 4); + at_enc_par = parity; if (!traceCrypto1) { - at_enc = bytes_to_num(data, 4); // decode key here) ks2 = ar_enc ^ prng_successor(nt, 64); @@ -848,16 +891,75 @@ int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile) { lfsr_rollback_word(revstate, uid ^ nt, 0); crypto1_get_lfsr(revstate, &lfsr); - printf("key> %x%x\n", (unsigned int)((lfsr & 0xFFFFFFFF00000000) >> 32), (unsigned int)(lfsr & 0xFFFFFFFF)); + crypto1_destroy(revstate); + ui64Key = lfsr; + printf("key> probable key:%x%x Prng:%s ks2:%08x ks3:%08x\n", + (unsigned int)((lfsr & 0xFFFFFFFF00000000) >> 32), (unsigned int)(lfsr & 0xFFFFFFFF), + validate_prng_nonce(nt) ? "WEAK": "HARDEND", + ks2, + ks3); AddLogUint64(logHexFileName, "key> ", lfsr); } else { - printf("key> nested not implemented!\n"); - at_enc = bytes_to_num(data, 4); - - crypto1_destroy(traceCrypto1); + if (validate_prng_nonce(nt)) { + struct Crypto1State *pcs; + pcs = crypto1_create(ui64Key); + uint32_t nt1 = crypto1_word(pcs, nt_enc ^ uid, 1) ^ nt_enc; + uint32_t ar = prng_successor(nt1, 64); + uint32_t at = prng_successor(nt1, 96); + printf("key> nested auth uid: %08x nt: %08x nt_parity: %s ar: %08x at: %08x\n", uid, nt1, printBitsPar(&nt_enc_par, 4), ar, at); + uint32_t nr1 = crypto1_word(pcs, nr_enc, 1) ^ nr_enc; + uint32_t ar1 = crypto1_word(pcs, 0, 0) ^ ar_enc; + uint32_t at1 = crypto1_word(pcs, 0, 0) ^ at_enc; + printf("key> the same key test. nr1: %08x ar1: %08x at1: %08x \n", nr1, ar1, at1); - // not implemented - traceState = TRACE_ERROR; + if (NTParityCheck(nt1)) + printf("key> the same key test OK. key=%x%x\n", (unsigned int)((ui64Key & 0xFFFFFFFF00000000) >> 32), (unsigned int)(ui64Key & 0xFFFFFFFF)); + else + printf("key> the same key test. check nt parity error.\n"); + + uint32_t ntc = prng_successor(nt, 90); + uint32_t ntx = 0; + int ntcnt = 0; + for (int i = 0; i < 16383; i++) { + ntc = prng_successor(ntc, 1); + if (NTParityCheck(ntc)){ + if (!ntcnt) + ntx = ntc; + ntcnt++; + } + } + if (ntcnt) + printf("key> nt candidate=%08x nonce distance=%d candidates count=%d\n", ntx, nonce_distance(nt, ntx), ntcnt); + else + printf("key> don't have any nt candidate( \n"); + + nt = ntx; + ks2 = ar_enc ^ prng_successor(ntx, 64); + ks3 = at_enc ^ prng_successor(ntx, 96); + + // decode key + revstate = lfsr_recovery64(ks2, ks3); + lfsr_rollback_word(revstate, 0, 0); + lfsr_rollback_word(revstate, 0, 0); + lfsr_rollback_word(revstate, nr_enc, 1); + lfsr_rollback_word(revstate, uid ^ nt, 0); + + crypto1_get_lfsr(revstate, &lfsr); + crypto1_destroy(revstate); + ui64Key = lfsr; + printf("key> probable key:%x%x ks2:%08x ks3:%08x\n", + (unsigned int)((lfsr & 0xFFFFFFFF00000000) >> 32), (unsigned int)(lfsr & 0xFFFFFFFF), + ks2, + ks3); + AddLogUint64(logHexFileName, "key> ", lfsr); + } else { + printf("key> hardnested not implemented!\n"); + + crypto1_destroy(traceCrypto1); + + // not implemented + traceState = TRACE_ERROR; + } } int blockShift = ((traceCurBlock & 0xFC) + 3) * 16; diff --git a/client/mifarehost.h b/client/mifarehost.h index 031dac1b..bef397bb 100644 --- a/client/mifarehost.h +++ b/client/mifarehost.h @@ -50,7 +50,7 @@ extern int mfCSetBlock(uint8_t blockNo, uint8_t *data, uint8_t *uid, bool wantWi extern int mfCGetBlock(uint8_t blockNo, uint8_t *data, uint8_t params); extern int mfTraceInit(uint8_t *tuid, uint8_t *atqa, uint8_t sak, bool wantSaveToEmlFile); -extern int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile); +extern int mfTraceDecode(uint8_t *data_src, int len, uint8_t parity, bool wantSaveToEmlFile); extern int isTraceCardEmpty(void); extern int isBlockEmpty(int blockN); @@ -61,5 +61,6 @@ extern int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t extern int mfCIdentify(); extern int DetectClassicPrng(void); +extern bool validate_prng_nonce(uint32_t nonce); #endif diff --git a/client/util.c b/client/util.c index 7e6b4074..bbc7f2cf 100644 --- a/client/util.c +++ b/client/util.c @@ -356,6 +356,23 @@ char * printBits(size_t const size, void const * const ptr) return buf; } +char * printBitsPar(const uint8_t *b, size_t len) { + static char buf1[512] = {0}; + static char buf2[512] = {0}; + static char *buf; + if (buf != buf1) + buf = buf1; + else + buf = buf2; + memset(buf, 0x00, 512); + + for (int i = 0; i < len; i++) { + buf[i] = ((b[i / 8] << (i % 8)) & 0x80) ? '1':'0'; + } + return buf; +} + + // ------------------------------------------------------------------------- // string parameters lib // ------------------------------------------------------------------------- diff --git a/client/util.h b/client/util.h index fd7ceaff..2e64d7ca 100644 --- a/client/util.h +++ b/client/util.h @@ -54,6 +54,7 @@ extern uint64_t bytes_to_num(uint8_t* src, size_t len); extern void num_to_bytebits(uint64_t n, size_t len, uint8_t *dest); extern void num_to_bytebitsLSBF(uint64_t n, size_t len, uint8_t *dest); extern char *printBits(size_t const size, void const * const ptr); +extern char * printBitsPar(const uint8_t *b, size_t len); extern uint32_t SwapBits(uint32_t value, int nrbits); extern uint8_t *SwapEndian64(const uint8_t *src, const size_t len, const uint8_t blockSize); extern void SwapEndian64ex(const uint8_t *src, const size_t len, const uint8_t blockSize, uint8_t *dest); diff --git a/common/parity.h b/common/parity.h index 615fdeee..c574db55 100644 --- a/common/parity.h +++ b/common/parity.h @@ -13,6 +13,7 @@ #include #include +#include "string.h" extern const uint8_t OddByteParity[256]; @@ -21,6 +22,11 @@ static inline bool oddparity8(const uint8_t x) { return OddByteParity[x]; } +static inline void oddparitybuf(const uint8_t *x, size_t len, uint8_t *parity) { + memset(parity, 0x00, (len - 1) / 8 + 1); + for (int i = 0; i < len; i++) + parity[i / 8] |= oddparity8(x[i]) << (7 - (i % 8)); +} static inline bool evenparity8(const uint8_t x) { return !OddByteParity[x]; From 4f131b53ea7e9a66f5c7269fc35b11f8038cbf3e Mon Sep 17 00:00:00 2001 From: merlokk Date: Fri, 9 Feb 2018 17:37:31 +0200 Subject: [PATCH 177/310] added cmdhflist file --- client/Makefile | 1 + client/cmdhf.c | 7 +++++++ client/cmdhflist.c | 17 +++++++++++++++++ client/cmdhflist.h | 11 +++++++++++ common/protocols.h | 9 +++++---- 5 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 client/cmdhflist.c create mode 100644 client/cmdhflist.h diff --git a/client/Makefile b/client/Makefile index bf642803..bf3c8d36 100644 --- a/client/Makefile +++ b/client/Makefile @@ -128,6 +128,7 @@ CMDSRCS = crapto1/crapto1.c\ emv/test/cda_test.c\ emv/cmdemv.c\ cmdhf.c \ + cmdhflist.c \ cmdhf14a.c \ cmdhf14b.c \ cmdhf15.c \ diff --git a/client/cmdhf.c b/client/cmdhf.c index 7a2f3252..34d95c50 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -1,5 +1,6 @@ //----------------------------------------------------------------------------- // Copyright (C) 2010 iZsh +// Merlok - 2017 // // 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 @@ -31,6 +32,7 @@ #include "cmdhftopaz.h" #include "protocols.h" #include "emv/cmdemv.h" +#include "cmdhflist.h" static int CmdHelp(const char *Cmd); @@ -466,6 +468,7 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui case TOPAZ: crcStatus = iso14443B_CRC_check(isResponse, frame, data_len); break; + case PROTO_MIFARE: case ISO_14443A: crcStatus = iso14443A_CRC_check(isResponse, frame, data_len); break; @@ -518,6 +521,7 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui { switch(protocol) { case ICLASS: annotateIclass(explanation,sizeof(explanation),frame,data_len); break; + case PROTO_MIFARE: case ISO_14443A: annotateIso14443a(explanation,sizeof(explanation),frame,data_len); break; case ISO_14443B: annotateIso14443b(explanation,sizeof(explanation),frame,data_len); break; case TOPAZ: annotateTopaz(explanation,sizeof(explanation),frame,data_len); break; @@ -583,6 +587,8 @@ int CmdHFList(const char *Cmd) if(!errors) { if(strcmp(type, "iclass") == 0) { protocol = ICLASS; + } else if(strcmp(type, "mf") == 0) { + protocol = PROTO_MIFARE; } else if(strcmp(type, "14a") == 0) { protocol = ISO_14443A; } else if(strcmp(type, "14b") == 0) { @@ -604,6 +610,7 @@ int CmdHFList(const char *Cmd) PrintAndLog("Supported values:"); PrintAndLog(" raw - just show raw data without annotations"); PrintAndLog(" 14a - interpret data as iso14443a communications"); + PrintAndLog(" mf - interpret data as iso14443a communications and decrypt crypto1 stream"); PrintAndLog(" 14b - interpret data as iso14443b communications"); PrintAndLog(" iclass - interpret data as iclass communications"); PrintAndLog(" topaz - interpret data as topaz communications"); diff --git a/client/cmdhflist.c b/client/cmdhflist.c new file mode 100644 index 00000000..86562e4c --- /dev/null +++ b/client/cmdhflist.c @@ -0,0 +1,17 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Merlok - 2017 +// +// 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. +//----------------------------------------------------------------------------- +// Command: hf mf list. It shows data from arm buffer. +//----------------------------------------------------------------------------- + +#include "cmdhflist.h" + +#include +#include +#include + + diff --git a/client/cmdhflist.h b/client/cmdhflist.h new file mode 100644 index 00000000..138e5590 --- /dev/null +++ b/client/cmdhflist.h @@ -0,0 +1,11 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Merlok - 2017 +// +// 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. +//----------------------------------------------------------------------------- +// Command: hf mf list. It shows data from arm buffer. +//----------------------------------------------------------------------------- + + diff --git a/common/protocols.h b/common/protocols.h index 31252ad3..57e6011f 100644 --- a/common/protocols.h +++ b/common/protocols.h @@ -200,10 +200,11 @@ NXP/Philips CUSTOM COMMANDS #define TOPAZ_WRITE_NE8 0x1B // Write-no-erase (eight bytes) -#define ISO_14443A 0 -#define ICLASS 1 -#define ISO_14443B 2 -#define TOPAZ 3 +#define ISO_14443A 0 +#define ICLASS 1 +#define ISO_14443B 2 +#define TOPAZ 3 +#define PROTO_MIFARE 4 //-- Picopass fuses #define FUSE_FPERS 0x80 From 6612a5a20136d0d4b11bd7f3fd113f4dd836119f Mon Sep 17 00:00:00 2001 From: merlokk Date: Fri, 9 Feb 2018 18:23:04 +0200 Subject: [PATCH 178/310] moved some functions and added auth sequence annotation --- client/cmdhf.c | 126 ++--------------------------- client/cmdhflist.c | 196 +++++++++++++++++++++++++++++++++++++++++++++ client/cmdhflist.h | 12 +++ 3 files changed, 213 insertions(+), 121 deletions(-) diff --git a/client/cmdhf.c b/client/cmdhf.c index 34d95c50..ae1b12a6 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -43,99 +43,6 @@ int CmdHFTune(const char *Cmd) return 0; } - -void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) -{ - switch(cmd[0]) - { - case ISO14443A_CMD_WUPA: snprintf(exp,size,"WUPA"); break; - case ISO14443A_CMD_ANTICOLL_OR_SELECT:{ - // 93 20 = Anticollision (usage: 9320 - answer: 4bytes UID+1byte UID-bytes-xor) - // 93 70 = Select (usage: 9370+5bytes 9320 answer - answer: 1byte SAK) - if(cmd[1] == 0x70) - { - snprintf(exp,size,"SELECT_UID"); break; - }else - { - snprintf(exp,size,"ANTICOLL"); break; - } - } - case ISO14443A_CMD_ANTICOLL_OR_SELECT_2:{ - //95 20 = Anticollision of cascade level2 - //95 70 = Select of cascade level2 - if(cmd[2] == 0x70) - { - snprintf(exp,size,"SELECT_UID-2"); break; - }else - { - snprintf(exp,size,"ANTICOLL-2"); break; - } - } - case ISO14443A_CMD_REQA: snprintf(exp,size,"REQA"); break; - case ISO14443A_CMD_READBLOCK: snprintf(exp,size,"READBLOCK(%d)",cmd[1]); break; - case ISO14443A_CMD_WRITEBLOCK: snprintf(exp,size,"WRITEBLOCK(%d)",cmd[1]); break; - case ISO14443A_CMD_HALT: snprintf(exp,size,"HALT"); break; - case ISO14443A_CMD_RATS: snprintf(exp,size,"RATS"); break; - case MIFARE_CMD_INC: snprintf(exp,size,"INC(%d)",cmd[1]); break; - case MIFARE_CMD_DEC: snprintf(exp,size,"DEC(%d)",cmd[1]); break; - case MIFARE_CMD_RESTORE: snprintf(exp,size,"RESTORE(%d)",cmd[1]); break; - case MIFARE_CMD_TRANSFER: snprintf(exp,size,"TRANSFER(%d)",cmd[1]); break; - case MIFARE_AUTH_KEYA:{ - if ( cmdsize > 3) - snprintf(exp,size,"AUTH-A(%d)",cmd[1]); - else - // case MIFARE_ULEV1_VERSION : both 0x60. - snprintf(exp,size,"EV1 VERSION"); - break; - } - case MIFARE_AUTH_KEYB: snprintf(exp,size,"AUTH-B(%d)",cmd[1]); break; - case MIFARE_MAGICWUPC1: snprintf(exp,size,"MAGIC WUPC1"); break; - case MIFARE_MAGICWUPC2: snprintf(exp,size,"MAGIC WUPC2"); break; - case MIFARE_MAGICWIPEC: snprintf(exp,size,"MAGIC WIPEC"); break; - case MIFARE_ULC_AUTH_1: snprintf(exp,size,"AUTH "); break; - case MIFARE_ULC_AUTH_2: snprintf(exp,size,"AUTH_ANSW"); break; - case MIFARE_ULEV1_AUTH: - if ( cmdsize == 7 ) - snprintf(exp,size,"PWD-AUTH KEY: 0x%02x%02x%02x%02x", cmd[1], cmd[2], cmd[3], cmd[4] ); - else - snprintf(exp,size,"PWD-AUTH"); - break; - case MIFARE_ULEV1_FASTREAD:{ - if ( cmdsize >=3 && cmd[2] <= 0xE6) - snprintf(exp,size,"READ RANGE (%d-%d)",cmd[1],cmd[2]); - else - snprintf(exp,size,"?"); - break; - } - case MIFARE_ULC_WRITE:{ - if ( cmd[1] < 0x21 ) - snprintf(exp,size,"WRITEBLOCK(%d)",cmd[1]); - else - snprintf(exp,size,"?"); - break; - } - case MIFARE_ULEV1_READ_CNT:{ - if ( cmd[1] < 5 ) - snprintf(exp,size,"READ CNT(%d)",cmd[1]); - else - snprintf(exp,size,"?"); - break; - } - case MIFARE_ULEV1_INCR_CNT:{ - if ( cmd[1] < 5 ) - snprintf(exp,size,"INCR(%d)",cmd[1]); - else - snprintf(exp,size,"?"); - break; - } - case MIFARE_ULEV1_READSIG: snprintf(exp,size,"READ_SIG"); break; - case MIFARE_ULEV1_CHECKTEAR: snprintf(exp,size,"CHK_TEARING(%d)",cmd[1]); break; - case MIFARE_ULEV1_VCSL: snprintf(exp,size,"VCSL"); break; - default: snprintf(exp,size,"?"); break; - } - return; -} - void annotateIclass(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) { switch(cmd[0]) @@ -246,33 +153,6 @@ void annotateIso14443b(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) } -/** - * @brief iso14443A_CRC_check Checks CRC in command or response - * @param isResponse - * @param data - * @param len - * @return 0 : CRC-command, CRC not ok - * 1 : CRC-command, CRC ok - * 2 : Not crc-command - */ - -uint8_t iso14443A_CRC_check(bool isResponse, uint8_t* data, uint8_t len) -{ - uint8_t b1,b2; - - if(len <= 2) return 2; - - if(isResponse & (len < 6)) return 2; - - ComputeCrc14443(CRC_14443_A, data, len-2, &b1, &b2); - if (b1 != data[len-2] || b2 != data[len-1]) { - return 0; - } else { - return 1; - } -} - - /** * @brief iso14443B_CRC_check Checks CRC in command or response * @param isResponse @@ -469,6 +349,8 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui crcStatus = iso14443B_CRC_check(isResponse, frame, data_len); break; case PROTO_MIFARE: + crcStatus = mifare_CRC_check(isResponse, frame, data_len); + break; case ISO_14443A: crcStatus = iso14443A_CRC_check(isResponse, frame, data_len); break; @@ -517,11 +399,13 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui EndOfTransmissionTimestamp = timestamp + duration; + if (protocol == PROTO_MIFARE) + annotateMifare(explanation, sizeof(explanation), frame, data_len, isResponse); + if(!isResponse) { switch(protocol) { case ICLASS: annotateIclass(explanation,sizeof(explanation),frame,data_len); break; - case PROTO_MIFARE: case ISO_14443A: annotateIso14443a(explanation,sizeof(explanation),frame,data_len); break; case ISO_14443B: annotateIso14443b(explanation,sizeof(explanation),frame,data_len); break; case TOPAZ: annotateTopaz(explanation,sizeof(explanation),frame,data_len); break; diff --git a/client/cmdhflist.c b/client/cmdhflist.c index 86562e4c..b1d432ee 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -13,5 +13,201 @@ #include #include #include +#include +#include +#include "util.h" +#include "data.h" +#include "ui.h" +#include "iso14443crc.h" +#include "parity.h" +#include "protocols.h" +enum MifareAuthSeq { + masNone, + masNt, + masNrAr, + masAt, + masData, + masDataNested, + masError, +}; +static enum MifareAuthSeq MifareAuthState; + +/** + * @brief iso14443A_CRC_check Checks CRC in command or response + * @param isResponse + * @param data + * @param len + * @return 0 : CRC-command, CRC not ok + * 1 : CRC-command, CRC ok + * 2 : Not crc-command + */ +uint8_t iso14443A_CRC_check(bool isResponse, uint8_t* data, uint8_t len) +{ + uint8_t b1,b2; + + if(len <= 2) return 2; + + if(isResponse & (len < 6)) return 2; + + ComputeCrc14443(CRC_14443_A, data, len-2, &b1, &b2); + if (b1 != data[len-2] || b2 != data[len-1]) { + return 0; + } else { + return 1; + } +} + +uint8_t mifare_CRC_check(bool isResponse, uint8_t* data, uint8_t len) +{ + switch(MifareAuthState) { + case masNone: + case masData: + case masDataNested: + case masError: + return iso14443A_CRC_check(isResponse, data, len); + default: + return 2; + } + +} + +void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) +{ + switch(cmd[0]) + { + case ISO14443A_CMD_WUPA: snprintf(exp,size,"WUPA"); break; + case ISO14443A_CMD_ANTICOLL_OR_SELECT:{ + // 93 20 = Anticollision (usage: 9320 - answer: 4bytes UID+1byte UID-bytes-xor) + // 93 70 = Select (usage: 9370+5bytes 9320 answer - answer: 1byte SAK) + if(cmd[1] == 0x70) + { + snprintf(exp,size,"SELECT_UID"); break; + }else + { + snprintf(exp,size,"ANTICOLL"); break; + } + } + case ISO14443A_CMD_ANTICOLL_OR_SELECT_2:{ + //95 20 = Anticollision of cascade level2 + //95 70 = Select of cascade level2 + if(cmd[2] == 0x70) + { + snprintf(exp,size,"SELECT_UID-2"); break; + }else + { + snprintf(exp,size,"ANTICOLL-2"); break; + } + } + case ISO14443A_CMD_REQA: snprintf(exp,size,"REQA"); break; + case ISO14443A_CMD_READBLOCK: snprintf(exp,size,"READBLOCK(%d)",cmd[1]); break; + case ISO14443A_CMD_WRITEBLOCK: snprintf(exp,size,"WRITEBLOCK(%d)",cmd[1]); break; + case ISO14443A_CMD_HALT: + snprintf(exp,size,"HALT"); + MifareAuthState = masNone; + break; + case ISO14443A_CMD_RATS: snprintf(exp,size,"RATS"); break; + case MIFARE_CMD_INC: snprintf(exp,size,"INC(%d)",cmd[1]); break; + case MIFARE_CMD_DEC: snprintf(exp,size,"DEC(%d)",cmd[1]); break; + case MIFARE_CMD_RESTORE: snprintf(exp,size,"RESTORE(%d)",cmd[1]); break; + case MIFARE_CMD_TRANSFER: snprintf(exp,size,"TRANSFER(%d)",cmd[1]); break; + case MIFARE_AUTH_KEYA: + if ( cmdsize > 3) { + snprintf(exp,size,"AUTH-A(%d)",cmd[1]); + MifareAuthState = masNt; + } else { + // case MIFARE_ULEV1_VERSION : both 0x60. + snprintf(exp,size,"EV1 VERSION"); + } + break; + case MIFARE_AUTH_KEYB: + MifareAuthState = masNt; + snprintf(exp,size,"AUTH-B(%d)",cmd[1]); + break; + case MIFARE_MAGICWUPC1: snprintf(exp,size,"MAGIC WUPC1"); break; + case MIFARE_MAGICWUPC2: snprintf(exp,size,"MAGIC WUPC2"); break; + case MIFARE_MAGICWIPEC: snprintf(exp,size,"MAGIC WIPEC"); break; + case MIFARE_ULC_AUTH_1: snprintf(exp,size,"AUTH "); break; + case MIFARE_ULC_AUTH_2: snprintf(exp,size,"AUTH_ANSW"); break; + case MIFARE_ULEV1_AUTH: + if ( cmdsize == 7 ) + snprintf(exp,size,"PWD-AUTH KEY: 0x%02x%02x%02x%02x", cmd[1], cmd[2], cmd[3], cmd[4] ); + else + snprintf(exp,size,"PWD-AUTH"); + break; + case MIFARE_ULEV1_FASTREAD:{ + if ( cmdsize >=3 && cmd[2] <= 0xE6) + snprintf(exp,size,"READ RANGE (%d-%d)",cmd[1],cmd[2]); + else + snprintf(exp,size,"?"); + break; + } + case MIFARE_ULC_WRITE:{ + if ( cmd[1] < 0x21 ) + snprintf(exp,size,"WRITEBLOCK(%d)",cmd[1]); + else + snprintf(exp,size,"?"); + break; + } + case MIFARE_ULEV1_READ_CNT:{ + if ( cmd[1] < 5 ) + snprintf(exp,size,"READ CNT(%d)",cmd[1]); + else + snprintf(exp,size,"?"); + break; + } + case MIFARE_ULEV1_INCR_CNT:{ + if ( cmd[1] < 5 ) + snprintf(exp,size,"INCR(%d)",cmd[1]); + else + snprintf(exp,size,"?"); + break; + } + case MIFARE_ULEV1_READSIG: snprintf(exp,size,"READ_SIG"); break; + case MIFARE_ULEV1_CHECKTEAR: snprintf(exp,size,"CHK_TEARING(%d)",cmd[1]); break; + case MIFARE_ULEV1_VCSL: snprintf(exp,size,"VCSL"); break; + default: snprintf(exp,size,"?"); break; + } + return; +} + +void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, bool isResponse) { + switch(MifareAuthState) { + case masNt: + if (cmdsize == 4) { + snprintf(exp,size,"AUTH: nt"); + MifareAuthState = masNrAr; + printf("--ntok\n"); + return; + } else { + MifareAuthState = masError; + printf("--err %d\n", cmdsize); + } + break; + case masNrAr: + if (cmdsize == 8) { + snprintf(exp,size,"AUTH: nr ar"); + MifareAuthState = masAt; + return; + } else { + MifareAuthState = masError; + } + break; + case masAt: + if (cmdsize == 4) { + snprintf(exp,size,"AUTH: at"); + MifareAuthState = masData; + return; + } else { + MifareAuthState = masError; + } + break; + default: + break; + } + + if (!isResponse) + annotateIso14443a(exp, size, cmd, cmdsize); + +} diff --git a/client/cmdhflist.h b/client/cmdhflist.h index 138e5590..9bed08ab 100644 --- a/client/cmdhflist.h +++ b/client/cmdhflist.h @@ -7,5 +7,17 @@ //----------------------------------------------------------------------------- // Command: hf mf list. It shows data from arm buffer. //----------------------------------------------------------------------------- +#ifndef CMDHFLIST_H +#define CMDHFLIST_H + +#include +#include +#include + +extern uint8_t iso14443A_CRC_check(bool isResponse, uint8_t* data, uint8_t len); +extern uint8_t mifare_CRC_check(bool isResponse, uint8_t* data, uint8_t len); +extern void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize); +extern void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, bool isResponse); +#endif // CMDHFLIST From 6c30a244eb553ed342318ad2ec11a45b2db3a909 Mon Sep 17 00:00:00 2001 From: merlokk Date: Fri, 9 Feb 2018 19:32:17 +0200 Subject: [PATCH 179/310] added some get auth data --- client/cmdhflist.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/client/cmdhflist.c b/client/cmdhflist.c index b1d432ee..a91df26a 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -70,7 +70,6 @@ uint8_t mifare_CRC_check(bool isResponse, uint8_t* data, uint8_t len) default: return 2; } - } void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) @@ -173,22 +172,33 @@ void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) } void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, bool isResponse) { +// uint32_t uid; // UID + static uint32_t nt; // tag challenge +// uint32_t nt_enc; // encrypted tag challenge +// uint8_t nt_enc_par; // encrypted tag challenge parity + static uint32_t nr_enc; // encrypted reader challenge + static uint32_t ar_enc; // encrypted reader response +// uint8_t ar_enc_par; // encrypted reader response parity + static uint32_t at_enc; // encrypted tag response +// uint8_t at_enc_par; // encrypted tag response parity + switch(MifareAuthState) { case masNt: if (cmdsize == 4) { - snprintf(exp,size,"AUTH: nt"); + snprintf(exp,size,"AUTH: nt %s", (MifareAuthState == masData) ? "(enc)" : ""); MifareAuthState = masNrAr; - printf("--ntok\n"); + nt = bytes_to_num(cmd, cmdsize); return; } else { MifareAuthState = masError; - printf("--err %d\n", cmdsize); } break; case masNrAr: if (cmdsize == 8) { - snprintf(exp,size,"AUTH: nr ar"); + snprintf(exp,size,"AUTH: nr ar (enc)"); MifareAuthState = masAt; + nr_enc = bytes_to_num(cmd, cmdsize); + ar_enc = bytes_to_num(&cmd[3], cmdsize); return; } else { MifareAuthState = masError; @@ -196,8 +206,9 @@ void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, bool break; case masAt: if (cmdsize == 4) { - snprintf(exp,size,"AUTH: at"); + snprintf(exp,size,"AUTH: at (enc)"); MifareAuthState = masData; + at_enc = bytes_to_num(cmd, cmdsize); return; } else { MifareAuthState = masError; From fb30f5a1013a4e37280c72dda7b37052e317634f Mon Sep 17 00:00:00 2001 From: merlokk Date: Fri, 9 Feb 2018 19:40:39 +0200 Subject: [PATCH 180/310] added check reader/tag --- client/cmdhflist.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/cmdhflist.c b/client/cmdhflist.c index a91df26a..065cbde2 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -184,8 +184,8 @@ void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, bool switch(MifareAuthState) { case masNt: - if (cmdsize == 4) { - snprintf(exp,size,"AUTH: nt %s", (MifareAuthState == masData) ? "(enc)" : ""); + if (cmdsize == 4 && isResponse) { + snprintf(exp,size,"AUTH: nt %s", (nt) ? "(enc)" : ""); MifareAuthState = masNrAr; nt = bytes_to_num(cmd, cmdsize); return; @@ -194,7 +194,7 @@ void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, bool } break; case masNrAr: - if (cmdsize == 8) { + if (cmdsize == 8 && !isResponse) { snprintf(exp,size,"AUTH: nr ar (enc)"); MifareAuthState = masAt; nr_enc = bytes_to_num(cmd, cmdsize); @@ -205,7 +205,7 @@ void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, bool } break; case masAt: - if (cmdsize == 4) { + if (cmdsize == 4 && isResponse) { snprintf(exp,size,"AUTH: at (enc)"); MifareAuthState = masData; at_enc = bytes_to_num(cmd, cmdsize); From 61e96805add5df3ceade89eaafed4695fa31a422 Mon Sep 17 00:00:00 2001 From: marshmellow42 Date: Fri, 9 Feb 2018 15:49:55 -0500 Subject: [PATCH 181/310] add bitbang option to lf cmdread by setting delay to 0 we can use cmd binary to bitbang the antenna. note that the timing isn't perfect (especially on the off periods) but is fairly close. worst i've seen it off is 8us on a large off period. but i don't have the best test equipment... --- armsrc/lfops.c | 79 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 16 deletions(-) diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 4344742b..5e9fb193 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -29,6 +29,7 @@ void ModThenAcquireRawAdcSamples125k(uint32_t delay_off, uint32_t period_0, uint32_t period_1, uint8_t *command) { + StartTicks(); int divisor_used = 95; // 125 KHz // see if 'h' was specified @@ -43,36 +44,82 @@ void ModThenAcquireRawAdcSamples125k(uint32_t delay_off, uint32_t period_0, uint /* Make sure the tag is reset */ FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(2500); + WaitMS(2500); + //power on LFSetupFPGAForADC(sc.divisor, 1); // And a little more time for the tag to fully power up - SpinDelay(2000); - + WaitMS(2000); + // if delay_off = 0 then just bitbang 1 = antenna on 0 = off for respective periods. + bool bitbang = delay_off == 0; // now modulate the reader field - while(*command != '\0' && *command != ' ') { + + if (bitbang) { + //HACK it appears my loop and if statements take up about 7 us so adjust waits accordingly... + uint8_t hack_cnt = 7; + if (period_0 < hack_cnt || period_1 < hack_cnt) { + DbpString("Warning periods cannot be less than 7 in bit bang mode"); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_D_OFF(); + return; + } + //prime cmd_len to save time comparing strings while modulating + int cmd_len = 0; + while(command[cmd_len] != '\0' && command[cmd_len] != ' ') + cmd_len++; + + int counter = 0; + bool off = false; + for (counter = 0; counter < cmd_len; counter++) { + //while(*command != '\0' && *command != ' ') { + // if cmd = 0 then turn field off + if (command[counter] == '0') { + // if field already off leave alone (affects timing otherwise) + if (off == false) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_D_OFF(); + off = true; + } + // note we appear to take about 6us to switch over (or run the if statements/loop...) + WaitUS(period_0-hack_cnt); + // else if cmd = 1 then turn field on + } else { + // if field already on leave alone (affects timing otherwise) + if (off) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); + LED_D_ON(); + off = false; + } + // note we appear to take about 6us to switch over (or run the if statements/loop...) + WaitUS(period_1-hack_cnt); + } + } + } else { // old mode of cmd read using delay as off period + while(*command != '\0' && *command != ' ') { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_D_OFF(); + WaitUS(delay_off); + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, sc.divisor); + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); + LED_D_ON(); + if(*(command++) == '0') { + WaitUS(period_0); + } else { + WaitUS(period_1); + } + } FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); - SpinDelayUs(delay_off); + WaitUS(delay_off); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, sc.divisor); - - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); - LED_D_ON(); - if(*(command++) == '0') - SpinDelayUs(period_0); - else - SpinDelayUs(period_1); } - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LED_D_OFF(); - SpinDelayUs(delay_off); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, sc.divisor); FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); // now do the read DoAcquisition_config(false, 0); + // note leaves field on... (for future commands?) } /* blank r/w tag data stream From aadc6bf1e162316eab391ca2ca61d05bdac7db80 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Sat, 10 Feb 2018 19:03:29 +0200 Subject: [PATCH 182/310] combine all auth data to single struct and fill it --- client/cmdhf.c | 1 + client/cmdhflist.c | 46 ++++++++++++++++++++++++++++++---------------- client/cmdhflist.h | 14 ++++++++++++++ 3 files changed, 45 insertions(+), 16 deletions(-) diff --git a/client/cmdhf.c b/client/cmdhf.c index ae1b12a6..2557607b 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -543,6 +543,7 @@ int CmdHFList(const char *Cmd) PrintAndLog(" Start | End | Src | Data (! denotes parity error) | CRC | Annotation |"); PrintAndLog("------------|------------|-----|-----------------------------------------------------------------|-----|--------------------|"); + ClearAuthData(); while(tracepos < traceLen) { tracepos = printTraceLine(tracepos, traceLen, trace, protocol, showWaitCycles, markCRCBytes); diff --git a/client/cmdhflist.c b/client/cmdhflist.c index 065cbde2..ec710b2c 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -28,11 +28,19 @@ enum MifareAuthSeq { masNt, masNrAr, masAt, + masFirstData, masData, masDataNested, masError, }; static enum MifareAuthSeq MifareAuthState; +static TAuthData AuthData; + +void ClearAuthData() { + AuthData.uid = 0; + AuthData.nt = 0; + AuthData.first_auth = false; +} /** * @brief iso14443A_CRC_check Checks CRC in command or response @@ -63,6 +71,7 @@ uint8_t mifare_CRC_check(bool isResponse, uint8_t* data, uint8_t len) { switch(MifareAuthState) { case masNone: + case masFirstData: case masData: case masDataNested: case masError: @@ -172,22 +181,27 @@ void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) } void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, bool isResponse) { -// uint32_t uid; // UID - static uint32_t nt; // tag challenge -// uint32_t nt_enc; // encrypted tag challenge -// uint8_t nt_enc_par; // encrypted tag challenge parity - static uint32_t nr_enc; // encrypted reader challenge - static uint32_t ar_enc; // encrypted reader response -// uint8_t ar_enc_par; // encrypted reader response parity - static uint32_t at_enc; // encrypted tag response -// uint8_t at_enc_par; // encrypted tag response parity - + // get UID + if (MifareAuthState == masNone) { + if (cmdsize == 7 && cmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && cmd[1] == 0x70) { + ClearAuthData(); + AuthData.uid = bytes_to_num(&cmd[2], 4); + } + if (cmdsize == 7 && cmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2 && cmd[1] == 0x70) { + ClearAuthData(); + AuthData.uid = bytes_to_num(&cmd[2], 4); + } + } + switch(MifareAuthState) { case masNt: if (cmdsize == 4 && isResponse) { - snprintf(exp,size,"AUTH: nt %s", (nt) ? "(enc)" : ""); + snprintf(exp,size,"AUTH: nt %s", (AuthData.first_auth) ? "" : "(enc)"); MifareAuthState = masNrAr; - nt = bytes_to_num(cmd, cmdsize); + if (AuthData.first_auth) + AuthData.nt = bytes_to_num(cmd, cmdsize); + else + AuthData.nt_enc = bytes_to_num(cmd, cmdsize); return; } else { MifareAuthState = masError; @@ -197,8 +211,8 @@ void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, bool if (cmdsize == 8 && !isResponse) { snprintf(exp,size,"AUTH: nr ar (enc)"); MifareAuthState = masAt; - nr_enc = bytes_to_num(cmd, cmdsize); - ar_enc = bytes_to_num(&cmd[3], cmdsize); + AuthData.nr_enc = bytes_to_num(cmd, cmdsize); + AuthData.ar_enc = bytes_to_num(&cmd[3], cmdsize); return; } else { MifareAuthState = masError; @@ -207,8 +221,8 @@ void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, bool case masAt: if (cmdsize == 4 && isResponse) { snprintf(exp,size,"AUTH: at (enc)"); - MifareAuthState = masData; - at_enc = bytes_to_num(cmd, cmdsize); + MifareAuthState = masFirstData; + AuthData.at_enc = bytes_to_num(cmd, cmdsize); return; } else { MifareAuthState = masError; diff --git a/client/cmdhflist.h b/client/cmdhflist.h index 9bed08ab..6ec2f6fc 100644 --- a/client/cmdhflist.h +++ b/client/cmdhflist.h @@ -14,6 +14,20 @@ #include #include +typedef struct { + uint32_t uid; // UID + uint32_t nt; // tag challenge + uint32_t nt_enc; // encrypted tag challenge + uint8_t nt_enc_par; // encrypted tag challenge parity + uint32_t nr_enc; // encrypted reader challenge + uint32_t ar_enc; // encrypted reader response + uint8_t ar_enc_par; // encrypted reader response parity + uint32_t at_enc; // encrypted tag response + uint8_t at_enc_par; // encrypted tag response parity + bool first_auth; // is first authentication +} TAuthData; +extern void ClearAuthData(); + extern uint8_t iso14443A_CRC_check(bool isResponse, uint8_t* data, uint8_t len); extern uint8_t mifare_CRC_check(bool isResponse, uint8_t* data, uint8_t len); extern void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize); From a31f7f899b6d62c94bc236fce4330d6bbb8996e7 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Sat, 10 Feb 2018 19:06:34 +0200 Subject: [PATCH 183/310] small fix --- client/cmdhflist.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/client/cmdhflist.c b/client/cmdhflist.c index ec710b2c..8e7b7f6e 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -85,7 +85,10 @@ void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) { switch(cmd[0]) { - case ISO14443A_CMD_WUPA: snprintf(exp,size,"WUPA"); break; + case ISO14443A_CMD_WUPA: + snprintf(exp,size,"WUPA"); + MifareAuthState = masNone; + break; case ISO14443A_CMD_ANTICOLL_OR_SELECT:{ // 93 20 = Anticollision (usage: 9320 - answer: 4bytes UID+1byte UID-bytes-xor) // 93 70 = Select (usage: 9370+5bytes 9320 answer - answer: 1byte SAK) @@ -108,7 +111,10 @@ void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) snprintf(exp,size,"ANTICOLL-2"); break; } } - case ISO14443A_CMD_REQA: snprintf(exp,size,"REQA"); break; + case ISO14443A_CMD_REQA: + snprintf(exp,size,"REQA"); + MifareAuthState = masNone; + break; case ISO14443A_CMD_READBLOCK: snprintf(exp,size,"READBLOCK(%d)",cmd[1]); break; case ISO14443A_CMD_WRITEBLOCK: snprintf(exp,size,"WRITEBLOCK(%d)",cmd[1]); break; case ISO14443A_CMD_HALT: From b957bcd382bf718f766c0774839752eb99f668af Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Sat, 10 Feb 2018 19:19:56 +0200 Subject: [PATCH 184/310] added get parity --- client/cmdhf.c | 5 ++++- client/cmdhflist.c | 5 ++++- client/cmdhflist.h | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/client/cmdhf.c b/client/cmdhf.c index 2557607b..3256b69a 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -400,7 +400,7 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui EndOfTransmissionTimestamp = timestamp + duration; if (protocol == PROTO_MIFARE) - annotateMifare(explanation, sizeof(explanation), frame, data_len, isResponse); + annotateMifare(explanation, sizeof(explanation), frame, data_len, parityBytes, parity_len, isResponse); if(!isResponse) { @@ -430,6 +430,9 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui (j == num_lines-1) ? explanation : ""); } } + +// if (DecodeMifareData(frame, data_len, isResponse)) { +// }; if (is_last_record(tracepos, trace, traceLen)) return traceLen; diff --git a/client/cmdhflist.c b/client/cmdhflist.c index 8e7b7f6e..5ff3192b 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -186,7 +186,7 @@ void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) return; } -void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, bool isResponse) { +void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, uint8_t* parity, uint8_t paritysize, bool isResponse) { // get UID if (MifareAuthState == masNone) { if (cmdsize == 7 && cmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && cmd[1] == 0x70) { @@ -208,6 +208,7 @@ void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, bool AuthData.nt = bytes_to_num(cmd, cmdsize); else AuthData.nt_enc = bytes_to_num(cmd, cmdsize); + AuthData.nt_enc_par = parity[0]; return; } else { MifareAuthState = masError; @@ -219,6 +220,7 @@ void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, bool MifareAuthState = masAt; AuthData.nr_enc = bytes_to_num(cmd, cmdsize); AuthData.ar_enc = bytes_to_num(&cmd[3], cmdsize); + AuthData.ar_enc_par = parity[0] << 4; return; } else { MifareAuthState = masError; @@ -229,6 +231,7 @@ void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, bool snprintf(exp,size,"AUTH: at (enc)"); MifareAuthState = masFirstData; AuthData.at_enc = bytes_to_num(cmd, cmdsize); + AuthData.at_enc_par = parity[0]; return; } else { MifareAuthState = masError; diff --git a/client/cmdhflist.h b/client/cmdhflist.h index 6ec2f6fc..329df7db 100644 --- a/client/cmdhflist.h +++ b/client/cmdhflist.h @@ -31,7 +31,7 @@ extern void ClearAuthData(); extern uint8_t iso14443A_CRC_check(bool isResponse, uint8_t* data, uint8_t len); extern uint8_t mifare_CRC_check(bool isResponse, uint8_t* data, uint8_t len); extern void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize); -extern void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, bool isResponse); +extern void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, uint8_t* parity, uint8_t paritysize, bool isResponse); #endif // CMDHFLIST From 7b215d149ac4fd5709635111d86a2062fc7ac3ad Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Sat, 10 Feb 2018 20:28:30 +0200 Subject: [PATCH 185/310] correct 1st key calculation --- client/cmdhf.c | 10 ++++++-- client/cmdhflist.c | 62 ++++++++++++++++++++++++++++++++++++++++------ client/cmdhflist.h | 2 +- 3 files changed, 63 insertions(+), 11 deletions(-) diff --git a/client/cmdhf.c b/client/cmdhf.c index 3256b69a..9f3ff4e4 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -300,6 +300,8 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui uint8_t topaz_reader_command[9]; uint32_t timestamp, first_timestamp, EndOfTransmissionTimestamp; char explanation[30] = {0}; + uint8_t mfData[32] = {0}; + size_t mfDataLen = 0; if (tracepos + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) > traceLen) return traceLen; @@ -431,8 +433,12 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui } } -// if (DecodeMifareData(frame, data_len, isResponse)) { -// }; + if (DecodeMifareData(frame, data_len, isResponse, mfData, &mfDataLen)) { + PrintAndLog(" | | |%-64s | %s| %s", + sprint_hex(mfData, mfDataLen), + "", + (false) ? explanation : ""); + }; if (is_last_record(tracepos, trace, traceLen)) return traceLen; diff --git a/client/cmdhflist.c b/client/cmdhflist.c index 5ff3192b..0eaafa68 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -21,6 +21,8 @@ #include "iso14443crc.h" #include "parity.h" #include "protocols.h" +#include "crapto1/crapto1.h" +#include "mifarehost.h" enum MifareAuthSeq { @@ -39,7 +41,7 @@ static TAuthData AuthData; void ClearAuthData() { AuthData.uid = 0; AuthData.nt = 0; - AuthData.first_auth = false; + AuthData.first_auth = true; } /** @@ -189,11 +191,11 @@ void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, uint8_t* parity, uint8_t paritysize, bool isResponse) { // get UID if (MifareAuthState == masNone) { - if (cmdsize == 7 && cmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && cmd[1] == 0x70) { + if (cmdsize == 9 && cmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && cmd[1] == 0x70) { ClearAuthData(); AuthData.uid = bytes_to_num(&cmd[2], 4); } - if (cmdsize == 7 && cmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2 && cmd[1] == 0x70) { + if (cmdsize == 9 && cmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2 && cmd[1] == 0x70) { ClearAuthData(); AuthData.uid = bytes_to_num(&cmd[2], 4); } @@ -205,9 +207,9 @@ void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, uint8 snprintf(exp,size,"AUTH: nt %s", (AuthData.first_auth) ? "" : "(enc)"); MifareAuthState = masNrAr; if (AuthData.first_auth) - AuthData.nt = bytes_to_num(cmd, cmdsize); + AuthData.nt = bytes_to_num(cmd, 4); else - AuthData.nt_enc = bytes_to_num(cmd, cmdsize); + AuthData.nt_enc = bytes_to_num(cmd, 4); AuthData.nt_enc_par = parity[0]; return; } else { @@ -218,8 +220,8 @@ void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, uint8 if (cmdsize == 8 && !isResponse) { snprintf(exp,size,"AUTH: nr ar (enc)"); MifareAuthState = masAt; - AuthData.nr_enc = bytes_to_num(cmd, cmdsize); - AuthData.ar_enc = bytes_to_num(&cmd[3], cmdsize); + AuthData.nr_enc = bytes_to_num(cmd, 4); + AuthData.ar_enc = bytes_to_num(&cmd[4], 4); AuthData.ar_enc_par = parity[0] << 4; return; } else { @@ -230,7 +232,7 @@ void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, uint8 if (cmdsize == 4 && isResponse) { snprintf(exp,size,"AUTH: at (enc)"); MifareAuthState = masFirstData; - AuthData.at_enc = bytes_to_num(cmd, cmdsize); + AuthData.at_enc = bytes_to_num(cmd, 4); AuthData.at_enc_par = parity[0]; return; } else { @@ -245,3 +247,47 @@ void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, uint8 annotateIso14443a(exp, size, cmd, cmdsize); } + +bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, bool isResponse, uint8_t *mfData, size_t *mfDataLen) { + *mfDataLen = 0; + + if (cmdsize > 32) + return false; + + if (MifareAuthState == masFirstData) { + if (AuthData.first_auth) { + uint32_t ks2 = AuthData.ar_enc ^ prng_successor(AuthData.nt, 64); + uint32_t ks3 = AuthData.at_enc ^ prng_successor(AuthData.nt, 96); + struct Crypto1State *revstate = lfsr_recovery64(ks2, ks3); + lfsr_rollback_word(revstate, 0, 0); + lfsr_rollback_word(revstate, 0, 0); + lfsr_rollback_word(revstate, AuthData.nr_enc, 1); + lfsr_rollback_word(revstate, AuthData.uid ^ AuthData.nt, 0); + + uint64_t lfsr = 0; + crypto1_get_lfsr(revstate, &lfsr); + crypto1_destroy(revstate); +// LastKey = lfsr; + printf("uid:%x nt:%x ar_enc:%x at_enc:%x\n", AuthData.uid, AuthData.nt, AuthData.ar_enc, AuthData.at_enc); + printf("AUTH: probable key:%x%x Prng:%s ks2:%08x ks3:%08x\n", + (unsigned int)((lfsr & 0xFFFFFFFF00000000) >> 32), (unsigned int)(lfsr & 0xFFFFFFFF), + validate_prng_nonce(AuthData.nt) ? "WEAK": "HARDEND", + ks2, + ks3); + + AuthData.first_auth = false; + } else { + } + + + + MifareAuthState = masData; + return true; + } + + if (MifareAuthState == masData) { + } + + return *mfDataLen > 0; +} + diff --git a/client/cmdhflist.h b/client/cmdhflist.h index 329df7db..7d95cc1c 100644 --- a/client/cmdhflist.h +++ b/client/cmdhflist.h @@ -32,6 +32,6 @@ extern uint8_t iso14443A_CRC_check(bool isResponse, uint8_t* data, uint8_t len); extern uint8_t mifare_CRC_check(bool isResponse, uint8_t* data, uint8_t len); extern void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize); extern void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, uint8_t* parity, uint8_t paritysize, bool isResponse); - +extern bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, bool isResponse, uint8_t *mfData, size_t *mfDataLen); #endif // CMDHFLIST From 28ee794f59aac1fac7aa18bfbaa4050d48a90260 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Sat, 10 Feb 2018 20:41:56 +0200 Subject: [PATCH 186/310] fix appearance --- client/cmdhf.c | 6 ++++-- client/cmdhflist.c | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/client/cmdhf.c b/client/cmdhf.c index 9f3ff4e4..00b1aa8d 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -434,10 +434,12 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui } if (DecodeMifareData(frame, data_len, isResponse, mfData, &mfDataLen)) { - PrintAndLog(" | | |%-64s | %s| %s", + annotateIso14443a(explanation, sizeof(explanation), mfData, mfDataLen); + + PrintAndLog(" | * | dec |%-64s | %-4s| %s", sprint_hex(mfData, mfDataLen), "", - (false) ? explanation : ""); + (true) ? explanation : ""); }; if (is_last_record(tracepos, trace, traceLen)) return traceLen; diff --git a/client/cmdhflist.c b/client/cmdhflist.c index 0eaafa68..3dd03970 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -268,15 +268,15 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, bool isResponse, uint8_t *m crypto1_get_lfsr(revstate, &lfsr); crypto1_destroy(revstate); // LastKey = lfsr; - printf("uid:%x nt:%x ar_enc:%x at_enc:%x\n", AuthData.uid, AuthData.nt, AuthData.ar_enc, AuthData.at_enc); - printf("AUTH: probable key:%x%x Prng:%s ks2:%08x ks3:%08x\n", + PrintAndLog(" | * | key | probable key:%x%x Prng:%s ks2:%08x ks3:%08x | |", (unsigned int)((lfsr & 0xFFFFFFFF00000000) >> 32), (unsigned int)(lfsr & 0xFFFFFFFF), - validate_prng_nonce(AuthData.nt) ? "WEAK": "HARDEND", + validate_prng_nonce(AuthData.nt) ? "WEAK": "HARD", ks2, ks3); AuthData.first_auth = false; } else { + printf("uid:%x nt:%x ar_enc:%x at_enc:%x\n", AuthData.uid, AuthData.nt, AuthData.ar_enc, AuthData.at_enc); } From edd4c8385b3d6efb1467f5cee3c9ad5fe1491d11 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Sat, 10 Feb 2018 22:10:38 +0200 Subject: [PATCH 187/310] small fix util.c --- client/util.c | 1 + 1 file changed, 1 insertion(+) diff --git a/client/util.c b/client/util.c index bbc7f2cf..5dc9a455 100644 --- a/client/util.c +++ b/client/util.c @@ -116,6 +116,7 @@ void hex_to_buffer(const uint8_t *buf, const uint8_t *hex_data, const size_t hex char *tmp = (char *)buf; size_t i; + memset(tmp, 0x00, hex_max_len); int maxLen = ( hex_len > hex_max_len) ? hex_max_len : hex_len; From 747885a6ed9670c06a17140dd5385a9bac25c6b0 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Sat, 10 Feb 2018 22:33:32 +0200 Subject: [PATCH 188/310] hf list mf 1st auth works --- client/cmdhf.c | 10 +++++++--- client/cmdhflist.c | 44 +++++++++++++++++++++++++++++++++++--------- client/mifarehost.h | 2 ++ 3 files changed, 44 insertions(+), 12 deletions(-) diff --git a/client/cmdhf.c b/client/cmdhf.c index 00b1aa8d..06382abb 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -434,11 +434,15 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui } if (DecodeMifareData(frame, data_len, isResponse, mfData, &mfDataLen)) { - annotateIso14443a(explanation, sizeof(explanation), mfData, mfDataLen); - + memset(explanation, 0x00, sizeof(explanation)); + if (!isResponse) { + explanation[0] = '>'; + annotateIso14443a(&explanation[1], sizeof(explanation) - 1, mfData, mfDataLen); + } + uint8_t crcc = iso14443A_CRC_check(isResponse, mfData, mfDataLen); PrintAndLog(" | * | dec |%-64s | %-4s| %s", sprint_hex(mfData, mfDataLen), - "", + (crcc == 0 ? "!crc" : (crcc == 1 ? " ok " : " ")), (true) ? explanation : ""); }; diff --git a/client/cmdhflist.c b/client/cmdhflist.c index 3dd03970..43e0e562 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -30,9 +30,9 @@ enum MifareAuthSeq { masNt, masNrAr, masAt, + masAuthComplete, masFirstData, masData, - masDataNested, masError, }; static enum MifareAuthSeq MifareAuthState; @@ -73,9 +73,6 @@ uint8_t mifare_CRC_check(bool isResponse, uint8_t* data, uint8_t len) { switch(MifareAuthState) { case masNone: - case masFirstData: - case masData: - case masDataNested: case masError: return iso14443A_CRC_check(isResponse, data, len); default: @@ -231,7 +228,7 @@ void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, uint8 case masAt: if (cmdsize == 4 && isResponse) { snprintf(exp,size,"AUTH: at (enc)"); - MifareAuthState = masFirstData; + MifareAuthState = masAuthComplete; AuthData.at_enc = bytes_to_num(cmd, 4); AuthData.at_enc_par = parity[0]; return; @@ -243,14 +240,26 @@ void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, uint8 break; } - if (!isResponse) + if (!isResponse && ((MifareAuthState == masNone) || (MifareAuthState == masError))) annotateIso14443a(exp, size, cmd, cmdsize); } bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, bool isResponse, uint8_t *mfData, size_t *mfDataLen) { + static struct Crypto1State *traceCrypto1; + uint64_t mfLastKey; + *mfDataLen = 0; + if (MifareAuthState == masAuthComplete) { + if (traceCrypto1) { + crypto1_destroy(traceCrypto1); + } + + MifareAuthState = masFirstData; + return false; + } + if (cmdsize > 32) return false; @@ -267,7 +276,7 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, bool isResponse, uint8_t *m uint64_t lfsr = 0; crypto1_get_lfsr(revstate, &lfsr); crypto1_destroy(revstate); -// LastKey = lfsr; + mfLastKey = lfsr; PrintAndLog(" | * | key | probable key:%x%x Prng:%s ks2:%08x ks3:%08x | |", (unsigned int)((lfsr & 0xFFFFFFFF00000000) >> 32), (unsigned int)(lfsr & 0xFFFFFFFF), validate_prng_nonce(AuthData.nt) ? "WEAK": "HARD", @@ -275,17 +284,34 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, bool isResponse, uint8_t *m ks3); AuthData.first_auth = false; + + traceCrypto1 = lfsr_recovery64(ks2, ks3); } else { printf("uid:%x nt:%x ar_enc:%x at_enc:%x\n", AuthData.uid, AuthData.nt, AuthData.ar_enc, AuthData.at_enc); + + // check last used key + if (mfLastKey) { + + } + + // check default keys + + // nested + if (validate_prng_nonce(AuthData.nt)) { + } + + //hardnested } MifareAuthState = masData; - return true; } - if (MifareAuthState == masData) { + if (MifareAuthState == masData && traceCrypto1) { + memcpy(mfData, cmd, cmdsize); + mf_crypto1_decrypt(traceCrypto1, mfData, cmdsize, 0); + *mfDataLen = cmdsize; } return *mfDataLen > 0; diff --git a/client/mifarehost.h b/client/mifarehost.h index bef397bb..457a879f 100644 --- a/client/mifarehost.h +++ b/client/mifarehost.h @@ -14,6 +14,7 @@ #include #include #include "data.h" +#include "crapto1/crapto1.h" // defaults // timeout in units. (ms * 106)/10 or us*0.0106 @@ -62,5 +63,6 @@ extern int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t extern int mfCIdentify(); extern int DetectClassicPrng(void); extern bool validate_prng_nonce(uint32_t nonce); +extern void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len, bool isEncrypted); #endif From c6a886fb4f25b8925956a267ed3d2f60455adf4a Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Sat, 10 Feb 2018 23:06:01 +0200 Subject: [PATCH 189/310] added mock of key check --- client/cmdhflist.c | 13 ++++++++++++- client/cmdhflist.h | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/client/cmdhflist.c b/client/cmdhflist.c index 43e0e562..30249579 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -23,6 +23,7 @@ #include "protocols.h" #include "crapto1/crapto1.h" #include "mifarehost.h" +#include "mifaredefault.h" enum MifareAuthSeq { @@ -291,10 +292,17 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, bool isResponse, uint8_t *m // check last used key if (mfLastKey) { - + if (NestedCheckKey(mfLastKey, &AuthData, cmd, cmdsize)) { + }; } // check default keys + for (int defaultKeyCounter = 0; defaultKeyCounter < MifareDefaultKeysSize; defaultKeyCounter++){ + if (NestedCheckKey(MifareDefaultKeys[defaultKeyCounter], &AuthData, cmd, cmdsize)) { + + break; + }; + } // nested if (validate_prng_nonce(AuthData.nt)) { @@ -317,3 +325,6 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, bool isResponse, uint8_t *m return *mfDataLen > 0; } +bool NestedCheckKey(uint64_t key, TAuthData *ad, uint8_t *cmd, uint8_t cmdsize) { + return false; +} \ No newline at end of file diff --git a/client/cmdhflist.h b/client/cmdhflist.h index 7d95cc1c..d0298de5 100644 --- a/client/cmdhflist.h +++ b/client/cmdhflist.h @@ -33,5 +33,6 @@ extern uint8_t mifare_CRC_check(bool isResponse, uint8_t* data, uint8_t len); extern void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize); extern void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, uint8_t* parity, uint8_t paritysize, bool isResponse); extern bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, bool isResponse, uint8_t *mfData, size_t *mfDataLen); +extern bool NestedCheckKey(uint64_t key, TAuthData *ad, uint8_t *cmd, uint8_t cmdsize); #endif // CMDHFLIST From dca8220f729d7855b12c89ecb402958ce6dbc81b Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Sat, 10 Feb 2018 23:25:28 +0200 Subject: [PATCH 190/310] mock of key checking --- client/cmdhflist.c | 55 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/client/cmdhflist.c b/client/cmdhflist.c index 30249579..dc3d9640 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -248,7 +248,7 @@ void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, uint8 bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, bool isResponse, uint8_t *mfData, size_t *mfDataLen) { static struct Crypto1State *traceCrypto1; - uint64_t mfLastKey; + static uint64_t mfLastKey; *mfDataLen = 0; @@ -325,6 +325,57 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, bool isResponse, uint8_t *m return *mfDataLen > 0; } +bool NTParityChk(TAuthData *ad, uint32_t ntx) { + if ( + (oddparity8(ntx >> 8 & 0xff) ^ (ntx & 0x01) ^ ((ad->nt_enc_par >> 5) & 0x01) ^ (ad->nt_enc & 0x01)) || + (oddparity8(ntx >> 16 & 0xff) ^ (ntx >> 8 & 0x01) ^ ((ad->nt_enc_par >> 6) & 0x01) ^ (ad->nt_enc >> 8 & 0x01)) || + (oddparity8(ntx >> 24 & 0xff) ^ (ntx >> 16 & 0x01) ^ ((ad->nt_enc_par >> 7) & 0x01) ^ (ad->nt_enc >> 16 & 0x01)) + ) + return false; + + uint32_t ar = prng_successor(ntx, 64); + if ( + (oddparity8(ar >> 8 & 0xff) ^ (ar & 0x01) ^ ((ad->ar_enc_par >> 5) & 0x01) ^ (ad->ar_enc & 0x01)) || + (oddparity8(ar >> 16 & 0xff) ^ (ar >> 8 & 0x01) ^ ((ad->ar_enc_par >> 6) & 0x01) ^ (ad->ar_enc >> 8 & 0x01)) || + (oddparity8(ar >> 24 & 0xff) ^ (ar >> 16 & 0x01) ^ ((ad->ar_enc_par >> 7) & 0x01) ^ (ad->ar_enc >> 16 & 0x01)) + ) + return false; + + uint32_t at = prng_successor(ntx, 96); + if ( + (oddparity8(ar & 0xff) ^ (at >> 24 & 0x01) ^ ((ad->ar_enc_par >> 4) & 0x01) ^ (ad->at_enc >> 24 & 0x01)) || + (oddparity8(at >> 8 & 0xff) ^ (at & 0x01) ^ ((ad->at_enc_par >> 5) & 0x01) ^ (ad->at_enc & 0x01)) || + (oddparity8(at >> 16 & 0xff) ^ (at >> 8 & 0x01) ^ ((ad->at_enc_par >> 6) & 0x01) ^ (ad->at_enc >> 8 & 0x01)) || + (oddparity8(at >> 24 & 0xff) ^ (at >> 16 & 0x01) ^ ((ad->at_enc_par >> 7) & 0x01) ^ (ad->at_enc >> 16 & 0x01)) + ) + return false; + + return true; +} + bool NestedCheckKey(uint64_t key, TAuthData *ad, uint8_t *cmd, uint8_t cmdsize) { - return false; + uint8_t buf[32] = {0}; + struct Crypto1State *pcs; + + pcs = crypto1_create(key); + uint32_t nt1 = crypto1_word(pcs, ad->nt_enc ^ ad->uid, 1) ^ ad->nt_enc; + uint32_t ar = prng_successor(nt1, 64); + uint32_t at = prng_successor(nt1, 96); + printf("key> nested auth uid: %08x nt: %08x nt_parity: %s ar: %08x at: %08x\n", ad->uid, nt1, printBitsPar(&ad->nt_enc_par, 4), ar, at); + uint32_t nr1 = crypto1_word(pcs, ad->nr_enc, 1) ^ ad->nr_enc; + uint32_t ar1 = crypto1_word(pcs, 0, 0) ^ ad->ar_enc; + uint32_t at1 = crypto1_word(pcs, 0, 0) ^ ad->at_enc; + printf("key> the same key test. nr1: %08x ar1: %08x at1: %08x \n", nr1, ar1, at1); + + if (NTParityChk(ad, nt1)) + printf("key> the same key test OK. key=%x%x\n", (unsigned int)((key & 0xFFFFFFFF00000000) >> 32), (unsigned int)(key & 0xFFFFFFFF)); + else { + printf("key> the same key test. check nt parity error.\n"); + return false; + } + + memcpy(buf, cmd, cmdsize); + mf_crypto1_decrypt(pcs, buf, cmdsize, 0); + + return CheckCrc14443(CRC_14443_A, buf, cmdsize); } \ No newline at end of file From e0158d33736110e3fbcdd8331acb7ce747ee9ba2 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Sat, 10 Feb 2018 23:28:15 +0200 Subject: [PATCH 191/310] added destroy --- client/cmdhflist.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/cmdhflist.c b/client/cmdhflist.c index dc3d9640..b1bd8a7a 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -377,5 +377,7 @@ bool NestedCheckKey(uint64_t key, TAuthData *ad, uint8_t *cmd, uint8_t cmdsize) memcpy(buf, cmd, cmdsize); mf_crypto1_decrypt(pcs, buf, cmdsize, 0); + crypto1_destroy(pcs); + return CheckCrc14443(CRC_14443_A, buf, cmdsize); } \ No newline at end of file From 7bea15816e2c9c4277df4670ab841ca5694c61d0 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Sun, 11 Feb 2018 00:28:23 +0200 Subject: [PATCH 192/310] added some cone in nested. not tested. --- client/cmdhflist.c | 38 ++++++++++++++++++++++++++++++++------ client/cmdhflist.h | 1 + 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/client/cmdhflist.c b/client/cmdhflist.c index b1bd8a7a..06bd8d18 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -297,18 +297,44 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, bool isResponse, uint8_t *m } // check default keys - for (int defaultKeyCounter = 0; defaultKeyCounter < MifareDefaultKeysSize; defaultKeyCounter++){ - if (NestedCheckKey(MifareDefaultKeys[defaultKeyCounter], &AuthData, cmd, cmdsize)) { - - break; - }; + if (!traceCrypto1) { + for (int defaultKeyCounter = 0; defaultKeyCounter < MifareDefaultKeysSize; defaultKeyCounter++){ + if (NestedCheckKey(MifareDefaultKeys[defaultKeyCounter], &AuthData, cmd, cmdsize)) { + + break; + }; + } } // nested - if (validate_prng_nonce(AuthData.nt)) { + if (!traceCrypto1 && validate_prng_nonce(AuthData.nt)) { + uint32_t ntx = prng_successor(AuthData.nt, 90); + for (int i = 0; i < 16383; i++) { + ntx = prng_successor(ntx, 1); + if (NTParityChk(&AuthData, ntx)){ + + uint32_t ks2 = AuthData.ar_enc ^ prng_successor(ntx, 64); + uint32_t ks3 = AuthData.at_enc ^ prng_successor(ntx, 96); + struct Crypto1State *pcs = lfsr_recovery64(ks2, ks3); + memcpy(mfData, cmd, cmdsize); + mf_crypto1_decrypt(pcs, mfData, cmdsize, 0); + + crypto1_destroy(pcs); + if (CheckCrc14443(CRC_14443_A, mfData, cmdsize)) { + traceCrypto1 = lfsr_recovery64(ks2, ks3); + break; + } + } + } + if (traceCrypto1) + printf("key> nt=%08x nonce distance=%d \n", ntx, nonce_distance(AuthData.nt, ntx)); + else + printf("key> don't have any valid nt( \n"); } //hardnested + if (!traceCrypto1) { + } } diff --git a/client/cmdhflist.h b/client/cmdhflist.h index d0298de5..aa037658 100644 --- a/client/cmdhflist.h +++ b/client/cmdhflist.h @@ -33,6 +33,7 @@ extern uint8_t mifare_CRC_check(bool isResponse, uint8_t* data, uint8_t len); extern void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize); extern void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, uint8_t* parity, uint8_t paritysize, bool isResponse); extern bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, bool isResponse, uint8_t *mfData, size_t *mfDataLen); +extern bool NTParityChk(TAuthData *ad, uint32_t ntx); extern bool NestedCheckKey(uint64_t key, TAuthData *ad, uint8_t *cmd, uint8_t cmdsize); #endif // CMDHFLIST From 779d9a0e90a20392785adbaa4974cafaa27005a8 Mon Sep 17 00:00:00 2001 From: marshmellow42 Date: Sat, 10 Feb 2018 17:30:32 -0500 Subject: [PATCH 193/310] reconfigure lf cmdread ... to use lf config settings instead of it's own settings. (now allows full options of lf config...) also it will now run `data samples` when the command completes making it not necessary to run manually... note: adjusted client wait message as it was confusing. --- armsrc/lfops.c | 48 ++++++++++++++++++++++++++---------------------- client/cmdlf.c | 30 +++++++++++------------------- client/cmdmain.c | 2 +- 3 files changed, 38 insertions(+), 42 deletions(-) diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 5e9fb193..c7a7a59d 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -4,7 +4,7 @@ // the license. //----------------------------------------------------------------------------- // Miscellaneous routines for low frequency tag operations. -// Tags supported here so far are Texas Instruments (TI), HID +// Tags supported here so far are Texas Instruments (TI), HID, EM4x05, EM410x // Also routines for raw mode reading/simulating of LF waveform //----------------------------------------------------------------------------- @@ -28,17 +28,12 @@ */ void ModThenAcquireRawAdcSamples125k(uint32_t delay_off, uint32_t period_0, uint32_t period_1, uint8_t *command) { - + // start timer StartTicks(); - int divisor_used = 95; // 125 KHz - // see if 'h' was specified - if (command[strlen((char *) command) - 1] == 'h') - divisor_used = 88; // 134.8 KHz - - sample_config sc = { 0,0,1, divisor_used, 0}; - setSamplingConfig(&sc); - //clear read buffer + // use lf config settings + sample_config *sc = getSamplingConfig(); + // clear read buffer BigBuf_Clear_keep_EM(); /* Make sure the tag is reset */ @@ -46,8 +41,8 @@ void ModThenAcquireRawAdcSamples125k(uint32_t delay_off, uint32_t period_0, uint FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); WaitMS(2500); - //power on - LFSetupFPGAForADC(sc.divisor, 1); + // power on + LFSetupFPGAForADC(sc->divisor, 1); // And a little more time for the tag to fully power up WaitMS(2000); @@ -56,15 +51,21 @@ void ModThenAcquireRawAdcSamples125k(uint32_t delay_off, uint32_t period_0, uint // now modulate the reader field if (bitbang) { - //HACK it appears my loop and if statements take up about 7 us so adjust waits accordingly... + // HACK it appears the loop and if statements take up about 7us so adjust waits accordingly... uint8_t hack_cnt = 7; if (period_0 < hack_cnt || period_1 < hack_cnt) { - DbpString("Warning periods cannot be less than 7 in bit bang mode"); + DbpString("Warning periods cannot be less than 7us in bit bang mode"); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); return; } - //prime cmd_len to save time comparing strings while modulating + + // hack2 needed--- it appears to take about 8-16us to turn the antenna back on + // leading to ~ 1 to 2 125khz samples extra in every off period + // so we should test for last 0 before next 1 and reduce period_0 by this extra amount... + // but is this time different for every antenna or other hw builds??? more testing needed + + // prime cmd_len to save time comparing strings while modulating int cmd_len = 0; while(command[cmd_len] != '\0' && command[cmd_len] != ' ') cmd_len++; @@ -72,7 +73,6 @@ void ModThenAcquireRawAdcSamples125k(uint32_t delay_off, uint32_t period_0, uint int counter = 0; bool off = false; for (counter = 0; counter < cmd_len; counter++) { - //while(*command != '\0' && *command != ' ') { // if cmd = 0 then turn field off if (command[counter] == '0') { // if field already off leave alone (affects timing otherwise) @@ -81,17 +81,17 @@ void ModThenAcquireRawAdcSamples125k(uint32_t delay_off, uint32_t period_0, uint LED_D_OFF(); off = true; } - // note we appear to take about 6us to switch over (or run the if statements/loop...) + // note we appear to take about 7us to switch over (or run the if statements/loop...) WaitUS(period_0-hack_cnt); // else if cmd = 1 then turn field on } else { // if field already on leave alone (affects timing otherwise) if (off) { - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); LED_D_ON(); off = false; } - // note we appear to take about 6us to switch over (or run the if statements/loop...) + // note we appear to take about 7us to switch over (or run the if statements/loop...) WaitUS(period_1-hack_cnt); } } @@ -100,7 +100,7 @@ void ModThenAcquireRawAdcSamples125k(uint32_t delay_off, uint32_t period_0, uint FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); WaitUS(delay_off); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, sc.divisor); + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, sc->divisor); FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); LED_D_ON(); if(*(command++) == '0') { @@ -112,14 +112,18 @@ void ModThenAcquireRawAdcSamples125k(uint32_t delay_off, uint32_t period_0, uint FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); WaitUS(delay_off); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, sc.divisor); + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, sc->divisor); } FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); // now do the read DoAcquisition_config(false, 0); - // note leaves field on... (for future commands?) + + // Turn off antenna + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + // tell client we are done + cmd_send(CMD_ACK,0,0,0,0,0); } /* blank r/w tag data stream diff --git a/client/cmdlf.c b/client/cmdlf.c index ef9c3cbb..42f73fa1 100644 --- a/client/cmdlf.c +++ b/client/cmdlf.c @@ -54,26 +54,24 @@ static int CmdHelp(const char *Cmd); int usage_lf_cmdread(void) { - PrintAndLog("Usage: lf cmdread d z o c [H] "); + PrintAndLog("Usage: lf cmdread d z o c "); PrintAndLog("Options: "); PrintAndLog(" h This help"); - PrintAndLog(" L Low frequency (125 KHz)"); - PrintAndLog(" H High frequency (134 KHz)"); - PrintAndLog(" d delay OFF period"); - PrintAndLog(" z time period ZERO"); - PrintAndLog(" o time period ONE"); + PrintAndLog(" d delay OFF period between bits (0 for bitbang mode)"); + PrintAndLog(" z time period ZERO (antenna off in bitbang mode)"); + PrintAndLog(" o time period ONE (antenna on in bitbang mode)"); PrintAndLog(" c Command bytes"); PrintAndLog(" ************* All periods in microseconds"); + PrintAndLog(" ************* Use lf config to configure options."); PrintAndLog("Examples:"); PrintAndLog(" lf cmdread d 80 z 100 o 200 c 11000"); - PrintAndLog(" lf cmdread d 80 z 100 o 100 c 11000 H"); + PrintAndLog(" lf cmdread d 80 z 100 o 100 c 11000"); return 0; } /* send a command before reading */ int CmdLFCommandRead(const char *Cmd) { - static char dummy[3] = {0x20,0x00,0x00}; UsbCommand c = {CMD_MOD_THEN_ACQUIRE_RAW_ADC_SAMPLES_125K}; bool errors = false; //uint8_t divisor = 95; //125khz @@ -84,14 +82,6 @@ int CmdLFCommandRead(const char *Cmd) { case 'h': return usage_lf_cmdread(); - case 'H': - //divisor = 88; - dummy[1]='h'; - cmdp++; - break; - case 'L': - cmdp++; - break; case 'c': param_getstr(Cmd, cmdp+1, (char *)&c.d.asBytes, sizeof(c.d.asBytes)); cmdp+=2; @@ -121,11 +111,13 @@ int CmdLFCommandRead(const char *Cmd) //Validations if(errors) return usage_lf_cmdread(); - // in case they specified 'H' - strcpy((char *)&c.d.asBytes + strlen((char *)c.d.asBytes), dummy); - clearCommandBuffer(); SendCommand(&c); + + WaitForResponse(CMD_ACK,NULL); + getSamples(0, true); + + return 0; } diff --git a/client/cmdmain.c b/client/cmdmain.c index 719617fd..8d9313f9 100644 --- a/client/cmdmain.c +++ b/client/cmdmain.c @@ -162,7 +162,7 @@ bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeo } if (msclock() - start_time > 2000 && show_warning) { PrintAndLog("Waiting for a response from the proxmark..."); - PrintAndLog("Don't forget to cancel its operation first by pressing on the button"); + PrintAndLog("You can cancel this operation by pressing the pm3 button"); show_warning = false; } } From e01bc7942cb7dc87350fd095388be58560f32f9a Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Sun, 11 Feb 2018 00:40:24 +0200 Subject: [PATCH 194/310] move ks to authdata --- client/cmdhflist.c | 18 +++++++++++------- client/cmdhflist.h | 2 ++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/client/cmdhflist.c b/client/cmdhflist.c index 06bd8d18..1701c13a 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -43,6 +43,8 @@ void ClearAuthData() { AuthData.uid = 0; AuthData.nt = 0; AuthData.first_auth = true; + AuthData.ks2 = 0; + AuthData.ks3 = 0; } /** @@ -266,9 +268,9 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, bool isResponse, uint8_t *m if (MifareAuthState == masFirstData) { if (AuthData.first_auth) { - uint32_t ks2 = AuthData.ar_enc ^ prng_successor(AuthData.nt, 64); - uint32_t ks3 = AuthData.at_enc ^ prng_successor(AuthData.nt, 96); - struct Crypto1State *revstate = lfsr_recovery64(ks2, ks3); + AuthData.ks2 = AuthData.ar_enc ^ prng_successor(AuthData.nt, 64); + AuthData.ks3 = AuthData.at_enc ^ prng_successor(AuthData.nt, 96); + struct Crypto1State *revstate = lfsr_recovery64(AuthData.ks2, AuthData.ks3); lfsr_rollback_word(revstate, 0, 0); lfsr_rollback_word(revstate, 0, 0); lfsr_rollback_word(revstate, AuthData.nr_enc, 1); @@ -281,12 +283,12 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, bool isResponse, uint8_t *m PrintAndLog(" | * | key | probable key:%x%x Prng:%s ks2:%08x ks3:%08x | |", (unsigned int)((lfsr & 0xFFFFFFFF00000000) >> 32), (unsigned int)(lfsr & 0xFFFFFFFF), validate_prng_nonce(AuthData.nt) ? "WEAK": "HARD", - ks2, - ks3); + AuthData.ks2, + AuthData.ks3); AuthData.first_auth = false; - traceCrypto1 = lfsr_recovery64(ks2, ks3); + traceCrypto1 = lfsr_recovery64(AuthData.ks2, AuthData.ks3); } else { printf("uid:%x nt:%x ar_enc:%x at_enc:%x\n", AuthData.uid, AuthData.nt, AuthData.ar_enc, AuthData.at_enc); @@ -321,7 +323,9 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, bool isResponse, uint8_t *m crypto1_destroy(pcs); if (CheckCrc14443(CRC_14443_A, mfData, cmdsize)) { - traceCrypto1 = lfsr_recovery64(ks2, ks3); + AuthData.ks2 = ks2; + AuthData.ks3 = ks3; + traceCrypto1 = lfsr_recovery64(AuthData.ks2, AuthData.ks3); break; } } diff --git a/client/cmdhflist.h b/client/cmdhflist.h index aa037658..76b57392 100644 --- a/client/cmdhflist.h +++ b/client/cmdhflist.h @@ -25,6 +25,8 @@ typedef struct { uint32_t at_enc; // encrypted tag response uint8_t at_enc_par; // encrypted tag response parity bool first_auth; // is first authentication + uint32_t ks2; // ar ^ ar_enc + uint32_t ks3; // at ^ at_enc } TAuthData; extern void ClearAuthData(); From 856da9a1c081abedef9af96220723eb1031f49e2 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Sun, 11 Feb 2018 00:44:24 +0200 Subject: [PATCH 195/310] added cipherstream recovery --- client/cmdhflist.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/client/cmdhflist.c b/client/cmdhflist.c index 1701c13a..64ffed5c 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -295,6 +295,7 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, bool isResponse, uint8_t *m // check last used key if (mfLastKey) { if (NestedCheckKey(mfLastKey, &AuthData, cmd, cmdsize)) { + traceCrypto1 = lfsr_recovery64(AuthData.ks2, AuthData.ks3); }; } @@ -302,7 +303,7 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, bool isResponse, uint8_t *m if (!traceCrypto1) { for (int defaultKeyCounter = 0; defaultKeyCounter < MifareDefaultKeysSize; defaultKeyCounter++){ if (NestedCheckKey(MifareDefaultKeys[defaultKeyCounter], &AuthData, cmd, cmdsize)) { - + traceCrypto1 = lfsr_recovery64(AuthData.ks2, AuthData.ks3); break; }; } @@ -409,5 +410,11 @@ bool NestedCheckKey(uint64_t key, TAuthData *ad, uint8_t *cmd, uint8_t cmdsize) crypto1_destroy(pcs); - return CheckCrc14443(CRC_14443_A, buf, cmdsize); + if(CheckCrc14443(CRC_14443_A, buf, cmdsize)) { + AuthData.ks2 = AuthData.ar_enc ^ ar; + AuthData.ks3 = AuthData.at_enc ^ at; + return true; + } else { + return false; + } } \ No newline at end of file From bf52d74adcfbe3947970d9c2984c36f77af0c9c7 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Sun, 11 Feb 2018 00:45:12 +0200 Subject: [PATCH 196/310] small fix --- client/cmdhflist.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/cmdhflist.c b/client/cmdhflist.c index 64ffed5c..7b3fdca5 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -417,4 +417,4 @@ bool NestedCheckKey(uint64_t key, TAuthData *ad, uint8_t *cmd, uint8_t cmdsize) } else { return false; } -} \ No newline at end of file +} From 2d7bdee30b93e3669850b9c16eed23174cf07b65 Mon Sep 17 00:00:00 2001 From: merlokk Date: Mon, 12 Feb 2018 18:53:12 +0200 Subject: [PATCH 197/310] added parity check of deciphered command --- client/cmdhf.c | 2 +- client/cmdhflist.c | 88 +++++++++++++++++++++++++++++++++------------- client/cmdhflist.h | 5 +-- 3 files changed, 68 insertions(+), 27 deletions(-) diff --git a/client/cmdhf.c b/client/cmdhf.c index 06382abb..ffbc4515 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -433,7 +433,7 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui } } - if (DecodeMifareData(frame, data_len, isResponse, mfData, &mfDataLen)) { + if (DecodeMifareData(frame, data_len, parityBytes, isResponse, mfData, &mfDataLen)) { memset(explanation, 0x00, sizeof(explanation)); if (!isResponse) { explanation[0] = '>'; diff --git a/client/cmdhflist.c b/client/cmdhflist.c index 7b3fdca5..a9691aa3 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -89,7 +89,6 @@ void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) { case ISO14443A_CMD_WUPA: snprintf(exp,size,"WUPA"); - MifareAuthState = masNone; break; case ISO14443A_CMD_ANTICOLL_OR_SELECT:{ // 93 20 = Anticollision (usage: 9320 - answer: 4bytes UID+1byte UID-bytes-xor) @@ -115,7 +114,6 @@ void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) } case ISO14443A_CMD_REQA: snprintf(exp,size,"REQA"); - MifareAuthState = masNone; break; case ISO14443A_CMD_READBLOCK: snprintf(exp,size,"READBLOCK(%d)",cmd[1]); break; case ISO14443A_CMD_WRITEBLOCK: snprintf(exp,size,"WRITEBLOCK(%d)",cmd[1]); break; @@ -189,6 +187,17 @@ void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) } void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, uint8_t* parity, uint8_t paritysize, bool isResponse) { + if (!isResponse && cmdsize == 1) { + switch(cmd[0]) { + case ISO14443A_CMD_WUPA: + case ISO14443A_CMD_REQA: + MifareAuthState = masNone; + break; + default: + break; + } + } + // get UID if (MifareAuthState == masNone) { if (cmdsize == 9 && cmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && cmd[1] == 0x70) { @@ -248,7 +257,7 @@ void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, uint8 } -bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, bool isResponse, uint8_t *mfData, size_t *mfDataLen) { +bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isResponse, uint8_t *mfData, size_t *mfDataLen) { static struct Crypto1State *traceCrypto1; static uint64_t mfLastKey; @@ -281,7 +290,8 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, bool isResponse, uint8_t *m crypto1_destroy(revstate); mfLastKey = lfsr; PrintAndLog(" | * | key | probable key:%x%x Prng:%s ks2:%08x ks3:%08x | |", - (unsigned int)((lfsr & 0xFFFFFFFF00000000) >> 32), (unsigned int)(lfsr & 0xFFFFFFFF), + (unsigned int)((lfsr & 0xFFFFFFFF00000000) >> 32), + (unsigned int)(lfsr & 0xFFFFFFFF), validate_prng_nonce(AuthData.nt) ? "WEAK": "HARD", AuthData.ks2, AuthData.ks3); @@ -290,19 +300,29 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, bool isResponse, uint8_t *m traceCrypto1 = lfsr_recovery64(AuthData.ks2, AuthData.ks3); } else { - printf("uid:%x nt:%x ar_enc:%x at_enc:%x\n", AuthData.uid, AuthData.nt, AuthData.ar_enc, AuthData.at_enc); - // check last used key if (mfLastKey) { - if (NestedCheckKey(mfLastKey, &AuthData, cmd, cmdsize)) { - traceCrypto1 = lfsr_recovery64(AuthData.ks2, AuthData.ks3); + if (NestedCheckKey(mfLastKey, &AuthData, cmd, cmdsize, parity)) { + PrintAndLog(" | * | key | last used key:%x%x ks2:%08x ks3:%08x | |", + (unsigned int)((mfLastKey & 0xFFFFFFFF00000000) >> 32), + (unsigned int)(mfLastKey & 0xFFFFFFFF), + AuthData.ks2, + AuthData.ks3); + + traceCrypto1 = lfsr_recovery64(AuthData.ks2, AuthData.ks3); }; } // check default keys if (!traceCrypto1) { for (int defaultKeyCounter = 0; defaultKeyCounter < MifareDefaultKeysSize; defaultKeyCounter++){ - if (NestedCheckKey(MifareDefaultKeys[defaultKeyCounter], &AuthData, cmd, cmdsize)) { + if (NestedCheckKey(MifareDefaultKeys[defaultKeyCounter], &AuthData, cmd, cmdsize, parity)) { + PrintAndLog(" | * | key | default key:%x%x ks2:%08x ks3:%08x | |", + (unsigned int)((MifareDefaultKeys[defaultKeyCounter] & 0xFFFFFFFF00000000) >> 32), + (unsigned int)(MifareDefaultKeys[defaultKeyCounter] & 0xFFFFFFFF), + AuthData.ks2, + AuthData.ks3); + traceCrypto1 = lfsr_recovery64(AuthData.ks2, AuthData.ks3); break; }; @@ -311,6 +331,7 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, bool isResponse, uint8_t *m // nested if (!traceCrypto1 && validate_prng_nonce(AuthData.nt)) { +printf("nested. uid:%x nt:%x ar_enc:%x at_enc:%x\n", AuthData.uid, AuthData.nt, AuthData.ar_enc, AuthData.at_enc); uint32_t ntx = prng_successor(AuthData.nt, 90); for (int i = 0; i < 16383; i++) { ntx = prng_successor(ntx, 1); @@ -339,6 +360,7 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, bool isResponse, uint8_t *m //hardnested if (!traceCrypto1) { + printf("hardnested not implemented. uid:%x nt:%x ar_enc:%x at_enc:%x\n", AuthData.uid, AuthData.nt, AuthData.ar_enc, AuthData.at_enc); } } @@ -384,37 +406,55 @@ bool NTParityChk(TAuthData *ad, uint32_t ntx) { return true; } -bool NestedCheckKey(uint64_t key, TAuthData *ad, uint8_t *cmd, uint8_t cmdsize) { +bool NestedCheckKey(uint64_t key, TAuthData *ad, uint8_t *cmd, uint8_t cmdsize, uint8_t *parity) { uint8_t buf[32] = {0}; struct Crypto1State *pcs; + AuthData.ks2 = 0; + AuthData.ks3 = 0; + pcs = crypto1_create(key); uint32_t nt1 = crypto1_word(pcs, ad->nt_enc ^ ad->uid, 1) ^ ad->nt_enc; uint32_t ar = prng_successor(nt1, 64); uint32_t at = prng_successor(nt1, 96); - printf("key> nested auth uid: %08x nt: %08x nt_parity: %s ar: %08x at: %08x\n", ad->uid, nt1, printBitsPar(&ad->nt_enc_par, 4), ar, at); - uint32_t nr1 = crypto1_word(pcs, ad->nr_enc, 1) ^ ad->nr_enc; + + crypto1_word(pcs, ad->nr_enc, 1); +// uint32_t nr1 = crypto1_word(pcs, ad->nr_enc, 1) ^ ad->nr_enc; // if needs deciphered nr uint32_t ar1 = crypto1_word(pcs, 0, 0) ^ ad->ar_enc; uint32_t at1 = crypto1_word(pcs, 0, 0) ^ ad->at_enc; - printf("key> the same key test. nr1: %08x ar1: %08x at1: %08x \n", nr1, ar1, at1); - if (NTParityChk(ad, nt1)) - printf("key> the same key test OK. key=%x%x\n", (unsigned int)((key & 0xFFFFFFFF00000000) >> 32), (unsigned int)(key & 0xFFFFFFFF)); - else { - printf("key> the same key test. check nt parity error.\n"); + if (!(ar == ar1 && at == at1 && NTParityChk(ad, nt1))) return false; - } memcpy(buf, cmd, cmdsize); mf_crypto1_decrypt(pcs, buf, cmdsize, 0); crypto1_destroy(pcs); - if(CheckCrc14443(CRC_14443_A, buf, cmdsize)) { - AuthData.ks2 = AuthData.ar_enc ^ ar; - AuthData.ks3 = AuthData.at_enc ^ at; - return true; - } else { + if(!CheckCrc14443(CRC_14443_A, buf, cmdsize)) return false; - } + + if (!CheckCrypto1Parity(cmd, cmdsize, buf, parity)) + return false; + + AuthData.ks2 = AuthData.ar_enc ^ ar; + AuthData.ks3 = AuthData.at_enc ^ at; + + return true; +} + +bool CheckCrypto1Parity(uint8_t *cmd, uint8_t cmdsize, uint8_t *cmd_enc, uint8_t *parity_enc) { + printf("parity check. size=%d\n", cmdsize); + printf("cmd =%s\n", sprint_hex(cmd, cmdsize)); + printf("cmd_enc=%s\n", sprint_hex(cmd_enc, cmdsize)); + printf("parity=%s\n", printBitsPar(parity_enc, cmdsize)); +// (oddparity8(ntx >> 8 & 0xff) ^ (ntx & 0x01) ^ ((ad->nt_enc_par >> 5) & 0x01) ^ (ad->nt_enc & 0x01)) || + for (int i = 0; i < cmdsize - 1; i++) { + bool b = oddparity8(cmd[i]) ^ (cmd[i + 1] & 0x01) ^ ((parity_enc[i / 8] >> (6 - i % 8)) & 0x01) ^ (cmd_enc[i + 1] & 0x01); + printf("i=%d b=%d\n", i, b); + if (b) + return false; + } + + return true; } diff --git a/client/cmdhflist.h b/client/cmdhflist.h index 76b57392..977be288 100644 --- a/client/cmdhflist.h +++ b/client/cmdhflist.h @@ -34,8 +34,9 @@ extern uint8_t iso14443A_CRC_check(bool isResponse, uint8_t* data, uint8_t len); extern uint8_t mifare_CRC_check(bool isResponse, uint8_t* data, uint8_t len); extern void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize); extern void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, uint8_t* parity, uint8_t paritysize, bool isResponse); -extern bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, bool isResponse, uint8_t *mfData, size_t *mfDataLen); +extern bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isResponse, uint8_t *mfData, size_t *mfDataLen); extern bool NTParityChk(TAuthData *ad, uint32_t ntx); -extern bool NestedCheckKey(uint64_t key, TAuthData *ad, uint8_t *cmd, uint8_t cmdsize); +extern bool NestedCheckKey(uint64_t key, TAuthData *ad, uint8_t *cmd, uint8_t cmdsize, uint8_t *parity); +extern bool CheckCrypto1Parity(uint8_t *cmd, uint8_t cmdsize, uint8_t *cmd_enc, uint8_t *parity_enc); #endif // CMDHFLIST From 597fca4ff17facc186df1de069d52b3f343c44f6 Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 13 Feb 2018 12:33:15 +0200 Subject: [PATCH 198/310] check parity works --- client/cmdhflist.c | 19 +++++++++++-------- client/cmdhflist.h | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/client/cmdhflist.c b/client/cmdhflist.c index a9691aa3..7059a223 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -431,29 +431,32 @@ bool NestedCheckKey(uint64_t key, TAuthData *ad, uint8_t *cmd, uint8_t cmdsize, crypto1_destroy(pcs); - if(!CheckCrc14443(CRC_14443_A, buf, cmdsize)) - return false; - if (!CheckCrypto1Parity(cmd, cmdsize, buf, parity)) return false; + if(!CheckCrc14443(CRC_14443_A, buf, cmdsize)) + return false; + AuthData.ks2 = AuthData.ar_enc ^ ar; AuthData.ks3 = AuthData.at_enc ^ at; return true; } -bool CheckCrypto1Parity(uint8_t *cmd, uint8_t cmdsize, uint8_t *cmd_enc, uint8_t *parity_enc) { +bool CheckCrypto1Parity(uint8_t *cmd_enc, uint8_t cmdsize, uint8_t *cmd, uint8_t *parity_enc) { + uint8_t parity[16]; + oddparitybuf(cmd, cmdsize, parity); printf("parity check. size=%d\n", cmdsize); printf("cmd =%s\n", sprint_hex(cmd, cmdsize)); printf("cmd_enc=%s\n", sprint_hex(cmd_enc, cmdsize)); - printf("parity=%s\n", printBitsPar(parity_enc, cmdsize)); + printf("parity =%s\n", printBitsPar(parity, cmdsize)); + printf("parity_enc=%s\n", printBitsPar(parity_enc, cmdsize)); // (oddparity8(ntx >> 8 & 0xff) ^ (ntx & 0x01) ^ ((ad->nt_enc_par >> 5) & 0x01) ^ (ad->nt_enc & 0x01)) || +// (oddparity8(ntx >> 24 & 0xff) ^ (ntx >> 16 & 0x01) ^ ((ad->nt_enc_par >> 7) & 0x01) ^ (ad->nt_enc >> 16 & 0x01)) for (int i = 0; i < cmdsize - 1; i++) { - bool b = oddparity8(cmd[i]) ^ (cmd[i + 1] & 0x01) ^ ((parity_enc[i / 8] >> (6 - i % 8)) & 0x01) ^ (cmd_enc[i + 1] & 0x01); - printf("i=%d b=%d\n", i, b); + bool b = oddparity8(cmd[i]) ^ (cmd[i + 1] & 0x01) ^ ((parity_enc[i / 8] >> (7 - i % 8)) & 0x01) ^ (cmd_enc[i + 1] & 0x01); if (b) - return false; + printf("!!! i=%d \n", i); } return true; diff --git a/client/cmdhflist.h b/client/cmdhflist.h index 977be288..835b17e2 100644 --- a/client/cmdhflist.h +++ b/client/cmdhflist.h @@ -37,6 +37,6 @@ extern void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize extern bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isResponse, uint8_t *mfData, size_t *mfDataLen); extern bool NTParityChk(TAuthData *ad, uint32_t ntx); extern bool NestedCheckKey(uint64_t key, TAuthData *ad, uint8_t *cmd, uint8_t cmdsize, uint8_t *parity); -extern bool CheckCrypto1Parity(uint8_t *cmd, uint8_t cmdsize, uint8_t *cmd_enc, uint8_t *parity_enc); +extern bool CheckCrypto1Parity(uint8_t *cmd_enc, uint8_t cmdsize, uint8_t *cmd, uint8_t *parity_enc); #endif // CMDHFLIST From 8253ca0e1b91fd5163483badbfd5c4a345cfbdb8 Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 13 Feb 2018 12:35:33 +0200 Subject: [PATCH 199/310] code cleaning --- client/cmdhflist.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/client/cmdhflist.c b/client/cmdhflist.c index 7059a223..962c4f82 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -444,19 +444,9 @@ bool NestedCheckKey(uint64_t key, TAuthData *ad, uint8_t *cmd, uint8_t cmdsize, } bool CheckCrypto1Parity(uint8_t *cmd_enc, uint8_t cmdsize, uint8_t *cmd, uint8_t *parity_enc) { - uint8_t parity[16]; - oddparitybuf(cmd, cmdsize, parity); - printf("parity check. size=%d\n", cmdsize); - printf("cmd =%s\n", sprint_hex(cmd, cmdsize)); - printf("cmd_enc=%s\n", sprint_hex(cmd_enc, cmdsize)); - printf("parity =%s\n", printBitsPar(parity, cmdsize)); - printf("parity_enc=%s\n", printBitsPar(parity_enc, cmdsize)); -// (oddparity8(ntx >> 8 & 0xff) ^ (ntx & 0x01) ^ ((ad->nt_enc_par >> 5) & 0x01) ^ (ad->nt_enc & 0x01)) || -// (oddparity8(ntx >> 24 & 0xff) ^ (ntx >> 16 & 0x01) ^ ((ad->nt_enc_par >> 7) & 0x01) ^ (ad->nt_enc >> 16 & 0x01)) for (int i = 0; i < cmdsize - 1; i++) { - bool b = oddparity8(cmd[i]) ^ (cmd[i + 1] & 0x01) ^ ((parity_enc[i / 8] >> (7 - i % 8)) & 0x01) ^ (cmd_enc[i + 1] & 0x01); - if (b) - printf("!!! i=%d \n", i); + if (oddparity8(cmd[i]) ^ (cmd[i + 1] & 0x01) ^ ((parity_enc[i / 8] >> (7 - i % 8)) & 0x01) ^ (cmd_enc[i + 1] & 0x01)) + return false; } return true; From 0113dcf3fd2d22217c481edac525f907be3c0e0b Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 13 Feb 2018 15:12:41 +0200 Subject: [PATCH 200/310] nested works --- client/cmdhflist.c | 68 ++++++++++++++++++++++++++++------------------ client/cmdhflist.h | 1 + 2 files changed, 42 insertions(+), 27 deletions(-) diff --git a/client/cmdhflist.c b/client/cmdhflist.c index 962c4f82..eb61c301 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -266,6 +266,7 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isRes if (MifareAuthState == masAuthComplete) { if (traceCrypto1) { crypto1_destroy(traceCrypto1); + traceCrypto1 = NULL; } MifareAuthState = masFirstData; @@ -279,19 +280,10 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isRes if (AuthData.first_auth) { AuthData.ks2 = AuthData.ar_enc ^ prng_successor(AuthData.nt, 64); AuthData.ks3 = AuthData.at_enc ^ prng_successor(AuthData.nt, 96); - struct Crypto1State *revstate = lfsr_recovery64(AuthData.ks2, AuthData.ks3); - lfsr_rollback_word(revstate, 0, 0); - lfsr_rollback_word(revstate, 0, 0); - lfsr_rollback_word(revstate, AuthData.nr_enc, 1); - lfsr_rollback_word(revstate, AuthData.uid ^ AuthData.nt, 0); - uint64_t lfsr = 0; - crypto1_get_lfsr(revstate, &lfsr); - crypto1_destroy(revstate); - mfLastKey = lfsr; - PrintAndLog(" | * | key | probable key:%x%x Prng:%s ks2:%08x ks3:%08x | |", - (unsigned int)((lfsr & 0xFFFFFFFF00000000) >> 32), - (unsigned int)(lfsr & 0xFFFFFFFF), + mfLastKey = GetCrypto1ProbableKey(&AuthData); + PrintAndLog(" | * | key | probable key:%010"PRIx64" Prng:%s ks2:%08x ks3:%08x | |", + mfLastKey, validate_prng_nonce(AuthData.nt) ? "WEAK": "HARD", AuthData.ks2, AuthData.ks3); @@ -300,12 +292,16 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isRes traceCrypto1 = lfsr_recovery64(AuthData.ks2, AuthData.ks3); } else { + if (traceCrypto1) { + crypto1_destroy(traceCrypto1); + traceCrypto1 = NULL; + } + // check last used key - if (mfLastKey) { + if (false && mfLastKey) { if (NestedCheckKey(mfLastKey, &AuthData, cmd, cmdsize, parity)) { - PrintAndLog(" | * | key | last used key:%x%x ks2:%08x ks3:%08x | |", - (unsigned int)((mfLastKey & 0xFFFFFFFF00000000) >> 32), - (unsigned int)(mfLastKey & 0xFFFFFFFF), + PrintAndLog(" | * | key | last used key:%010"PRIx64" ks2:%08x ks3:%08x | |", + mfLastKey, AuthData.ks2, AuthData.ks3); @@ -314,15 +310,15 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isRes } // check default keys - if (!traceCrypto1) { + if (false && !traceCrypto1) { for (int defaultKeyCounter = 0; defaultKeyCounter < MifareDefaultKeysSize; defaultKeyCounter++){ if (NestedCheckKey(MifareDefaultKeys[defaultKeyCounter], &AuthData, cmd, cmdsize, parity)) { - PrintAndLog(" | * | key | default key:%x%x ks2:%08x ks3:%08x | |", - (unsigned int)((MifareDefaultKeys[defaultKeyCounter] & 0xFFFFFFFF00000000) >> 32), - (unsigned int)(MifareDefaultKeys[defaultKeyCounter] & 0xFFFFFFFF), + PrintAndLog(" | * | key | default key:%010"PRIx64" ks2:%08x ks3:%08x | |", + MifareDefaultKeys[defaultKeyCounter], AuthData.ks2, AuthData.ks3); + mfLastKey = MifareDefaultKeys[defaultKeyCounter]; traceCrypto1 = lfsr_recovery64(AuthData.ks2, AuthData.ks3); break; }; @@ -331,8 +327,7 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isRes // nested if (!traceCrypto1 && validate_prng_nonce(AuthData.nt)) { -printf("nested. uid:%x nt:%x ar_enc:%x at_enc:%x\n", AuthData.uid, AuthData.nt, AuthData.ar_enc, AuthData.at_enc); - uint32_t ntx = prng_successor(AuthData.nt, 90); + uint32_t ntx = prng_successor(AuthData.nt, 90); for (int i = 0; i < 16383; i++) { ntx = prng_successor(ntx, 1); if (NTParityChk(&AuthData, ntx)){ @@ -344,18 +339,22 @@ printf("nested. uid:%x nt:%x ar_enc:%x at_enc:%x\n", AuthData.uid, AuthData.nt, mf_crypto1_decrypt(pcs, mfData, cmdsize, 0); crypto1_destroy(pcs); - if (CheckCrc14443(CRC_14443_A, mfData, cmdsize)) { + if (CheckCrypto1Parity(cmd, cmdsize, mfData, parity) && CheckCrc14443(CRC_14443_A, mfData, cmdsize)) { AuthData.ks2 = ks2; AuthData.ks3 = ks3; + + AuthData.nt = ntx; + mfLastKey = GetCrypto1ProbableKey(&AuthData); + PrintAndLog(" | * | key | nested probable key:%010"PRIx64" ks2:%08x ks3:%08x | |", + mfLastKey, + AuthData.ks2, + AuthData.ks3); + traceCrypto1 = lfsr_recovery64(AuthData.ks2, AuthData.ks3); break; } } } - if (traceCrypto1) - printf("key> nt=%08x nonce distance=%d \n", ntx, nonce_distance(AuthData.nt, ntx)); - else - printf("key> don't have any valid nt( \n"); } //hardnested @@ -437,6 +436,7 @@ bool NestedCheckKey(uint64_t key, TAuthData *ad, uint8_t *cmd, uint8_t cmdsize, if(!CheckCrc14443(CRC_14443_A, buf, cmdsize)) return false; + AuthData.nt = nt1; AuthData.ks2 = AuthData.ar_enc ^ ar; AuthData.ks3 = AuthData.at_enc ^ at; @@ -451,3 +451,17 @@ bool CheckCrypto1Parity(uint8_t *cmd_enc, uint8_t cmdsize, uint8_t *cmd, uint8_t return true; } + +uint64_t GetCrypto1ProbableKey(TAuthData *ad) { + struct Crypto1State *revstate = lfsr_recovery64(ad->ks2, ad->ks3); + lfsr_rollback_word(revstate, 0, 0); + lfsr_rollback_word(revstate, 0, 0); + lfsr_rollback_word(revstate, ad->nr_enc, 1); + lfsr_rollback_word(revstate, ad->uid ^ ad->nt, 0); + + uint64_t lfsr = 0; + crypto1_get_lfsr(revstate, &lfsr); + crypto1_destroy(revstate); + + return lfsr; +} diff --git a/client/cmdhflist.h b/client/cmdhflist.h index 835b17e2..c2c9c03e 100644 --- a/client/cmdhflist.h +++ b/client/cmdhflist.h @@ -38,5 +38,6 @@ extern bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, boo extern bool NTParityChk(TAuthData *ad, uint32_t ntx); extern bool NestedCheckKey(uint64_t key, TAuthData *ad, uint8_t *cmd, uint8_t cmdsize, uint8_t *parity); extern bool CheckCrypto1Parity(uint8_t *cmd_enc, uint8_t cmdsize, uint8_t *cmd, uint8_t *parity_enc); +extern uint64_t GetCrypto1ProbableKey(TAuthData *ad); #endif // CMDHFLIST From 45b4ac09c9ef89b9e5892156e69eda860b29381e Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 13 Feb 2018 17:07:49 +0200 Subject: [PATCH 201/310] added hardnested sketch. final cleaning. --- client/cmdhflist.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/client/cmdhflist.c b/client/cmdhflist.c index eb61c301..1fa02cb3 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -298,7 +298,7 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isRes } // check last used key - if (false && mfLastKey) { + if (mfLastKey) { if (NestedCheckKey(mfLastKey, &AuthData, cmd, cmdsize, parity)) { PrintAndLog(" | * | key | last used key:%010"PRIx64" ks2:%08x ks3:%08x | |", mfLastKey, @@ -310,7 +310,7 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isRes } // check default keys - if (false && !traceCrypto1) { + if (!traceCrypto1) { for (int defaultKeyCounter = 0; defaultKeyCounter < MifareDefaultKeysSize; defaultKeyCounter++){ if (NestedCheckKey(MifareDefaultKeys[defaultKeyCounter], &AuthData, cmd, cmdsize, parity)) { PrintAndLog(" | * | key | default key:%010"PRIx64" ks2:%08x ks3:%08x | |", @@ -360,6 +360,33 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isRes //hardnested if (!traceCrypto1) { printf("hardnested not implemented. uid:%x nt:%x ar_enc:%x at_enc:%x\n", AuthData.uid, AuthData.nt, AuthData.ar_enc, AuthData.at_enc); + MifareAuthState = masError; + + /* TOO SLOW( needs to have more strong filter. with this filter - aprox 4 mln tests + uint32_t t = msclock(); + uint32_t t1 = t; + int n = 0; + for (uint32_t i = 0; i < 0xFFFFFFFF; i++) { + if (NTParityChk(&AuthData, i)){ + + uint32_t ks2 = AuthData.ar_enc ^ prng_successor(i, 64); + uint32_t ks3 = AuthData.at_enc ^ prng_successor(i, 96); + struct Crypto1State *pcs = lfsr_recovery64(ks2, ks3); + + + + + n++; + + if (!(n % 100000)) { + printf("delta=%d n=%d ks2=%x ks3=%x \n", msclock() - t1 , n, ks2, ks3); + t1 = msclock(); + } + + } + } + printf("delta=%d n=%d\n", msclock() - t, n); + */ } } From 868deeb783002631e3d57c0358986c6fd866e11a Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 13 Feb 2018 17:14:51 +0200 Subject: [PATCH 202/310] move annotate* functions --- client/cmdhf.c | 110 --------------------------------------------- client/cmdhflist.c | 110 +++++++++++++++++++++++++++++++++++++++++++++ client/cmdhflist.h | 4 ++ 3 files changed, 114 insertions(+), 110 deletions(-) diff --git a/client/cmdhf.c b/client/cmdhf.c index ffbc4515..ab7bfcdf 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -43,116 +43,6 @@ int CmdHFTune(const char *Cmd) return 0; } -void annotateIclass(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) -{ - switch(cmd[0]) - { - case ICLASS_CMD_ACTALL: snprintf(exp,size,"ACTALL"); break; - case ICLASS_CMD_READ_OR_IDENTIFY:{ - if(cmdsize > 1){ - snprintf(exp,size,"READ(%d)",cmd[1]); - }else{ - snprintf(exp,size,"IDENTIFY"); - } - break; - } - case ICLASS_CMD_SELECT: snprintf(exp,size,"SELECT"); break; - case ICLASS_CMD_PAGESEL: snprintf(exp,size,"PAGESEL(%d)", cmd[1]); break; - case ICLASS_CMD_READCHECK_KC:snprintf(exp,size,"READCHECK[Kc](%d)", cmd[1]); break; - case ICLASS_CMD_READCHECK_KD:snprintf(exp,size,"READCHECK[Kd](%d)", cmd[1]); break; - case ICLASS_CMD_CHECK: snprintf(exp,size,"CHECK"); break; - case ICLASS_CMD_DETECT: snprintf(exp,size,"DETECT"); break; - case ICLASS_CMD_HALT: snprintf(exp,size,"HALT"); break; - case ICLASS_CMD_UPDATE: snprintf(exp,size,"UPDATE(%d)",cmd[1]); break; - case ICLASS_CMD_ACT: snprintf(exp,size,"ACT"); break; - case ICLASS_CMD_READ4: snprintf(exp,size,"READ4(%d)",cmd[1]); break; - default: snprintf(exp,size,"?"); break; - } - return; -} - -void annotateIso15693(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) -{ - - if(cmd[0] == 0x26) - { - switch(cmd[1]){ - case ISO15693_INVENTORY :snprintf(exp, size, "INVENTORY");break; - case ISO15693_STAYQUIET :snprintf(exp, size, "STAY_QUIET");break; - default: snprintf(exp,size,"?"); break; - - } - }else if(cmd[0] == 0x02) - { - switch(cmd[1]) - { - case ISO15693_READBLOCK :snprintf(exp, size, "READBLOCK");break; - case ISO15693_WRITEBLOCK :snprintf(exp, size, "WRITEBLOCK");break; - case ISO15693_LOCKBLOCK :snprintf(exp, size, "LOCKBLOCK");break; - case ISO15693_READ_MULTI_BLOCK :snprintf(exp, size, "READ_MULTI_BLOCK");break; - case ISO15693_SELECT :snprintf(exp, size, "SELECT");break; - case ISO15693_RESET_TO_READY :snprintf(exp, size, "RESET_TO_READY");break; - case ISO15693_WRITE_AFI :snprintf(exp, size, "WRITE_AFI");break; - case ISO15693_LOCK_AFI :snprintf(exp, size, "LOCK_AFI");break; - case ISO15693_WRITE_DSFID :snprintf(exp, size, "WRITE_DSFID");break; - case ISO15693_LOCK_DSFID :snprintf(exp, size, "LOCK_DSFID");break; - case ISO15693_GET_SYSTEM_INFO :snprintf(exp, size, "GET_SYSTEM_INFO");break; - case ISO15693_READ_MULTI_SECSTATUS :snprintf(exp, size, "READ_MULTI_SECSTATUS");break; - default: snprintf(exp,size,"?"); break; - } - } -} - - -void annotateTopaz(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) -{ - switch(cmd[0]) { - case TOPAZ_REQA :snprintf(exp, size, "REQA");break; - case TOPAZ_WUPA :snprintf(exp, size, "WUPA");break; - case TOPAZ_RID :snprintf(exp, size, "RID");break; - case TOPAZ_RALL :snprintf(exp, size, "RALL");break; - case TOPAZ_READ :snprintf(exp, size, "READ");break; - case TOPAZ_WRITE_E :snprintf(exp, size, "WRITE-E");break; - case TOPAZ_WRITE_NE :snprintf(exp, size, "WRITE-NE");break; - case TOPAZ_RSEG :snprintf(exp, size, "RSEG");break; - case TOPAZ_READ8 :snprintf(exp, size, "READ8");break; - case TOPAZ_WRITE_E8 :snprintf(exp, size, "WRITE-E8");break; - case TOPAZ_WRITE_NE8 :snprintf(exp, size, "WRITE-NE8");break; - default: snprintf(exp,size,"?"); break; - } -} - - -/** -06 00 = INITIATE -0E xx = SELECT ID (xx = Chip-ID) -0B = Get UID -08 yy = Read Block (yy = block number) -09 yy dd dd dd dd = Write Block (yy = block number; dd dd dd dd = data to be written) -0C = Reset to Inventory -0F = Completion -0A 11 22 33 44 55 66 = Authenticate (11 22 33 44 55 66 = data to authenticate) -**/ - -void annotateIso14443b(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) -{ - switch(cmd[0]){ - case ISO14443B_REQB : snprintf(exp,size,"REQB");break; - case ISO14443B_ATTRIB : snprintf(exp,size,"ATTRIB");break; - case ISO14443B_HALT : snprintf(exp,size,"HALT");break; - case ISO14443B_INITIATE : snprintf(exp,size,"INITIATE");break; - case ISO14443B_SELECT : snprintf(exp,size,"SELECT(%d)",cmd[1]);break; - case ISO14443B_GET_UID : snprintf(exp,size,"GET UID");break; - case ISO14443B_READ_BLK : snprintf(exp,size,"READ_BLK(%d)", cmd[1]);break; - case ISO14443B_WRITE_BLK : snprintf(exp,size,"WRITE_BLK(%d)",cmd[1]);break; - case ISO14443B_RESET : snprintf(exp,size,"RESET");break; - case ISO14443B_COMPLETION : snprintf(exp,size,"COMPLETION");break; - case ISO14443B_AUTHENTICATE : snprintf(exp,size,"AUTHENTICATE");break; - default : snprintf(exp,size ,"?");break; - } - -} - /** * @brief iso14443B_CRC_check Checks CRC in command or response * @param isResponse diff --git a/client/cmdhflist.c b/client/cmdhflist.c index 1fa02cb3..bbfb24c8 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -83,6 +83,116 @@ uint8_t mifare_CRC_check(bool isResponse, uint8_t* data, uint8_t len) } } +void annotateIclass(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) +{ + switch(cmd[0]) + { + case ICLASS_CMD_ACTALL: snprintf(exp,size,"ACTALL"); break; + case ICLASS_CMD_READ_OR_IDENTIFY:{ + if(cmdsize > 1){ + snprintf(exp,size,"READ(%d)",cmd[1]); + }else{ + snprintf(exp,size,"IDENTIFY"); + } + break; + } + case ICLASS_CMD_SELECT: snprintf(exp,size,"SELECT"); break; + case ICLASS_CMD_PAGESEL: snprintf(exp,size,"PAGESEL(%d)", cmd[1]); break; + case ICLASS_CMD_READCHECK_KC:snprintf(exp,size,"READCHECK[Kc](%d)", cmd[1]); break; + case ICLASS_CMD_READCHECK_KD:snprintf(exp,size,"READCHECK[Kd](%d)", cmd[1]); break; + case ICLASS_CMD_CHECK: snprintf(exp,size,"CHECK"); break; + case ICLASS_CMD_DETECT: snprintf(exp,size,"DETECT"); break; + case ICLASS_CMD_HALT: snprintf(exp,size,"HALT"); break; + case ICLASS_CMD_UPDATE: snprintf(exp,size,"UPDATE(%d)",cmd[1]); break; + case ICLASS_CMD_ACT: snprintf(exp,size,"ACT"); break; + case ICLASS_CMD_READ4: snprintf(exp,size,"READ4(%d)",cmd[1]); break; + default: snprintf(exp,size,"?"); break; + } + return; +} + +void annotateIso15693(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) +{ + + if(cmd[0] == 0x26) + { + switch(cmd[1]){ + case ISO15693_INVENTORY :snprintf(exp, size, "INVENTORY");break; + case ISO15693_STAYQUIET :snprintf(exp, size, "STAY_QUIET");break; + default: snprintf(exp,size,"?"); break; + + } + }else if(cmd[0] == 0x02) + { + switch(cmd[1]) + { + case ISO15693_READBLOCK :snprintf(exp, size, "READBLOCK");break; + case ISO15693_WRITEBLOCK :snprintf(exp, size, "WRITEBLOCK");break; + case ISO15693_LOCKBLOCK :snprintf(exp, size, "LOCKBLOCK");break; + case ISO15693_READ_MULTI_BLOCK :snprintf(exp, size, "READ_MULTI_BLOCK");break; + case ISO15693_SELECT :snprintf(exp, size, "SELECT");break; + case ISO15693_RESET_TO_READY :snprintf(exp, size, "RESET_TO_READY");break; + case ISO15693_WRITE_AFI :snprintf(exp, size, "WRITE_AFI");break; + case ISO15693_LOCK_AFI :snprintf(exp, size, "LOCK_AFI");break; + case ISO15693_WRITE_DSFID :snprintf(exp, size, "WRITE_DSFID");break; + case ISO15693_LOCK_DSFID :snprintf(exp, size, "LOCK_DSFID");break; + case ISO15693_GET_SYSTEM_INFO :snprintf(exp, size, "GET_SYSTEM_INFO");break; + case ISO15693_READ_MULTI_SECSTATUS :snprintf(exp, size, "READ_MULTI_SECSTATUS");break; + default: snprintf(exp,size,"?"); break; + } + } +} + + +void annotateTopaz(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) +{ + switch(cmd[0]) { + case TOPAZ_REQA :snprintf(exp, size, "REQA");break; + case TOPAZ_WUPA :snprintf(exp, size, "WUPA");break; + case TOPAZ_RID :snprintf(exp, size, "RID");break; + case TOPAZ_RALL :snprintf(exp, size, "RALL");break; + case TOPAZ_READ :snprintf(exp, size, "READ");break; + case TOPAZ_WRITE_E :snprintf(exp, size, "WRITE-E");break; + case TOPAZ_WRITE_NE :snprintf(exp, size, "WRITE-NE");break; + case TOPAZ_RSEG :snprintf(exp, size, "RSEG");break; + case TOPAZ_READ8 :snprintf(exp, size, "READ8");break; + case TOPAZ_WRITE_E8 :snprintf(exp, size, "WRITE-E8");break; + case TOPAZ_WRITE_NE8 :snprintf(exp, size, "WRITE-NE8");break; + default: snprintf(exp,size,"?"); break; + } +} + + +/** +06 00 = INITIATE +0E xx = SELECT ID (xx = Chip-ID) +0B = Get UID +08 yy = Read Block (yy = block number) +09 yy dd dd dd dd = Write Block (yy = block number; dd dd dd dd = data to be written) +0C = Reset to Inventory +0F = Completion +0A 11 22 33 44 55 66 = Authenticate (11 22 33 44 55 66 = data to authenticate) +**/ + +void annotateIso14443b(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) +{ + switch(cmd[0]){ + case ISO14443B_REQB : snprintf(exp,size,"REQB");break; + case ISO14443B_ATTRIB : snprintf(exp,size,"ATTRIB");break; + case ISO14443B_HALT : snprintf(exp,size,"HALT");break; + case ISO14443B_INITIATE : snprintf(exp,size,"INITIATE");break; + case ISO14443B_SELECT : snprintf(exp,size,"SELECT(%d)",cmd[1]);break; + case ISO14443B_GET_UID : snprintf(exp,size,"GET UID");break; + case ISO14443B_READ_BLK : snprintf(exp,size,"READ_BLK(%d)", cmd[1]);break; + case ISO14443B_WRITE_BLK : snprintf(exp,size,"WRITE_BLK(%d)",cmd[1]);break; + case ISO14443B_RESET : snprintf(exp,size,"RESET");break; + case ISO14443B_COMPLETION : snprintf(exp,size,"COMPLETION");break; + case ISO14443B_AUTHENTICATE : snprintf(exp,size,"AUTHENTICATE");break; + default : snprintf(exp,size ,"?");break; + } + +} + void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) { switch(cmd[0]) diff --git a/client/cmdhflist.h b/client/cmdhflist.h index c2c9c03e..8f289b48 100644 --- a/client/cmdhflist.h +++ b/client/cmdhflist.h @@ -32,6 +32,10 @@ extern void ClearAuthData(); extern uint8_t iso14443A_CRC_check(bool isResponse, uint8_t* data, uint8_t len); extern uint8_t mifare_CRC_check(bool isResponse, uint8_t* data, uint8_t len); +extern void annotateIclass(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize); +extern void annotateIso15693(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize); +extern void annotateTopaz(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize); +extern void annotateIso14443b(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize); extern void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize); extern void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, uint8_t* parity, uint8_t paritysize, bool isResponse); extern bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isResponse, uint8_t *mfData, size_t *mfDataLen); From 079563a092d41495df0cc5ac00772d08b802d465 Mon Sep 17 00:00:00 2001 From: merlokk Date: Wed, 14 Feb 2018 12:36:22 +0200 Subject: [PATCH 203/310] small fix and added line to changelog. --- CHANGELOG.md | 1 + client/cmdhflist.c | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4162c638..f1d32ae0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added to `hf emv exec` MSD path for VISA and Mastercard and some other compatible EMV cards (Merlok) - Added to `hf emv exec` SDA, DDA, fast DDA, CDA calculations for VISA and Mastercard and some other compatible EMV cards (Merlok) - Added `hf emv test` - crypto tests for DES, AES, SHA, RSA, SDA, DDA, CDA and some other crypto functions (Merlok) +- Added `hf list mf` - deciphers crypto1 stream and works with first authentication and weak nested authentications (Merlok) ## [3.0.1][2017-06-08] diff --git a/client/cmdhflist.c b/client/cmdhflist.c index bbfb24c8..82e35c27 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -325,11 +325,12 @@ void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, uint8 if (cmdsize == 4 && isResponse) { snprintf(exp,size,"AUTH: nt %s", (AuthData.first_auth) ? "" : "(enc)"); MifareAuthState = masNrAr; - if (AuthData.first_auth) + if (AuthData.first_auth) { AuthData.nt = bytes_to_num(cmd, 4); - else + } else { AuthData.nt_enc = bytes_to_num(cmd, 4); AuthData.nt_enc_par = parity[0]; + } return; } else { MifareAuthState = masError; From 2896e490d9851053666f6b83c0ec3ffe7eb686ab Mon Sep 17 00:00:00 2001 From: marshmellow42 Date: Wed, 14 Feb 2018 15:41:39 -0500 Subject: [PATCH 204/310] update changelog +fix a comment typo + move clear bigbuf after fpgadownloadandgo... --- CHANGELOG.md | 2 ++ armsrc/lfops.c | 7 ++++--- armsrc/lfsampling.c | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4162c638..36e57b5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac ## [unreleased][unreleased] ### Changed +- Adjusted `lf cmdread` to respond to client when complete and the client will then automatically call `data samples` - Improved backdoor detection missbehaving magic s50/1k tag (Fl0-0) - Deleted wipe functionality from `hf mf csetuid` (Merlok) - Changed `hf mf nested` logic (Merlok) @@ -21,6 +22,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Changed start sequence in Qt mode (fix: short commands hangs main Qt thread) (Merlok) ### Added +- Added a bitbang mode to `lf cmdread` if delay is 0 the cmd bits turn off and on the antenna with 0 and 1 respectively (marshmellow) - Added PAC/Stanley detection to lf search (marshmellow) - Added lf pac demod and lf pac read - extracts the raw blocks from a PAC/Stanley tag (marshmellow) - Added hf mf c* commands compatibity for 4k and gen1b backdoor (Fl0-0) diff --git a/armsrc/lfops.c b/armsrc/lfops.c index c7a7a59d..f3bbbf84 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -33,14 +33,15 @@ void ModThenAcquireRawAdcSamples125k(uint32_t delay_off, uint32_t period_0, uint // use lf config settings sample_config *sc = getSamplingConfig(); - // clear read buffer - BigBuf_Clear_keep_EM(); - /* Make sure the tag is reset */ + // Make sure the tag is reset FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); WaitMS(2500); + // clear read buffer (after fpga bitstream loaded...) + BigBuf_Clear_keep_EM(); + // power on LFSetupFPGAForADC(sc->divisor, 1); diff --git a/armsrc/lfsampling.c b/armsrc/lfsampling.c index 084201a5..3b076265 100644 --- a/armsrc/lfsampling.c +++ b/armsrc/lfsampling.c @@ -266,7 +266,7 @@ uint32_t SnoopLF() } /** -* acquisition of Cotag LF signal. Similart to other LF, since the Cotag has such long datarate RF/384 +* acquisition of Cotag LF signal. Similar to other LF, since the Cotag has such long datarate RF/384 * and is Manchester?, we directly gather the manchester data into bigbuff **/ #define COTAG_T1 384 From 6dec58aa4492e4b8cc465e8c148d03ace52392f7 Mon Sep 17 00:00:00 2001 From: merlokk Date: Thu, 15 Feb 2018 00:18:02 +0200 Subject: [PATCH 205/310] small fix `hf list mf` --- client/cmdhflist.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/cmdhflist.c b/client/cmdhflist.c index 82e35c27..87d5b67d 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -393,7 +393,7 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isRes AuthData.ks3 = AuthData.at_enc ^ prng_successor(AuthData.nt, 96); mfLastKey = GetCrypto1ProbableKey(&AuthData); - PrintAndLog(" | * | key | probable key:%010"PRIx64" Prng:%s ks2:%08x ks3:%08x | |", + PrintAndLog(" | * | key | probable key:%012"PRIx64" Prng:%s ks2:%08x ks3:%08x | |", mfLastKey, validate_prng_nonce(AuthData.nt) ? "WEAK": "HARD", AuthData.ks2, @@ -411,7 +411,7 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isRes // check last used key if (mfLastKey) { if (NestedCheckKey(mfLastKey, &AuthData, cmd, cmdsize, parity)) { - PrintAndLog(" | * | key | last used key:%010"PRIx64" ks2:%08x ks3:%08x | |", + PrintAndLog(" | * | key | last used key:%012"PRIx64" ks2:%08x ks3:%08x | |", mfLastKey, AuthData.ks2, AuthData.ks3); @@ -424,7 +424,7 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isRes if (!traceCrypto1) { for (int defaultKeyCounter = 0; defaultKeyCounter < MifareDefaultKeysSize; defaultKeyCounter++){ if (NestedCheckKey(MifareDefaultKeys[defaultKeyCounter], &AuthData, cmd, cmdsize, parity)) { - PrintAndLog(" | * | key | default key:%010"PRIx64" ks2:%08x ks3:%08x | |", + PrintAndLog(" | * | key | default key:%012"PRIx64" ks2:%08x ks3:%08x | |", MifareDefaultKeys[defaultKeyCounter], AuthData.ks2, AuthData.ks3); From 738ed6e476ad7298db771d74931aa6d58fd8889b Mon Sep 17 00:00:00 2001 From: merlokk Date: Thu, 15 Feb 2018 00:20:00 +0200 Subject: [PATCH 206/310] small fix --- client/cmdhflist.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/cmdhflist.c b/client/cmdhflist.c index 87d5b67d..cf69abba 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -456,7 +456,7 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isRes AuthData.nt = ntx; mfLastKey = GetCrypto1ProbableKey(&AuthData); - PrintAndLog(" | * | key | nested probable key:%010"PRIx64" ks2:%08x ks3:%08x | |", + PrintAndLog(" | * | key | nested probable key:%012"PRIx64" ks2:%08x ks3:%08x | |", mfLastKey, AuthData.ks2, AuthData.ks3); From f43b1038a21c1339c2fa1279409080943980b165 Mon Sep 17 00:00:00 2001 From: Tom Harkness Date: Thu, 22 Feb 2018 23:38:53 +1100 Subject: [PATCH 207/310] fix for swapped parity bits --- client/scripts/lf_bulk_program.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/scripts/lf_bulk_program.lua b/client/scripts/lf_bulk_program.lua index 4758c203..274f7510 100644 --- a/client/scripts/lf_bulk_program.lua +++ b/client/scripts/lf_bulk_program.lua @@ -61,8 +61,8 @@ local function cardHex(i,f) --As the function defaults to even parity and returns a boolean, --perform a 'not' function to get odd parity - high = evenparity(string.sub(stream,0,12)) and 1 or 0 - low = not evenparity(string.sub(stream,13)) and 1 or 0 + high = not evenparity(string.sub(stream,0,12)) and 1 or 0 + low = evenparity(string.sub(stream,13)) and 1 or 0 bits = bit32.bor(bit32.lshift(id,1), low) bits = bit32.bor(bits, bit32.lshift(high,25)) From d4d439a8aa5520671293ac7d9f01d8b7c424ebc0 Mon Sep 17 00:00:00 2001 From: Iceman Date: Mon, 26 Feb 2018 07:16:08 +0100 Subject: [PATCH 208/310] Update README.md Added hackerwarehouse --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7258f918..09f23a6b 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ The Proxmark3 is available for purchase (assembled and tested) from the following locations: * [RyscCorp](https://proxmark3.com/) (US) +* [Hackerwarehouse](https://hackerwarehouse.com/) (US) * [Elechouse](http://www.elechouse.com/) (HK) * [Lab401](https://lab401.com/) (FR) * [RFxSecure](http://www.rfxsecure.com/) (SG) From a5a830168fd3f6760b5b7cad51e3f2f0505097e9 Mon Sep 17 00:00:00 2001 From: Michael Farrell Date: Thu, 22 Mar 2018 02:05:03 +1100 Subject: [PATCH 209/310] OSX: Multiple libedit/readline fixes (#585) * OSX: Multiple libedit/readline fixes. --- client/proxmark3.c | 2 +- client/ui.c | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/client/proxmark3.c b/client/proxmark3.c index 95dd7cb9..1b64f68b 100644 --- a/client/proxmark3.c +++ b/client/proxmark3.c @@ -132,7 +132,7 @@ main_loop(char *script_cmds_file, char *script_cmd, bool usb_present) { printf("executing commands from file: %s\n", script_cmds_file); } } - + read_history(".history"); while(1) { diff --git a/client/ui.c b/client/ui.c index 8faed6e8..dc122d2f 100644 --- a/client/ui.c +++ b/client/ui.c @@ -53,8 +53,11 @@ void PrintAndLog(char *fmt, ...) } } + // If there is an incoming message from the hardware (eg: lf hid read) in + // the background (while the prompt is displayed and accepting user input), + // stash the prompt and bring it back later. #ifdef RL_STATE_READCMD - // We are using GNU readline. + // We are using GNU readline. libedit (OSX) doesn't support this flag. int need_hack = (rl_readline_state & RL_STATE_READCMD) > 0; if (need_hack) { @@ -64,9 +67,6 @@ void PrintAndLog(char *fmt, ...) rl_replace_line("", 0); rl_redisplay(); } -#else - // We are using libedit (OSX), which doesn't support this flag. - int need_hack = 0; #endif va_start(argptr, fmt); @@ -76,6 +76,8 @@ void PrintAndLog(char *fmt, ...) va_end(argptr); printf("\n"); +#ifdef RL_STATE_READCMD + // We are using GNU readline. libedit (OSX) doesn't support this flag. if (need_hack) { rl_restore_prompt(); rl_replace_line(saved_line, 0); @@ -83,6 +85,7 @@ void PrintAndLog(char *fmt, ...) rl_redisplay(); free(saved_line); } +#endif if (logging && logfile) { vfprintf(logfile, fmt, argptr2); From b24930c764849e16048936d62ff426a05083d2b1 Mon Sep 17 00:00:00 2001 From: James Chambers Date: Fri, 23 Mar 2018 15:26:30 -0400 Subject: [PATCH 210/310] mfu read block: retry block with bad CRC (#584) * mfu read block: retry block with bad CRC, command timeout, or unknown response. --- armsrc/mifareutil.c | 49 ++++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c index 38ca934a..684b5e36 100644 --- a/armsrc/mifareutil.c +++ b/armsrc/mifareutil.c @@ -405,31 +405,48 @@ int mifare_ultra_auth(uint8_t *keybytes){ return 1; } + +#define MFU_MAX_RETRIES 5 int mifare_ultra_readblock(uint8_t blockNo, uint8_t *blockData) { uint16_t len; uint8_t bt[2]; uint8_t receivedAnswer[MAX_FRAME_SIZE]; uint8_t receivedAnswerPar[MAX_PARITY_SIZE]; - + uint8_t retries; + int result = 0; - len = mifare_sendcmd_short(NULL, 1, 0x30, blockNo, receivedAnswer, receivedAnswerPar, NULL); - if (len == 1) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); - return 1; + for (retries = 0; retries < MFU_MAX_RETRIES; retries++) { + len = mifare_sendcmd_short(NULL, 1, 0x30, blockNo, receivedAnswer, receivedAnswerPar, NULL); + if (len == 1) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); + result = 1; + continue; + } + if (len != 18) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: card timeout. len: %x", len); + result = 2; + continue; + } + + memcpy(bt, receivedAnswer + 16, 2); + AppendCrc14443a(receivedAnswer, 16); + if (bt[0] != receivedAnswer[16] || bt[1] != receivedAnswer[17]) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd CRC response error."); + result = 3; + continue; + } + + // No errors encountered; don't retry + result = 0; + break; } - if (len != 18) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: card timeout. len: %x", len); - return 2; + + if (result != 0) { + Dbprintf("Cmd Error: too many retries; read failed"); + return result; } - - memcpy(bt, receivedAnswer + 16, 2); - AppendCrc14443a(receivedAnswer, 16); - if (bt[0] != receivedAnswer[16] || bt[1] != receivedAnswer[17]) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd CRC response error."); - return 3; - } - + memcpy(blockData, receivedAnswer, 14); return 0; } From b05611a30bc5d4815a7603afe6593f2e852be91a Mon Sep 17 00:00:00 2001 From: Matthew Daley Date: Wed, 28 Mar 2018 23:37:07 +1300 Subject: [PATCH 211/310] Fix offset Indala UID display Commit 1dae9811f22b7f2cea340cee6945cb349046129d extended the amount of fixed bits searched for when decoding 64-bit Indala. These additional bits come from the end of one UID, and therefore need to be skipped past when actually retrieving the UID. --- common/lfdemod.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/common/lfdemod.c b/common/lfdemod.c index f470371a..fd149045 100644 --- a/common/lfdemod.c +++ b/common/lfdemod.c @@ -1780,6 +1780,7 @@ int IOdemodFSK(uint8_t *dest, size_t size, int *waveStartIdx) { // indala id decoding int indala64decode(uint8_t *bitStream, size_t *size, uint8_t *invert) { //standard 64 bit indala formats including 26 bit 40134 format + // Note: these start with 3 bits from the end of one UID; the rest are from a subsequent one uint8_t preamble64[] = {1,0,1,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 1}; uint8_t preamble64_i[] = {0,1,0,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 0}; size_t startidx = 0; @@ -1791,6 +1792,10 @@ int indala64decode(uint8_t *bitStream, size_t *size, uint8_t *invert) { *invert ^= 1; } if (found_size != 64) return -2; + + // Skip the aforementioned 3 bits from the previous UID + startidx += 3; + if (*invert==1) for (size_t i = startidx; i < found_size + startidx; i++) bitStream[i] ^= 1; From 7ecf7cf6cc7620a34306902ee3505afc17dc0de0 Mon Sep 17 00:00:00 2001 From: marshmellow42 Date: Wed, 28 Mar 2018 08:50:08 -0400 Subject: [PATCH 212/310] Revert "Fix offset Indala UID display" --- common/lfdemod.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/common/lfdemod.c b/common/lfdemod.c index fd149045..f470371a 100644 --- a/common/lfdemod.c +++ b/common/lfdemod.c @@ -1780,7 +1780,6 @@ int IOdemodFSK(uint8_t *dest, size_t size, int *waveStartIdx) { // indala id decoding int indala64decode(uint8_t *bitStream, size_t *size, uint8_t *invert) { //standard 64 bit indala formats including 26 bit 40134 format - // Note: these start with 3 bits from the end of one UID; the rest are from a subsequent one uint8_t preamble64[] = {1,0,1,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 1}; uint8_t preamble64_i[] = {0,1,0,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 0}; size_t startidx = 0; @@ -1792,10 +1791,6 @@ int indala64decode(uint8_t *bitStream, size_t *size, uint8_t *invert) { *invert ^= 1; } if (found_size != 64) return -2; - - // Skip the aforementioned 3 bits from the previous UID - startidx += 3; - if (*invert==1) for (size_t i = startidx; i < found_size + startidx; i++) bitStream[i] ^= 1; From 53c7a7053d06af60388507c7d38252af1b96696a Mon Sep 17 00:00:00 2001 From: AnthraX1 Date: Sat, 31 Mar 2018 02:54:45 +1100 Subject: [PATCH 213/310] Fix empty key list bug in mfkeys.lua (#579) When input key list size is greater than 85, table.concat() returns empty because the last parameter is the end offset not the increment value. --- client/scripts/mfkeys.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/scripts/mfkeys.lua b/client/scripts/mfkeys.lua index 33027d31..671ce03d 100644 --- a/client/scripts/mfkeys.lua +++ b/client/scripts/mfkeys.lua @@ -57,7 +57,7 @@ function checkBlock(blockNo, keys, keyType) while remaining > 0 do local n,data = remaining, nil if remaining > 85 then n = 85 end - local data = table.concat(keys,"",start,n) + local data = table.concat(keys, "", start, start + n - 1) --print("data",data) --print("data len", #data) print(("Testing block %d, keytype %d, with %d keys"):format(blockNo, keyType, n)) From 3bcc4d77e1ea1d383e3dd15935efda2f36af4881 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Fri, 30 Mar 2018 17:55:41 +0200 Subject: [PATCH 214/310] add: save and load options in hf list (#577) --- client/cmdhf.c | 181 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 133 insertions(+), 48 deletions(-) diff --git a/client/cmdhf.c b/client/cmdhf.c index ab7bfcdf..82313ae0 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -355,21 +355,44 @@ int CmdHFList(const char *Cmd) { bool showWaitCycles = false; bool markCRCBytes = false; + bool loadFromFile = false; + bool saveToFile = false; + char param1 = '\0'; + char param2 = '\0'; + char param3 = '\0'; char type[40] = {0}; - int tlen = param_getstr(Cmd,0,type, sizeof(type)); - char param1 = param_getchar(Cmd, 1); - char param2 = param_getchar(Cmd, 2); - bool errors = false; + char filename[FILE_PATH_SIZE]; uint8_t protocol = 0; - //Validate params + + // parse command line + int tlen = param_getstr(Cmd, 0, type, sizeof(type)); + if (param_getlength(Cmd, 1) == 1) { + param1 = param_getchar(Cmd, 1); + } else { + param_getstr(Cmd, 1, filename, sizeof(filename)); + } + if (param_getlength(Cmd, 2) == 1) { + param2 = param_getchar(Cmd, 2); + } else if (strlen(filename) == 0) { + param_getstr(Cmd, 2, filename, sizeof(filename)); + } + if (param_getlength(Cmd, 3) == 1) { + param3 = param_getchar(Cmd, 3); + } else if (strlen(filename) == 0) { + param_getstr(Cmd, 3, filename, sizeof(filename)); + } + + // Validate param1 + bool errors = false; if(tlen == 0) { errors = true; } if(param1 == 'h' - || (param1 != 0 && param1 != 'f' && param1 != 'c') - || (param2 != 0 && param2 != 'f' && param2 != 'c')) { + || (param1 != 0 && param1 != 'f' && param1 != 'c' && param1 != 'l') + || (param2 != 0 && param2 != 'f' && param2 != 'c' && param2 != 'l') + || (param3 != 0 && param3 != 'f' && param3 != 'c' && param3 != 'l')) { errors = true; } @@ -382,20 +405,45 @@ int CmdHFList(const char *Cmd) protocol = ISO_14443A; } else if(strcmp(type, "14b") == 0) { protocol = ISO_14443B; - } else if(strcmp(type,"topaz")== 0) { + } else if(strcmp(type,"topaz") == 0) { protocol = TOPAZ; - } else if(strcmp(type,"raw")== 0) { - protocol = -1;//No crc, no annotations + } else if(strcmp(type,"raw") == 0) { + protocol = -1; //No crc, no annotations + } else if (strcmp(type, "save") == 0) { + saveToFile = true; } else { errors = true; } } + + if (param1 == 'f' || param2 == 'f' || param3 == 'f') { + showWaitCycles = true; + } + if (param1 == 'c' || param2 == 'c' || param3 == 'c') { + markCRCBytes = true; + } + + if (param1 == 'l' || param2 == 'l' || param3 == 'l') { + loadFromFile = true; + } + + if ((loadFromFile || saveToFile) && strlen(filename) == 0) { + errors = true; + } + + if (loadFromFile && saveToFile) { + errors = true; + } + if (errors) { - PrintAndLog("List protocol data in trace buffer."); - PrintAndLog("Usage: hf list [f][c]"); + PrintAndLog("List or save protocol data."); + PrintAndLog("Usage: hf list [f] [c] [l ]"); + PrintAndLog(" hf list save "); PrintAndLog(" f - show frame delay times as well"); PrintAndLog(" c - mark CRC bytes"); + PrintAndLog(" l - load data from file instead of trace buffer"); + PrintAndLog(" save - save data to file"); PrintAndLog("Supported values:"); PrintAndLog(" raw - just show raw data without annotations"); PrintAndLog(" 14a - interpret data as iso14443a communications"); @@ -406,52 +454,89 @@ int CmdHFList(const char *Cmd) PrintAndLog(""); PrintAndLog("example: hf list 14a f"); PrintAndLog("example: hf list iclass"); + PrintAndLog("example: hf list save myCardTrace.trc"); + PrintAndLog("example: hf list 14a l myCardTrace.trc"); return 0; } - if (param1 == 'f' || param2 == 'f') { - showWaitCycles = true; - } - - if (param1 == 'c' || param2 == 'c') { - markCRCBytes = true; - } - uint8_t *trace; - uint16_t tracepos = 0; - trace = malloc(USB_CMD_DATA_SIZE); - - // Query for the size of the trace - UsbCommand response; - GetFromBigBuf(trace, USB_CMD_DATA_SIZE, 0); - WaitForResponse(CMD_ACK, &response); - uint16_t traceLen = response.arg[2]; - if (traceLen > USB_CMD_DATA_SIZE) { - uint8_t *p = realloc(trace, traceLen); - if (p == NULL) { + uint32_t tracepos = 0; + uint32_t traceLen = 0; + + if (loadFromFile) { + #define TRACE_CHUNK_SIZE (1<<16) // 64K to start with. Will be enough for BigBuf and some room for future extensions + FILE *tracefile = NULL; + size_t bytes_read; + trace = malloc(TRACE_CHUNK_SIZE); + if (trace == NULL) { PrintAndLog("Cannot allocate memory for trace"); - free(trace); return 2; } - trace = p; - GetFromBigBuf(trace, traceLen, 0); - WaitForResponse(CMD_ACK, NULL); + if ((tracefile = fopen(filename,"rb")) == NULL) { + PrintAndLog("Could not open file %s", filename); + free(trace); + return 0; + } + while (!feof(tracefile)) { + bytes_read = fread(trace+traceLen, 1, TRACE_CHUNK_SIZE, tracefile); + traceLen += bytes_read; + if (!feof(tracefile)) { + uint8_t *p = realloc(trace, traceLen + TRACE_CHUNK_SIZE); + if (p == NULL) { + PrintAndLog("Cannot allocate memory for trace"); + free(trace); + fclose(tracefile); + return 2; + } + trace = p; + } + } + fclose(tracefile); + } else { + trace = malloc(USB_CMD_DATA_SIZE); + // Query for the size of the trace + UsbCommand response; + GetFromBigBuf(trace, USB_CMD_DATA_SIZE, 0); + WaitForResponse(CMD_ACK, &response); + traceLen = response.arg[2]; + if (traceLen > USB_CMD_DATA_SIZE) { + uint8_t *p = realloc(trace, traceLen); + if (p == NULL) { + PrintAndLog("Cannot allocate memory for trace"); + free(trace); + return 2; + } + trace = p; + GetFromBigBuf(trace, traceLen, 0); + WaitForResponse(CMD_ACK, NULL); + } } - - PrintAndLog("Recorded Activity (TraceLen = %d bytes)", traceLen); - PrintAndLog(""); - PrintAndLog("Start = Start of Start Bit, End = End of last modulation. Src = Source of Transfer"); - PrintAndLog("iso14443a - All times are in carrier periods (1/13.56Mhz)"); - PrintAndLog("iClass - Timings are not as accurate"); - PrintAndLog(""); - PrintAndLog(" Start | End | Src | Data (! denotes parity error) | CRC | Annotation |"); - PrintAndLog("------------|------------|-----|-----------------------------------------------------------------|-----|--------------------|"); - ClearAuthData(); - while(tracepos < traceLen) - { - tracepos = printTraceLine(tracepos, traceLen, trace, protocol, showWaitCycles, markCRCBytes); + if (saveToFile) { + FILE *tracefile = NULL; + if ((tracefile = fopen(filename,"wb")) == NULL) { + PrintAndLog("Could not create file %s", filename); + return 1; + } + fwrite(trace, 1, traceLen, tracefile); + PrintAndLog("Recorded Activity (TraceLen = %d bytes) written to file %s", traceLen, filename); + fclose(tracefile); + } else { + PrintAndLog("Recorded Activity (TraceLen = %d bytes)", traceLen); + PrintAndLog(""); + PrintAndLog("Start = Start of Start Bit, End = End of last modulation. Src = Source of Transfer"); + PrintAndLog("iso14443a - All times are in carrier periods (1/13.56Mhz)"); + PrintAndLog("iClass - Timings are not as accurate"); + PrintAndLog(""); + PrintAndLog(" Start | End | Src | Data (! denotes parity error) | CRC | Annotation |"); + PrintAndLog("------------|------------|-----|-----------------------------------------------------------------|-----|--------------------|"); + + ClearAuthData(); + while(tracepos < traceLen) + { + tracepos = printTraceLine(tracepos, traceLen, trace, protocol, showWaitCycles, markCRCBytes); + } } free(trace); From f5ecd97b15797e14e691bb6f3562ec1685c96bca Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Sat, 31 Mar 2018 09:52:43 +0200 Subject: [PATCH 215/310] USB comm: prepare for @micolous change (PR#463) (#587) * move communication related code from proxmark3.c and cmdmain.c to new file comms.c * replace byte_t by uint8_t in uart_posix.c and uart_win32.c * move OpenProxmark() and CloseProxmark() from flasher.c to flash.c * move print_lock mutex including initializer to ui.c * minor changes in printing help texts * no changes in comms functionality yet --- client/Makefile | 3 +- client/cmdmain.c | 152 +-------------------------- client/cmdmain.h | 7 +- client/cmdparser.c | 4 +- client/comms.c | 252 +++++++++++++++++++++++++++++++++++++++++++++ client/comms.h | 45 ++++++++ client/flash.c | 39 +++++-- client/flash.h | 6 +- client/flasher.c | 58 ++++------- client/proxgui.h | 1 - client/proxmark3.c | 98 ++---------------- client/ui.c | 3 +- client/ui.h | 1 - uart/uart.h | 14 ++- uart/uart_posix.c | 4 +- uart/uart_win32.c | 4 +- 16 files changed, 384 insertions(+), 307 deletions(-) create mode 100644 client/comms.c create mode 100644 client/comms.h diff --git a/client/Makefile b/client/Makefile index bf3c8d36..883f3b6f 100644 --- a/client/Makefile +++ b/client/Makefile @@ -170,7 +170,8 @@ CMDSRCS = crapto1/crapto1.c\ cmdscript.c\ pm3_binlib.c\ pm3_bitlib.c\ - protocols.c + protocols.c\ + comms.c cpu_arch = $(shell uname -m) ifneq ($(findstring 86, $(cpu_arch)), ) diff --git a/client/cmdmain.c b/client/cmdmain.c index 8d9313f9..10948c97 100644 --- a/client/cmdmain.c +++ b/client/cmdmain.c @@ -34,15 +34,6 @@ unsigned int current_command = CMD_UNKNOWN; static int CmdHelp(const char *Cmd); static int CmdQuit(const char *Cmd); -//For storing command that are received from the device -#define CMD_BUFFER_SIZE 50 -static UsbCommand cmdBuffer[CMD_BUFFER_SIZE]; -//Points to the next empty position to write to -static int cmd_head;//Starts as 0 -//Points to the position of the last unread command -static int cmd_tail;//Starts as 0 -// to lock cmdBuffer operations from different threads -static pthread_mutex_t cmdBufferMutex = PTHREAD_MUTEX_INITIALIZER; static command_t CommandTable[] = { @@ -61,6 +52,7 @@ command_t* getTopLevelCommandTable() { return CommandTable; } + int CmdHelp(const char *Cmd) { CmdsHelp(CommandTable); @@ -72,113 +64,6 @@ int CmdQuit(const char *Cmd) return 99; } -/** - * @brief This method should be called when sending a new command to the pm3. In case any old - * responses from previous commands are stored in the buffer, a call to this method should clear them. - * A better method could have been to have explicit command-ACKS, so we can know which ACK goes to which - * operation. Right now we'll just have to live with this. - */ -void clearCommandBuffer() -{ - //This is a very simple operation - pthread_mutex_lock(&cmdBufferMutex); - cmd_tail = cmd_head; - pthread_mutex_unlock(&cmdBufferMutex); -} - -/** - * @brief storeCommand stores a USB command in a circular buffer - * @param UC - */ -void storeCommand(UsbCommand *command) -{ - pthread_mutex_lock(&cmdBufferMutex); - if( ( cmd_head+1) % CMD_BUFFER_SIZE == cmd_tail) - { - //If these two are equal, we're about to overwrite in the - // circular buffer. - PrintAndLog("WARNING: Command buffer about to overwrite command! This needs to be fixed!"); - } - //Store the command at the 'head' location - UsbCommand* destination = &cmdBuffer[cmd_head]; - memcpy(destination, command, sizeof(UsbCommand)); - - cmd_head = (cmd_head +1) % CMD_BUFFER_SIZE; //increment head and wrap - pthread_mutex_unlock(&cmdBufferMutex); -} - - -/** - * @brief getCommand gets a command from an internal circular buffer. - * @param response location to write command - * @return 1 if response was returned, 0 if nothing has been received - */ -int getCommand(UsbCommand* response) -{ - pthread_mutex_lock(&cmdBufferMutex); - //If head == tail, there's nothing to read, or if we just got initialized - if(cmd_head == cmd_tail){ - pthread_mutex_unlock(&cmdBufferMutex); - return 0; - } - //Pick out the next unread command - UsbCommand* last_unread = &cmdBuffer[cmd_tail]; - memcpy(response, last_unread, sizeof(UsbCommand)); - //Increment tail - this is a circular buffer, so modulo buffer size - cmd_tail = (cmd_tail +1 ) % CMD_BUFFER_SIZE; - pthread_mutex_unlock(&cmdBufferMutex); - return 1; -} - - -/** - * Waits for a certain response type. This method waits for a maximum of - * ms_timeout milliseconds for a specified response command. - *@brief WaitForResponseTimeout - * @param cmd command to wait for - * @param response struct to copy received command into. - * @param ms_timeout - * @return true if command was returned, otherwise false - */ -bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning) { - - UsbCommand resp; - - if (response == NULL) { - response = &resp; - } - - uint64_t start_time = msclock(); - - // Wait until the command is received - while (true) { - while(getCommand(response)) { - if(response->cmd == cmd){ - return true; - } - } - if (msclock() - start_time > ms_timeout) { - break; - } - if (msclock() - start_time > 2000 && show_warning) { - PrintAndLog("Waiting for a response from the proxmark..."); - PrintAndLog("You can cancel this operation by pressing the pm3 button"); - show_warning = false; - } - } - return false; -} - - -bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout) { - return WaitForResponseTimeoutW(cmd, response, ms_timeout, true); -} - -bool WaitForResponse(uint32_t cmd, UsbCommand* response) { - return WaitForResponseTimeoutW(cmd, response, -1, true); -} - - //----------------------------------------------------------------------------- // Entry point into our code: called whenever the user types a command and // then presses Enter, which the full command line that they typed. @@ -187,38 +72,3 @@ int CommandReceived(char *Cmd) { return CmdsParse(CommandTable, Cmd); } - -//----------------------------------------------------------------------------- -// Entry point into our code: called whenever we received a packet over USB -// that we weren't necessarily expecting, for example a debug print. -//----------------------------------------------------------------------------- -void UsbCommandReceived(UsbCommand *UC) -{ - switch(UC->cmd) { - // First check if we are handling a debug message - case CMD_DEBUG_PRINT_STRING: { - char s[USB_CMD_DATA_SIZE+1]; - memset(s, 0x00, sizeof(s)); - size_t len = MIN(UC->arg[0],USB_CMD_DATA_SIZE); - memcpy(s,UC->d.asBytes,len); - PrintAndLog("#db# %s", s); - return; - } break; - - case CMD_DEBUG_PRINT_INTEGERS: { - PrintAndLog("#db# %08x, %08x, %08x \r\n", UC->arg[0], UC->arg[1], UC->arg[2]); - return; - } break; - - case CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K: { - memcpy(sample_buf+(UC->arg[0]),UC->d.asBytes,UC->arg[1]); - return; - } break; - - default: - storeCommand(UC); - break; - } - -} - diff --git a/client/cmdmain.h b/client/cmdmain.h index d39bc114..a833b41e 100644 --- a/client/cmdmain.h +++ b/client/cmdmain.h @@ -15,13 +15,10 @@ #include #include "usb_cmd.h" #include "cmdparser.h" +#include "comms.h" + -extern void UsbCommandReceived(UsbCommand *UC); extern int CommandReceived(char *Cmd); -extern bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning); -extern bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout); -extern bool WaitForResponse(uint32_t cmd, UsbCommand* response); -extern void clearCommandBuffer(); extern command_t* getTopLevelCommandTable(); #endif diff --git a/client/cmdparser.c b/client/cmdparser.c index 32508997..f4d3c404 100644 --- a/client/cmdparser.c +++ b/client/cmdparser.c @@ -8,12 +8,14 @@ // Command parser //----------------------------------------------------------------------------- +#include "cmdparser.h" + #include #include #include #include "ui.h" -#include "cmdparser.h" #include "proxmark3.h" +#include "comms.h" void CmdsHelp(const command_t Commands[]) diff --git a/client/comms.c b/client/comms.c new file mode 100644 index 00000000..5b8266fe --- /dev/null +++ b/client/comms.c @@ -0,0 +1,252 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2009 Michael Gernoth +// Copyright (C) 2010 iZsh +// +// 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. +//----------------------------------------------------------------------------- +// Code for communicating with the proxmark3 hardware. +//----------------------------------------------------------------------------- + +#include + +#include "comms.h" +#include "uart.h" +#include "ui.h" +#include "common.h" +#include "data.h" +#include "util_posix.h" + +// Declare globals. + +// Serial port that we are communicating with the PM3 on. +serial_port sp; + +// If TRUE, then there is no active connection to the PM3, and we will drop commands sent. +bool offline; + +// Transmit buffer. +// TODO: Use locks and execute this on the main thread, rather than the receiver +// thread. Running on the main thread means we need to be careful in the +// flasher, as it means SendCommand is no longer async, and can't be used as a +// buffer for a pending command when the connection is re-established. +static UsbCommand txcmd; +volatile static bool txcmd_pending = false; + +// Used by UsbReceiveCommand as a ring buffer for messages that are yet to be +// processed by a command handler (WaitForResponse{,Timeout}) +static UsbCommand cmdBuffer[CMD_BUFFER_SIZE]; + +// Points to the next empty position to write to +static int cmd_head = 0; + +// Points to the position of the last unread command +static int cmd_tail = 0; + +// to lock cmdBuffer operations from different threads +static pthread_mutex_t cmdBufferMutex = PTHREAD_MUTEX_INITIALIZER; + + +void SendCommand(UsbCommand *c) { + #if 0 + printf("Sending %d bytes\n", sizeof(UsbCommand)); + #endif + + if (offline) { + PrintAndLog("Sending bytes to proxmark failed - offline"); + return; + } + /** + The while-loop below causes hangups at times, when the pm3 unit is unresponsive + or disconnected. The main console thread is alive, but comm thread just spins here. + Not good.../holiman + **/ + while(txcmd_pending); + txcmd = *c; + txcmd_pending = true; +} + + +/** + * @brief This method should be called when sending a new command to the pm3. In case any old + * responses from previous commands are stored in the buffer, a call to this method should clear them. + * A better method could have been to have explicit command-ACKS, so we can know which ACK goes to which + * operation. Right now we'll just have to live with this. + */ +void clearCommandBuffer() +{ + //This is a very simple operation + pthread_mutex_lock(&cmdBufferMutex); + cmd_tail = cmd_head; + pthread_mutex_unlock(&cmdBufferMutex); +} + +/** + * @brief storeCommand stores a USB command in a circular buffer + * @param UC + */ +void storeCommand(UsbCommand *command) +{ + pthread_mutex_lock(&cmdBufferMutex); + if( (cmd_head+1) % CMD_BUFFER_SIZE == cmd_tail) + { + // If these two are equal, we're about to overwrite in the + // circular buffer. + PrintAndLog("WARNING: Command buffer about to overwrite command! This needs to be fixed!"); + } + + // Store the command at the 'head' location + UsbCommand* destination = &cmdBuffer[cmd_head]; + memcpy(destination, command, sizeof(UsbCommand)); + + cmd_head = (cmd_head +1) % CMD_BUFFER_SIZE; //increment head and wrap + pthread_mutex_unlock(&cmdBufferMutex); +} + + +/** + * @brief getCommand gets a command from an internal circular buffer. + * @param response location to write command + * @return 1 if response was returned, 0 if nothing has been received + */ +int getCommand(UsbCommand* response) +{ + pthread_mutex_lock(&cmdBufferMutex); + //If head == tail, there's nothing to read, or if we just got initialized + if (cmd_head == cmd_tail){ + pthread_mutex_unlock(&cmdBufferMutex); + return 0; + } + + //Pick out the next unread command + UsbCommand* last_unread = &cmdBuffer[cmd_tail]; + memcpy(response, last_unread, sizeof(UsbCommand)); + //Increment tail - this is a circular buffer, so modulo buffer size + cmd_tail = (cmd_tail + 1) % CMD_BUFFER_SIZE; + + pthread_mutex_unlock(&cmdBufferMutex); + return 1; +} + + +//----------------------------------------------------------------------------- +// Entry point into our code: called whenever we received a packet over USB +// that we weren't necessarily expecting, for example a debug print. +//----------------------------------------------------------------------------- +void UsbCommandReceived(UsbCommand *UC) +{ + switch(UC->cmd) { + // First check if we are handling a debug message + case CMD_DEBUG_PRINT_STRING: { + char s[USB_CMD_DATA_SIZE+1]; + memset(s, 0x00, sizeof(s)); + size_t len = MIN(UC->arg[0],USB_CMD_DATA_SIZE); + memcpy(s,UC->d.asBytes,len); + PrintAndLog("#db# %s", s); + return; + } break; + + case CMD_DEBUG_PRINT_INTEGERS: { + PrintAndLog("#db# %08x, %08x, %08x \r\n", UC->arg[0], UC->arg[1], UC->arg[2]); + return; + } break; + + case CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K: { + memcpy(sample_buf+(UC->arg[0]),UC->d.asBytes,UC->arg[1]); + return; + } break; + + default: + storeCommand(UC); + break; + } + +} + + +void +#ifdef __has_attribute +#if __has_attribute(force_align_arg_pointer) +__attribute__((force_align_arg_pointer)) +#endif +#endif +*uart_receiver(void *targ) { + receiver_arg *arg = (receiver_arg*)targ; + size_t rxlen; + uint8_t rx[sizeof(UsbCommand)]; + uint8_t *prx = rx; + + while (arg->run) { + rxlen = 0; + if (uart_receive(sp, prx, sizeof(UsbCommand) - (prx-rx), &rxlen) && rxlen) { + prx += rxlen; + if (prx-rx < sizeof(UsbCommand)) { + continue; + } + UsbCommandReceived((UsbCommand*)rx); + } + prx = rx; + + if(txcmd_pending) { + if (!uart_send(sp, (uint8_t*) &txcmd, sizeof(UsbCommand))) { + PrintAndLog("Sending bytes to proxmark failed"); + } + txcmd_pending = false; + } + } + + pthread_exit(NULL); + return NULL; +} + + +/** + * Waits for a certain response type. This method waits for a maximum of + * ms_timeout milliseconds for a specified response command. + *@brief WaitForResponseTimeout + * @param cmd command to wait for + * @param response struct to copy received command into. + * @param ms_timeout + * @return true if command was returned, otherwise false + */ +bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning) { + + UsbCommand resp; + + if (response == NULL) { + response = &resp; + } + + uint64_t start_time = msclock(); + + // Wait until the command is received + while (true) { + while(getCommand(response)) { + if(response->cmd == cmd){ + return true; + } + } + + if (msclock() - start_time > ms_timeout) { + break; + } + + if (msclock() - start_time > 2000 && show_warning) { + PrintAndLog("Waiting for a response from the proxmark..."); + PrintAndLog("You can cancel this operation by pressing the pm3 button"); + show_warning = false; + } + } + return false; +} + + +bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout) { + return WaitForResponseTimeoutW(cmd, response, ms_timeout, true); +} + +bool WaitForResponse(uint32_t cmd, UsbCommand* response) { + return WaitForResponseTimeoutW(cmd, response, -1, true); +} + diff --git a/client/comms.h b/client/comms.h new file mode 100644 index 00000000..40576018 --- /dev/null +++ b/client/comms.h @@ -0,0 +1,45 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2009 Michael Gernoth +// Copyright (C) 2010 iZsh +// +// 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. +//----------------------------------------------------------------------------- +// Code for communicating with the proxmark3 hardware. +//----------------------------------------------------------------------------- + +#ifndef COMMS_H_ +#define COMMS_H_ + +#include +#include + +#include "usb_cmd.h" +#include "uart.h" + +#ifndef CMD_BUFFER_SIZE +#define CMD_BUFFER_SIZE 50 +#endif + +typedef struct { + // If TRUE, continue running the uart_receiver thread + bool run; + + // Lock around serial port receives + pthread_mutex_t recv_lock; +} receiver_arg; + +void SendCommand(UsbCommand *c); + +void *uart_receiver(void *targ); +void UsbCommandReceived(UsbCommand *UC); +void clearCommandBuffer(); +bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning); +bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout); +bool WaitForResponse(uint32_t cmd, UsbCommand* response); + +extern serial_port sp; +extern bool offline; + +#endif // COMMS_H_ diff --git a/client/flash.c b/client/flash.c index 7622e8a5..894095e7 100644 --- a/client/flash.c +++ b/client/flash.c @@ -20,11 +20,12 @@ #include "elf.h" #include "proxendian.h" #include "usb_cmd.h" +#include "uart.h" void SendCommand(UsbCommand* txcmd); void ReceiveCommand(UsbCommand* rxcmd); -void CloseProxmark(); -int OpenProxmark(size_t i); + +serial_port sp; // FIXME: what the fuckity fuck unsigned int current_command = CMD_UNKNOWN; @@ -44,6 +45,22 @@ static const uint8_t elf_ident[] = { EV_CURRENT }; +void CloseProxmark(const char *serial_port_name) { + // Clean up the port + uart_close(sp); + // Fix for linux, it seems that it is extremely slow to release the serial port file descriptor /dev/* + unlink(serial_port_name); +} + +int OpenProxmark(size_t i, const char *serial_port_name) { + sp = uart_open(serial_port_name); + if (sp == INVALID_SERIAL_PORT || sp == CLAIMED_SERIAL_PORT) { + //poll once a second + return 0; + } + return 1; +} + // Turn PHDRs into flasher segments, checking for PHDR sanity and merging adjacent // unaligned segments if needed static int build_segs_from_phdrs(flash_file_t *ctx, FILE *fd, Elf32_Phdr *phdrs, int num_phdrs) @@ -278,7 +295,7 @@ static int get_proxmark_state(uint32_t *state) { UsbCommand c; c.cmd = CMD_DEVICE_INFO; - SendCommand(&c); + SendCommand(&c); UsbCommand resp; ReceiveCommand(&resp); @@ -338,14 +355,14 @@ static int enter_bootloader(char *serial_port_name) SendCommand(&c); fprintf(stderr,"Press and hold down button NOW if your bootloader requires it.\n"); } - msleep(100); - CloseProxmark(); + msleep(100); + CloseProxmark(serial_port_name); fprintf(stderr,"Waiting for Proxmark to reappear on %s",serial_port_name); - do { + do { sleep(1); fprintf(stderr, "."); - } while (!OpenProxmark(0)); + } while (!OpenProxmark(0, serial_port_name)); fprintf(stderr," Found.\n"); return 0; @@ -357,7 +374,7 @@ static int enter_bootloader(char *serial_port_name) static int wait_for_ack(void) { - UsbCommand ack; + UsbCommand ack; ReceiveCommand(&ack); if (ack.cmd != CMD_ACK) { printf("Error: Unexpected reply 0x%04" PRIx64 " (expected ACK)\n", ack.cmd); @@ -472,7 +489,7 @@ void flash_free(flash_file_t *ctx) // just reset the unit int flash_stop_flashing(void) { UsbCommand c = {CMD_HARDWARE_RESET}; - SendCommand(&c); - msleep(100); - return 0; + SendCommand(&c); + msleep(100); + return 0; } diff --git a/client/flash.h b/client/flash.h index 3e9f77a7..7f365924 100644 --- a/client/flash.h +++ b/client/flash.h @@ -11,6 +11,7 @@ #include #include "elf.h" +#include "uart.h" typedef struct { void *data; @@ -26,10 +27,13 @@ typedef struct { } flash_file_t; int flash_load(flash_file_t *ctx, const char *name, int can_write_bl); -int flash_start_flashing(int enable_bl_writes,char *serial_port_name); +int flash_start_flashing(int enable_bl_writes, char *serial_port_name); int flash_write(flash_file_t *ctx); void flash_free(flash_file_t *ctx); int flash_stop_flashing(void); +void CloseProxmark(const char *serial_port_name); +int OpenProxmark(size_t i, const char *serial_port_name); +extern serial_port sp; #endif diff --git a/client/flasher.c b/client/flasher.c index f257d994..2bb87df9 100644 --- a/client/flasher.c +++ b/client/flasher.c @@ -23,9 +23,6 @@ # include #endif -static serial_port sp; -static char* serial_port_name; - void cmd_debug(UsbCommand* UC) { // Debug printf("UsbCommand length[len=%zd]\n",sizeof(UsbCommand)); @@ -43,15 +40,15 @@ void cmd_debug(UsbCommand* UC) { void SendCommand(UsbCommand* txcmd) { // printf("send: "); // cmd_debug(txcmd); - if (!uart_send(sp,(byte_t*)txcmd,sizeof(UsbCommand))) { + if (!uart_send(sp,(uint8_t*)txcmd,sizeof(UsbCommand))) { printf("Sending bytes to proxmark failed\n"); exit(1); } } void ReceiveCommand(UsbCommand* rxcmd) { - byte_t* prxcmd = (byte_t*)rxcmd; - byte_t* prx = prxcmd; + uint8_t* prxcmd = (uint8_t*)rxcmd; + uint8_t* prx = prxcmd; size_t rxlen; while (true) { if (uart_receive(sp, prx, sizeof(UsbCommand) - (prx-prxcmd), &rxlen)) { @@ -63,32 +60,17 @@ void ReceiveCommand(UsbCommand* rxcmd) { } } -void CloseProxmark() { - // Clean up the port - uart_close(sp); - // Fix for linux, it seems that it is extremely slow to release the serial port file descriptor /dev/* - unlink(serial_port_name); -} - -int OpenProxmark(size_t i) { - sp = uart_open(serial_port_name); - if (sp == INVALID_SERIAL_PORT || sp == CLAIMED_SERIAL_PORT) { - //poll once a second - return 0; - } - return 1; -} - static void usage(char *argv0) { fprintf(stderr, "Usage: %s [-b] image.elf [image.elf...]\n\n", argv0); fprintf(stderr, "\t-b\tEnable flashing of bootloader area (DANGEROUS)\n\n"); - //Is the example below really true? /Martin - fprintf(stderr, "Example:\n\n\t %s path/to/osimage.elf path/to/fpgaimage.elf\n", argv0); - fprintf(stderr, "\nExample (Linux):\n\n\t %s /dev/ttyACM0 armsrc/obj/fullimage.elf\n", argv0); - fprintf(stderr, "\nNote (Linux): if the flasher gets stuck in 'Waiting for Proxmark to reappear on ',\n"); - fprintf(stderr, " you need to blacklist proxmark for modem-manager - see wiki for more details:\n"); - fprintf(stderr, " http://code.google.com/p/proxmark3/wiki/Linux\n\n"); + fprintf(stderr, "\nExample:\n\n\t %s "SERIAL_PORT_H" armsrc/obj/fullimage.elf\n", argv0); +#ifdef __linux__ + fprintf(stderr, "\nNote (Linux): if the flasher gets stuck at 'Waiting for Proxmark to reappear',\n"); + fprintf(stderr, " you may need to blacklist proxmark for modem-manager. v1.4.14 and later\n"); + fprintf(stderr, " include this configuration patch already. The change can be found at:\n"); + fprintf(stderr, " https://cgit.freedesktop.org/ModemManager/ModemManager/commit/?id=6e7ff47\n\n"); +#endif } #define MAX_FILES 4 @@ -126,16 +108,16 @@ int main(int argc, char **argv) } } - serial_port_name = argv[1]; - - fprintf(stderr,"Waiting for Proxmark to appear on %s",serial_port_name); - do { - msleep(1000); - fprintf(stderr, "."); - } while (!OpenProxmark(0)); - fprintf(stderr," Found.\n"); + char* serial_port_name = argv[1]; - res = flash_start_flashing(can_write_bl,serial_port_name); + fprintf(stderr,"Waiting for Proxmark to appear on %s", serial_port_name); + do { + msleep(1000); + fprintf(stderr, "."); + } while (!OpenProxmark(0, serial_port_name)); + fprintf(stderr," Found.\n"); + + res = flash_start_flashing(can_write_bl, serial_port_name); if (res < 0) return -1; @@ -155,7 +137,7 @@ int main(int argc, char **argv) if (res < 0) return -1; - CloseProxmark(); + CloseProxmark(serial_port_name); fprintf(stderr, "All done.\n\n"); fprintf(stderr, "Have a nice day!\n"); diff --git a/client/proxgui.h b/client/proxgui.h index 77bcbf01..5cad6e3a 100644 --- a/client/proxgui.h +++ b/client/proxgui.h @@ -30,7 +30,6 @@ extern int s_Buff[MAX_GRAPH_TRACE_LEN]; extern double CursorScaleFactor; extern int PlotGridX, PlotGridY, PlotGridXdefault, PlotGridYdefault, CursorCPos, CursorDPos, GridOffset; extern int CommandFinished; -extern int offline; extern bool GridLocked; //Operations defined in data_operations diff --git a/client/proxmark3.c b/client/proxmark3.c index 1b64f68b..fc258609 100644 --- a/client/proxmark3.c +++ b/client/proxmark3.c @@ -28,79 +28,6 @@ #include "cmdhw.h" #include "whereami.h" -#ifdef _WIN32 -#define SERIAL_PORT_H "com3" -#else -#define SERIAL_PORT_H "/dev/ttyACM0" -#endif - -// a global mutex to prevent interlaced printing from different threads -pthread_mutex_t print_lock; - -static serial_port sp; -static UsbCommand txcmd; -volatile static bool txcmd_pending = false; - -void SendCommand(UsbCommand *c) { - #if 0 - printf("Sending %d bytes\n", sizeof(UsbCommand)); - #endif - - if (offline) { - PrintAndLog("Sending bytes to proxmark failed - offline"); - return; - } - /** - The while-loop below causes hangups at times, when the pm3 unit is unresponsive - or disconnected. The main console thread is alive, but comm thread just spins here. - Not good.../holiman - **/ - while(txcmd_pending); - txcmd = *c; - txcmd_pending = true; -} - -struct receiver_arg { - int run; -}; - -byte_t rx[sizeof(UsbCommand)]; -byte_t* prx = rx; - - -static void -#ifdef __has_attribute -#if __has_attribute(force_align_arg_pointer) -__attribute__((force_align_arg_pointer)) -#endif -#endif -*uart_receiver(void *targ) { - struct receiver_arg *arg = (struct receiver_arg*)targ; - size_t rxlen; - - while (arg->run) { - rxlen = 0; - if (uart_receive(sp, prx, sizeof(UsbCommand) - (prx-rx), &rxlen) && rxlen) { - prx += rxlen; - if (prx-rx < sizeof(UsbCommand)) { - continue; - } - UsbCommandReceived((UsbCommand*)rx); - } - prx = rx; - - if(txcmd_pending) { - if (!uart_send(sp, (byte_t*) &txcmd, sizeof(UsbCommand))) { - PrintAndLog("Sending bytes to proxmark failed"); - } - txcmd_pending = false; - } - } - - pthread_exit(NULL); - return NULL; -} - void #ifdef __has_attribute @@ -109,15 +36,17 @@ __attribute__((force_align_arg_pointer)) #endif #endif main_loop(char *script_cmds_file, char *script_cmd, bool usb_present) { - struct receiver_arg rarg; + receiver_arg conn; char *cmd = NULL; pthread_t reader_thread; bool execCommand = (script_cmd != NULL); bool stdinOnPipe = !isatty(STDIN_FILENO); + memset(&conn, 0, sizeof(receiver_arg)); + if (usb_present) { - rarg.run = 1; - pthread_create(&reader_thread, NULL, &uart_receiver, &rarg); + conn.run = true; + pthread_create(&reader_thread, NULL, &uart_receiver, &conn); // cache Version information now: CmdVersion(NULL); } @@ -135,7 +64,7 @@ main_loop(char *script_cmds_file, char *script_cmd, bool usb_present) { read_history(".history"); - while(1) { + while (1) { // If there is a script file if (script_file) { @@ -207,7 +136,7 @@ main_loop(char *script_cmds_file, char *script_cmd, bool usb_present) { write_history(".history"); if (usb_present) { - rarg.run = 0; + conn.run = false; pthread_join(reader_thread, NULL); } @@ -257,9 +186,8 @@ static void set_my_executable_path(void) static void show_help(bool showFullHelp, char *command_line){ printf("syntax: %s [-h|-help|-m|-f|-flush|-w|-wait|-c|-command|-l|-lua] [cmd_script_file_name] [command][lua_script_name]\n", command_line); - printf("\tLinux example:'%s /dev/ttyACM0'\n", command_line); - printf("\tWindows example:'%s com3'\n\n", command_line); - + printf("\texample: %s "SERIAL_PORT_H"\n\n", command_line); + if (showFullHelp){ printf("help: <-h|-help> Dump all interactive command's help at once.\n"); printf("\t%s -h\n\n", command_line); @@ -287,7 +215,7 @@ int main(int argc, char* argv[]) { bool addLuaExec = false; char *script_cmds_file = NULL; char *script_cmd = NULL; - + if (argc < 2) { show_help(true, argv[0]); return 1; @@ -392,9 +320,6 @@ int main(int argc, char* argv[]) { usb_present = true; offline = 0; } - - // create a mutex to avoid interlacing print commands from our different threads - pthread_mutex_init(&print_lock, NULL); #ifdef HAVE_GUI #ifdef _WIN32 @@ -422,8 +347,5 @@ int main(int argc, char* argv[]) { uart_close(sp); } - // clean up mutex - pthread_mutex_destroy(&print_lock); - exit(0); } diff --git a/client/ui.c b/client/ui.c index dc122d2f..b0669a22 100644 --- a/client/ui.c +++ b/client/ui.c @@ -31,8 +31,7 @@ bool showDemod = true; static char *logfilename = "proxmark3.log"; #ifndef EXTERNAL_PRINTANDLOG -// Declared in proxmark3.c -extern pthread_mutex_t print_lock; +static pthread_mutex_t print_lock = PTHREAD_MUTEX_INITIALIZER; void PrintAndLog(char *fmt, ...) { diff --git a/client/ui.h b/client/ui.h index 4049033d..28512ca9 100644 --- a/client/ui.h +++ b/client/ui.h @@ -23,7 +23,6 @@ void SetLogFilename(char *fn); extern double CursorScaleFactor; extern int PlotGridX, PlotGridY, PlotGridXdefault, PlotGridYdefault, CursorCPos, CursorDPos, GridOffset; -extern int offline; extern int flushAfterWrite; //buzzy extern bool GridLocked; extern bool showDemod; diff --git a/uart/uart.h b/uart/uart.h index fe75a683..3b563be2 100644 --- a/uart/uart.h +++ b/uart/uart.h @@ -39,7 +39,15 @@ #include #include -typedef unsigned char byte_t; +/* Used to substitute for an example serial port path on each platform. + */ +#ifdef _WIN32 +#define SERIAL_PORT_H "com3" +#elif __APPLE__ +#define SERIAL_PORT_H "/dev/tty.usbmodem*" +#else +#define SERIAL_PORT_H "/dev/ttyACM0" +#endif /* serial_port is declared as a void*, which you should cast to whatever type * makes sense to your connection method. Both the posix and win32 @@ -78,13 +86,13 @@ void uart_close(const serial_port sp); * partial read may have completed into the buffer by the corresponding * implementation, so pszRxLen should be checked to see if any data was written. */ -bool uart_receive(const serial_port sp, byte_t* pbtRx, size_t pszMaxRxLen, size_t* pszRxLen); +bool uart_receive(const serial_port sp, uint8_t* pbtRx, size_t pszMaxRxLen, size_t* pszRxLen); /* Sends a buffer to a given serial port. * pbtTx: A pointer to a buffer containing the data to send. * szTxLen: The amount of data to be sent. */ -bool uart_send(const serial_port sp, const byte_t* pbtTx, const size_t szTxLen); +bool uart_send(const serial_port sp, const uint8_t* pbtTx, const size_t szTxLen); /* Sets the current speed of the serial port, in baud. */ diff --git a/uart/uart_posix.c b/uart/uart_posix.c index 45e0d3d2..0e7f7f47 100644 --- a/uart/uart_posix.c +++ b/uart/uart_posix.c @@ -133,7 +133,7 @@ void uart_close(const serial_port sp) { free(sp); } -bool uart_receive(const serial_port sp, byte_t* pbtRx, size_t pszMaxRxLen, size_t* pszRxLen) { +bool uart_receive(const serial_port sp, uint8_t* pbtRx, size_t pszMaxRxLen, size_t* pszRxLen) { int res; int byteCount; fd_set rfds; @@ -192,7 +192,7 @@ bool uart_receive(const serial_port sp, byte_t* pbtRx, size_t pszMaxRxLen, size_ return true; } -bool uart_send(const serial_port sp, const byte_t* pbtTx, const size_t szTxLen) { +bool uart_send(const serial_port sp, const uint8_t* pbtTx, const size_t szTxLen) { int32_t res; size_t szPos = 0; fd_set rfds; diff --git a/uart/uart_win32.c b/uart/uart_win32.c index 121b2b51..b15e8786 100644 --- a/uart/uart_win32.c +++ b/uart/uart_win32.c @@ -107,11 +107,11 @@ void uart_close(const serial_port sp) { free(sp); } -bool uart_receive(const serial_port sp, byte_t *pbtRx, size_t pszMaxRxLen, size_t *pszRxLen) { +bool uart_receive(const serial_port sp, uint8_t *pbtRx, size_t pszMaxRxLen, size_t *pszRxLen) { return ReadFile(((serial_port_windows*)sp)->hPort, pbtRx, pszMaxRxLen, (LPDWORD)pszRxLen, NULL); } -bool uart_send(const serial_port sp, const byte_t* pbtTx, const size_t szTxLen) { +bool uart_send(const serial_port sp, const uint8_t* pbtTx, const size_t szTxLen) { DWORD dwTxLen = 0; return WriteFile(((serial_port_windows*)sp)->hPort, pbtTx, szTxLen, &dwTxLen, NULL); } From e069547c27276069fd8d1cf93a281b819127bec4 Mon Sep 17 00:00:00 2001 From: Tom Harkness Date: Tue, 3 Apr 2018 16:14:51 +1000 Subject: [PATCH 216/310] Fix for swapped parity when using lf_bulk_program.lua (#591) --- client/scripts/lf_bulk_program.lua | 43 +++++++++++++++--------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/client/scripts/lf_bulk_program.lua b/client/scripts/lf_bulk_program.lua index 274f7510..2556f8a5 100644 --- a/client/scripts/lf_bulk_program.lua +++ b/client/scripts/lf_bulk_program.lua @@ -9,7 +9,7 @@ bit32 = require('bit32') usage = [[ script run lf_bulk_program.lua -f facility -b base_id_num -c count - e.g: + e.g: script run lf_bulk_program.lua -f 1 -b 1000 -c 10 ]] author = "Brian Redbeard" @@ -32,12 +32,12 @@ function toBits(num,bits) end --[[Likely, I'm an idiot, but I couldn't find any parity functions in Lua - This can also be done with a combination of bitwise operations (in fact, + This can also be done with a combination of bitwise operations (in fact, is the canonically "correct" way to do it, but my brain doesn't just default to this and so counting some ones is good enough for me]]-- local function evenparity(s) local _, count = string.gsub(s, "1", "") - + local p = count % 2 if (p == 0) then return(false) @@ -45,7 +45,7 @@ local function evenparity(s) return(true) end end - + local function isempty(s) return s == nil or s == '' @@ -57,12 +57,13 @@ end local function cardHex(i,f) fac = bit32.lshift(f,16) id = bit32.bor(i, fac) - stream=toBits(id,26) + stream=toBits(id,24) --As the function defaults to even parity and returns a boolean, --perform a 'not' function to get odd parity - high = not evenparity(string.sub(stream,0,12)) and 1 or 0 - low = evenparity(string.sub(stream,13)) and 1 or 0 + high = evenparity(string.sub(stream,1,12)) and 1 or 0 + low = not evenparity(string.sub(stream,13)) and 1 or 0 + bits = bit32.bor(bit32.lshift(id,1), low) bits = bit32.bor(bits, bit32.lshift(high,25)) @@ -71,13 +72,13 @@ local function cardHex(i,f) --to create a higher order and lower order component which we will --then assemble in the return. The math above defines the proper --encoding as per HID/Weigand/etc. These bit flips are due to the - --format length check on bit 38 (cmdlfhid.c:64) and + --format length check on bit 38 (cmdlfhid.c:64) and --bit 31 (cmdlfhid.c:66). preamble = bit32.bor(0, bit32.lshift(1,5)) bits = bit32.bor(bits, bit32.lshift(1,26)) return ("%04x%08x"):format(preamble,bits) - + end local function main(args) @@ -86,22 +87,22 @@ local function main(args) --long arguments, but it seems this library was chosen for BSD style --compatibility for o, a in getopt.getopt(args, 'f:b:c:h') do - if o == 'f' then - if isempty(a) then + if o == 'f' then + if isempty(a) then print("You did not supply a facility code, using 0") facility = 0 - else + else facility = a end - elseif o == 'b' then - if isempty(a) then + elseif o == 'b' then + if isempty(a) then print("You must supply the flag -b (base id)") return else baseid = a end - elseif o == 'c' then - if isempty(a) then + elseif o == 'c' then + if isempty(a) then print("You must supply the flag -c (count)") return else @@ -118,22 +119,22 @@ local function main(args) --works, specifying ":" does not enforce supplying a value, thus we --need to do these checks all over again. - if isempty(baseid) then + if isempty(baseid) then print("You must supply the flag -b (base id)") print(usage) return end - if isempty(count) then + if isempty(count) then print("You must supply the flag -c (count)") print(usage) return end --If the facility ID is non specified, ensure we code it as zero - if isempty(facility) then + if isempty(facility) then print("Using 0 for the facility code as -f was not supplied") - facility = 0 + facility = 0 end --The next baseid + count function presents a logic/UX conflict @@ -144,7 +145,7 @@ local function main(args) endid = baseid + count - for cardnum = baseid,endid do + for cardnum = baseid,endid do local card = cardHex(cardnum, facility) print("Press enter to program card "..cardnum..":"..facility.." (hex: "..card..")") --This would be better with "press any key", but we'll take From 61aaee35cc838af65f943c9b6f62db28beb48c93 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Tue, 24 Apr 2018 08:27:29 +0200 Subject: [PATCH 217/310] USB comms: part 2 towards @micolous PR#463 (#595) * change variable 'offline' from global to static * change variable 'FlushAfterWrite' from global to static * remove unused global variable 'current_command' * WaitForResponseTimeoutW(CMD_UNKNOWN, ...) waits for any command * #include "printf.h" or in iso15693tools.c to define sprintf() * and some minor changes/comments --- client/cmdlf.c | 8 ++++---- client/cmdmain.c | 2 -- client/cmdparser.c | 2 +- client/comms.c | 28 +++++++++++++++++++++++----- client/comms.h | 6 +++++- client/flash.c | 12 ++++++------ client/flash.h | 2 +- client/hid-flasher/proxusb.c | 2 -- client/proxmark3.c | 12 ++++++------ client/ui.c | 10 +++++++--- client/ui.h | 2 +- common/Makefile.common | 2 +- common/iso15693tools.c | 8 ++++++-- 13 files changed, 61 insertions(+), 35 deletions(-) diff --git a/client/cmdlf.c b/client/cmdlf.c index 42f73fa1..63dd737e 100644 --- a/client/cmdlf.c +++ b/client/cmdlf.c @@ -327,7 +327,7 @@ int CmdLFSetConfig(const char *Cmd) } bool lf_read(bool silent, uint32_t samples) { - if (offline) return false; + if (IsOffline()) return false; UsbCommand c = {CMD_ACQUIRE_RAW_ADC_SAMPLES_125K, {silent,samples,0}}; clearCommandBuffer(); //And ship it to device @@ -870,7 +870,7 @@ int CmdVchDemod(const char *Cmd) int CheckChipType(char cmdp) { uint32_t wordData = 0; - if (offline || cmdp == '1') return 0; + if (IsOffline() || cmdp == '1') return 0; save_restoreGB(GRAPH_SAVE); save_restoreDB(GRAPH_SAVE); @@ -915,7 +915,7 @@ int CmdLFfind(const char *Cmd) return 0; } - if (!offline && (cmdp != '1')) { + if (!IsOffline() && (cmdp != '1')) { lf_read(true, 30000); } else if (GraphTraceLen < minLength) { PrintAndLog("Data in Graphbuffer was too small."); @@ -931,7 +931,7 @@ int CmdLFfind(const char *Cmd) // only run if graphbuffer is just noise as it should be for hitag/cotag if (graphJustNoise(GraphBuffer, testLen)) { // only run these tests if we are in online mode - if (!offline && (cmdp != '1')) { + if (!IsOffline() && (cmdp != '1')) { // test for em4x05 in reader talk first mode. if (EM4x05Block0Test(&wordData)) { PrintAndLog("\nValid EM4x05/EM4x69 Chip Found\nUse lf em 4x05readword/dump commands to read\n"); diff --git a/client/cmdmain.c b/client/cmdmain.c index 10948c97..a45e3430 100644 --- a/client/cmdmain.c +++ b/client/cmdmain.c @@ -29,8 +29,6 @@ #include "cmdscript.h" -unsigned int current_command = CMD_UNKNOWN; - static int CmdHelp(const char *Cmd); static int CmdQuit(const char *Cmd); diff --git a/client/cmdparser.c b/client/cmdparser.c index f4d3c404..34230d52 100644 --- a/client/cmdparser.c +++ b/client/cmdparser.c @@ -25,7 +25,7 @@ void CmdsHelp(const command_t Commands[]) int i = 0; while (Commands[i].Name) { - if (!offline || Commands[i].Offline) + if (!IsOffline() || Commands[i].Offline) PrintAndLog("%-16s %s", Commands[i].Name, Commands[i].Help); ++i; } diff --git a/client/comms.c b/client/comms.c index 5b8266fe..2dd5534c 100644 --- a/client/comms.c +++ b/client/comms.c @@ -24,7 +24,7 @@ serial_port sp; // If TRUE, then there is no active connection to the PM3, and we will drop commands sent. -bool offline; +static bool offline; // Transmit buffer. // TODO: Use locks and execute this on the main thread, rather than the receiver @@ -47,10 +47,20 @@ static int cmd_tail = 0; // to lock cmdBuffer operations from different threads static pthread_mutex_t cmdBufferMutex = PTHREAD_MUTEX_INITIALIZER; +// These wrappers are required because it is not possible to access a static +// global variable outside of the context of a single file. + +void SetOffline(bool new_offline) { + offline = new_offline; +} + +bool IsOffline() { + return offline; +} void SendCommand(UsbCommand *c) { - #if 0 - printf("Sending %d bytes\n", sizeof(UsbCommand)); + #ifdef COMMS_DEBUG + printf("Sending %04x cmd\n", c->cmd); #endif if (offline) { @@ -153,6 +163,8 @@ void UsbCommandReceived(UsbCommand *UC) } break; case CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K: { + // FIXME: This does unsanitised copies into memory when we don't know + // the size of the buffer. memcpy(sample_buf+(UC->arg[0]),UC->d.asBytes,UC->arg[1]); return; } break; @@ -205,15 +217,20 @@ __attribute__((force_align_arg_pointer)) * Waits for a certain response type. This method waits for a maximum of * ms_timeout milliseconds for a specified response command. *@brief WaitForResponseTimeout - * @param cmd command to wait for + * @param cmd command to wait for, or CMD_UNKNOWN to take any command. * @param response struct to copy received command into. * @param ms_timeout + * @param show_warning * @return true if command was returned, otherwise false */ bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning) { UsbCommand resp; + #ifdef COMMS_DEBUG + printf("Waiting for %04x cmd\n", cmd); + #endif + if (response == NULL) { response = &resp; } @@ -223,7 +240,7 @@ bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeo // Wait until the command is received while (true) { while(getCommand(response)) { - if(response->cmd == cmd){ + if (cmd == CMD_UNKNOWN || response->cmd == cmd) { return true; } } @@ -233,6 +250,7 @@ bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeo } if (msclock() - start_time > 2000 && show_warning) { + // 2 seconds elapsed (but this doesn't mean the timeout was exceeded) PrintAndLog("Waiting for a response from the proxmark..."); PrintAndLog("You can cancel this operation by pressing the pm3 button"); show_warning = false; diff --git a/client/comms.h b/client/comms.h index 40576018..51a1467d 100644 --- a/client/comms.h +++ b/client/comms.h @@ -30,6 +30,11 @@ typedef struct { pthread_mutex_t recv_lock; } receiver_arg; + +// Wrappers required as static variables can only be used in one file. +void SetOffline(bool new_offline); +bool IsOffline(); + void SendCommand(UsbCommand *c); void *uart_receiver(void *targ); @@ -40,6 +45,5 @@ bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeou bool WaitForResponse(uint32_t cmd, UsbCommand* response); extern serial_port sp; -extern bool offline; #endif // COMMS_H_ diff --git a/client/flash.c b/client/flash.c index 894095e7..e3714185 100644 --- a/client/flash.c +++ b/client/flash.c @@ -27,9 +27,6 @@ void ReceiveCommand(UsbCommand* rxcmd); serial_port sp; -// FIXME: what the fuckity fuck -unsigned int current_command = CMD_UNKNOWN; - #define FLASH_START 0x100000 #define FLASH_SIZE (256*1024) #define FLASH_END (FLASH_START + FLASH_SIZE) @@ -52,13 +49,14 @@ void CloseProxmark(const char *serial_port_name) { unlink(serial_port_name); } -int OpenProxmark(size_t i, const char *serial_port_name) { +bool OpenProxmark(size_t i, const char *serial_port_name) { sp = uart_open(serial_port_name); if (sp == INVALID_SERIAL_PORT || sp == CLAIMED_SERIAL_PORT) { //poll once a second - return 0; + return false; } - return 1; + + return true; } // Turn PHDRs into flasher segments, checking for PHDR sanity and merging adjacent @@ -355,6 +353,7 @@ static int enter_bootloader(char *serial_port_name) SendCommand(&c); fprintf(stderr,"Press and hold down button NOW if your bootloader requires it.\n"); } + msleep(100); CloseProxmark(serial_port_name); @@ -363,6 +362,7 @@ static int enter_bootloader(char *serial_port_name) sleep(1); fprintf(stderr, "."); } while (!OpenProxmark(0, serial_port_name)); + fprintf(stderr," Found.\n"); return 0; diff --git a/client/flash.h b/client/flash.h index 7f365924..f8ffd221 100644 --- a/client/flash.h +++ b/client/flash.h @@ -32,7 +32,7 @@ int flash_write(flash_file_t *ctx); void flash_free(flash_file_t *ctx); int flash_stop_flashing(void); void CloseProxmark(const char *serial_port_name); -int OpenProxmark(size_t i, const char *serial_port_name); +bool OpenProxmark(size_t i, const char *serial_port_name); extern serial_port sp; #endif diff --git a/client/hid-flasher/proxusb.c b/client/hid-flasher/proxusb.c index 04dbb784..364b21a3 100644 --- a/client/hid-flasher/proxusb.c +++ b/client/hid-flasher/proxusb.c @@ -31,7 +31,6 @@ usb_dev_handle *devh = NULL; static unsigned int claimed_iface = 0; unsigned char return_on_error = 0; unsigned char error_occured = 0; -extern unsigned int current_command; void SendCommand(UsbCommand *c) { @@ -40,7 +39,6 @@ void SendCommand(UsbCommand *c) #if 0 printf("Sending %d bytes\n", sizeof(UsbCommand)); #endif - current_command = c->cmd; ret = usb_bulk_write(devh, 0x01, (char*)c, sizeof(UsbCommand), 1000); if (ret<0) { error_occured = 1; diff --git a/client/proxmark3.c b/client/proxmark3.c index fc258609..6587bcff 100644 --- a/client/proxmark3.c +++ b/client/proxmark3.c @@ -41,14 +41,17 @@ main_loop(char *script_cmds_file, char *script_cmd, bool usb_present) { pthread_t reader_thread; bool execCommand = (script_cmd != NULL); bool stdinOnPipe = !isatty(STDIN_FILENO); - + memset(&conn, 0, sizeof(receiver_arg)); if (usb_present) { conn.run = true; + SetOffline(false); pthread_create(&reader_thread, NULL, &uart_receiver, &conn); // cache Version information now: CmdVersion(NULL); + } else { + SetOffline(true); } // file with script @@ -64,7 +67,7 @@ main_loop(char *script_cmds_file, char *script_cmd, bool usb_present) { read_history(".history"); - while (1) { + while (1) { // If there is a script file if (script_file) { @@ -235,7 +238,7 @@ int main(int argc, char* argv[]) { if(strcmp(argv[i],"-f") == 0 || strcmp(argv[i],"-flush") == 0){ printf("Output will be flushed after every print.\n"); - flushAfterWrite = 1; + SetFlushAfterWrite(true); } if(strcmp(argv[i],"-w") == 0 || strcmp(argv[i],"-wait") == 0){ @@ -311,14 +314,11 @@ int main(int argc, char* argv[]) { if (sp == INVALID_SERIAL_PORT) { printf("ERROR: invalid serial port\n"); usb_present = false; - offline = 1; } else if (sp == CLAIMED_SERIAL_PORT) { printf("ERROR: serial port is claimed by another process\n"); usb_present = false; - offline = 1; } else { usb_present = true; - offline = 0; } #ifdef HAVE_GUI diff --git a/client/ui.c b/client/ui.c index b0669a22..50a6ec7d 100644 --- a/client/ui.c +++ b/client/ui.c @@ -22,8 +22,7 @@ double CursorScaleFactor = 1; int PlotGridX=0, PlotGridY=0, PlotGridXdefault= 64, PlotGridYdefault= 64, CursorCPos= 0, CursorDPos= 0; -int offline; -int flushAfterWrite = 0; //buzzy +bool flushAfterWrite = false; //buzzy int GridOffset = 0; bool GridLocked = false; bool showDemod = true; @@ -93,7 +92,7 @@ void PrintAndLog(char *fmt, ...) } va_end(argptr2); - if (flushAfterWrite == 1) //buzzy + if (flushAfterWrite) //buzzy { fflush(NULL); } @@ -106,3 +105,8 @@ void SetLogFilename(char *fn) { logfilename = fn; } + +void SetFlushAfterWrite(bool flush_after_write) { + flushAfterWrite = flush_after_write; +} + diff --git a/client/ui.h b/client/ui.h index 28512ca9..1273fe9e 100644 --- a/client/ui.h +++ b/client/ui.h @@ -20,10 +20,10 @@ void ShowGraphWindow(void); void RepaintGraphWindow(void); void PrintAndLog(char *fmt, ...); void SetLogFilename(char *fn); +void SetFlushAfterWrite(bool flush_after_write); extern double CursorScaleFactor; extern int PlotGridX, PlotGridY, PlotGridXdefault, PlotGridYdefault, CursorCPos, CursorDPos, GridOffset; -extern int flushAfterWrite; //buzzy extern bool GridLocked; extern bool showDemod; diff --git a/common/Makefile.common b/common/Makefile.common index 29b72a4c..f31ff7bb 100644 --- a/common/Makefile.common +++ b/common/Makefile.common @@ -29,7 +29,7 @@ GZIP=gzip OBJDIR = obj -INCLUDE = -I../include -I../common +INCLUDE = -I../include -I../common -I. TAR=tar TARFLAGS = -C .. -rvf diff --git a/common/iso15693tools.c b/common/iso15693tools.c index 26e636ca..f1214458 100644 --- a/common/iso15693tools.c +++ b/common/iso15693tools.c @@ -11,6 +11,12 @@ #include #include //#include "iso15693tools.h" +#ifdef ON_DEVICE +#include "printf.h" +#else +#include +#endif + #define POLY 0x8408 @@ -51,8 +57,6 @@ int Iso15693AddCrc(uint8_t *req, int n) { } -int sprintf(char *str, const char *format, ...); - // returns a string representation of the UID // UID is transmitted and stored LSB first, displayed MSB first // target char* buffer, where to put the UID, if NULL a static buffer is returned From 818efbebb87ec5485fbf367021dca42514dfdee0 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Sat, 28 Apr 2018 10:09:16 +0200 Subject: [PATCH 218/310] USB comms: part 3 towards @micolous PR#463 * change variable 'sp' from global to static * move code to open and close USB port to comms.c (OpenProxmark() and CloseProxmark()) * change scope of USBCommandReceived() to static * (flasher still unchanged) --- client/comms.c | 47 +++++++++++++++++++++++++++++++++++++--------- client/comms.h | 7 +++---- client/proxmark3.c | 33 ++++---------------------------- 3 files changed, 45 insertions(+), 42 deletions(-) diff --git a/client/comms.c b/client/comms.c index 2dd5534c..97d58ef9 100644 --- a/client/comms.c +++ b/client/comms.c @@ -21,7 +21,7 @@ // Declare globals. // Serial port that we are communicating with the PM3 on. -serial_port sp; +static serial_port sp; // If TRUE, then there is no active connection to the PM3, and we will drop commands sent. static bool offline; @@ -58,6 +58,38 @@ bool IsOffline() { return offline; } +bool OpenProxmark(char *portname, bool waitCOMPort, int timeout) { + if (!waitCOMPort) { + sp = uart_open(portname); + } else { + printf("Waiting for Proxmark to appear on %s ", portname); + fflush(stdout); + int openCount = 0; + do { + sp = uart_open(portname); + msleep(1000); + printf("."); + fflush(stdout); + } while(++openCount < timeout && (sp == INVALID_SERIAL_PORT || sp == CLAIMED_SERIAL_PORT)); + printf("\n"); + } + + // check result of uart opening + if (sp == INVALID_SERIAL_PORT) { + printf("ERROR: invalid serial port\n"); + return false; + } else if (sp == CLAIMED_SERIAL_PORT) { + printf("ERROR: serial port is claimed by another process\n"); + return false; + } else { + return true; + } +} + +void CloseProxmark(void) { + uart_close(sp); +} + void SendCommand(UsbCommand *c) { #ifdef COMMS_DEBUG printf("Sending %04x cmd\n", c->cmd); @@ -73,6 +105,7 @@ void SendCommand(UsbCommand *c) { Not good.../holiman **/ while(txcmd_pending); + txcmd = *c; txcmd_pending = true; } @@ -140,11 +173,7 @@ int getCommand(UsbCommand* response) } -//----------------------------------------------------------------------------- -// Entry point into our code: called whenever we received a packet over USB -// that we weren't necessarily expecting, for example a debug print. -//----------------------------------------------------------------------------- -void UsbCommandReceived(UsbCommand *UC) +static void UsbCommandReceived(UsbCommand *UC) { switch(UC->cmd) { // First check if we are handling a debug message @@ -170,7 +199,7 @@ void UsbCommandReceived(UsbCommand *UC) } break; default: - storeCommand(UC); + storeCommand(UC); break; } @@ -184,12 +213,12 @@ __attribute__((force_align_arg_pointer)) #endif #endif *uart_receiver(void *targ) { - receiver_arg *arg = (receiver_arg*)targ; + receiver_arg *conn = (receiver_arg*)targ; size_t rxlen; uint8_t rx[sizeof(UsbCommand)]; uint8_t *prx = rx; - while (arg->run) { + while (conn->run) { rxlen = 0; if (uart_receive(sp, prx, sizeof(UsbCommand) - (prx-rx), &rxlen) && rxlen) { prx += rxlen; diff --git a/client/comms.h b/client/comms.h index 51a1467d..616f7ddb 100644 --- a/client/comms.h +++ b/client/comms.h @@ -31,19 +31,18 @@ typedef struct { } receiver_arg; -// Wrappers required as static variables can only be used in one file. void SetOffline(bool new_offline); bool IsOffline(); +bool OpenProxmark(char *portname, bool waitCOMPort, int timeout); +void CloseProxmark(void); + void SendCommand(UsbCommand *c); void *uart_receiver(void *targ); -void UsbCommandReceived(UsbCommand *UC); void clearCommandBuffer(); bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning); bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout); bool WaitForResponse(uint32_t cmd, UsbCommand* response); -extern serial_port sp; - #endif // COMMS_H_ diff --git a/client/proxmark3.c b/client/proxmark3.c index 6587bcff..88cb5fa7 100644 --- a/client/proxmark3.c +++ b/client/proxmark3.c @@ -21,7 +21,6 @@ #include "util_posix.h" #include "proxgui.h" #include "cmdmain.h" -#include "uart.h" #include "ui.h" #include "util.h" #include "cmdparser.h" @@ -137,7 +136,7 @@ main_loop(char *script_cmds_file, char *script_cmd, bool usb_present) { } write_history(".history"); - + if (usb_present) { conn.run = false; pthread_join(reader_thread, NULL); @@ -293,33 +292,9 @@ int main(int argc, char* argv[]) { // set global variables set_my_executable_path(); - - // open uart - if (!waitCOMPort) { - sp = uart_open(argv[1]); - } else { - printf("Waiting for Proxmark to appear on %s ", argv[1]); - fflush(stdout); - int openCount = 0; - do { - sp = uart_open(argv[1]); - msleep(1000); - printf("."); - fflush(stdout); - } while(++openCount < 20 && (sp == INVALID_SERIAL_PORT || sp == CLAIMED_SERIAL_PORT)); - printf("\n"); - } - // check result of uart opening - if (sp == INVALID_SERIAL_PORT) { - printf("ERROR: invalid serial port\n"); - usb_present = false; - } else if (sp == CLAIMED_SERIAL_PORT) { - printf("ERROR: serial port is claimed by another process\n"); - usb_present = false; - } else { - usb_present = true; - } + // try to open USB connection to Proxmark + usb_present = OpenProxmark(argv[1], waitCOMPort, 20); #ifdef HAVE_GUI #ifdef _WIN32 @@ -344,7 +319,7 @@ int main(int argc, char* argv[]) { // Clean up the port if (usb_present) { - uart_close(sp); + CloseProxmark(); } exit(0); From 854fdf15e074c6d282f08ff0bea555810f52c664 Mon Sep 17 00:00:00 2001 From: marshmellow42 Date: Thu, 3 May 2018 13:36:51 -0400 Subject: [PATCH 219/310] fix bug if -DWITH_ISO14443a_StandAlone is removed from makefile (#598) --- armsrc/appmain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 64768922..679a533a 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -31,7 +31,7 @@ #endif // Craig Young - 14a stand-alone code -#ifdef WITH_ISO14443a_StandAlone +#ifdef WITH_ISO14443a #include "iso14443a.h" #endif From dbac9ffb40d8a1a390918613f122bb0a0e60e904 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Fri, 4 May 2018 08:19:37 +0200 Subject: [PATCH 220/310] Change driver file proxmark3.inf to support both old and new Vendor/Product IDs (see http://www.proxmark.org/forum/viewtopic.php?pid=32072#p32072) --- driver/proxmark3.inf | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/driver/proxmark3.inf b/driver/proxmark3.inf index 122ebd55..52dbd251 100644 --- a/driver/proxmark3.inf +++ b/driver/proxmark3.inf @@ -3,16 +3,18 @@ Signature="$Windows NT$" Class=Ports ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318} Provider=%ProviderName% -DriverVer=31/05/2017,1.1.0.0 +DriverVer=03/05/2018,1.1.1.0 [MANUFACTURER] %ProviderName%=DeviceList, NTx86, NTamd64 [DeviceList.NTx86] %DeviceName%=DriverInstall,USB\VID_9AC4&PID_4B8F +%DeviceName_old%=DriverInstall,USB\VID_2d2d&PID_504d [DeviceList.NTamd64] %DeviceName%=DriverInstall,USB\VID_9AC4&PID_4B8F +%DeviceName_old%=DriverInstall,USB\VID_2d2d&PID_504d [DriverInstall] include=mdmcpq.inf @@ -30,3 +32,4 @@ HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider" [Strings] ProviderName = "proxmark.org" DeviceName = "Proxmark3" +DeviceName_old = "Proxmark3 (old)" From e475fce44d187f6a22f74263dd8e07e30dbd1672 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Fri, 4 May 2018 08:25:08 +0200 Subject: [PATCH 221/310] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7d20dcf..13fc97fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac ### Fixed - Changed start sequence in Qt mode (fix: short commands hangs main Qt thread) (Merlok) +- Changed driver file proxmark3.inf to support both old and new Product/Vendor IDs (piwi) ### Added - Added a bitbang mode to `lf cmdread` if delay is 0 the cmd bits turn off and on the antenna with 0 and 1 respectively (marshmellow) @@ -64,7 +65,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added lf hitag write 24, the command writes a block to hitag2 tags in crypto mode (henjo) ### Added -- Added hf mf hardnested, an attack working for hardened Mifare cards (EV1, Mifare Plus SL1) where hf mf nested fails +- Added hf mf hardnested, an attack working for hardened Mifare cards (EV1, Mifare Plus SL1) where hf mf nested fails (piwi) - Added experimental testmode write option for t55xx (danger) (marshmellow) - Added t55xx p1detect to `lf search` chip detections (marshmellow) - Added lf t55xx p1detect, detect page 1 of a t55xx tag based on E015 mfg code (marshmellow) From a468b4277ac961158b6984be92b1be186e30797a Mon Sep 17 00:00:00 2001 From: marshmellow42 Date: Fri, 4 May 2018 23:54:01 -0400 Subject: [PATCH 222/310] more graceful exit to lf search if no signal found --- client/cmdlf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/client/cmdlf.c b/client/cmdlf.c index 63dd737e..51e89581 100644 --- a/client/cmdlf.c +++ b/client/cmdlf.c @@ -947,6 +947,7 @@ int CmdLFfind(const char *Cmd) return 1; } } + PrintAndLog("\nNo Data Found! - maybe not an LF tag?\n"); return 0; } From babca445ffa9f7af6f5e1deb78476754c484a2b1 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Tue, 8 May 2018 07:54:49 +0200 Subject: [PATCH 223/310] rework of GetFromBigBuf() (#597) * this should fix crashes reported in issue #497 * don't allow receiver thread to write directly into arbitrary main thread's memory * instead use cmdBuffer[] for CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K as well * add timeout and warning options to GetFromBigBuf(), same as in WaitForResponseTimeoutW() * move GetFromBigBuf() from data.c to comms.c * remove data.c and data.h --- client/Makefile | 1 - client/cmddata.c | 12 +++----- client/cmdhf.c | 7 ++--- client/cmdhf14a.c | 1 - client/cmdhf14b.c | 1 - client/cmdhf15.c | 1 - client/cmdhficlass.c | 7 ++--- client/cmdhflegic.c | 7 ++--- client/cmdhflist.c | 1 - client/cmdhfmfu.c | 4 +-- client/cmdhw.c | 6 +--- client/cmdlf.c | 1 - client/cmdlfcotag.c | 6 ++-- client/cmdlfem4x.c | 4 +-- client/cmdlfhitag.c | 7 ++--- client/cmdlfio.c | 1 - client/cmdlft55xx.c | 4 +-- client/cmdlfti.c | 5 ++-- client/cmdmain.c | 1 - client/cmdscript.c | 1 - client/comms.c | 66 ++++++++++++++++++++++++++++++++++++++------ client/comms.h | 1 + client/data.c | 25 ----------------- client/data.h | 23 --------------- client/mifarehost.h | 2 +- client/util.c | 1 - client/util.h | 7 +++++ 27 files changed, 87 insertions(+), 116 deletions(-) delete mode 100644 client/data.c delete mode 100644 client/data.h diff --git a/client/Makefile b/client/Makefile index 883f3b6f..d4ac33b2 100644 --- a/client/Makefile +++ b/client/Makefile @@ -105,7 +105,6 @@ CMDSRCS = crapto1/crapto1.c\ crc64.c \ iso14443crc.c \ iso15693tools.c \ - data.c \ graph.c \ ui.c \ cmddata.c \ diff --git a/client/cmddata.c b/client/cmddata.c index 9bfe58f9..471665b7 100644 --- a/client/cmddata.c +++ b/client/cmddata.c @@ -12,7 +12,6 @@ #include // also included in util.h #include #include // for CmdNorm INT_MIN && INT_MAX -#include "data.h" // also included in util.h #include "cmddata.h" #include "util.h" #include "cmdmain.h" @@ -591,8 +590,7 @@ int CmdBitsamples(const char *Cmd) int cnt = 0; uint8_t got[12288]; - GetFromBigBuf(got,sizeof(got),0); - WaitForResponse(CMD_ACK,NULL); + GetFromBigBuf(got, sizeof(got), 0 , NULL, -1, false); for (int j = 0; j < sizeof(got); j++) { for (int k = 0; k < 8; k++) { @@ -1131,8 +1129,7 @@ int CmdHexsamples(const char *Cmd) return 0; } - GetFromBigBuf(got,requested,offset); - WaitForResponse(CMD_ACK,NULL); + GetFromBigBuf(got, requested, offset, NULL, -1, false); i = 0; for (j = 0; j < requested; j++) { @@ -1200,10 +1197,9 @@ int getSamples(int n, bool silent) n = sizeof(got); if (!silent) PrintAndLog("Reading %d bytes from device memory\n", n); - GetFromBigBuf(got,n,0); - if (!silent) PrintAndLog("Data fetched"); UsbCommand response; - WaitForResponse(CMD_ACK, &response); + GetFromBigBuf(got, n, 0, &response, -1, false); + if (!silent) PrintAndLog("Data fetched"); uint8_t bits_per_sample = 8; //Old devices without this feature would send 0 at arg[0] diff --git a/client/cmdhf.c b/client/cmdhf.c index 82313ae0..4a672255 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -14,7 +14,6 @@ #include #include "proxmark3.h" #include "util.h" -#include "data.h" #include "ui.h" #include "iso14443crc.h" #include "parity.h" @@ -497,8 +496,7 @@ int CmdHFList(const char *Cmd) trace = malloc(USB_CMD_DATA_SIZE); // Query for the size of the trace UsbCommand response; - GetFromBigBuf(trace, USB_CMD_DATA_SIZE, 0); - WaitForResponse(CMD_ACK, &response); + GetFromBigBuf(trace, USB_CMD_DATA_SIZE, 0, &response, -1, false); traceLen = response.arg[2]; if (traceLen > USB_CMD_DATA_SIZE) { uint8_t *p = realloc(trace, traceLen); @@ -508,8 +506,7 @@ int CmdHFList(const char *Cmd) return 2; } trace = p; - GetFromBigBuf(trace, traceLen, 0); - WaitForResponse(CMD_ACK, NULL); + GetFromBigBuf(trace, traceLen, 0, NULL, -1, false); } } diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 480923d6..4684ae29 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -20,7 +20,6 @@ #include "util.h" #include "util_posix.h" #include "iso14443crc.h" -#include "data.h" #include "proxmark3.h" #include "ui.h" #include "cmdparser.h" diff --git a/client/cmdhf14b.c b/client/cmdhf14b.c index 36932cbd..bae40c09 100644 --- a/client/cmdhf14b.c +++ b/client/cmdhf14b.c @@ -15,7 +15,6 @@ #include #include "iso14443crc.h" #include "proxmark3.h" -#include "data.h" #include "graph.h" #include "util.h" #include "ui.h" diff --git a/client/cmdhf15.c b/client/cmdhf15.c index 8ddbea89..570a0420 100644 --- a/client/cmdhf15.c +++ b/client/cmdhf15.c @@ -28,7 +28,6 @@ #include #include "proxmark3.h" -#include "data.h" #include "graph.h" #include "ui.h" #include "util.h" diff --git a/client/cmdhficlass.c b/client/cmdhficlass.c index d42f7eef..e99c3285 100644 --- a/client/cmdhficlass.c +++ b/client/cmdhficlass.c @@ -16,7 +16,6 @@ #include #include #include "iso14443crc.h" // Can also be used for iClass, using 0xE012 as CRC-type -#include "data.h" #include "proxmark3.h" #include "ui.h" #include "cmdparser.h" @@ -750,8 +749,7 @@ int CmdHFiClassReader_Dump(const char *Cmd) { blocksRead = (sizeof(tag_data)/8) - blockno; } // response ok - now get bigbuf content of the dump - GetFromBigBuf(tag_data+(blockno*8), blocksRead*8, startindex); - WaitForResponse(CMD_ACK,NULL); + GetFromBigBuf(tag_data+(blockno*8), blocksRead*8, startindex, NULL, -1, false); size_t gotBytes = blocksRead*8 + blockno*8; // try AA2 @@ -793,8 +791,7 @@ int CmdHFiClassReader_Dump(const char *Cmd) { blocksRead = (sizeof(tag_data) - gotBytes)/8; } // get dumped data from bigbuf - GetFromBigBuf(tag_data+gotBytes, blocksRead*8, startindex); - WaitForResponse(CMD_ACK,NULL); + GetFromBigBuf(tag_data+gotBytes, blocksRead*8, startindex, NULL, -1, false); gotBytes += blocksRead*8; } else { //field is still on - turn it off... diff --git a/client/cmdhflegic.c b/client/cmdhflegic.c index c9f3485e..9c9613ae 100644 --- a/client/cmdhflegic.c +++ b/client/cmdhflegic.c @@ -12,7 +12,6 @@ #include #include #include "proxmark3.h" -#include "data.h" #include "ui.h" #include "cmdparser.h" #include "cmdhflegic.h" @@ -64,8 +63,7 @@ int CmdLegicDecode(const char *Cmd) char token_type[4]; // copy data from proxmark into buffer - GetFromBigBuf(data_buf,sizeof(data_buf),0); - WaitForResponse(CMD_ACK,NULL); + GetFromBigBuf(data_buf, sizeof(data_buf), 0, NULL, -1, false); // Output CDF System area (9 bytes) plus remaining header area (12 bytes) @@ -294,8 +292,7 @@ int CmdLegicSave(const char *Cmd) return -1; } - GetFromBigBuf(got,requested,offset); - WaitForResponse(CMD_ACK,NULL); + GetFromBigBuf(got, requested, offset, NULL, -1, false); for (int j = 0; j < requested; j += 8) { fprintf(f, "%02x %02x %02x %02x %02x %02x %02x %02x\n", diff --git a/client/cmdhflist.c b/client/cmdhflist.c index cf69abba..c87aa237 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -16,7 +16,6 @@ #include #include #include "util.h" -#include "data.h" #include "ui.h" #include "iso14443crc.h" #include "parity.h" diff --git a/client/cmdhfmfu.c b/client/cmdhfmfu.c index 63c41728..ac76c8dd 100644 --- a/client/cmdhfmfu.c +++ b/client/cmdhfmfu.c @@ -22,7 +22,6 @@ #include "mifare.h" #include "util.h" #include "protocols.h" -#include "data.h" #define MAX_UL_BLOCKS 0x0f #define MAX_ULC_BLOCKS 0x2b @@ -1325,8 +1324,7 @@ int CmdHF14AMfUDump(const char *Cmd){ PrintAndLog("Data exceeded Buffer size!"); bufferSize = sizeof(data); } - GetFromBigBuf(data, bufferSize, startindex); - WaitForResponse(CMD_ACK,NULL); + GetFromBigBuf(data, bufferSize, startindex, NULL, -1, false); Pages = bufferSize/4; // Load lock bytes. diff --git a/client/cmdhw.c b/client/cmdhw.c index 8f7243ad..4ede122f 100644 --- a/client/cmdhw.c +++ b/client/cmdhw.c @@ -18,7 +18,6 @@ #include "cmdhw.h" #include "cmdmain.h" #include "cmddata.h" -#include "data.h" /* low-level hardware control */ @@ -429,13 +428,10 @@ int CmdVersion(const char *Cmd) int CmdStatus(const char *Cmd) { - uint8_t speed_test_buffer[USB_CMD_DATA_SIZE]; - sample_buf = speed_test_buffer; - clearCommandBuffer(); UsbCommand c = {CMD_STATUS}; SendCommand(&c); - if (!WaitForResponseTimeout(CMD_ACK,&c,1900)) { + if (!WaitForResponseTimeout(CMD_ACK, &c, 1900)) { PrintAndLog("Status command failed. USB Speed Test timed out"); } return 0; diff --git a/client/cmdlf.c b/client/cmdlf.c index 51e89581..285d377a 100644 --- a/client/cmdlf.c +++ b/client/cmdlf.c @@ -22,7 +22,6 @@ #include "graph.h" // for graph data #include "cmdparser.h" // for getting cli commands included in cmdmain.h #include "cmdmain.h" // for sending cmds to device -#include "data.h" // for GetFromBigBuf #include "cmddata.h" // for `lf search` #include "cmdlfawid.h" // for awid menu #include "cmdlfem4x.h" // for em4x menu diff --git a/client/cmdlfcotag.c b/client/cmdlfcotag.c index 6d035e8c..ca163480 100644 --- a/client/cmdlfcotag.c +++ b/client/cmdlfcotag.c @@ -13,7 +13,6 @@ #include "proxmark3.h" #include "ui.h" #include "cmddata.h" -#include "data.h" #include "cmdlfcotag.h" #include "lfdemod.h" #include "usb_cmd.h" @@ -99,10 +98,9 @@ int CmdCOTAGRead(const char *Cmd) { getSamples(0, true); break; } case 1: { - GetFromBigBuf(DemodBuffer, COTAG_BITS, 0); - DemodBufferLen = COTAG_BITS; UsbCommand response; - if ( !WaitForResponseTimeout(CMD_ACK, &response, 1000) ) { + DemodBufferLen = COTAG_BITS; + if (!GetFromBigBuf(DemodBuffer, COTAG_BITS, 0, &response, 1000, true)) { PrintAndLog("timeout while waiting for reply."); return -1; } diff --git a/client/cmdlfem4x.c b/client/cmdlfem4x.c index 6d562be0..ff2ae5b0 100644 --- a/client/cmdlfem4x.c +++ b/client/cmdlfem4x.c @@ -15,7 +15,6 @@ #include "proxmark3.h" #include "ui.h" #include "util.h" -#include "data.h" #include "graph.h" #include "cmdparser.h" #include "cmddata.h" @@ -804,8 +803,7 @@ int usage_lf_em_read(void) { bool downloadSamplesEM() { // 8 bit preamble + 32 bit word response (max clock (128) * 40bits = 5120 samples) uint8_t got[6000]; - GetFromBigBuf(got, sizeof(got), 0); - if ( !WaitForResponseTimeout(CMD_ACK, NULL, 4000) ) { + if (!GetFromBigBuf(got, sizeof(got), 0, NULL, 4000, true)) { PrintAndLog("command execution time out"); return false; } diff --git a/client/cmdlfhitag.c b/client/cmdlfhitag.c index 73c02a14..26ba9238 100644 --- a/client/cmdlfhitag.c +++ b/client/cmdlfhitag.c @@ -11,7 +11,6 @@ #include #include #include -#include "data.h" #include "proxmark3.h" #include "ui.h" #include "cmdparser.h" @@ -34,8 +33,7 @@ int CmdLFHitagList(const char *Cmd) // Query for the actual size of the trace UsbCommand response; - GetFromBigBuf(got, USB_CMD_DATA_SIZE, 0); - WaitForResponse(CMD_ACK, &response); + GetFromBigBuf(got, USB_CMD_DATA_SIZE, 0, &response, -1, false); uint16_t traceLen = response.arg[2]; if (traceLen > USB_CMD_DATA_SIZE) { uint8_t *p = realloc(got, traceLen); @@ -45,8 +43,7 @@ int CmdLFHitagList(const char *Cmd) return 2; } got = p; - GetFromBigBuf(got, traceLen, 0); - WaitForResponse(CMD_ACK,NULL); + GetFromBigBuf(got, traceLen, 0, NULL, -1, false); } PrintAndLog("recorded activity (TraceLen = %d bytes):"); diff --git a/client/cmdlfio.c b/client/cmdlfio.c index 4ccd5538..f2f2df06 100644 --- a/client/cmdlfio.c +++ b/client/cmdlfio.c @@ -15,7 +15,6 @@ #include #include "cmdlfio.h" #include "proxmark3.h" -#include "data.h" #include "graph.h" #include "ui.h" #include "cmdparser.h" diff --git a/client/cmdlft55xx.c b/client/cmdlft55xx.c index c5a6dd3f..a9fbb71e 100644 --- a/client/cmdlft55xx.c +++ b/client/cmdlft55xx.c @@ -21,7 +21,6 @@ #include "cmdlf.h" #include "cmdlft55xx.h" #include "util.h" -#include "data.h" #include "lfdemod.h" #include "cmdhf14a.h" //for getTagInfo #include "protocols.h" @@ -1355,8 +1354,7 @@ int CmdResetRead(const char *Cmd) { } uint8_t got[BIGBUF_SIZE-1]; - GetFromBigBuf(got,sizeof(got),0); - WaitForResponse(CMD_ACK,NULL); + GetFromBigBuf(got, sizeof(got), 0, NULL, -1 , 0); setGraphBuf(got, sizeof(got)); return 1; } diff --git a/client/cmdlfti.c b/client/cmdlfti.c index 5dae9c0e..7e0af94b 100644 --- a/client/cmdlfti.c +++ b/client/cmdlfti.c @@ -8,16 +8,17 @@ // Low frequency TI commands //----------------------------------------------------------------------------- +#include "cmdlfti.h" + #include #include #include #include "crc16.h" #include "proxmark3.h" -#include "data.h" #include "ui.h" #include "graph.h" #include "cmdparser.h" -#include "cmdlfti.h" +#include "util.h" static int CmdHelp(const char *Cmd); diff --git a/client/cmdmain.c b/client/cmdmain.c index a45e3430..c3b46145 100644 --- a/client/cmdmain.c +++ b/client/cmdmain.c @@ -17,7 +17,6 @@ #include #include "cmdparser.h" #include "proxmark3.h" -#include "data.h" #include "usb_cmd.h" #include "ui.h" #include "cmdhf.h" diff --git a/client/cmdscript.c b/client/cmdscript.c index 0d19f496..7b0e9000 100644 --- a/client/cmdscript.c +++ b/client/cmdscript.c @@ -17,7 +17,6 @@ #include "proxmark3.h" #include "scripting.h" -#include "data.h" #include "ui.h" #include "graph.h" #include "cmdparser.h" diff --git a/client/comms.c b/client/comms.c index 97d58ef9..b62f6fda 100644 --- a/client/comms.c +++ b/client/comms.c @@ -15,7 +15,6 @@ #include "uart.h" #include "ui.h" #include "common.h" -#include "data.h" #include "util_posix.h" // Declare globals. @@ -173,6 +172,10 @@ int getCommand(UsbCommand* response) } +//---------------------------------------------------------------------------------- +// Entry point into our code: called whenever we received a packet over USB. +// Handle debug commands directly, store all other commands in circular buffer. +//---------------------------------------------------------------------------------- static void UsbCommandReceived(UsbCommand *UC) { switch(UC->cmd) { @@ -191,13 +194,6 @@ static void UsbCommandReceived(UsbCommand *UC) return; } break; - case CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K: { - // FIXME: This does unsanitised copies into memory when we don't know - // the size of the buffer. - memcpy(sample_buf+(UC->arg[0]),UC->d.asBytes,UC->arg[1]); - return; - } break; - default: storeCommand(UC); break; @@ -242,6 +238,58 @@ __attribute__((force_align_arg_pointer)) } + +/** + * Data transfer from Proxmark to client. This method times out after + * ms_timeout milliseconds. + * @brief GetFromBigBuf + * @param dest Destination address for transfer + * @param bytes number of bytes to be transferred + * @param start_index offset into Proxmark3 BigBuf[] + * @param response struct to copy last command (CMD_ACK) into + * @param ms_timeout timeout in milliseconds + * @param show_warning display message after 2 seconds + * @return true if command was returned, otherwise false + */ +bool GetFromBigBuf(uint8_t *dest, int bytes, int start_index, UsbCommand *response, size_t ms_timeout, bool show_warning) +{ + UsbCommand c = {CMD_DOWNLOAD_RAW_ADC_SAMPLES_125K, {start_index, bytes, 0}}; + SendCommand(&c); + + uint64_t start_time = msclock(); + + UsbCommand resp; + if (response == NULL) { + response = &resp; + } + + int bytes_completed = 0; + while(true) { + if (getCommand(response)) { + if (response->cmd == CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K) { + int copy_bytes = MIN(bytes - bytes_completed, response->arg[1]); + memcpy(dest + response->arg[0], response->d.asBytes, copy_bytes); + bytes_completed += copy_bytes; + } else if (response->cmd == CMD_ACK) { + return true; + } + } + + if (msclock() - start_time > ms_timeout) { + break; + } + + if (msclock() - start_time > 2000 && show_warning) { + PrintAndLog("Waiting for a response from the proxmark..."); + PrintAndLog("You can cancel this operation by pressing the pm3 button"); + show_warning = false; + } + } + + return false; +} + + /** * Waits for a certain response type. This method waits for a maximum of * ms_timeout milliseconds for a specified response command. @@ -249,7 +297,7 @@ __attribute__((force_align_arg_pointer)) * @param cmd command to wait for, or CMD_UNKNOWN to take any command. * @param response struct to copy received command into. * @param ms_timeout - * @param show_warning + * @param show_warning display message after 2 seconds * @return true if command was returned, otherwise false */ bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning) { diff --git a/client/comms.h b/client/comms.h index 616f7ddb..111677ad 100644 --- a/client/comms.h +++ b/client/comms.h @@ -44,5 +44,6 @@ void clearCommandBuffer(); bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning); bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout); bool WaitForResponse(uint32_t cmd, UsbCommand* response); +bool GetFromBigBuf(uint8_t *dest, int bytes, int start_index, UsbCommand *response, size_t ms_timeout, bool show_warning); #endif // COMMS_H_ diff --git a/client/data.c b/client/data.c deleted file mode 100644 index 4d7d1e41..00000000 --- a/client/data.c +++ /dev/null @@ -1,25 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (C) 2010 iZsh -// -// 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. -//----------------------------------------------------------------------------- -// Data utilities -//----------------------------------------------------------------------------- - -#include -#include -#include "data.h" -#include "ui.h" -#include "proxmark3.h" -#include "cmdmain.h" - -uint8_t* sample_buf; - -void GetFromBigBuf(uint8_t *dest, int bytes, int start_index) -{ - sample_buf = dest; - UsbCommand c = {CMD_DOWNLOAD_RAW_ADC_SAMPLES_125K, {start_index, bytes, 0}}; - SendCommand(&c); -} diff --git a/client/data.h b/client/data.h deleted file mode 100644 index 7d85e1f1..00000000 --- a/client/data.h +++ /dev/null @@ -1,23 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (C) 2010 iZsh -// -// 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. -//----------------------------------------------------------------------------- -// Data utilities -//----------------------------------------------------------------------------- - -#ifndef DATA_H__ -#define DATA_H__ - -#include - -#define FILE_PATH_SIZE 1000 - -extern uint8_t* sample_buf; -#define arraylen(x) (sizeof(x)/sizeof((x)[0])) - -void GetFromBigBuf(uint8_t *dest, int bytes, int start_index); - -#endif diff --git a/client/mifarehost.h b/client/mifarehost.h index 457a879f..6a37fef1 100644 --- a/client/mifarehost.h +++ b/client/mifarehost.h @@ -13,8 +13,8 @@ #include #include -#include "data.h" #include "crapto1/crapto1.h" +#include "util.h" // defaults // timeout in units. (ms * 106)/10 or us*0.0106 diff --git a/client/util.c b/client/util.c index 5dc9a455..4f84e3b5 100644 --- a/client/util.c +++ b/client/util.c @@ -16,7 +16,6 @@ #include #include #include -#include "data.h" #ifdef _WIN32 #include diff --git a/client/util.h b/client/util.h index 2e64d7ca..878938f4 100644 --- a/client/util.h +++ b/client/util.h @@ -24,10 +24,17 @@ #ifndef MAX # define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif +#ifndef arraylen +#define arraylen(x) (sizeof(x)/sizeof((x)[0])) +#endif #define EVEN 0 #define ODD 1 +#ifndef FILE_PATH_SIZE +#define FILE_PATH_SIZE 2000 +#endif + extern int ukbhit(void); extern void AddLogLine(char *fileName, char *extData, char *c); From 0000a4cda2598535c278570465be973743951c5d Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Fri, 11 May 2018 05:24:02 +0200 Subject: [PATCH 224/310] fix HitagS simulation erors (issue #605) (#606) --- armsrc/hitagS.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/armsrc/hitagS.c b/armsrc/hitagS.c index dc2281b9..7d36d84b 100644 --- a/armsrc/hitagS.c +++ b/armsrc/hitagS.c @@ -985,39 +985,39 @@ void SimulateHitagSTag(bool tag_mem_supplied, byte_t* data) { tag.max_page=0; //con1 tag.auth=0; - if((tag.pages[1][2]&0x80)==1) + if (tag.pages[1][2]&0x80) tag.auth=1; tag.LCON=0; - if((tag.pages[1][2]&0x2)==1) + if (tag.pages[1][2]&0x2) tag.LCON=1; tag.LKP=0; - if((tag.pages[1][2]&0x1)==1) + if (tag.pages[1][2]&0x1) tag.LKP=1; //con2 //0=read write 1=read only tag.LCK7=0; - if((tag.pages[1][1]&0x80)==1) + if (tag.pages[1][1]&0x80) tag.LCK7=1; tag.LCK6=0; - if((tag.pages[1][1]&0x40)==1) + if (tag.pages[1][1]&0x40) tag.LCK6=1; tag.LCK5=0; - if((tag.pages[1][1]&0x20)==1) + if (tag.pages[1][1]&0x20) tag.LCK5=1; tag.LCK4=0; - if((tag.pages[1][1]&0x10)==1) + if (tag.pages[1][1]&0x10) tag.LCK4=1; tag.LCK3=0; - if((tag.pages[1][1]&0x8)==1) + if (tag.pages[1][1]&0x8) tag.LCK3=1; tag.LCK2=0; - if((tag.pages[1][1]&0x4)==1) + if (tag.pages[1][1]&0x4) tag.LCK2=1; tag.LCK1=0; - if((tag.pages[1][1]&0x2)==1) + if (tag.pages[1][1]&0x2) tag.LCK1=1; tag.LCK0=0; - if((tag.pages[1][1]&0x1)==1) + if (tag.pages[1][1]&0x1) tag.LCK0=1; // Set up simulator mode, frequency divisor which will drive the FPGA From e8924be8bac6e8ddc91d1950368ef51c454a6e55 Mon Sep 17 00:00:00 2001 From: marshmellow42 Date: Tue, 15 May 2018 02:19:49 -0400 Subject: [PATCH 225/310] fix gui (plot) bugs (#604) G keypress event would cause a crash if no data Q keypress didn't work. Thanks to @iceman1001 for identifying and providing the fixes. --- client/proxguiqt.cpp | 5 ++++- client/proxguiqt.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/client/proxguiqt.cpp b/client/proxguiqt.cpp index dc8279b5..ab0976cc 100644 --- a/client/proxguiqt.cpp +++ b/client/proxguiqt.cpp @@ -269,6 +269,7 @@ int Plot::xCoordOf(int i, QRect r ) int Plot::yCoordOf(int v, QRect r, int maxVal) { int z = (r.bottom() - r.top())/2; + if ( maxVal == 0 ) maxVal++; return -(z * v) / maxVal + z; } @@ -579,6 +580,8 @@ Plot::Plot(QWidget *parent) : QWidget(parent), GraphStart(0), GraphPixelsPerPoin CursorBPos = 0; setWindowTitle(tr("Sliders")); + + master = parent; } void Plot::closeEvent(QCloseEvent *event) @@ -688,7 +691,7 @@ void Plot::keyPressEvent(QKeyEvent *event) break; case Qt::Key_Q: - this->hide(); + master->hide(); break; default: diff --git a/client/proxguiqt.h b/client/proxguiqt.h index 45a65b04..e43311ee 100644 --- a/client/proxguiqt.h +++ b/client/proxguiqt.h @@ -28,6 +28,7 @@ class Plot: public QWidget { private: + QWidget *master; int GraphStart; double GraphPixelsPerPoint; int CursorAPos; From 803674b2d60e78d29c45fc08db2c454c6c6389e1 Mon Sep 17 00:00:00 2001 From: Dominic Celiano Date: Mon, 28 May 2018 20:32:48 +0100 Subject: [PATCH 226/310] Added mifarePlus.lua script for communication with Mifare Plus. (#593) Personalize MifarePlus EV1 cards --- .DS_Store | Bin 0 -> 12292 bytes client/scripts/mifarePlus.lua | 340 ++++++++++++++++++++++++++++++++++ 2 files changed, 340 insertions(+) create mode 100644 .DS_Store create mode 100644 client/scripts/mifarePlus.lua diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..ecae787283f908888f99eb9d6fa860cbb5da7cff GIT binary patch literal 12292 zcmeHN3s6+o89pDP-d(S9AqdOLvWtL-A}uD5c~m+v<}s7DX`0DQlT6ZdVyDyo=bXFC-USow zXm#w~nS0MY=iGDep2Pp2@B98A#uys!Y(--jt1L5`FP?*YYEiJqwIN0J2 z2G)kWyuZ^O^!N7$!rfZB+vRun`&|+5GQYpwx5rCuX<>IL==ZM;hJE}s8@eLiT@l_l zrmJgXI2ejFikEe3T7TGkm6zV-8}K&K8^c{b`XnEy!E|I=c5Z&robu|r`o@toEj>fc zRI@qjasW{t)|Waxw5ixvARS_N2#gH z9qjF;-yN3CPHTgrJ*!;Jzuo2vN7nRt1N3=5i>5rQbpvl-=J9wvilP)a@7pIVvB1f9 zemV+d!I-JQS)t!6%*Lu%6Ki8zS%?j>+t@*NgdJta*<Z#2|)oJ#NHJ*oV7t z0Ecil9>BNp9XyKf;YmD;-{S;E@CUq(x9}d`m%lHC+b%y=%OZNkX?^ar8p?>(l9OS} zx3wrr+zP~I8=JrQlbbXU(%IN7R?D2MgKc9$c0Jq24%4KcU_WKQVJFxt><#t~`-uI6 zoo4?8gNde_gM7?EDdwRJ3(Od1=%;hTD5BvNfyX)wRNhhrIK!t zy-Vt;cM>TC+1t=ay^Jdu(|MryobCRR=sjn-lZe`1L9{*}#l&(KVkwpp&s|PTcMb8} zc64AJaow$Op@+CGC-y;)b?w-N0Zp!S}QB!^?Dq|J1 zVxDT=(oRaXJ>rV=hZ|g>O}w_&71`0F-**Iq{@A^%XFX}zZY{Gh7>Kxh0dGhQChbU% zZ0;me2o5#``vab^cu%|RP!yA5>eehnLzQ*arH-nqs=cL-q1xK{rH%#FReSfElG3F=PVGa{>79&Dk5sy4MY_59Ja}nLkDr73 zR;OF-1+#_gjZ78pXg0^G>@#_c@UC?D$-McftZa+LsJ^vnX;aO)mb|g*oA+39Ei)6; zw-``lDIQhdqGgV`bX0wdq2=bvIQ1=>s?9Y9^(`9f%!?D$w`i`nILA}pd=?G47E7G= z?;6)EoL*$eDNQr&-IZ?eCz zw@7)v&$TxIGdZX#9CRiMQABc->+TwI)hmUYHj@HhgDz|k`de!6od}U2y$Ltt7JMFG zB_Yb)k?Zc`cuZPc%mrIw7AS7;6cxIg#YxQ0@$AsHJkz}L;@IKKsx&^rg1my_xfKf* zEj`ajKqgANyV-VjNWh=AJ8U)`33%01Xfq2W;FZ~h)W?y4S4!tmk3<4qt*kQJbR^)_ znuTVwjs(2A_)6-PNRX3l$uci9H&Q6eZ_?N~ncMvo;CY-(?F2i?UMDkqhfM5a_HRt2 zC`UHMIdWk`9$`QU=8AYn4Qf$`MQ9X8wi0V8z{9a%Jq37Na8r=SD}p>Bgb^Vu_$&o_ zZqVZ&`|%~*iLc;Zd|et>%-EV`J$l8%qsEr7)2C95rjKoBuFG|=k4-Rcw|^w+Wn*Hp z&1Gt9EDCzj;;`fTdMvDX;X<=K)M?AJss>Uge2e$Yq#$TK!y_*fap4Oan&n?!>aJ}LkM*UKQ!f0%YQy9_xd6dt5V)UF7m$%tRlqQ%= zX@U$`$u?(`ZC0b6?6MiHB0;bookUVLQj&mMri`6)>l`5K=HLR+;;a?6nawAHDL4GK&=SFbt~6d3I>c(5(CkFBiOi_U2*+=!pL0y8`5{09#1lqZ! z2B58|Gyv^_8UxTST5JH?rOp`8Mge_=iQrs0Q|kIGp!e_WG==ge3h+I9=;gq>n?iXU zdO10~RY30niWeLd(EAX+NufLry~lXm08im(I$h+UyjLY=#-v>$MSLw0e-Z%L@P8?n zB{p3o{ZH%8v7e6FJpaG^%>Vx<9!g%4E0A1)%X0;g+SK0EKy!;G9TKO>bL|c~x6;WE z{@k!E#Spsa>3Gp6Qt{MQL63-o?w$OG%3)d39UcGke*&N!jUkbj#Q#s1-bwtwunE13 Hg#Z5m#5c2R literal 0 HcmV?d00001 diff --git a/client/scripts/mifarePlus.lua b/client/scripts/mifarePlus.lua new file mode 100644 index 00000000..061ff736 --- /dev/null +++ b/client/scripts/mifarePlus.lua @@ -0,0 +1,340 @@ +local cmds = require('commands') +local lib14a = require('read14a') +getopt = require('getopt') -- Used to get command line arguments + +example = "script run mifarePlus" +author = "Dominic Celiano" +desc = +[[ +Purpose: Lua script to communicate with the Mifare Plus EV1, including personalization (setting the keys) and proximity check. Manually edit the file to add to the commands you can send the card. + +Please read the NXP manual before running this script to prevent making irreversible changes. Also note: + - The Mifare Plus must start in SL0 for personalization. Card can then be moved to SL1 or SL3. + - The keys are hardcoded in the script to "00...". Unless you change this, only use this script for testing purposes. + - Make sure you choose your card size correctly (2kB or 4kB). + +Small changes can be to made this script to communicate with the Mifare Plus S, X, or SE. + +Arguments: + -h : this help +]] + +SIXTEEN_BYTES_ZEROS = "00000000000000000000000000000000" + +GETVERS_INIT = "0360" -- Begins the GetVersion command +GETVERS_CONT = "03AF" -- Continues the GetVersion command +POWEROFF = "OFF" +WRITEPERSO = "03A8" +COMMITPERSO = "03AA" +AUTH_FIRST = "0370" +AUTH_CONT = "0372" +AUTH_NONFIRST = "0376" +PREPAREPC = "03F0" +PROXIMITYCHECK = "03F2" +VERIFYPC = "03FD" +READPLAINNOMACUNMACED = "0336" + +--- +-- This is only meant to be used when errors occur +function oops(err) + print("ERROR: ",err) +end + +--- +-- Usage help +function help() + print(desc) + print("Example usage") + print(example) +end + +--- +-- Used to send raw data to the firmware to subsequently forward the data to the card. +function sendRaw(rawdata, crc, power) + print((": %s"):format(rawdata)) + + local flags = lib14a.ISO14A_COMMAND.ISO14A_RAW + if crc then + flags = flags + lib14a.ISO14A_COMMAND.ISO14A_APPEND_CRC + end + if power then + flags = flags + lib14a.ISO14A_COMMAND.ISO14A_NO_DISCONNECT + end + + local command = Command:new{cmd = cmds.CMD_READER_ISO_14443a, + arg1 = flags, -- Send raw + arg2 = string.len(rawdata) / 2, -- arg2 contains the length, which is half the length of the ASCII-string rawdata + data = rawdata} + local ignore_response = false + local result, err = lib14a.sendToDevice(command, ignore_response) + if result then + --unpack the first 4 parts of the result as longs, and the last as an extremely long string to later be cut down based on arg1, the number of bytes returned + local count,cmd,arg1,arg2,arg3,data = bin.unpack('LLLLH512',result) + returned_bytes = string.sub(data, 1, arg1 * 2) + if returned_bytes ~= "" then + print((": %s"):format(returned_bytes)) -- need to multiply by 2 because the hex digits are actually two bytes when they are strings + end + return returned_bytes + else + err = "Error sending the card raw data." + oops(err) + end +end + +function writePerso() + -- Used to write any data, including the keys (Key A and Key B), for all the sectors. + -- writePerso() command parameters: + -- 1 byte - 0xA8 - Command Code + -- 2 bytes - Address of the first block or key to be written to (40 blocks are numbered from 0x0000 to 0x00FF) + -- X bytes - The data bytes to be written, starting from the first block. Amount of data sent can be from 16 to 240 bytes in 16 byte increments. This allows + -- up to 15 blocks to be written at once. + -- response from PICC: + -- 0x90 - OK + -- 0x09 - targeted block is invalid for writes, i.e. block 0, which contains manufacturer data + -- 0x0B - command invalid + -- 0x0C - unexpected command length + + + + cardsize = 4 --need to set to 4 for 4k or 2 for 2k + if(cardsize == 4) then + numsectors = 39 + elseif(cardsize == 2) then + numsectors = 31 + else + oops("Invalid card size") + end + + -- Write to the AES sector keys + print("Setting AES Sector keys") + for i=0,numsectors do --for each sector number + local keyA_block = "40" .. string.format("%02x", i * 2) + local keyB_block = "40" .. string.format("%02x", (i * 2) + 1) + --Can also calculate the keys fancily to make them unique, if desired + keyA = SIXTEEN_BYTES_ZEROS + keyB = SIXTEEN_BYTES_ZEROS + writeBlock(keyA_block, keyA) + writeBlock(keyB_block, keyB) + end + print("Finished setting AES Sector keys") + + print("Setting misc keys which haven't been set yet.") + --CardMasterKey + blocknum = "9000" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --CardConfigurationKey + blocknum = "9001" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --L3SwitchKey + blocknum = "9003" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --SL1CardAuthKey + blocknum = "9004" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --L3SectorSwitchKey + blocknum = "9006" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --L1L3MixSectorSwitchKey + blocknum = "9007" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --VC Keys + --VCProximityKey + blocknum = "A001" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --VCSelectENCKey + blocknum = "A080" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --VCSelectMACKey + blocknum = "A081" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --TransactionMACKey1 + blocknum = "C000" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --TransactionMACConfKey1 + blocknum = "C001" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + print("Finished setting misc keys.") + + print("WritePerso finished! Card is ready to move into new security level.") +end + +function writeBlock(blocknum, data) + -- Method writes 16 bytes of the string sent (data) to the specified block number + -- The block numbers sent to the card need to be in little endian format (i.e. block 0x0001 is sent as 0x1000) + blocknum_little_endian = string.sub(blocknum, 3, 4) .. string.sub(blocknum, 1, 2) + commandString = WRITEPERSO .. blocknum_little_endian .. data --Write 16 bytes (32 hex chars). + response = sendRaw(commandString, true, true) --0x90 is returned upon success + if string.sub(response, 3, 4) ~= "90" then + oops(("error occurred while trying to write to block %s"):format(blocknum)) + end +end + +function authenticateAES() + -- Used to try to authenticate with the AES keys we programmed into the card, to ensure the authentication works correctly. + commandString = AUTH_FIRST + commandString = commandString .. "" +end + +function getVersion() + sendRaw(GETVERS_INIT, true, true) + sendRaw(GETVERS_CONT, true, true) + sendRaw(GETVERS_CONT, true, true) +end + +function commitPerso(SL) + --pass SL as "01" to move to SL1 or "03" to move to SL3. + commandString = COMMITPERSO .. SL + response = sendRaw(commandString, true, true) --0x90 is returned upon success + if string.sub(response, 3, 4) ~= "90" then + oops("error occurred while trying to switch security level") + end +end + +function calculateMAC(MAC_input) + -- Pad the input if it is not a multiple of 16 bytes (32 nibbles). + if(string.len(MAC_input) % 32 ~= 0) then + MAC_input = MAC_input .. "80" + end + while(string.len(MAC_input) % 32 ~= 0) do + MAC_input = MAC_input .. "0" + end + print("Padded MAC Input = " .. MAC_input .. ", length (bytes) = " .. string.len(MAC_input) / 2) + + --The MAC would actually be calculated here, and the output stored in raw_output + raw_output = "00010203040506070001020304050607" -- Dummy filler for now of 16-byte output. To be filled with actual MAC for testing purposes. + + -- The final 8-byte MAC output is a concatenation of every 2nd byte starting from the second MSB. + final_output = "" + j = 3 + for i = 1,8 do + final_output = final_output .. string.sub(RndR, j, j + 1) .. string.sub(RndC, j, j + 1) + j = j + 4 + end + return final_output +end + +function proximityCheck() + --PreparePC-- + commandString = PREPAREPC + response = sendRaw(commandString, true, true) + if(response == "") then + print("ERROR: This card does not support the Proximity Check command.") + return + end + OPT = string.sub(response, 5, 6) + if(tonumber(OPT) == 1) then + pps_present = true + else + pps_present = false + end + pubRespTime = string.sub(response, 7, 10) + if(pps_present == true) then + pps = string.sub(response, 11, 12) + else + pps = nil + end + print("OPT = " .. OPT .. " pubRespTime = " .. pubRespTime .. " pps = " .. pps) + + --PC-- + RndC = "0001020304050607" --Random Challenge + num_rounds = 8 --Needs to be 1, 2, 4, or 8 + part_len = 8 / num_rounds + j = 1 + RndR = "" + for i = 1,num_rounds do + pRndC = "" + for q = 1,(part_len*2) do + pRndC = pRndC .. string.sub(RndC,j,j) + j = j + 1 + end + commandString = PROXIMITYCHECK .. "0" .. tostring(part_len) .. pRndC + pRndR = string.sub(sendRaw(commandString, true, true), 3, 3+part_len) + RndR = RndR .. pRndR + end + print("RndC = " .. RndC .. " RndR = " .. RndR) + + --VerifyPC-- + MAC_input = "FD" .. OPT .. pubRespTime + if(pps_present == true) then + MAC_input = MAC_input .. pps + end + rnum_concat = "" + rnum_concat = RndR .. RndC --temporary (only works for when a single random challenge (8 bytes) is sent) + -- j = 1 + -- for i = 1,8 do + -- rnum_concat = rnum_concat .. string.sub(RndR, j, j + 1) .. string.sub(RndC, j, j + 1) + -- j = j + 2 + -- end + MAC_input = MAC_input .. rnum_concat + print("Concatenation of random numbers = " .. rnum_concat) + print("Final PCD concatenation before input into MAC function = " .. MAC_input) + MAC_tag = calculateMAC(MAC_input) + print("8-byte PCD MAC_tag (placeholder - currently incorrect) = " .. MAC_tag) + commandString = VERIFYPC .. MAC_tag + response = sendRaw(commandString, true, true) + print(response) + PICC_MAC = string.sub(response, 5, 20) + print("8-byte MAC returned by PICC = " .. PICC_MAC) + MAC_input = "90" .. string.sub(MAC_input, 3) + print("Final PICC concatenation before input into MAC function = " .. MAC_input) + MAC_tag = calculateMAC(MAC_input) + print("8-byte PICC MAC_tag (placeholder - currently incorrect) = " .. MAC_tag) + +end + +--- +-- The main entry point +function main(args) + print("") -- Print a blank line to make things look cleaner + for o, a in getopt.getopt(args, 'h') do -- Populate command line arguments + if o == "h" then help() return end + end + + -- Initialize the card using the already-present read14a library + info,err = lib14a.read14443a(true, false) + --Perform RATS and PPS (Protocol and Parameter Selection) check to finish the ISO 14443-4 protocol. + response = sendRaw("e050", true, true) + if(response == "") then + print("No response from RATS.") + end + response = sendRaw("D01100", true, true) + if(response == "") then + print("No response from PPS check.") + end + if err then + oops(err) + sendRaw(POWEROFF, false, false) + return + else + print(("Connected to card with a UID of %s."):format(info.uid)) + end + + + -- Now, the card is initialized and we can do more interesting things. + + --writePerso() + --commitPerso("03") --move to SL3 + --getVersion() + proximityCheck() + + --commandString = VERIFYPC .. "186EFDE8DDC7D30B" + -- MAC = f5180d6e 40fdeae8 e9dd6ac7 bcd3350b + -- response = sendRaw(commandString, true, true) + + -- attempt to read VCProximityKey at block A001 + -- commandString = READPLAINNOMACUNMACED .. "01A0" .. "01" + -- response = sendRaw(commandString, true, true) + + -- authenticate with CardConfigurationKey + -- commandString = AUTH_FIRST .. "0190" .. "00" + -- response = sendRaw(commandString, true, true) + + -- Power off the Proxmark + sendRaw(POWEROFF, false, false) + + + +end + + +main(args) -- Call the main function From 9b4661c53baa99a649b26bd123b6f5d71e7bd812 Mon Sep 17 00:00:00 2001 From: Fl0-0 Date: Tue, 29 May 2018 18:05:32 +0200 Subject: [PATCH 227/310] Delete .DS_Store (#613) Mac hidden file accidentally merged in PR #593. --- .DS_Store | Bin 12292 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index ecae787283f908888f99eb9d6fa860cbb5da7cff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12292 zcmeHN3s6+o89pDP-d(S9AqdOLvWtL-A}uD5c~m+v<}s7DX`0DQlT6ZdVyDyo=bXFC-USow zXm#w~nS0MY=iGDep2Pp2@B98A#uys!Y(--jt1L5`FP?*YYEiJqwIN0J2 z2G)kWyuZ^O^!N7$!rfZB+vRun`&|+5GQYpwx5rCuX<>IL==ZM;hJE}s8@eLiT@l_l zrmJgXI2ejFikEe3T7TGkm6zV-8}K&K8^c{b`XnEy!E|I=c5Z&robu|r`o@toEj>fc zRI@qjasW{t)|Waxw5ixvARS_N2#gH z9qjF;-yN3CPHTgrJ*!;Jzuo2vN7nRt1N3=5i>5rQbpvl-=J9wvilP)a@7pIVvB1f9 zemV+d!I-JQS)t!6%*Lu%6Ki8zS%?j>+t@*NgdJta*<Z#2|)oJ#NHJ*oV7t z0Ecil9>BNp9XyKf;YmD;-{S;E@CUq(x9}d`m%lHC+b%y=%OZNkX?^ar8p?>(l9OS} zx3wrr+zP~I8=JrQlbbXU(%IN7R?D2MgKc9$c0Jq24%4KcU_WKQVJFxt><#t~`-uI6 zoo4?8gNde_gM7?EDdwRJ3(Od1=%;hTD5BvNfyX)wRNhhrIK!t zy-Vt;cM>TC+1t=ay^Jdu(|MryobCRR=sjn-lZe`1L9{*}#l&(KVkwpp&s|PTcMb8} zc64AJaow$Op@+CGC-y;)b?w-N0Zp!S}QB!^?Dq|J1 zVxDT=(oRaXJ>rV=hZ|g>O}w_&71`0F-**Iq{@A^%XFX}zZY{Gh7>Kxh0dGhQChbU% zZ0;me2o5#``vab^cu%|RP!yA5>eehnLzQ*arH-nqs=cL-q1xK{rH%#FReSfElG3F=PVGa{>79&Dk5sy4MY_59Ja}nLkDr73 zR;OF-1+#_gjZ78pXg0^G>@#_c@UC?D$-McftZa+LsJ^vnX;aO)mb|g*oA+39Ei)6; zw-``lDIQhdqGgV`bX0wdq2=bvIQ1=>s?9Y9^(`9f%!?D$w`i`nILA}pd=?G47E7G= z?;6)EoL*$eDNQr&-IZ?eCz zw@7)v&$TxIGdZX#9CRiMQABc->+TwI)hmUYHj@HhgDz|k`de!6od}U2y$Ltt7JMFG zB_Yb)k?Zc`cuZPc%mrIw7AS7;6cxIg#YxQ0@$AsHJkz}L;@IKKsx&^rg1my_xfKf* zEj`ajKqgANyV-VjNWh=AJ8U)`33%01Xfq2W;FZ~h)W?y4S4!tmk3<4qt*kQJbR^)_ znuTVwjs(2A_)6-PNRX3l$uci9H&Q6eZ_?N~ncMvo;CY-(?F2i?UMDkqhfM5a_HRt2 zC`UHMIdWk`9$`QU=8AYn4Qf$`MQ9X8wi0V8z{9a%Jq37Na8r=SD}p>Bgb^Vu_$&o_ zZqVZ&`|%~*iLc;Zd|et>%-EV`J$l8%qsEr7)2C95rjKoBuFG|=k4-Rcw|^w+Wn*Hp z&1Gt9EDCzj;;`fTdMvDX;X<=K)M?AJss>Uge2e$Yq#$TK!y_*fap4Oan&n?!>aJ}LkM*UKQ!f0%YQy9_xd6dt5V)UF7m$%tRlqQ%= zX@U$`$u?(`ZC0b6?6MiHB0;bookUVLQj&mMri`6)>l`5K=HLR+;;a?6nawAHDL4GK&=SFbt~6d3I>c(5(CkFBiOi_U2*+=!pL0y8`5{09#1lqZ! z2B58|Gyv^_8UxTST5JH?rOp`8Mge_=iQrs0Q|kIGp!e_WG==ge3h+I9=;gq>n?iXU zdO10~RY30niWeLd(EAX+NufLry~lXm08im(I$h+UyjLY=#-v>$MSLw0e-Z%L@P8?n zB{p3o{ZH%8v7e6FJpaG^%>Vx<9!g%4E0A1)%X0;g+SK0EKy!;G9TKO>bL|c~x6;WE z{@k!E#Spsa>3Gp6Qt{MQL63-o?w$OG%3)d39UcGke*&N!jUkbj#Q#s1-bwtwunE13 Hg#Z5m#5c2R From ad939de5017f3451376d6f559858a30bae675964 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Sun, 3 Jun 2018 14:25:20 +0200 Subject: [PATCH 228/310] USB comms: part 4 towards @micolous PR #463 * make uart_communication(), storeCommand() and getCommand() static in comms.c * move receiver thread creation and respective mutexes to comms.c * add mutex and signal for tx buffer * use comms.c for flasher as well * remove comm functions from client/proxmark3.h * this completes isolating all USB communication related functions in comms.c * don't assume a port to be defined by a name. Change parameter in OpenProxmark() to void* * comms.c: set sp and serial_port_name to NULL when offline --- client/Makefile | 8 +- client/cmddata.c | 5 +- client/cmdhf.c | 5 +- client/cmdhf14a.c | 2 +- client/cmdhf14b.c | 5 +- client/cmdhf14b.h | 2 + client/cmdhf15.c | 5 +- client/cmdhf15.h | 2 + client/cmdhfepa.c | 2 +- client/cmdhficlass.c | 2 +- client/cmdhflegic.c | 6 +- client/cmdhfmf.c | 2 +- client/cmdhfmfhard.c | 1 + client/cmdhfmfu.c | 2 +- client/cmdhftopaz.c | 5 +- client/cmdhw.c | 5 +- client/cmdlf.c | 5 +- client/cmdlfawid.c | 5 +- client/cmdlfcotag.c | 6 +- client/cmdlfem4x.c | 5 +- client/cmdlffdx.c | 2 +- client/cmdlfhid.c | 5 +- client/cmdlfhitag.c | 4 +- client/cmdlfindala.c | 5 +- client/cmdlfio.c | 5 +- client/cmdlfjablotron.c | 3 +- client/cmdlfnexwatch.c | 6 +- client/cmdlfnoralsy.c | 4 +- client/cmdlfpac.c | 4 +- client/cmdlfpcf7931.c | 6 +- client/cmdlfpcf7931.h | 2 + client/cmdlfpresco.c | 6 +- client/cmdlfpyramid.c | 6 +- client/cmdlfsecurakey.c | 4 +- client/cmdlft55xx.c | 5 +- client/cmdlft55xx.h | 3 + client/cmdlfti.c | 2 +- client/cmdlfviking.c | 6 +- client/cmdlfvisa2000.c | 2 +- client/cmdmain.c | 4 +- client/cmdmain.h | 5 - client/comms.c | 208 +++++++++++++++++++++++++--------------- client/comms.h | 12 +-- client/flash.c | 54 ++++------- client/flash.h | 9 +- client/flasher.c | 50 +++------- client/mifarehost.c | 2 +- client/proxmark3.c | 16 +--- client/proxmark3.h | 1 - client/scripting.c | 1 + 50 files changed, 274 insertions(+), 248 deletions(-) diff --git a/client/Makefile b/client/Makefile index d4ac33b2..917dc767 100644 --- a/client/Makefile +++ b/client/Makefile @@ -82,7 +82,9 @@ POSTCOMPILE = $(MV) -f $(OBJDIR)/$*.Td $(OBJDIR)/$*.d CORESRCS = uart_posix.c \ uart_win32.c \ util.c \ - util_posix.c + util_posix.c \ + ui.c \ + comms.c CMDSRCS = crapto1/crapto1.c\ crapto1/crypto1.c\ @@ -106,7 +108,6 @@ CMDSRCS = crapto1/crapto1.c\ iso14443crc.c \ iso15693tools.c \ graph.c \ - ui.c \ cmddata.c \ lfdemod.c \ emv/crypto_polarssl.c\ @@ -169,8 +170,7 @@ CMDSRCS = crapto1/crapto1.c\ cmdscript.c\ pm3_binlib.c\ pm3_bitlib.c\ - protocols.c\ - comms.c + protocols.c cpu_arch = $(shell uname -m) ifneq ($(findstring 86, $(cpu_arch)), ) diff --git a/client/cmddata.c b/client/cmddata.c index 471665b7..c4f0e8a3 100644 --- a/client/cmddata.c +++ b/client/cmddata.c @@ -8,14 +8,15 @@ // Data and Graph commands //----------------------------------------------------------------------------- +#include "cmddata.h" + #include // also included in util.h #include // also included in util.h #include #include // for CmdNorm INT_MIN && INT_MAX -#include "cmddata.h" #include "util.h" #include "cmdmain.h" -#include "proxmark3.h" +#include "comms.h" #include "ui.h" // for show graph controls #include "graph.h" // for graph data #include "cmdparser.h"// already included in cmdmain.h diff --git a/client/cmdhf.c b/client/cmdhf.c index 4a672255..17c31209 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -9,17 +9,18 @@ // High frequency commands //----------------------------------------------------------------------------- +#include "cmdhf.h" + #include #include #include -#include "proxmark3.h" +#include "comms.h" #include "util.h" #include "ui.h" #include "iso14443crc.h" #include "parity.h" #include "cmdmain.h" #include "cmdparser.h" -#include "cmdhf.h" #include "cmdhf14a.h" #include "cmdhf14b.h" #include "cmdhf15.h" diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 4684ae29..859dec2f 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -20,7 +20,7 @@ #include "util.h" #include "util_posix.h" #include "iso14443crc.h" -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "cmdparser.h" #include "common.h" diff --git a/client/cmdhf14b.c b/client/cmdhf14b.c index bae40c09..ff0bf7c9 100644 --- a/client/cmdhf14b.c +++ b/client/cmdhf14b.c @@ -8,18 +8,19 @@ // High frequency ISO14443B commands //----------------------------------------------------------------------------- +#include "cmdhf14b.h" + #include #include #include #include #include #include "iso14443crc.h" -#include "proxmark3.h" +#include "comms.h" #include "graph.h" #include "util.h" #include "ui.h" #include "cmdparser.h" -#include "cmdhf14b.h" #include "cmdmain.h" #include "cmdhf14a.h" diff --git a/client/cmdhf14b.h b/client/cmdhf14b.h index a45b7434..4fcae927 100644 --- a/client/cmdhf14b.h +++ b/client/cmdhf14b.h @@ -11,6 +11,8 @@ #ifndef CMDHF14B_H__ #define CMDHF14B_H__ +#include + int CmdHF14B(const char *Cmd); int CmdHF14BList(const char *Cmd); int CmdHF14BInfo(const char *Cmd); diff --git a/client/cmdhf15.c b/client/cmdhf15.c index 570a0420..08cc3b15 100644 --- a/client/cmdhf15.c +++ b/client/cmdhf15.c @@ -22,17 +22,18 @@ // the client. Signal Processing & decoding is done on the pc. This is the slowest // variant, but offers the possibility to analyze the waveforms directly. +#include "cmdhf15.h" + #include #include #include #include -#include "proxmark3.h" +#include "comms.h" #include "graph.h" #include "ui.h" #include "util.h" #include "cmdparser.h" -#include "cmdhf15.h" #include "iso15693tools.h" #include "cmdmain.h" diff --git a/client/cmdhf15.h b/client/cmdhf15.h index c6264604..d0517fe5 100644 --- a/client/cmdhf15.h +++ b/client/cmdhf15.h @@ -11,6 +11,8 @@ #ifndef CMDHF15_H__ #define CMDHF15_H__ +#include + int CmdHF15(const char *Cmd); int CmdHF15Demod(const char *Cmd); diff --git a/client/cmdhfepa.c b/client/cmdhfepa.c index f3456afb..ac1f4268 100644 --- a/client/cmdhfepa.c +++ b/client/cmdhfepa.c @@ -17,7 +17,7 @@ #include #include "util.h" #include "util_posix.h" -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "cmdparser.h" #include "common.h" diff --git a/client/cmdhficlass.c b/client/cmdhficlass.c index e99c3285..499f7aae 100644 --- a/client/cmdhficlass.c +++ b/client/cmdhficlass.c @@ -16,7 +16,7 @@ #include #include #include "iso14443crc.h" // Can also be used for iClass, using 0xE012 as CRC-type -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "cmdparser.h" #include "cmdhficlass.h" diff --git a/client/cmdhflegic.c b/client/cmdhflegic.c index 9c9613ae..691e1978 100644 --- a/client/cmdhflegic.c +++ b/client/cmdhflegic.c @@ -8,15 +8,17 @@ // High frequency Legic commands //----------------------------------------------------------------------------- +#include "cmdhflegic.h" + #include #include #include -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "cmdparser.h" -#include "cmdhflegic.h" #include "cmdmain.h" #include "util.h" + static int CmdHelp(const char *Cmd); static command_t CommandTable[] = diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index a2da01c9..b50d6013 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -15,7 +15,7 @@ #include #include #include -#include "proxmark3.h" +#include "comms.h" #include "cmdmain.h" #include "cmdhfmfhard.h" #include "parity.h" diff --git a/client/cmdhfmfhard.c b/client/cmdhfmfhard.c index cf19436c..be618d6e 100644 --- a/client/cmdhfmfhard.c +++ b/client/cmdhfmfhard.c @@ -25,6 +25,7 @@ #include #include #include "proxmark3.h" +#include "comms.h" #include "cmdmain.h" #include "ui.h" #include "util.h" diff --git a/client/cmdhfmfu.c b/client/cmdhfmfu.c index ac76c8dd..e4115d71 100644 --- a/client/cmdhfmfu.c +++ b/client/cmdhfmfu.c @@ -12,7 +12,7 @@ #include #include -#include "proxmark3.h" +#include "comms.h" #include "usb_cmd.h" #include "cmdmain.h" #include "ui.h" diff --git a/client/cmdhftopaz.c b/client/cmdhftopaz.c index 39ff1804..b0b8a3d2 100644 --- a/client/cmdhftopaz.c +++ b/client/cmdhftopaz.c @@ -8,17 +8,18 @@ // High frequency Topaz (NFC Type 1) commands //----------------------------------------------------------------------------- +#include "cmdhftopaz.h" + #include #include #include #include #include "cmdmain.h" #include "cmdparser.h" -#include "cmdhftopaz.h" #include "cmdhf14a.h" #include "ui.h" #include "mifare.h" -#include "proxmark3.h" +#include "comms.h" #include "iso14443crc.h" #include "protocols.h" diff --git a/client/cmdhw.c b/client/cmdhw.c index 4ede122f..bdab01eb 100644 --- a/client/cmdhw.c +++ b/client/cmdhw.c @@ -8,14 +8,15 @@ // Hardware commands //----------------------------------------------------------------------------- +#include "cmdhw.h" + #include #include #include #include #include "ui.h" -#include "proxmark3.h" +#include "comms.h" #include "cmdparser.h" -#include "cmdhw.h" #include "cmdmain.h" #include "cmddata.h" diff --git a/client/cmdlf.c b/client/cmdlf.c index 285d377a..c09a299c 100644 --- a/client/cmdlf.c +++ b/client/cmdlf.c @@ -8,14 +8,15 @@ // Low frequency commands //----------------------------------------------------------------------------- +#include "cmdlf.h" + #include #include #include #include #include #include -#include "proxmark3.h" -#include "cmdlf.h" +#include "comms.h" #include "lfdemod.h" // for psk2TOpsk1 #include "util.h" // for parsing cli command utils #include "ui.h" // for show graph controls diff --git a/client/cmdlfawid.c b/client/cmdlfawid.c index 141ba172..cde94555 100644 --- a/client/cmdlfawid.c +++ b/client/cmdlfawid.c @@ -11,10 +11,11 @@ // FSK2a, RF/50, 96 bits (complete) //----------------------------------------------------------------------------- +#include "cmdlfawid.h" + #include #include // sscanf -#include "proxmark3.h" // Definitions, USB controls, etc -#include "cmdlfawid.h" +#include "comms.h" // Definitions, USB controls, etc #include "ui.h" // PrintAndLog #include "cmdparser.h" // CmdsParse, CmdsHelp #include "lfdemod.h" // parityTest + diff --git a/client/cmdlfcotag.c b/client/cmdlfcotag.c index ca163480..d1808c9a 100644 --- a/client/cmdlfcotag.c +++ b/client/cmdlfcotag.c @@ -7,13 +7,15 @@ //----------------------------------------------------------------------------- // Low frequency COTAG commands //----------------------------------------------------------------------------- + +#include "cmdlfcotag.h" + #include #include #include -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "cmddata.h" -#include "cmdlfcotag.h" #include "lfdemod.h" #include "usb_cmd.h" #include "cmdmain.h" diff --git a/client/cmdlfem4x.c b/client/cmdlfem4x.c index ff2ae5b0..cdaeb5ed 100644 --- a/client/cmdlfem4x.c +++ b/client/cmdlfem4x.c @@ -8,11 +8,12 @@ // Low frequency EM4x commands //----------------------------------------------------------------------------- +#include "cmdlfem4x.h" + #include #include #include -#include "cmdlfem4x.h" -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "util.h" #include "graph.h" diff --git a/client/cmdlffdx.c b/client/cmdlffdx.c index 2e2ee9fd..20f834ce 100644 --- a/client/cmdlffdx.c +++ b/client/cmdlffdx.c @@ -13,7 +13,7 @@ #include #include #include -#include "proxmark3.h" +#include "comms.h" #include "ui.h" // for PrintAndLog #include "util.h" #include "cmdparser.h" diff --git a/client/cmdlfhid.c b/client/cmdlfhid.c index 19b5a142..ca58d8ab 100644 --- a/client/cmdlfhid.c +++ b/client/cmdlfhid.c @@ -8,10 +8,11 @@ // Low frequency HID commands (known) //----------------------------------------------------------------------------- +#include "cmdlfhid.h" + #include #include -#include "cmdlfhid.h" -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "graph.h" #include "cmdparser.h" diff --git a/client/cmdlfhitag.c b/client/cmdlfhitag.c index 26ba9238..0fd8801b 100644 --- a/client/cmdlfhitag.c +++ b/client/cmdlfhitag.c @@ -8,10 +8,12 @@ // Low frequency Hitag support //----------------------------------------------------------------------------- +#include "cmdlfhitag.h" + #include #include #include -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "cmdparser.h" #include "common.h" diff --git a/client/cmdlfindala.c b/client/cmdlfindala.c index 0a4f7834..b30231c5 100644 --- a/client/cmdlfindala.c +++ b/client/cmdlfindala.c @@ -8,10 +8,11 @@ // PSK1, rf/32, 64 or 224 bits (known) //----------------------------------------------------------------------------- +#include "cmdlfindala.h" + #include #include -#include "cmdlfindala.h" -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "graph.h" #include "cmdparser.h" diff --git a/client/cmdlfio.c b/client/cmdlfio.c index f2f2df06..feb7d373 100644 --- a/client/cmdlfio.c +++ b/client/cmdlfio.c @@ -8,13 +8,14 @@ // FSK2a, rf/64, 64 bits (complete) //----------------------------------------------------------------------------- +#include "cmdlfio.h" + #include #include #include #include #include -#include "cmdlfio.h" -#include "proxmark3.h" +#include "comms.h" #include "graph.h" #include "ui.h" #include "cmdparser.h" diff --git a/client/cmdlfjablotron.c b/client/cmdlfjablotron.c index 9c69099e..4756266c 100644 --- a/client/cmdlfjablotron.c +++ b/client/cmdlfjablotron.c @@ -9,10 +9,11 @@ //----------------------------------------------------------------------------- #include "cmdlfjablotron.h" + #include #include #include -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "util.h" #include "graph.h" diff --git a/client/cmdlfnexwatch.c b/client/cmdlfnexwatch.c index caabe835..d44c2a27 100644 --- a/client/cmdlfnexwatch.c +++ b/client/cmdlfnexwatch.c @@ -7,12 +7,14 @@ // Low frequency Honeywell NexWatch tag commands // PSK1 RF/16, RF/2, 128 bits long (known) //----------------------------------------------------------------------------- + +#include "cmdlfnexwatch.h" + #include #include #include #include -#include "cmdlfnexwatch.h" -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "util.h" #include "graph.h" diff --git a/client/cmdlfnoralsy.c b/client/cmdlfnoralsy.c index 2c90fa14..52c35081 100644 --- a/client/cmdlfnoralsy.c +++ b/client/cmdlfnoralsy.c @@ -7,11 +7,13 @@ // Low frequency Noralsy tag commands // ASK/Manchester, STT, RF/32, 96 bits long (some bits unknown) //----------------------------------------------------------------------------- + #include "cmdlfnoralsy.h" + #include #include #include -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "util.h" #include "graph.h" diff --git a/client/cmdlfpac.c b/client/cmdlfpac.c index ef6b394b..190361ec 100644 --- a/client/cmdlfpac.c +++ b/client/cmdlfpac.c @@ -7,10 +7,12 @@ // Low frequency Stanley/PAC tag commands // NRZ, RF/32, 128 bits long (unknown cs) //----------------------------------------------------------------------------- + #include "cmdlfpac.h" + #include #include -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "util.h" #include "graph.h" diff --git a/client/cmdlfpcf7931.c b/client/cmdlfpcf7931.c index ffaf946f..9c694b8d 100644 --- a/client/cmdlfpcf7931.c +++ b/client/cmdlfpcf7931.c @@ -8,9 +8,12 @@ //----------------------------------------------------------------------------- // Low frequency PCF7931 commands //----------------------------------------------------------------------------- + +#include "cmdlfpcf7931.h" + #include #include -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "util.h" #include "graph.h" @@ -18,7 +21,6 @@ #include "cmddata.h" #include "cmdmain.h" #include "cmdlf.h" -#include "cmdlfpcf7931.h" static int CmdHelp(const char *Cmd); diff --git a/client/cmdlfpcf7931.h b/client/cmdlfpcf7931.h index e093039c..8b24f03a 100644 --- a/client/cmdlfpcf7931.h +++ b/client/cmdlfpcf7931.h @@ -12,6 +12,8 @@ #ifndef CMDLFPCF7931_H__ #define CMDLFPCF7931_H__ +#include + struct pcf7931_config{ uint8_t Pwd[7]; uint16_t InitDelay; diff --git a/client/cmdlfpresco.c b/client/cmdlfpresco.c index 2f4bacfe..865b384b 100644 --- a/client/cmdlfpresco.c +++ b/client/cmdlfpresco.c @@ -7,11 +7,13 @@ // Low frequency Presco tag commands // ASK/Manchester, rf/32, 128 bits (complete) //----------------------------------------------------------------------------- + +#include "cmdlfpresco.h" + #include #include #include -#include "cmdlfpresco.h" -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "util.h" #include "graph.h" diff --git a/client/cmdlfpyramid.c b/client/cmdlfpyramid.c index 366889f3..82367720 100644 --- a/client/cmdlfpyramid.c +++ b/client/cmdlfpyramid.c @@ -7,11 +7,13 @@ // Low frequency Farpoint / Pyramid tag commands // FSK2a, rf/50, 128 bits (complete) //----------------------------------------------------------------------------- + +#include "cmdlfpyramid.h" + #include #include #include -#include "cmdlfpyramid.h" -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "util.h" #include "graph.h" diff --git a/client/cmdlfsecurakey.c b/client/cmdlfsecurakey.c index 8ae81250..44b77060 100644 --- a/client/cmdlfsecurakey.c +++ b/client/cmdlfsecurakey.c @@ -7,11 +7,13 @@ // Low frequency Securakey tag commands // ASK/Manchester, RF/40, 96 bits long (unknown cs) //----------------------------------------------------------------------------- + #include "cmdlfsecurakey.h" + #include #include #include -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "util.h" #include "graph.h" diff --git a/client/cmdlft55xx.c b/client/cmdlft55xx.c index a9fbb71e..f53ff0d6 100644 --- a/client/cmdlft55xx.c +++ b/client/cmdlft55xx.c @@ -7,19 +7,20 @@ // Low frequency T55xx commands //----------------------------------------------------------------------------- +#include "cmdlft55xx.h" + #include #include #include #include #include -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "graph.h" #include "cmdmain.h" #include "cmdparser.h" #include "cmddata.h" #include "cmdlf.h" -#include "cmdlft55xx.h" #include "util.h" #include "lfdemod.h" #include "cmdhf14a.h" //for getTagInfo diff --git a/client/cmdlft55xx.h b/client/cmdlft55xx.h index 4f0fd21d..2ae3e69b 100644 --- a/client/cmdlft55xx.h +++ b/client/cmdlft55xx.h @@ -10,6 +10,9 @@ #ifndef CMDLFT55XX_H__ #define CMDLFT55XX_H__ +#include +#include + typedef struct { uint32_t bl1; uint32_t bl2; diff --git a/client/cmdlfti.c b/client/cmdlfti.c index 7e0af94b..ff463971 100644 --- a/client/cmdlfti.c +++ b/client/cmdlfti.c @@ -14,7 +14,7 @@ #include #include #include "crc16.h" -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "graph.h" #include "cmdparser.h" diff --git a/client/cmdlfviking.c b/client/cmdlfviking.c index 779156c8..73a9126a 100644 --- a/client/cmdlfviking.c +++ b/client/cmdlfviking.c @@ -7,11 +7,13 @@ // Low frequency Viking tag commands (AKA FDI Matalec Transit) // ASK/Manchester, RF/32, 64 bits (complete) //----------------------------------------------------------------------------- + +#include "cmdlfviking.h" + #include #include #include -#include "proxmark3.h" -#include "cmdlfviking.h" +#include "comms.h" #include "ui.h" #include "util.h" #include "graph.h" diff --git a/client/cmdlfvisa2000.c b/client/cmdlfvisa2000.c index 04589ba5..33259794 100644 --- a/client/cmdlfvisa2000.c +++ b/client/cmdlfvisa2000.c @@ -13,7 +13,7 @@ #include #include -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "util.h" #include "graph.h" diff --git a/client/cmdmain.c b/client/cmdmain.c index c3b46145..01d4c9a7 100644 --- a/client/cmdmain.c +++ b/client/cmdmain.c @@ -50,13 +50,13 @@ command_t* getTopLevelCommandTable() return CommandTable; } -int CmdHelp(const char *Cmd) +static int CmdHelp(const char *Cmd) { CmdsHelp(CommandTable); return 0; } -int CmdQuit(const char *Cmd) +static int CmdQuit(const char *Cmd) { return 99; } diff --git a/client/cmdmain.h b/client/cmdmain.h index a833b41e..42b49145 100644 --- a/client/cmdmain.h +++ b/client/cmdmain.h @@ -11,12 +11,7 @@ #ifndef CMDMAIN_H__ #define CMDMAIN_H__ -#include -#include -#include "usb_cmd.h" #include "cmdparser.h" -#include "comms.h" - extern int CommandReceived(char *Cmd); extern command_t* getTopLevelCommandTable(); diff --git a/client/comms.c b/client/comms.c index b62f6fda..eeaff79c 100644 --- a/client/comms.c +++ b/client/comms.c @@ -9,33 +9,42 @@ // Code for communicating with the proxmark3 hardware. //----------------------------------------------------------------------------- -#include - #include "comms.h" + +#include +#ifdef __linux__ +#include // for unlink() +#endif #include "uart.h" #include "ui.h" #include "common.h" #include "util_posix.h" -// Declare globals. // Serial port that we are communicating with the PM3 on. -static serial_port sp; +static serial_port sp = NULL; +static char *serial_port_name = NULL; // If TRUE, then there is no active connection to the PM3, and we will drop commands sent. static bool offline; +typedef struct { + bool run; // If TRUE, continue running the uart_communication thread + bool block_after_ACK; // if true, block after receiving an ACK package +} communication_arg_t; + +static communication_arg_t conn; +static pthread_t USB_communication_thread; + // Transmit buffer. -// TODO: Use locks and execute this on the main thread, rather than the receiver -// thread. Running on the main thread means we need to be careful in the -// flasher, as it means SendCommand is no longer async, and can't be used as a -// buffer for a pending command when the connection is re-established. -static UsbCommand txcmd; -volatile static bool txcmd_pending = false; +static UsbCommand txBuffer; +static bool txBuffer_pending = false; +static pthread_mutex_t txBufferMutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t txBufferSig = PTHREAD_COND_INITIALIZER; // Used by UsbReceiveCommand as a ring buffer for messages that are yet to be // processed by a command handler (WaitForResponse{,Timeout}) -static UsbCommand cmdBuffer[CMD_BUFFER_SIZE]; +static UsbCommand rxBuffer[CMD_BUFFER_SIZE]; // Points to the next empty position to write to static int cmd_head = 0; @@ -43,8 +52,8 @@ static int cmd_head = 0; // Points to the position of the last unread command static int cmd_tail = 0; -// to lock cmdBuffer operations from different threads -static pthread_mutex_t cmdBufferMutex = PTHREAD_MUTEX_INITIALIZER; +// to lock rxBuffer operations from different threads +static pthread_mutex_t rxBufferMutex = PTHREAD_MUTEX_INITIALIZER; // These wrappers are required because it is not possible to access a static // global variable outside of the context of a single file. @@ -57,38 +66,6 @@ bool IsOffline() { return offline; } -bool OpenProxmark(char *portname, bool waitCOMPort, int timeout) { - if (!waitCOMPort) { - sp = uart_open(portname); - } else { - printf("Waiting for Proxmark to appear on %s ", portname); - fflush(stdout); - int openCount = 0; - do { - sp = uart_open(portname); - msleep(1000); - printf("."); - fflush(stdout); - } while(++openCount < timeout && (sp == INVALID_SERIAL_PORT || sp == CLAIMED_SERIAL_PORT)); - printf("\n"); - } - - // check result of uart opening - if (sp == INVALID_SERIAL_PORT) { - printf("ERROR: invalid serial port\n"); - return false; - } else if (sp == CLAIMED_SERIAL_PORT) { - printf("ERROR: serial port is claimed by another process\n"); - return false; - } else { - return true; - } -} - -void CloseProxmark(void) { - uart_close(sp); -} - void SendCommand(UsbCommand *c) { #ifdef COMMS_DEBUG printf("Sending %04x cmd\n", c->cmd); @@ -98,15 +75,22 @@ void SendCommand(UsbCommand *c) { PrintAndLog("Sending bytes to proxmark failed - offline"); return; } - /** - The while-loop below causes hangups at times, when the pm3 unit is unresponsive - or disconnected. The main console thread is alive, but comm thread just spins here. - Not good.../holiman - **/ - while(txcmd_pending); - txcmd = *c; - txcmd_pending = true; + pthread_mutex_lock(&txBufferMutex); + /** + This causes hangups at times, when the pm3 unit is unresponsive or disconnected. The main console thread is alive, + but comm thread just spins here. Not good.../holiman + **/ + while (txBuffer_pending) { + pthread_cond_wait(&txBufferSig, &txBufferMutex); // wait for communication thread to complete sending a previous commmand + } + + txBuffer = *c; + txBuffer_pending = true; + pthread_cond_signal(&txBufferSig); // tell communication thread that a new command can be send + + pthread_mutex_unlock(&txBufferMutex); + } @@ -119,18 +103,18 @@ void SendCommand(UsbCommand *c) { void clearCommandBuffer() { //This is a very simple operation - pthread_mutex_lock(&cmdBufferMutex); + pthread_mutex_lock(&rxBufferMutex); cmd_tail = cmd_head; - pthread_mutex_unlock(&cmdBufferMutex); + pthread_mutex_unlock(&rxBufferMutex); } /** * @brief storeCommand stores a USB command in a circular buffer * @param UC */ -void storeCommand(UsbCommand *command) +static void storeCommand(UsbCommand *command) { - pthread_mutex_lock(&cmdBufferMutex); + pthread_mutex_lock(&rxBufferMutex); if( (cmd_head+1) % CMD_BUFFER_SIZE == cmd_tail) { // If these two are equal, we're about to overwrite in the @@ -139,11 +123,11 @@ void storeCommand(UsbCommand *command) } // Store the command at the 'head' location - UsbCommand* destination = &cmdBuffer[cmd_head]; + UsbCommand* destination = &rxBuffer[cmd_head]; memcpy(destination, command, sizeof(UsbCommand)); cmd_head = (cmd_head +1) % CMD_BUFFER_SIZE; //increment head and wrap - pthread_mutex_unlock(&cmdBufferMutex); + pthread_mutex_unlock(&rxBufferMutex); } @@ -152,22 +136,22 @@ void storeCommand(UsbCommand *command) * @param response location to write command * @return 1 if response was returned, 0 if nothing has been received */ -int getCommand(UsbCommand* response) +static int getCommand(UsbCommand* response) { - pthread_mutex_lock(&cmdBufferMutex); + pthread_mutex_lock(&rxBufferMutex); //If head == tail, there's nothing to read, or if we just got initialized if (cmd_head == cmd_tail){ - pthread_mutex_unlock(&cmdBufferMutex); + pthread_mutex_unlock(&rxBufferMutex); return 0; } //Pick out the next unread command - UsbCommand* last_unread = &cmdBuffer[cmd_tail]; + UsbCommand* last_unread = &rxBuffer[cmd_tail]; memcpy(response, last_unread, sizeof(UsbCommand)); //Increment tail - this is a circular buffer, so modulo buffer size cmd_tail = (cmd_tail + 1) % CMD_BUFFER_SIZE; - pthread_mutex_unlock(&cmdBufferMutex); + pthread_mutex_unlock(&rxBufferMutex); return 1; } @@ -202,35 +186,54 @@ static void UsbCommandReceived(UsbCommand *UC) } -void +static void #ifdef __has_attribute #if __has_attribute(force_align_arg_pointer) __attribute__((force_align_arg_pointer)) #endif #endif -*uart_receiver(void *targ) { - receiver_arg *conn = (receiver_arg*)targ; +*uart_communication(void *targ) { + communication_arg_t *conn = (communication_arg_t*)targ; size_t rxlen; - uint8_t rx[sizeof(UsbCommand)]; - uint8_t *prx = rx; + UsbCommand rx; + UsbCommand *prx = ℞ while (conn->run) { rxlen = 0; - if (uart_receive(sp, prx, sizeof(UsbCommand) - (prx-rx), &rxlen) && rxlen) { + bool ACK_received = false; + if (uart_receive(sp, (uint8_t *)prx, sizeof(UsbCommand) - (prx-&rx), &rxlen) && rxlen) { prx += rxlen; - if (prx-rx < sizeof(UsbCommand)) { + if (prx-&rx < sizeof(UsbCommand)) { continue; } - UsbCommandReceived((UsbCommand*)rx); + UsbCommandReceived(&rx); + if (rx.cmd == CMD_ACK) { + ACK_received = true; + } } - prx = rx; + prx = ℞ - if(txcmd_pending) { - if (!uart_send(sp, (uint8_t*) &txcmd, sizeof(UsbCommand))) { + + pthread_mutex_lock(&txBufferMutex); + + if (conn->block_after_ACK) { + // if we just received an ACK, wait here until a new command is to be transmitted + if (ACK_received) { + while (!txBuffer_pending) { + pthread_cond_wait(&txBufferSig, &txBufferMutex); + } + } + } + + if(txBuffer_pending) { + if (!uart_send(sp, (uint8_t*) &txBuffer, sizeof(UsbCommand))) { PrintAndLog("Sending bytes to proxmark failed"); } - txcmd_pending = false; + txBuffer_pending = false; + pthread_cond_signal(&txBufferSig); // tell main thread that txBuffer is empty } + + pthread_mutex_unlock(&txBufferMutex); } pthread_exit(NULL); @@ -238,7 +241,6 @@ __attribute__((force_align_arg_pointer)) } - /** * Data transfer from Proxmark to client. This method times out after * ms_timeout milliseconds. @@ -289,6 +291,58 @@ bool GetFromBigBuf(uint8_t *dest, int bytes, int start_index, UsbCommand *respon return false; } + +bool OpenProxmark(void *port, bool wait_for_port, int timeout, bool flash_mode) { + char *portname = (char *)port; + if (!wait_for_port) { + sp = uart_open(portname); + } else { + printf("Waiting for Proxmark to appear on %s ", portname); + fflush(stdout); + int openCount = 0; + do { + sp = uart_open(portname); + msleep(1000); + printf("."); + fflush(stdout); + } while(++openCount < timeout && (sp == INVALID_SERIAL_PORT || sp == CLAIMED_SERIAL_PORT)); + printf("\n"); + } + + // check result of uart opening + if (sp == INVALID_SERIAL_PORT) { + printf("ERROR: invalid serial port\n"); + sp = NULL; + serial_port_name = NULL; + return false; + } else if (sp == CLAIMED_SERIAL_PORT) { + printf("ERROR: serial port is claimed by another process\n"); + sp = NULL; + serial_port_name = NULL; + return false; + } else { + // start the USB communication thread + serial_port_name = portname; + conn.run = true; + conn.block_after_ACK = flash_mode; + pthread_create(&USB_communication_thread, NULL, &uart_communication, &conn); + return true; + } +} + + +void CloseProxmark(void) { + conn.run = false; + pthread_join(USB_communication_thread, NULL); + uart_close(sp); +#ifdef __linux__ + // Fix for linux, it seems that it is extremely slow to release the serial port file descriptor /dev/* + if (serial_port_name) { + unlink(serial_port_name); + } +#endif +} + /** * Waits for a certain response type. This method waits for a maximum of diff --git a/client/comms.h b/client/comms.h index 111677ad..68981165 100644 --- a/client/comms.h +++ b/client/comms.h @@ -22,24 +22,14 @@ #define CMD_BUFFER_SIZE 50 #endif -typedef struct { - // If TRUE, continue running the uart_receiver thread - bool run; - - // Lock around serial port receives - pthread_mutex_t recv_lock; -} receiver_arg; - - void SetOffline(bool new_offline); bool IsOffline(); -bool OpenProxmark(char *portname, bool waitCOMPort, int timeout); +bool OpenProxmark(void *port, bool wait_for_port, int timeout, bool flash_mode); void CloseProxmark(void); void SendCommand(UsbCommand *c); -void *uart_receiver(void *targ); void clearCommandBuffer(); bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning); bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout); diff --git a/client/flash.c b/client/flash.c index e3714185..e43ebd1b 100644 --- a/client/flash.c +++ b/client/flash.c @@ -20,12 +20,7 @@ #include "elf.h" #include "proxendian.h" #include "usb_cmd.h" -#include "uart.h" - -void SendCommand(UsbCommand* txcmd); -void ReceiveCommand(UsbCommand* rxcmd); - -serial_port sp; +#include "comms.h" #define FLASH_START 0x100000 #define FLASH_SIZE (256*1024) @@ -42,22 +37,6 @@ static const uint8_t elf_ident[] = { EV_CURRENT }; -void CloseProxmark(const char *serial_port_name) { - // Clean up the port - uart_close(sp); - // Fix for linux, it seems that it is extremely slow to release the serial port file descriptor /dev/* - unlink(serial_port_name); -} - -bool OpenProxmark(size_t i, const char *serial_port_name) { - sp = uart_open(serial_port_name); - if (sp == INVALID_SERIAL_PORT || sp == CLAIMED_SERIAL_PORT) { - //poll once a second - return false; - } - - return true; -} // Turn PHDRs into flasher segments, checking for PHDR sanity and merging adjacent // unaligned segments if needed @@ -207,7 +186,7 @@ static int check_segs(flash_file_t *ctx, int can_write_bl) { } // Load an ELF file and prepare it for flashing -int flash_load(flash_file_t *ctx, const char *name, int can_write_bl) +int flash_load(flash_file_t *ctx, const char *name, bool can_write_bl) { FILE *fd = NULL; Elf32_Ehdr ehdr; @@ -295,7 +274,7 @@ static int get_proxmark_state(uint32_t *state) c.cmd = CMD_DEVICE_INFO; SendCommand(&c); UsbCommand resp; - ReceiveCommand(&resp); + WaitForResponse(CMD_UNKNOWN, &resp); // wait for any response. No timeout. // Three outcomes: // 1. The old bootrom code will ignore CMD_DEVICE_INFO, but respond with an ACK @@ -355,17 +334,16 @@ static int enter_bootloader(char *serial_port_name) } msleep(100); - CloseProxmark(serial_port_name); + CloseProxmark(); - fprintf(stderr,"Waiting for Proxmark to reappear on %s",serial_port_name); - do { - sleep(1); - fprintf(stderr, "."); - } while (!OpenProxmark(0, serial_port_name)); - - fprintf(stderr," Found.\n"); - - return 0; + bool opened = OpenProxmark(serial_port_name, true, 120, true); // wait for 2 minutes + if (opened) { + fprintf(stderr," Found.\n"); + return 0; + } else { + fprintf(stderr,"Error: Proxmark not found.\n"); + return -1; + } } fprintf(stderr, "Error: Unknown Proxmark mode\n"); @@ -375,7 +353,7 @@ static int enter_bootloader(char *serial_port_name) static int wait_for_ack(void) { UsbCommand ack; - ReceiveCommand(&ack); + WaitForResponse(CMD_UNKNOWN, &ack); if (ack.cmd != CMD_ACK) { printf("Error: Unexpected reply 0x%04" PRIx64 " (expected ACK)\n", ack.cmd); return -1; @@ -424,12 +402,12 @@ static int write_block(uint32_t address, uint8_t *data, uint32_t length) memset(block_buf, 0xFF, BLOCK_SIZE); memcpy(block_buf, data, length); - UsbCommand c; + UsbCommand c; c.cmd = CMD_FINISH_WRITE; c.arg[0] = address; memcpy(c.d.asBytes, block_buf, length); - SendCommand(&c); - return wait_for_ack(); + SendCommand(&c); + return wait_for_ack(); } // Write a file's segments to Flash diff --git a/client/flash.h b/client/flash.h index f8ffd221..06c7c96e 100644 --- a/client/flash.h +++ b/client/flash.h @@ -10,8 +10,7 @@ #define __FLASH_H__ #include -#include "elf.h" -#include "uart.h" +#include typedef struct { void *data; @@ -26,14 +25,10 @@ typedef struct { flash_seg_t *segments; } flash_file_t; -int flash_load(flash_file_t *ctx, const char *name, int can_write_bl); +int flash_load(flash_file_t *ctx, const char *name, bool can_write_bl); int flash_start_flashing(int enable_bl_writes, char *serial_port_name); int flash_write(flash_file_t *ctx); void flash_free(flash_file_t *ctx); int flash_stop_flashing(void); -void CloseProxmark(const char *serial_port_name); -bool OpenProxmark(size_t i, const char *serial_port_name); - -extern serial_port sp; #endif diff --git a/client/flasher.c b/client/flasher.c index 2bb87df9..a008f7bc 100644 --- a/client/flasher.c +++ b/client/flasher.c @@ -10,18 +10,14 @@ #include #include #include +#include #include "proxmark3.h" #include "util.h" #include "util_posix.h" #include "flash.h" -#include "uart.h" +#include "comms.h" #include "usb_cmd.h" -#ifdef _WIN32 -# define unlink(x) -#else -# include -#endif void cmd_debug(UsbCommand* UC) { // Debug @@ -37,29 +33,6 @@ void cmd_debug(UsbCommand* UC) { printf("...\n"); } -void SendCommand(UsbCommand* txcmd) { -// printf("send: "); -// cmd_debug(txcmd); - if (!uart_send(sp,(uint8_t*)txcmd,sizeof(UsbCommand))) { - printf("Sending bytes to proxmark failed\n"); - exit(1); - } -} - -void ReceiveCommand(UsbCommand* rxcmd) { - uint8_t* prxcmd = (uint8_t*)rxcmd; - uint8_t* prx = prxcmd; - size_t rxlen; - while (true) { - if (uart_receive(sp, prx, sizeof(UsbCommand) - (prx-prxcmd), &rxlen)) { - prx += rxlen; - if ((prx-prxcmd) >= sizeof(UsbCommand)) { - return; - } - } - } -} - static void usage(char *argv0) { fprintf(stderr, "Usage: %s [-b] image.elf [image.elf...]\n\n", argv0); @@ -77,7 +50,7 @@ static void usage(char *argv0) int main(int argc, char **argv) { - int can_write_bl = 0; + int can_write_bl = false; int num_files = 0; int res; flash_file_t files[MAX_FILES]; @@ -92,7 +65,7 @@ int main(int argc, char **argv) for (int i = 2; i < argc; i++) { if (argv[i][0] == '-') { if (!strcmp(argv[i], "-b")) { - can_write_bl = 1; + can_write_bl = true; } else { usage(argv[0]); return -1; @@ -110,12 +83,12 @@ int main(int argc, char **argv) char* serial_port_name = argv[1]; - fprintf(stderr,"Waiting for Proxmark to appear on %s", serial_port_name); - do { - msleep(1000); - fprintf(stderr, "."); - } while (!OpenProxmark(0, serial_port_name)); - fprintf(stderr," Found.\n"); + if (!OpenProxmark(serial_port_name, true, 120, true)) { // wait for 2 minutes + fprintf(stderr, "Could not find Proxmark on %s.\n\n", serial_port_name); + return -1; + } else { + fprintf(stderr," Found.\n"); + } res = flash_start_flashing(can_write_bl, serial_port_name); if (res < 0) @@ -137,7 +110,8 @@ int main(int argc, char **argv) if (res < 0) return -1; - CloseProxmark(serial_port_name); + // Stop the command thread. + CloseProxmark(); fprintf(stderr, "All done.\n\n"); fprintf(stderr, "Have a nice day!\n"); diff --git a/client/mifarehost.c b/client/mifarehost.c index e1ced176..2d69cfae 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -16,7 +16,7 @@ #include #include "crapto1/crapto1.h" -#include "proxmark3.h" +#include "comms.h" #include "usb_cmd.h" #include "cmdmain.h" #include "ui.h" diff --git a/client/proxmark3.c b/client/proxmark3.c index 88cb5fa7..40c46613 100644 --- a/client/proxmark3.c +++ b/client/proxmark3.c @@ -9,6 +9,8 @@ // Main binary //----------------------------------------------------------------------------- +#include "proxmark3.h" + #include #include #include @@ -17,7 +19,6 @@ #include #include -#include "proxmark3.h" #include "util_posix.h" #include "proxgui.h" #include "cmdmain.h" @@ -26,7 +27,7 @@ #include "cmdparser.h" #include "cmdhw.h" #include "whereami.h" - +#include "comms.h" void #ifdef __has_attribute @@ -35,18 +36,12 @@ __attribute__((force_align_arg_pointer)) #endif #endif main_loop(char *script_cmds_file, char *script_cmd, bool usb_present) { - receiver_arg conn; char *cmd = NULL; - pthread_t reader_thread; bool execCommand = (script_cmd != NULL); bool stdinOnPipe = !isatty(STDIN_FILENO); - memset(&conn, 0, sizeof(receiver_arg)); - if (usb_present) { - conn.run = true; SetOffline(false); - pthread_create(&reader_thread, NULL, &uart_receiver, &conn); // cache Version information now: CmdVersion(NULL); } else { @@ -138,8 +133,7 @@ main_loop(char *script_cmds_file, char *script_cmd, bool usb_present) { write_history(".history"); if (usb_present) { - conn.run = false; - pthread_join(reader_thread, NULL); + CloseProxmark(); } if (script_file) { @@ -294,7 +288,7 @@ int main(int argc, char* argv[]) { set_my_executable_path(); // try to open USB connection to Proxmark - usb_present = OpenProxmark(argv[1], waitCOMPort, 20); + usb_present = OpenProxmark(argv[1], waitCOMPort, 20, false); #ifdef HAVE_GUI #ifdef _WIN32 diff --git a/client/proxmark3.h b/client/proxmark3.h index c6185c43..86d09bc0 100644 --- a/client/proxmark3.h +++ b/client/proxmark3.h @@ -20,7 +20,6 @@ extern "C" { #endif -void SendCommand(UsbCommand *c); const char *get_my_executable_path(void); const char *get_my_executable_directory(void); void main_loop(char *script_cmds_file, char *script_cmd, bool usb_present); diff --git a/client/scripting.c b/client/scripting.c index 0c761cb2..e73c7138 100644 --- a/client/scripting.c +++ b/client/scripting.c @@ -15,6 +15,7 @@ #include #include #include "proxmark3.h" +#include "comms.h" #include "usb_cmd.h" #include "cmdmain.h" #include "util.h" From 2bb7f7e327df13f288b2b98a71bb390c516cc982 Mon Sep 17 00:00:00 2001 From: Michael Farrell Date: Mon, 4 Jun 2018 21:54:41 +1000 Subject: [PATCH 229/310] Fixes a double-free issue in CloseProxmark: (#617) - CloseProxmark now clears global state. - CloseProxmark now checks for a non-null serial_port before calling uart_close, to avoid unintentional double-free'ing serial_port. - main now calls CloseProxmark once. --- client/comms.c | 10 +++++++++- client/proxmark3.c | 4 ---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/client/comms.c b/client/comms.c index eeaff79c..be0cfd10 100644 --- a/client/comms.c +++ b/client/comms.c @@ -334,13 +334,21 @@ bool OpenProxmark(void *port, bool wait_for_port, int timeout, bool flash_mode) void CloseProxmark(void) { conn.run = false; pthread_join(USB_communication_thread, NULL); - uart_close(sp); + + if (sp) { + uart_close(sp); + } + #ifdef __linux__ // Fix for linux, it seems that it is extremely slow to release the serial port file descriptor /dev/* if (serial_port_name) { unlink(serial_port_name); } #endif + + // Clean up our state + sp = NULL; + serial_port_name = NULL; } diff --git a/client/proxmark3.c b/client/proxmark3.c index 40c46613..6fb066e8 100644 --- a/client/proxmark3.c +++ b/client/proxmark3.c @@ -131,10 +131,6 @@ main_loop(char *script_cmds_file, char *script_cmd, bool usb_present) { } write_history(".history"); - - if (usb_present) { - CloseProxmark(); - } if (script_file) { fclose(script_file); From 44964fd181988c54ed4df58dc015dc09e1a7ac3a Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Wed, 13 Jun 2018 08:13:20 +0200 Subject: [PATCH 230/310] Code cleanup (#616) * coverity fixes (including a real bug in cmdhftopaz.c) * Typo fix * replace TRUE/FALSE by stdbool true/false --- armsrc/appmain.c | 112 +++++++++++++++++------------------ armsrc/hitag2.c | 10 ++-- armsrc/hitagS.c | 11 ++-- armsrc/iclass.c | 74 +++++++++++------------ armsrc/iso14443b.c | 68 ++++++++++----------- armsrc/iso15693.c | 8 +-- armsrc/lfops.c | 6 +- armsrc/mifarecmd.c | 2 +- armsrc/mifaresniff.c | 12 ++-- armsrc/printf.c | 1 + client/cmdhf.c | 2 +- client/cmdhf14a.c | 13 ++-- client/cmdhfepa.c | 9 +-- client/cmdhflist.c | 4 +- client/cmdhfmf.c | 15 ++--- client/cmdhfmfhard.c | 1 - client/cmdhfmfu.c | 2 +- client/cmdhftopaz.c | 7 +-- client/cmdlfhitag.c | 17 +++--- client/cmdlfindala.c | 2 +- client/emv/crypto_polarssl.c | 6 +- client/flash.c | 6 +- client/fpga_compress.c | 2 +- client/mifarehost.c | 3 +- client/proxgui.cpp | 7 +-- client/proxguiqt.h | 1 - client/scripting.c | 2 +- client/util_posix.c | 2 +- include/proxmark3.h | 3 - tools/mfkey/mfkey32.c | 2 +- 30 files changed, 198 insertions(+), 212 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 679a533a..e8581216 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -394,8 +394,8 @@ void StandAloneMode14a() FpgaDownloadAndGo(FPGA_BITSTREAM_HF); int selected = 0; - int playing = 0, iGotoRecord = 0, iGotoClone = 0; - int cardRead[OPTS] = {0}; + bool playing = false, GotoRecord = false, GotoClone = false; + bool cardRead[OPTS] = {false}; uint8_t readUID[10] = {0}; uint32_t uid_1st[OPTS]={0}; uint32_t uid_2nd[OPTS]={0}; @@ -411,9 +411,9 @@ void StandAloneMode14a() WDT_HIT(); SpinDelay(300); - if (iGotoRecord == 1 || cardRead[selected] == 0) + if (GotoRecord || !cardRead[selected]) { - iGotoRecord = 0; + GotoRecord = false; LEDsoff(); LED(selected + 1, 0); LED(LED_RED2, 0); @@ -438,7 +438,7 @@ void StandAloneMode14a() else if (cardRead[(selected+1)%OPTS]) { Dbprintf("Button press detected but no card in bank[%d] so playing from bank[%d]", selected, (selected+1)%OPTS); selected = (selected+1)%OPTS; - break; // playing = 1; + break; } else { Dbprintf("Button press detected but no stored tag to play. (Ignoring button)"); @@ -488,14 +488,14 @@ void StandAloneMode14a() LED(selected + 1, 0); // Next state is replay: - playing = 1; + playing = true; - cardRead[selected] = 1; + cardRead[selected] = true; } /* MF Classic UID clone */ - else if (iGotoClone==1) + else if (GotoClone) { - iGotoClone=0; + GotoClone=false; LEDsoff(); LED(selected + 1, 0); LED(LED_ORANGE, 250); @@ -546,7 +546,7 @@ void StandAloneMode14a() MifareCGetBlock(0x3F, 1, 0, oldBlock0); if (oldBlock0[0] == 0 && oldBlock0[0] == oldBlock0[1] && oldBlock0[1] == oldBlock0[2] && oldBlock0[2] == oldBlock0[3]) { Dbprintf("No changeable tag detected. Returning to replay mode for bank[%d]", selected); - playing = 1; + playing = true; } else { Dbprintf("UID from target tag: %02X%02X%02X%02X", oldBlock0[0],oldBlock0[1],oldBlock0[2],oldBlock0[3]); @@ -564,14 +564,14 @@ void StandAloneMode14a() if (memcmp(testBlock0,newBlock0,16)==0) { DbpString("Cloned successfull!"); - cardRead[selected] = 0; // Only if the card was cloned successfully should we clear it - playing = 0; - iGotoRecord = 1; + cardRead[selected] = false; // Only if the card was cloned successfully should we clear it + playing = false; + GotoRecord = true; selected = (selected+1) % OPTS; } else { Dbprintf("Clone failed. Back to replay mode on bank[%d]", selected); - playing = 1; + playing = true; } } LEDsoff(); @@ -579,61 +579,55 @@ void StandAloneMode14a() } // Change where to record (or begin playing) - else if (playing==1) // button_pressed == BUTTON_SINGLE_CLICK && cardRead[selected]) + else if (playing) // button_pressed == BUTTON_SINGLE_CLICK && cardRead[selected]) { LEDsoff(); LED(selected + 1, 0); // Begin transmitting - if (playing) - { - LED(LED_GREEN, 0); - DbpString("Playing"); - for ( ; ; ) { - WDT_HIT(); - int button_action = BUTTON_HELD(1000); - if (button_action == 0) { // No button action, proceed with sim - uint8_t data[512] = {0}; // in case there is a read command received we shouldn't break - Dbprintf("Simulating ISO14443a tag with uid[0]: %08x, uid[1]: %08x [Bank: %u]", uid_1st[selected],uid_2nd[selected],selected); - if (hi14a_card[selected].sak == 8 && hi14a_card[selected].atqa[0] == 4 && hi14a_card[selected].atqa[1] == 0) { - DbpString("Mifare Classic"); - SimulateIso14443aTag(1,uid_1st[selected], uid_2nd[selected], data); // Mifare Classic - } - else if (hi14a_card[selected].sak == 0 && hi14a_card[selected].atqa[0] == 0x44 && hi14a_card[selected].atqa[1] == 0) { - DbpString("Mifare Ultralight"); - SimulateIso14443aTag(2,uid_1st[selected],uid_2nd[selected],data); // Mifare Ultralight - } - else if (hi14a_card[selected].sak == 20 && hi14a_card[selected].atqa[0] == 0x44 && hi14a_card[selected].atqa[1] == 3) { - DbpString("Mifare DESFire"); - SimulateIso14443aTag(3,uid_1st[selected],uid_2nd[selected],data); // Mifare DESFire - } - else { - Dbprintf("Unrecognized tag type -- defaulting to Mifare Classic emulation"); - SimulateIso14443aTag(1,uid_1st[selected], uid_2nd[selected], data); - } + LED(LED_GREEN, 0); + DbpString("Playing"); + for ( ; ; ) { + WDT_HIT(); + int button_action = BUTTON_HELD(1000); + if (button_action == 0) { // No button action, proceed with sim + uint8_t data[512] = {0}; // in case there is a read command received we shouldn't break + Dbprintf("Simulating ISO14443a tag with uid[0]: %08x, uid[1]: %08x [Bank: %u]", uid_1st[selected],uid_2nd[selected],selected); + if (hi14a_card[selected].sak == 8 && hi14a_card[selected].atqa[0] == 4 && hi14a_card[selected].atqa[1] == 0) { + DbpString("Mifare Classic"); + SimulateIso14443aTag(1,uid_1st[selected], uid_2nd[selected], data); // Mifare Classic } - else if (button_action == BUTTON_SINGLE_CLICK) { - selected = (selected + 1) % OPTS; - Dbprintf("Done playing. Switching to record mode on bank %d",selected); - iGotoRecord = 1; - break; + else if (hi14a_card[selected].sak == 0 && hi14a_card[selected].atqa[0] == 0x44 && hi14a_card[selected].atqa[1] == 0) { + DbpString("Mifare Ultralight"); + SimulateIso14443aTag(2,uid_1st[selected],uid_2nd[selected],data); // Mifare Ultralight } - else if (button_action == BUTTON_HOLD) { - Dbprintf("Playtime over. Begin cloning..."); - iGotoClone = 1; - break; + else if (hi14a_card[selected].sak == 20 && hi14a_card[selected].atqa[0] == 0x44 && hi14a_card[selected].atqa[1] == 3) { + DbpString("Mifare DESFire"); + SimulateIso14443aTag(3,uid_1st[selected],uid_2nd[selected],data); // Mifare DESFire + } + else { + Dbprintf("Unrecognized tag type -- defaulting to Mifare Classic emulation"); + SimulateIso14443aTag(1,uid_1st[selected], uid_2nd[selected], data); } - WDT_HIT(); } - - /* We pressed a button so ignore it here with a delay */ - SpinDelay(300); - LEDsoff(); - LED(selected + 1, 0); + else if (button_action == BUTTON_SINGLE_CLICK) { + selected = (selected + 1) % OPTS; + Dbprintf("Done playing. Switching to record mode on bank %d",selected); + GotoRecord = true; + break; + } + else if (button_action == BUTTON_HOLD) { + Dbprintf("Playtime over. Begin cloning..."); + GotoClone = true; + break; + } + WDT_HIT(); } - else - while(BUTTON_PRESS()) - WDT_HIT(); + + /* We pressed a button so ignore it here with a delay */ + SpinDelay(300); + LEDsoff(); + LED(selected + 1, 0); } } } diff --git a/armsrc/hitag2.c b/armsrc/hitag2.c index aec01860..8e690a7b 100644 --- a/armsrc/hitag2.c +++ b/armsrc/hitag2.c @@ -813,13 +813,13 @@ void SnoopHitag(uint32_t type) { int lastbit; bool bSkip; int tag_sof; - byte_t rx[HITAG_FRAME_LEN]; + byte_t rx[HITAG_FRAME_LEN] = {0}; size_t rxlen=0; FpgaDownloadAndGo(FPGA_BITSTREAM_LF); // Clean up trace and prepare it for storing frames - set_tracing(TRUE); + set_tracing(true); clear_trace(); auth_table_len = 0; @@ -1032,7 +1032,7 @@ void SimulateHitagTag(bool tag_mem_supplied, byte_t* data) { FpgaDownloadAndGo(FPGA_BITSTREAM_LF); // Clean up trace and prepare it for storing frames - set_tracing(TRUE); + set_tracing(true); clear_trace(); auth_table_len = 0; @@ -1225,7 +1225,7 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { bSuccessful = false; // Clean up trace and prepare it for storing frames - set_tracing(TRUE); + set_tracing(true); clear_trace(); //DbpString("Starting Hitag reader family"); @@ -1548,7 +1548,7 @@ void WriterHitag(hitag_function htf, hitag_data* htd, int page) { bSuccessful = false; // Clean up trace and prepare it for storing frames - set_tracing(TRUE); + set_tracing(true); clear_trace(); //DbpString("Starting Hitag reader family"); diff --git a/armsrc/hitagS.c b/armsrc/hitagS.c index 7d36d84b..f6ba0c6b 100644 --- a/armsrc/hitagS.c +++ b/armsrc/hitagS.c @@ -210,7 +210,7 @@ static void hitag_send_bit(int bit) { ; LOW(GPIO_SSC_DOUT); while (AT91C_BASE_TC0->TC_CV < T0 * 32) - ;; + ; } LED_A_OFF(); break; @@ -945,7 +945,6 @@ void SimulateHitagSTag(bool tag_mem_supplied, byte_t* data) { int i, j; byte_t rx[HITAG_FRAME_LEN]; size_t rxlen = 0; -//bool bQuitTraceFull = false; bQuiet = false; byte_t txbuf[HITAG_FRAME_LEN]; byte_t* tx = txbuf; @@ -953,7 +952,7 @@ void SimulateHitagSTag(bool tag_mem_supplied, byte_t* data) { BigBuf_free(); // Clean up trace and prepare it for storing frames - set_tracing(TRUE); + set_tracing(true); clear_trace(); DbpString("Starting HitagS simulation"); @@ -1216,7 +1215,7 @@ void ReadHitagS(hitag_function htf, hitag_data* htd) { bSuccessful = false; // Clean up trace and prepare it for storing frames - set_tracing(TRUE); + set_tracing(true); clear_trace(); bQuiet = false; @@ -1560,7 +1559,7 @@ void WritePageHitagS(hitag_function htf, hitag_data* htd,int page_) { tag.tstate = NO_OP; // Clean up trace and prepare it for storing frames - set_tracing(TRUE); + set_tracing(true); clear_trace(); bQuiet = false; @@ -1847,7 +1846,7 @@ void check_challenges(bool file_given, byte_t* data) { bSuccessful = false; // Clean up trace and prepare it for storing frames - set_tracing(TRUE); + set_tracing(true); clear_trace(); bQuiet = false; diff --git a/armsrc/iclass.c b/armsrc/iclass.c index f9aedc95..c587f8ea 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -91,7 +91,7 @@ static RAMFUNC int OutOfNDecoding(int bit) if(!Uart.bitBuffer) { Uart.bitBuffer = bit ^ 0xFF0; - return FALSE; + return false; } else { Uart.bitBuffer <<= 4; @@ -102,7 +102,7 @@ static RAMFUNC int OutOfNDecoding(int bit) Uart.output[Uart.byteCnt] = Uart.bitBuffer & 0xFF; Uart.byteCnt++; Uart.swapper = 0; - if(Uart.byteCnt > 15) { return TRUE; } + if(Uart.byteCnt > 15) { return true; } } else { Uart.swapper = 1; @@ -139,12 +139,12 @@ static RAMFUNC int OutOfNDecoding(int bit) Uart.highCnt = 0; if(Uart.byteCnt == 0) { // Its not straightforward to show single EOFs - // So just leave it and do not return TRUE + // So just leave it and do not return true Uart.output[0] = 0xf0; Uart.byteCnt++; } else { - return TRUE; + return true; } } else if(Uart.state != STATE_START_OF_COMMUNICATION) { @@ -263,7 +263,7 @@ static RAMFUNC int OutOfNDecoding(int bit) Uart.byteCnt++; Uart.output[Uart.byteCnt] = 0xAA; Uart.byteCnt++; - return TRUE; + return true; }*/ } @@ -318,7 +318,7 @@ static RAMFUNC int OutOfNDecoding(int bit) } } - return FALSE; + return false; } //============================================================================= @@ -371,7 +371,7 @@ static RAMFUNC int ManchesterDecoding(int v) if(Demod.buff < 3) { Demod.buff++; - return FALSE; + return false; } if(Demod.state==DEMOD_UNSYNCD) { @@ -473,7 +473,7 @@ static RAMFUNC int ManchesterDecoding(int v) Demod.len++; Demod.state = DEMOD_UNSYNCD; // error = 0x0f; - return TRUE; + return true; } else { Demod.state = DEMOD_ERROR_WAIT; @@ -557,7 +557,7 @@ static RAMFUNC int ManchesterDecoding(int v) } Demod.state = DEMOD_UNSYNCD; - return TRUE; + return true; } else { Demod.output[Demod.len] = 0xad; @@ -612,14 +612,14 @@ static RAMFUNC int ManchesterDecoding(int v) Demod.len++; Demod.output[Demod.len] = 0xBB; Demod.len++; - return TRUE; + return true; } } } // end (state != UNSYNCED) - return FALSE; + return false; } //============================================================================= @@ -639,7 +639,7 @@ void RAMFUNC SnoopIClass(void) // We won't start recording the frames that we acquire until we trigger; // a good trigger condition to get started is probably when we see a // response from the tag. - //int triggered = FALSE; // FALSE to wait first for card + //int triggered = false; // false to wait first for card // The command (reader -> tag) that we're receiving. // The length of a received command will in most cases be no more than 18 bytes. @@ -656,9 +656,9 @@ void RAMFUNC SnoopIClass(void) // The DMA buffer, used to stream samples from the FPGA uint8_t *dmaBuf = BigBuf_malloc(DMA_BUFFER_SIZE); - set_tracing(TRUE); + set_tracing(true); clear_trace(); - iso14a_set_trigger(FALSE); + iso14a_set_trigger(false); int lastRxCounter; uint8_t *upTo; @@ -749,12 +749,12 @@ void RAMFUNC SnoopIClass(void) time_stop = (GetCountSspClk()-time_0) << 4; LED_C_ON(); - //if(!LogTrace(Uart.output,Uart.byteCnt, rsamples, Uart.parityBits,TRUE)) break; - //if(!LogTrace(NULL, 0, Uart.endTime*16 - DELAY_READER_AIR2ARM_AS_SNIFFER, 0, TRUE)) break; + //if(!LogTrace(Uart.output,Uart.byteCnt, rsamples, Uart.parityBits,true)) break; + //if(!LogTrace(NULL, 0, Uart.endTime*16 - DELAY_READER_AIR2ARM_AS_SNIFFER, 0, true)) break; if(tracing) { uint8_t parity[MAX_PARITY_SIZE]; GetParity(Uart.output, Uart.byteCnt, parity); - LogTrace(Uart.output,Uart.byteCnt, time_start, time_stop, parity, TRUE); + LogTrace(Uart.output,Uart.byteCnt, time_start, time_stop, parity, true); } @@ -782,7 +782,7 @@ void RAMFUNC SnoopIClass(void) if(tracing) { uint8_t parity[MAX_PARITY_SIZE]; GetParity(Demod.output, Demod.len, parity); - LogTrace(Demod.output, Demod.len, time_start, time_stop, parity, FALSE); + LogTrace(Demod.output, Demod.len, time_start, time_stop, parity, false); } // And ready to receive another response. @@ -830,7 +830,7 @@ void rotateCSN(uint8_t* originalCSN, uint8_t* rotatedCSN) { //----------------------------------------------------------------------------- // Wait for commands from reader // Stop when button is pressed -// Or return TRUE when command is captured +// Or return true when command is captured //----------------------------------------------------------------------------- static int GetIClassCommandFromReader(uint8_t *received, int *len, int maxLen) { @@ -848,7 +848,7 @@ static int GetIClassCommandFromReader(uint8_t *received, int *len, int maxLen) for(;;) { WDT_HIT(); - if(BUTTON_PRESS()) return FALSE; + if(BUTTON_PRESS()) return false; if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { AT91C_BASE_SSC->SSC_THR = 0x00; @@ -858,7 +858,7 @@ static int GetIClassCommandFromReader(uint8_t *received, int *len, int maxLen) if(OutOfNDecoding(b & 0x0f)) { *len = Uart.byteCnt; - return TRUE; + return true; } } } @@ -993,7 +993,7 @@ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Enable and clear the trace - set_tracing(TRUE); + set_tracing(true); clear_trace(); //Use the emulator memory for SIM uint8_t *emulator = BigBuf_get_EM_addr(); @@ -1325,11 +1325,11 @@ int doIClassSimulation( int simulationMode, uint8_t *reader_mac_buf) if (tracing) { uint8_t parity[MAX_PARITY_SIZE]; GetParity(receivedCmd, len, parity); - LogTrace(receivedCmd,len, (r2t_time-time_0)<< 4, (r2t_time-time_0) << 4, parity, TRUE); + LogTrace(receivedCmd,len, (r2t_time-time_0)<< 4, (r2t_time-time_0) << 4, parity, true); if (trace_data != NULL) { GetParity(trace_data, trace_data_size, parity); - LogTrace(trace_data, trace_data_size, (t2r_time-time_0) << 4, (t2r_time-time_0) << 4, parity, FALSE); + LogTrace(trace_data, trace_data_size, (t2r_time-time_0) << 4, (t2r_time-time_0) << 4, parity, false); } if(!tracing) { DbpString("Trace full"); @@ -1420,7 +1420,7 @@ static void TransmitIClassCommand(const uint8_t *cmd, int len, int *samples, int uint8_t sendbyte; - bool firstpart = TRUE; + bool firstpart = true; c = 0; for(;;) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { @@ -1512,14 +1512,14 @@ void ReaderTransmitIClass(uint8_t* frame, int len) if (tracing) { uint8_t par[MAX_PARITY_SIZE]; GetParity(frame, len, par); - LogTrace(frame, len, rsamples, rsamples, par, TRUE); + LogTrace(frame, len, rsamples, rsamples, par, true); } } //----------------------------------------------------------------------------- // Wait a certain time for tag response -// If a response is captured return TRUE -// If it takes too long return FALSE +// If a response is captured return true +// If it takes too long return false //----------------------------------------------------------------------------- static int GetIClassAnswer(uint8_t *receivedResponse, int maxLen, int *samples, int *elapsed) //uint8_t *buffer { @@ -1538,27 +1538,27 @@ static int GetIClassAnswer(uint8_t *receivedResponse, int maxLen, int *samples, uint8_t b; if (elapsed) *elapsed = 0; - bool skip = FALSE; + bool skip = false; c = 0; for(;;) { WDT_HIT(); - if(BUTTON_PRESS()) return FALSE; + if(BUTTON_PRESS()) return false; if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { AT91C_BASE_SSC->SSC_THR = 0x00; // To make use of exact timing of next command from reader!! if (elapsed) (*elapsed)++; } if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - if(c < timeout) { c++; } else { return FALSE; } + if(c < timeout) { c++; } else { return false; } b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; skip = !skip; if(skip) continue; if(ManchesterDecoding(b & 0x0f)) { *samples = c << 3; - return TRUE; + return true; } } } @@ -1567,14 +1567,14 @@ static int GetIClassAnswer(uint8_t *receivedResponse, int maxLen, int *samples, int ReaderReceiveIClass(uint8_t* receivedAnswer) { int samples = 0; - if (!GetIClassAnswer(receivedAnswer,160,&samples,0)) return FALSE; + if (!GetIClassAnswer(receivedAnswer,160,&samples,0)) return false; rsamples += samples; if (tracing) { uint8_t parity[MAX_PARITY_SIZE]; GetParity(receivedAnswer, Demod.len, parity); - LogTrace(receivedAnswer,Demod.len,rsamples,rsamples,parity,FALSE); + LogTrace(receivedAnswer,Demod.len,rsamples,rsamples,parity,false); } - if(samples == 0) return FALSE; + if(samples == 0) return false; return Demod.len; } @@ -1582,7 +1582,7 @@ void setupIclassReader() { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Reset trace buffer - set_tracing(TRUE); + set_tracing(true); clear_trace(); // Setup SSC @@ -1822,7 +1822,7 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *MAC) { uint8_t resp[ICLASS_BUFFER_SIZE]; setupIclassReader(); - set_tracing(TRUE); + set_tracing(true); while(!BUTTON_PRESS()) { diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index 22227e74..75769859 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -243,7 +243,7 @@ static RAMFUNC int Handle14443bUartBit(uint8_t bit) LED_A_OFF(); // Finished receiving Uart.state = STATE_UNSYNCD; if (Uart.byteCnt != 0) { - return TRUE; + return true; } } else { // this is an error @@ -259,7 +259,7 @@ static RAMFUNC int Handle14443bUartBit(uint8_t bit) break; } - return FALSE; + return false; } @@ -283,7 +283,7 @@ static void UartInit(uint8_t *data) // Receive a command (from the reader to us, where we are the simulated tag), // and store it in the given buffer, up to the given maximum length. Keeps // spinning, waiting for a well-framed command, until either we get one -// (returns TRUE) or someone presses the pushbutton on the board (FALSE). +// (returns true) or someone presses the pushbutton on the board (false). // // Assume that we're called with the SSC (to the FPGA) and ADC path set // correctly. @@ -302,20 +302,20 @@ static int GetIso14443bCommandFromReader(uint8_t *received, uint16_t *len) for(;;) { WDT_HIT(); - if(BUTTON_PRESS()) return FALSE; + if(BUTTON_PRESS()) return false; if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; for(uint8_t mask = 0x80; mask != 0x00; mask >>= 1) { if(Handle14443bUartBit(b & mask)) { *len = Uart.byteCnt; - return TRUE; + return true; } } } } - return FALSE; + return false; } //----------------------------------------------------------------------------- @@ -347,7 +347,7 @@ void SimulateIso14443bTag(void) FpgaDownloadAndGo(FPGA_BITSTREAM_HF); clear_trace(); - set_tracing(TRUE); + set_tracing(true); const uint8_t *resp; uint8_t *respCode; @@ -387,7 +387,7 @@ void SimulateIso14443bTag(void) if (tracing) { uint8_t parity[MAX_PARITY_SIZE]; - LogTrace(receivedCmd, len, 0, 0, parity, TRUE); + LogTrace(receivedCmd, len, 0, 0, parity, true); } // Good, look at the command now. @@ -464,7 +464,7 @@ void SimulateIso14443bTag(void) // trace the response: if (tracing) { uint8_t parity[MAX_PARITY_SIZE]; - LogTrace(resp, respLen, 0, 0, parity, FALSE); + LogTrace(resp, respLen, 0, 0, parity, false); } } @@ -702,7 +702,7 @@ static RAMFUNC int Handle14443bSamplesDemod(int ci, int cq) LED_C_OFF(); if(s == 0x000) { // This is EOF (start, stop and all data bits == '0' - return TRUE; + return true; } } } @@ -716,7 +716,7 @@ static RAMFUNC int Handle14443bSamplesDemod(int ci, int cq) break; } - return FALSE; + return false; } @@ -739,12 +739,12 @@ static void DemodInit(uint8_t *data) /* * Demodulate the samples we received from the tag, also log to tracebuffer - * quiet: set to 'TRUE' to disable debug output + * quiet: set to 'true' to disable debug output */ static void GetSamplesFor14443bDemod(int n, bool quiet) { int max = 0; - bool gotFrame = FALSE; + bool gotFrame = false; int lastRxCounter, ci, cq, samples = 0; // Allocate memory from BigBuf for some buffers @@ -792,7 +792,7 @@ static void GetSamplesFor14443bDemod(int n, bool quiet) samples += 2; if(Handle14443bSamplesDemod(ci, cq)) { - gotFrame = TRUE; + gotFrame = true; break; } } @@ -808,7 +808,7 @@ static void GetSamplesFor14443bDemod(int n, bool quiet) //Tracing if (tracing && Demod.len > 0) { uint8_t parity[MAX_PARITY_SIZE]; - LogTrace(Demod.output, Demod.len, 0, 0, parity, FALSE); + LogTrace(Demod.output, Demod.len, 0, 0, parity, false); } } @@ -929,7 +929,7 @@ static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len) TransmitFor14443b(); if (tracing) { uint8_t parity[MAX_PARITY_SIZE]; - LogTrace(cmd,len, 0, 0, parity, TRUE); + LogTrace(cmd,len, 0, 0, parity, true); } } @@ -951,7 +951,7 @@ int iso14443b_apdu(uint8_t const *message, size_t message_length, uint8_t *respo // send CodeAndTransmit14443bAsReader(message_frame, message_length + 4); // get response - GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT*100, TRUE); + GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT*100, true); if(Demod.len < 3) { return 0; @@ -981,7 +981,7 @@ int iso14443b_select_card() // first, wake up the tag CodeAndTransmit14443bAsReader(wupb, sizeof(wupb)); - GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); + GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, true); // ATQB too short? if (Demod.len < 14) { @@ -996,7 +996,7 @@ int iso14443b_select_card() attrib[7] = Demod.output[10] & 0x0F; ComputeCrc14443(CRC_14443_B, attrib, 9, attrib + 9, attrib + 10); CodeAndTransmit14443bAsReader(attrib, sizeof(attrib)); - GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); + GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, true); // Answer to ATTRIB too short? if(Demod.len < 3) { @@ -1056,12 +1056,12 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) SpinDelay(200); clear_trace(); - set_tracing(TRUE); + set_tracing(true); // First command: wake up the tag using the INITIATE command uint8_t cmd1[] = {0x06, 0x00, 0x97, 0x5b}; CodeAndTransmit14443bAsReader(cmd1, sizeof(cmd1)); - GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); + GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, true); if (Demod.len == 0) { DbpString("No response from tag"); @@ -1077,7 +1077,7 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) cmd1[1] = Demod.output[0]; ComputeCrc14443(CRC_14443_B, cmd1, 2, &cmd1[2], &cmd1[3]); CodeAndTransmit14443bAsReader(cmd1, sizeof(cmd1)); - GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); + GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, true); if (Demod.len != 3) { Dbprintf("Expected 3 bytes from tag, got %d", Demod.len); return; @@ -1099,7 +1099,7 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) cmd1[0] = 0x0B; ComputeCrc14443(CRC_14443_B, cmd1, 1 , &cmd1[1], &cmd1[2]); CodeAndTransmit14443bAsReader(cmd1, 3); // Only first three bytes for this one - GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); + GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, true); if (Demod.len != 10) { Dbprintf("Expected 10 bytes from tag, got %d", Demod.len); return; @@ -1128,7 +1128,7 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) cmd1[1] = i; ComputeCrc14443(CRC_14443_B, cmd1, 2, &cmd1[2], &cmd1[3]); CodeAndTransmit14443bAsReader(cmd1, sizeof(cmd1)); - GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); + GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, true); if (Demod.len != 6) { // Check if we got an answer from the tag DbpString("Expected 6 bytes from tag, got less..."); return; @@ -1174,13 +1174,13 @@ void RAMFUNC SnoopIso14443b(void) // We won't start recording the frames that we acquire until we trigger; // a good trigger condition to get started is probably when we see a // response from the tag. - int triggered = TRUE; // TODO: set and evaluate trigger condition + int triggered = true; // TODO: set and evaluate trigger condition FpgaDownloadAndGo(FPGA_BITSTREAM_HF); BigBuf_free(); clear_trace(); - set_tracing(TRUE); + set_tracing(true); // The DMA buffer, used to stream samples from the FPGA int8_t *dmaBuf = (int8_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE); @@ -1217,8 +1217,8 @@ void RAMFUNC SnoopIso14443b(void) FpgaSetupSscDma((uint8_t*) dmaBuf, ISO14443B_DMA_BUFFER_SIZE); uint8_t parity[MAX_PARITY_SIZE]; - bool TagIsActive = FALSE; - bool ReaderIsActive = FALSE; + bool TagIsActive = false; + bool ReaderIsActive = false; // And now we loop, receiving samples. for(;;) { @@ -1259,7 +1259,7 @@ void RAMFUNC SnoopIso14443b(void) if (!TagIsActive) { // no need to try decoding reader data if the tag is sending if(Handle14443bUartBit(ci & 0x01)) { if(triggered && tracing) { - LogTrace(Uart.output, Uart.byteCnt, samples, samples, parity, TRUE); + LogTrace(Uart.output, Uart.byteCnt, samples, samples, parity, true); } /* And ready to receive another command. */ UartReset(); @@ -1269,7 +1269,7 @@ void RAMFUNC SnoopIso14443b(void) } if(Handle14443bUartBit(cq & 0x01)) { if(triggered && tracing) { - LogTrace(Uart.output, Uart.byteCnt, samples, samples, parity, TRUE); + LogTrace(Uart.output, Uart.byteCnt, samples, samples, parity, true); } /* And ready to receive another command. */ UartReset(); @@ -1287,9 +1287,9 @@ void RAMFUNC SnoopIso14443b(void) if(tracing) { uint8_t parity[MAX_PARITY_SIZE]; - LogTrace(Demod.output, Demod.len, samples, samples, parity, FALSE); + LogTrace(Demod.output, Demod.len, samples, samples, parity, false); } - triggered = TRUE; + triggered = true; // And ready to receive another response. DemodReset(); @@ -1330,12 +1330,12 @@ void SendRawCommand14443B(uint32_t datalen, uint32_t recv, uint8_t powerfield, u FpgaSetupSsc(); if (datalen){ - set_tracing(TRUE); + set_tracing(true); CodeAndTransmit14443bAsReader(data, datalen); if(recv) { - GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); + GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, true); uint16_t iLen = MIN(Demod.len, USB_CMD_DATA_SIZE); cmd_send(CMD_ACK, iLen, 0, 0, Demod.output, iLen); } diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index e11e5a9c..1f0b8193 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -305,7 +305,7 @@ static int GetIso15693AnswerFromTag(uint8_t *receivedResponse, int maxLen, int * FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); //spindelay(60); // greg - experiment to get rid of some of the 0 byte/failed reads c = 0; - getNext = FALSE; + getNext = false; for(;;) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { AT91C_BASE_SSC->SSC_THR = 0x43; @@ -444,7 +444,7 @@ static int GetIso15693AnswerFromSniff(uint8_t *receivedResponse, int maxLen, int FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); //spindelay(60); // greg - experiment to get rid of some of the 0 byte/failed reads c = 0; - getNext = FALSE; + getNext = false; for(;;) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { AT91C_BASE_SSC->SSC_THR = 0x43; @@ -612,7 +612,7 @@ void AcquireRawAdcSamplesIso15693(void) FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); c = 0; - getNext = FALSE; + getNext = false; for(;;) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { AT91C_BASE_SSC->SSC_THR = 0x43; @@ -666,7 +666,7 @@ void RecordRawAdcSamplesIso15693(void) FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); c = 0; - getNext = FALSE; + getNext = false; for(;;) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { AT91C_BASE_SSC->SSC_THR = 0x43; diff --git a/armsrc/lfops.c b/armsrc/lfops.c index f3bbbf84..911ba8da 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -568,7 +568,7 @@ static void fcAll(uint8_t fc, int *n, uint8_t clock, uint16_t *modCnt) uint8_t wavesPerClock = clock/fc; uint8_t mod = clock % fc; //modifier uint8_t modAdj = fc/mod; //how often to apply modifier - bool modAdjOk = !(fc % mod); //if (fc % mod==0) modAdjOk=TRUE; + bool modAdjOk = !(fc % mod); //if (fc % mod==0) modAdjOk=true; // loop through clock - step field clock for (uint8_t idx=0; idx < wavesPerClock; idx++){ // put 1/2 FC length 1's and 1/2 0's per field clock wave (to create the wave) @@ -820,9 +820,9 @@ void CmdPSKsimTag(uint16_t arg1, uint16_t arg2, size_t size, uint8_t *BitStream) for (i=0; i= 128 && blockN <= 256) { return ((blockN & 0x0F) == 0x0F); } - return FALSE; + return false; } void MifareCWipe(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain){ diff --git a/armsrc/mifaresniff.c b/armsrc/mifaresniff.c index f20f2557..5391e5f9 100644 --- a/armsrc/mifaresniff.c +++ b/armsrc/mifaresniff.c @@ -35,7 +35,7 @@ bool MfSniffInit(void){ sniffSAK = 0; sniffUIDType = SNF_UID_4; - return FALSE; + return false; } bool MfSniffEnd(void){ @@ -43,7 +43,7 @@ bool MfSniffEnd(void){ cmd_send(CMD_ACK,0,0,0,0,0); LED_B_OFF(); - return FALSE; + return false; } bool RAMFUNC MfSniffLogic(const uint8_t *data, uint16_t len, uint8_t *parity, uint16_t bitCnt, bool reader) { @@ -112,7 +112,7 @@ bool RAMFUNC MfSniffLogic(const uint8_t *data, uint16_t len, uint8_t *parity, ui sniffBuf[11] = sniffSAK; sniffBuf[12] = 0xFF; sniffBuf[13] = 0xFF; - LogTrace(sniffBuf, 14, 0, 0, NULL, TRUE); + LogTrace(sniffBuf, 14, 0, 0, NULL, true); sniffState = SNF_CARD_CMD; } // intentionally no break; case SNF_CARD_CMD:{ @@ -127,14 +127,14 @@ bool RAMFUNC MfSniffLogic(const uint8_t *data, uint16_t len, uint8_t *parity, ui } - return FALSE; + return false; } bool RAMFUNC MfSniffSend(uint16_t maxTimeoutMs) { if (BigBuf_get_traceLen() && (GetTickCount() > timerData + maxTimeoutMs)) { return intMfSniffSend(); } - return FALSE; + return false; } // internal sending function. not a RAMFUNC. @@ -162,5 +162,5 @@ bool intMfSniffSend() { clear_trace(); - return TRUE; + return true; } diff --git a/armsrc/printf.c b/armsrc/printf.c index 94ed809d..79c123df 100644 --- a/armsrc/printf.c +++ b/armsrc/printf.c @@ -175,6 +175,7 @@ reswitch: switch (ch = (u_char)*fmt++) { padc = '0'; goto reswitch; } + // intentionally fall through to next case case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': for (n = 0;; ++fmt) { diff --git a/client/cmdhf.c b/client/cmdhf.c index 17c31209..93906a7d 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -361,7 +361,7 @@ int CmdHFList(const char *Cmd) char param2 = '\0'; char param3 = '\0'; char type[40] = {0}; - char filename[FILE_PATH_SIZE]; + char filename[FILE_PATH_SIZE] = {0}; uint8_t protocol = 0; // parse command line diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 859dec2f..03ab0b5a 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -486,12 +486,12 @@ int CmdHF14AInfo(const char *Cmd) // try to see if card responses to "chinese magic backdoor" commands. - mfCIdentify(); + (void)mfCIdentify(); if (isMifareClassic) { switch(DetectClassicPrng()) { case 0: - PrintAndLog("Prng detection: HARDEND (hardnested)"); + PrintAndLog("Prng detection: HARDENED (hardnested)"); break; case 1: PrintAndLog("Prng detection: WEAK"); @@ -1032,12 +1032,9 @@ static command_t CommandTable[] = }; int CmdHF14A(const char *Cmd) { - // flush - WaitForResponseTimeout(CMD_ACK,NULL,100); - - // parse - CmdsParse(CommandTable, Cmd); - return 0; + (void)WaitForResponseTimeout(CMD_ACK,NULL,100); + CmdsParse(CommandTable, Cmd); + return 0; } int CmdHelp(const char *Cmd) diff --git a/client/cmdhfepa.c b/client/cmdhfepa.c index ac1f4268..76664bf5 100644 --- a/client/cmdhfepa.c +++ b/client/cmdhfepa.c @@ -202,10 +202,7 @@ int CmdHelp(const char *Cmd) int CmdHFEPA(const char *Cmd) { - // flush - WaitForResponseTimeout(CMD_ACK,NULL,100); - - // parse - CmdsParse(CommandTable, Cmd); - return 0; + (void)WaitForResponseTimeout(CMD_ACK,NULL,100); + CmdsParse(CommandTable, Cmd); + return 0; } diff --git a/client/cmdhflist.c b/client/cmdhflist.c index c87aa237..5538b6fb 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -559,8 +559,10 @@ bool NestedCheckKey(uint64_t key, TAuthData *ad, uint8_t *cmd, uint8_t cmdsize, uint32_t ar1 = crypto1_word(pcs, 0, 0) ^ ad->ar_enc; uint32_t at1 = crypto1_word(pcs, 0, 0) ^ ad->at_enc; - if (!(ar == ar1 && at == at1 && NTParityChk(ad, nt1))) + if (!(ar == ar1 && at == at1 && NTParityChk(ad, nt1))) { + crypto1_destroy(pcs); return false; + } memcpy(buf, cmd, cmdsize); mf_crypto1_decrypt(pcs, buf, cmdsize, 0); diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index b50d6013..e2a4ba1e 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -726,7 +726,6 @@ int CmdHF14AMfNested(const char *Cmd) blockNo = i * 4; keyType = j; num_to_bytes(e_sector[i].Key[j], 6, key); - keyFound = true; break; } @@ -737,6 +736,7 @@ int CmdHF14AMfNested(const char *Cmd) // Can't found a key.... if (!keyFound) { PrintAndLog("Can't found any of the known keys."); + free(e_sector); return 4; } PrintAndLog("--auto key. block no:%3d, key type:%c key:%s", blockNo, keyType?'B':'A', sprint_hex(key, 6)); @@ -1187,7 +1187,10 @@ int CmdHF14AMfChk(const char *Cmd) // initialize storage for found keys e_sector = calloc(SectorsCnt, sizeof(sector_t)); - if (e_sector == NULL) return 1; + if (e_sector == NULL) { + free(keyBlock); + return 1; + } for (uint8_t keyAB = 0; keyAB < 2; keyAB++) { for (uint16_t sectorNo = 0; sectorNo < SectorsCnt; sectorNo++) { e_sector[sectorNo].Key[keyAB] = 0xffffffffffff; @@ -2666,11 +2669,9 @@ static command_t CommandTable[] = int CmdHFMF(const char *Cmd) { - // flush - WaitForResponseTimeout(CMD_ACK,NULL,100); - - CmdsParse(CommandTable, Cmd); - return 0; + (void)WaitForResponseTimeout(CMD_ACK,NULL,100); + CmdsParse(CommandTable, Cmd); + return 0; } int CmdHelp(const char *Cmd) diff --git a/client/cmdhfmfhard.c b/client/cmdhfmfhard.c index be618d6e..4a379c71 100644 --- a/client/cmdhfmfhard.c +++ b/client/cmdhfmfhard.c @@ -276,7 +276,6 @@ static void init_bitflip_bitarrays(void) if (bytesread != filesize) { printf("File read error with %s. Aborting...\n", state_file_name); fclose(statesfile); - inflateEnd(&compressed_stream); exit(5); } fclose(statesfile); diff --git a/client/cmdhfmfu.c b/client/cmdhfmfu.c index e4115d71..1077ec99 100644 --- a/client/cmdhfmfu.c +++ b/client/cmdhfmfu.c @@ -1834,7 +1834,7 @@ static command_t CommandTable[] = }; int CmdHFMFUltra(const char *Cmd){ - WaitForResponseTimeout(CMD_ACK,NULL,100); + (void)WaitForResponseTimeout(CMD_ACK,NULL,100); CmdsParse(CommandTable, Cmd); return 0; } diff --git a/client/cmdhftopaz.c b/client/cmdhftopaz.c index b0b8a3d2..6058cabe 100644 --- a/client/cmdhftopaz.c +++ b/client/cmdhftopaz.c @@ -455,7 +455,7 @@ int CmdHFTopazReader(const char *Cmd) PrintAndLog("HR0 : %02x (%sa Topaz tag (%scapable of carrying a NDEF message), %s memory map)", rid_response[0], (rid_response[0] & 0xF0) == 0x10 ? "" : "not ", (rid_response[0] & 0xF0) == 0x10 ? "" : "not ", - (rid_response[0] & 0x0F) == 0x10 ? "static" : "dynamic"); + (rid_response[0] & 0x0F) == 0x01 ? "static" : "dynamic"); PrintAndLog("HR1 : %02x", rid_response[1]); status = topaz_rall(uid_echo, rall_response); @@ -554,10 +554,7 @@ static command_t CommandTable[] = int CmdHFTopaz(const char *Cmd) { - // flush - WaitForResponseTimeout(CMD_ACK,NULL,100); - - // parse + (void)WaitForResponseTimeout(CMD_ACK,NULL,100); CmdsParse(CommandTable, Cmd); return 0; } diff --git a/client/cmdlfhitag.c b/client/cmdlfhitag.c index 0fd8801b..8eef2359 100644 --- a/client/cmdlfhitag.c +++ b/client/cmdlfhitag.c @@ -66,6 +66,7 @@ int CmdLFHitagList(const char *Cmd) if (strlen(filename) > 0) { if ((pf = fopen(filename,"wb")) == NULL) { PrintAndLog("Error: Could not open file [%s]",filename); + free(got); return 1; } } @@ -166,11 +167,11 @@ int CmdLFHitagSim(const char *Cmd) { return 1; } tag_mem_supplied = true; - if (fread(c.d.asBytes,48,1,pf) == 0) { - PrintAndLog("Error: File reading error"); - fclose(pf); + if (fread(c.d.asBytes,1,48,pf) != 48) { + PrintAndLog("Error: File reading error"); + fclose(pf); return 1; - } + } fclose(pf); } else { tag_mem_supplied = false; @@ -289,7 +290,7 @@ int CmdLFHitagSimS(const char *Cmd) { return 1; } tag_mem_supplied = true; - if (fread(c.d.asBytes, 4*64, 1, pf) == 0) { + if (fread(c.d.asBytes, 1, 4*64, pf) != 4*64) { PrintAndLog("Error: File reading error"); fclose(pf); return 1; @@ -321,9 +322,9 @@ int CmdLFHitagCheckChallenges(const char *Cmd) { return 1; } file_given = true; - if (fread(c.d.asBytes,8*60,1,pf) == 0) { - PrintAndLog("Error: File reading error"); - fclose(pf); + if (fread(c.d.asBytes,1,8*60,pf) != 8*60) { + PrintAndLog("Error: File reading error"); + fclose(pf); return 1; } fclose(pf); diff --git a/client/cmdlfindala.c b/client/cmdlfindala.c index b30231c5..02c906d0 100644 --- a/client/cmdlfindala.c +++ b/client/cmdlfindala.c @@ -287,7 +287,7 @@ int CmdIndalaDemod(const char *Cmd) { } int CmdIndalaClone(const char *Cmd) { - UsbCommand c; + UsbCommand c = {0}; unsigned int uid1, uid2, uid3, uid4, uid5, uid6, uid7; uid1 = uid2 = uid3 = uid4 = uid5 = uid6 = uid7 = 0; diff --git a/client/emv/crypto_polarssl.c b/client/emv/crypto_polarssl.c index 3d11afe5..5a3db8ea 100644 --- a/client/emv/crypto_polarssl.c +++ b/client/emv/crypto_polarssl.c @@ -105,7 +105,7 @@ static struct crypto_pk *crypto_pk_polarssl_open_rsa(va_list vl) int res = rsa_check_pubkey(&cp->ctx); if(res != 0) { fprintf(stderr, "PolarSSL public key error res=%x exp=%d mod=%d.\n", res * -1, explen, modlen); - + free(cp); return NULL; } @@ -150,6 +150,7 @@ static struct crypto_pk *crypto_pk_polarssl_open_priv_rsa(va_list vl) int res = rsa_check_privkey(&cp->ctx); if(res != 0) { fprintf(stderr, "PolarSSL private key error res=%x exp=%d mod=%d.\n", res * -1, explen, modlen); + free(cp); return NULL; } @@ -184,6 +185,7 @@ static struct crypto_pk *crypto_pk_polarssl_genkey_rsa(va_list vl) int res = rsa_gen_key(&cp->ctx, &myrand, NULL, nbits, exp); if (res) { fprintf(stderr, "PolarSSL private key generation error res=%x exp=%d nbits=%d.\n", res * -1, exp, nbits); + free(cp); return NULL; } @@ -216,6 +218,7 @@ static unsigned char *crypto_pk_polarssl_encrypt(const struct crypto_pk *_cp, co res = rsa_public(&cp->ctx, buf, result); if(res) { printf("RSA encrypt failed. Error: %x data len: %zd key len: %zd\n", res * -1, len, keylen); + free(result); return NULL; } @@ -242,6 +245,7 @@ static unsigned char *crypto_pk_polarssl_decrypt(const struct crypto_pk *_cp, co res = rsa_private(&cp->ctx, buf, result); // CHECK??? if(res) { printf("RSA decrypt failed. Error: %x data len: %zd key len: %zd\n", res * -1, len, keylen); + free(result); return NULL; } diff --git a/client/flash.c b/client/flash.c index e43ebd1b..9a443cb8 100644 --- a/client/flash.c +++ b/client/flash.c @@ -40,7 +40,7 @@ static const uint8_t elf_ident[] = { // Turn PHDRs into flasher segments, checking for PHDR sanity and merging adjacent // unaligned segments if needed -static int build_segs_from_phdrs(flash_file_t *ctx, FILE *fd, Elf32_Phdr *phdrs, int num_phdrs) +static int build_segs_from_phdrs(flash_file_t *ctx, FILE *fd, Elf32_Phdr *phdrs, uint16_t num_phdrs) { Elf32_Phdr *phdr = phdrs; flash_seg_t *seg; @@ -191,7 +191,7 @@ int flash_load(flash_file_t *ctx, const char *name, bool can_write_bl) FILE *fd = NULL; Elf32_Ehdr ehdr; Elf32_Phdr *phdrs = NULL; - int num_phdrs; + uint16_t num_phdrs; int res; fd = fopen(name, "rb"); @@ -270,7 +270,7 @@ fail: // Get the state of the proxmark, backwards compatible static int get_proxmark_state(uint32_t *state) { - UsbCommand c; + UsbCommand c = {0}; c.cmd = CMD_DEVICE_INFO; SendCommand(&c); UsbCommand resp; diff --git a/client/fpga_compress.c b/client/fpga_compress.c index fc298a8b..bd1e8be2 100644 --- a/client/fpga_compress.c +++ b/client/fpga_compress.c @@ -108,7 +108,7 @@ int zlib_compress(FILE *infile[], uint8_t num_infiles, FILE *outfile, bool hardn for(uint16_t j = 0; j < num_infiles; j++) { for(uint16_t k = 0; k < FPGA_INTERLEAVE_SIZE; k++) { - c = fgetc(infile[j]); + c = (uint8_t)fgetc(infile[j]); if (!feof(infile[j])) { fpga_config[i++] = c; } else if (num_infiles > 1) { diff --git a/client/mifarehost.c b/client/mifarehost.c index 2d69cfae..9be04b4d 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -328,7 +328,7 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, struct Crypto1State *p1, *p2, *p3, *p4; // flush queue - WaitForResponseTimeout(CMD_ACK, NULL, 100); + (void)WaitForResponseTimeout(CMD_ACK,NULL,100); UsbCommand c = {CMD_MIFARE_NESTED, {blockNo + keyType * 0x100, trgBlockNo + trgKeyType * 0x100, calibrate}}; memcpy(c.d.asBytes, key, 6); @@ -910,6 +910,7 @@ int mfTraceDecode(uint8_t *data_src, int len, uint8_t parity, bool wantSaveToEml uint32_t nr1 = crypto1_word(pcs, nr_enc, 1) ^ nr_enc; uint32_t ar1 = crypto1_word(pcs, 0, 0) ^ ar_enc; uint32_t at1 = crypto1_word(pcs, 0, 0) ^ at_enc; + crypto1_destroy(pcs); printf("key> the same key test. nr1: %08x ar1: %08x at1: %08x \n", nr1, ar1, at1); if (NTParityCheck(nt1)) diff --git a/client/proxgui.cpp b/client/proxgui.cpp index e899174c..18749158 100644 --- a/client/proxgui.cpp +++ b/client/proxgui.cpp @@ -62,12 +62,9 @@ extern "C" void MainGraphics(void) extern "C" void InitGraphics(int argc, char **argv, char *script_cmds_file, char *script_cmd, bool usb_present) { #ifdef Q_WS_X11 - bool useGUI = getenv("DISPLAY") != 0; -#else - bool useGUI = true; -#endif - if (!useGUI) + if (getenv("DISPLAY") == NULL) return; +#endif main_loop_thread = new WorkerThread(script_cmds_file, script_cmd, usb_present); gui = new ProxGuiQT(argc, argv, main_loop_thread); diff --git a/client/proxguiqt.h b/client/proxguiqt.h index e43311ee..5f7199fc 100644 --- a/client/proxguiqt.h +++ b/client/proxguiqt.h @@ -110,7 +110,6 @@ class ProxGuiQT : public QObject ProxWidget *plotwidget; int argc; char **argv; - void (*main_func)(void); WorkerThread *proxmarkThread; public: diff --git a/client/scripting.c b/client/scripting.c index e73c7138..232da889 100644 --- a/client/scripting.c +++ b/client/scripting.c @@ -221,7 +221,7 @@ static int l_iso14443b_crc(lua_State *L) unsigned char buf[USB_CMD_DATA_SIZE]; size_t len = 0; const char *data = luaL_checklstring(L, 1, &len); - if (USB_CMD_DATA_SIZE < len) + if (len > USB_CMD_DATA_SIZE-2) len = USB_CMD_DATA_SIZE-2; for (int i = 0; i < len; i += 2) { diff --git a/client/util_posix.c b/client/util_posix.c index 32158ada..435e41f3 100644 --- a/client/util_posix.c +++ b/client/util_posix.c @@ -117,7 +117,7 @@ uint64_t msclock() { #include struct _timeb t; _ftime(&t); - return 1000 * t.time + t.millitm; + return 1000 * (uint64_t)t.time + t.millitm; // NORMAL CODE (use _ftime_s) //struct _timeb t; diff --git a/include/proxmark3.h b/include/proxmark3.h index 4a59636e..6fb6624f 100644 --- a/include/proxmark3.h +++ b/include/proxmark3.h @@ -61,9 +61,6 @@ #define SPI_FPGA_MODE 0 #define SPI_LCD_MODE 1 -#define TRUE 1 -#define FALSE 0 - //#define PACKED __attribute__((__packed__)) #define LED_A_ON() HIGH(GPIO_LED_A) diff --git a/tools/mfkey/mfkey32.c b/tools/mfkey/mfkey32.c index a5665ab2..d84305d0 100755 --- a/tools/mfkey/mfkey32.c +++ b/tools/mfkey/mfkey32.c @@ -10,7 +10,7 @@ // 32 bit recover key from 2 nonces int main (int argc, char *argv[]) { - nonces_t data; + nonces_t data = {0}; uint32_t ks2; // keystream used to encrypt reader response uint64_t key; // recovered key From fdee1ffa8419e8357913582f53e74218cae5b3d4 Mon Sep 17 00:00:00 2001 From: stuiterveer Date: Wed, 20 Jun 2018 06:34:00 +0200 Subject: [PATCH 231/310] Ignore DS_Store (#620) Prevent further commits containing DS_Store files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a7cc8803..85f92ea8 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ usb_cmd.lua version.c client/ui/ui_overlays.h *.Td +.DS_Store *.exe hardnested_stats.txt From 577b1c27f5baab911609412a5dc15a95b92504d9 Mon Sep 17 00:00:00 2001 From: Michael Farrell Date: Mon, 9 Jul 2018 15:49:03 +0800 Subject: [PATCH 232/310] Allow disabling unlink() with -DNO_UNLINK. (#625) --- client/comms.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/comms.c b/client/comms.c index be0cfd10..2030f8f3 100644 --- a/client/comms.c +++ b/client/comms.c @@ -12,7 +12,7 @@ #include "comms.h" #include -#ifdef __linux__ +#if defined(__linux__) && !defined(NO_UNLINK) #include // for unlink() #endif #include "uart.h" @@ -339,8 +339,10 @@ void CloseProxmark(void) { uart_close(sp); } -#ifdef __linux__ +#if defined(__linux__) && !defined(NO_UNLINK) // Fix for linux, it seems that it is extremely slow to release the serial port file descriptor /dev/* + // + // This may be disabled at compile-time with -DNO_UNLINK (used for a JNI-based serial port on Android). if (serial_port_name) { unlink(serial_port_name); } From b2e0ac5d3bd01ac9383d321efc483304cc7e0d6d Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Thu, 19 Jul 2018 18:01:18 +0200 Subject: [PATCH 233/310] fix: array index out of range in mfkeys.log (thanks to @TomHarkness) --- client/scripts/mfkeys.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/scripts/mfkeys.lua b/client/scripts/mfkeys.lua index 671ce03d..e8abd0b9 100644 --- a/client/scripts/mfkeys.lua +++ b/client/scripts/mfkeys.lua @@ -68,7 +68,7 @@ function checkBlock(blockNo, keys, keyType) data = data} local status = checkCommand(command) if status then return status, blockNo end - start = start+n+1 + start = start + n remaining = remaining - n end return nil From 00349d82044bae7fce7c0da8ed1a9449ef602c8a Mon Sep 17 00:00:00 2001 From: Fl0-0 Date: Thu, 26 Jul 2018 17:17:11 +0200 Subject: [PATCH 234/310] Fix Gcc 8 warnings (#632) --- client/util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/util.c b/client/util.c index 4f84e3b5..2be1e46b 100644 --- a/client/util.c +++ b/client/util.c @@ -226,7 +226,7 @@ char *sprint_hex_ascii(const uint8_t *data, const size_t len) { memset(buf, 0x00, 1024); size_t max_len = (len > 255) ? 255 : len; // max 255 bytes * 3 + 2 characters = 767 in buffer - sprintf(tmp, "%s| ", sprint_hex(data, max_len) ); + sprintf(tmp, "%.765s| ", sprint_hex(data, max_len) ); size_t i = 0; size_t pos = (max_len * 3)+2; From 913a54a86accab2a6959c4fcaf53711502f2d954 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Thu, 26 Jul 2018 17:18:10 +0200 Subject: [PATCH 235/310] save 2.3K flash-memory by avoiding a single floating point operation (#630) --- armsrc/util.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/armsrc/util.c b/armsrc/util.c index e25c6e0b..59dfe07b 100644 --- a/armsrc/util.c +++ b/armsrc/util.c @@ -489,13 +489,13 @@ void WaitTicks(uint32_t ticks){ // 1us = 1.5ticks. void WaitUS(uint16_t us){ if ( us == 0 ) return; - WaitTicks( (uint32_t)(us * 1.5) ); + WaitTicks( (uint32_t)us * 3 / 2 ) ; } void WaitMS(uint16_t ms){ if (ms == 0) return; - WaitTicks( (uint32_t)(ms * 1500) ); + WaitTicks( (uint32_t)ms * 1500 ); } From ab20cc35b7cef4e69a4e74f2b59d9e9b7fe2d022 Mon Sep 17 00:00:00 2001 From: Michael Farrell Date: Tue, 31 Jul 2018 03:33:06 +1000 Subject: [PATCH 236/310] Adds `lf hid pack` and `lf hid unpack` offline commands. (#614) * These are used to pack and unpack the bit length, facility code and card number from "short" HID Prox IDs. * This also simplifies some repeated code that is used to parse hex digits in lfhid, and adds basic client-side validation to the `lf hid clone` and `lf hid sim` commands, for when an ID that is too long is entered. * Implements parity checks/calculation for 26-bit H10301 cards. * Adds links to useful resources for HID Prox cards. * Adds an explicit warning about the lack of parities on non-26-bit cards. * Changes all the examples to use 26-bit IDs that have a parity bit set. --- client/cmdlfhid.c | 328 ++++++++++++++++++++++++++++++++++++++-------- client/cmdlfhid.h | 29 ++++ 2 files changed, 301 insertions(+), 56 deletions(-) diff --git a/client/cmdlfhid.c b/client/cmdlfhid.c index ca58d8ab..433a093d 100644 --- a/client/cmdlfhid.c +++ b/client/cmdlfhid.c @@ -6,6 +6,16 @@ // the license. //----------------------------------------------------------------------------- // Low frequency HID commands (known) +// +// Useful resources: +// RF interface, programming a T55x7 clone, 26-bit HID H10301 encoding: +// http://www.proxmark.org/files/Documents/125%20kHz%20-%20HID/HID_format_example.pdf +// +// "Understanding Card Data Formats" +// https://www.hidglobal.com/sites/default/files/hid-understanding_card_data_formats-wp-en.pdf +// +// "What Format Do You Need?" +// https://www.hidglobal.com/sites/default/files/resource_files/hid-prox-br-en.pdf //----------------------------------------------------------------------------- #include "cmdlfhid.h" @@ -18,9 +28,171 @@ #include "cmdparser.h" #include "cmddata.h" //for g_debugMode, demodbuff cmds #include "lfdemod.h" // for HIDdemodFSK +#include "parity.h" // for parity +#include "util.h" // for param_get8,32 static int CmdHelp(const char *Cmd); + +/** + * Packs a "short" (<38-bit) HID ID from component parts. + * + * This only works with 26, 34, 35 and 37 bit card IDs. + * + * NOTE: Parity calculation is only supported on 26-bit tags. Other card lengths + * may have invalid parity. + * + * Returns false on invalid inputs. + */ +bool pack_short_hid(/* out */ uint32_t *hi, /* out */ uint32_t *lo, /* in */ const short_hid_info *info) { + uint32_t high = 0, low = 0; + + switch (info->fmtLen) { + case 26: // HID H10301 + low |= (info->cardnum & 0xffff) << 1; + low |= (info->fc & 0xff) << 17; + + if (info->parityValid) { + // Calculate parity + low |= oddparity32((low >> 1) & 0xfff) & 1; + low |= (evenparity32((low >> 13) & 0xfff) & 1) << 25; + } + break; + + case 34: + low |= (info->cardnum & 0xffff) << 1; + low |= (info->fc & 0x7fff) << 17; + high |= (info->fc & 0x8000) >> 15; + // TODO: Calculate parity + break; + + case 35: + low |= (info->cardnum & 0xfffff) << 1; + low |= (info->fc & 0x7ff) << 21; + high |= (info->fc & 0x800) >> 11; + // TODO: Calculate parity + break; + + case 37: + low |= (info->cardnum & 0x7ffff) << 1; + low |= (info->fc & 0xfff) << 20; + high |= (info->fc & 0xf000) >> 12; + // TODO: Calculate parity + break; + + default: + // Invalid / unsupported length + return false; + } + + // Set the highest bit + if (info->fmtLen != 37) { + // Bit 37 is always set + high |= 0x20; + + // Set the bit corresponding to the length. + if (info->fmtLen < 32) { + low |= 1 << info->fmtLen; + } else { + high |= 1 << (info->fmtLen - 32); + } + } + + // Return result only if successful. + *hi = high; + *lo = low; + return true; +} + + +/** + * Unpacks a "short" (<38-bit) HID ID into its component parts. + * + * This only works with 26, 34, 35 and 37 bit card IDs. + * + * NOTE: Parity checking is only supported on 26-bit tags. + * + * Returns false on invalid inputs. + */ +bool unpack_short_hid(short_hid_info *out, uint32_t hi, uint32_t lo) { + memset(out, 0, sizeof(short_hid_info)); + + if (((hi >> 5) & 1) == 1) { + // if bit 38 is set then < 37 bit format is used + uint32_t lo2 = 0; + // get bits 21-37 to check for format len bit + lo2 = (((hi & 31) << 12) | (lo >> 20)); + uint8_t idx3 = 1; + // find last bit set to 1 (format len bit) + while (lo2 > 1) { + lo2 = lo2 >> 1; + idx3++; + } + + out->fmtLen = idx3 + 19; + + switch (out->fmtLen) { + case 26: // HID H10301 + out->cardnum = (lo >> 1) & 0xFFFF; + out->fc = (lo >> 17) & 0xFF; + + if (g_debugMode) { + PrintAndLog("oddparity : input=%x, calculated=%d, provided=%d", + (lo >> 1) & 0xFFF, oddparity32((lo >> 1) & 0xFFF), lo & 1); + PrintAndLog("evenparity: input=%x, calculated=%d, provided=%d", + (lo >> 13) & 0xFFF, evenparity32((lo >> 13) & 0xFFF) & 1, (lo >> 25) & 1); + } + + out->parityValid = + (oddparity32((lo >> 1) & 0xFFF) == (lo & 1)) && + ((evenparity32((lo >> 13) & 0xFFF) & 1) == ((lo >> 25) & 1)); + break; + + case 34: + out->cardnum = (lo >> 1) & 0xFFFF; + out->fc = ((hi & 1) << 15) | (lo >> 17); + // TODO: Calculate parity + break; + + case 35: + out->cardnum = (lo >> 1) & 0xFFFFF; + out->fc = ((hi & 1) << 11) | (lo >> 21); + // TODO: Calculate parity + break; + + default: + return false; + } + } else { + // If bit 38 is not set, then 37 bit format is used + out->fmtLen = 37; + out->cardnum = (lo >> 1) & 0x7FFFF; + out->fc = ((hi & 0xF) << 12) | (lo >> 20); + // TODO: Calculate parity + } + return true; +} + + +/** + * Converts a hex string to component "hi" and "lo" 32-bit integers, one nibble + * at a time. + * + * Returns the number of nibbles (4 bits) entered. + */ +int hexstring_to_int64(/* out */ uint32_t* hi, /* out */ uint32_t* lo, const char* str) { + // TODO: Replace this with param_gethex when it supports arbitrary length + // inputs. + int n = 0, i = 0; + + while (sscanf(&str[i++], "%1x", &n ) == 1) { + *hi = (*hi << 4) | (*lo >> 28); + *lo = (*lo << 4) | (n & 0xf); + } + + return i - 1; +} + //by marshmellow (based on existing demod + holiman's refactor) //HID Prox demod - FSK RF/50 with preamble of 00011101 (then manchester encoded) //print full HID Prox ID and some bit format details if found @@ -60,45 +232,19 @@ int CmdFSKdemodHID(const char *Cmd) (unsigned int) hi2, (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF); } else { //standard HID tags <38 bits - uint8_t fmtLen = 0; - uint32_t fc = 0; - uint32_t cardnum = 0; - if (((hi>>5)&1)==1){//if bit 38 is set then < 37 bit format is used - uint32_t lo2=0; - lo2=(((hi & 31) << 12) | (lo>>20)); //get bits 21-37 to check for format len bit - uint8_t idx3 = 1; - while(lo2>1){ //find last bit set to 1 (format len bit) - lo2=lo2>>1; - idx3++; - } - fmtLen =idx3+19; - fc =0; - cardnum=0; - if(fmtLen==26){ - cardnum = (lo>>1)&0xFFFF; - fc = (lo>>17)&0xFF; - } - if(fmtLen==34){ - cardnum = (lo>>1)&0xFFFF; - fc= ((hi&1)<<15)|(lo>>17); - } - if(fmtLen==35){ - cardnum = (lo>>1)&0xFFFFF; - fc = ((hi&1)<<11)|(lo>>21); - } - } - else { //if bit 38 is not set then 37 bit format is used - fmtLen = 37; - fc = 0; - cardnum = 0; - if(fmtLen == 37){ - cardnum = (lo>>1)&0x7FFFF; - fc = ((hi&0xF)<<12)|(lo>>20); - } - } - PrintAndLog("HID Prox TAG ID: %x%08x (%d) - Format Len: %dbit - FC: %d - Card: %d", + short_hid_info card_info; + bool ret = unpack_short_hid(&card_info, (uint32_t)hi, (uint32_t)lo); + PrintAndLog("HID Prox TAG ID: %x%08x (%d) - Format Len: %u bits - FC: %u - Card: %u", (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF, - (unsigned int) fmtLen, (unsigned int) fc, (unsigned int) cardnum); + card_info.fmtLen, card_info.fc, card_info.cardnum); + + if (card_info.fmtLen == 26) { + PrintAndLog("Parity: %s", card_info.parityValid ? "valid" : "invalid"); + } + + if (!ret) { + PrintAndLog("Invalid or unsupported tag length."); + } } setDemodBuf(BitStream,BitLen,idx); setClockGrid(50, waveIdx + (idx*50)); @@ -112,7 +258,7 @@ int CmdFSKdemodHID(const char *Cmd) int CmdHIDReadFSK(const char *Cmd) { int findone=0; - if(Cmd[0]=='1') findone=1; + if(Cmd[0]=='1') findone=1; UsbCommand c={CMD_HID_DEMOD_FSK}; c.arg[0]=findone; SendCommand(&c); @@ -121,30 +267,30 @@ int CmdHIDReadFSK(const char *Cmd) int CmdHIDSim(const char *Cmd) { - uint32_t hi = 0, lo = 0; - int n = 0, i = 0; + uint32_t hi = 0, lo = 0; + hexstring_to_int64(&hi, &lo, Cmd); + if (hi >= 0x40) { + PrintAndLog("This looks like a long tag ID. Use 'lf simfsk' for long tags. Aborting!"); + return 0; + } - while (sscanf(&Cmd[i++], "%1x", &n ) == 1) { - hi = (hi << 4) | (lo >> 28); - lo = (lo << 4) | (n & 0xf); - } + PrintAndLog("Emulating tag with ID %x%08x", hi, lo); + PrintAndLog("Press pm3-button to abort simulation"); - PrintAndLog("Emulating tag with ID %x%08x", hi, lo); - PrintAndLog("Press pm3-button to abort simulation"); - - UsbCommand c = {CMD_HID_SIM_TAG, {hi, lo, 0}}; - SendCommand(&c); - return 0; + UsbCommand c = {CMD_HID_SIM_TAG, {hi, lo, 0}}; + SendCommand(&c); + return 0; } int CmdHIDClone(const char *Cmd) { unsigned int hi2 = 0, hi = 0, lo = 0; - int n = 0, i = 0; UsbCommand c; if (strchr(Cmd,'l') != 0) { - while (sscanf(&Cmd[i++], "%1x", &n ) == 1) { + int n = 0, i = 0; + + while (sscanf(&Cmd[i++], "%1x", &n ) == 1) { hi2 = (hi2 << 4) | (hi >> 28); hi = (hi << 4) | (lo >> 28); lo = (lo << 4) | (n & 0xf); @@ -155,9 +301,10 @@ int CmdHIDClone(const char *Cmd) c.d.asBytes[0] = 1; } else { - while (sscanf(&Cmd[i++], "%1x", &n ) == 1) { - hi = (hi << 4) | (lo >> 28); - lo = (lo << 4) | (n & 0xf); + hexstring_to_int64(&hi, &lo, Cmd); + if (hi >= 0x40) { + PrintAndLog("This looks like a long tag ID. Aborting!"); + return 0; } PrintAndLog("Cloning tag with ID %x%08x", hi, lo); @@ -175,6 +322,73 @@ int CmdHIDClone(const char *Cmd) return 0; } + +int CmdHIDPack(const char *Cmd) { + uint32_t hi = 0, lo = 0; + short_hid_info card_info; + + if (strlen(Cmd)<3) { + PrintAndLog("Usage: lf hid pack "); + PrintAndLog(" sample: lf hid pack 26 123 4567"); + return 0; + } + + card_info.fmtLen = param_get8(Cmd, 0); + card_info.fc = param_get32ex(Cmd, 1, 0, 10); + card_info.cardnum = param_get32ex(Cmd, 2, 0, 10); + card_info.parityValid = true; + + // TODO + if (card_info.fmtLen != 26) { + PrintAndLog("Warning: Parity bits are only calculated for 26 bit IDs -- this may be invalid!"); + } + + bool ret = pack_short_hid(&hi, &lo, &card_info); + + if (ret) { + PrintAndLog("HID Prox TAG ID: %x%08x (%d) - Format Len: %u bits - FC: %u - Card: %u", + (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF, + card_info.fmtLen, card_info.fc, card_info.cardnum); + } else { + PrintAndLog("Invalid or unsupported tag length."); + } + return 0; +} + + +int CmdHIDUnpack(const char *Cmd) +{ + uint32_t hi = 0, lo = 0; + if (strlen(Cmd)<1) { + PrintAndLog("Usage: lf hid unpack "); + PrintAndLog(" sample: lf hid unpack 2006f623ae"); + return 0; + } + + hexstring_to_int64(&hi, &lo, Cmd); + if (hi >= 0x40) { + PrintAndLog("This looks like a long tag ID. Aborting!"); + return 0; + } + + short_hid_info card_info; + bool ret = unpack_short_hid(&card_info, hi, lo); + + PrintAndLog("HID Prox TAG ID: %x%08x (%d) - Format Len: %u bits - FC: %u - Card: %u", + (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF, + card_info.fmtLen, card_info.fc, card_info.cardnum); + + if (card_info.fmtLen == 26) { + PrintAndLog("Parity: %s", card_info.parityValid ? "valid" : "invalid"); + } + + if (!ret) { + PrintAndLog("Invalid or unsupported tag length."); + } + return 0; +} + + static command_t CommandTable[] = { {"help", CmdHelp, 1, "This help"}, @@ -182,6 +396,8 @@ static command_t CommandTable[] = {"read", CmdHIDReadFSK, 0, "['1'] Realtime HID FSK Read from antenna (option '1' for one tag only)"}, {"sim", CmdHIDSim, 0, " -- HID tag simulator"}, {"clone", CmdHIDClone, 0, " ['l'] -- Clone HID to T55x7 (tag must be in antenna)(option 'l' for 84bit ID)"}, + {"pack", CmdHIDPack, 1, " -- packs a <38 bit (short) HID ID from its length, facility code and card number"}, + {"unpack", CmdHIDUnpack, 1, " -- unpacks a <38 bit (short) HID ID to its length, facility code and card number"}, {NULL, NULL, 0, NULL} }; diff --git a/client/cmdlfhid.h b/client/cmdlfhid.h index b03f7041..e53a95c6 100644 --- a/client/cmdlfhid.h +++ b/client/cmdlfhid.h @@ -11,10 +11,39 @@ #ifndef CMDLFHID_H__ #define CMDLFHID_H__ +#include +#include + +// Structure for unpacked "short" (<38 bits) HID Prox tags. +typedef struct { + // Format length, in bits. + uint8_t fmtLen; + + // Facility code. + uint32_t fc; + + // Card number. + uint32_t cardnum; + + // Parity validity. + // + // When used with pack_short_hid, this determines if we should calculate + // parity values for the ID. + // + // When used with unpack_short_hid, this indicates if we got valid parity + // values for the ID. + bool parityValid; +} short_hid_info; + +bool pack_short_hid(/* out */ uint32_t *hi, /* out */ uint32_t *lo, /* in */ const short_hid_info *info); +bool unpack_short_hid(short_hid_info* out, uint32_t hi, uint32_t lo); + int CmdLFHID(const char *Cmd); int CmdFSKdemodHID(const char *Cmd); int CmdHIDReadDemod(const char *Cmd); int CmdHIDSim(const char *Cmd); int CmdHIDClone(const char *Cmd); +int CmdHIDPack(const char *Cmd); +int CmdHIDUnpack(const char *Cmd); #endif From b6a04aff48853b786aa0be1256bb8b01b35d8bbc Mon Sep 17 00:00:00 2001 From: grauerfuchs <42082416+grauerfuchs@users.noreply.github.com> Date: Sat, 4 Aug 2018 06:26:36 -0400 Subject: [PATCH 237/310] fix truncation issue on "lf t55 config" modulation Fix for issue #637 --- client/cmdlft55xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/cmdlft55xx.c b/client/cmdlft55xx.c index f53ff0d6..2a096cd0 100644 --- a/client/cmdlft55xx.c +++ b/client/cmdlft55xx.c @@ -207,7 +207,7 @@ void printT5xxHeader(uint8_t page){ int CmdT55xxSetConfig(const char *Cmd) { uint8_t offset = 0; - char modulation[5] = {0x00}; + char modulation[6] = {0x00}; char tmp = 0x00; uint8_t bitRate = 0; uint8_t rates[9] = {8,16,32,40,50,64,100,128,0}; From 5125e4263c36063f49d3b410b78da293b705b7d2 Mon Sep 17 00:00:00 2001 From: marshmellow42 Date: Sun, 5 Aug 2018 12:13:44 -0400 Subject: [PATCH 238/310] allow common makefile options-defines (#635) --- armsrc/Makefile | 13 ++++-- client/Makefile | 58 +++++++++++++++----------- common/Makefile_Enabled_Options.common | 40 ++++++++++++++++++ 3 files changed, 82 insertions(+), 29 deletions(-) create mode 100644 common/Makefile_Enabled_Options.common diff --git a/armsrc/Makefile b/armsrc/Makefile index dea5d06c..3a7293e5 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -10,10 +10,16 @@ APP_INCLUDES = apps.h #remove one of the following defines and comment out the relevant line #in the next section to remove that particular feature from compilation -APP_CFLAGS = -DWITH_ISO14443a_StandAlone -DWITH_LF -DWITH_ISO15693 -DWITH_ISO14443a -DWITH_ISO14443b -DWITH_ICLASS -DWITH_LEGICRF -DWITH_HITAG -DWITH_CRC -DON_DEVICE -DWITH_HFSNOOP \ - -fno-strict-aliasing -ffunction-sections -fdata-sections -#-DWITH_LCD +APP_CFLAGS = -DON_DEVICE \ + -fno-strict-aliasing -ffunction-sections -fdata-sections +include ../common/Makefile_Enabled_Options.common + +ifneq (,$(findstring LCD,$(APP_CFLAGS))) + SRC_LCD = fonts.c LCD.c +else + SRC_LCD = +endif #SRC_LCD = fonts.c LCD.c SRC_LF = lfops.c hitag2.c hitagS.c lfsampling.c pcf7931.c lfdemod.c protocols.c SRC_ISO15693 = iso15693.c iso15693tools.c @@ -21,7 +27,6 @@ SRC_ISO14443a = epa.c iso14443a.c mifareutil.c mifarecmd.c mifaresniff.c mifares SRC_ISO14443b = iso14443b.c SRC_CRAPTO1 = crypto1.c des.c SRC_CRC = iso14443crc.c crc.c crc16.c crc32.c parity.c - #the FPGA bitstream files. Note: order matters! FPGA_BITSTREAMS = fpga_lf.bit fpga_hf.bit diff --git a/client/Makefile b/client/Makefile index 917dc767..2d256b72 100644 --- a/client/Makefile +++ b/client/Makefile @@ -23,6 +23,10 @@ LDFLAGS = $(ENV_LDFLAGS) CFLAGS = $(ENV_CFLAGS) -std=c99 -D_ISOC99_SOURCE -I. -I../include -I../common -I../common/polarssl -I../zlib -I../uart -I/opt/local/include -I../liblua -Wall -g -O3 CXXFLAGS = -I../include -Wall -O3 +APP_CFLAGS = +include ../common/Makefile_Enabled_Options.common +CFLAGS += $(APP_CFLAGS) + LUAPLATFORM = generic platform = $(shell uname) ifneq (,$(findstring MINGW,$(platform))) @@ -37,32 +41,34 @@ else endif endif -# Check for correctly configured Qt5 -QTINCLUDES = $(shell pkg-config --cflags Qt5Core Qt5Widgets 2>/dev/null) -QTLDLIBS = $(shell pkg-config --libs Qt5Core Qt5Widgets 2>/dev/null) -MOC = $(shell pkg-config --variable=host_bins Qt5Core)/moc -UIC = $(shell pkg-config --variable=host_bins Qt5Core)/uic -ifeq ($(QTINCLUDES), ) -# if Qt5 not found check for correctly configured Qt4 - QTINCLUDES = $(shell pkg-config --cflags QtCore QtGui 2>/dev/null) - QTLDLIBS = $(shell pkg-config --libs QtCore QtGui 2>/dev/null) - MOC = $(shell pkg-config --variable=moc_location QtCore) - UIC = $(shell pkg-config --variable=uic_location QtCore) -else - CXXFLAGS += -std=c++11 -fPIC -endif -ifeq ($(QTINCLUDES), ) -# if both pkg-config commands failed, search in common places - ifneq ($(QTDIR), ) - QTINCLUDES = -I$(QTDIR)/include -I$(QTDIR)/include/QtCore -I$(QTDIR)/include/QtGui - QTLDLIBS = -L$(QTDIR)/lib -lQtCore4 -lQtGui4 - ifneq ($(wildcard $(QTDIR)/include/QtWidgets),) - QTINCLUDES += -I$(QTDIR)/include/QtWidgets - QTLDLIBS = -L$(QTDIR)/lib -lQt5Widgets -lQt5Gui -lQt5Core - CXXFLAGS += -std=c++11 -fPIC +ifneq (,$(findstring WITH_GUI,$(APP_CFLAGS))) + # Check for correctly configured Qt5 + QTINCLUDES = $(shell pkg-config --cflags Qt5Core Qt5Widgets 2>/dev/null) + QTLDLIBS = $(shell pkg-config --libs Qt5Core Qt5Widgets 2>/dev/null) + MOC = $(shell pkg-config --variable=host_bins Qt5Core)/moc + UIC = $(shell pkg-config --variable=host_bins Qt5Core)/uic + ifeq ($(QTINCLUDES), ) + # if Qt5 not found check for correctly configured Qt4 + QTINCLUDES = $(shell pkg-config --cflags QtCore QtGui 2>/dev/null) + QTLDLIBS = $(shell pkg-config --libs QtCore QtGui 2>/dev/null) + MOC = $(shell pkg-config --variable=moc_location QtCore) + UIC = $(shell pkg-config --variable=uic_location QtCore) + else + CXXFLAGS += -std=c++11 -fPIC + endif + ifeq ($(QTINCLUDES), ) + # if both pkg-config commands failed, search in common places + ifneq ($(QTDIR), ) + QTINCLUDES = -I$(QTDIR)/include -I$(QTDIR)/include/QtCore -I$(QTDIR)/include/QtGui + QTLDLIBS = -L$(QTDIR)/lib -lQtCore4 -lQtGui4 + ifneq ($(wildcard $(QTDIR)/include/QtWidgets),) + QTINCLUDES += -I$(QTDIR)/include/QtWidgets + QTLDLIBS = -L$(QTDIR)/lib -lQt5Widgets -lQt5Gui -lQt5Core + CXXFLAGS += -std=c++11 -fPIC + endif + MOC = $(QTDIR)/bin/moc + UIC = $(QTDIR)/bin/uic endif - MOC = $(QTDIR)/bin/moc - UIC = $(QTDIR)/bin/uic endif endif @@ -79,6 +85,7 @@ DEPFLAGS = -MT $@ -MMD -MP -MF $(OBJDIR)/$*.Td # make temporary to final dependeny files after successful compilation POSTCOMPILE = $(MV) -f $(OBJDIR)/$*.Td $(OBJDIR)/$*.d + CORESRCS = uart_posix.c \ uart_win32.c \ util.c \ @@ -303,6 +310,7 @@ DEPENDENCY_FILES = $(patsubst %.c, $(OBJDIR)/%.d, $(CORESRCS) $(CMDSRCS) $(ZLIBS $(patsubst %.cpp, $(OBJDIR)/%.d, $(QTGUISRCS)) \ $(OBJDIR)/proxmark3.d $(OBJDIR)/flash.d $(OBJDIR)/flasher.d $(OBJDIR)/fpga_compress.d + $(DEPENDENCY_FILES): ; .PRECIOUS: $(DEPENDENCY_FILES) diff --git a/common/Makefile_Enabled_Options.common b/common/Makefile_Enabled_Options.common new file mode 100644 index 00000000..acb413bf --- /dev/null +++ b/common/Makefile_Enabled_Options.common @@ -0,0 +1,40 @@ +#NOTES: +# Do not put any comments inside the definition below (before the final flag) +# All definition lines except the last must end in a \ +# +#BEGIN +APP_CFLAGS += -DWITH_ISO14443a_StandAlone \ + -DWITH_ISO15693 \ + -DWITH_ISO14443a \ + -DWITH_ISO14443b \ + -DWITH_ICLASS \ + -DWITH_LEGICRF \ + -DWITH_HITAG \ + -DWITH_CRC \ + -DWITH_HFSNOOP \ + -DWITH_GUI +#END + + +### Standalone modes: +#-DWITH_ISO14443a_StandAlone +#-DWITH_LF +# +# if both WITH_LF and WITH_ISO4443a_StandAlone are defined +# ISO14443a_StandAlone will be the only one that runs +# You must remove it and define WITH_LF for LF standalone mode + +### Other options: +#-DWITH_ISO15693 \ Include ISO15693 support in build +#-DWITH_ISO14443a \ include ISO14443a support in build +#-DWITH_ISO14443b \ include ISO14443b support in build +#-DWITH_ICLASS \ include ICLASS support in build +#-DWITH_LEGICRF \ include LEGIC support in build +#-DWITH_HITAG \ include HITAG support in build +#-DWITH_CRC \ include CRC support in build +#-DWITH_HFSNOOP \ include HFSNOOP support in build +#-DWITH_SMARTCARD \ include SMARTCARD support in build +#-DWITH_GUI \ include QT GUI/Graph support in build +#-DWITH_LCD \ include LCD support in build (experimental?) + +#marshmellow NOTE: tested GUI, and SMARTCARD removal only... From 472345daee388f8d6786ead2f04b4ae0ff0462d4 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Sun, 5 Aug 2018 18:15:03 +0200 Subject: [PATCH 239/310] mod hw version: (#631) * create fpga version info at compile time (by additional functionality in fpgacompress) * remove hw version caching (prepare USB reconnect) * fix calculation of available compressed bytes in fpga_loader.c --- armsrc/Makefile | 11 +++ armsrc/appmain.c | 12 ++- armsrc/fpgaloader.c | 101 +++----------------- armsrc/fpgaloader.h | 12 ++- bootrom/Makefile | 1 + client/cmdhw.c | 14 +-- client/fpga_compress.c | 204 +++++++++++++++++++++++++++++++++++++---- common/Makefile.common | 7 +- common/fpga.h | 18 ++++ 9 files changed, 249 insertions(+), 131 deletions(-) create mode 100644 common/fpga.h diff --git a/armsrc/Makefile b/armsrc/Makefile index 3a7293e5..f0a0c0ff 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -64,6 +64,9 @@ ARMSRC = fpgaloader.c \ optimized_cipher.c \ hfsnoop.c +VERSIONSRC = version.c \ + fpga_version_info.c + # Do not move this inclusion before the definition of {THUMB,ASM,ARM}SRC include ../common/Makefile.common @@ -74,6 +77,14 @@ all: $(OBJS) .DELETE_ON_ERROR: +# version.c should be remade on every compilation +.PHONY: version.c +version.c: default_version.c + perl ../tools/mkversion.pl .. > $@ || $(COPY) $^ $@ + +fpga_version_info.c: $(FPGA_BITSTREAMS) $(FPGA_COMPRESSOR) + $(FPGA_COMPRESSOR) -v $(filter %.bit,$^) $@ + $(OBJDIR)/fpga_all.o: $(OBJDIR)/fpga_all.bit.z $(OBJCOPY) -O elf32-littlearm -I binary -B arm --prefix-sections=fpga_all_bit $^ $@ diff --git a/armsrc/appmain.c b/armsrc/appmain.c index e8581216..eabe9fbe 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -16,6 +16,7 @@ #include "cmd.h" #include "proxmark3.h" #include "apps.h" +#include "fpga.h" #include "util.h" #include "printf.h" #include "string.h" @@ -286,6 +287,7 @@ void ReadMem(int addr) extern struct version_information version_information; /* bootrom version information is pointed to from _bootphase1_version_pointer */ extern char *_bootphase1_version_pointer, _flash_start, _flash_end, _bootrom_start, _bootrom_end, __data_src_start__; + void SendVersion(void) { char temp[USB_CMD_DATA_SIZE]; /* Limited data payload in USB packets */ @@ -306,10 +308,12 @@ void SendVersion(void) FormatVersionInformation(temp, sizeof(temp), "os: ", &version_information); strncat(VersionString, temp, sizeof(VersionString) - strlen(VersionString) - 1); - FpgaGatherVersion(FPGA_BITSTREAM_LF, temp, sizeof(temp)); - strncat(VersionString, temp, sizeof(VersionString) - strlen(VersionString) - 1); - FpgaGatherVersion(FPGA_BITSTREAM_HF, temp, sizeof(temp)); - strncat(VersionString, temp, sizeof(VersionString) - strlen(VersionString) - 1); + for (int i = 0; i < fpga_bitstream_num; i++) { + strncat(VersionString, fpga_version_information[i], sizeof(VersionString) - strlen(VersionString) - 1); + if (i < fpga_bitstream_num - 1) { + strncat(VersionString, "\n", sizeof(VersionString) - strlen(VersionString) - 1); + } + } // Send Chip ID and used flash memory uint32_t text_and_rodata_section_size = (uint32_t)&__data_src_start__ - (uint32_t)&_flash_start; diff --git a/armsrc/fpgaloader.c b/armsrc/fpgaloader.c index c0b04f3c..77223bd0 100644 --- a/armsrc/fpgaloader.c +++ b/armsrc/fpgaloader.c @@ -10,20 +10,21 @@ // mode once it is configured. //----------------------------------------------------------------------------- +#include "fpgaloader.h" + #include #include #include -#include "fpgaloader.h" +#include "apps.h" +#include "fpga.h" #include "proxmark3.h" #include "util.h" #include "string.h" #include "BigBuf.h" #include "zlib.h" -extern void Dbprintf(const char *fmt, ...); - // remember which version of the bitstream we have already downloaded to the FPGA -static int downloaded_bitstream = FPGA_BITSTREAM_ERR; +static int downloaded_bitstream = 0; // this is where the bitstreams are located in memory: extern uint8_t _binary_obj_fpga_all_bit_z_start, _binary_obj_fpga_all_bit_z_end; @@ -31,10 +32,7 @@ extern uint8_t _binary_obj_fpga_all_bit_z_start, _binary_obj_fpga_all_bit_z_end; static uint8_t *fpga_image_ptr = NULL; static uint32_t uncompressed_bytes_cnt; -static const uint8_t _bitparse_fixed_header[] = {0x00, 0x09, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x00, 0x00, 0x01}; -#define FPGA_BITSTREAM_FIXED_HEADER_SIZE sizeof(_bitparse_fixed_header) #define OUTPUT_BUFFER_LEN 80 -#define FPGA_INTERLEAVE_SIZE 288 //----------------------------------------------------------------------------- // Set up the Serial Peripheral Interface as master @@ -201,7 +199,7 @@ static int get_from_fpga_combined_stream(z_streamp compressed_fpga_stream, uint8 //---------------------------------------------------------------------------- static int get_from_fpga_stream(int bitstream_version, z_streamp compressed_fpga_stream, uint8_t *output_buffer) { - while((uncompressed_bytes_cnt / FPGA_INTERLEAVE_SIZE) % FPGA_BITSTREAM_MAX != (bitstream_version - 1)) { + while((uncompressed_bytes_cnt / FPGA_INTERLEAVE_SIZE) % fpga_bitstream_num != (bitstream_version - 1)) { // skip undesired data belonging to other bitstream_versions get_from_fpga_combined_stream(compressed_fpga_stream, output_buffer); } @@ -234,7 +232,7 @@ static bool reset_fpga_stream(int bitstream_version, z_streamp compressed_fpga_s // initialize z_stream structure for inflate: compressed_fpga_stream->next_in = &_binary_obj_fpga_all_bit_z_start; - compressed_fpga_stream->avail_in = &_binary_obj_fpga_all_bit_z_start - &_binary_obj_fpga_all_bit_z_end; + compressed_fpga_stream->avail_in = &_binary_obj_fpga_all_bit_z_end - &_binary_obj_fpga_all_bit_z_start; compressed_fpga_stream->next_out = output_buffer; compressed_fpga_stream->avail_out = OUTPUT_BUFFER_LEN; compressed_fpga_stream->zalloc = &fpga_inflate_malloc; @@ -248,8 +246,8 @@ static bool reset_fpga_stream(int bitstream_version, z_streamp compressed_fpga_s header[i] = get_from_fpga_stream(bitstream_version, compressed_fpga_stream, output_buffer); } - // Check for a valid .bit file (starts with _bitparse_fixed_header) - if(memcmp(_bitparse_fixed_header, header, FPGA_BITSTREAM_FIXED_HEADER_SIZE) == 0) { + // Check for a valid .bit file (starts with bitparse_fixed_header) + if(memcmp(bitparse_fixed_header, header, FPGA_BITSTREAM_FIXED_HEADER_SIZE) == 0) { return true; } else { return false; @@ -427,7 +425,7 @@ void FpgaDownloadAndGo(int bitstream_version) } unsigned int bitstream_length; - if(bitparse_find_section(bitstream_version, 'e', &bitstream_length, &compressed_fpga_stream, output_buffer)) { + if (bitparse_find_section(bitstream_version, 'e', &bitstream_length, &compressed_fpga_stream, output_buffer)) { DownloadFPGA(bitstream_version, bitstream_length, &compressed_fpga_stream, output_buffer); downloaded_bitstream = bitstream_version; } @@ -442,77 +440,6 @@ void FpgaDownloadAndGo(int bitstream_version) } -//----------------------------------------------------------------------------- -// Gather version information from FPGA image. Needs to decompress the begin -// of the respective (HF or LF) image. -// Note: decompression makes use of (i.e. overwrites) BigBuf[]. It is therefore -// advisable to call this only once and store the results for later use. -//----------------------------------------------------------------------------- -void FpgaGatherVersion(int bitstream_version, char *dst, int len) -{ - unsigned int fpga_info_len; - char tempstr[40] = {0x00}; - z_stream compressed_fpga_stream; - uint8_t output_buffer[OUTPUT_BUFFER_LEN] = {0x00}; - - dst[0] = '\0'; - - // ensure that we can allocate enough memory for decompression: - BigBuf_free(); BigBuf_Clear_ext(false); - - if (!reset_fpga_stream(bitstream_version, &compressed_fpga_stream, output_buffer)) - return; - - if(bitparse_find_section(bitstream_version, 'a', &fpga_info_len, &compressed_fpga_stream, output_buffer)) { - for (uint16_t i = 0; i < fpga_info_len; i++) { - char c = (char)get_from_fpga_stream(bitstream_version, &compressed_fpga_stream, output_buffer); - if (i < sizeof(tempstr)) { - tempstr[i] = c; - } - } - if (!memcmp("fpga_lf", tempstr, 7)) - strncat(dst, "LF ", len-1); - else if (!memcmp("fpga_hf", tempstr, 7)) - strncat(dst, "HF ", len-1); - } - strncat(dst, "FPGA image built", len-1); - if(bitparse_find_section(bitstream_version, 'b', &fpga_info_len, &compressed_fpga_stream, output_buffer)) { - strncat(dst, " for ", len-1); - for (uint16_t i = 0; i < fpga_info_len; i++) { - char c = (char)get_from_fpga_stream(bitstream_version, &compressed_fpga_stream, output_buffer); - if (i < sizeof(tempstr)) { - tempstr[i] = c; - } - } - strncat(dst, tempstr, len-1); - } - if(bitparse_find_section(bitstream_version, 'c', &fpga_info_len, &compressed_fpga_stream, output_buffer)) { - strncat(dst, " on ", len-1); - for (uint16_t i = 0; i < fpga_info_len; i++) { - char c = (char)get_from_fpga_stream(bitstream_version, &compressed_fpga_stream, output_buffer); - if (i < sizeof(tempstr)) { - tempstr[i] = c; - } - } - strncat(dst, tempstr, len-1); - } - if(bitparse_find_section(bitstream_version, 'd', &fpga_info_len, &compressed_fpga_stream, output_buffer)) { - strncat(dst, " at ", len-1); - for (uint16_t i = 0; i < fpga_info_len; i++) { - char c = (char)get_from_fpga_stream(bitstream_version, &compressed_fpga_stream, output_buffer); - if (i < sizeof(tempstr)) { - tempstr[i] = c; - } - } - strncat(dst, tempstr, len-1); - } - - strncat(dst, "\n", len-1); - - inflateEnd(&compressed_fpga_stream); -} - - //----------------------------------------------------------------------------- // Send a 16 bit command/data pair to the FPGA. // The bit format is: C3 C2 C1 C0 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0 @@ -562,12 +489,8 @@ void SetAdcMuxFor(uint32_t whichGpio) } void Fpga_print_status(void) { - Dbprintf("Fgpa"); - switch(downloaded_bitstream) { - case FPGA_BITSTREAM_HF: Dbprintf(" mode....................HF"); break; - case FPGA_BITSTREAM_LF: Dbprintf(" mode....................LF"); break; - default: Dbprintf(" mode....................%d", downloaded_bitstream); break; - } + Dbprintf("Currently loaded FPGA image:"); + Dbprintf(" %s", fpga_version_information[downloaded_bitstream-1]); } int FpgaGetCurrent() { diff --git a/armsrc/fpgaloader.h b/armsrc/fpgaloader.h index 7dfc5c12..fa16771d 100644 --- a/armsrc/fpgaloader.h +++ b/armsrc/fpgaloader.h @@ -10,10 +10,15 @@ // mode once it is configured. //----------------------------------------------------------------------------- +#ifndef __FPGALOADER_H +#define __FPGALOADER_H + +#include +#include + void FpgaSendCommand(uint16_t cmd, uint16_t v); void FpgaWriteConfWord(uint8_t v); void FpgaDownloadAndGo(int bitstream_version); -void FpgaGatherVersion(int bitstream_version, char *dst, int len); void FpgaSetupSsc(void); void SetupSpi(int mode); bool FpgaSetupSscDma(uint8_t *buf, int len); @@ -24,12 +29,9 @@ int FpgaGetCurrent(); void SetAdcMuxFor(uint32_t whichGpio); // definitions for multiple FPGA config files support -#define FPGA_BITSTREAM_MAX 2 // the total number of FPGA bitstreams (configs) -#define FPGA_BITSTREAM_ERR 0 #define FPGA_BITSTREAM_LF 1 #define FPGA_BITSTREAM_HF 2 - // Definitions for the FPGA commands. #define FPGA_CMD_SET_CONFREG (1<<12) #define FPGA_CMD_SET_DIVISOR (2<<12) @@ -72,3 +74,5 @@ void SetAdcMuxFor(uint32_t whichGpio); #define FPGA_HF_ISO14443A_TAGSIM_MOD (2<<0) #define FPGA_HF_ISO14443A_READER_LISTEN (3<<0) #define FPGA_HF_ISO14443A_READER_MOD (4<<0) + +#endif diff --git a/bootrom/Makefile b/bootrom/Makefile index 92373995..59c22aa2 100644 --- a/bootrom/Makefile +++ b/bootrom/Makefile @@ -10,6 +10,7 @@ ARMSRC = THUMBSRC = cmd.c usb_cdc.c bootrom.c ASMSRC = ram-reset.s flash-reset.s +VERSIONSRC = ## There is a strange bug with the linker: Sometimes it will not emit the glue to call ## BootROM from ARM mode. The symbol is emitted, but the section will be filled with diff --git a/client/cmdhw.c b/client/cmdhw.c index bdab01eb..f994e938 100644 --- a/client/cmdhw.c +++ b/client/cmdhw.c @@ -408,21 +408,13 @@ int CmdVersion(const char *Cmd) clearCommandBuffer(); UsbCommand c = {CMD_VERSION}; - static UsbCommand resp = {0, {0, 0, 0}}; + UsbCommand resp = {0, {0, 0, 0}}; - if (resp.arg[0] == 0 && resp.arg[1] == 0) { // no cached information available - SendCommand(&c); - if (WaitForResponseTimeout(CMD_ACK,&resp,1000)) { - PrintAndLog("Prox/RFID mark3 RFID instrument"); - PrintAndLog((char*)resp.d.asBytes); - lookupChipID(resp.arg[0], resp.arg[1]); - } - } else { - PrintAndLog("[[[ Cached information ]]]\n"); + SendCommand(&c); + if (WaitForResponseTimeout(CMD_ACK,&resp,1000)) { PrintAndLog("Prox/RFID mark3 RFID instrument"); PrintAndLog((char*)resp.d.asBytes); lookupChipID(resp.arg[0], resp.arg[1]); - PrintAndLog(""); } return 0; } diff --git a/client/fpga_compress.c b/client/fpga_compress.c index bd1e8be2..418a02b8 100644 --- a/client/fpga_compress.c +++ b/client/fpga_compress.c @@ -1,4 +1,6 @@ //----------------------------------------------------------------------------- +// piwi, 2017, 2018 +// // 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. @@ -11,9 +13,11 @@ #include #include +#include #include #include #include +#include "fpga.h" #include "zlib.h" #define MAX(a,b) ((a)>(b)?(a):(b)) @@ -37,18 +41,18 @@ #define COMPRESS_MAX_NICE_LENGTH 258 #define COMPRESS_MAX_CHAIN 8192 -#define FPGA_INTERLEAVE_SIZE 288 // (the FPGA's internal config frame size is 288 bits. Interleaving with 288 bytes should give best compression) -#define FPGA_CONFIG_SIZE 42336L // our current fpga_[lh]f.bit files are 42175 bytes. Rounded up to next multiple of FPGA_INTERLEAVE_SIZE #define HARDNESTED_TABLE_SIZE (sizeof(uint32_t) * ((1L<<19)+1)) static void usage(void) { fprintf(stdout, "Usage: fpga_compress ... \n"); fprintf(stdout, " Combine n FPGA bitstream files and compress them into one.\n\n"); - fprintf(stdout, " fpga_compress -d "); - fprintf(stdout, " Decompress . Write result to "); - fprintf(stdout, " fpga_compress -t "); - fprintf(stdout, " Compress hardnested table . Write result to "); + fprintf(stdout, " fpga_compress -v ... \n"); + fprintf(stdout, " Extract Version Information from FPGA bitstream files and write it to \n\n"); + fprintf(stdout, " fpga_compress -d \n"); + fprintf(stdout, " Decompress . Write result to \n\n"); + fprintf(stdout, " fpga_compress -t \n"); + fprintf(stdout, " Compress hardnested table . Write result to \n\n"); } @@ -60,7 +64,7 @@ static voidpf fpga_deflate_malloc(voidpf opaque, uInt items, uInt size) static void fpga_deflate_free(voidpf opaque, voidpf address) { - return free(address); + free(address); } @@ -252,9 +256,162 @@ int zlib_decompress(FILE *infile, FILE *outfile) } +/* Simple Xilinx .bit parser. The file starts with the fixed opaque byte sequence + * 00 09 0f f0 0f f0 0f f0 0f f0 00 00 01 + * After that the format is 1 byte section type (ASCII character), 2 byte length + * (big endian), bytes content. Except for section 'e' which has 4 bytes + * length. + */ +static int bitparse_find_section(FILE *infile, char section_name, unsigned int *section_length) +{ + int result = 0; + #define MAX_FPGA_BIT_STREAM_HEADER_SEARCH 100 // maximum number of bytes to search for the requested section + uint16_t numbytes = 0; + while(numbytes < MAX_FPGA_BIT_STREAM_HEADER_SEARCH) { + char current_name = (char)fgetc(infile); + numbytes++; + if(current_name < 'a' || current_name > 'e') { + /* Strange section name, abort */ + break; + } + unsigned int current_length = 0; + switch(current_name) { + case 'e': + /* Four byte length field */ + current_length += fgetc(infile) << 24; + current_length += fgetc(infile) << 16; + numbytes += 2; + default: /* Fall through, two byte length field */ + current_length += fgetc(infile) << 8; + current_length += fgetc(infile) << 0; + numbytes += 2; + } + + if(current_name != 'e' && current_length > 255) { + /* Maybe a parse error */ + break; + } + + if(current_name == section_name) { + /* Found it */ + *section_length = current_length; + result = 1; + break; + } + + for (uint16_t i = 0; i < current_length && numbytes < MAX_FPGA_BIT_STREAM_HEADER_SEARCH; i++) { + (void)fgetc(infile); + numbytes++; + } + } + + return result; +} + + +static int FpgaGatherVersion(FILE *infile, char* infile_name, char *dst, int len) +{ + unsigned int fpga_info_len; + char tempstr[40] = {0x00}; + + dst[0] = '\0'; + + for (uint16_t i = 0; i < FPGA_BITSTREAM_FIXED_HEADER_SIZE; i++) { + if (fgetc(infile) != bitparse_fixed_header[i]) { + fprintf(stderr, "Invalid FPGA file. Aborting...\n\n"); + return(EXIT_FAILURE); + } + } + + strncat(dst, basename(infile_name), len-1); + // if (bitparse_find_section(infile, 'a', &fpga_info_len)) { + // for (uint16_t i = 0; i < fpga_info_len; i++) { + // char c = (char)fgetc(infile); + // if (i < sizeof(tempstr)) { + // tempstr[i] = c; + // } + // } + // strncat(dst, tempstr, len-1); + // } + strncat(dst, " built", len-1); + if (bitparse_find_section(infile, 'b', &fpga_info_len)) { + strncat(dst, " for ", len-1); + for (uint16_t i = 0; i < fpga_info_len; i++) { + char c = (char)fgetc(infile); + if (i < sizeof(tempstr)) { + tempstr[i] = c; + } + } + strncat(dst, tempstr, len-1); + } + if (bitparse_find_section(infile, 'c', &fpga_info_len)) { + strncat(dst, " on ", len-1); + for (uint16_t i = 0; i < fpga_info_len; i++) { + char c = (char)fgetc(infile); + if (i < sizeof(tempstr)) { + tempstr[i] = c; + } + } + strncat(dst, tempstr, len-1); + } + if (bitparse_find_section(infile, 'd', &fpga_info_len)) { + strncat(dst, " at ", len-1); + for (uint16_t i = 0; i < fpga_info_len; i++) { + char c = (char)fgetc(infile); + if (i < sizeof(tempstr)) { + tempstr[i] = c; + } + } + strncat(dst, tempstr, len-1); + } + return 0; +} + + +static void print_version_info_preamble(FILE *outfile, int num_infiles) { + fprintf(outfile, "//-----------------------------------------------------------------------------\n"); + fprintf(outfile, "// piwi, 2018\n"); + fprintf(outfile, "//\n"); + fprintf(outfile, "// This code is licensed to you under the terms of the GNU GPL, version 2 or,\n"); + fprintf(outfile, "// at your option, any later version. See the LICENSE.txt file for the text of\n"); + fprintf(outfile, "// the license.\n"); + fprintf(outfile, "//-----------------------------------------------------------------------------\n"); + fprintf(outfile, "// Version information on fpga images\n"); + fprintf(outfile, "//\n"); + fprintf(outfile, "// This file is generated by fpga_compress. Don't edit!\n"); + fprintf(outfile, "//-----------------------------------------------------------------------------\n"); + fprintf(outfile, "\n"); + fprintf(outfile, "\n"); + fprintf(outfile, "const int fpga_bitstream_num = %d;\n", num_infiles); + fprintf(outfile, "const char* const fpga_version_information[%d] = {\n", num_infiles); +} + + +static int generate_fpga_version_info(FILE *infile[], char *infile_names[], int num_infiles, FILE *outfile) { + + char version_string[80] = ""; + + print_version_info_preamble(outfile, num_infiles); + + for (int i = 0; i < num_infiles; i++) { + FpgaGatherVersion(infile[i], infile_names[i], version_string, sizeof(version_string)); + fprintf(outfile, "\t\"%s\"", version_string); + if (i != num_infiles-1) { + fprintf(outfile, ","); + } + fprintf(outfile,"\n"); + } + + fprintf(outfile, "};\n"); + + return 0; +} + + int main(int argc, char **argv) { FILE **infiles; + char **infile_names; FILE *outfile; if (argc == 1 || argc == 2) { @@ -271,43 +428,56 @@ int main(int argc, char **argv) } infiles[0] = fopen(argv[2], "rb"); if (infiles[0] == NULL) { - fprintf(stderr, "Error. Cannot open input file %s", argv[2]); + fprintf(stderr, "Error. Cannot open input file %s\n\n", argv[2]); return(EXIT_FAILURE); } outfile = fopen(argv[3], "wb"); if (outfile == NULL) { - fprintf(stderr, "Error. Cannot open output file %s", argv[3]); + fprintf(stderr, "Error. Cannot open output file %s\n\n", argv[3]); return(EXIT_FAILURE); } return zlib_decompress(infiles[0], outfile); - } else { // Compress + } else { // Compress or gemerate version info bool hardnested_mode = false; + bool generate_version_file = false; int num_input_files = 0; - if (!strcmp(argv[1], "-t")) { // hardnested table + if (!strcmp(argv[1], "-t")) { // compress one hardnested table if (argc != 4) { usage(); return(EXIT_FAILURE); } hardnested_mode = true; num_input_files = 1; - } else { + } else if (!strcmp(argv[1], "-v")) { // generate version info + generate_version_file = true; + num_input_files = argc-3; + } else { // compress 1..n fpga files num_input_files = argc-2; } + infiles = calloc(num_input_files, sizeof(FILE*)); - for (uint16_t i = 0; i < num_input_files; i++) { - infiles[i] = fopen(argv[i+(hardnested_mode?2:1)], "rb"); + infile_names = calloc(num_input_files, sizeof(char*)); + for (uint16_t i = 0; i < num_input_files; i++) { + infile_names[i] = argv[i+((hardnested_mode || generate_version_file)?2:1)]; + infiles[i] = fopen(infile_names[i], "rb"); if (infiles[i] == NULL) { - fprintf(stderr, "Error. Cannot open input file %s", argv[i+(hardnested_mode?2:1)]); + fprintf(stderr, "Error. Cannot open input file %s\n\n", infile_names[i]); return(EXIT_FAILURE); } } outfile = fopen(argv[argc-1], "wb"); if (outfile == NULL) { - fprintf(stderr, "Error. Cannot open output file %s", argv[argc-1]); + fprintf(stderr, "Error. Cannot open output file %s\n\n", argv[argc-1]); return(EXIT_FAILURE); } - return zlib_compress(infiles, num_input_files, outfile, hardnested_mode); + if (generate_version_file) { + if (generate_fpga_version_info(infiles, infile_names, num_input_files, outfile)) { + return(EXIT_FAILURE); + } + } else { + return zlib_compress(infiles, num_input_files, outfile, hardnested_mode); + } } } diff --git a/common/Makefile.common b/common/Makefile.common index f31ff7bb..0ab89b3d 100644 --- a/common/Makefile.common +++ b/common/Makefile.common @@ -75,7 +75,7 @@ LIBS = -lgcc THUMBOBJ = $(patsubst %.c,$(OBJDIR)/%.o,$(notdir $(THUMBSRC))) ARMOBJ = $(patsubst %.c,$(OBJDIR)/%.o,$(notdir $(ARMSRC))) ASMOBJ = $(patsubst %.s,$(OBJDIR)/%.o,$(notdir $(ASMSRC))) -VERSIONOBJ = $(OBJDIR)/version.o +VERSIONOBJ = $(patsubst %.c,$(OBJDIR)/%.o,$(notdir $(VERSIONSRC))) $(THUMBOBJ): $(OBJDIR)/%.o: %.c $(INCLUDES) $(CC) $(CFLAGS) -mthumb -mthumb-interwork -o $@ $< @@ -99,11 +99,6 @@ OBJCOPY_TRANSLATIONS = --no-change-warnings \ $(OBJDIR)/%.s19: $(OBJDIR)/%.elf $(OBJCOPY) -Osrec --srec-forceS3 --strip-debug $(OBJCOPY_TRANSLATIONS) $^ $@ -# version.c should be remade on every compilation -.PHONY: version.c -version.c: default_version.c - perl ../tools/mkversion.pl .. > $@ || $(COPY) $^ $@ - # Automatic dependency generation DEPENDENCY_FILES = $(patsubst %.c,$(OBJDIR)/%.d,$(notdir $(THUMBSRC))) \ $(patsubst %.c,$(OBJDIR)/%.d,$(notdir $(ARMSRC))) \ diff --git a/common/fpga.h b/common/fpga.h new file mode 100644 index 00000000..b99a7593 --- /dev/null +++ b/common/fpga.h @@ -0,0 +1,18 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- + +#ifndef __FPGA_H +#define __FPGA_H + +#define FPGA_BITSTREAM_FIXED_HEADER_SIZE sizeof(bitparse_fixed_header) +#define FPGA_INTERLEAVE_SIZE 288 +#define FPGA_CONFIG_SIZE 42336L // our current fpga_[lh]f.bit files are 42175 bytes. Rounded up to next multiple of FPGA_INTERLEAVE_SIZE + +static const uint8_t bitparse_fixed_header[] = {0x00, 0x09, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x00, 0x00, 0x01}; +extern const int fpga_bitstream_num; +extern const char* const fpga_version_information[]; + +#endif From a2937cea8db9a2ef51becff18d3b0227b837b576 Mon Sep 17 00:00:00 2001 From: marshmellow42 Date: Mon, 6 Aug 2018 07:27:37 -0400 Subject: [PATCH 240/310] Separate WITH_LF and WITH_LF_StandAlone --- armsrc/appmain.c | 6 +++--- common/Makefile_Enabled_Options.common | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index eabe9fbe..27f43b3f 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -367,7 +367,7 @@ void SendStatus(void) cmd_send(CMD_ACK,1,0,0,0,0); } -#if defined(WITH_ISO14443a_StandAlone) || defined(WITH_LF) +#if defined(WITH_ISO14443a_StandAlone) || defined(WITH_LF_StandAlone) #define OPTS 2 @@ -635,7 +635,7 @@ void StandAloneMode14a() } } } -#elif WITH_LF +#elif WITH_LF_StandAlone // samy's sniff and repeat routine void SamyRun() { @@ -1428,7 +1428,7 @@ void __attribute__((noreturn)) AppMain(void) } WDT_HIT(); -#ifdef WITH_LF +#ifdef WITH_LF_StandAlone #ifndef WITH_ISO14443a_StandAlone if (BUTTON_HELD(1000) > 0) SamyRun(); diff --git a/common/Makefile_Enabled_Options.common b/common/Makefile_Enabled_Options.common index acb413bf..462ae56a 100644 --- a/common/Makefile_Enabled_Options.common +++ b/common/Makefile_Enabled_Options.common @@ -4,6 +4,7 @@ # #BEGIN APP_CFLAGS += -DWITH_ISO14443a_StandAlone \ + -DWITH_LF\ -DWITH_ISO15693 \ -DWITH_ISO14443a \ -DWITH_ISO14443b \ @@ -18,7 +19,7 @@ APP_CFLAGS += -DWITH_ISO14443a_StandAlone \ ### Standalone modes: #-DWITH_ISO14443a_StandAlone -#-DWITH_LF +#-DWITH_LF_StandAlone # # if both WITH_LF and WITH_ISO4443a_StandAlone are defined # ISO14443a_StandAlone will be the only one that runs From dfdca20c6cdbac73c620754572d49f1def70ff1f Mon Sep 17 00:00:00 2001 From: marshmellow42 Date: Mon, 6 Aug 2018 07:39:39 -0400 Subject: [PATCH 241/310] fix option comments to be more clear --- common/Makefile_Enabled_Options.common | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/common/Makefile_Enabled_Options.common b/common/Makefile_Enabled_Options.common index 462ae56a..8774946e 100644 --- a/common/Makefile_Enabled_Options.common +++ b/common/Makefile_Enabled_Options.common @@ -21,12 +21,13 @@ APP_CFLAGS += -DWITH_ISO14443a_StandAlone \ #-DWITH_ISO14443a_StandAlone #-DWITH_LF_StandAlone # -# if both WITH_LF and WITH_ISO4443a_StandAlone are defined +# if both WITH_LF_StandAlone and WITH_ISO4443a_StandAlone are defined # ISO14443a_StandAlone will be the only one that runs -# You must remove it and define WITH_LF for LF standalone mode +# You must remove it and define WITH_LF_StandAlone for LF standalone mode ### Other options: -#-DWITH_ISO15693 \ Include ISO15693 support in build +#-DWITH_LF \ include LF support in build +#-DWITH_ISO15693 \ include ISO15693 support in build #-DWITH_ISO14443a \ include ISO14443a support in build #-DWITH_ISO14443b \ include ISO14443b support in build #-DWITH_ICLASS \ include ICLASS support in build From 6a0915eac23a40d8ca0bdcaa5203164a7fb16e0e Mon Sep 17 00:00:00 2001 From: Fl0-0 Date: Tue, 7 Aug 2018 09:23:11 +0200 Subject: [PATCH 242/310] Bootrom version fix + .gitignore (#645) * Fix bootrom Makefile * Add armsrc/fpga_version_info.c to .gitignore --- .gitignore | 1 + bootrom/Makefile | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 85f92ea8..c551f7a2 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ !client/hardnested/tables/*.z usb_cmd.lua version.c +armsrc/fpga_version_info.c client/ui/ui_overlays.h *.Td .DS_Store diff --git a/bootrom/Makefile b/bootrom/Makefile index 59c22aa2..dd1e7e08 100644 --- a/bootrom/Makefile +++ b/bootrom/Makefile @@ -10,7 +10,7 @@ ARMSRC = THUMBSRC = cmd.c usb_cdc.c bootrom.c ASMSRC = ram-reset.s flash-reset.s -VERSIONSRC = +VERSIONSRC = version.c ## There is a strange bug with the linker: Sometimes it will not emit the glue to call ## BootROM from ARM mode. The symbol is emitted, but the section will be filled with @@ -22,6 +22,11 @@ VERSIONSRC = # stdint.h provided locally until GCC 4.5 becomes C99 compliant APP_CFLAGS = -I. +# version.c should be remade on every compilation +.PHONY: version.c +version.c: default_version.c + perl ../tools/mkversion.pl .. > $@ || $(COPY) $^ $@ + # Do not move this inclusion before the definition of {THUMB,ASM,ARM}SRC include ../common/Makefile.common From 8ff31e935d19bc53dd8f27877b7450cafba4a942 Mon Sep 17 00:00:00 2001 From: AntiCat Date: Tue, 14 Aug 2018 09:24:02 +0200 Subject: [PATCH 243/310] fix: 32bit tick timer based on TC0 and TC1 (#653) TC1 counts the number of TC0 overflows (carry bits). In random conditions TC1 would return or stay at zero, instead of counting up. This due to the behavior of the reset signal. SAM7S Series Datasheet, 33.5.6 Trigger: Regardless of the trigger used, it will be taken into account at the following active edge of the selected clock. This means that the counter value can be read differently from zero just after a trigger, especially when a low frequency signal is selected as the clock. The new code first prepares TC1 and asserts TC1 trigger and then prepares TC0 and asserts TC0 trigger. The TC0 start-up will reset TC1. --- armsrc/util.c | 69 ++++++++++++++++++++++++++++++++------------------- armsrc/util.h | 3 ++- 2 files changed, 46 insertions(+), 26 deletions(-) diff --git a/armsrc/util.c b/armsrc/util.c index 59dfe07b..8a8d6657 100644 --- a/armsrc/util.c +++ b/armsrc/util.c @@ -448,31 +448,52 @@ uint32_t RAMFUNC GetCountSspClk(){ // ------------------------------------------------------------------------- -// Timer for bitbanging, or LF stuff when you need a very precis timer +// Timer for bitbanging, or LF stuff when you need a very precis timer // 1us = 1.5ticks // ------------------------------------------------------------------------- void StartTicks(void){ - //initialization of the timer - // tc1 is higher 0xFFFF0000 - // tc0 is lower 0x0000FFFF + // initialization of the timer AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1); AT91C_BASE_TCB->TCB_BMR = AT91C_TCB_TC0XC0S_NONE | AT91C_TCB_TC1XC1S_TIOA0 | AT91C_TCB_TC2XC2S_NONE; - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; - AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK | // MCK(48MHz) / 32 - AT91C_TC_WAVE | AT91C_TC_WAVESEL_UP_AUTO | AT91C_TC_ACPA_CLEAR | - AT91C_TC_ACPC_SET | AT91C_TC_ASWTRG_SET; - AT91C_BASE_TC0->TC_RA = 1; - AT91C_BASE_TC0->TC_RC = 0; - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; // timer disable - AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_XC1; // from TC0 - - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - AT91C_BASE_TCB->TCB_BCR = 1; - - // wait until timer becomes zero. - while (AT91C_BASE_TC1->TC_CV > 0); + // disable TC0 and TC1 for re-configuration + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + + // first configure TC1 (higher, 0xFFFF0000) 16 bit counter + AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_XC1; // just connect to TIOA0 from TC0 + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // re-enable timer and wait for TC0 + + // second configure TC0 (lower, 0x0000FFFF) 16 bit counter + AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK | // MCK(48MHz) / 32 + AT91C_TC_WAVE | AT91C_TC_WAVESEL_UP_AUTO | + AT91C_TC_ACPA_CLEAR | // RA comperator clears TIOA (carry bit) + AT91C_TC_ACPC_SET | // RC comperator sets TIOA (carry bit) + AT91C_TC_ASWTRG_SET; // SWTriger sets TIOA (carry bit) + AT91C_BASE_TC0->TC_RC = 0; // set TIOA (carry bit) on overflow, return to zero + AT91C_BASE_TC0->TC_RA = 1; // clear carry bit on next clock cycle + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // reset and re-enable timer + + // synchronized startup procedure + while (AT91C_BASE_TC0->TC_CV > 0); // wait until TC0 returned to zero + while (AT91C_BASE_TC0->TC_CV < 2); // and has started (TC_CV > TC_RA, now TC1 is cleared) + + // return to zero + AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; + AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; + while (AT91C_BASE_TC0->TC_CV > 0); +} + + +uint32_t GetTicks(void) { + uint32_t hi, lo; + + do { + hi = AT91C_BASE_TC1->TC_CV; + lo = AT91C_BASE_TC0->TC_CV; + } while(hi != AT91C_BASE_TC1->TC_CV); + + return (hi << 16) | lo; } @@ -480,30 +501,28 @@ void StartTicks(void){ // if called with a high number, this will trigger the WDT... void WaitTicks(uint32_t ticks){ if ( ticks == 0 ) return; - ticks += GET_TICKS; - while (GET_TICKS < ticks); + ticks += GetTicks(); + while (GetTicks() < ticks); } // Wait / Spindelay in us (microseconds) // 1us = 1.5ticks. void WaitUS(uint16_t us){ - if ( us == 0 ) return; WaitTicks( (uint32_t)us * 3 / 2 ) ; } void WaitMS(uint16_t ms){ - if (ms == 0) return; WaitTicks( (uint32_t)ms * 1500 ); } // Starts Clock and waits until its reset void ResetTicks(void){ - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - while (AT91C_BASE_TC1->TC_CV > 0); + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + while (AT91C_BASE_TC0->TC_CV > 0); } diff --git a/armsrc/util.h b/armsrc/util.h index 0148e1dd..58dcdd52 100644 --- a/armsrc/util.h +++ b/armsrc/util.h @@ -44,7 +44,7 @@ void FormatVersionInformation(char *dst, int len, const char *prefix, void *vers //iceman's ticks.h #ifndef GET_TICKS -# define GET_TICKS (uint32_t)((AT91C_BASE_TC1->TC_CV << 16) | AT91C_BASE_TC0->TC_CV) +# define GET_TICKS GetTicks() #endif void SpinDelay(int ms); @@ -62,6 +62,7 @@ void ResetSspClk(void); uint32_t RAMFUNC GetCountSspClk(); extern void StartTicks(void); +extern uint32_t GetTicks(void); extern void WaitTicks(uint32_t ticks); extern void WaitUS(uint16_t us); extern void WaitMS(uint16_t ms); From c80eb8ba79b86a157f9769203b7ddc26960d2dc4 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Tue, 14 Aug 2018 11:15:51 +0300 Subject: [PATCH 244/310] some cards need to have Le=0x00, some need to not have... (#651) --- client/emv/emvcore.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/client/emv/emvcore.c b/client/emv/emvcore.c index 08c6e3db..36f6f8eb 100644 --- a/client/emv/emvcore.c +++ b/client/emv/emvcore.c @@ -221,7 +221,7 @@ struct tlvdb *GetdCVVRawFromTrack2(const struct tlv *track2) { return tlvdb_fixed(0x02, dCVVlen, dCVV); } -int EMVExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { +int EMVExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool IncludeLe, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { uint8_t data[APDU_RES_LEN] = {0}; *ResultLen = 0; @@ -237,10 +237,10 @@ int EMVExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, uint8_t *Re memcpy(&data[5], apdu.data, apdu.Lc); if (APDULogging) - PrintAndLog(">>>> %s", sprint_hex(data, 6 + apdu.Lc)); + PrintAndLog(">>>> %s", sprint_hex(data, (IncludeLe?6:5) + apdu.Lc)); - // 6 byes + data = INS + CLA + P1 + P2 + Lc + + Le - int res = ExchangeAPDU14a(data, 6 + apdu.Lc, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); + // 6 byes + data = INS + CLA + P1 + P2 + Lc + + Le(?IncludeLe) + int res = ExchangeAPDU14a(data, (IncludeLe?6:5) + apdu.Lc, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); if (res) { return res; @@ -270,11 +270,11 @@ int EMVExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, uint8_t *Re } int EMVExchange(bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { - return EMVExchangeEx(false, LeaveFieldON, apdu, Result, MaxResultLen, ResultLen, sw, tlv); + return EMVExchangeEx(false, LeaveFieldON, apdu, true, Result, MaxResultLen, ResultLen, sw, tlv); } int EMVSelect(bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { - return EMVExchangeEx(ActivateField, LeaveFieldON, (sAPDU){0x00, 0xa4, 0x04, 0x00, AIDLen, AID}, Result, MaxResultLen, ResultLen, sw, tlv); + return EMVExchangeEx(ActivateField, LeaveFieldON, (sAPDU){0x00, 0xa4, 0x04, 0x00, AIDLen, AID}, true, Result, MaxResultLen, ResultLen, sw, tlv); } int EMVSelectPSE(bool ActivateField, bool LeaveFieldON, uint8_t PSENum, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { @@ -453,7 +453,12 @@ int EMVGPO(bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, si } int EMVReadRecord(bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { - return EMVExchange(LeaveFieldON, (sAPDU){0x00, 0xb2, SFIrec, (SFI << 3) | 0x04, 0, NULL}, Result, MaxResultLen, ResultLen, sw, tlv); + int res = EMVExchange(LeaveFieldON, (sAPDU){0x00, 0xb2, SFIrec, (SFI << 3) | 0x04, 0, NULL}, Result, MaxResultLen, ResultLen, sw, tlv); + if (*sw == 0x6700) { + PrintAndLog(">>> trying to reissue command withouth Le..."); + res = EMVExchangeEx(false, LeaveFieldON, (sAPDU){0x00, 0xb2, SFIrec, (SFI << 3) | 0x04, 0, NULL}, false, Result, MaxResultLen, ResultLen, sw, tlv); + } + return res; } int EMVAC(bool LeaveFieldON, uint8_t RefControl, uint8_t *CDOL, size_t CDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { @@ -461,7 +466,12 @@ int EMVAC(bool LeaveFieldON, uint8_t RefControl, uint8_t *CDOL, size_t CDOLLen, } int EMVGenerateChallenge(bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { - return EMVExchange(LeaveFieldON, (sAPDU){0x00, 0x84, 0x00, 0x00, 0x00, NULL}, Result, MaxResultLen, ResultLen, sw, tlv); + int res = EMVExchange(LeaveFieldON, (sAPDU){0x00, 0x84, 0x00, 0x00, 0x00, NULL}, Result, MaxResultLen, ResultLen, sw, tlv); + if (*sw == 0x6700) { + PrintAndLog(">>> trying to reissue command withouth Le..."); + res = EMVExchangeEx(false, LeaveFieldON, (sAPDU){0x00, 0x84, 0x00, 0x00, 0x00, NULL}, false, Result, MaxResultLen, ResultLen, sw, tlv); + } + return res; } int EMVInternalAuthenticate(bool LeaveFieldON, uint8_t *DDOL, size_t DDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { From 315e18e66cea20bd426be9b05337f53c9055e0c7 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Wed, 15 Aug 2018 14:03:20 +0200 Subject: [PATCH 245/310] ISO15693 device side improvements (#652) * ISO15693 device side improvements * increase accuracy by doubling the sample frequency (hi_read_rx_xcorr.v) * adjust armsrc/iso15693.c and client/cmdhf15.c accordingly * use more accurate approximation for sqrt(ci^2 + cq^2) * improve EOF detection (was often mistaken for Logic0, resulting in "error, uneven octet! (extra bits!)") * hi_read_r_xcorr.v: avoid overflows during accumulation and truncation * explicitely cast unsigned ADC samples to signed --- armsrc/iso15693.c | 126 +++++++++++++++++++-------------------- client/cmdhf15.c | 49 ++++++++------- fpga/fpga_hf.bit | Bin 42175 -> 42175 bytes fpga/hi_read_rx_xcorr.v | 129 ++++++++++++++++++++++++++-------------- 4 files changed, 174 insertions(+), 130 deletions(-) diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index 1f0b8193..ad6f5cfc 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -81,7 +81,10 @@ #define AddCrc(data,datalen) Iso15693AddCrc(data,datalen) #define sprintUID(target,uid) Iso15693sprintUID(target,uid) -int DEBUG=0; +// approximate amplitude=sqrt(ci^2+cq^2) +#define AMPLITUDE(ci, cq) (MAX(ABS(ci), ABS(cq)) + (MIN(ABS(ci), ABS(cq))>>1)) + +static int DEBUG = 0; // --------------------------- @@ -303,13 +306,9 @@ static int GetIso15693AnswerFromTag(uint8_t *receivedResponse, int maxLen, int * // NOW READ RESPONSE FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); - //spindelay(60); // greg - experiment to get rid of some of the 0 byte/failed reads c = 0; getNext = false; for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x43; - } if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { int8_t b; b = (int8_t)AT91C_BASE_SSC->SSC_RHR; @@ -319,11 +318,11 @@ static int GetIso15693AnswerFromTag(uint8_t *receivedResponse, int maxLen, int * // every other is Q. We just want power, so abs(I) + abs(Q) is // close to what we want. if(getNext) { - uint8_t r = ABS(b) + ABS(prev); + uint8_t r = AMPLITUDE(b, prev); - dest[c++] = (uint8_t)r; + dest[c++] = r; - if(c >= 2000) { + if(c >= 4000) { break; } } else { @@ -341,12 +340,10 @@ static int GetIso15693AnswerFromTag(uint8_t *receivedResponse, int maxLen, int * int i, j; int max = 0, maxPos=0; - int skip = 4; - - // if(GraphTraceLen < 1000) return; // THIS CHECKS FOR A BUFFER TO SMALL + int skip = 2; // First, correlate for SOF - for(i = 0; i < 100; i++) { + for(i = 0; i < 200; i++) { // usually, SOF is found around i = 60 int corr = 0; for(j = 0; j < arraylen(FrameSOF); j += skip) { corr += FrameSOF[j]*dest[i+(j/skip)]; @@ -356,7 +353,7 @@ static int GetIso15693AnswerFromTag(uint8_t *receivedResponse, int maxLen, int * maxPos = i; } } - // Dbprintf("SOF at %d, correlation %d", maxPos,max/(arraylen(FrameSOF)/skip)); + if (DEBUG) Dbprintf("SOF at %d, correlation %d", maxPos, max/(arraylen(FrameSOF)/skip)); int k = 0; // this will be our return value @@ -370,10 +367,15 @@ static int GetIso15693AnswerFromTag(uint8_t *receivedResponse, int maxLen, int * memset(outBuf, 0, sizeof(outBuf)); uint8_t mask = 0x01; for(;;) { - int corr0 = 0, corr1 = 0, corrEOF = 0; + int corr0 = 0, corr00 = 0, corr01 = 0, corr1 = 0, corrEOF = 0; for(j = 0; j < arraylen(Logic0); j += skip) { corr0 += Logic0[j]*dest[i+(j/skip)]; } + corr01 = corr00 = corr0; + for(j = 0; j < arraylen(Logic0); j += skip) { + corr00 += Logic0[j]*dest[i+arraylen(Logic0)/skip+(j/skip)]; + corr01 += Logic1[j]*dest[i+arraylen(Logic0)/skip+(j/skip)]; + } for(j = 0; j < arraylen(Logic1); j += skip) { corr1 += Logic1[j]*dest[i+(j/skip)]; } @@ -381,11 +383,14 @@ static int GetIso15693AnswerFromTag(uint8_t *receivedResponse, int maxLen, int * corrEOF += FrameEOF[j]*dest[i+(j/skip)]; } // Even things out by the length of the target waveform. + corr00 *= 2; + corr01 *= 2; corr0 *= 4; corr1 *= 4; - if(corrEOF > corr1 && corrEOF > corr0) { - // Dbprintf("EOF at %d", i); + if(corrEOF > corr1 && corrEOF > corr00 && corrEOF > corr01) { + if (DEBUG) Dbprintf("EOF at %d, correlation %d (corr01: %d, corr00: %d, corr1: %d, corr0: %d)", + i, corrEOF, corr01, corr00, corr1, corr0); break; } else if(corr1 > corr0) { i += arraylen(Logic1)/skip; @@ -398,7 +403,7 @@ static int GetIso15693AnswerFromTag(uint8_t *receivedResponse, int maxLen, int * k++; mask = 0x01; } - if((i+(int)arraylen(FrameEOF)) >= 2000) { + if((i+(int)arraylen(FrameEOF)/skip) >= 4000) { DbpString("ran off end!"); break; } @@ -446,9 +451,6 @@ static int GetIso15693AnswerFromSniff(uint8_t *receivedResponse, int maxLen, int c = 0; getNext = false; for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x43; - } if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { int8_t b = (int8_t)AT91C_BASE_SSC->SSC_RHR; @@ -457,11 +459,11 @@ static int GetIso15693AnswerFromSniff(uint8_t *receivedResponse, int maxLen, int // every other is Q. We just want power, so abs(I) + abs(Q) is // close to what we want. if(getNext) { - uint8_t r = ABS(b) + ABS(prev); + uint8_t r = AMPLITUDE(b, prev); - dest[c++] = (uint8_t)r; + dest[c++] = r; - if(c >= 20000) { + if(c >= BIGBUF_SIZE) { break; } } else { @@ -479,12 +481,10 @@ static int GetIso15693AnswerFromSniff(uint8_t *receivedResponse, int maxLen, int int i, j; int max = 0, maxPos=0; - int skip = 4; - -// if(GraphTraceLen < 1000) return; // THIS CHECKS FOR A BUFFER TO SMALL + int skip = 2; // First, correlate for SOF - for(i = 0; i < 19000; i++) { + for(i = 0; i < 38000; i++) { int corr = 0; for(j = 0; j < arraylen(FrameSOF); j += skip) { corr += FrameSOF[j]*dest[i+(j/skip)]; @@ -494,7 +494,7 @@ static int GetIso15693AnswerFromSniff(uint8_t *receivedResponse, int maxLen, int maxPos = i; } } -// DbpString("SOF at %d, correlation %d", maxPos,max/(arraylen(FrameSOF)/skip)); + if (DEBUG) Dbprintf("SOF at %d, correlation %d", maxPos,max/(arraylen(FrameSOF)/skip)); int k = 0; // this will be our return value @@ -508,10 +508,15 @@ static int GetIso15693AnswerFromSniff(uint8_t *receivedResponse, int maxLen, int memset(outBuf, 0, sizeof(outBuf)); uint8_t mask = 0x01; for(;;) { - int corr0 = 0, corr1 = 0, corrEOF = 0; + int corr0 = 0, corr00 = 0, corr01 = 0, corr1 = 0, corrEOF = 0; for(j = 0; j < arraylen(Logic0); j += skip) { corr0 += Logic0[j]*dest[i+(j/skip)]; } + corr01 = corr00 = corr0; + for(j = 0; j < arraylen(Logic0); j += skip) { + corr00 += Logic0[j]*dest[i+arraylen(Logic0)/skip+(j/skip)]; + corr01 += Logic1[j]*dest[i+arraylen(Logic0)/skip+(j/skip)]; + } for(j = 0; j < arraylen(Logic1); j += skip) { corr1 += Logic1[j]*dest[i+(j/skip)]; } @@ -519,11 +524,14 @@ static int GetIso15693AnswerFromSniff(uint8_t *receivedResponse, int maxLen, int corrEOF += FrameEOF[j]*dest[i+(j/skip)]; } // Even things out by the length of the target waveform. + corr00 *= 2; + corr01 *= 2; corr0 *= 4; corr1 *= 4; - if(corrEOF > corr1 && corrEOF > corr0) { - // DbpString("EOF at %d", i); + if(corrEOF > corr1 && corrEOF > corr00 && corrEOF > corr01) { + if (DEBUG) Dbprintf("EOF at %d, correlation %d (corr01: %d, corr00: %d, corr1: %d, corr0: %d)", + i, corrEOF, corr01, corr00, corr1, corr0); break; } else if(corr1 > corr0) { i += arraylen(Logic1)/skip; @@ -536,7 +544,7 @@ static int GetIso15693AnswerFromSniff(uint8_t *receivedResponse, int maxLen, int k++; mask = 0x01; } - if((i+(int)arraylen(FrameEOF)) >= 2000) { + if((i+(int)arraylen(FrameEOF)/skip) >= BIGBUF_SIZE) { DbpString("ran off end!"); break; } @@ -602,10 +610,6 @@ void AcquireRawAdcSamplesIso15693(void) break; } } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; - (void)r; - } WDT_HIT(); } @@ -614,9 +618,6 @@ void AcquireRawAdcSamplesIso15693(void) c = 0; getNext = false; for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x43; - } if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { int8_t b; b = (int8_t)AT91C_BASE_SSC->SSC_RHR; @@ -626,11 +627,11 @@ void AcquireRawAdcSamplesIso15693(void) // every other is Q. We just want power, so abs(I) + abs(Q) is // close to what we want. if(getNext) { - uint8_t r = ABS(b) + ABS(prev); + uint8_t r = AMPLITUDE(b, prev); - dest[c++] = (uint8_t)r; + dest[c++] = r; - if(c >= 2000) { + if(c >= 4000) { break; } } else { @@ -668,9 +669,6 @@ void RecordRawAdcSamplesIso15693(void) c = 0; getNext = false; for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x43; - } if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { int8_t b; b = (int8_t)AT91C_BASE_SSC->SSC_RHR; @@ -680,11 +678,11 @@ void RecordRawAdcSamplesIso15693(void) // every other is Q. We just want power, so abs(I) + abs(Q) is // close to what we want. if(getNext) { - uint8_t r = ABS(b) + ABS(prev); + uint8_t r = AMPLITUDE(b, prev); - dest[c++] = (uint8_t)r; + dest[c++] = r; - if(c >= 7000) { + if(c >= 14000) { break; } } else { @@ -836,7 +834,7 @@ int SendDataTag(uint8_t *send, int sendlen, int init, int speed, uint8_t **recv) if (init) Iso15693InitReader(); int answerLen=0; - uint8_t *answer = BigBuf_get_addr() + 3660; + uint8_t *answer = BigBuf_get_addr() + 4000; if (recv != NULL) memset(answer, 0, 100); if (!speed) { @@ -957,7 +955,7 @@ void ReaderIso15693(uint32_t parameter) int answerLen1 = 0; int answerLen2 = 0; - int answerLen3 = 0; + // int answerLen3 = 0; int i = 0; int samples = 0; int tsamples = 0; @@ -967,11 +965,11 @@ void ReaderIso15693(uint32_t parameter) FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - uint8_t *answer1 = BigBuf_get_addr() + 3660; - uint8_t *answer2 = BigBuf_get_addr() + 3760; - uint8_t *answer3 = BigBuf_get_addr() + 3860; + uint8_t *answer1 = BigBuf_get_addr() + 4000; + uint8_t *answer2 = BigBuf_get_addr() + 4100; + // uint8_t *answer3 = BigBuf_get_addr() + 4200; // Blank arrays - memset(answer1, 0x00, 300); + memset(answer1, 0x00, 200); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // Setup SSC @@ -1025,21 +1023,21 @@ void ReaderIso15693(uint32_t parameter) TagUID[3],TagUID[2],TagUID[1],TagUID[0]); - Dbprintf("%d octets read from SELECT request:", answerLen2); - DbdecodeIso15693Answer(answerLen2,answer2); - Dbhexdump(answerLen2,answer2,true); + // Dbprintf("%d octets read from SELECT request:", answerLen2); + // DbdecodeIso15693Answer(answerLen2,answer2); + // Dbhexdump(answerLen2,answer2,true); - Dbprintf("%d octets read from XXX request:", answerLen3); - DbdecodeIso15693Answer(answerLen3,answer3); - Dbhexdump(answerLen3,answer3,true); + // Dbprintf("%d octets read from XXX request:", answerLen3); + // DbdecodeIso15693Answer(answerLen3,answer3); + // Dbhexdump(answerLen3,answer3,true); // read all pages if (answerLen1>=12 && DEBUG) { i=0; while (i<32) { // sanity check, assume max 32 pages BuildReadBlockRequest(TagUID,i); - TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait); - answerLen2 = GetIso15693AnswerFromTag(answer2, 100, &samples, &elapsed); + TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait); + answerLen2 = GetIso15693AnswerFromTag(answer2, 100, &samples, &elapsed); if (answerLen2>0) { Dbprintf("READ SINGLE BLOCK %d returned %d octets:",i,answerLen2); DbdecodeIso15693Answer(answerLen2,answer2); @@ -1073,7 +1071,7 @@ void SimTagIso15693(uint32_t parameter, uint8_t *uid) FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - uint8_t *buf = BigBuf_get_addr() + 3660; + uint8_t *buf = BigBuf_get_addr() + 4000; memset(buf, 0x00, 100); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); @@ -1177,7 +1175,7 @@ void DirectTag15693Command(uint32_t datalen,uint32_t speed, uint32_t recv, uint8 if (recv) { LED_B_ON(); - cmd_send(CMD_ACK,recvlen>48?48:recvlen,0,0,recvbuf,48); + cmd_send(CMD_ACK,recvlen>48?48:recvlen,0,0,recvbuf,48); LED_B_OFF(); if (DEBUG) { diff --git a/client/cmdhf15.c b/client/cmdhf15.c index 08cc3b15..b6a84c1c 100644 --- a/client/cmdhf15.c +++ b/client/cmdhf15.c @@ -268,7 +268,7 @@ static char* TagErrorStr(uint8_t error) { case 0x02: return "The command is not recognised"; case 0x03: return "The option is not supported."; case 0x0f: return "Unknown error."; - case 0x10: return "The specified block is not available (doesn’t exist)."; + case 0x10: return "The specified block is not available (doesn't exist)."; case 0x11: return "The specified block is already -locked and thus cannot be locked again"; case 0x12: return "The specified block is locked and its content cannot be changed."; case 0x13: return "The specified block was not successfully programmed."; @@ -286,12 +286,12 @@ int CmdHF15Demod(const char *Cmd) int i, j; int max = 0, maxPos = 0; - int skip = 4; + int skip = 2; - if (GraphTraceLen < 1000) return 0; + if (GraphTraceLen < 2000) return 0; // First, correlate for SOF - for (i = 0; i < 100; i++) { + for (i = 0; i < 200; i++) { int corr = 0; for (j = 0; j < arraylen(FrameSOF); j += skip) { corr += FrameSOF[j] * GraphBuffer[i + (j / skip)]; @@ -310,23 +310,30 @@ int CmdHF15Demod(const char *Cmd) memset(outBuf, 0, sizeof(outBuf)); uint8_t mask = 0x01; for (;;) { - int corr0 = 0, corr1 = 0, corrEOF = 0; - for (j = 0; j < arraylen(Logic0); j += skip) { - corr0 += Logic0[j] * GraphBuffer[i + (j / skip)]; - } - for (j = 0; j < arraylen(Logic1); j += skip) { - corr1 += Logic1[j] * GraphBuffer[i + (j / skip)]; - } - for (j = 0; j < arraylen(FrameEOF); j += skip) { - corrEOF += FrameEOF[j] * GraphBuffer[i + (j / skip)]; - } - // Even things out by the length of the target waveform. - corr0 *= 4; - corr1 *= 4; - - if (corrEOF > corr1 && corrEOF > corr0) { - PrintAndLog("EOF at %d", i); - break; + int corr0 = 0, corr00 = 0, corr01 = 0, corr1 = 0, corrEOF = 0; + for(j = 0; j < arraylen(Logic0); j += skip) { + corr0 += Logic0[j]*GraphBuffer[i+(j/skip)]; + } + corr01 = corr00 = corr0; + for(j = 0; j < arraylen(Logic0); j += skip) { + corr00 += Logic0[j]*GraphBuffer[i+arraylen(Logic0)/skip+(j/skip)]; + corr01 += Logic1[j]*GraphBuffer[i+arraylen(Logic0)/skip+(j/skip)]; + } + for(j = 0; j < arraylen(Logic1); j += skip) { + corr1 += Logic1[j]*GraphBuffer[i+(j/skip)]; + } + for(j = 0; j < arraylen(FrameEOF); j += skip) { + corrEOF += FrameEOF[j]*GraphBuffer[i+(j/skip)]; + } + // Even things out by the length of the target waveform. + corr00 *= 2; + corr01 *= 2; + corr0 *= 4; + corr1 *= 4; + + if(corrEOF > corr1 && corrEOF > corr00 && corrEOF > corr01) { + PrintAndLog("EOF at %d", i); + break; } else if (corr1 > corr0) { i += arraylen(Logic1) / skip; outBuf[k] |= mask; diff --git a/fpga/fpga_hf.bit b/fpga/fpga_hf.bit index 229151dfbf50cb8de730a85446ccbc18407e8474..939ba93a54ce00e9425a1156d3900f1af58c4c8e 100644 GIT binary patch literal 42175 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 literal 42175 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 Z%S`zFE0UDq-{Xn`{Cl{VmhxZne*^UawK@O* diff --git a/fpga/hi_read_rx_xcorr.v b/fpga/hi_read_rx_xcorr.v index 433d6736..f637abf2 100644 --- a/fpga/hi_read_rx_xcorr.v +++ b/fpga/hi_read_rx_xcorr.v @@ -27,22 +27,12 @@ assign pwr_hi = ck_1356megb & (~snoop); assign pwr_oe1 = 1'b0; assign pwr_oe3 = 1'b0; assign pwr_oe4 = 1'b0; +// Unused. +assign pwr_lo = 1'b0; +assign pwr_oe2 = 1'b0; -reg [2:0] fc_div; -always @(negedge ck_1356megb) - fc_div <= fc_div + 1; +assign adc_clk = ck_1356megb; // sample frequency is 13,56 MHz -(* clock_signal = "yes" *) reg adc_clk; // sample frequency, always 16 * fc -always @(ck_1356megb, xcorr_is_848, xcorr_quarter_freq, fc_div) - if (xcorr_is_848 & ~xcorr_quarter_freq) // fc = 847.5 kHz, standard ISO14443B - adc_clk <= ck_1356megb; - else if (~xcorr_is_848 & ~xcorr_quarter_freq) // fc = 423.75 kHz - adc_clk <= fc_div[0]; - else if (xcorr_is_848 & xcorr_quarter_freq) // fc = 211.875 kHz - adc_clk <= fc_div[1]; - else // fc = 105.9375 kHz - adc_clk <= fc_div[2]; - // When we're a reader, we just need to do the BPSK demod; but when we're an // eavesdropper, we also need to pick out the commands sent by the reader, // using AM. Do this the same way that we do it for the simulated tag. @@ -69,15 +59,27 @@ begin end end -// Let us report a correlation every 4 subcarrier cycles, or 4*16=64 samples, -// so we need a 6-bit counter. + +// Let us report a correlation every 64 samples. I.e. +// one Q/I pair after 4 subcarrier cycles for the 848kHz subcarrier, +// one Q/I pair after 2 subcarrier cycles for the 424kHz subcarriers, +// one Q/I pair for each subcarrier cyle for the 212kHz subcarrier. +// We need a 6-bit counter for the timing. reg [5:0] corr_i_cnt; -// And a couple of registers in which to accumulate the correlations. -// We would add at most 32 times the difference between unmodulated and modulated signal. It should +always @(negedge adc_clk) +begin + corr_i_cnt <= corr_i_cnt + 1; +end + +// And a couple of registers in which to accumulate the correlations. From the 64 samples +// we would add at most 32 times the difference between unmodulated and modulated signal. It should // be safe to assume that a tag will not be able to modulate the carrier signal by more than 25%. // 32 * 255 * 0,25 = 2040, which can be held in 11 bits. Add 1 bit for sign. -reg signed [11:0] corr_i_accum; -reg signed [11:0] corr_q_accum; +// Temporary we might need more bits. For the 212kHz subcarrier we could possible add 32 times the +// maximum signal value before a first subtraction would occur. 32 * 255 = 8160 can be held in 13 bits. +// Add one bit for sign -> need 14 bit registers but final result will fit into 12 bits. +reg signed [13:0] corr_i_accum; +reg signed [13:0] corr_q_accum; // we will report maximum 8 significant bits reg signed [7:0] corr_i_out; reg signed [7:0] corr_q_out; @@ -86,12 +88,29 @@ reg ssp_clk; reg ssp_frame; -always @(negedge adc_clk) -begin - corr_i_cnt <= corr_i_cnt + 1; -end - +// The subcarrier reference signals +reg subcarrier_I; +reg subcarrier_Q; +always @(corr_i_cnt or xcorr_is_848 or xcorr_quarter_freq) +begin + if (xcorr_is_848 & ~xcorr_quarter_freq) // 848 kHz + begin + subcarrier_I = ~corr_i_cnt[3]; + subcarrier_Q = ~(corr_i_cnt[3] ^ corr_i_cnt[2]); + end + else if (xcorr_is_848 & xcorr_quarter_freq) // 212 kHz + begin + subcarrier_I = ~corr_i_cnt[5]; + subcarrier_Q = ~(corr_i_cnt[5] ^ corr_i_cnt[4]); + end + else + begin // 424 kHz + subcarrier_I = ~corr_i_cnt[4]; + subcarrier_Q = ~(corr_i_cnt[4] ^ corr_i_cnt[3]); + end +end + // ADC data appears on the rising edge, so sample it on the falling edge always @(negedge adc_clk) begin @@ -103,36 +122,60 @@ begin if(snoop) begin // Send 7 most significant bits of tag signal (signed), plus 1 bit reader signal - corr_i_out <= {corr_i_accum[11:5], after_hysteresis_prev_prev}; - corr_q_out <= {corr_q_accum[11:5], after_hysteresis_prev}; + if (corr_i_accum[13:11] == 3'b000 || corr_i_accum[13:11] == 3'b111) + corr_i_out <= {corr_i_accum[11:5], after_hysteresis_prev_prev}; + else // truncate to maximum value + if (corr_i_accum[13] == 1'b0) + corr_i_out <= {7'b0111111, after_hysteresis_prev_prev}; + else + corr_i_out <= {7'b1000000, after_hysteresis_prev_prev}; + if (corr_q_accum[13:11] == 3'b000 || corr_q_accum[13:11] == 3'b111) + corr_q_out <= {corr_q_accum[11:5], after_hysteresis_prev}; + else // truncate to maximum value + if (corr_q_accum[13] == 1'b0) + corr_q_out <= {7'b0111111, after_hysteresis_prev}; + else + corr_q_out <= {7'b1000000, after_hysteresis_prev}; after_hysteresis_prev_prev <= after_hysteresis; end else begin - // 8 bits of tag signal - corr_i_out <= corr_i_accum[11:4]; - corr_q_out <= corr_q_accum[11:4]; + // Send 8 bits of tag signal + if (corr_i_accum[13:11] == 3'b000 || corr_i_accum[13:11] == 3'b111) + corr_i_out <= corr_i_accum[11:4]; + else // truncate to maximum value + if (corr_i_accum[13] == 1'b0) + corr_i_out <= 8'b01111111; + else + corr_i_out <= 8'b10000000; + if (corr_q_accum[13:11] == 3'b000 || corr_q_accum[13:11] == 3'b111) + corr_q_out <= corr_q_accum[11:4]; + else // truncate to maximum value + if (corr_q_accum[13] == 1'b0) + corr_q_out <= 8'b01111111; + else + corr_q_out <= 8'b10000000; end - - corr_i_accum <= adc_d; - corr_q_accum <= adc_d; + // Initialize next correlation. + // Both I and Q reference signals are high when corr_i_nct == 0. Therefore need to accumulate. + corr_i_accum <= $signed({1'b0,adc_d}); + corr_q_accum <= $signed({1'b0,adc_d}); end else begin - if(corr_i_cnt[3]) - corr_i_accum <= corr_i_accum - adc_d; + if (subcarrier_I) + corr_i_accum <= corr_i_accum + $signed({1'b0,adc_d}); else - corr_i_accum <= corr_i_accum + adc_d; + corr_i_accum <= corr_i_accum - $signed({1'b0,adc_d}); - if(corr_i_cnt[3] == corr_i_cnt[2]) // phase shifted by pi/2 - corr_q_accum <= corr_q_accum + adc_d; + if (subcarrier_Q) + corr_q_accum <= corr_q_accum + $signed({1'b0,adc_d}); else - corr_q_accum <= corr_q_accum - adc_d; + corr_q_accum <= corr_q_accum - $signed({1'b0,adc_d}); end - // The logic in hi_simulate.v reports 4 samples per bit. We report two - // (I, Q) pairs per bit, so we should do 2 samples per pair. + // for each Q/I pair report two reader signal samples when sniffing if(corr_i_cnt == 6'd32) after_hysteresis_prev <= after_hysteresis; @@ -167,8 +210,4 @@ assign ssp_din = corr_i_out[7]; assign dbg = corr_i_cnt[3]; -// Unused. -assign pwr_lo = 1'b0; -assign pwr_oe2 = 1'b0; - endmodule From 1ee624fe6a48d83544df3ce2078e127390551e15 Mon Sep 17 00:00:00 2001 From: grauerfuchs <42082416+grauerfuchs@users.noreply.github.com> Date: Sun, 19 Aug 2018 11:14:52 -0400 Subject: [PATCH 246/310] Adding native support for HID long-format Removal of "l" flag for long writes; not needed anymore. Added HID Corporate 1000 48-bit format to known formats list Simulation not yet updated; need to modify ARM commands to support it Completed parity support on all included HID formats --- client/cmdlfhid.c | 334 +++++++++++++++++++++++++++------------------- client/cmdlfhid.h | 15 ++- 2 files changed, 203 insertions(+), 146 deletions(-) diff --git a/client/cmdlfhid.c b/client/cmdlfhid.c index 433a093d..1a9c3477 100644 --- a/client/cmdlfhid.c +++ b/client/cmdlfhid.c @@ -29,109 +29,146 @@ #include "cmddata.h" //for g_debugMode, demodbuff cmds #include "lfdemod.h" // for HIDdemodFSK #include "parity.h" // for parity -#include "util.h" // for param_get8,32 +#include "util.h" // for param_get8,32,64 static int CmdHelp(const char *Cmd); - /** - * Packs a "short" (<38-bit) HID ID from component parts. + * Packs an HID ID from component parts. * - * This only works with 26, 34, 35 and 37 bit card IDs. - * - * NOTE: Parity calculation is only supported on 26-bit tags. Other card lengths - * may have invalid parity. + * This only works with 26, 34, 35, 37, and 48 bit card IDs. * * Returns false on invalid inputs. */ -bool pack_short_hid(/* out */ uint32_t *hi, /* out */ uint32_t *lo, /* in */ const short_hid_info *info) { - uint32_t high = 0, low = 0; +bool pack_hid(/* out */ uint32_t *hi2, /* out */ uint32_t *hi, /* out */ uint32_t *lo, /* in */ const hid_info *info) { + uint32_t higher = 0, high = 0, low = 0; switch (info->fmtLen) { - case 26: // HID H10301 - low |= (info->cardnum & 0xffff) << 1; - low |= (info->fc & 0xff) << 17; + case 26: // HID H10301 + low |= (info->cardnum & 0xffff) << 1; + low |= (info->fc & 0xff) << 17; - if (info->parityValid) { - // Calculate parity - low |= oddparity32((low >> 1) & 0xfff) & 1; - low |= (evenparity32((low >> 13) & 0xfff) & 1) << 25; - } + if (info->parityValid) { + // Calculate parity + low |= oddparity32((low >> 1) & 0xfff) & 1; + low |= (evenparity32((low >> 13) & 0xfff) & 1) << 25; + } break; - case 34: + case 34: // H10306 low |= (info->cardnum & 0xffff) << 1; low |= (info->fc & 0x7fff) << 17; high |= (info->fc & 0x8000) >> 15; - // TODO: Calculate parity + + if (info->parityValid) { + // Calculate parity + high |= (evenparity32((high & 0x00000001) ^ (low & 0xFFFE0000)) & 1) << 1; + low |= (oddparity32(low & 0x0001FFFE) & 1); + } break; - case 35: + case 35: // (Corporate 1000 35-bit) low |= (info->cardnum & 0xfffff) << 1; low |= (info->fc & 0x7ff) << 21; high |= (info->fc & 0x800) >> 11; - // TODO: Calculate parity + + if (info->parityValid) { + // Calculate parity + high |= (evenparity32((high & 0x00000001) ^ (low & 0xB6DB6DB6)) & 1) << 1; + low |= (oddparity32( (high & 0x00000003) ^ (low & 0x6DB6DB6C)) & 1); + high |= (oddparity32( (high & 0x00000003) ^ (low & 0xFFFFFFFF)) & 1) << 2; + } break; - case 37: + case 37: //H10304 low |= (info->cardnum & 0x7ffff) << 1; low |= (info->fc & 0xfff) << 20; high |= (info->fc & 0xf000) >> 12; - // TODO: Calculate parity + + if (info->parityValid) { + // Calculate parity + high |= (evenparity32((high & 0x0000000F) ^ (low & 0xFFFC0000)) & 1) << 4; + low |= (oddparity32(low & 0x0007FFFE) & 1); + } + break; + + case 48: // Corporate 1000 48-bit + low |= (info->cardnum & 0x7FFFFF) << 1; + low |= (info->fc & 0xff) << 24; + high |= (info->fc & 0x3FFF00) >> 8; + + if (info->parityValid) { + // Calculate parity + high |= (evenparity32((high & 0x00001B6D) ^ (low & 0xB6DB6DB6)) & 1) << 14; + low |= (oddparity32( (high & 0x000036DB) ^ (low & 0x6DB6DB6C)) & 1); + high |= (oddparity32( (high & 0x00007FFF) ^ (low & 0xFFFFFFFF)) & 1) << 15; + } break; default: // Invalid / unsupported length return false; } - - // Set the highest bit - if (info->fmtLen != 37) { + + // Set the format length bits + if (info->fmtLen < 37) { // Bit 37 is always set high |= 0x20; // Set the bit corresponding to the length. - if (info->fmtLen < 32) { + if (info->fmtLen < 32) low |= 1 << info->fmtLen; - } else { + else high |= 1 << (info->fmtLen - 32); - } + + } else if (info->fmtLen > 37){ + if (info->fmtLen < 64) + high |= 1 << (info->fmtLen - 32); + else + higher |= 1 << (info->fmtLen - 64); } - // Return result only if successful. + *hi2 = higher; *hi = high; *lo = low; return true; } - /** - * Unpacks a "short" (<38-bit) HID ID into its component parts. + * Unpacks an HID ID into its component parts. * - * This only works with 26, 34, 35 and 37 bit card IDs. - * - * NOTE: Parity checking is only supported on 26-bit tags. + * This only works with 26, 34, 35, 37, and 48 bit card IDs. * * Returns false on invalid inputs. */ -bool unpack_short_hid(short_hid_info *out, uint32_t hi, uint32_t lo) { - memset(out, 0, sizeof(short_hid_info)); +bool unpack_hid(hid_info *out, uint32_t hi2, uint32_t hi, uint32_t lo) { + memset(out, 0, sizeof(hid_info)); + uint8_t fmtLen = 0; + + uint32_t hFmt; // for calculating card length + if ((hi2 & 0x000FFFFF) > 0) { // > 64 bits + hFmt = hi2 & 0x000FFFFF; + fmtLen = 64; + } else if ((hi & 0xFFFFFFC0) > 0) { // < 63-38 bits + hFmt = hi & 0xFFFFFFC0; + fmtLen = 32; + } else if ((hi & 0x00000020) == 0) { // 37 bits + hFmt = 0; + fmtLen = 37; + } else if ((hi & 0x0000001F) > 0){ // 36-32 bits + hFmt = hi & 0x0000001F; + fmtLen = 32; + } else { // <32 bits + hFmt = lo; + fmtLen = 0; + } - if (((hi >> 5) & 1) == 1) { - // if bit 38 is set then < 37 bit format is used - uint32_t lo2 = 0; - // get bits 21-37 to check for format len bit - lo2 = (((hi & 31) << 12) | (lo >> 20)); - uint8_t idx3 = 1; - // find last bit set to 1 (format len bit) - while (lo2 > 1) { - lo2 = lo2 >> 1; - idx3++; - } - - out->fmtLen = idx3 + 19; - - switch (out->fmtLen) { + while (hFmt > 1) { + hFmt >>= 1; + fmtLen++; + } + out->fmtLen = fmtLen; + switch (out->fmtLen) { case 26: // HID H10301 out->cardnum = (lo >> 1) & 0xFFFF; out->fc = (lo >> 17) & 0xFF; @@ -148,44 +185,80 @@ bool unpack_short_hid(short_hid_info *out, uint32_t hi, uint32_t lo) { ((evenparity32((lo >> 13) & 0xFFF) & 1) == ((lo >> 25) & 1)); break; - case 34: + case 34: // HID H10306 out->cardnum = (lo >> 1) & 0xFFFF; out->fc = ((hi & 1) << 15) | (lo >> 17); - // TODO: Calculate parity + out->parityValid = + ((evenparity32((hi & 0x00000001) ^ (lo & 0xFFFE0000)) & 1) == ((hi >> 1) & 1)) && + ((oddparity32(lo & 0x0001FFFE) & 1) == ((lo & 1))); break; - case 35: + case 35: // HID Corporate 1000-35 out->cardnum = (lo >> 1) & 0xFFFFF; out->fc = ((hi & 1) << 11) | (lo >> 21); - // TODO: Calculate parity + out->parityValid = + (evenparity32((hi & 0x00000001) ^ (lo & 0xB6DB6DB6)) == ((hi >> 1) & 1)) && + (oddparity32( (hi & 0x00000003) ^ (lo & 0x6DB6DB6C)) == ((lo >> 0) & 1)) && + (oddparity32( (hi & 0x00000003) ^ (lo & 0xFFFFFFFF)) == ((hi >> 2) & 1)); + if (g_debugMode) { + PrintAndLog("Parity check: calculated {%d, %d, %d}, provided {%d, %d, %d}", + evenparity32((hi & 0x00000001) ^ (lo & 0xB6DB6DB6)), + oddparity32( (hi & 0x00000003) ^ (lo & 0x6DB6DB6C)), + oddparity32( (hi & 0x00000003) ^ (lo & 0xFFFFFFFF)), + ((hi >> 1) & 1), + ((lo >> 0) & 1), + ((hi >> 2) & 1) + ); + } + break; + + case 37: // HID H10304 + out->fmtLen = 37; + out->cardnum = (lo >> 1) & 0x7FFFF; + out->fc = ((hi & 0xF) << 12) | (lo >> 20); + out->parityValid = + (evenparity32((hi & 0x0000000F) ^ (lo & 0xFFFC0000)) == ((hi >> 4) & 1)) && + (oddparity32( lo & 0x0007FFFE) == (lo & 1)); + break; + + case 48: // HID Corporate 1000-48 + out->cardnum = (lo >> 1) & 0x7FFFFF; //Start 24, 23 length + out->fc = ((hi & 0x3FFF) << 8 ) | (lo >> 24); //Start 2, 22 length + out->parityValid = + (evenparity32((hi & 0x00001B6D) ^ (lo & 0xB6DB6DB6)) == ((hi >> 14) & 1)) && + (oddparity32( (hi & 0x000036DB) ^ (lo & 0x6DB6DB6C)) == ((lo >> 0) & 1)) && + (oddparity32( (hi & 0x00007FFF) ^ (lo & 0xFFFFFFFF)) == ((hi >> 15) & 1)); + if (g_debugMode) { + PrintAndLog("Parity check: calculated {%d, %d, %d}, provided {%d, %d, %d}", + evenparity32((hi & 0x00001B6D) ^ (lo & 0xB6DB6DB6)), + oddparity32( (hi & 0x000036DB) ^ (lo & 0x6DB6DB6C)), + oddparity32( (hi & 0x00007FFF) ^ (lo & 0xFFFFFFFF)), + ((hi >> 14) & 1), + ((lo >> 0) & 1), + ((hi >> 15) & 1) + ); + } break; default: return false; - } - } else { - // If bit 38 is not set, then 37 bit format is used - out->fmtLen = 37; - out->cardnum = (lo >> 1) & 0x7FFFF; - out->fc = ((hi & 0xF) << 12) | (lo >> 20); - // TODO: Calculate parity } return true; } - /** - * Converts a hex string to component "hi" and "lo" 32-bit integers, one nibble + * Converts a hex string to component "hi2", "hi" and "lo" 32-bit integers, one nibble * at a time. * * Returns the number of nibbles (4 bits) entered. */ -int hexstring_to_int64(/* out */ uint32_t* hi, /* out */ uint32_t* lo, const char* str) { +int hexstring_to_int96(/* out */ uint32_t* hi2,/* out */ uint32_t* hi, /* out */ uint32_t* lo, const char* str) { // TODO: Replace this with param_gethex when it supports arbitrary length // inputs. int n = 0, i = 0; while (sscanf(&str[i++], "%1x", &n ) == 1) { + *hi2 = (*hi2 << 4) | (*hi >> 28); *hi = (*hi << 4) | (*lo >> 28); *lo = (*lo << 4) | (n & 0xf); } @@ -227,24 +300,24 @@ int CmdFSKdemodHID(const char *Cmd) if (g_debugMode) PrintAndLog("DEBUG: Error - no values found"); return 0; } - if (hi2 != 0){ //extra large HID tags - PrintAndLog("HID Prox TAG ID: %x%08x%08x (%d)", - (unsigned int) hi2, (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF); - } - else { //standard HID tags <38 bits - short_hid_info card_info; - bool ret = unpack_short_hid(&card_info, (uint32_t)hi, (uint32_t)lo); + + hid_info card_info; + bool ret = unpack_hid(&card_info, (uint32_t)hi2, (uint32_t)hi, (uint32_t)lo); + + if (hi2 != 0) + PrintAndLog("HID Prox TAG ID: %x%08x%08x (%d) - Format Len: %u bits - FC: %u - Card: %u", + (unsigned int) hi2, (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF, + card_info.fmtLen, card_info.fc, card_info.cardnum); + else PrintAndLog("HID Prox TAG ID: %x%08x (%d) - Format Len: %u bits - FC: %u - Card: %u", (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF, card_info.fmtLen, card_info.fc, card_info.cardnum); - if (card_info.fmtLen == 26) { - PrintAndLog("Parity: %s", card_info.parityValid ? "valid" : "invalid"); - } - - if (!ret) { - PrintAndLog("Invalid or unsupported tag length."); - } + if (card_info.fmtLen == 26 || card_info.fmtLen == 35 || card_info.fmtLen == 48) { + PrintAndLog("Parity: %s", card_info.parityValid ? "valid" : "invalid"); + } + if (!ret) { + PrintAndLog("Invalid or unsupported tag length."); } setDemodBuf(BitStream,BitLen,idx); setClockGrid(50, waveIdx + (idx*50)); @@ -267,9 +340,9 @@ int CmdHIDReadFSK(const char *Cmd) int CmdHIDSim(const char *Cmd) { - uint32_t hi = 0, lo = 0; - hexstring_to_int64(&hi, &lo, Cmd); - if (hi >= 0x40) { + uint32_t hi2 = 0, hi = 0, lo = 0; + hexstring_to_int96(&hi2, &hi, &lo, Cmd); + if (hi >= 0x40 || hi2 != 0) { PrintAndLog("This looks like a long tag ID. Use 'lf simfsk' for long tags. Aborting!"); return 0; } @@ -286,30 +359,13 @@ int CmdHIDClone(const char *Cmd) { unsigned int hi2 = 0, hi = 0, lo = 0; UsbCommand c; - - if (strchr(Cmd,'l') != 0) { - int n = 0, i = 0; - - while (sscanf(&Cmd[i++], "%1x", &n ) == 1) { - hi2 = (hi2 << 4) | (hi >> 28); - hi = (hi << 4) | (lo >> 28); - lo = (lo << 4) | (n & 0xf); - } - + hexstring_to_int96(&hi2, &hi, &lo, Cmd); + + if (hi >= 0x40 || hi2 != 0) { PrintAndLog("Cloning tag with long ID %x%08x%08x", hi2, hi, lo); - c.d.asBytes[0] = 1; - } - else { - hexstring_to_int64(&hi, &lo, Cmd); - if (hi >= 0x40) { - PrintAndLog("This looks like a long tag ID. Aborting!"); - return 0; - } - + } else { PrintAndLog("Cloning tag with ID %x%08x", hi, lo); - - hi2 = 0; c.d.asBytes[0] = 0; } @@ -324,31 +380,32 @@ int CmdHIDClone(const char *Cmd) int CmdHIDPack(const char *Cmd) { - uint32_t hi = 0, lo = 0; - short_hid_info card_info; - + uint32_t hi2 = 0, hi = 0, lo = 0; + if (strlen(Cmd)<3) { PrintAndLog("Usage: lf hid pack "); PrintAndLog(" sample: lf hid pack 26 123 4567"); return 0; } + uint8_t fmtLen = param_get8(Cmd, 0); - card_info.fmtLen = param_get8(Cmd, 0); + hid_info card_info; + card_info.fmtLen = fmtLen; card_info.fc = param_get32ex(Cmd, 1, 0, 10); - card_info.cardnum = param_get32ex(Cmd, 2, 0, 10); + card_info.cardnum = param_get64ex(Cmd, 2, 0, 10); card_info.parityValid = true; - // TODO - if (card_info.fmtLen != 26) { - PrintAndLog("Warning: Parity bits are only calculated for 26 bit IDs -- this may be invalid!"); - } - - bool ret = pack_short_hid(&hi, &lo, &card_info); - + bool ret = pack_hid(&hi2, &hi, &lo, &card_info); if (ret) { - PrintAndLog("HID Prox TAG ID: %x%08x (%d) - Format Len: %u bits - FC: %u - Card: %u", - (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF, - card_info.fmtLen, card_info.fc, card_info.cardnum); + if (hi2 != 0) { + PrintAndLog("HID Prox TAG ID: %x%08x%08x (%d) - Format Len: %u bits - FC: %u - Card: %u", + (unsigned int) hi2, (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF, + card_info.fmtLen, card_info.fc, card_info.cardnum); + } else { + PrintAndLog("HID Prox TAG ID: %x%08x (%d) - Format Len: %u bits - FC: %u - Card: %u", + (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF, + card_info.fmtLen, card_info.fc, card_info.cardnum); + } } else { PrintAndLog("Invalid or unsupported tag length."); } @@ -358,30 +415,29 @@ int CmdHIDPack(const char *Cmd) { int CmdHIDUnpack(const char *Cmd) { - uint32_t hi = 0, lo = 0; + uint32_t hi2 = 0, hi = 0, lo = 0; if (strlen(Cmd)<1) { PrintAndLog("Usage: lf hid unpack "); PrintAndLog(" sample: lf hid unpack 2006f623ae"); return 0; } - hexstring_to_int64(&hi, &lo, Cmd); - if (hi >= 0x40) { - PrintAndLog("This looks like a long tag ID. Aborting!"); - return 0; + hexstring_to_int96(&hi2, &hi, &lo, Cmd); + + hid_info card_info; + bool ret = unpack_hid(&card_info, hi2, hi, lo); + + if (hi2 != 0) { + PrintAndLog("HID Prox TAG ID: %x%08x%08x (%d) - Format Len: %u bits - FC: %u - Card: %u", + (unsigned int) hi2, (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF, + card_info.fmtLen, card_info.fc, card_info.cardnum); + } else { + PrintAndLog("HID Prox TAG ID: %x%08x (%d) - Format Len: %u bits - FC: %u - Card: %u", + (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF, + card_info.fmtLen, card_info.fc, card_info.cardnum); } - - short_hid_info card_info; - bool ret = unpack_short_hid(&card_info, hi, lo); - - PrintAndLog("HID Prox TAG ID: %x%08x (%d) - Format Len: %u bits - FC: %u - Card: %u", - (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF, - card_info.fmtLen, card_info.fc, card_info.cardnum); - - if (card_info.fmtLen == 26) { - PrintAndLog("Parity: %s", card_info.parityValid ? "valid" : "invalid"); - } - + PrintAndLog("Parity: %s", card_info.parityValid ? "valid" : "invalid"); + if (!ret) { PrintAndLog("Invalid or unsupported tag length."); } @@ -395,9 +451,9 @@ static command_t CommandTable[] = {"demod", CmdFSKdemodHID, 1, "Demodulate HID Prox from GraphBuffer"}, {"read", CmdHIDReadFSK, 0, "['1'] Realtime HID FSK Read from antenna (option '1' for one tag only)"}, {"sim", CmdHIDSim, 0, " -- HID tag simulator"}, - {"clone", CmdHIDClone, 0, " ['l'] -- Clone HID to T55x7 (tag must be in antenna)(option 'l' for 84bit ID)"}, - {"pack", CmdHIDPack, 1, " -- packs a <38 bit (short) HID ID from its length, facility code and card number"}, - {"unpack", CmdHIDUnpack, 1, " -- unpacks a <38 bit (short) HID ID to its length, facility code and card number"}, + {"clone", CmdHIDClone, 0, " -- Clone HID to T55x7 (tag must be in antenna)"}, + {"pack", CmdHIDPack, 1, " -- packs an HID ID from its length, facility code and card number"}, + {"unpack", CmdHIDUnpack, 1, " -- unpacks an HID ID to its length, facility code and card number"}, {NULL, NULL, 0, NULL} }; diff --git a/client/cmdlfhid.h b/client/cmdlfhid.h index e53a95c6..463be56f 100644 --- a/client/cmdlfhid.h +++ b/client/cmdlfhid.h @@ -14,7 +14,7 @@ #include #include -// Structure for unpacked "short" (<38 bits) HID Prox tags. +// Structure for unpacked HID Prox tags. typedef struct { // Format length, in bits. uint8_t fmtLen; @@ -23,20 +23,21 @@ typedef struct { uint32_t fc; // Card number. - uint32_t cardnum; + uint64_t cardnum; // Parity validity. // - // When used with pack_short_hid, this determines if we should calculate + // When used with pack_hid, this determines if we should calculate // parity values for the ID. // - // When used with unpack_short_hid, this indicates if we got valid parity + // When used with unpack_hid, this indicates if we got valid parity // values for the ID. bool parityValid; -} short_hid_info; +} hid_info; + +bool pack_hid(/* out */ uint32_t *hi2, /* out */ uint32_t *hi, /* out */ uint32_t *lo, /* in */ const hid_info *info); +bool unpack_hid(hid_info* out, uint32_t hi2, uint32_t hi, uint32_t lo); -bool pack_short_hid(/* out */ uint32_t *hi, /* out */ uint32_t *lo, /* in */ const short_hid_info *info); -bool unpack_short_hid(short_hid_info* out, uint32_t hi, uint32_t lo); int CmdLFHID(const char *Cmd); int CmdFSKdemodHID(const char *Cmd); From da05bc6eca632aa8ef5251942919ab8650191357 Mon Sep 17 00:00:00 2001 From: AntiCat Date: Mon, 20 Aug 2018 22:29:34 +0200 Subject: [PATCH 247/310] Legic: rewrite reader to use xcorrelation and precise timing (#654) * Legic: rewrite reader to use xcorrelation and precise timing - Even tough Legic tags transmit just AM, receiving using xcorrelation results in a significantly better signal quality. - Switching from bit bang to a hardware based ssc frees up CPU time for other tasks e.g. prng and demodulation - Having all times based on a fixed ts, results in perfect rwd-tag synchronization without magic +/- calculations. * hi_read_tx: remove jerry-riged hysteresis based receiver - This feature got obsolete by a x-correlation based receiver. * Legic: adjusted sampling to new ssp clock speed - Sampling is 4 times faster and pipeline daly reduced to 1/4. The new code samples each bit earyler to account for the shorter pipeline. That introduced bit errors by leeking the next bit into the current one. * Legic: average 8 samples for better noise rejection. * Update CHANGELOG.md --- CHANGELOG.md | 1 + armsrc/legicrf.c | 843 +++++++++++++++++++++++++--------------------- armsrc/legicrf.h | 2 +- fpga/hi_read_tx.v | 19 +- include/legic.h | 27 ++ 5 files changed, 492 insertions(+), 400 deletions(-) create mode 100644 include/legic.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 13fc97fb..a7e2e045 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Changed `hf 14a reader` to just reqest-anticilission-select sequence (Merlok) - Changed `hf 14a raw` - works with LED's and some exchange logic (Merlok) - Changed TLV parser messages to more convenient (Merlok) +- Rewritten Legic Prime reader (`hf legic reader`, `write` and `fill`) - it is using xcorrelation now (AntiCat) ### Fixed - Changed start sequence in Qt mode (fix: short commands hangs main Qt thread) (Merlok) diff --git a/armsrc/legicrf.c b/armsrc/legicrf.c index 27dcc297..a611c741 100644 --- a/armsrc/legicrf.c +++ b/armsrc/legicrf.c @@ -1,5 +1,7 @@ //----------------------------------------------------------------------------- // (c) 2009 Henryk Plötz +// 2016 Iceman +// 2018 AntiCat (rwd rewritten) // // 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 @@ -15,6 +17,7 @@ #include "legicrf.h" #include "legic_prng.h" +#include "legic.h" #include "crc.h" static struct legic_frame { @@ -40,6 +43,460 @@ static int legic_reqresp_drift; AT91PS_TC timer; AT91PS_TC prng_timer; +static legic_card_select_t card;/* metadata of currently selected card */ + +//----------------------------------------------------------------------------- +// Frame timing and pseudorandom number generator +// +// The Prng is forwarded every 100us (TAG_BIT_PERIOD), except when the reader is +// transmitting. In that case the prng has to be forwarded every bit transmitted: +// - 60us for a 0 (RWD_TIME_0) +// - 100us for a 1 (RWD_TIME_1) +// +// The data dependent timing makes writing comprehensible code significantly +// harder. The current aproach forwards the prng data based if there is data on +// air and time based, using GET_TICKS, during computational and wait periodes. +// +// To not have the necessity to calculate/guess exection time dependend timeouts +// tx_frame and rx_frame use a shared timestamp to coordinate tx and rx timeslots. +//----------------------------------------------------------------------------- + +static uint32_t last_frame_end; /* ts of last bit of previews rx or tx frame */ + +#define RWD_TIME_PAUSE 30 /* 20us */ +#define RWD_TIME_1 150 /* READER_TIME_PAUSE 20us off + 80us on = 100us */ +#define RWD_TIME_0 90 /* READER_TIME_PAUSE 20us off + 40us on = 60us */ +#define RWD_FRAME_WAIT 330 /* 220us from TAG frame end to READER frame start */ +#define TAG_FRAME_WAIT 495 /* 330us from READER frame end to TAG frame start */ +#define TAG_BIT_PERIOD 150 /* 100us */ +#define TAG_WRITE_TIMEOUT 60 /* 40 * 100us (write should take at most 3.6ms) */ + +#define SIM_DIVISOR 586 /* prng_time/DIV count prng needs to be forwared */ +#define SIM_SHIFT 900 /* prng_time+SHIFT shift of delayed start */ +#define RWD_TIME_FUZZ 20 /* rather generous 13us, since the peak detector + /+ hysteresis fuzz quite a bit */ + +#define LEGIC_READ 0x01 /* Read Command */ +#define LEGIC_WRITE 0x00 /* Write Command */ + +#define SESSION_IV 0x55 /* An arbitrary chose session IV, all shoud work */ +#define OFFSET_LOG 1024 /* The largest Legic Prime card is 1k */ +#define WRITE_LOWERLIMIT 4 /* UID and MCC are not writable */ + +#define INPUT_THRESHOLD 8 /* heuristically determined, lower values */ + /* lead to detecting false ack during write */ + +#define FUZZ_EQUAL(value, target, fuzz) ((value) > ((target)-(fuzz)) && (value) < ((target)+(fuzz))) + +//----------------------------------------------------------------------------- +// I/O interface abstraction (FPGA -> ARM) +//----------------------------------------------------------------------------- + +static inline uint8_t rx_byte_from_fpga() { + for(;;) { + WDT_HIT(); + + // wait for byte be become available in rx holding register + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + return AT91C_BASE_SSC->SSC_RHR; + } + } +} + +//----------------------------------------------------------------------------- +// Demodulation (Reader) +//----------------------------------------------------------------------------- + +// Returns a demedulated bit +// +// The FPGA running xcorrelation samples the subcarrier at ~13.56 MHz. The mode +// was initialy designed to receive BSPK/2-PSK. Hance, it reports an I/Q pair +// every 4.7us (8 bits i and 8 bits q). +// +// The subcarrier amplitude can be calculated using Pythagoras sqrt(i^2 + q^2). +// To reduce CPU time the amplitude is approximated by using linear functions: +// am = MAX(ABS(i),ABS(q)) + 1/2*MIN(ABS(i),ABSq)) +// +// Note: The SSC receiver is never synchronized the calculation my be performed +// on a I/Q pair from two subsequent correlations, but does not matter. +// +// The bit time is 99.1us (21 I/Q pairs). The receiver skips the first 5 samples +// and averages the next (most stable) 8 samples. The final 8 samples are dropped +// also. +// +// The demedulated should be alligned to the bit periode by the caller. This is +// done in rx_bit_as_reader and rx_ack_as_reader. +static inline bool rx_bit_as_reader() { + int32_t cq = 0; + int32_t ci = 0; + + // skip first 5 I/Q pairs + for(size_t i = 0; i<5; ++i) { + (int8_t)rx_byte_from_fpga(); + (int8_t)rx_byte_from_fpga(); + } + + // sample next 8 I/Q pairs + for(size_t i = 0; i<8; ++i) { + cq += (int8_t)rx_byte_from_fpga(); + ci += (int8_t)rx_byte_from_fpga(); + } + + // calculate power + int32_t power = (MAX(ABS(ci), ABS(cq)) + (MIN(ABS(ci), ABS(cq)) >> 1)); + + // compare average (power / 8) to threshold + return ((power >> 3) > INPUT_THRESHOLD); +} + +//----------------------------------------------------------------------------- +// Modulation (Reader) +// +// I've tried to modulate the Legic specific pause-puls using ssc and the default +// ssc clock of 105.4 kHz (bit periode of 9.4us) - previous commit. However, +// the timing was not precise enough. By increasing the ssc clock this could +// be circumvented, but the adventage over bitbang would be little. +//----------------------------------------------------------------------------- + +static inline void tx_bit_as_reader(bool bit) { + // insert pause + LOW(GPIO_SSC_DOUT); + last_frame_end += RWD_TIME_PAUSE; + while(GET_TICKS < last_frame_end) { }; + HIGH(GPIO_SSC_DOUT); + + // return to high, wait for bit periode to end + last_frame_end += (bit ? RWD_TIME_1 : RWD_TIME_0) - RWD_TIME_PAUSE; + while(GET_TICKS < last_frame_end) { }; +} + +//----------------------------------------------------------------------------- +// Frame Handling (Reader) +// +// The LEGIC RF protocol from card to reader does not include explicit frame +// start/stop information or length information. The reader must know beforehand +// how many bits it wants to receive. +// Notably: a card sending a stream of 0-bits is indistinguishable from no card +// present. +//----------------------------------------------------------------------------- + +static void tx_frame_as_reader(uint32_t frame, uint8_t len) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); + + // wait for next tx timeslot + last_frame_end += RWD_FRAME_WAIT; + while(GET_TICKS < last_frame_end) { }; + + // transmit frame, MSB first + for(uint8_t i = 0; i < len; ++i) { + bool bit = (frame >> i) & 0x01; + tx_bit_as_reader(bit ^ legic_prng_get_bit()); + legic_prng_forward(1); + }; + + // add pause to mark end of the frame + LOW(GPIO_SSC_DOUT); + last_frame_end += RWD_TIME_PAUSE; + while(GET_TICKS < last_frame_end) { }; + HIGH(GPIO_SSC_DOUT); +} + +static uint32_t rx_frame_as_reader(uint8_t len) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR + | FPGA_HF_READER_RX_XCORR_848_KHZ + | FPGA_HF_READER_RX_XCORR_QUARTER_FREQ); + + // hold sampling until card is expected to respond + last_frame_end += TAG_FRAME_WAIT; + while(GET_TICKS < last_frame_end) { }; + + uint32_t frame = 0; + for(uint8_t i = 0; i < len; i++) { + frame |= (rx_bit_as_reader() ^ legic_prng_get_bit()) << i; + legic_prng_forward(1); + + // rx_bit_as_reader runs only 95us, resync to TAG_BIT_PERIOD + last_frame_end += TAG_BIT_PERIOD; + while(GET_TICKS < last_frame_end) { }; + } + + return frame; +} + +static bool rx_ack_as_reader() { + // change fpga into rx mode + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR + | FPGA_HF_READER_RX_XCORR_848_KHZ + | FPGA_HF_READER_RX_XCORR_QUARTER_FREQ); + + // hold sampling until card is expected to respond + last_frame_end += TAG_FRAME_WAIT; + while(GET_TICKS < last_frame_end) { }; + + uint32_t ack = 0; + for(uint8_t i = 0; i < TAG_WRITE_TIMEOUT; ++i) { + // sample bit + ack = rx_bit_as_reader(); + legic_prng_forward(1); + + // rx_bit_as_reader runs only 95us, resync to TAG_BIT_PERIOD + last_frame_end += TAG_BIT_PERIOD; + while(GET_TICKS < last_frame_end) { }; + + // check if it was an ACK + if(ack) { + break; + } + } + + return ack; +} + +//----------------------------------------------------------------------------- +// Legic Reader +//----------------------------------------------------------------------------- + +int init_card(uint8_t cardtype, legic_card_select_t *p_card) { + p_card->tagtype = cardtype; + + switch(p_card->tagtype) { + case 0x0d: + p_card->cmdsize = 6; + p_card->addrsize = 5; + p_card->cardsize = 22; + break; + case 0x1d: + p_card->cmdsize = 9; + p_card->addrsize = 8; + p_card->cardsize = 256; + break; + case 0x3d: + p_card->cmdsize = 11; + p_card->addrsize = 10; + p_card->cardsize = 1024; + break; + default: + p_card->cmdsize = 0; + p_card->addrsize = 0; + p_card->cardsize = 0; + return 2; + } + return 0; +} + +static void init_reader(bool clear_mem) { + // configure FPGA + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR + | FPGA_HF_READER_RX_XCORR_848_KHZ + | FPGA_HF_READER_RX_XCORR_QUARTER_FREQ); + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + LED_D_ON(); + + // configure SSC with defaults + FpgaSetupSsc(); + + // re-claim GPIO_SSC_DOUT as GPIO and enable output + AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; + AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; + HIGH(GPIO_SSC_DOUT); + + // init crc calculator + crc_init(&legic_crc, 4, 0x19 >> 1, 0x05, 0); + + // start us timer + StartTicks(); +} + +// Setup reader to card connection +// +// The setup consists of a three way handshake: +// - Transmit initialisation vector 7 bits +// - Receive card type 6 bits +// - Acknowledge frame 6 bits +static uint32_t setup_phase_reader(uint8_t iv) { + // init coordination timestamp + last_frame_end = GET_TICKS; + + // Switch on carrier and let the card charge for 5ms. + last_frame_end += 7500; + while(GET_TICKS < last_frame_end) { }; + + legic_prng_init(0); + tx_frame_as_reader(iv, 7); + + // configure iv + legic_prng_init(iv); + legic_prng_forward(2); + + // receive card type + int32_t card_type = rx_frame_as_reader(6); + legic_prng_forward(3); + + // send obsfuscated acknowledgment frame + switch (card_type) { + case 0x0D: + tx_frame_as_reader(0x19, 6); // MIM22 | READCMD = 0x18 | 0x01 + break; + case 0x1D: + case 0x3D: + tx_frame_as_reader(0x39, 6); // MIM256 | READCMD = 0x38 | 0x01 + break; + } + + return card_type; +} + +static uint8_t calc_crc4(uint16_t cmd, uint8_t cmd_sz, uint8_t value) { + crc_clear(&legic_crc); + crc_update(&legic_crc, (value << cmd_sz) | cmd, 8 + cmd_sz); + return crc_finish(&legic_crc); +} + +static int16_t read_byte(uint16_t index, uint8_t cmd_sz) { + uint16_t cmd = (index << 1) | LEGIC_READ; + + // read one byte + LED_B_ON(); + legic_prng_forward(2); + tx_frame_as_reader(cmd, cmd_sz); + legic_prng_forward(2); + uint32_t frame = rx_frame_as_reader(12); + LED_B_OFF(); + + // split frame into data and crc + uint8_t byte = BYTEx(frame, 0); + uint8_t crc = BYTEx(frame, 1); + + // check received against calculated crc + uint8_t calc_crc = calc_crc4(cmd, cmd_sz, byte); + if(calc_crc != crc) { + Dbprintf("!!! crc mismatch: %x != %x !!!", calc_crc, crc); + return -1; + } + + legic_prng_forward(1); + + return byte; +} + +// Transmit write command, wait until (3.6ms) the tag sends back an unencrypted +// ACK ('1' bit) and forward the prng time based. +bool write_byte(uint16_t index, uint8_t byte, uint8_t addr_sz) { + uint32_t cmd = index << 1 | LEGIC_WRITE; // prepare command + uint8_t crc = calc_crc4(cmd, addr_sz + 1, byte); // calculate crc + cmd |= byte << (addr_sz + 1); // append value + cmd |= (crc & 0xF) << (addr_sz + 1 + 8); // and crc + + // send write command + LED_C_ON(); + legic_prng_forward(2); + tx_frame_as_reader(cmd, addr_sz + 1 + 8 + 4); // sz = addr_sz + cmd + data + crc + legic_prng_forward(3); + LED_C_OFF(); + + // wait for ack + return rx_ack_as_reader(); +} + +//----------------------------------------------------------------------------- +// Command Line Interface +// +// Only this functions are public / called from appmain.c +//----------------------------------------------------------------------------- +void LegicRfReader(int offset, int bytes) { + uint8_t *BigBuf = BigBuf_get_addr(); + memset(BigBuf, 0, 1024); + + // configure ARM and FPGA + init_reader(false); + + // establish shared secret and detect card type + DbpString("Reading card ..."); + uint8_t card_type = setup_phase_reader(SESSION_IV); + if(init_card(card_type, &card) != 0) { + Dbprintf("No or unknown card found, aborting"); + goto OUT; + } + + // if no argument is specified create full dump + if(bytes == -1) { + bytes = card.cardsize; + } + + // do not read beyond card memory + if(bytes + offset > card.cardsize) { + bytes = card.cardsize - offset; + } + + for(uint16_t i = 0; i < bytes; ++i) { + int16_t byte = read_byte(offset + i, card.cmdsize); + if(byte == -1) { + Dbprintf("operation failed @ 0x%03.3x", bytes); + goto OUT; + } + BigBuf[i] = byte; + } + + // OK + Dbprintf("Card (MIM %i) read, use 'hf legic decode' or", card.cardsize); + Dbprintf("'data hexsamples %d' to view results", (bytes+7) & ~7); + +OUT: + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_B_OFF(); + LED_C_OFF(); + LED_D_OFF(); + StopTicks(); +} + +void LegicRfWriter(int bytes, int offset) { + uint8_t *BigBuf = BigBuf_get_addr(); + + // configure ARM and FPGA + init_reader(false); + + // uid is not writeable + if(offset <= WRITE_LOWERLIMIT) { + goto OUT; + } + + // establish shared secret and detect card type + Dbprintf("Writing 0x%02.2x - 0x%02.2x ...", offset, offset+bytes); + uint8_t card_type = setup_phase_reader(SESSION_IV); + if(init_card(card_type, &card) != 0) { + Dbprintf("No or unknown card found, aborting"); + goto OUT; + } + + // do not write beyond card memory + if(bytes + offset > card.cardsize) { + bytes = card.cardsize - offset; + } + + // write in reverse order, only then is DCF (decremental field) writable + while(bytes-- > 0 && !BUTTON_PRESS()) { + if(!write_byte(bytes + offset, BigBuf[bytes], card.addrsize)) { + Dbprintf("operation failed @ 0x%03.3x", bytes); + goto OUT; + } + } + + // OK + DbpString("Write successful"); + +OUT: + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_B_OFF(); + LED_C_OFF(); + LED_D_OFF(); + StopTicks(); +} + +//----------------------------------------------------------------------------- +// Legic Simulator +//----------------------------------------------------------------------------- + static void setup_timer(void) { /* Set up Timer 1 to use for measuring time between pulses. Since we're bit-banging @@ -62,22 +519,6 @@ static void setup_timer(void) prng_timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; } -/* At TIMER_CLOCK3 (MCK/32) */ -#define RWD_TIME_1 150 /* RWD_TIME_PAUSE off, 80us on = 100us */ -#define RWD_TIME_0 90 /* RWD_TIME_PAUSE off, 40us on = 60us */ -#define RWD_TIME_PAUSE 30 /* 20us */ -#define RWD_TIME_FUZZ 20 /* rather generous 13us, since the peak detector + hysteresis fuzz quite a bit */ -#define TAG_TIME_BIT 150 /* 100us for every bit */ -#define TAG_TIME_WAIT 490 /* time from RWD frame end to tag frame start, experimentally determined */ - -#define SIM_DIVISOR 586 /* prng_time/SIM_DIVISOR count prng needs to be forwared */ -#define SIM_SHIFT 900 /* prng_time+SIM_SHIFT shift of delayed start */ - -#define SESSION_IV 0x55 -#define OFFSET_LOG 1024 - -#define FUZZ_EQUAL(value, target, fuzz) ((value) > ((target)-(fuzz)) && (value) < ((target)+(fuzz))) - /* Generate Keystream */ static uint32_t get_key_stream(int skip, int count) { @@ -138,11 +579,11 @@ static void frame_send_tag(uint16_t response, int bits, int crypt) } /* Wait for the frame start */ - while(timer->TC_CV < (TAG_TIME_WAIT - 30)) ; + while(timer->TC_CV < (TAG_FRAME_WAIT - 30)) ; int i; for(i=0; iTC_CV + TAG_TIME_BIT; + int nextbit = timer->TC_CV + TAG_BIT_PERIOD; int bit = response & 1; response = response >> 1; if(bit) { @@ -155,126 +596,6 @@ static void frame_send_tag(uint16_t response, int bits, int crypt) AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; } -/* Send a frame in reader mode, the FPGA must have been set up by - * LegicRfReader - */ -static void frame_send_rwd(uint32_t data, int bits) -{ - /* Start clock */ - timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - while(timer->TC_CV > 1) ; /* Wait till the clock has reset */ - - int i; - for(i=0; iTC_CV; - int pause_end = starttime + RWD_TIME_PAUSE, bit_end; - int bit = data & 1; - data = data >> 1; - - if(bit ^ legic_prng_get_bit()) { - bit_end = starttime + RWD_TIME_1; - } else { - bit_end = starttime + RWD_TIME_0; - } - - /* RWD_TIME_PAUSE time off, then some time on, so that the complete bit time is - * RWD_TIME_x, where x is the bit to be transmitted */ - AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; - while(timer->TC_CV < pause_end) ; - AT91C_BASE_PIOA->PIO_SODR = GPIO_SSC_DOUT; - legic_prng_forward(1); /* bit duration is longest. use this time to forward the lfsr */ - - while(timer->TC_CV < bit_end) ; - } - - { - /* One final pause to mark the end of the frame */ - int pause_end = timer->TC_CV + RWD_TIME_PAUSE; - AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; - while(timer->TC_CV < pause_end) ; - AT91C_BASE_PIOA->PIO_SODR = GPIO_SSC_DOUT; - } - - /* Reset the timer, to measure time until the start of the tag frame */ - timer->TC_CCR = AT91C_TC_SWTRG; - while(timer->TC_CV > 1) ; /* Wait till the clock has reset */ -} - -/* Receive a frame from the card in reader emulation mode, the FPGA and - * timer must have been set up by LegicRfReader and frame_send_rwd. - * - * The LEGIC RF protocol from card to reader does not include explicit - * frame start/stop information or length information. The reader must - * know beforehand how many bits it wants to receive. (Notably: a card - * sending a stream of 0-bits is indistinguishable from no card present.) - * - * Receive methodology: There is a fancy correlator in hi_read_rx_xcorr, but - * I'm not smart enough to use it. Instead I have patched hi_read_tx to output - * the ADC signal with hysteresis on SSP_DIN. Bit-bang that signal and look - * for edges. Count the edges in each bit interval. If they are approximately - * 0 this was a 0-bit, if they are approximately equal to the number of edges - * expected for a 212kHz subcarrier, this was a 1-bit. For timing we use the - * timer that's still running from frame_send_rwd in order to get a synchronization - * with the frame that we just sent. - * - * FIXME: Because we're relying on the hysteresis to just do the right thing - * the range is severely reduced (and you'll probably also need a good antenna). - * So this should be fixed some time in the future for a proper receiver. - */ -static void frame_receive_rwd(struct legic_frame * const f, int bits, int crypt) -{ - uint32_t the_bit = 1; /* Use a bitmask to save on shifts */ - uint32_t data=0; - int i, old_level=0, edges=0; - int next_bit_at = TAG_TIME_WAIT; - - if(bits > 32) { - bits = 32; - } - - AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_DIN; - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DIN; - - /* we have some time now, precompute the cipher - * since we cannot compute it on the fly while reading */ - legic_prng_forward(2); - - if(crypt) - { - for(i=0; iTC_CV < next_bit_at) ; - - next_bit_at += TAG_TIME_BIT; - - for(i=0; iTC_CV < next_bit_at) { - int level = (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN); - if(level != old_level) - edges++; - old_level = level; - } - next_bit_at += TAG_TIME_BIT; - - if(edges > 20 && edges < 60) { /* expected are 42 edges */ - data ^= the_bit; - } - the_bit <<= 1; - } - - f->data = data; - f->bits = bits; - - /* Reset the timer, to synchronize the next frame */ - timer->TC_CCR = AT91C_TC_SWTRG; - while(timer->TC_CV > 1) ; /* Wait till the clock has reset */ -} - static void frame_append_bit(struct legic_frame * const f, int bit) { if(f->bits >= 31) { @@ -290,250 +611,6 @@ static void frame_clean(struct legic_frame * const f) f->bits = 0; } -static uint32_t perform_setup_phase_rwd(int iv) -{ - - /* Switch on carrier and let the tag charge for 1ms */ - AT91C_BASE_PIOA->PIO_SODR = GPIO_SSC_DOUT; - SpinDelay(1); - - legic_prng_init(0); /* no keystream yet */ - frame_send_rwd(iv, 7); - legic_prng_init(iv); - - frame_clean(¤t_frame); - frame_receive_rwd(¤t_frame, 6, 1); - legic_prng_forward(1); /* we wait anyways */ - while(timer->TC_CV < 387) ; /* ~ 258us */ - frame_send_rwd(0x19, 6); - - return current_frame.data; -} - -static void LegicCommonInit(void) { - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); - - /* Bitbang the transmitter */ - AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; - - setup_timer(); - - crc_init(&legic_crc, 4, 0x19 >> 1, 0x5, 0); -} - -static void switch_off_tag_rwd(void) -{ - /* Switch off carrier, make sure tag is reset */ - AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; - SpinDelay(10); - - WDT_HIT(); -} -/* calculate crc for a legic command */ -static int LegicCRC(int byte_index, int value, int cmd_sz) { - crc_clear(&legic_crc); - crc_update(&legic_crc, 1, 1); /* CMD_READ */ - crc_update(&legic_crc, byte_index, cmd_sz-1); - crc_update(&legic_crc, value, 8); - return crc_finish(&legic_crc); -} - -int legic_read_byte(int byte_index, int cmd_sz) { - int byte; - - legic_prng_forward(4); /* we wait anyways */ - while(timer->TC_CV < 387) ; /* ~ 258us + 100us*delay */ - - frame_send_rwd(1 | (byte_index << 1), cmd_sz); - frame_clean(¤t_frame); - - frame_receive_rwd(¤t_frame, 12, 1); - - byte = current_frame.data & 0xff; - if( LegicCRC(byte_index, byte, cmd_sz) != (current_frame.data >> 8) ) { - Dbprintf("!!! crc mismatch: expected %x but got %x !!!", - LegicCRC(byte_index, current_frame.data & 0xff, cmd_sz), current_frame.data >> 8); - return -1; - } - - return byte; -} - -/* legic_write_byte() is not included, however it's trivial to implement - * and here are some hints on what remains to be done: - * - * * assemble a write_cmd_frame with crc and send it - * * wait until the tag sends back an ACK ('1' bit unencrypted) - * * forward the prng based on the timing - */ -int legic_write_byte(int byte, int addr, int addr_sz) { - //do not write UID, CRC, DCF - if(addr <= 0x06) { - return 0; - } - - //== send write command ============================== - crc_clear(&legic_crc); - crc_update(&legic_crc, 0, 1); /* CMD_WRITE */ - crc_update(&legic_crc, addr, addr_sz); - crc_update(&legic_crc, byte, 8); - - uint32_t crc = crc_finish(&legic_crc); - uint32_t cmd = ((crc <<(addr_sz+1+8)) //CRC - |(byte <<(addr_sz+1)) //Data - |(addr <<1) //Address - |(0x00 <<0)); //CMD = W - uint32_t cmd_sz = addr_sz+1+8+4; //crc+data+cmd - - legic_prng_forward(2); /* we wait anyways */ - while(timer->TC_CV < 387) {}; /* ~ 258us */ - frame_send_rwd(cmd, cmd_sz); - - //== wait for ack ==================================== - int t, old_level=0, edges=0; - int next_bit_at =0; - while(timer->TC_CV < 387) ; /* ~ 258us */ - for(t=0; t<80; t++) { - edges = 0; - next_bit_at += TAG_TIME_BIT; - while(timer->TC_CV < next_bit_at) { - int level = (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN); - if(level != old_level) { - edges++; - } - old_level = level; - } - if(edges > 20 && edges < 60) { /* expected are 42 edges */ - int t = timer->TC_CV; - int c = t/TAG_TIME_BIT; - timer->TC_CCR = AT91C_TC_SWTRG; - while(timer->TC_CV > 1) ; /* Wait till the clock has reset */ - legic_prng_forward(c); - return 0; - } - } - timer->TC_CCR = AT91C_TC_SWTRG; - while(timer->TC_CV > 1) {}; /* Wait till the clock has reset */ - return -1; -} - -int LegicRfReader(int offset, int bytes) { - int byte_index=0, cmd_sz=0, card_sz=0; - - LegicCommonInit(); - - uint8_t *BigBuf = BigBuf_get_addr(); - memset(BigBuf, 0, 1024); - - DbpString("setting up legic card"); - uint32_t tag_type = perform_setup_phase_rwd(SESSION_IV); - switch_off_tag_rwd(); //we lose to mutch time with dprintf - switch(tag_type) { - case 0x1d: - DbpString("MIM 256 card found, reading card ..."); - cmd_sz = 9; - card_sz = 256; - break; - case 0x3d: - DbpString("MIM 1024 card found, reading card ..."); - cmd_sz = 11; - card_sz = 1024; - break; - default: - Dbprintf("Unknown card format: %x",tag_type); - return -1; - } - if(bytes == -1) { - bytes = card_sz; - } - if(bytes+offset >= card_sz) { - bytes = card_sz-offset; - } - - perform_setup_phase_rwd(SESSION_IV); - - LED_B_ON(); - while(byte_index < bytes) { - int r = legic_read_byte(byte_index+offset, cmd_sz); - if(r == -1 ||BUTTON_PRESS()) { - DbpString("operation aborted"); - switch_off_tag_rwd(); - LED_B_OFF(); - LED_C_OFF(); - return -1; - } - BigBuf[byte_index] = r; - WDT_HIT(); - byte_index++; - if(byte_index & 0x10) LED_C_ON(); else LED_C_OFF(); - } - LED_B_OFF(); - LED_C_OFF(); - switch_off_tag_rwd(); - Dbprintf("Card read, use 'hf legic decode' or"); - Dbprintf("'data hexsamples %d' to view results", (bytes+7) & ~7); - return 0; -} - -void LegicRfWriter(int bytes, int offset) { - int byte_index=0, addr_sz=0; - uint8_t *BigBuf = BigBuf_get_addr(); - - LegicCommonInit(); - - DbpString("setting up legic card"); - uint32_t tag_type = perform_setup_phase_rwd(SESSION_IV); - switch_off_tag_rwd(); - switch(tag_type) { - case 0x1d: - if(offset+bytes > 0x100) { - Dbprintf("Error: can not write to 0x%03.3x on MIM 256", offset+bytes); - return; - } - addr_sz = 8; - Dbprintf("MIM 256 card found, writing 0x%02.2x - 0x%02.2x ...", offset, offset+bytes); - break; - case 0x3d: - if(offset+bytes > 0x400) { - Dbprintf("Error: can not write to 0x%03.3x on MIM 1024", offset+bytes); - return; - } - addr_sz = 10; - Dbprintf("MIM 1024 card found, writing 0x%03.3x - 0x%03.3x ...", offset, offset+bytes); - break; - default: - Dbprintf("No or unknown card found, aborting"); - return; - } - - LED_B_ON(); - perform_setup_phase_rwd(SESSION_IV); - legic_prng_forward(2); - while(byte_index < bytes) { - int r = legic_write_byte(BigBuf[byte_index+offset], byte_index+offset, addr_sz); - if((r != 0) || BUTTON_PRESS()) { - Dbprintf("operation aborted @ 0x%03.3x", byte_index); - switch_off_tag_rwd(); - LED_B_OFF(); - LED_C_OFF(); - return; - } - WDT_HIT(); - byte_index++; - if(byte_index & 0x10) LED_C_ON(); else LED_C_OFF(); - } - LED_B_OFF(); - LED_C_OFF(); - DbpString("write successful"); -} - -int timestamp; - /* Handle (whether to respond) a frame in tag mode */ static void frame_handle_tag(struct legic_frame const * const f) { @@ -588,7 +665,7 @@ static void frame_handle_tag(struct legic_frame const * const f) int key = get_key_stream(-1, 11); //legic_phase_drift, 11); int addr = f->data ^ key; addr = addr >> 1; int data = BigBuf[addr]; - int hash = LegicCRC(addr, data, 11) << 8; + int hash = calc_crc4(addr, data, 11) << 8; BigBuf[OFFSET_LOG+legic_read_count] = (uint8_t)addr; legic_read_count++; diff --git a/armsrc/legicrf.h b/armsrc/legicrf.h index 57ab7e6d..46459856 100644 --- a/armsrc/legicrf.h +++ b/armsrc/legicrf.h @@ -12,7 +12,7 @@ #define __LEGICRF_H extern void LegicRfSimulate(int phase, int frame, int reqresp); -extern int LegicRfReader(int bytes, int offset); +extern void LegicRfReader(int bytes, int offset); extern void LegicRfWriter(int bytes, int offset); #endif /* __LEGICRF_H */ diff --git a/fpga/hi_read_tx.v b/fpga/hi_read_tx.v index fc309cde..756683cd 100644 --- a/fpga/hi_read_tx.v +++ b/fpga/hi_read_tx.v @@ -71,21 +71,8 @@ always @(negedge ssp_clk) assign ssp_frame = (hi_byte_div == 3'b000); -// Implement a hysteresis to give out the received signal on -// ssp_din. Sample at fc. -assign adc_clk = ck_1356meg; +assign ssp_din = 1'b0; -// ADC data appears on the rising edge, so sample it on the falling edge -reg after_hysteresis; -always @(negedge adc_clk) -begin - if(& adc_d[7:0]) after_hysteresis <= 1'b1; - else if(~(| adc_d[7:0])) after_hysteresis <= 1'b0; -end +assign dbg = ssp_frame; - -assign ssp_din = after_hysteresis; - -assign dbg = ssp_din; - -endmodule +endmodule \ No newline at end of file diff --git a/include/legic.h b/include/legic.h new file mode 100644 index 00000000..246af0e8 --- /dev/null +++ b/include/legic.h @@ -0,0 +1,27 @@ +//----------------------------------------------------------------------------- +// (c) 2016 Iceman +// +// 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. +//----------------------------------------------------------------------------- +// LEGIC type prototyping +//----------------------------------------------------------------------------- + +#ifndef _LEGIC_H_ +#define _LEGIC_H_ + +#include "common.h" + +//----------------------------------------------------------------------------- +// LEGIC +//----------------------------------------------------------------------------- +typedef struct { + uint8_t uid[4]; + uint32_t tagtype; + uint8_t cmdsize; + uint8_t addrsize; + uint16_t cardsize; +} legic_card_select_t; + +#endif // _LEGIC_H_ From f6842317960fbf41cf981171789daebec8dbf803 Mon Sep 17 00:00:00 2001 From: AntiCat Date: Tue, 21 Aug 2018 05:08:06 +0200 Subject: [PATCH 248/310] Legic: fixed write (#655) Due to an oversight the bytes to be written were fetched from the wrong location. This is fixed now. --- armsrc/legicrf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/armsrc/legicrf.c b/armsrc/legicrf.c index a611c741..2a236b6f 100644 --- a/armsrc/legicrf.c +++ b/armsrc/legicrf.c @@ -476,7 +476,7 @@ void LegicRfWriter(int bytes, int offset) { // write in reverse order, only then is DCF (decremental field) writable while(bytes-- > 0 && !BUTTON_PRESS()) { - if(!write_byte(bytes + offset, BigBuf[bytes], card.addrsize)) { + if(!write_byte(bytes + offset, BigBuf[bytes + offset], card.addrsize)) { Dbprintf("operation failed @ 0x%03.3x", bytes); goto OUT; } From 43591e6464af07466ffd87fcb970527ba748253a Mon Sep 17 00:00:00 2001 From: marshmellow42 Date: Mon, 20 Aug 2018 23:08:49 -0400 Subject: [PATCH 249/310] Add Smartcard functions (RDV4.0) (#646) * allow common makefile options-defines * remove non-existing file references * Uncomment lcd option (still) not enabled by default use Makefile_Enabled_Options.common to enable lcd if desired. * Add Smartcard Functions * add smartcard to menu + make get atr work sc is now functioning as far as my limited knowledge takes me * sc cleanup - add init to all sc commands... because cmds won't work until the first init happens. (multiple inits don't appear to affect it negatively) * default options to exclude Smartcard for main repo * update changelog --- CHANGELOG.md | 1 + armsrc/Makefile | 9 +- armsrc/appmain.c | 38 ++- armsrc/i2c.c | 720 ++++++++++++++++++++++++++++++++++++++++++ armsrc/i2c.h | 58 ++++ client/Makefile | 10 +- client/cmdhf.c | 6 + client/cmdmain.c | 7 +- client/cmdsmartcard.c | 707 +++++++++++++++++++++++++++++++++++++++++ client/cmdsmartcard.h | 39 +++ common/lfdemod.c | 4 +- common/protocols.h | 34 +- include/smartcard.h | 29 ++ include/usb_cmd.h | 21 +- 14 files changed, 1661 insertions(+), 22 deletions(-) create mode 100644 armsrc/i2c.c create mode 100644 armsrc/i2c.h create mode 100644 client/cmdsmartcard.c create mode 100644 client/cmdsmartcard.h create mode 100644 include/smartcard.h diff --git a/CHANGELOG.md b/CHANGELOG.md index a7e2e045..da6463e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Changed driver file proxmark3.inf to support both old and new Product/Vendor IDs (piwi) ### Added +- Added `sc` smartcard (contact card) commands - reader, info, raw, upgrade, setclock, list (hardware version RDV4.0 only) must turn option on in makefile options (Willok, Iceman, marshmellow) - Added a bitbang mode to `lf cmdread` if delay is 0 the cmd bits turn off and on the antenna with 0 and 1 respectively (marshmellow) - Added PAC/Stanley detection to lf search (marshmellow) - Added lf pac demod and lf pac read - extracts the raw blocks from a PAC/Stanley tag (marshmellow) diff --git a/armsrc/Makefile b/armsrc/Makefile index f0a0c0ff..d4b13c6b 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -15,18 +15,22 @@ APP_CFLAGS = -DON_DEVICE \ include ../common/Makefile_Enabled_Options.common -ifneq (,$(findstring LCD,$(APP_CFLAGS))) +ifneq (,$(findstring WITH_LCD,$(APP_CFLAGS))) SRC_LCD = fonts.c LCD.c else SRC_LCD = endif -#SRC_LCD = fonts.c LCD.c SRC_LF = lfops.c hitag2.c hitagS.c lfsampling.c pcf7931.c lfdemod.c protocols.c SRC_ISO15693 = iso15693.c iso15693tools.c SRC_ISO14443a = epa.c iso14443a.c mifareutil.c mifarecmd.c mifaresniff.c mifaresim.c SRC_ISO14443b = iso14443b.c SRC_CRAPTO1 = crypto1.c des.c SRC_CRC = iso14443crc.c crc.c crc16.c crc32.c parity.c +ifneq (,$(findstring WITH_SMARTCARD,$(APP_CFLAGS))) + SRC_SMARTCARD = i2c.c +else + SRC_SMARTCARD = +endif #the FPGA bitstream files. Note: order matters! FPGA_BITSTREAMS = fpga_lf.bit fpga_hf.bit @@ -44,6 +48,7 @@ THUMBSRC = start.c \ $(SRC_ISO15693) \ $(SRC_LF) \ $(SRC_ZLIB) \ + $(SRC_SMARTCARD) \ appmain.c \ printf.c \ util.c \ diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 27f43b3f..4034788a 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -30,6 +30,10 @@ #ifdef WITH_LCD #include "LCD.h" #endif +#ifdef WITH_SMARTCARD + #include "i2c.h" +#endif + // Craig Young - 14a stand-alone code #ifdef WITH_ISO14443a @@ -357,12 +361,15 @@ void SendStatus(void) { BigBuf_print_status(); Fpga_print_status(); +#ifdef WITH_SMARTCARD + I2C_print_status(); +#endif printConfig(); //LF Sampling config printUSBSpeed(); Dbprintf("Various"); - Dbprintf(" MF_DBGLEVEL......%d", MF_DBGLEVEL); - Dbprintf(" ToSendMax........%d",ToSendMax); - Dbprintf(" ToSendBit........%d",ToSendBit); + Dbprintf(" MF_DBGLEVEL........%d", MF_DBGLEVEL); + Dbprintf(" ToSendMax..........%d", ToSendMax); + Dbprintf(" ToSendBit..........%d", ToSendBit); cmd_send(CMD_ACK,1,0,0,0,0); } @@ -1253,6 +1260,31 @@ void UsbPacketReceived(uint8_t *packet, int len) HfSnoop(c->arg[0], c->arg[1]); break; #endif +#ifdef WITH_SMARTCARD + case CMD_SMART_ATR: { + SmartCardAtr(); + break; + } + case CMD_SMART_SETCLOCK:{ + SmartCardSetClock(c->arg[0]); + break; + } + case CMD_SMART_RAW: { + SmartCardRaw(c->arg[0], c->arg[1], c->d.asBytes); + break; + } + case CMD_SMART_UPLOAD: { + // upload file from client + uint8_t *mem = BigBuf_get_addr(); + memcpy( mem + c->arg[0], c->d.asBytes, USB_CMD_DATA_SIZE); + cmd_send(CMD_ACK,1,0,0,0,0); + break; + } + case CMD_SMART_UPGRADE: { + SmartCardUpgrade(c->arg[0]); + break; + } +#endif case CMD_BUFF_CLEAR: BigBuf_Clear(); diff --git a/armsrc/i2c.c b/armsrc/i2c.c new file mode 100644 index 00000000..721b4b2e --- /dev/null +++ b/armsrc/i2c.c @@ -0,0 +1,720 @@ +//----------------------------------------------------------------------------- +// Willok, June 2018 +// Edits by Iceman, July 2018 +// +// 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. +//----------------------------------------------------------------------------- +// The main i2c code, for communications with smart card module +//----------------------------------------------------------------------------- +#include "i2c.h" +#include "mifareutil.h" //for mf_dbglevel +#include "string.h" //for memset memcmp + +// ¶¨ÒåÁ¬½ÓÒý½Å +#define GPIO_RST AT91C_PIO_PA1 +#define GPIO_SCL AT91C_PIO_PA5 +#define GPIO_SDA AT91C_PIO_PA7 + +#define SCL_H HIGH(GPIO_SCL) +#define SCL_L LOW(GPIO_SCL) +#define SDA_H HIGH(GPIO_SDA) +#define SDA_L LOW(GPIO_SDA) + +#define SCL_read (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SCL) +#define SDA_read (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SDA) + +#define I2C_ERROR "I2C_WaitAck Error" + +volatile unsigned long c; + +// Ö±½ÓʹÓÃÑ­»·À´ÑÓʱ£¬Ò»¸öÑ­»· 6 ÌõÖ¸Á48M£¬ Delay=1 ´ó¸ÅΪ 200kbps +// timer. +// I2CSpinDelayClk(4) = 12.31us +// I2CSpinDelayClk(1) = 3.07us +void __attribute__((optimize("O0"))) I2CSpinDelayClk(uint16_t delay) { + for (c = delay * 2; c; c--) {}; +} + +// ͨѶÑÓ³Ùº¯Êý communication delay function +#define I2C_DELAY_1CLK I2CSpinDelayClk(1) +#define I2C_DELAY_2CLK I2CSpinDelayClk(2) +#define I2C_DELAY_XCLK(x) I2CSpinDelayClk((x)) + + +#define ISO7618_MAX_FRAME 255 + +void I2C_init(void) { + // ÅäÖø´Î»Òý½Å£¬¹Ø±ÕÉÏÀ­£¬ÍÆÍìÊä³ö£¬Ä¬ÈÏ¸ß + // Configure reset pin, close up pull up, push-pull output, default high + AT91C_BASE_PIOA->PIO_PPUDR = GPIO_RST; + AT91C_BASE_PIOA->PIO_MDDR = GPIO_RST; + + // ÅäÖà I2C Òý½Å£¬¿ªÆôÉÏÀ­£¬¿ªÂ©Êä³ö + // Configure I2C pin, open up, open leakage + AT91C_BASE_PIOA->PIO_PPUER |= (GPIO_SCL | GPIO_SDA); // ´ò¿ªÉÏÀ­ Open up the pull up + AT91C_BASE_PIOA->PIO_MDER |= (GPIO_SCL | GPIO_SDA); + + // ĬÈÏÈý¸ùÏßÈ«²¿À­¸ß + // default three lines all pull up + AT91C_BASE_PIOA->PIO_SODR |= (GPIO_SCL | GPIO_SDA | GPIO_RST); + + // ÔÊÐíÊä³ö + // allow output + AT91C_BASE_PIOA->PIO_OER |= (GPIO_SCL | GPIO_SDA | GPIO_RST); + AT91C_BASE_PIOA->PIO_PER |= (GPIO_SCL | GPIO_SDA | GPIO_RST); +} + + +// ÉèÖø´Î»×´Ì¬ +// set the reset state +void I2C_SetResetStatus(uint8_t LineRST, uint8_t LineSCK, uint8_t LineSDA) { + if (LineRST) + HIGH(GPIO_RST); + else + LOW(GPIO_RST); + + if (LineSCK) + HIGH(GPIO_SCL); + else + LOW(GPIO_SCL); + + if (LineSDA) + HIGH(GPIO_SDA); + else + LOW(GPIO_SDA); +} + +// ¸´Î»½øÈëÖ÷³ÌÐò +// Reset the SIM_Adapter, then enter the main program +// Note: the SIM_Adapter will not enter the main program after power up. Please run this function before use SIM_Adapter. +void I2C_Reset_EnterMainProgram(void) { + I2C_SetResetStatus(0, 0, 0); // À­µÍ¸´Î»Ïß + SpinDelay(30); + I2C_SetResetStatus(1, 0, 0); // ½â³ý¸´Î» + SpinDelay(30); + I2C_SetResetStatus(1, 1, 1); // À­¸ßÊý¾ÝÏß + SpinDelay(10); +} + +// ¸´Î»½øÈëÒýµ¼Ä£Ê½ +// Reset the SIM_Adapter, then enter the bootloader program +// Reserve£ºFor firmware update. +void I2C_Reset_EnterBootloader(void) { + I2C_SetResetStatus(0, 1, 1); // À­µÍ¸´Î»Ïß + SpinDelay(100); + I2C_SetResetStatus(1, 1, 1); // ½â³ý¸´Î» + SpinDelay(10); +} + +// µÈ´ýʱÖÓ±ä¸ß +// Wait for the clock to go High. +bool WaitSCL_H_delay(uint32_t delay) { + while (delay--) { + if (SCL_read) { + return true; + } + I2C_DELAY_1CLK; + } + return false; +} + +// 5000 * 3.07us = 15350us. 15.35ms +bool WaitSCL_H(void) { + return WaitSCL_H_delay(5000); +} + +// Wait max 300ms or until SCL goes LOW. +// Which ever comes first +bool WaitSCL_L_300ms(void) { + volatile uint16_t delay = 300; + while ( delay-- ) { + // exit on SCL LOW + if (!SCL_read) + return true; + + SpinDelay(1); + } + return (delay == 0); +} + +bool I2C_Start(void) { + + I2C_DELAY_XCLK(4); + SDA_H; I2C_DELAY_1CLK; + SCL_H; + if (!WaitSCL_H()) return false; + + I2C_DELAY_2CLK; + + if (!SCL_read) return false; + if (!SDA_read) return false; + + SDA_L; I2C_DELAY_2CLK; + return true; +} + +bool I2C_WaitForSim() { + // variable delay here. + if (!WaitSCL_L_300ms()) + return false; + + // 8051 speaks with smart card. + // 1000*50*3.07 = 153.5ms + // 1byte transfer == 1ms + if (!WaitSCL_H_delay(2000*50) ) + return false; + + return true; +} + +// send i2c STOP +void I2C_Stop(void) { + SCL_L; I2C_DELAY_2CLK; + SDA_L; I2C_DELAY_2CLK; + SCL_H; I2C_DELAY_2CLK; + if (!WaitSCL_H()) return; + SDA_H; + I2C_DELAY_XCLK(8); +} + +// Send i2c ACK +void I2C_Ack(void) { + SCL_L; I2C_DELAY_2CLK; + SDA_L; I2C_DELAY_2CLK; + SCL_H; I2C_DELAY_2CLK; + SCL_L; I2C_DELAY_2CLK; +} + +// Send i2c NACK +void I2C_NoAck(void) { + SCL_L; I2C_DELAY_2CLK; + SDA_H; I2C_DELAY_2CLK; + SCL_H; I2C_DELAY_2CLK; + SCL_L; I2C_DELAY_2CLK; +} + +bool I2C_WaitAck(void) { + SCL_L; I2C_DELAY_1CLK; + SDA_H; I2C_DELAY_1CLK; + SCL_H; + if (!WaitSCL_H()) + return false; + + I2C_DELAY_2CLK; + if (SDA_read) { + SCL_L; + return false; + } + SCL_L; + return true; +} + +void I2C_SendByte(uint8_t data) { + uint8_t i = 8; + + while (i--) { + SCL_L; I2C_DELAY_1CLK; + + if (data & 0x80) + SDA_H; + else + SDA_L; + + data <<= 1; + I2C_DELAY_1CLK; + + SCL_H; + if (!WaitSCL_H()) + return; + + I2C_DELAY_2CLK; + } + SCL_L; +} + +uint8_t I2C_ReadByte(void) { + uint8_t i = 8, b = 0; + + SDA_H; + while (i--) { + b <<= 1; + SCL_L; I2C_DELAY_2CLK; + SCL_H; + if (!WaitSCL_H()) + return 0; + + I2C_DELAY_2CLK; + if (SDA_read) + b |= 0x01; + } + SCL_L; + return b; +} + +// Sends one byte ( command to be written, SlaveDevice address) +bool I2C_WriteCmd(uint8_t device_cmd, uint8_t device_address) { + bool bBreak = true; + do { + if (!I2C_Start()) + return false; + //[C0] + I2C_SendByte(device_address & 0xFE); + if (!I2C_WaitAck()) + break; + + I2C_SendByte(device_cmd); + if (!I2C_WaitAck()) + break; + + bBreak = false; + } while (false); + + I2C_Stop(); + if (bBreak) { + if ( MF_DBGLEVEL > 3 ) DbpString(I2C_ERROR); + return false; + } + return true; +} + +// дÈë1×Ö½ÚÊý¾Ý £¨´ýдÈëÊý¾Ý£¬´ýдÈëµØÖ·£¬Æ÷¼þÀàÐÍ£© +// Sends 1 byte data (Data to be written, command to be written , SlaveDevice address ). +bool I2C_WriteByte(uint8_t data, uint8_t device_cmd, uint8_t device_address) { + bool bBreak = true; + do { + if (!I2C_Start()) + return false; + + I2C_SendByte(device_address & 0xFE); + if (!I2C_WaitAck()) + break; + + I2C_SendByte(device_cmd); + if (!I2C_WaitAck()) + break; + + I2C_SendByte(data); + if (!I2C_WaitAck()) + break; + + bBreak = false; + } while (false); + + I2C_Stop(); + if (bBreak) { + if ( MF_DBGLEVEL > 3 ) DbpString(I2C_ERROR); + return false; + } + return true; +} + +// дÈë1´®Êý¾Ý£¨´ýдÈëÊý×鵨ַ£¬´ýдÈ볤¶È£¬´ýдÈëµØÖ·£¬Æ÷¼þÀàÐÍ£© +//Sends a string of data (Array, length, command to be written , SlaveDevice address ). +// len = uint8 (max buffer to write 256bytes) +bool I2C_BufferWrite(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address) { + bool bBreak = true; + do { + if (!I2C_Start()) + return false; + + I2C_SendByte(device_address & 0xFE); + if (!I2C_WaitAck()) + break; + + I2C_SendByte(device_cmd); + if (!I2C_WaitAck()) + break; + + while (len) { + + I2C_SendByte(*data); + if (!I2C_WaitAck()) + break; + + len--; + data++; + } + + if (len == 0) + bBreak = false; + } while (false); + + I2C_Stop(); + if (bBreak) { + if ( MF_DBGLEVEL > 3 ) DbpString(I2C_ERROR); + return false; + } + return true; +} + +// ¶Á³ö1´®Êý¾Ý£¨´æ·Å¶Á³öÊý¾Ý£¬´ý¶Á³ö³¤¶È£¬´ø¶Á³öµØÖ·£¬Æ÷¼þÀàÐÍ£© +// read 1 strings of data (Data array, Readout length, command to be written , SlaveDevice address ). +// len = uint8 (max buffer to read 256bytes) +uint8_t I2C_BufferRead(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address) { + + if ( !data || len == 0 ) + return 0; + + // extra wait 500us (514us measured) + // 200us (xx measured) + SpinDelayUs(200); + bool bBreak = true; + uint8_t readcount = 0; + + do { + if (!I2C_Start()) + return 0; + + // 0xB0 / 0xC0 == i2c write + I2C_SendByte(device_address & 0xFE); + if (!I2C_WaitAck()) + break; + + I2C_SendByte(device_cmd); + if (!I2C_WaitAck()) + break; + + // 0xB1 / 0xC1 == i2c read + I2C_Start(); + I2C_SendByte(device_address | 1); + if (!I2C_WaitAck()) + break; + + bBreak = false; + } while (false); + + if (bBreak) { + I2C_Stop(); + if ( MF_DBGLEVEL > 3 ) DbpString(I2C_ERROR); + return 0; + } + + // reading + while (len) { + + *data = I2C_ReadByte(); + + len--; + + // ¶ÁÈ¡µÄµÚÒ»¸ö×Ö½ÚΪºóÐø³¤¶È + // The first byte in response is the message length + if (!readcount && (len > *data)) { + len = *data; + } else { + data++; + } + readcount++; + + // acknowledgements. After last byte send NACK. + if (len == 0) + I2C_NoAck(); + else + I2C_Ack(); + } + + I2C_Stop(); + // return bytecount - first byte (which is length byte) + return (readcount) ? --readcount : 0; +} + +uint8_t I2C_ReadFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address) { + //START, 0xB0, 0x00, 0x00, START, 0xB1, xx, yy, zz, ......, STOP + bool bBreak = true; + uint8_t readcount = 0; + + // sending + do { + if (!I2C_Start()) + return 0; + + // 0xB0 / 0xC0 i2c write + I2C_SendByte(device_address & 0xFE); + if (!I2C_WaitAck()) + break; + + // msb + I2C_SendByte(msb); + if (!I2C_WaitAck()) + break; + + // lsb + I2C_SendByte(lsb); + if (!I2C_WaitAck()) + break; + + // 0xB1 / 0xC1 i2c read + I2C_Start(); + I2C_SendByte(device_address | 1); + if (!I2C_WaitAck()) + break; + + bBreak = false; + } while (false); + + if (bBreak) { + I2C_Stop(); + if ( MF_DBGLEVEL > 3 ) DbpString(I2C_ERROR); + return 0; + } + + // reading + while (len) { + *data = I2C_ReadByte(); + + data++; + readcount++; + len--; + + // acknowledgements. After last byte send NACK. + if (len == 0) + I2C_NoAck(); + else + I2C_Ack(); + } + + I2C_Stop(); + return readcount; +} + +bool I2C_WriteFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address) { + //START, 0xB0, 0x00, 0x00, xx, yy, zz, ......, STOP + bool bBreak = true; + + do { + if (!I2C_Start()) + return false; + + // 0xB0 == i2c write + I2C_SendByte(device_address & 0xFE); + if (!I2C_WaitAck()) + break; + + // msb + I2C_SendByte(msb); + if (!I2C_WaitAck()) + break; + + // lsb + I2C_SendByte(lsb); + if (!I2C_WaitAck()) + break; + + while (len) { + I2C_SendByte(*data); + if (!I2C_WaitAck()) + break; + + len--; + data++; + } + + if (len == 0) + bBreak = false; + } while (false); + + I2C_Stop(); + if (bBreak) { + if ( MF_DBGLEVEL > 3 ) DbpString(I2C_ERROR); + return false; + } + return true; +} + +void I2C_print_status(void) { + DbpString("Smart card module (ISO 7816)"); + uint8_t resp[] = {0,0,0,0}; + I2C_init(); + I2C_Reset_EnterMainProgram(); + uint8_t len = I2C_BufferRead(resp, sizeof(resp), I2C_DEVICE_CMD_GETVERSION, I2C_DEVICE_ADDRESS_MAIN); + if ( len > 0 ) + Dbprintf(" version.................v%x.%02x", resp[0], resp[1]); + else + DbpString(" version.................FAILED"); +} + +bool GetATR(smart_card_atr_t *card_ptr) { + + // clear + if ( card_ptr ) { + card_ptr->atr_len = 0; + memset(card_ptr->atr, 0, sizeof(card_ptr->atr)); + } + + // Send ATR + // start [C0 01] stop start C1 len aa bb cc stop] + I2C_WriteCmd(I2C_DEVICE_CMD_GENERATE_ATR, I2C_DEVICE_ADDRESS_MAIN); + uint8_t cmd[1] = {1}; + LogTrace(cmd, 1, 0, 0, NULL, true); + + //wait for sim card to answer. + if (!I2C_WaitForSim()) + return false; + + // read answer + uint8_t len = I2C_BufferRead(card_ptr->atr, sizeof(card_ptr->atr), I2C_DEVICE_CMD_READ, I2C_DEVICE_ADDRESS_MAIN); + + if ( len == 0 ) + return false; + + // for some reason we only get first byte of atr, if that is so, send dummy command to retrieve the rest of the atr + if (len == 1) { + + uint8_t data[1] = {0}; + I2C_BufferWrite(data, len, I2C_DEVICE_CMD_SEND, I2C_DEVICE_ADDRESS_MAIN); + + if ( !I2C_WaitForSim() ) + return false; + + uint8_t len2 = I2C_BufferRead(card_ptr->atr + len, sizeof(card_ptr->atr) - len, I2C_DEVICE_CMD_READ, I2C_DEVICE_ADDRESS_MAIN); + len = len + len2; + } + + if ( card_ptr ) { + card_ptr->atr_len = len; + LogTrace(card_ptr->atr, card_ptr->atr_len, 0, 0, NULL, false); + } + + return true; +} + +void SmartCardAtr(void) { + smart_card_atr_t card; + LED_D_ON(); + clear_trace(); + set_tracing(true); + I2C_init(); + I2C_Reset_EnterMainProgram(); + bool isOK = GetATR( &card ); + cmd_send(CMD_ACK, isOK, sizeof(smart_card_atr_t), 0, &card, sizeof(smart_card_atr_t)); + set_tracing(false); + LEDsoff(); +} + +void SmartCardRaw( uint64_t arg0, uint64_t arg1, uint8_t *data ) { + + LED_D_ON(); + + uint8_t len = 0; + uint8_t *resp = BigBuf_malloc(ISO7618_MAX_FRAME); + smartcard_command_t flags = arg0; + + if ((flags & SC_CONNECT)) + clear_trace(); + + set_tracing(true); + + if ((flags & SC_CONNECT)) { + + I2C_init(); + I2C_Reset_EnterMainProgram(); + + if ( !(flags & SC_NO_SELECT) ) { + smart_card_atr_t card; + bool gotATR = GetATR( &card ); + //cmd_send(CMD_ACK, gotATR, sizeof(smart_card_atr_t), 0, &card, sizeof(smart_card_atr_t)); + if ( !gotATR ) + goto OUT; + } + } + + if ((flags & SC_RAW)) { + + LogTrace(data, arg1, 0, 0, NULL, true); + + // Send raw bytes + // asBytes = A0 A4 00 00 02 + // arg1 = len 5 + I2C_BufferWrite(data, arg1, I2C_DEVICE_CMD_SEND, I2C_DEVICE_ADDRESS_MAIN); + + if ( !I2C_WaitForSim() ) + goto OUT; + + len = I2C_BufferRead(resp, ISO7618_MAX_FRAME, I2C_DEVICE_CMD_READ, I2C_DEVICE_ADDRESS_MAIN); + LogTrace(resp, len, 0, 0, NULL, false); + } +OUT: + cmd_send(CMD_ACK, len, 0, 0, resp, len); + set_tracing(false); + LEDsoff(); +} + +void SmartCardUpgrade(uint64_t arg0) { + + LED_C_ON(); + + #define I2C_BLOCK_SIZE 128 + // write. Sector0, with 11,22,33,44 + // erase is 128bytes, and takes 50ms to execute + + I2C_init(); + I2C_Reset_EnterBootloader(); + + bool isOK = true; + uint8_t res = 0; + uint16_t length = arg0; + uint16_t pos = 0; + uint8_t *fwdata = BigBuf_get_addr(); + uint8_t *verfiydata = BigBuf_malloc(I2C_BLOCK_SIZE); + + while (length) { + + uint8_t msb = (pos >> 8) & 0xFF; + uint8_t lsb = pos & 0xFF; + + Dbprintf("FW %02X%02X", msb, lsb); + + size_t size = MIN(I2C_BLOCK_SIZE, length); + + // write + res = I2C_WriteFW(fwdata+pos, size, msb, lsb, I2C_DEVICE_ADDRESS_BOOT); + if ( !res ) { + DbpString("Writing failed"); + isOK = false; + break; + } + + // writing takes time. + SpinDelay(50); + + // read + res = I2C_ReadFW(verfiydata, size, msb, lsb, I2C_DEVICE_ADDRESS_BOOT); + if ( res == 0) { + DbpString("Reading back failed"); + isOK = false; + break; + } + + // cmp + if ( 0 != memcmp(fwdata+pos, verfiydata, size)) { + DbpString("not equal data"); + isOK = false; + break; + } + + length -= size; + pos += size; + } + cmd_send(CMD_ACK, isOK, pos, 0, 0, 0); + LED_C_OFF(); +} + +// unfinished (or not needed?) +//void SmartCardSetBaud(uint64_t arg0) { +//} + +void SmartCardSetClock(uint64_t arg0) { + LED_D_ON(); + set_tracing(true); + I2C_init(); + I2C_Reset_EnterMainProgram(); + + // Send SIM CLC + // start [C0 05 xx] stop + I2C_WriteByte(arg0, I2C_DEVICE_CMD_SIM_CLC, I2C_DEVICE_ADDRESS_MAIN); + + cmd_send(CMD_ACK, 1, 0, 0, 0, 0); + set_tracing(false); + LEDsoff(); +} diff --git a/armsrc/i2c.h b/armsrc/i2c.h new file mode 100644 index 00000000..4c5c5228 --- /dev/null +++ b/armsrc/i2c.h @@ -0,0 +1,58 @@ +//----------------------------------------------------------------------------- +// Willok, June 2018 +// Edits by Iceman, July 2018 +// +// 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. +//----------------------------------------------------------------------------- +// The main i2c code, for communications with smart card module +//----------------------------------------------------------------------------- +#ifndef __I2C_H +#define __I2C_H + +#include +#include "proxmark3.h" +#include "apps.h" +#include "util.h" +#include "BigBuf.h" +#include "smartcard.h" + +#define I2C_DEVICE_ADDRESS_BOOT 0xB0 +#define I2C_DEVICE_ADDRESS_MAIN 0xC0 + +#define I2C_DEVICE_CMD_GENERATE_ATR 0x01 +#define I2C_DEVICE_CMD_SEND 0x02 +#define I2C_DEVICE_CMD_READ 0x03 +#define I2C_DEVICE_CMD_SETBAUD 0x04 +#define I2C_DEVICE_CMD_SIM_CLC 0x05 +#define I2C_DEVICE_CMD_GETVERSION 0x06 + + +void I2C_init(void); +void I2C_Reset(void); +void I2C_SetResetStatus(uint8_t LineRST, uint8_t LineSCK, uint8_t LineSDA); + +void I2C_Reset_EnterMainProgram(void); +void I2C_Reset_EnterBootloader(void); + +bool I2C_WriteCmd(uint8_t device_cmd, uint8_t device_address); + +bool I2C_WriteByte(uint8_t data, uint8_t device_cmd, uint8_t device_address); +bool I2C_BufferWrite(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address); +uint8_t I2C_BufferRead(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address); + +// for firmware +uint8_t I2C_ReadFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address); +bool I2C_WriteFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address); + +bool GetATR(smart_card_atr_t *card_ptr); + +// generic functions +void SmartCardAtr(void); +void SmartCardRaw(uint64_t arg0, uint64_t arg1, uint8_t *data); +void SmartCardUpgrade(uint64_t arg0); +//void SmartCardSetBaud(uint64_t arg0); +void SmartCardSetClock(uint64_t arg0); +void I2C_print_status(void); +#endif diff --git a/client/Makefile b/client/Makefile index 2d256b72..c6ca1cf1 100644 --- a/client/Makefile +++ b/client/Makefile @@ -26,6 +26,11 @@ CXXFLAGS = -I../include -Wall -O3 APP_CFLAGS = include ../common/Makefile_Enabled_Options.common CFLAGS += $(APP_CFLAGS) +ifneq (,$(findstring WITH_SMARTCARD,$(APP_CFLAGS))) + SRC_SMARTCARD = cmdsmartcard.c +else + SRC_SMARTCARD = +endif LUAPLATFORM = generic platform = $(shell uname) @@ -93,7 +98,8 @@ CORESRCS = uart_posix.c \ ui.c \ comms.c -CMDSRCS = crapto1/crapto1.c\ +CMDSRCS = $(SRC_SMARTCARD) \ + crapto1/crapto1.c\ crapto1/crypto1.c\ polarssl/des.c \ polarssl/aes.c\ @@ -310,9 +316,7 @@ DEPENDENCY_FILES = $(patsubst %.c, $(OBJDIR)/%.d, $(CORESRCS) $(CMDSRCS) $(ZLIBS $(patsubst %.cpp, $(OBJDIR)/%.d, $(QTGUISRCS)) \ $(OBJDIR)/proxmark3.d $(OBJDIR)/flash.d $(OBJDIR)/flasher.d $(OBJDIR)/fpga_compress.d - $(DEPENDENCY_FILES): ; .PRECIOUS: $(DEPENDENCY_FILES) -include $(DEPENDENCY_FILES) - diff --git a/client/cmdhf.c b/client/cmdhf.c index 93906a7d..b973354d 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -353,6 +353,12 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui int CmdHFList(const char *Cmd) { + #ifdef WITH_SMARTCARD + PrintAndLog("TEST_WITH_SMARTCARD"); + #endif + #ifdef WITH_TEST + PrintAndLog("TEST_WITH_TEST"); + #endif bool showWaitCycles = false; bool markCRCBytes = false; bool loadFromFile = false; diff --git a/client/cmdmain.c b/client/cmdmain.c index 01d4c9a7..f503021a 100644 --- a/client/cmdmain.c +++ b/client/cmdmain.c @@ -26,7 +26,9 @@ #include "util.h" #include "util_posix.h" #include "cmdscript.h" - +#ifdef WITH_SMARTCARD + #include "cmdsmartcard.h" +#endif static int CmdHelp(const char *Cmd); static int CmdQuit(const char *Cmd); @@ -39,6 +41,9 @@ static command_t CommandTable[] = {"hf", CmdHF, 1, "{ High Frequency commands... }"}, {"hw", CmdHW, 1, "{ Hardware commands... }"}, {"lf", CmdLF, 1, "{ Low Frequency commands... }"}, +#ifdef WITH_SMARTCARD + {"sc", CmdSmartcard,1,"{ Smartcard commands... }"}, +#endif {"script",CmdScript,1, "{ Scripting commands }"}, {"quit", CmdQuit, 1, "Exit program"}, {"exit", CmdQuit, 1, "Exit program"}, diff --git a/client/cmdsmartcard.c b/client/cmdsmartcard.c new file mode 100644 index 00000000..b2a5705d --- /dev/null +++ b/client/cmdsmartcard.c @@ -0,0 +1,707 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 iceman +// +// 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. +//----------------------------------------------------------------------------- +// Proxmark3 RDV40 Smartcard module commands +//----------------------------------------------------------------------------- +#include "cmdsmartcard.h" +#include "smartcard.h" +#include "comms.h" +#include "protocols.h" + + +static int CmdHelp(const char *Cmd); + +int usage_sm_raw(void) { + PrintAndLog("Usage: sc raw [h|r|c] d <0A 0B 0C ... hex>"); + PrintAndLog(" h : this help"); + PrintAndLog(" r : do not read response"); + PrintAndLog(" a : active signal field ON without select"); + PrintAndLog(" s : active signal field ON with select"); + PrintAndLog(" t : executes TLV decoder if it is possible"); + PrintAndLog(" d : bytes to send"); + PrintAndLog(""); + PrintAndLog("Examples:"); + PrintAndLog(" sc raw d 11223344"); + return 0; +} +int usage_sm_reader(void) { + PrintAndLog("Usage: sc reader [h|s]"); + PrintAndLog(" h : this help"); + PrintAndLog(" s : silent (no messages)"); + PrintAndLog(""); + PrintAndLog("Examples:"); + PrintAndLog(" sc reader"); + return 0; +} +int usage_sm_info(void) { + PrintAndLog("Usage: sc info [h|s]"); + PrintAndLog(" h : this help"); + PrintAndLog(" s : silent (no messages)"); + PrintAndLog(""); + PrintAndLog("Examples:"); + PrintAndLog(" sc info"); + return 0; +} +int usage_sm_upgrade(void) { + PrintAndLog("Upgrade firmware"); + PrintAndLog("Usage: sc upgrade f "); + PrintAndLog(" h : this help"); + PrintAndLog(" f : firmware file name"); + PrintAndLog(""); + PrintAndLog("Examples:"); + PrintAndLog(" sc upgrade f myfile"); + PrintAndLog(""); + PrintAndLog("WARNING - Dangerous command, do wrong and you will brick the smart card socket"); + return 0; +} +int usage_sm_setclock(void) { + PrintAndLog("Usage: sc setclock [h] c "); + PrintAndLog(" h : this help"); + PrintAndLog(" c <> : clockspeed (0 = 16mhz, 1=8mhz, 2=4mhz) "); + PrintAndLog(""); + PrintAndLog("Examples:"); + PrintAndLog(" sc setclock c 2"); + return 0; +} + +int CmdSmartRaw(const char *Cmd) { + + int hexlen = 0; + bool active = false; + bool active_select = false; + uint8_t cmdp = 0; + bool errors = false, reply = true, decodeTLV = false, breakloop = false; + uint8_t data[USB_CMD_DATA_SIZE] = {0x00}; + + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': return usage_sm_raw(); + case 'r': + reply = false; + cmdp++; + break; + case 'a': + active = true; + cmdp++; + break; + case 's': + active_select = true; + cmdp++; + break; + case 't': + decodeTLV = true; + cmdp++; + break; + case 'd': { + switch (param_gethex_to_eol(Cmd, cmdp+1, data, sizeof(data), &hexlen)) { + case 1: + PrintAndLog("Invalid HEX value."); + return 1; + case 2: + PrintAndLog("Too many bytes. Max %d bytes", sizeof(data)); + return 1; + case 3: + PrintAndLog("Hex must have an even number of digits."); + return 1; + } + cmdp++; + breakloop = true; + break; + } + default: + PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + + if ( breakloop ) + break; + } + + //Validations + if (errors || cmdp == 0 ) return usage_sm_raw(); + + // arg0 = RFU flags + // arg1 = length + UsbCommand c = {CMD_SMART_RAW, {0, hexlen, 0}}; + + if (active || active_select) { + c.arg[0] |= SC_CONNECT; + if (active) + c.arg[0] |= SC_NO_SELECT; + } + + if (hexlen > 0) { + c.arg[0] |= SC_RAW; + } + + memcpy(c.d.asBytes, data, hexlen ); + clearCommandBuffer(); + SendCommand(&c); + + // reading response from smart card + if ( reply ) { + UsbCommand resp; + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { + PrintAndLog("smart card response failed"); + return 1; + } + uint32_t datalen = resp.arg[0]; + + if ( !datalen ) { + PrintAndLog("smart card response failed"); + return 1; + } + + PrintAndLog("received %i bytes", datalen); + + if (!datalen) + return 1; + + uint8_t *data = resp.d.asBytes; + + // TLV decoder + if (decodeTLV ) { + + if (datalen >= 2) { + PrintAndLog("%02x %02x | %s", data[datalen - 2], data[datalen - 1], GetAPDUCodeDescription(data[datalen - 2], data[datalen - 1])); + } + if (datalen > 4) { + TLVPrintFromBuffer(data, datalen - 2); + } + } else { + PrintAndLog("%s", sprint_hex(data, datalen)); + } + } + return 0; +} + +int CmdSmartUpgrade(const char *Cmd) { + + PrintAndLog("WARNING - Smartcard socket firmware upgrade."); + PrintAndLog("Dangerous command, do wrong and you will brick the smart card socket"); + + FILE *f; + char filename[FILE_PATH_SIZE] = {0}; + uint8_t cmdp = 0; + bool errors = false; + + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'f': + //File handling and reading + if ( param_getstr(Cmd, cmdp+1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE ) { + PrintAndLog("Filename too long"); + errors = true; + break; + } + cmdp += 2; + break; + case 'h': + return usage_sm_upgrade(); + default: + PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + + //Validations + if (errors || cmdp == 0 ) return usage_sm_upgrade(); + + // load file + f = fopen(filename, "rb"); + if ( !f ) { + PrintAndLog("File: %s: not found or locked.", filename); + return 1; + } + + // get filesize in order to malloc memory + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + + if (fsize < 0) { + PrintAndLog("error, when getting filesize"); + fclose(f); + return 1; + } + + uint8_t *dump = calloc(fsize, sizeof(uint8_t)); + if (!dump) { + PrintAndLog("error, cannot allocate memory "); + fclose(f); + return 1; + } + + size_t bytes_read = fread(dump, 1, fsize, f); + if (f) + fclose(f); + + PrintAndLog("Smartcard socket firmware uploading to PM3"); + //Send to device + uint32_t index = 0; + uint32_t bytes_sent = 0; + uint32_t bytes_remaining = bytes_read; + + while (bytes_remaining > 0){ + uint32_t bytes_in_packet = MIN(USB_CMD_DATA_SIZE, bytes_remaining); + UsbCommand c = {CMD_SMART_UPLOAD, {index + bytes_sent, bytes_in_packet, 0}}; + + // Fill usb bytes with 0xFF + memset(c.d.asBytes, 0xFF, USB_CMD_DATA_SIZE); + memcpy(c.d.asBytes, dump + bytes_sent, bytes_in_packet); + clearCommandBuffer(); + SendCommand(&c); + if ( !WaitForResponseTimeout(CMD_ACK, NULL, 2000) ) { + PrintAndLog("timeout while waiting for reply."); + free(dump); + return 1; + } + + bytes_remaining -= bytes_in_packet; + bytes_sent += bytes_in_packet; + printf("."); fflush(stdout); + } + free(dump); + printf("\n"); + PrintAndLog("Smartcard socket firmware updating, don\'t turn off your PM3!"); + + // trigger the firmware upgrade + UsbCommand c = {CMD_SMART_UPGRADE, {bytes_read, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2500) ) { + PrintAndLog("timeout while waiting for reply."); + return 1; + } + if ( (resp.arg[0] && 0xFF ) ) + PrintAndLog("Smartcard socket firmware upgraded successful"); + else + PrintAndLog("Smartcard socket firmware updating failed"); + return 0; +} + +int CmdSmartInfo(const char *Cmd){ + uint8_t cmdp = 0; + bool errors = false, silent = false; + + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': return usage_sm_info(); + case 's': + silent = true; + break; + default: + PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + cmdp++; + } + + //Validations + if (errors ) return usage_sm_info(); + + UsbCommand c = {CMD_SMART_ATR, {0, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2500) ) { + if (!silent) PrintAndLog("smart card select failed"); + return 1; + } + + uint8_t isok = resp.arg[0] & 0xFF; + if (!isok) { + if (!silent) PrintAndLog("smart card select failed"); + return 1; + } + + smart_card_atr_t card; + memcpy(&card, (smart_card_atr_t *)resp.d.asBytes, sizeof(smart_card_atr_t)); + + // print header + PrintAndLog("\n--- Smartcard Information ---------"); + PrintAndLog("-------------------------------------------------------------"); + PrintAndLog("ISO76183 ATR : %s", sprint_hex(card.atr, card.atr_len)); + PrintAndLog("look up ATR"); + PrintAndLog("http://smartcard-atr.appspot.com/parse?ATR=%s", sprint_hex_inrow(card.atr, card.atr_len) ); + return 0; +} + +int CmdSmartReader(const char *Cmd){ + uint8_t cmdp = 0; + bool errors = false, silent = false; + + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': return usage_sm_reader(); + case 's': + silent = true; + break; + default: + PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + cmdp++; + } + + //Validations + if (errors ) return usage_sm_reader(); + + UsbCommand c = {CMD_SMART_ATR, {0, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2500) ) { + if (!silent) PrintAndLog("smart card select failed"); + return 1; + } + + uint8_t isok = resp.arg[0] & 0xFF; + if (!isok) { + if (!silent) PrintAndLog("smart card select failed"); + return 1; + } + smart_card_atr_t card; + memcpy(&card, (smart_card_atr_t *)resp.d.asBytes, sizeof(smart_card_atr_t)); + PrintAndLog("ISO7816-3 ATR : %s", sprint_hex(card.atr, card.atr_len)); + return 0; +} + +int CmdSmartSetClock(const char *Cmd){ + uint8_t cmdp = 0; + bool errors = false; + uint8_t clock = 0; + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': return usage_sm_setclock(); + case 'c': + clock = param_get8ex(Cmd, cmdp+1, 2, 10); + if ( clock > 2) + errors = true; + + cmdp += 2; + break; + default: + PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + + //Validations + if (errors || cmdp == 0) return usage_sm_setclock(); + + UsbCommand c = {CMD_SMART_SETCLOCK, {clock, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2500) ) { + PrintAndLog("smart card select failed"); + return 1; + } + + uint8_t isok = resp.arg[0] & 0xFF; + if (!isok) { + PrintAndLog("smart card set clock failed"); + return 1; + } + + switch (clock) { + case 0: + PrintAndLog("Clock changed to 16mhz giving 10800 baudrate"); + break; + case 1: + PrintAndLog("Clock changed to 8mhz giving 21600 baudrate"); + break; + case 2: + PrintAndLog("Clock changed to 4mhz giving 86400 baudrate"); + break; + default: + break; + } + return 0; +} + + +// iso 7816-3 +void annotateIso7816(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize){ + // S-block + if ( (cmd[0] & 0xC0) && (cmdsize == 3) ) { + switch ( (cmd[0] & 0x3f) ) { + case 0x00 : snprintf(exp, size, "S-block RESYNCH req"); break; + case 0x20 : snprintf(exp, size, "S-block RESYNCH resp"); break; + case 0x01 : snprintf(exp, size, "S-block IFS req"); break; + case 0x21 : snprintf(exp, size, "S-block IFS resp"); break; + case 0x02 : snprintf(exp, size, "S-block ABORT req"); break; + case 0x22 : snprintf(exp, size, "S-block ABORT resp"); break; + case 0x03 : snprintf(exp, size, "S-block WTX reqt"); break; + case 0x23 : snprintf(exp, size, "S-block WTX resp"); break; + default : snprintf(exp, size, "S-block"); break; + } + } + // R-block (ack) + else if ( ((cmd[0] & 0xD0) == 0x80) && ( cmdsize > 2) ) { + if ( (cmd[0] & 0x10) == 0 ) + snprintf(exp, size, "R-block ACK"); + else + snprintf(exp, size, "R-block NACK"); + } + // I-block + else { + + int pos = (cmd[0] == 2 || cmd[0] == 3) ? 2 : 3; + switch ( cmd[pos] ) { + case ISO7816_READ_BINARY :snprintf(exp, size, "READ BIN");break; + case ISO7816_WRITE_BINARY :snprintf(exp, size, "WRITE BIN");break; + case ISO7816_UPDATE_BINARY :snprintf(exp, size, "UPDATE BIN");break; + case ISO7816_ERASE_BINARY :snprintf(exp, size, "ERASE BIN");break; + case ISO7816_READ_RECORDS :snprintf(exp, size, "READ RECORDS");break; + case ISO7816_WRITE_RECORDS :snprintf(exp, size, "WRITE RECORDS");break; + case ISO7816_APPEND_RECORD :snprintf(exp, size, "APPEND RECORD");break; + case ISO7816_UPDATE_RECORD :snprintf(exp, size, "UPDATE RECORD");break; + case ISO7816_GET_DATA :snprintf(exp, size, "GET DATA");break; + case ISO7816_PUT_DATA :snprintf(exp, size, "PUT DATA");break; + case ISO7816_SELECT_FILE :snprintf(exp, size, "SELECT FILE");break; + case ISO7816_VERIFY :snprintf(exp, size, "VERIFY");break; + case ISO7816_INTERNAL_AUTHENTICATION :snprintf(exp, size, "INTERNAL AUTH");break; + case ISO7816_EXTERNAL_AUTHENTICATION :snprintf(exp, size, "EXTERNAL AUTH");break; + case ISO7816_GET_CHALLENGE :snprintf(exp, size, "GET CHALLENGE");break; + case ISO7816_MANAGE_CHANNEL :snprintf(exp, size, "MANAGE CHANNEL");break; + default :snprintf(exp, size, "?"); break; + } + } +} + + +uint16_t printScTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace) { + // sanity check + if (tracepos + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) > traceLen) return traceLen; + + bool isResponse; + uint16_t data_len, parity_len; + uint32_t duration, timestamp, first_timestamp, EndOfTransmissionTimestamp; + char explanation[30] = {0}; + + first_timestamp = *((uint32_t *)(trace)); + timestamp = *((uint32_t *)(trace + tracepos)); + tracepos += 4; + + duration = *((uint16_t *)(trace + tracepos)); + tracepos += 2; + + data_len = *((uint16_t *)(trace + tracepos)); + tracepos += 2; + + if (data_len & 0x8000) { + data_len &= 0x7fff; + isResponse = true; + } else { + isResponse = false; + } + + parity_len = (data_len-1)/8 + 1; + if (tracepos + data_len + parity_len > traceLen) { + return traceLen; + } + uint8_t *frame = trace + tracepos; + tracepos += data_len; + //uint8_t *parityBytes = trace + tracepos; + tracepos += parity_len; + + //--- Draw the data column + char line[18][110]; + + if (data_len == 0 ) { + sprintf(line[0],""); + return tracepos; + } + + for (int j = 0; j < data_len && j/18 < 18; j++) { + snprintf(line[j/18]+(( j % 18) * 4),110, "%02x ", frame[j]); + } + + EndOfTransmissionTimestamp = timestamp + duration; + + annotateIso7816(explanation,sizeof(explanation),frame,data_len); + + int num_lines = MIN((data_len - 1)/18 + 1, 18); + for (int j = 0; j < num_lines ; j++) { + if (j == 0) { + PrintAndLog(" %10u | %10u | %s |%-72s | %s| %s", + (timestamp - first_timestamp), + (EndOfTransmissionTimestamp - first_timestamp), + (isResponse ? "Tag" : "Rdr"), + line[j], + " ", + (j == num_lines-1) ? explanation : ""); + } else { + PrintAndLog(" | | |%-72s | %s| %s", + line[j], + " ", + (j == num_lines-1) ? explanation : ""); + } + } + + // if is last record + if (tracepos + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) >= traceLen) return traceLen; + + return tracepos; +} + +int ScTraceList(const char *Cmd) { + bool loadFromFile = false; + bool saveToFile = false; + char type[5] = {0}; + char filename[FILE_PATH_SIZE] = {0}; + + // parse command line + param_getstr(Cmd, 0, type, sizeof(type)); + param_getstr(Cmd, 1, filename, sizeof(filename)); + + bool errors = false; + if(type[0] == 'h') { + errors = true; + } + + if(!errors) { + if (strcmp(type, "s") == 0) { + saveToFile = true; + } else if (strcmp(type,"l") == 0) { + loadFromFile = true; + } + } + + if ((loadFromFile || saveToFile) && strlen(filename) == 0) { + errors = true; + } + + if (loadFromFile && saveToFile) { + errors = true; + } + + if (errors) { + PrintAndLog("List or save protocol data."); + PrintAndLog("Usage: sc list [l ]"); + PrintAndLog(" sc list [s ]"); + PrintAndLog(" l - load data from file instead of trace buffer"); + PrintAndLog(" s - save data to file"); + PrintAndLog(""); + PrintAndLog("example: sc list"); + PrintAndLog("example: sc list save myCardTrace.trc"); + PrintAndLog("example: sc list l myCardTrace.trc"); + return 0; + } + + uint8_t *trace; + uint32_t tracepos = 0; + uint32_t traceLen = 0; + + if (loadFromFile) { + #define TRACE_CHUNK_SIZE (1<<16) // 64K to start with. Will be enough for BigBuf and some room for future extensions + FILE *tracefile = NULL; + size_t bytes_read; + trace = malloc(TRACE_CHUNK_SIZE); + if (trace == NULL) { + PrintAndLog("Cannot allocate memory for trace"); + return 2; + } + if ((tracefile = fopen(filename,"rb")) == NULL) { + PrintAndLog("Could not open file %s", filename); + free(trace); + return 0; + } + while (!feof(tracefile)) { + bytes_read = fread(trace+traceLen, 1, TRACE_CHUNK_SIZE, tracefile); + traceLen += bytes_read; + if (!feof(tracefile)) { + uint8_t *p = realloc(trace, traceLen + TRACE_CHUNK_SIZE); + if (p == NULL) { + PrintAndLog("Cannot allocate memory for trace"); + free(trace); + fclose(tracefile); + return 2; + } + trace = p; + } + } + fclose(tracefile); + } else { + trace = malloc(USB_CMD_DATA_SIZE); + // Query for the size of the trace + UsbCommand response; + GetFromBigBuf(trace, USB_CMD_DATA_SIZE, 0, &response, -1, false); + traceLen = response.arg[2]; + if (traceLen > USB_CMD_DATA_SIZE) { + uint8_t *p = realloc(trace, traceLen); + if (p == NULL) { + PrintAndLog("Cannot allocate memory for trace"); + free(trace); + return 2; + } + trace = p; + GetFromBigBuf(trace, traceLen, 0, NULL, -1, false); + } + } + + if (saveToFile) { + FILE *tracefile = NULL; + if ((tracefile = fopen(filename,"wb")) == NULL) { + PrintAndLog("Could not create file %s", filename); + return 1; + } + fwrite(trace, 1, traceLen, tracefile); + PrintAndLog("Recorded Activity (TraceLen = %d bytes) written to file %s", traceLen, filename); + fclose(tracefile); + } else { + PrintAndLog("Recorded Activity (TraceLen = %d bytes)", traceLen); + PrintAndLog(""); + PrintAndLog("Start = Start of Start Bit, End = End of last modulation. Src = Source of Transfer"); + PrintAndLog(""); + PrintAndLog(" Start | End | Src | Data (! denotes parity error) | CRC | Annotation |"); + PrintAndLog("------------|------------|-----|-------------------------------------------------------------------------|-----|--------------------|"); + + while(tracepos < traceLen) + { + tracepos = printScTraceLine(tracepos, traceLen, trace); + } + } + + free(trace); + return 0; +} + +int CmdSmartList(const char *Cmd) { + ScTraceList(Cmd); + return 0; +} + +static command_t CommandTable[] = { + {"help", CmdHelp, 1, "This help"}, + {"list", CmdSmartList, 0, "List ISO 7816 history"}, + {"info", CmdSmartInfo, 1, "Tag information [rdv40]"}, + {"reader", CmdSmartReader, 1, "Act like an IS07816 reader [rdv40]"}, + {"raw", CmdSmartRaw, 1, "Send raw hex data to tag [rdv40]"}, + {"upgrade", CmdSmartUpgrade, 1, "Upgrade firmware [rdv40]"}, + {"setclock",CmdSmartSetClock, 1, "Set clock speed"}, + {NULL, NULL, 0, NULL} +}; + +int CmdSmartcard(const char *Cmd) { + clearCommandBuffer(); + CmdsParse(CommandTable, Cmd); + return 0; +} + +int CmdHelp(const char *Cmd) { + CmdsHelp(CommandTable); + return 0; +} diff --git a/client/cmdsmartcard.h b/client/cmdsmartcard.h new file mode 100644 index 00000000..caa06f4f --- /dev/null +++ b/client/cmdsmartcard.h @@ -0,0 +1,39 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 iceman +// +// 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. +//----------------------------------------------------------------------------- +// Proxmark3 RDV40 Smartcard module commands +//----------------------------------------------------------------------------- + +#ifndef CMDSMARTCARD_H__ +#define CMDSMARTCARD_H__ + +#include +#include +#include +#include +#include "proxmark3.h" +#include "ui.h" +#include "cmdparser.h" +#include "common.h" +#include "util.h" +#include "loclass/fileutils.h" // saveFile +#include "cmdmain.h" // getfromdevice +#include "emv/emvcore.h" // decodeTVL +#include "emv/apduinfo.h" // APDUcode description + +extern int CmdSmartcard(const char *Cmd); + +extern int CmdSmartRaw(const char* cmd); +extern int CmdSmartUpgrade(const char* cmd); +extern int CmdSmartInfo(const char* cmd); +extern int CmdSmartReader(const char *Cmd); + +extern int usage_sm_raw(void); +extern int usage_sm_reader(void); +extern int usage_sm_info(void); +extern int usage_sm_upgrade(void); +#endif diff --git a/common/lfdemod.c b/common/lfdemod.c index f470371a..76900047 100644 --- a/common/lfdemod.c +++ b/common/lfdemod.c @@ -10,8 +10,8 @@ // // NOTES: // LF Demod functions are placed here to allow the flexability to use client or -// device side. Most BUT NOT ALL of these functions are currenlty safe for -// device side use currently. (DetectST for example...) +// device side. Most BUT NOT ALL of these functions are currently safe for +// device side use. (DetectST for example...) // // There are likely many improvements to the code that could be made, please // make suggestions... diff --git a/common/protocols.h b/common/protocols.h index 57e6011f..9ba69d5c 100644 --- a/common/protocols.h +++ b/common/protocols.h @@ -200,11 +200,12 @@ NXP/Philips CUSTOM COMMANDS #define TOPAZ_WRITE_NE8 0x1B // Write-no-erase (eight bytes) -#define ISO_14443A 0 -#define ICLASS 1 -#define ISO_14443B 2 -#define TOPAZ 3 -#define PROTO_MIFARE 4 +#define ISO_14443A 0 +#define ICLASS 1 +#define ISO_14443B 2 +#define TOPAZ 3 +#define PROTO_MIFARE 4 +#define ISO_7816_4 5 //-- Picopass fuses #define FUSE_FPERS 0x80 @@ -216,6 +217,29 @@ NXP/Philips CUSTOM COMMANDS #define FUSE_FPROD0 0x02 #define FUSE_RA 0x01 +// ISO 7816-4 Basic interindustry commands. For command APDU's. +#define ISO7816_READ_BINARY 0xB0 +#define ISO7816_WRITE_BINARY 0xD0 +#define ISO7816_UPDATE_BINARY 0xD6 +#define ISO7816_ERASE_BINARY 0x0E +#define ISO7816_READ_RECORDS 0xB2 +#define ISO7816_WRITE_RECORDS 0xD2 +#define ISO7816_APPEND_RECORD 0xE2 +#define ISO7816_UPDATE_RECORD 0xDC +#define ISO7816_GET_DATA 0xCA +#define ISO7816_PUT_DATA 0xDA +#define ISO7816_SELECT_FILE 0xA4 +#define ISO7816_VERIFY 0x20 +#define ISO7816_INTERNAL_AUTHENTICATION 0x88 +#define ISO7816_EXTERNAL_AUTHENTICATION 0x82 +#define ISO7816_GET_CHALLENGE 0xB4 +#define ISO7816_MANAGE_CHANNEL 0x70 +// ISO7816-4 For response APDU's +#define ISO7816_OK 0x9000 +// 6x xx = ERROR + + + void printIclassDumpInfo(uint8_t* iclass_dump); void getMemConfig(uint8_t mem_cfg, uint8_t chip_cfg, uint8_t *max_blk, uint8_t *app_areas, uint8_t *kb); diff --git a/include/smartcard.h b/include/smartcard.h new file mode 100644 index 00000000..9bed8c9d --- /dev/null +++ b/include/smartcard.h @@ -0,0 +1,29 @@ +//----------------------------------------------------------------------------- +// (c) 2018 Iceman, adapted by Marshmellow +// +// 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. +//----------------------------------------------------------------------------- +// smart card type prototyping +//----------------------------------------------------------------------------- +#ifndef __SMARTCARD_H +#define __SMARTCARD_H + +//----------------------------------------------------------------------------- +// ISO 7618 Smart Card +//----------------------------------------------------------------------------- +typedef struct { + uint8_t atr_len; + uint8_t atr[30]; +} __attribute__((__packed__)) smart_card_atr_t; + +typedef enum SMARTCARD_COMMAND { + SC_CONNECT = (1 << 0), + SC_NO_DISCONNECT = (1 << 1), + SC_RAW = (1 << 2), + SC_NO_SELECT = (1 << 3) +} smartcard_command_t; + + +#endif diff --git a/include/usb_cmd.h b/include/usb_cmd.h index 194a9d53..bdff7261 100644 --- a/include/usb_cmd.h +++ b/include/usb_cmd.h @@ -60,8 +60,17 @@ typedef struct{ #define CMD_BUFF_CLEAR 0x0105 #define CMD_READ_MEM 0x0106 #define CMD_VERSION 0x0107 -#define CMD_STATUS 0x0108 -#define CMD_PING 0x0109 +#define CMD_STATUS 0x0108 +#define CMD_PING 0x0109 + +// RDV40, Smart card operations +#define CMD_SMART_RAW 0x0140 +#define CMD_SMART_UPGRADE 0x0141 +#define CMD_SMART_UPLOAD 0x0142 +#define CMD_SMART_ATR 0x0143 +// CMD_SMART_SETBAUD is unused for now +#define CMD_SMART_SETBAUD 0x0144 +#define CMD_SMART_SETCLOCK 0x0145 // For low-frequency tags #define CMD_READ_TI_TYPE 0x0202 @@ -126,10 +135,10 @@ typedef struct{ #define CMD_READER_HITAG 0x0372 #define CMD_SIMULATE_HITAG_S 0x0368 -#define CMD_TEST_HITAGS_TRACES 0x0367 -#define CMD_READ_HITAG_S 0x0373 -#define CMD_WR_HITAG_S 0x0375 -#define CMD_EMU_HITAG_S 0x0376 +#define CMD_TEST_HITAGS_TRACES 0x0367 +#define CMD_READ_HITAG_S 0x0373 +#define CMD_WR_HITAG_S 0x0375 +#define CMD_EMU_HITAG_S 0x0376 #define CMD_SIMULATE_TAG_ISO_14443B 0x0381 From b5a5fc4d9f6bae6a02f1d71ea4769f86b6cb6fb8 Mon Sep 17 00:00:00 2001 From: grauerfuchs <42082416+grauerfuchs@users.noreply.github.com> Date: Mon, 27 Aug 2018 14:03:46 -0400 Subject: [PATCH 250/310] Redesign of lf hid card format handler as discussed with @marshmellow42 The new handler accepts multiple formats of the same length. Because of this, the existing pack/unpack commands are unsupported and have been removed and replaced with 'lf hid encode' and 'lf hid decode'. The decode command will test a packed Prox ID against all programmed formats and return results for all matching formats. The encode command takes the parameter of format name instead of bit length (as per the old pack command). Additionally, an 'lf hid write' command has been added as a single-command combination of encode and clone. To support easier addition of new formats, a library for handling card fields has been added. This will allow direct access to the card bits, to linear fields, and to non-linear (jumping) fields in a single line of code without having to resort to managing bit positions or masks on the underlying data. A number of new formats have been added as working examples of the new support functions. --- client/Makefile | 2 + client/cmdlfhid.c | 382 ++++++++------------------- client/cmdlfhid.h | 31 +-- client/hidcardformats.c | 512 ++++++++++++++++++++++++++++++++++++ client/hidcardformats.h | 32 +++ client/hidcardformatutils.c | 172 ++++++++++++ client/hidcardformatutils.h | 47 ++++ 7 files changed, 874 insertions(+), 304 deletions(-) create mode 100644 client/hidcardformats.c create mode 100644 client/hidcardformats.h create mode 100644 client/hidcardformatutils.c create mode 100644 client/hidcardformatutils.h diff --git a/client/Makefile b/client/Makefile index c6ca1cf1..7c79b63b 100644 --- a/client/Makefile +++ b/client/Makefile @@ -160,6 +160,8 @@ CMDSRCS = $(SRC_SMARTCARD) \ cmdlfem4x.c \ cmdlffdx.c \ cmdlfgproxii.c \ + hidcardformatutils.c\ + hidcardformats.c\ cmdlfhid.c \ cmdlfhitag.c \ cmdlfio.c \ diff --git a/client/cmdlfhid.c b/client/cmdlfhid.c index 1a9c3477..d8e151b9 100644 --- a/client/cmdlfhid.c +++ b/client/cmdlfhid.c @@ -28,223 +28,10 @@ #include "cmdparser.h" #include "cmddata.h" //for g_debugMode, demodbuff cmds #include "lfdemod.h" // for HIDdemodFSK -#include "parity.h" // for parity +#include "hidcardformats.h" +#include "hidcardformatutils.h" #include "util.h" // for param_get8,32,64 -static int CmdHelp(const char *Cmd); - -/** - * Packs an HID ID from component parts. - * - * This only works with 26, 34, 35, 37, and 48 bit card IDs. - * - * Returns false on invalid inputs. - */ -bool pack_hid(/* out */ uint32_t *hi2, /* out */ uint32_t *hi, /* out */ uint32_t *lo, /* in */ const hid_info *info) { - uint32_t higher = 0, high = 0, low = 0; - - switch (info->fmtLen) { - case 26: // HID H10301 - low |= (info->cardnum & 0xffff) << 1; - low |= (info->fc & 0xff) << 17; - - if (info->parityValid) { - // Calculate parity - low |= oddparity32((low >> 1) & 0xfff) & 1; - low |= (evenparity32((low >> 13) & 0xfff) & 1) << 25; - } - break; - - case 34: // H10306 - low |= (info->cardnum & 0xffff) << 1; - low |= (info->fc & 0x7fff) << 17; - high |= (info->fc & 0x8000) >> 15; - - if (info->parityValid) { - // Calculate parity - high |= (evenparity32((high & 0x00000001) ^ (low & 0xFFFE0000)) & 1) << 1; - low |= (oddparity32(low & 0x0001FFFE) & 1); - } - break; - - case 35: // (Corporate 1000 35-bit) - low |= (info->cardnum & 0xfffff) << 1; - low |= (info->fc & 0x7ff) << 21; - high |= (info->fc & 0x800) >> 11; - - if (info->parityValid) { - // Calculate parity - high |= (evenparity32((high & 0x00000001) ^ (low & 0xB6DB6DB6)) & 1) << 1; - low |= (oddparity32( (high & 0x00000003) ^ (low & 0x6DB6DB6C)) & 1); - high |= (oddparity32( (high & 0x00000003) ^ (low & 0xFFFFFFFF)) & 1) << 2; - } - break; - - case 37: //H10304 - low |= (info->cardnum & 0x7ffff) << 1; - low |= (info->fc & 0xfff) << 20; - high |= (info->fc & 0xf000) >> 12; - - if (info->parityValid) { - // Calculate parity - high |= (evenparity32((high & 0x0000000F) ^ (low & 0xFFFC0000)) & 1) << 4; - low |= (oddparity32(low & 0x0007FFFE) & 1); - } - break; - - case 48: // Corporate 1000 48-bit - low |= (info->cardnum & 0x7FFFFF) << 1; - low |= (info->fc & 0xff) << 24; - high |= (info->fc & 0x3FFF00) >> 8; - - if (info->parityValid) { - // Calculate parity - high |= (evenparity32((high & 0x00001B6D) ^ (low & 0xB6DB6DB6)) & 1) << 14; - low |= (oddparity32( (high & 0x000036DB) ^ (low & 0x6DB6DB6C)) & 1); - high |= (oddparity32( (high & 0x00007FFF) ^ (low & 0xFFFFFFFF)) & 1) << 15; - } - break; - - default: - // Invalid / unsupported length - return false; - } - - // Set the format length bits - if (info->fmtLen < 37) { - // Bit 37 is always set - high |= 0x20; - - // Set the bit corresponding to the length. - if (info->fmtLen < 32) - low |= 1 << info->fmtLen; - else - high |= 1 << (info->fmtLen - 32); - - } else if (info->fmtLen > 37){ - if (info->fmtLen < 64) - high |= 1 << (info->fmtLen - 32); - else - higher |= 1 << (info->fmtLen - 64); - } - // Return result only if successful. - *hi2 = higher; - *hi = high; - *lo = low; - return true; -} - -/** - * Unpacks an HID ID into its component parts. - * - * This only works with 26, 34, 35, 37, and 48 bit card IDs. - * - * Returns false on invalid inputs. - */ -bool unpack_hid(hid_info *out, uint32_t hi2, uint32_t hi, uint32_t lo) { - memset(out, 0, sizeof(hid_info)); - uint8_t fmtLen = 0; - - uint32_t hFmt; // for calculating card length - if ((hi2 & 0x000FFFFF) > 0) { // > 64 bits - hFmt = hi2 & 0x000FFFFF; - fmtLen = 64; - } else if ((hi & 0xFFFFFFC0) > 0) { // < 63-38 bits - hFmt = hi & 0xFFFFFFC0; - fmtLen = 32; - } else if ((hi & 0x00000020) == 0) { // 37 bits - hFmt = 0; - fmtLen = 37; - } else if ((hi & 0x0000001F) > 0){ // 36-32 bits - hFmt = hi & 0x0000001F; - fmtLen = 32; - } else { // <32 bits - hFmt = lo; - fmtLen = 0; - } - - while (hFmt > 1) { - hFmt >>= 1; - fmtLen++; - } - out->fmtLen = fmtLen; - switch (out->fmtLen) { - case 26: // HID H10301 - out->cardnum = (lo >> 1) & 0xFFFF; - out->fc = (lo >> 17) & 0xFF; - - if (g_debugMode) { - PrintAndLog("oddparity : input=%x, calculated=%d, provided=%d", - (lo >> 1) & 0xFFF, oddparity32((lo >> 1) & 0xFFF), lo & 1); - PrintAndLog("evenparity: input=%x, calculated=%d, provided=%d", - (lo >> 13) & 0xFFF, evenparity32((lo >> 13) & 0xFFF) & 1, (lo >> 25) & 1); - } - - out->parityValid = - (oddparity32((lo >> 1) & 0xFFF) == (lo & 1)) && - ((evenparity32((lo >> 13) & 0xFFF) & 1) == ((lo >> 25) & 1)); - break; - - case 34: // HID H10306 - out->cardnum = (lo >> 1) & 0xFFFF; - out->fc = ((hi & 1) << 15) | (lo >> 17); - out->parityValid = - ((evenparity32((hi & 0x00000001) ^ (lo & 0xFFFE0000)) & 1) == ((hi >> 1) & 1)) && - ((oddparity32(lo & 0x0001FFFE) & 1) == ((lo & 1))); - break; - - case 35: // HID Corporate 1000-35 - out->cardnum = (lo >> 1) & 0xFFFFF; - out->fc = ((hi & 1) << 11) | (lo >> 21); - out->parityValid = - (evenparity32((hi & 0x00000001) ^ (lo & 0xB6DB6DB6)) == ((hi >> 1) & 1)) && - (oddparity32( (hi & 0x00000003) ^ (lo & 0x6DB6DB6C)) == ((lo >> 0) & 1)) && - (oddparity32( (hi & 0x00000003) ^ (lo & 0xFFFFFFFF)) == ((hi >> 2) & 1)); - if (g_debugMode) { - PrintAndLog("Parity check: calculated {%d, %d, %d}, provided {%d, %d, %d}", - evenparity32((hi & 0x00000001) ^ (lo & 0xB6DB6DB6)), - oddparity32( (hi & 0x00000003) ^ (lo & 0x6DB6DB6C)), - oddparity32( (hi & 0x00000003) ^ (lo & 0xFFFFFFFF)), - ((hi >> 1) & 1), - ((lo >> 0) & 1), - ((hi >> 2) & 1) - ); - } - break; - - case 37: // HID H10304 - out->fmtLen = 37; - out->cardnum = (lo >> 1) & 0x7FFFF; - out->fc = ((hi & 0xF) << 12) | (lo >> 20); - out->parityValid = - (evenparity32((hi & 0x0000000F) ^ (lo & 0xFFFC0000)) == ((hi >> 4) & 1)) && - (oddparity32( lo & 0x0007FFFE) == (lo & 1)); - break; - - case 48: // HID Corporate 1000-48 - out->cardnum = (lo >> 1) & 0x7FFFFF; //Start 24, 23 length - out->fc = ((hi & 0x3FFF) << 8 ) | (lo >> 24); //Start 2, 22 length - out->parityValid = - (evenparity32((hi & 0x00001B6D) ^ (lo & 0xB6DB6DB6)) == ((hi >> 14) & 1)) && - (oddparity32( (hi & 0x000036DB) ^ (lo & 0x6DB6DB6C)) == ((lo >> 0) & 1)) && - (oddparity32( (hi & 0x00007FFF) ^ (lo & 0xFFFFFFFF)) == ((hi >> 15) & 1)); - if (g_debugMode) { - PrintAndLog("Parity check: calculated {%d, %d, %d}, provided {%d, %d, %d}", - evenparity32((hi & 0x00001B6D) ^ (lo & 0xB6DB6DB6)), - oddparity32( (hi & 0x000036DB) ^ (lo & 0x6DB6DB6C)), - oddparity32( (hi & 0x00007FFF) ^ (lo & 0xFFFFFFFF)), - ((hi >> 14) & 1), - ((lo >> 0) & 1), - ((hi >> 15) & 1) - ); - } - break; - - default: - return false; - } - return true; -} /** * Converts a hex string to component "hi2", "hi" and "lo" 32-bit integers, one nibble @@ -301,21 +88,18 @@ int CmdFSKdemodHID(const char *Cmd) return 0; } - hid_info card_info; - bool ret = unpack_hid(&card_info, (uint32_t)hi2, (uint32_t)hi, (uint32_t)lo); - if (hi2 != 0) - PrintAndLog("HID Prox TAG ID: %x%08x%08x (%d) - Format Len: %u bits - FC: %u - Card: %u", - (unsigned int) hi2, (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF, - card_info.fmtLen, card_info.fc, card_info.cardnum); + PrintAndLog("HID Prox TAG ID: %x%08x%08x", + (unsigned int) hi2, (unsigned int) hi, (unsigned int) lo + ); else - PrintAndLog("HID Prox TAG ID: %x%08x (%d) - Format Len: %u bits - FC: %u - Card: %u", - (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF, - card_info.fmtLen, card_info.fc, card_info.cardnum); + PrintAndLog("HID Prox TAG ID: %x%08x", + (unsigned int) hi, (unsigned int) lo + ); + + hidproxmessage_t packed = initialize_proxmessage_object(hi2, hi, lo); + bool ret = HIDTryUnpack(&packed); - if (card_info.fmtLen == 26 || card_info.fmtLen == 35 || card_info.fmtLen == 48) { - PrintAndLog("Parity: %s", card_info.parityValid ? "valid" : "invalid"); - } if (!ret) { PrintAndLog("Invalid or unsupported tag length."); } @@ -370,7 +154,7 @@ int CmdHIDClone(const char *Cmd) } c.cmd = CMD_HID_CLONE_TAG; - c.arg[0] = hi2; + c.arg[0] = (hi2 & 0x000FFFFF); c.arg[1] = hi; c.arg[2] = lo; @@ -378,73 +162,118 @@ int CmdHIDClone(const char *Cmd) return 0; } - -int CmdHIDPack(const char *Cmd) { - uint32_t hi2 = 0, hi = 0, lo = 0; - +int CmdHIDDecode(const char *Cmd){ if (strlen(Cmd)<3) { - PrintAndLog("Usage: lf hid pack "); - PrintAndLog(" sample: lf hid pack 26 123 4567"); + PrintAndLog("Usage: lf hid decode "); + PrintAndLog(" sample: lf hid decode 2006f623ae"); return 0; } - uint8_t fmtLen = param_get8(Cmd, 0); - hid_info card_info; - card_info.fmtLen = fmtLen; - card_info.fc = param_get32ex(Cmd, 1, 0, 10); - card_info.cardnum = param_get64ex(Cmd, 2, 0, 10); - card_info.parityValid = true; + uint32_t top = 0, mid = 0, bot = 0; + hexstring_to_int96(&top, &mid, &bot, Cmd); + hidproxmessage_t packed = initialize_proxmessage_object(top, mid, bot); + HIDTryUnpack(&packed); + return 0; +} +int CmdHIDEncode(const char *Cmd) { + if (strlen(Cmd) == 0) { + PrintAndLog("Usage: lf hid encode "); + PrintAndLog(" sample: lf hid encode H10301 123 4567"); + return 0; + } - bool ret = pack_hid(&hi2, &hi, &lo, &card_info); - if (ret) { - if (hi2 != 0) { - PrintAndLog("HID Prox TAG ID: %x%08x%08x (%d) - Format Len: %u bits - FC: %u - Card: %u", - (unsigned int) hi2, (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF, - card_info.fmtLen, card_info.fc, card_info.cardnum); + int formatIndex = -1; + if (!strcmp(Cmd, "help") || !strcmp(Cmd, "h") || !strcmp(Cmd, "list") || !strcmp(Cmd, "?")){ + HIDListFormats(); + return 0; + } else { + char format[16]; + memset(format, 0, sizeof(format)); + param_getstr(Cmd, 0, format, sizeof(format)); + formatIndex = HIDFindCardFormat(format); + if (formatIndex == -1) { + HIDListFormats(); + return 0; + } + } + + hidproxcard_t card; + memset(&card, 0, sizeof(hidproxcard_t)); + card.FacilityCode = param_get32ex(Cmd, 1, 0, 10); + card.CardNumber = param_get64ex(Cmd, 2, 0, 10); + card.ParitySupported = true; // Try to encode parity if supported. + + hidproxmessage_t packed; + memset(&packed, 0, sizeof(hidproxmessage_t)); + if (HIDPack(formatIndex, &card, &packed)){ + if (packed.top != 0) { + PrintAndLog("HID Prox TAG ID: %x%08x%08x", + (unsigned int)packed.top, (unsigned int)packed.mid, (unsigned int)packed.bot); } else { - PrintAndLog("HID Prox TAG ID: %x%08x (%d) - Format Len: %u bits - FC: %u - Card: %u", - (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF, - card_info.fmtLen, card_info.fc, card_info.cardnum); + PrintAndLog("HID Prox TAG ID: %x%08x", + (unsigned int)packed.mid, (unsigned int)packed.bot); } } else { - PrintAndLog("Invalid or unsupported tag length."); + PrintAndLog("The provided data could not be encoded with the selected format."); } return 0; } - -int CmdHIDUnpack(const char *Cmd) -{ - uint32_t hi2 = 0, hi = 0, lo = 0; - if (strlen(Cmd)<1) { - PrintAndLog("Usage: lf hid unpack "); - PrintAndLog(" sample: lf hid unpack 2006f623ae"); +int CmdHIDWrite(const char *Cmd) { + if (strlen(Cmd) == 0) { + PrintAndLog("Usage: lf hid write "); + PrintAndLog(" sample: lf hid write H10301 123 4567"); return 0; } - hexstring_to_int96(&hi2, &hi, &lo, Cmd); - - hid_info card_info; - bool ret = unpack_hid(&card_info, hi2, hi, lo); - - if (hi2 != 0) { - PrintAndLog("HID Prox TAG ID: %x%08x%08x (%d) - Format Len: %u bits - FC: %u - Card: %u", - (unsigned int) hi2, (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF, - card_info.fmtLen, card_info.fc, card_info.cardnum); + int formatIndex = -1; + if (!strcmp(Cmd, "help") || !strcmp(Cmd, "h") || !strcmp(Cmd, "list") || !strcmp(Cmd, "?")){ + HIDListFormats(); + return 0; } else { - PrintAndLog("HID Prox TAG ID: %x%08x (%d) - Format Len: %u bits - FC: %u - Card: %u", - (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF, - card_info.fmtLen, card_info.fc, card_info.cardnum); + char format[16]; + memset(format, 0, sizeof(format)); + param_getstr(Cmd, 0, format, sizeof(format)); + formatIndex = HIDFindCardFormat(format); + if (formatIndex == -1) { + HIDListFormats(); + return 0; + } } - PrintAndLog("Parity: %s", card_info.parityValid ? "valid" : "invalid"); - - if (!ret) { - PrintAndLog("Invalid or unsupported tag length."); + + hidproxcard_t card; + memset(&card, 0, sizeof(hidproxcard_t)); + card.FacilityCode = param_get32ex(Cmd, 1, 0, 10); + card.CardNumber = param_get64ex(Cmd, 2, 0, 10); + card.ParitySupported = true; // Try to encode parity if supported. + + hidproxmessage_t packed; + memset(&packed, 0, sizeof(hidproxmessage_t)); + if (HIDPack(formatIndex, &card, &packed)){ + UsbCommand c; + if (packed.top != 0) { + PrintAndLog("HID Prox TAG ID: %x%08x%08x", + (unsigned int)packed.top, (unsigned int)packed.mid, (unsigned int)packed.bot); + c.d.asBytes[0] = 1; + } else { + PrintAndLog("HID Prox TAG ID: %x%08x", + (unsigned int)packed.mid, (unsigned int)packed.bot); + c.d.asBytes[0] = 0; + } + + c.cmd = CMD_HID_CLONE_TAG; + c.arg[0] = (packed.top & 0x000FFFFF); + c.arg[1] = packed.mid; + c.arg[2] = packed.bot; + SendCommand(&c); + + } else { + PrintAndLog("The provided data could not be encoded with the selected format."); } return 0; } - +static int CmdHelp(const char *Cmd); // define this now so the below won't error out. static command_t CommandTable[] = { {"help", CmdHelp, 1, "This help"}, @@ -452,8 +281,9 @@ static command_t CommandTable[] = {"read", CmdHIDReadFSK, 0, "['1'] Realtime HID FSK Read from antenna (option '1' for one tag only)"}, {"sim", CmdHIDSim, 0, " -- HID tag simulator"}, {"clone", CmdHIDClone, 0, " -- Clone HID to T55x7 (tag must be in antenna)"}, - {"pack", CmdHIDPack, 1, " -- packs an HID ID from its length, facility code and card number"}, - {"unpack", CmdHIDUnpack, 1, " -- unpacks an HID ID to its length, facility code and card number"}, + {"decode", CmdHIDDecode, 1, " -- Try to decode an HID tag and show its contents"}, + {"encode", CmdHIDEncode, 1, " -- Encode an HID ID with the specified format, facility code and card number"}, + {"write", CmdHIDWrite, 0, " -- Encode and write to a T55x7 tag (tag must be in antenna)"}, {NULL, NULL, 0, NULL} }; diff --git a/client/cmdlfhid.h b/client/cmdlfhid.h index 463be56f..ef907f67 100644 --- a/client/cmdlfhid.h +++ b/client/cmdlfhid.h @@ -14,37 +14,12 @@ #include #include -// Structure for unpacked HID Prox tags. -typedef struct { - // Format length, in bits. - uint8_t fmtLen; - - // Facility code. - uint32_t fc; - - // Card number. - uint64_t cardnum; - - // Parity validity. - // - // When used with pack_hid, this determines if we should calculate - // parity values for the ID. - // - // When used with unpack_hid, this indicates if we got valid parity - // values for the ID. - bool parityValid; -} hid_info; - -bool pack_hid(/* out */ uint32_t *hi2, /* out */ uint32_t *hi, /* out */ uint32_t *lo, /* in */ const hid_info *info); -bool unpack_hid(hid_info* out, uint32_t hi2, uint32_t hi, uint32_t lo); - - int CmdLFHID(const char *Cmd); int CmdFSKdemodHID(const char *Cmd); int CmdHIDReadDemod(const char *Cmd); int CmdHIDSim(const char *Cmd); int CmdHIDClone(const char *Cmd); -int CmdHIDPack(const char *Cmd); -int CmdHIDUnpack(const char *Cmd); - +int CmdHIDDecode(const char *Cmd); +int CmdHIDEncode(const char *Cmd); +int CmdHIDWrite(const char *Cmd); #endif diff --git a/client/hidcardformats.c b/client/hidcardformats.c new file mode 100644 index 00000000..379eb68f --- /dev/null +++ b/client/hidcardformats.c @@ -0,0 +1,512 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 grauerfuchs +// +// 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. +//----------------------------------------------------------------------------- +// HID card format packing/unpacking routines +//----------------------------------------------------------------------------- + +#include +#include +#include +#include "cmddata.h" +#include "hidcardformats.h" +#include "hidcardformatutils.h" +#include "parity.h" // for parity +#include "ui.h" + +bool Pack_H10301(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_t)); + if (card->FacilityCode > 0xFF) return false; // Can't encode FC. + if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. + + packed->Length = 26; // Set number of bits + packed->bot |= (card->CardNumber & 0xFFFF) << 1; + packed->bot |= (card->FacilityCode & 0xFF) << 17; + if (card->ParitySupported){ + packed->bot |= oddparity32((packed->bot >> 1) & 0xFFF) & 1; + packed->bot |= (evenparity32((packed->bot >> 13) & 0xFFF) & 1) << 25; + } + return add_HID_header(packed); +} +bool Unpack_H10301(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 26) return false; // Wrong length? Stop here. + card->CardNumber = (packed->bot >> 1) & 0xFFFF; + card->FacilityCode = (packed->bot >> 17) & 0xFF; + card->ParitySupported = true; + card->ParityValid = + (oddparity32((packed->bot >> 1) & 0xFFF) == (packed->bot & 1)) && + ((evenparity32((packed->bot >> 13) & 0xFFF) & 1) == ((packed->bot >> 25) & 1)); + return true; +} + +bool Pack_Tecom27(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_t)); + if (card->FacilityCode > 0x7FF) return false; // Can't encode FC. + if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. + packed->Length = 27; + set_nonlinear_field(packed, card->FacilityCode, 10, (uint8_t[]){15, 19, 24, 23, 22, 18, 6, 10, 14, 3, 2}); + set_nonlinear_field(packed, card->CardNumber, 16, (uint8_t[]){0, 1, 13, 12, 9, 26, 20, 16, 17, 21, 25, 7, 8, 11, 4, 5}); + return add_HID_header(packed); +} +bool Unpack_Tecom27(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 27) return false; // Wrong length? Stop here. + card->CardNumber = get_nonlinear_field(packed, 16, (uint8_t[]){0, 1, 13, 12, 9, 26, 20, 16, 17, 21, 25, 7, 8, 11, 4, 5}); + card->FacilityCode = get_nonlinear_field(packed, 10, (uint8_t[]){15, 19, 24, 23, 22, 18, 6, 10, 14, 3, 2}); + card->ParitySupported = false; + return true; +} + +bool Pack_2804W(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_t)); + if (card->FacilityCode > 0x0FF) return false; // Can't encode FC. + if (card->CardNumber > 0x7FFF) return false; // Can't encode CN. + packed->Length = 28; + set_linear_field(packed, card->FacilityCode, 4, 8); + set_linear_field(packed, card->CardNumber, 12, 15); + if (card->ParitySupported){ + set_bit_by_position(packed, + oddparity32(get_nonlinear_field(packed, 16, (uint8_t[]){4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26})) + , 2); + set_bit_by_position(packed, + evenparity32(get_linear_field(packed, 1, 13)) + , 0); + set_bit_by_position(packed, + oddparity32(get_linear_field(packed, 0, 27)) + , 27); + } + return add_HID_header(packed); +} +bool Unpack_2804W(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 28) return false; // Wrong length? Stop here. + card->FacilityCode = get_linear_field(packed, 4, 8); + card->CardNumber = get_linear_field(packed, 12, 15); + card->ParitySupported = true; + card->ParityValid = + (get_bit_by_position(packed, 0) == evenparity32(get_linear_field(packed, 1, 13))) && + (get_bit_by_position(packed, 2) == oddparity32(get_nonlinear_field(packed, 16, (uint8_t[]){4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26}))) && + (get_bit_by_position(packed, 27) == oddparity32(get_linear_field(packed, 0, 27))); + return true; +} + +bool Pack_ATSW30(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_t)); + if (card->FacilityCode > 0xFFF) return false; // Can't encode FC. + if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. + packed->Length = 30; + set_linear_field(packed, card->FacilityCode, 1, 12); + set_linear_field(packed, card->CardNumber, 13, 16); + if (card->ParitySupported){ + set_bit_by_position(packed, + evenparity32(get_linear_field(packed, 1, 12)) + , 0); + set_bit_by_position(packed, + oddparity32(get_linear_field(packed, 13, 16)) + , 29); + } + return add_HID_header(packed); +} +bool Unpack_ATSW30(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 30) return false; // Wrong length? Stop here. + card->FacilityCode = get_linear_field(packed, 1, 12); + card->CardNumber = get_linear_field(packed, 13, 16); + card->ParitySupported = true; + card->ParityValid = + (get_bit_by_position(packed, 0) == evenparity32(get_linear_field(packed, 1, 12))) && + (get_bit_by_position(packed, 29) == oddparity32(get_linear_field(packed, 13, 16))); + return true; +} +bool Pack_ADT31(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_t)); + if (card->FacilityCode > 0x0F) return false; // Can't encode FC. + if (card->CardNumber > 0x7FFFFF) return false; // Can't encode CN. + packed->Length = 31; + set_linear_field(packed, card->FacilityCode, 1, 4); + set_linear_field(packed, card->CardNumber, 5, 23); + // Parity not known, but 4 bits are unused. + return add_HID_header(packed); +} +bool Unpack_ADT31(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 31) return false; // Wrong length? Stop here. + card->FacilityCode = get_linear_field(packed, 1, 4); + card->CardNumber = get_linear_field(packed, 5, 23); + card->ParitySupported = false; + return true; +} + +bool Pack_D10202(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_t)); + if (card->FacilityCode > 0x007F) return false; // Can't encode FC. + if (card->CardNumber > 0x00FFFFFF) return false; // Can't encode CN. + + packed->Length = 33; // Set number of bits + set_linear_field(packed, card->FacilityCode, 1, 7); + set_linear_field(packed, card->CardNumber, 8, 24); + + if (card->ParitySupported){ + set_bit_by_position(packed, evenparity32(get_linear_field(packed, 1, 16)), 0); + set_bit_by_position(packed, oddparity32(get_linear_field(packed, 16, 16)), 32); + } + return add_HID_header(packed); +} +bool Unpack_D10202(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 33) return false; // Wrong length? Stop here. + + card->CardNumber = get_linear_field(packed, 8, 24); + card->FacilityCode = get_linear_field(packed, 1, 7); + card->ParitySupported = true; + card->ParityValid = + (get_bit_by_position(packed, 0) == evenparity32(get_linear_field(packed, 1, 16))) && + (get_bit_by_position(packed, 32) == oddparity32(get_linear_field(packed, 16, 16))); + return true; +} + + +bool Pack_H10306(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_t)); + if (card->FacilityCode > 0xFFFF) return false; // Can't encode FC. + if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. + + packed->Length = 34; // Set number of bits + packed->bot |= (card->CardNumber & 0xFFFF) << 1; + packed->bot |= (card->FacilityCode & 0x7FFF) << 17; + packed->mid |= (card->FacilityCode & 0x8000) >> 15; + if (card->ParitySupported){ + packed->mid |= (evenparity32((packed->mid & 0x00000001) ^ (packed->bot & 0xFFFE0000)) & 1) << 1; + packed->bot |= ( oddparity32(packed->bot & 0x0001FFFE) & 1); + } + return add_HID_header(packed); +} +bool Unpack_H10306(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 34) return false; // Wrong length? Stop here. + + card->CardNumber = (packed->bot >> 1) & 0xFFFF; + card->FacilityCode = ((packed->mid & 1) << 15) | ((packed->bot >> 17) & 0xFF); + card->ParitySupported = true; + card->ParityValid = + ((evenparity32((packed->mid & 0x00000001) ^ (packed->bot & 0xFFFE0000)) & 1) == ((packed->mid >> 1) & 1)) && + ((oddparity32(packed->bot & 0x0001FFFE) & 1) == ((packed->bot & 1))); + return true; +} +bool Pack_N1002(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_t)); + if (card->FacilityCode > 0xFF) return false; // Can't encode FC. + if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. + + packed->Length = 34; // Set number of bits + set_linear_field(packed, card->FacilityCode, 9, 8); + set_linear_field(packed, card->CardNumber, 17, 16); + return add_HID_header(packed); +} +bool Unpack_N1002(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 34) return false; // Wrong length? Stop here. + + card->CardNumber = get_linear_field(packed, 17, 16); + card->FacilityCode = get_linear_field(packed, 9, 8); + card->ParitySupported = false; + return true; +} + + +bool Pack_C1k35s(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_t)); + if (card->FacilityCode > 0xFFF) return false; // Can't encode FC. + if (card->CardNumber > 0xFFFFF) return false; // Can't encode CN. + + packed->Length = 35; // Set number of bits + packed->bot |= (card->CardNumber & 0x000FFFFF) << 1; + packed->bot |= (card->FacilityCode & 0x000007FF) << 21; + packed->mid |= (card->FacilityCode & 0x00000800) >> 11; + if (card->ParitySupported){ + packed->mid |= (evenparity32((packed->mid & 0x00000001) ^ (packed->bot & 0xB6DB6DB6)) & 1) << 1; + packed->bot |= ( oddparity32((packed->mid & 0x00000003) ^ (packed->bot & 0x6DB6DB6C)) & 1); + packed->mid |= ( oddparity32((packed->mid & 0x00000003) ^ (packed->bot & 0xFFFFFFFF)) & 1) << 2; + } + return add_HID_header(packed); +} +bool Unpack_C1k35s(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 35) return false; // Wrong length? Stop here. + + card->CardNumber = (packed->bot >> 1) & 0x000FFFFF; + card->FacilityCode = ((packed->mid & 1) << 11) | ((packed->bot >> 21)); + card->ParitySupported = true; + card->ParityValid = + (evenparity32((packed->mid & 0x00000001) ^ (packed->bot & 0xB6DB6DB6)) == ((packed->mid >> 1) & 1)) && + ( oddparity32((packed->mid & 0x00000003) ^ (packed->bot & 0x6DB6DB6C)) == ((packed->bot >> 0) & 1)) && + ( oddparity32((packed->mid & 0x00000003) ^ (packed->bot & 0xFFFFFFFF)) == ((packed->mid >> 2) & 1)); + return true; +} + +bool Pack_H10320(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_t)); + if (card->FacilityCode > 0) return false; // Can't encode FC. (none in this format) + if (card->CardNumber > 99999999) return false; // Can't encode CN. + + packed->Length = 36; // Set number of bits + // This card is BCD-encoded rather than binary. Set the 4-bit groups independently. + set_linear_field(packed, (card->CardNumber / 10000000) % 10, 0, 4); + set_linear_field(packed, (card->CardNumber / 1000000) % 10, 4, 4); + set_linear_field(packed, (card->CardNumber / 100000) % 10, 8, 4); + set_linear_field(packed, (card->CardNumber / 10000) % 10, 12, 4); + set_linear_field(packed, (card->CardNumber / 1000) % 10, 16, 4); + set_linear_field(packed, (card->CardNumber / 100) % 10, 20, 4); + set_linear_field(packed, (card->CardNumber / 10) % 10, 24, 4); + set_linear_field(packed, (card->CardNumber / 1) % 10, 28, 4); + if (card->ParitySupported){ + set_bit_by_position(packed, evenparity32( + get_nonlinear_field(packed, 8, (uint8_t[]){0, 4, 8, 12, 16, 20, 24, 28}) + ), 32); + set_bit_by_position(packed, oddparity32( + get_nonlinear_field(packed, 8, (uint8_t[]){1, 5, 9, 13, 17, 21, 25, 29}) + ), 33); + set_bit_by_position(packed, evenparity32( + get_nonlinear_field(packed, 8, (uint8_t[]){2, 6, 10, 14, 18, 22, 28, 30}) + ), 34); + set_bit_by_position(packed, evenparity32( + get_nonlinear_field(packed, 8, (uint8_t[]){3, 7, 11, 15, 19, 23, 29, 31}) + ), 35); + } + return add_HID_header(packed); +} +bool Unpack_H10320(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 36) return false; // Wrong length? Stop here. + // This card is BCD-encoded rather than binary. Get the 4-bit groups independently. + card->CardNumber += get_linear_field(packed, 0, 4) * 10000000; + card->CardNumber += get_linear_field(packed, 4, 4) * 1000000; + card->CardNumber += get_linear_field(packed, 8, 4) * 100000; + card->CardNumber += get_linear_field(packed, 12, 4) * 10000; + card->CardNumber += get_linear_field(packed, 16, 4) * 1000; + card->CardNumber += get_linear_field(packed, 20, 4) * 100; + card->CardNumber += get_linear_field(packed, 24, 4) * 10; + card->CardNumber += get_linear_field(packed, 28, 4); + card->ParitySupported = true; + card->ParityValid = + (get_bit_by_position(packed, 32) == evenparity32(get_nonlinear_field(packed, 8, (uint8_t[]){0, 4, 8, 12, 16, 20, 24, 28}))) && + (get_bit_by_position(packed, 33) == oddparity32(get_nonlinear_field(packed, 8, (uint8_t[]){1, 5, 9, 13, 17, 21, 25, 29}))) && + (get_bit_by_position(packed, 34) == evenparity32(get_nonlinear_field(packed, 8, (uint8_t[]){2, 6, 10, 14, 18, 22, 28, 30}))) && + (get_bit_by_position(packed, 35) == evenparity32(get_nonlinear_field(packed, 8, (uint8_t[]){3, 7, 11, 15, 19, 23, 29, 31}))); + return true; +} + + +bool Pack_H10302(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_t)); + if (card->FacilityCode > 0) return false; // Can't encode FC. (none in this format) + if (card->CardNumber > 0x00000007FFFFFFFF) return false; // Can't encode CN. + + packed->Length = 37; // Set number of bits + set_linear_field(packed, card->CardNumber, 1, 35); + if (card->ParitySupported){ + set_bit_by_position(packed, + evenparity32(get_linear_field(packed, 1, 18)) + , 0); + set_bit_by_position(packed, + oddparity32(get_linear_field(packed, 18, 18)) + , 36); + } + return add_HID_header(packed); +} +bool Unpack_H10302(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 37) return false; // Wrong length? Stop here. + + card->CardNumber = get_linear_field(packed, 1, 35); + card->ParitySupported = true; + card->ParityValid = + (get_bit_by_position(packed, 0) == evenparity32(get_linear_field(packed, 1, 18))) && + (get_bit_by_position(packed, 36) == oddparity32(get_linear_field(packed, 18, 18))); + + return true; +} + +bool Pack_H10304(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_t)); + if (card->FacilityCode > 0x0000FFFF) return false; // Can't encode FC. + if (card->CardNumber > 0x0007FFFF) return false; // Can't encode CN. + + packed->Length = 37; // Set number of bits + packed->bot |= (card->CardNumber & 0x0007FFFF) << 1; + packed->bot |= (card->FacilityCode & 0x00000FFF) << 20; + packed->mid |= (card->FacilityCode & 0x0000F000) >> 12; + if (card->ParitySupported){ + packed->mid |= (evenparity32((packed->mid & 0x0000000F) ^ (packed->bot & 0xFFFC0000)) & 1) << 4; + packed->bot |= ( oddparity32(packed->bot & 0x0007FFFE) & 1); + } + return add_HID_header(packed); +} +bool Unpack_H10304(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 37) return false; // Wrong length? Stop here. + + card->CardNumber = (packed->bot >> 1) & 0x0007FFFF; + card->FacilityCode = ((packed->mid & 0xF) << 12) | ((packed->bot >> 20)); + card->ParitySupported = true; + card->ParityValid = + (evenparity32((packed->mid & 0x0000000F) ^ (packed->bot & 0xFFFC0000)) == ((packed->mid >> 4) & 1)) && + (oddparity32( packed->bot & 0x0007FFFE) == (packed->bot & 1)); + return true; +} + + +bool Pack_P10001(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_t)); + if (card->FacilityCode > 0xFFF) return false; // Can't encode FC. + if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. + + packed->Length = 40; // Set number of bits + set_linear_field(packed, 0xF, 0, 4); + set_linear_field(packed, card->FacilityCode, 4, 12); + set_linear_field(packed, card->CardNumber, 16, 16); + + if (card->ParitySupported){ + set_linear_field(packed, + get_linear_field(packed, 0, 8) ^ + get_linear_field(packed, 8, 8) ^ + get_linear_field(packed, 16, 8) ^ + get_linear_field(packed, 24, 8) + , 32, 8); + } + return add_HID_header(packed); +} +bool Unpack_P10001(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 40) return false; // Wrong length? Stop here. + + card->CardNumber = get_linear_field(packed, 16, 16); + card->FacilityCode = get_linear_field(packed, 4, 12); + card->ParitySupported = true; + card->ParityValid = ( + get_linear_field(packed, 0, 8) ^ + get_linear_field(packed, 8, 8) ^ + get_linear_field(packed, 16, 8) ^ + get_linear_field(packed, 24, 8) + ) == get_linear_field(packed, 32, 8); + return true; +} + + +bool Pack_C1k48s(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_t)); + if (card->FacilityCode > 0x003FFFFF) return false; // Can't encode FC. + if (card->CardNumber > 0x007FFFFF) return false; // Can't encode CN. + + packed->Length = 48; // Set number of bits + packed->bot |= (card->CardNumber & 0x007FFFFF) << 1; + packed->bot |= (card->FacilityCode & 0x000000FF) << 24; + packed->mid |= (card->FacilityCode & 0x003FFF00) >> 8; + if (card->ParitySupported){ + packed->mid |= (evenparity32((packed->mid & 0x00001B6D) ^ (packed->bot & 0xB6DB6DB6)) & 1) << 14; + packed->bot |= ( oddparity32((packed->mid & 0x000036DB) ^ (packed->bot & 0x6DB6DB6C)) & 1); + packed->mid |= ( oddparity32((packed->mid & 0x00007FFF) ^ (packed->bot & 0xFFFFFFFF)) & 1) << 15; + } + return add_HID_header(packed); +} +bool Unpack_C1k48s(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 48) return false; // Wrong length? Stop here. + + card->CardNumber = (packed->bot >> 1) & 0x007FFFFF; + card->FacilityCode = ((packed->mid & 0x00003FFF) << 8) | ((packed->bot >> 24)); + card->ParitySupported = true; + card->ParityValid = + (evenparity32((packed->mid & 0x00001B6D) ^ (packed->bot & 0xB6DB6DB6)) == ((packed->mid >> 14) & 1)) && + ( oddparity32((packed->mid & 0x000036DB) ^ (packed->bot & 0x6DB6DB6C)) == ((packed->bot >> 0) & 1)) && + ( oddparity32((packed->mid & 0x00007FFF) ^ (packed->bot & 0xFFFFFFFF)) == ((packed->mid >> 15) & 1)); + return true; +} + +static const hidcardformat_t FormatTable[] = { + {"H10301", Pack_H10301, Unpack_H10301, "HID H10301 26-bit"}, // imported from old pack/unpack + {"Tecom27", Pack_Tecom27, Unpack_Tecom27, "Tecom 27-bit"}, // from cardinfo.barkweb.com.au + {"2804W", Pack_2804W, Unpack_2804W, "2804 Wiegand"}, // from cardinfo.barkweb.com.au + {"ATSW30", Pack_ATSW30, Unpack_ATSW30, "ATS Wiegand 30-bit"}, // from cardinfo.barkweb.com.au + {"ADT31", Pack_ADT31, Unpack_ADT31, "HID ADT 31-bit"}, // from cardinfo.barkweb.com.au + {"D10202", Pack_D10202, Unpack_D10202, "HID D10202 33-bit"}, // from cardinfo.barkweb.com.au + {"H10306", Pack_H10306, Unpack_H10306, "HID H10306 34-bit"}, // imported from old pack/unpack + {"N1002", Pack_N1002, Unpack_N1002, "HID N1002 34-bit"}, // from cardinfo.barkweb.com.au + {"C1k35s", Pack_C1k35s, Unpack_C1k35s, "HID Corporate 1000 35-bit standard layout"}, // imported from old pack/unpack + {"H10320", Pack_H10320, Unpack_H10320, "HID H10320 36-bit BCD, Card num only"}, // from Proxmark forums + {"H10302", Pack_H10302, Unpack_H10302, "HID H10302 37-bit huge, Card num only"}, // from Proxmark forums + {"H10304", Pack_H10304, Unpack_H10304, "HID H10304 37-bit"}, // imported from old pack/unpack + {"P10001", Pack_P10001, Unpack_P10001, "HID P10001 Honeywell 40-bit"}, // from cardinfo.barkweb.com.au + {"C1k48s", Pack_C1k48s, Unpack_C1k48s, "HID Corporate 1000 48-bit standard layout"}, // imported from old pack/unpack + {NULL, NULL, NULL, NULL} // Must null terminate array +}; + +void HIDListFormats(){ + if (FormatTable[0].Name == NULL) + return; + int i = 0; + PrintAndLog("%-10s %s", "Name", "Description"); + PrintAndLog("------------------------------------------------------------"); + while (FormatTable[i].Name) + { + PrintAndLog("%-10s %s", FormatTable[i].Name, FormatTable[i].Descrp); + ++i; + } + PrintAndLog(""); + return; +} + +hidcardformat_t HIDGetCardFormat(int idx){ + return FormatTable[idx]; +} + +int HIDFindCardFormat(const char *format) +{ + if (FormatTable[0].Name == NULL) + return -1; + int i = 0; + while (FormatTable[i].Name && strcmp(FormatTable[i].Name, format)) + { + ++i; + } + + if (FormatTable[i].Name) { + return i; + } else { + return -1; + } +} + +bool HIDPack(/* in */int FormatIndex, /* in */hidproxcard_t* card, /* out */hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_t)); + if (FormatIndex < 0 || FormatIndex >= (sizeof(FormatTable) / sizeof(FormatTable[0]))) return false; + return FormatTable[FormatIndex].Pack(card, packed); +} + +bool HIDTryUnpack(/* in */hidproxmessage_t* packed){ + if (FormatTable[0].Name == NULL) + return false; + + bool result = false; + int i = 0; + hidproxcard_t card; + memset(&card, 0, sizeof(hidproxcard_t)); + while (FormatTable[i].Name) + { + if (FormatTable[i].Unpack(packed, &card)){ + result = true; + PrintAndLog("%-16s FC: %u, Card %"PRIu64", Parity %s", + FormatTable[i].Name, + card.FacilityCode, + card.CardNumber, + (card.ParitySupported) ? ((card.ParityValid) ? "valid" : "invalid") : "n/a" + ); + } + ++i; + } + return result; +} diff --git a/client/hidcardformats.h b/client/hidcardformats.h new file mode 100644 index 00000000..8ff887f7 --- /dev/null +++ b/client/hidcardformats.h @@ -0,0 +1,32 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 grauerfuchs +// +// 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. +//----------------------------------------------------------------------------- +// HID card format packing/unpacking routines +//----------------------------------------------------------------------------- + +#ifndef HIDCARDFORMATS_H__ +#define HIDCARDFORMATS_H__ + +#include +#include +#include "hidcardformatutils.h" + +// Structure for defined HID card formats available for packing/unpacking +typedef struct hidcardformat_s{ + const char* Name; + bool (*Pack)(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed); + bool (*Unpack)(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card); + const char* Descrp; +} hidcardformat_t; + +void HIDListFormats(); +int HIDFindCardFormat(const char *format); +hidcardformat_t HIDGetCardFormat(int idx); +bool HIDPack(/* in */int FormatIndex, /* in */hidproxcard_t* card, /* out */hidproxmessage_t* packed); +bool HIDTryUnpack(/* in */hidproxmessage_t* packed); + +#endif diff --git a/client/hidcardformatutils.c b/client/hidcardformatutils.c new file mode 100644 index 00000000..d7c159cd --- /dev/null +++ b/client/hidcardformatutils.c @@ -0,0 +1,172 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 grauerfuchs +// +// 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. +//----------------------------------------------------------------------------- +// HID card format packing/unpacking support functions +//----------------------------------------------------------------------------- + +#include +#include +#include +#include +#include "hidcardformatutils.h" +#include "ui.h" + +bool get_bit_by_position(/* in */hidproxmessage_t* data, /* in */uint8_t pos){ + if (pos >= data->Length) return false; + pos = (data->Length - pos) - 1; // invert ordering; Indexing goes from 0 to 1. Subtract 1 for weight of bit. + bool result = false; + if (pos > 95) + result = false; + else if (pos > 63) + result = (data->top >> (pos - 64)) & 1; + else if (pos > 31) + result = (data->mid >> (pos - 32)) & 1; + else + result = (data->bot >> pos) & 1; + return result; +} +bool set_bit_by_position(/* inout */hidproxmessage_t* data, /* in */bool value, /* in */uint8_t pos){ + if (pos >= data->Length) return false; + pos = (data->Length - pos) - 1; // invert ordering; Indexing goes from 0 to 1. Subtract 1 for weight of bit. + if (pos > 95) { + return false; + } else if (pos > 63) { + if (value) + data->top |= (1 << (pos - 64)); + else + data->top &= ~(1 << (pos - 64)); + return true; + } else if (pos > 31) { + if (value) + data->mid |= (1 << (pos - 32)); + else + data->mid &= ~(1 << (pos - 32)); + return true; + } else { + if (value) + data->bot |= (1 << pos); + else + data->bot &= ~(1 << pos); + return true; + } +} +/** + * Safeguard the data by doing a manual deep copy + * + * At the time of the initial writing, the struct does not contain pointers. That doesn't + * mean it won't eventually contain one, however. To prevent memory leaks and erroneous + * aliasing, perform the copy function manually instead. Hence, this function. + * + * If the definition of the hid_proxmessage struct changes, this function must also + * be updated to match. + */ +void proxmessage_datacopy(/*in*/hidproxmessage_t* src, /*out*/hidproxmessage_t* dest){ + dest->bot = src->bot; + dest->mid = src->mid; + dest->top = src->top; + dest->Length = src->Length; +} +/** + * + * Yes, this is horribly inefficient for linear data. + * The current code is a temporary measure to have a working function in place + * until all the bugs shaken from the block/chunk version of the code. + * + */ +uint64_t get_linear_field(/* in */hidproxmessage_t* data, uint8_t firstBit, uint8_t length){ + uint64_t result = 0; + for (uint8_t i = 0; i < length; i++ ) { + result = (result << 1) | get_bit_by_position(data, firstBit + i); + } + return result; +} +bool set_linear_field(/* inout */hidproxmessage_t* data, uint64_t value, uint8_t firstBit, uint8_t length){ + hidproxmessage_t tmpdata; + proxmessage_datacopy(data, &tmpdata); + bool result = true; + for (int i = 0; i < length; i++){ + result &= set_bit_by_position(&tmpdata, (value >> ((length - i) - 1)) & 1, firstBit + i); + } + if (result) proxmessage_datacopy(&tmpdata, data); + return result; +} + +uint64_t get_nonlinear_field(/* in */hidproxmessage_t* data, uint8_t numBits, uint8_t* bits){ + uint64_t result = 0; + for (int i = 0; i < numBits; i++){ + result = (result << 1) | (get_bit_by_position(data, *(bits+i)) & 1); + } + return result; +} +bool set_nonlinear_field(/* inout */hidproxmessage_t* data, uint64_t value, uint8_t numBits, uint8_t* bits){ + hidproxmessage_t tmpdata; + proxmessage_datacopy(data, &tmpdata); + bool result = true; + for (int i = 0; i < numBits; i++){ + result &= set_bit_by_position(&tmpdata, (value >> ((numBits - i) - 1)) & 1, *(bits + i)); + } + if (result) proxmessage_datacopy(&tmpdata, data); + return result; +} + +uint8_t get_length_from_header(/* inout */hidproxmessage_t* data) { + uint8_t len = 0; + + uint32_t hFmt; // for calculating card length + if ((data->top & 0x000FFFFF) > 0) { // > 64 bits + hFmt = data->top & 0x000FFFFF; + len = 64; + } else if ((data->mid & 0xFFFFFFC0) > 0) { // < 63-38 bits + hFmt = data->mid & 0xFFFFFFC0; + len = 32; + } else if ((data->mid & 0x00000020) == 0) { // 37 bits + hFmt = 0; + len = 37; + } else if ((data->mid & 0x0000001F) > 0){ // 36-32 bits + hFmt = data->mid & 0x0000001F; + len = 32; + } else { // <32 bits + hFmt = data->bot; + len = 0; + } + + while (hFmt > 1) { + hFmt >>= 1; + len++; + } + return len; +} + +hidproxmessage_t initialize_proxmessage_object(uint32_t top, uint32_t mid, uint32_t bot){ + struct hidproxmessage_s result; + memset(&result, 0, sizeof(hidproxmessage_t)); + result.top = top; + result.mid = mid; + result.bot = bot; + result.Length = get_length_from_header(&result); + return result; +} +bool add_HID_header(/* inout */hidproxmessage_t* data){ + if (data->Length > 84 || data->Length == 0) return false; // Invalid value + + if (data->Length >= 64){ + data->top |= 1 << (data->Length - 64); // leading 1: start bit + data->top |= 0x09e00000; // Extended-length header + } else if (data->Length > 37){ + data->mid |= 1 << (data->Length - 32); // leading 1: start bit + data->top |= 0x09e00000; // Extended-length header + } else if (data->Length == 37){ + // No header bits added to 37-bit cards + } else if (data->Length >= 32){ + data->mid |= 0x20; // Bit 37; standard header + data->mid |= 1 << (data->Length - 32); // leading 1: start bit + } else { + data->mid |= 0x20; // Bit 37; standard header + data->bot |= 1 << data->Length; // leading 1: start bit + } + return true; +} \ No newline at end of file diff --git a/client/hidcardformatutils.h b/client/hidcardformatutils.h new file mode 100644 index 00000000..afb76f1d --- /dev/null +++ b/client/hidcardformatutils.h @@ -0,0 +1,47 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 grauerfuchs +// +// 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. +//----------------------------------------------------------------------------- +// HID card format packing/unpacking support functions +//----------------------------------------------------------------------------- + +#ifndef HIDCARDFORMATUTILS_H__ +#define HIDCARDFORMATUTILS_H__ + +#include +#include +#include + +// Structure for packed HID messages +// Always align lowest value (last transmitted) bit to ordinal position 0 (lowest valued bit bottom) +typedef struct hidproxmessage_s{ + uint8_t Length; // Number of encoded bits in prox message (excluding headers and preamble) + uint32_t top; // Bits in x<<64 positions + uint32_t mid; // Bits in x<<32 positions + uint32_t bot; // Lowest ordinal positions +} hidproxmessage_t; + +// Structure for unpacked HID prox cards +typedef struct hidproxcard_s{ + uint32_t FacilityCode; + uint64_t CardNumber; + uint8_t IssueLevel; + bool ParitySupported; // Only valid for responses + bool ParityValid; // Only valid for responses +} hidproxcard_t; + +bool get_bit_by_position(/* in */hidproxmessage_t* data, /* in */uint8_t pos); +bool set_bit_by_position(/* inout */hidproxmessage_t* data, /* in */bool value, /* in */uint8_t pos); + +uint64_t get_linear_field(/* in */hidproxmessage_t* data, /* in */uint8_t firstBit, /* in */uint8_t length); +bool set_linear_field(/* inout */hidproxmessage_t* data, /* in */uint64_t value, /* in */uint8_t firstBit, /* in */uint8_t length); + +uint64_t get_nonlinear_field(/* in */hidproxmessage_t* data, /* in */uint8_t numBits, /* in */uint8_t* bits); +bool set_nonlinear_field(/* inout */hidproxmessage_t* data, /* in */uint64_t value, /* in */uint8_t numBits, /* in */uint8_t* bits); + +hidproxmessage_t initialize_proxmessage_object(/* in */uint32_t top, /* in */uint32_t mid, /* in */uint32_t bot); +bool add_HID_header(/* inout */hidproxmessage_t* data); +#endif From 5d643cc03337448602b9ea7aee4d9c4581ca9434 Mon Sep 17 00:00:00 2001 From: grauerfuchs <42082416+grauerfuchs@users.noreply.github.com> Date: Tue, 28 Aug 2018 13:34:21 -0400 Subject: [PATCH 251/310] lf hid: Added encode/decode support for Issue Level Issue Level added to encode/decode support By default, invalid parity will not show on decode (with option to show) Added two new 36-bit formats including first format to use issue level --- client/cmdlfhid.c | 18 ++-- client/hidcardformats.c | 160 ++++++++++++++++++++++++++++-------- client/hidcardformats.h | 2 +- client/hidcardformatutils.c | 2 +- client/hidcardformatutils.h | 2 +- 5 files changed, 142 insertions(+), 42 deletions(-) diff --git a/client/cmdlfhid.c b/client/cmdlfhid.c index d8e151b9..66dfae8b 100644 --- a/client/cmdlfhid.c +++ b/client/cmdlfhid.c @@ -98,7 +98,7 @@ int CmdFSKdemodHID(const char *Cmd) ); hidproxmessage_t packed = initialize_proxmessage_object(hi2, hi, lo); - bool ret = HIDTryUnpack(&packed); + bool ret = HIDTryUnpack(&packed, false); if (!ret) { PrintAndLog("Invalid or unsupported tag length."); @@ -164,20 +164,26 @@ int CmdHIDClone(const char *Cmd) int CmdHIDDecode(const char *Cmd){ if (strlen(Cmd)<3) { - PrintAndLog("Usage: lf hid decode "); + PrintAndLog("Usage: lf hid decode {p}"); + PrintAndLog(" (optional) p: Ignore invalid parity"); PrintAndLog(" sample: lf hid decode 2006f623ae"); return 0; } uint32_t top = 0, mid = 0, bot = 0; + bool ignoreParity = false; hexstring_to_int96(&top, &mid, &bot, Cmd); hidproxmessage_t packed = initialize_proxmessage_object(top, mid, bot); - HIDTryUnpack(&packed); + + char opt = param_getchar(Cmd, 1); + if (opt == 'p') ignoreParity = true; + + HIDTryUnpack(&packed, ignoreParity); return 0; } int CmdHIDEncode(const char *Cmd) { if (strlen(Cmd) == 0) { - PrintAndLog("Usage: lf hid encode "); + PrintAndLog("Usage: lf hid encode [issue level (decimal)]"); PrintAndLog(" sample: lf hid encode H10301 123 4567"); return 0; } @@ -201,6 +207,7 @@ int CmdHIDEncode(const char *Cmd) { memset(&card, 0, sizeof(hidproxcard_t)); card.FacilityCode = param_get32ex(Cmd, 1, 0, 10); card.CardNumber = param_get64ex(Cmd, 2, 0, 10); + card.IssueLevel = param_get32ex(Cmd, 3, 0, 10); card.ParitySupported = true; // Try to encode parity if supported. hidproxmessage_t packed; @@ -221,7 +228,7 @@ int CmdHIDEncode(const char *Cmd) { int CmdHIDWrite(const char *Cmd) { if (strlen(Cmd) == 0) { - PrintAndLog("Usage: lf hid write "); + PrintAndLog("Usage: lf hid write [issue level (decimal)]"); PrintAndLog(" sample: lf hid write H10301 123 4567"); return 0; } @@ -245,6 +252,7 @@ int CmdHIDWrite(const char *Cmd) { memset(&card, 0, sizeof(hidproxcard_t)); card.FacilityCode = param_get32ex(Cmd, 1, 0, 10); card.CardNumber = param_get64ex(Cmd, 2, 0, 10); + card.IssueLevel = param_get32ex(Cmd, 3, 0, 10); card.ParitySupported = true; // Try to encode parity if supported. hidproxmessage_t packed; diff --git a/client/hidcardformats.c b/client/hidcardformats.c index 379eb68f..4d0c2a50 100644 --- a/client/hidcardformats.c +++ b/client/hidcardformats.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "cmddata.h" #include "hidcardformats.h" #include "hidcardformatutils.h" @@ -21,7 +22,8 @@ bool Pack_H10301(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ memset(packed, 0, sizeof(hidproxmessage_t)); if (card->FacilityCode > 0xFF) return false; // Can't encode FC. if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. - + if (card->IssueLevel > 0) return false; // Not used in this format + packed->Length = 26; // Set number of bits packed->bot |= (card->CardNumber & 0xFFFF) << 1; packed->bot |= (card->FacilityCode & 0xFF) << 17; @@ -47,6 +49,8 @@ bool Pack_Tecom27(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ memset(packed, 0, sizeof(hidproxmessage_t)); if (card->FacilityCode > 0x7FF) return false; // Can't encode FC. if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. + if (card->IssueLevel > 0) return false; // Not used in this format + packed->Length = 27; set_nonlinear_field(packed, card->FacilityCode, 10, (uint8_t[]){15, 19, 24, 23, 22, 18, 6, 10, 14, 3, 2}); set_nonlinear_field(packed, card->CardNumber, 16, (uint8_t[]){0, 1, 13, 12, 9, 26, 20, 16, 17, 21, 25, 7, 8, 11, 4, 5}); @@ -65,6 +69,8 @@ bool Pack_2804W(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ memset(packed, 0, sizeof(hidproxmessage_t)); if (card->FacilityCode > 0x0FF) return false; // Can't encode FC. if (card->CardNumber > 0x7FFF) return false; // Can't encode CN. + if (card->IssueLevel > 0) return false; // Not used in this format + packed->Length = 28; set_linear_field(packed, card->FacilityCode, 4, 8); set_linear_field(packed, card->CardNumber, 12, 15); @@ -98,6 +104,8 @@ bool Pack_ATSW30(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ memset(packed, 0, sizeof(hidproxmessage_t)); if (card->FacilityCode > 0xFFF) return false; // Can't encode FC. if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. + if (card->IssueLevel > 0) return false; // Not used in this format + packed->Length = 30; set_linear_field(packed, card->FacilityCode, 1, 12); set_linear_field(packed, card->CardNumber, 13, 16); @@ -126,6 +134,8 @@ bool Pack_ADT31(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ memset(packed, 0, sizeof(hidproxmessage_t)); if (card->FacilityCode > 0x0F) return false; // Can't encode FC. if (card->CardNumber > 0x7FFFFF) return false; // Can't encode CN. + if (card->IssueLevel > 0) return false; // Not used in this format + packed->Length = 31; set_linear_field(packed, card->FacilityCode, 1, 4); set_linear_field(packed, card->CardNumber, 5, 23); @@ -145,7 +155,8 @@ bool Pack_D10202(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ memset(packed, 0, sizeof(hidproxmessage_t)); if (card->FacilityCode > 0x007F) return false; // Can't encode FC. if (card->CardNumber > 0x00FFFFFF) return false; // Can't encode CN. - + if (card->IssueLevel > 0) return false; // Not used in this format + packed->Length = 33; // Set number of bits set_linear_field(packed, card->FacilityCode, 1, 7); set_linear_field(packed, card->CardNumber, 8, 24); @@ -174,7 +185,8 @@ bool Pack_H10306(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ memset(packed, 0, sizeof(hidproxmessage_t)); if (card->FacilityCode > 0xFFFF) return false; // Can't encode FC. if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. - + if (card->IssueLevel > 0) return false; // Not used in this format + packed->Length = 34; // Set number of bits packed->bot |= (card->CardNumber & 0xFFFF) << 1; packed->bot |= (card->FacilityCode & 0x7FFF) << 17; @@ -201,7 +213,8 @@ bool Pack_N1002(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ memset(packed, 0, sizeof(hidproxmessage_t)); if (card->FacilityCode > 0xFF) return false; // Can't encode FC. if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. - + if (card->IssueLevel > 0) return false; // Not used in this format + packed->Length = 34; // Set number of bits set_linear_field(packed, card->FacilityCode, 9, 8); set_linear_field(packed, card->CardNumber, 17, 16); @@ -222,7 +235,8 @@ bool Pack_C1k35s(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ memset(packed, 0, sizeof(hidproxmessage_t)); if (card->FacilityCode > 0xFFF) return false; // Can't encode FC. if (card->CardNumber > 0xFFFFF) return false; // Can't encode CN. - + if (card->IssueLevel > 0) return false; // Not used in this format + packed->Length = 35; // Set number of bits packed->bot |= (card->CardNumber & 0x000FFFFF) << 1; packed->bot |= (card->FacilityCode & 0x000007FF) << 21; @@ -252,17 +266,13 @@ bool Pack_H10320(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ memset(packed, 0, sizeof(hidproxmessage_t)); if (card->FacilityCode > 0) return false; // Can't encode FC. (none in this format) if (card->CardNumber > 99999999) return false; // Can't encode CN. - + if (card->IssueLevel > 0) return false; // Not used in this format + packed->Length = 36; // Set number of bits // This card is BCD-encoded rather than binary. Set the 4-bit groups independently. - set_linear_field(packed, (card->CardNumber / 10000000) % 10, 0, 4); - set_linear_field(packed, (card->CardNumber / 1000000) % 10, 4, 4); - set_linear_field(packed, (card->CardNumber / 100000) % 10, 8, 4); - set_linear_field(packed, (card->CardNumber / 10000) % 10, 12, 4); - set_linear_field(packed, (card->CardNumber / 1000) % 10, 16, 4); - set_linear_field(packed, (card->CardNumber / 100) % 10, 20, 4); - set_linear_field(packed, (card->CardNumber / 10) % 10, 24, 4); - set_linear_field(packed, (card->CardNumber / 1) % 10, 28, 4); + for (uint32_t idx = 0; idx < 8; idx++){ + set_linear_field(packed, (uint64_t)(card->CardNumber / pow(10, 7-idx)) % 10, idx * 4, 4); + } if (card->ParitySupported){ set_bit_by_position(packed, evenparity32( get_nonlinear_field(packed, 8, (uint8_t[]){0, 4, 8, 12, 16, 20, 24, 28}) @@ -283,14 +293,16 @@ bool Unpack_H10320(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ memset(card, 0, sizeof(hidproxcard_t)); if (packed->Length != 36) return false; // Wrong length? Stop here. // This card is BCD-encoded rather than binary. Get the 4-bit groups independently. - card->CardNumber += get_linear_field(packed, 0, 4) * 10000000; - card->CardNumber += get_linear_field(packed, 4, 4) * 1000000; - card->CardNumber += get_linear_field(packed, 8, 4) * 100000; - card->CardNumber += get_linear_field(packed, 12, 4) * 10000; - card->CardNumber += get_linear_field(packed, 16, 4) * 1000; - card->CardNumber += get_linear_field(packed, 20, 4) * 100; - card->CardNumber += get_linear_field(packed, 24, 4) * 10; - card->CardNumber += get_linear_field(packed, 28, 4); + for (uint32_t idx = 0; idx < 8; idx++){ + uint64_t val = get_linear_field(packed, idx * 4, 4); + if (val > 9){ + // Violation of BCD; Zero and exit. + card->CardNumber = 0; + return false; + } else { + card->CardNumber += val * pow(10, 7-idx); + } + } card->ParitySupported = true; card->ParityValid = (get_bit_by_position(packed, 32) == evenparity32(get_nonlinear_field(packed, 8, (uint8_t[]){0, 4, 8, 12, 16, 20, 24, 28}))) && @@ -300,12 +312,84 @@ bool Unpack_H10320(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ return true; } +bool Pack_S12906(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_t)); + if (card->FacilityCode > 0xFF) return false; // Can't encode FC. + if (card->IssueLevel > 0x03) return false; // Can't encode IL. + if (card->CardNumber > 0x00FFFFFF) return false; // Can't encode CN. + + packed->Length = 36; // Set number of bits + set_linear_field(packed, card->FacilityCode, 1, 8); + set_linear_field(packed, card->IssueLevel, 9, 2); + set_linear_field(packed, card->CardNumber, 11, 24); + if (card->ParitySupported){ + set_bit_by_position(packed, + oddparity32(get_linear_field(packed, 1, 17)) + , 0); + set_bit_by_position(packed, + oddparity32(get_linear_field(packed, 17, 18)) + , 35); + } + return add_HID_header(packed); +} +bool Unpack_S12906(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 36) return false; // Wrong length? Stop here. + + card->FacilityCode = get_linear_field(packed, 1, 8); + card->IssueLevel = get_linear_field(packed, 9, 2); + card->CardNumber = get_linear_field(packed, 11, 24); + + card->ParitySupported = true; + card->ParityValid = + (get_bit_by_position(packed, 0) == oddparity32(get_linear_field(packed, 1, 17))) && + (get_bit_by_position(packed, 35) == oddparity32(get_linear_field(packed, 17, 18))); + + return true; +} + +bool Pack_Sie36(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_t)); + if (card->FacilityCode > 0x0003FFFF) return false; // Can't encode FC. + if (card->IssueLevel > 0x00) return false; // Can't encode IL. + if (card->CardNumber > 0x0000FFFF) return false; // Can't encode CN. + if (card->IssueLevel > 0) return false; // Not used in this format + + packed->Length = 36; // Set number of bits + set_linear_field(packed, card->FacilityCode, 1, 18); + set_linear_field(packed, card->CardNumber, 19, 16); + if (card->ParitySupported){ + set_bit_by_position(packed, + oddparity32(get_nonlinear_field(packed, 23, (uint8_t[]){1, 3, 4, 6, 7, 9, 10, 12, 13, 15, 16, 18, 19, 21, 22, 24, 25, 27, 28, 30, 31, 33, 34})) + , 0); + set_bit_by_position(packed, + evenparity32(get_nonlinear_field(packed, 23, (uint8_t[]){1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26, 28, 29, 31, 32, 34})) + , 35); + } + return add_HID_header(packed); +} +bool Unpack_Sie36(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 36) return false; // Wrong length? Stop here. + + card->FacilityCode = get_linear_field(packed, 1, 18); + card->CardNumber = get_linear_field(packed, 19, 16); + + card->ParitySupported = true; + card->ParityValid = + (get_bit_by_position(packed, 0) == oddparity32(get_nonlinear_field(packed, 23, (uint8_t[]){1, 3, 4, 6, 7, 9, 10, 12, 13, 15, 16, 18, 19, 21, 22, 24, 25, 27, 28, 30, 31, 33, 34}))) && + (get_bit_by_position(packed, 35) == oddparity32(get_nonlinear_field(packed, 23, (uint8_t[]){1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26, 28, 29, 31, 32, 34}))); + + return true; +} + bool Pack_H10302(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ memset(packed, 0, sizeof(hidproxmessage_t)); if (card->FacilityCode > 0) return false; // Can't encode FC. (none in this format) if (card->CardNumber > 0x00000007FFFFFFFF) return false; // Can't encode CN. - + if (card->IssueLevel > 0) return false; // Not used in this format + packed->Length = 37; // Set number of bits set_linear_field(packed, card->CardNumber, 1, 35); if (card->ParitySupported){ @@ -335,7 +419,8 @@ bool Pack_H10304(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ memset(packed, 0, sizeof(hidproxmessage_t)); if (card->FacilityCode > 0x0000FFFF) return false; // Can't encode FC. if (card->CardNumber > 0x0007FFFF) return false; // Can't encode CN. - + if (card->IssueLevel > 0) return false; // Not used in this format + packed->Length = 37; // Set number of bits packed->bot |= (card->CardNumber & 0x0007FFFF) << 1; packed->bot |= (card->FacilityCode & 0x00000FFF) << 20; @@ -364,7 +449,8 @@ bool Pack_P10001(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ memset(packed, 0, sizeof(hidproxmessage_t)); if (card->FacilityCode > 0xFFF) return false; // Can't encode FC. if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. - + if (card->IssueLevel > 0) return false; // Not used in this format + packed->Length = 40; // Set number of bits set_linear_field(packed, 0xF, 0, 4); set_linear_field(packed, card->FacilityCode, 4, 12); @@ -401,7 +487,8 @@ bool Pack_C1k48s(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ memset(packed, 0, sizeof(hidproxmessage_t)); if (card->FacilityCode > 0x003FFFFF) return false; // Can't encode FC. if (card->CardNumber > 0x007FFFFF) return false; // Can't encode CN. - + if (card->IssueLevel > 0) return false; // Not used in this format + packed->Length = 48; // Set number of bits packed->bot |= (card->CardNumber & 0x007FFFFF) << 1; packed->bot |= (card->FacilityCode & 0x000000FF) << 24; @@ -437,6 +524,8 @@ static const hidcardformat_t FormatTable[] = { {"H10306", Pack_H10306, Unpack_H10306, "HID H10306 34-bit"}, // imported from old pack/unpack {"N1002", Pack_N1002, Unpack_N1002, "HID N1002 34-bit"}, // from cardinfo.barkweb.com.au {"C1k35s", Pack_C1k35s, Unpack_C1k35s, "HID Corporate 1000 35-bit standard layout"}, // imported from old pack/unpack + {"S12906", Pack_S12906, Unpack_S12906, "HID Simplex 36-bit"}, // from cardinfo.barkweb.com.au + {"Sie36", Pack_Sie36, Unpack_Sie36, "HID 36-bit Siemens"}, // from cardinfo.barkweb.com.au {"H10320", Pack_H10320, Unpack_H10320, "HID H10320 36-bit BCD, Card num only"}, // from Proxmark forums {"H10302", Pack_H10302, Unpack_H10302, "HID H10302 37-bit huge, Card num only"}, // from Proxmark forums {"H10304", Pack_H10304, Unpack_H10304, "HID H10304 37-bit"}, // imported from old pack/unpack @@ -487,7 +576,7 @@ bool HIDPack(/* in */int FormatIndex, /* in */hidproxcard_t* card, /* out */hidp return FormatTable[FormatIndex].Pack(card, packed); } -bool HIDTryUnpack(/* in */hidproxmessage_t* packed){ +bool HIDTryUnpack(/* in */hidproxmessage_t* packed, /* in */bool ignoreParity){ if (FormatTable[0].Name == NULL) return false; @@ -498,13 +587,16 @@ bool HIDTryUnpack(/* in */hidproxmessage_t* packed){ while (FormatTable[i].Name) { if (FormatTable[i].Unpack(packed, &card)){ - result = true; - PrintAndLog("%-16s FC: %u, Card %"PRIu64", Parity %s", - FormatTable[i].Name, - card.FacilityCode, - card.CardNumber, - (card.ParitySupported) ? ((card.ParityValid) ? "valid" : "invalid") : "n/a" - ); + if (ignoreParity || !card.ParitySupported || card.ParityValid){ + result = true; + PrintAndLog("%-16s FC: %u, Card: %"PRIu64", IL: %u, Parity %s", + FormatTable[i].Name, + card.FacilityCode, + card.CardNumber, + card.IssueLevel, + (card.ParitySupported) ? ((card.ParityValid) ? "valid" : "invalid") : "n/a" + ); + } } ++i; } diff --git a/client/hidcardformats.h b/client/hidcardformats.h index 8ff887f7..b8c6afb4 100644 --- a/client/hidcardformats.h +++ b/client/hidcardformats.h @@ -27,6 +27,6 @@ void HIDListFormats(); int HIDFindCardFormat(const char *format); hidcardformat_t HIDGetCardFormat(int idx); bool HIDPack(/* in */int FormatIndex, /* in */hidproxcard_t* card, /* out */hidproxmessage_t* packed); -bool HIDTryUnpack(/* in */hidproxmessage_t* packed); +bool HIDTryUnpack(/* in */hidproxmessage_t* packed, /* in */bool ignoreParity); #endif diff --git a/client/hidcardformatutils.c b/client/hidcardformatutils.c index d7c159cd..3abee223 100644 --- a/client/hidcardformatutils.c +++ b/client/hidcardformatutils.c @@ -61,7 +61,7 @@ bool set_bit_by_position(/* inout */hidproxmessage_t* data, /* in */bool value, * mean it won't eventually contain one, however. To prevent memory leaks and erroneous * aliasing, perform the copy function manually instead. Hence, this function. * - * If the definition of the hid_proxmessage struct changes, this function must also + * If the definition of the hidproxmessage struct changes, this function must also * be updated to match. */ void proxmessage_datacopy(/*in*/hidproxmessage_t* src, /*out*/hidproxmessage_t* dest){ diff --git a/client/hidcardformatutils.h b/client/hidcardformatutils.h index afb76f1d..821fc9ad 100644 --- a/client/hidcardformatutils.h +++ b/client/hidcardformatutils.h @@ -29,7 +29,7 @@ typedef struct hidproxcard_s{ uint32_t FacilityCode; uint64_t CardNumber; uint8_t IssueLevel; - bool ParitySupported; // Only valid for responses + bool ParitySupported; bool ParityValid; // Only valid for responses } hidproxcard_t; From be59094de9527e0e935d6d1cdd445d5b78946f44 Mon Sep 17 00:00:00 2001 From: grauerfuchs <42082416+grauerfuchs@users.noreply.github.com> Date: Thu, 30 Aug 2018 21:01:21 -0400 Subject: [PATCH 252/310] lf hid improvements - encoding and long tag simulation **DEVICE FIRMWARE UPDATE** The code changes needed to support long tag emulation required an update to the device firmware. As of this patch, devices running older firmware will not be able to read or emulate HID tags until the firmware is updated. Additionally, devices with the firmware from this update or newer will not properly read or encode HID tags with a prior version client. The 'lf hid encode' command has been further refined, and is now entirely parameterized to support use of fields other than facility code and card number. The client help data has been updated to show the correct syntax. --- armsrc/appmain.c | 4 +- armsrc/apps.h | 4 +- armsrc/lfops.c | 94 ++++------- client/cmdlfhid.c | 224 ++++++++++++------------- client/hidcardformats.c | 324 +++++++++++++++++------------------- client/hidcardformats.h | 10 ++ client/hidcardformatutils.h | 4 +- 7 files changed, 311 insertions(+), 353 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 4034788a..48620d71 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -947,10 +947,10 @@ void UsbPacketReceived(uint8_t *packet, int len) cmd_send(CMD_ACK,SnoopLF(),0,0,0,0); break; case CMD_HID_DEMOD_FSK: - CmdHIDdemodFSK(c->arg[0], 0, 0, 1); + CmdHIDdemodFSK(c->arg[0], 0, 0, 0, 1); break; case CMD_HID_SIM_TAG: - CmdHIDsimTAG(c->arg[0], c->arg[1], 1); + CmdHIDsimTAG(c->arg[0], c->arg[1], c->arg[2], 1); break; case CMD_FSK_SIM_TAG: CmdFSKsimTAG(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); diff --git a/armsrc/apps.h b/armsrc/apps.h index 542e6b90..5b981f4f 100644 --- a/armsrc/apps.h +++ b/armsrc/apps.h @@ -68,11 +68,11 @@ void AcquireTiType(void); void AcquireRawBitsTI(void); void SimulateTagLowFrequency(int period, int gap, int ledcontrol); void SimulateTagLowFrequencyBidir(int divisor, int max_bitlen); -void CmdHIDsimTAG(int hi, int lo, int ledcontrol); +void CmdHIDsimTAG(int hi2, int hi, int lo, int ledcontrol); void CmdFSKsimTAG(uint16_t arg1, uint16_t arg2, size_t size, uint8_t *BitStream); void CmdASKsimTag(uint16_t arg1, uint16_t arg2, size_t size, uint8_t *BitStream); void CmdPSKsimTag(uint16_t arg1, uint16_t arg2, size_t size, uint8_t *BitStream); -void CmdHIDdemodFSK(int findone, int *high, int *low, int ledcontrol); +void CmdHIDdemodFSK(int findone, int *high2, int *high, int *low, int ledcontrol); void CmdAWIDdemodFSK(int findone, int *high, int *low, int ledcontrol); // Realtime demodulation mode for AWID26 void CmdEM410xdemod(int findone, int *high, int *low, int ledcontrol); void CmdIOdemodFSK(int findone, int *high, int *low, int ledcontrol); diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 911ba8da..7ad4dabe 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -593,7 +593,7 @@ static void fcAll(uint8_t fc, int *n, uint8_t clock, uint16_t *modCnt) // prepare a waveform pattern in the buffer based on the ID given then // simulate a HID tag until the button is pressed -void CmdHIDsimTAG(int hi, int lo, int ledcontrol) +void CmdHIDsimTAG(int hi2, int hi, int lo, int ledcontrol) { int n=0, i=0; /* @@ -606,8 +606,8 @@ void CmdHIDsimTAG(int hi, int lo, int ledcontrol) nor 1 bits, they are special patterns (a = set of 12 fc8 and b = set of 10 fc10) */ - if (hi>0xFFF) { - DbpString("Tags can only have 44 bits. - USE lf simfsk for larger tags"); + if (hi2>0x0FFFFFFF) { + DbpString("Tags can only have 44 or 84 bits. - USE lf simfsk for larger tags"); return; } // set LF so we don't kill the bigbuf we are setting with simulation data. @@ -621,13 +621,35 @@ void CmdHIDsimTAG(int hi, int lo, int ledcontrol) fc(8, &n); fc(10, &n); // logical 0 WDT_HIT(); - // manchester encode bits 43 to 32 - for (i=11; i>=0; i--) { - if ((i%4)==3) fc(0,&n); - if ((hi>>i)&1) { - fc(10, &n); fc(8, &n); // low-high transition - } else { - fc(8, &n); fc(10, &n); // high-low transition + if (hi2 > 0 || hi > 0xFFF){ + // manchester encode bits 91 to 64 (91-84 are part of the header) + for (i=27; i>=0; i--) { + if ((i%4)==3) fc(0,&n); + if ((hi2>>i)&1) { + fc(10, &n); fc(8, &n); // low-high transition + } else { + fc(8, &n); fc(10, &n); // high-low transition + } + } + WDT_HIT(); + // manchester encode bits 63 to 32 + for (i=31; i>=0; i--) { + if ((i%4)==3) fc(0,&n); + if ((hi>>i)&1) { + fc(10, &n); fc(8, &n); // low-high transition + } else { + fc(8, &n); fc(10, &n); // high-low transition + } + } + } else { + // manchester encode bits 43 to 32 + for (i=11; i>=0; i--) { + if ((i%4)==3) fc(0,&n); + if ((hi>>i)&1) { + fc(10, &n); fc(8, &n); // low-high transition + } else { + fc(8, &n); fc(10, &n); // high-low transition + } } } @@ -839,7 +861,7 @@ void CmdPSKsimTag(uint16_t arg1, uint16_t arg2, size_t size, uint8_t *BitStream) } // loop to get raw HID waveform then FSK demodulate the TAG ID from it -void CmdHIDdemodFSK(int findone, int *high, int *low, int ledcontrol) +void CmdHIDdemodFSK(int findone, int *high2, int *high, int *low, int ledcontrol) { uint8_t *dest = BigBuf_get_addr(); //const size_t sizeOfBigBuff = BigBuf_max_traceLen(); @@ -869,56 +891,12 @@ void CmdHIDdemodFSK(int findone, int *high, int *low, int ledcontrol) if (hi2 != 0){ //extra large HID tags 88/192 bits Dbprintf("TAG ID: %x%08x%08x (%d)", (unsigned int) hi2, (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF); - }else { //standard HID tags 44/96 bits - //Dbprintf("TAG ID: %x%08x (%d)",(unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF); //old print cmd - uint8_t bitlen = 0; - uint32_t fc = 0; - uint32_t cardnum = 0; - if (((hi>>5)&1) == 1){//if bit 38 is set then < 37 bit format is used - uint32_t lo2=0; - lo2=(((hi & 31) << 12) | (lo>>20)); //get bits 21-37 to check for format len bit - uint8_t idx3 = 1; - while(lo2 > 1){ //find last bit set to 1 (format len bit) - lo2=lo2 >> 1; - idx3++; - } - bitlen = idx3+19; - fc =0; - cardnum=0; - if(bitlen == 26){ - cardnum = (lo>>1)&0xFFFF; - fc = (lo>>17)&0xFF; - } - if(bitlen == 37){ - cardnum = (lo>>1)&0x7FFFF; - fc = ((hi&0xF)<<12)|(lo>>20); - } - if(bitlen == 34){ - cardnum = (lo>>1)&0xFFFF; - fc= ((hi&1)<<15)|(lo>>17); - } - if(bitlen == 35){ - cardnum = (lo>>1)&0xFFFFF; - fc = ((hi&1)<<11)|(lo>>21); - } - } - else { //if bit 38 is not set then 37 bit format is used - bitlen= 37; - fc =0; - cardnum=0; - if(bitlen==37){ - cardnum = (lo>>1)&0x7FFFF; - fc = ((hi&0xF)<<12)|(lo>>20); - } - } - //Dbprintf("TAG ID: %x%08x (%d)", - // (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF); - Dbprintf("TAG ID: %x%08x (%d) - Format Len: %dbit - FC: %d - Card: %d", - (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF, - (unsigned int) bitlen, (unsigned int) fc, (unsigned int) cardnum); + } else { //standard HID tags 44/96 bits + Dbprintf("TAG ID: %x%08x (%d)",(unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF); //old print cmd } if (findone){ if (ledcontrol) LED_A_OFF(); + *high2 = hi2; *high = hi; *low = lo; break; diff --git a/client/cmdlfhid.c b/client/cmdlfhid.c index 66dfae8b..38e7073c 100644 --- a/client/cmdlfhid.c +++ b/client/cmdlfhid.c @@ -53,6 +53,90 @@ int hexstring_to_int96(/* out */ uint32_t* hi2,/* out */ uint32_t* hi, /* out */ return i - 1; } +void usage_encode(){ + PrintAndLog("Usage: lf hid encode [ ] {...}"); + PrintAndLog(" Fields: c: Card number"); + PrintAndLog(" f: Facility code"); + PrintAndLog(" i: Issue Level"); + PrintAndLog(" o: OEM code"); + PrintAndLog(" example: lf hid encode H10301 f 123 c 4567"); +} +void PrintProxTagId(hidproxmessage_t *packed){ + if (packed->top != 0) { + PrintAndLog("HID Prox TAG ID: %x%08x%08x", + (uint32_t)packed->top, (uint32_t)packed->mid, (uint32_t)packed->bot); + } else { + PrintAndLog("HID Prox TAG ID: %x%08x", + (uint32_t)packed->mid, (uint32_t)packed->bot); + } +} +bool Encode(/* in */ const char *Cmd, /* out */ hidproxmessage_t *packed){ + int formatIndex = -1; + char format[16]; + memset(format, 0, sizeof(format)); + if (!strcmp(Cmd, "help") || !strcmp(Cmd, "h") || !strcmp(Cmd, "list") || !strcmp(Cmd, "?")){ + usage_encode(); + return false; + } else { + param_getstr(Cmd, 0, format, sizeof(format)); + formatIndex = HIDFindCardFormat(format); + if (formatIndex == -1) { + printf("Unknown format: %s\r\n", format); + return false; + } + } + hidproxcard_t data; + memset(&data, 0, sizeof(hidproxcard_t)); + uint8_t cmdp = 1; + while(param_getchar(Cmd, cmdp) != 0x00) { + switch(param_getchar(Cmd, cmdp)) { + case 'I': + case 'i': + data.IssueLevel = param_get32ex(Cmd, cmdp+1, 0, 10); + cmdp += 2; + break; + case 'F': + case 'f': + data.FacilityCode = param_get32ex(Cmd, cmdp+1, 0, 10); + cmdp += 2; + break; + case 'C': + case 'c': + data.CardNumber = param_get64ex(Cmd, cmdp+1, 0, 10); + cmdp += 2; + break; + case 'o': + case 'O': + data.OEM = param_get32ex(Cmd, cmdp+1, 0, 10); + cmdp += 2; + break; + default: + PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + return false; + } + } + memset(packed, 0, sizeof(hidproxmessage_t)); + if (!HIDPack(formatIndex, &data, packed)) + { + PrintAndLog("The card data could not be encoded in the selected format."); + return false; + } else { + return true; + } + +} +void Write(hidproxmessage_t *packed){ + UsbCommand c; + c.d.asBytes[0] = (packed->top != 0 && ((packed->mid & 0xFFFFFFC0) != 0)) + ? 1 : 0; // Writing long format? + c.cmd = CMD_HID_CLONE_TAG; + c.arg[0] = (packed->top & 0x000FFFFF); + c.arg[1] = packed->mid; + c.arg[2] = packed->bot; + SendCommand(&c); +} + + //by marshmellow (based on existing demod + holiman's refactor) //HID Prox demod - FSK RF/50 with preamble of 00011101 (then manchester encoded) //print full HID Prox ID and some bit format details if found @@ -88,18 +172,10 @@ int CmdFSKdemodHID(const char *Cmd) return 0; } - if (hi2 != 0) - PrintAndLog("HID Prox TAG ID: %x%08x%08x", - (unsigned int) hi2, (unsigned int) hi, (unsigned int) lo - ); - else - PrintAndLog("HID Prox TAG ID: %x%08x", - (unsigned int) hi, (unsigned int) lo - ); - hidproxmessage_t packed = initialize_proxmessage_object(hi2, hi, lo); - bool ret = HIDTryUnpack(&packed, false); + PrintProxTagId(&packed); + bool ret = HIDTryUnpack(&packed, false); if (!ret) { PrintAndLog("Invalid or unsupported tag length."); } @@ -126,39 +202,25 @@ int CmdHIDSim(const char *Cmd) { uint32_t hi2 = 0, hi = 0, lo = 0; hexstring_to_int96(&hi2, &hi, &lo, Cmd); - if (hi >= 0x40 || hi2 != 0) { - PrintAndLog("This looks like a long tag ID. Use 'lf simfsk' for long tags. Aborting!"); - return 0; + if (hi2 != 0) { + PrintAndLog("Emulating tag with ID %x%08x%08x", hi2, hi, lo); + } else { + PrintAndLog("Emulating tag with ID %x%08x", hi, lo); } - PrintAndLog("Emulating tag with ID %x%08x", hi, lo); PrintAndLog("Press pm3-button to abort simulation"); - UsbCommand c = {CMD_HID_SIM_TAG, {hi, lo, 0}}; + UsbCommand c = {CMD_HID_SIM_TAG, {hi2, hi, lo}}; SendCommand(&c); return 0; } int CmdHIDClone(const char *Cmd) { - unsigned int hi2 = 0, hi = 0, lo = 0; - UsbCommand c; - hexstring_to_int96(&hi2, &hi, &lo, Cmd); - - if (hi >= 0x40 || hi2 != 0) { - PrintAndLog("Cloning tag with long ID %x%08x%08x", hi2, hi, lo); - c.d.asBytes[0] = 1; - } else { - PrintAndLog("Cloning tag with ID %x%08x", hi, lo); - c.d.asBytes[0] = 0; - } - - c.cmd = CMD_HID_CLONE_TAG; - c.arg[0] = (hi2 & 0x000FFFFF); - c.arg[1] = hi; - c.arg[2] = lo; - - SendCommand(&c); + unsigned int top = 0, mid = 0, bot = 0; + hexstring_to_int96(&top, &mid, &bot, Cmd); + hidproxmessage_t packed = initialize_proxmessage_object(top, mid, bot); + Write(&packed); return 0; } @@ -183,104 +245,35 @@ int CmdHIDDecode(const char *Cmd){ } int CmdHIDEncode(const char *Cmd) { if (strlen(Cmd) == 0) { - PrintAndLog("Usage: lf hid encode [issue level (decimal)]"); - PrintAndLog(" sample: lf hid encode H10301 123 4567"); + usage_encode(); return 0; } - int formatIndex = -1; - if (!strcmp(Cmd, "help") || !strcmp(Cmd, "h") || !strcmp(Cmd, "list") || !strcmp(Cmd, "?")){ - HIDListFormats(); - return 0; - } else { - char format[16]; - memset(format, 0, sizeof(format)); - param_getstr(Cmd, 0, format, sizeof(format)); - formatIndex = HIDFindCardFormat(format); - if (formatIndex == -1) { - HIDListFormats(); - return 0; - } - } - - hidproxcard_t card; - memset(&card, 0, sizeof(hidproxcard_t)); - card.FacilityCode = param_get32ex(Cmd, 1, 0, 10); - card.CardNumber = param_get64ex(Cmd, 2, 0, 10); - card.IssueLevel = param_get32ex(Cmd, 3, 0, 10); - card.ParitySupported = true; // Try to encode parity if supported. - hidproxmessage_t packed; memset(&packed, 0, sizeof(hidproxmessage_t)); - if (HIDPack(formatIndex, &card, &packed)){ - if (packed.top != 0) { - PrintAndLog("HID Prox TAG ID: %x%08x%08x", - (unsigned int)packed.top, (unsigned int)packed.mid, (unsigned int)packed.bot); - } else { - PrintAndLog("HID Prox TAG ID: %x%08x", - (unsigned int)packed.mid, (unsigned int)packed.bot); - } - } else { - PrintAndLog("The provided data could not be encoded with the selected format."); - } + if (Encode(Cmd, &packed)) + PrintProxTagId(&packed); return 0; } int CmdHIDWrite(const char *Cmd) { if (strlen(Cmd) == 0) { - PrintAndLog("Usage: lf hid write [issue level (decimal)]"); - PrintAndLog(" sample: lf hid write H10301 123 4567"); + usage_encode(); return 0; } - - int formatIndex = -1; - if (!strcmp(Cmd, "help") || !strcmp(Cmd, "h") || !strcmp(Cmd, "list") || !strcmp(Cmd, "?")){ - HIDListFormats(); - return 0; - } else { - char format[16]; - memset(format, 0, sizeof(format)); - param_getstr(Cmd, 0, format, sizeof(format)); - formatIndex = HIDFindCardFormat(format); - if (formatIndex == -1) { - HIDListFormats(); - return 0; - } - } - - hidproxcard_t card; - memset(&card, 0, sizeof(hidproxcard_t)); - card.FacilityCode = param_get32ex(Cmd, 1, 0, 10); - card.CardNumber = param_get64ex(Cmd, 2, 0, 10); - card.IssueLevel = param_get32ex(Cmd, 3, 0, 10); - card.ParitySupported = true; // Try to encode parity if supported. - hidproxmessage_t packed; memset(&packed, 0, sizeof(hidproxmessage_t)); - if (HIDPack(formatIndex, &card, &packed)){ - UsbCommand c; - if (packed.top != 0) { - PrintAndLog("HID Prox TAG ID: %x%08x%08x", - (unsigned int)packed.top, (unsigned int)packed.mid, (unsigned int)packed.bot); - c.d.asBytes[0] = 1; - } else { - PrintAndLog("HID Prox TAG ID: %x%08x", - (unsigned int)packed.mid, (unsigned int)packed.bot); - c.d.asBytes[0] = 0; - } - - c.cmd = CMD_HID_CLONE_TAG; - c.arg[0] = (packed.top & 0x000FFFFF); - c.arg[1] = packed.mid; - c.arg[2] = packed.bot; - SendCommand(&c); - - } else { - PrintAndLog("The provided data could not be encoded with the selected format."); + if (Encode(Cmd, &packed)){ + PrintProxTagId(&packed); + Write(&packed); } return 0; } +int CmdHIDFormats(){ + HIDListFormats(); + return 0; +} static int CmdHelp(const char *Cmd); // define this now so the below won't error out. static command_t CommandTable[] = { @@ -290,8 +283,9 @@ static command_t CommandTable[] = {"sim", CmdHIDSim, 0, " -- HID tag simulator"}, {"clone", CmdHIDClone, 0, " -- Clone HID to T55x7 (tag must be in antenna)"}, {"decode", CmdHIDDecode, 1, " -- Try to decode an HID tag and show its contents"}, - {"encode", CmdHIDEncode, 1, " -- Encode an HID ID with the specified format, facility code and card number"}, - {"write", CmdHIDWrite, 0, " -- Encode and write to a T55x7 tag (tag must be in antenna)"}, + {"encode", CmdHIDEncode, 1, " -- Encode an HID ID with the specified format and fields"}, + {"formats", CmdHIDFormats, 1, "List supported card formats"}, + {"write", CmdHIDWrite, 0, " -- Encode and write to a T55x7 tag (tag must be in antenna)"}, {NULL, NULL, 0, NULL} }; diff --git a/client/hidcardformats.c b/client/hidcardformats.c index 4d0c2a50..8df5bdde 100644 --- a/client/hidcardformats.c +++ b/client/hidcardformats.c @@ -23,14 +23,12 @@ bool Pack_H10301(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ if (card->FacilityCode > 0xFF) return false; // Can't encode FC. if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. if (card->IssueLevel > 0) return false; // Not used in this format - + if (card->OEM > 0) return false; // Not used in this format packed->Length = 26; // Set number of bits packed->bot |= (card->CardNumber & 0xFFFF) << 1; packed->bot |= (card->FacilityCode & 0xFF) << 17; - if (card->ParitySupported){ - packed->bot |= oddparity32((packed->bot >> 1) & 0xFFF) & 1; - packed->bot |= (evenparity32((packed->bot >> 13) & 0xFFF) & 1) << 25; - } + packed->bot |= oddparity32((packed->bot >> 1) & 0xFFF) & 1; + packed->bot |= (evenparity32((packed->bot >> 13) & 0xFFF) & 1) << 25; return add_HID_header(packed); } bool Unpack_H10301(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ @@ -38,7 +36,6 @@ bool Unpack_H10301(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ if (packed->Length != 26) return false; // Wrong length? Stop here. card->CardNumber = (packed->bot >> 1) & 0xFFFF; card->FacilityCode = (packed->bot >> 17) & 0xFF; - card->ParitySupported = true; card->ParityValid = (oddparity32((packed->bot >> 1) & 0xFFF) == (packed->bot & 1)) && ((evenparity32((packed->bot >> 13) & 0xFFF) & 1) == ((packed->bot >> 25) & 1)); @@ -50,7 +47,7 @@ bool Pack_Tecom27(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ if (card->FacilityCode > 0x7FF) return false; // Can't encode FC. if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. if (card->IssueLevel > 0) return false; // Not used in this format - + if (card->OEM > 0) return false; // Not used in this format packed->Length = 27; set_nonlinear_field(packed, card->FacilityCode, 10, (uint8_t[]){15, 19, 24, 23, 22, 18, 6, 10, 14, 3, 2}); set_nonlinear_field(packed, card->CardNumber, 16, (uint8_t[]){0, 1, 13, 12, 9, 26, 20, 16, 17, 21, 25, 7, 8, 11, 4, 5}); @@ -61,7 +58,6 @@ bool Unpack_Tecom27(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ if (packed->Length != 27) return false; // Wrong length? Stop here. card->CardNumber = get_nonlinear_field(packed, 16, (uint8_t[]){0, 1, 13, 12, 9, 26, 20, 16, 17, 21, 25, 7, 8, 11, 4, 5}); card->FacilityCode = get_nonlinear_field(packed, 10, (uint8_t[]){15, 19, 24, 23, 22, 18, 6, 10, 14, 3, 2}); - card->ParitySupported = false; return true; } @@ -70,21 +66,19 @@ bool Pack_2804W(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ if (card->FacilityCode > 0x0FF) return false; // Can't encode FC. if (card->CardNumber > 0x7FFF) return false; // Can't encode CN. if (card->IssueLevel > 0) return false; // Not used in this format - + if (card->OEM > 0) return false; // Not used in this format packed->Length = 28; set_linear_field(packed, card->FacilityCode, 4, 8); set_linear_field(packed, card->CardNumber, 12, 15); - if (card->ParitySupported){ - set_bit_by_position(packed, - oddparity32(get_nonlinear_field(packed, 16, (uint8_t[]){4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26})) - , 2); - set_bit_by_position(packed, - evenparity32(get_linear_field(packed, 1, 13)) - , 0); - set_bit_by_position(packed, - oddparity32(get_linear_field(packed, 0, 27)) - , 27); - } + set_bit_by_position(packed, + oddparity32(get_nonlinear_field(packed, 16, (uint8_t[]){4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26})) + , 2); + set_bit_by_position(packed, + evenparity32(get_linear_field(packed, 1, 13)) + , 0); + set_bit_by_position(packed, + oddparity32(get_linear_field(packed, 0, 27)) + , 27); return add_HID_header(packed); } bool Unpack_2804W(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ @@ -92,7 +86,6 @@ bool Unpack_2804W(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ if (packed->Length != 28) return false; // Wrong length? Stop here. card->FacilityCode = get_linear_field(packed, 4, 8); card->CardNumber = get_linear_field(packed, 12, 15); - card->ParitySupported = true; card->ParityValid = (get_bit_by_position(packed, 0) == evenparity32(get_linear_field(packed, 1, 13))) && (get_bit_by_position(packed, 2) == oddparity32(get_nonlinear_field(packed, 16, (uint8_t[]){4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26}))) && @@ -105,18 +98,16 @@ bool Pack_ATSW30(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ if (card->FacilityCode > 0xFFF) return false; // Can't encode FC. if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. if (card->IssueLevel > 0) return false; // Not used in this format - + if (card->OEM > 0) return false; // Not used in this format packed->Length = 30; set_linear_field(packed, card->FacilityCode, 1, 12); set_linear_field(packed, card->CardNumber, 13, 16); - if (card->ParitySupported){ - set_bit_by_position(packed, - evenparity32(get_linear_field(packed, 1, 12)) - , 0); - set_bit_by_position(packed, - oddparity32(get_linear_field(packed, 13, 16)) - , 29); - } + set_bit_by_position(packed, + evenparity32(get_linear_field(packed, 1, 12)) + , 0); + set_bit_by_position(packed, + oddparity32(get_linear_field(packed, 13, 16)) + , 29); return add_HID_header(packed); } bool Unpack_ATSW30(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ @@ -124,7 +115,6 @@ bool Unpack_ATSW30(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ if (packed->Length != 30) return false; // Wrong length? Stop here. card->FacilityCode = get_linear_field(packed, 1, 12); card->CardNumber = get_linear_field(packed, 13, 16); - card->ParitySupported = true; card->ParityValid = (get_bit_by_position(packed, 0) == evenparity32(get_linear_field(packed, 1, 12))) && (get_bit_by_position(packed, 29) == oddparity32(get_linear_field(packed, 13, 16))); @@ -135,7 +125,7 @@ bool Pack_ADT31(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ if (card->FacilityCode > 0x0F) return false; // Can't encode FC. if (card->CardNumber > 0x7FFFFF) return false; // Can't encode CN. if (card->IssueLevel > 0) return false; // Not used in this format - + if (card->OEM > 0) return false; // Not used in this format packed->Length = 31; set_linear_field(packed, card->FacilityCode, 1, 4); set_linear_field(packed, card->CardNumber, 5, 23); @@ -147,7 +137,6 @@ bool Unpack_ADT31(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ if (packed->Length != 31) return false; // Wrong length? Stop here. card->FacilityCode = get_linear_field(packed, 1, 4); card->CardNumber = get_linear_field(packed, 5, 23); - card->ParitySupported = false; return true; } @@ -156,105 +145,88 @@ bool Pack_D10202(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ if (card->FacilityCode > 0x007F) return false; // Can't encode FC. if (card->CardNumber > 0x00FFFFFF) return false; // Can't encode CN. if (card->IssueLevel > 0) return false; // Not used in this format - + if (card->OEM > 0) return false; // Not used in this format packed->Length = 33; // Set number of bits set_linear_field(packed, card->FacilityCode, 1, 7); set_linear_field(packed, card->CardNumber, 8, 24); - - if (card->ParitySupported){ - set_bit_by_position(packed, evenparity32(get_linear_field(packed, 1, 16)), 0); - set_bit_by_position(packed, oddparity32(get_linear_field(packed, 16, 16)), 32); - } + set_bit_by_position(packed, evenparity32(get_linear_field(packed, 1, 16)), 0); + set_bit_by_position(packed, oddparity32(get_linear_field(packed, 16, 16)), 32); return add_HID_header(packed); } bool Unpack_D10202(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ memset(card, 0, sizeof(hidproxcard_t)); - if (packed->Length != 33) return false; // Wrong length? Stop here. - + if (packed->Length != 33) return false; // Wrong length? Stop here. card->CardNumber = get_linear_field(packed, 8, 24); card->FacilityCode = get_linear_field(packed, 1, 7); - card->ParitySupported = true; card->ParityValid = (get_bit_by_position(packed, 0) == evenparity32(get_linear_field(packed, 1, 16))) && (get_bit_by_position(packed, 32) == oddparity32(get_linear_field(packed, 16, 16))); return true; } - bool Pack_H10306(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ memset(packed, 0, sizeof(hidproxmessage_t)); if (card->FacilityCode > 0xFFFF) return false; // Can't encode FC. if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. if (card->IssueLevel > 0) return false; // Not used in this format - + if (card->OEM > 0) return false; // Not used in this format packed->Length = 34; // Set number of bits packed->bot |= (card->CardNumber & 0xFFFF) << 1; packed->bot |= (card->FacilityCode & 0x7FFF) << 17; packed->mid |= (card->FacilityCode & 0x8000) >> 15; - if (card->ParitySupported){ - packed->mid |= (evenparity32((packed->mid & 0x00000001) ^ (packed->bot & 0xFFFE0000)) & 1) << 1; - packed->bot |= ( oddparity32(packed->bot & 0x0001FFFE) & 1); - } + packed->mid |= (evenparity32((packed->mid & 0x00000001) ^ (packed->bot & 0xFFFE0000)) & 1) << 1; + packed->bot |= ( oddparity32(packed->bot & 0x0001FFFE) & 1); return add_HID_header(packed); } bool Unpack_H10306(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ memset(card, 0, sizeof(hidproxcard_t)); if (packed->Length != 34) return false; // Wrong length? Stop here. - card->CardNumber = (packed->bot >> 1) & 0xFFFF; card->FacilityCode = ((packed->mid & 1) << 15) | ((packed->bot >> 17) & 0xFF); - card->ParitySupported = true; card->ParityValid = ((evenparity32((packed->mid & 0x00000001) ^ (packed->bot & 0xFFFE0000)) & 1) == ((packed->mid >> 1) & 1)) && ((oddparity32(packed->bot & 0x0001FFFE) & 1) == ((packed->bot & 1))); return true; } -bool Pack_N1002(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ +bool Pack_N10002(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ memset(packed, 0, sizeof(hidproxmessage_t)); if (card->FacilityCode > 0xFF) return false; // Can't encode FC. if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. if (card->IssueLevel > 0) return false; // Not used in this format - + if (card->OEM > 0) return false; // Not used in this format packed->Length = 34; // Set number of bits set_linear_field(packed, card->FacilityCode, 9, 8); set_linear_field(packed, card->CardNumber, 17, 16); return add_HID_header(packed); } -bool Unpack_N1002(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ +bool Unpack_N10002(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ memset(card, 0, sizeof(hidproxcard_t)); if (packed->Length != 34) return false; // Wrong length? Stop here. - card->CardNumber = get_linear_field(packed, 17, 16); card->FacilityCode = get_linear_field(packed, 9, 8); - card->ParitySupported = false; return true; } - bool Pack_C1k35s(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ memset(packed, 0, sizeof(hidproxmessage_t)); if (card->FacilityCode > 0xFFF) return false; // Can't encode FC. if (card->CardNumber > 0xFFFFF) return false; // Can't encode CN. if (card->IssueLevel > 0) return false; // Not used in this format - + if (card->OEM > 0) return false; // Not used in this format packed->Length = 35; // Set number of bits packed->bot |= (card->CardNumber & 0x000FFFFF) << 1; packed->bot |= (card->FacilityCode & 0x000007FF) << 21; packed->mid |= (card->FacilityCode & 0x00000800) >> 11; - if (card->ParitySupported){ - packed->mid |= (evenparity32((packed->mid & 0x00000001) ^ (packed->bot & 0xB6DB6DB6)) & 1) << 1; - packed->bot |= ( oddparity32((packed->mid & 0x00000003) ^ (packed->bot & 0x6DB6DB6C)) & 1); - packed->mid |= ( oddparity32((packed->mid & 0x00000003) ^ (packed->bot & 0xFFFFFFFF)) & 1) << 2; - } + packed->mid |= (evenparity32((packed->mid & 0x00000001) ^ (packed->bot & 0xB6DB6DB6)) & 1) << 1; + packed->bot |= ( oddparity32((packed->mid & 0x00000003) ^ (packed->bot & 0x6DB6DB6C)) & 1); + packed->mid |= ( oddparity32((packed->mid & 0x00000003) ^ (packed->bot & 0xFFFFFFFF)) & 1) << 2; return add_HID_header(packed); } bool Unpack_C1k35s(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ memset(card, 0, sizeof(hidproxcard_t)); if (packed->Length != 35) return false; // Wrong length? Stop here. - card->CardNumber = (packed->bot >> 1) & 0x000FFFFF; card->FacilityCode = ((packed->mid & 1) << 11) | ((packed->bot >> 21)); - card->ParitySupported = true; card->ParityValid = (evenparity32((packed->mid & 0x00000001) ^ (packed->bot & 0xB6DB6DB6)) == ((packed->mid >> 1) & 1)) && ( oddparity32((packed->mid & 0x00000003) ^ (packed->bot & 0x6DB6DB6C)) == ((packed->bot >> 0) & 1)) && @@ -267,26 +239,24 @@ bool Pack_H10320(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ if (card->FacilityCode > 0) return false; // Can't encode FC. (none in this format) if (card->CardNumber > 99999999) return false; // Can't encode CN. if (card->IssueLevel > 0) return false; // Not used in this format - + if (card->OEM > 0) return false; // Not used in this format packed->Length = 36; // Set number of bits // This card is BCD-encoded rather than binary. Set the 4-bit groups independently. for (uint32_t idx = 0; idx < 8; idx++){ set_linear_field(packed, (uint64_t)(card->CardNumber / pow(10, 7-idx)) % 10, idx * 4, 4); } - if (card->ParitySupported){ - set_bit_by_position(packed, evenparity32( - get_nonlinear_field(packed, 8, (uint8_t[]){0, 4, 8, 12, 16, 20, 24, 28}) - ), 32); - set_bit_by_position(packed, oddparity32( - get_nonlinear_field(packed, 8, (uint8_t[]){1, 5, 9, 13, 17, 21, 25, 29}) - ), 33); - set_bit_by_position(packed, evenparity32( - get_nonlinear_field(packed, 8, (uint8_t[]){2, 6, 10, 14, 18, 22, 28, 30}) - ), 34); - set_bit_by_position(packed, evenparity32( - get_nonlinear_field(packed, 8, (uint8_t[]){3, 7, 11, 15, 19, 23, 29, 31}) - ), 35); - } + set_bit_by_position(packed, evenparity32( + get_nonlinear_field(packed, 8, (uint8_t[]){0, 4, 8, 12, 16, 20, 24, 28}) + ), 32); + set_bit_by_position(packed, oddparity32( + get_nonlinear_field(packed, 8, (uint8_t[]){1, 5, 9, 13, 17, 21, 25, 29}) + ), 33); + set_bit_by_position(packed, evenparity32( + get_nonlinear_field(packed, 8, (uint8_t[]){2, 6, 10, 14, 18, 22, 28, 30}) + ), 34); + set_bit_by_position(packed, evenparity32( + get_nonlinear_field(packed, 8, (uint8_t[]){3, 7, 11, 15, 19, 23, 29, 31}) + ), 35); return add_HID_header(packed); } bool Unpack_H10320(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ @@ -303,7 +273,6 @@ bool Unpack_H10320(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ card->CardNumber += val * pow(10, 7-idx); } } - card->ParitySupported = true; card->ParityValid = (get_bit_by_position(packed, 32) == evenparity32(get_nonlinear_field(packed, 8, (uint8_t[]){0, 4, 8, 12, 16, 20, 24, 28}))) && (get_bit_by_position(packed, 33) == oddparity32(get_nonlinear_field(packed, 8, (uint8_t[]){1, 5, 9, 13, 17, 21, 25, 29}))) && @@ -317,101 +286,112 @@ bool Pack_S12906(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ if (card->FacilityCode > 0xFF) return false; // Can't encode FC. if (card->IssueLevel > 0x03) return false; // Can't encode IL. if (card->CardNumber > 0x00FFFFFF) return false; // Can't encode CN. - + if (card->OEM > 0) return false; // Not used in this format packed->Length = 36; // Set number of bits set_linear_field(packed, card->FacilityCode, 1, 8); set_linear_field(packed, card->IssueLevel, 9, 2); set_linear_field(packed, card->CardNumber, 11, 24); - if (card->ParitySupported){ - set_bit_by_position(packed, - oddparity32(get_linear_field(packed, 1, 17)) - , 0); - set_bit_by_position(packed, - oddparity32(get_linear_field(packed, 17, 18)) - , 35); - } + set_bit_by_position(packed, + oddparity32(get_linear_field(packed, 1, 17)) + , 0); + set_bit_by_position(packed, + oddparity32(get_linear_field(packed, 17, 18)) + , 35); return add_HID_header(packed); } bool Unpack_S12906(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ memset(card, 0, sizeof(hidproxcard_t)); if (packed->Length != 36) return false; // Wrong length? Stop here. - card->FacilityCode = get_linear_field(packed, 1, 8); card->IssueLevel = get_linear_field(packed, 9, 2); card->CardNumber = get_linear_field(packed, 11, 24); - - card->ParitySupported = true; card->ParityValid = (get_bit_by_position(packed, 0) == oddparity32(get_linear_field(packed, 1, 17))) && - (get_bit_by_position(packed, 35) == oddparity32(get_linear_field(packed, 17, 18))); - + (get_bit_by_position(packed, 35) == oddparity32(get_linear_field(packed, 17, 18))); return true; } bool Pack_Sie36(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ memset(packed, 0, sizeof(hidproxmessage_t)); if (card->FacilityCode > 0x0003FFFF) return false; // Can't encode FC. - if (card->IssueLevel > 0x00) return false; // Can't encode IL. if (card->CardNumber > 0x0000FFFF) return false; // Can't encode CN. if (card->IssueLevel > 0) return false; // Not used in this format - + if (card->OEM > 0) return false; // Not used in this format packed->Length = 36; // Set number of bits set_linear_field(packed, card->FacilityCode, 1, 18); set_linear_field(packed, card->CardNumber, 19, 16); - if (card->ParitySupported){ - set_bit_by_position(packed, - oddparity32(get_nonlinear_field(packed, 23, (uint8_t[]){1, 3, 4, 6, 7, 9, 10, 12, 13, 15, 16, 18, 19, 21, 22, 24, 25, 27, 28, 30, 31, 33, 34})) - , 0); - set_bit_by_position(packed, - evenparity32(get_nonlinear_field(packed, 23, (uint8_t[]){1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26, 28, 29, 31, 32, 34})) - , 35); - } + set_bit_by_position(packed, + oddparity32(get_nonlinear_field(packed, 23, (uint8_t[]){1, 3, 4, 6, 7, 9, 10, 12, 13, 15, 16, 18, 19, 21, 22, 24, 25, 27, 28, 30, 31, 33, 34})) + , 0); + set_bit_by_position(packed, + evenparity32(get_nonlinear_field(packed, 23, (uint8_t[]){1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26, 28, 29, 31, 32, 34})) + , 35); return add_HID_header(packed); } bool Unpack_Sie36(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ memset(card, 0, sizeof(hidproxcard_t)); if (packed->Length != 36) return false; // Wrong length? Stop here. - card->FacilityCode = get_linear_field(packed, 1, 18); card->CardNumber = get_linear_field(packed, 19, 16); - - card->ParitySupported = true; card->ParityValid = (get_bit_by_position(packed, 0) == oddparity32(get_nonlinear_field(packed, 23, (uint8_t[]){1, 3, 4, 6, 7, 9, 10, 12, 13, 15, 16, 18, 19, 21, 22, 24, 25, 27, 28, 30, 31, 33, 34}))) && (get_bit_by_position(packed, 35) == oddparity32(get_nonlinear_field(packed, 23, (uint8_t[]){1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26, 28, 29, 31, 32, 34}))); - return true; } +bool Pack_C15001(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_t)); + if (card->FacilityCode > 0x000000FF) return false; // Can't encode FC. + if (card->CardNumber > 0x0000FFFF) return false; // Can't encode CN. + if (card->IssueLevel > 0) return false; // Not used in this format + if (card->OEM > 0x000003FF) return false; // Can't encode OEM. + packed->Length = 36; // Set number of bits + set_linear_field(packed, card->OEM, 1, 10); + set_linear_field(packed, card->FacilityCode, 11, 8); + set_linear_field(packed, card->CardNumber, 19, 16); + set_bit_by_position(packed, + evenparity32(get_linear_field(packed, 1, 17)) + , 0); + set_bit_by_position(packed, + oddparity32(get_linear_field(packed, 18, 17)) + , 35); + return add_HID_header(packed); +} +bool Unpack_C15001(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 36) return false; // Wrong length? Stop here. + card->OEM = get_linear_field(packed, 1, 10); + card->FacilityCode = get_linear_field(packed, 11, 8); + card->CardNumber = get_linear_field(packed, 19, 16); + card->ParityValid = + (get_bit_by_position(packed, 0) == evenparity32(get_linear_field(packed, 1, 17))) && + (get_bit_by_position(packed, 35) == oddparity32(get_linear_field(packed, 18, 17))); + return true; +} bool Pack_H10302(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ memset(packed, 0, sizeof(hidproxmessage_t)); if (card->FacilityCode > 0) return false; // Can't encode FC. (none in this format) if (card->CardNumber > 0x00000007FFFFFFFF) return false; // Can't encode CN. if (card->IssueLevel > 0) return false; // Not used in this format - + if (card->OEM > 0) return false; // Not used in this format packed->Length = 37; // Set number of bits set_linear_field(packed, card->CardNumber, 1, 35); - if (card->ParitySupported){ - set_bit_by_position(packed, - evenparity32(get_linear_field(packed, 1, 18)) - , 0); - set_bit_by_position(packed, - oddparity32(get_linear_field(packed, 18, 18)) - , 36); - } + set_bit_by_position(packed, + evenparity32(get_linear_field(packed, 1, 18)) + , 0); + set_bit_by_position(packed, + oddparity32(get_linear_field(packed, 18, 18)) + , 36); return add_HID_header(packed); } bool Unpack_H10302(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ memset(card, 0, sizeof(hidproxcard_t)); if (packed->Length != 37) return false; // Wrong length? Stop here. - card->CardNumber = get_linear_field(packed, 1, 35); - card->ParitySupported = true; card->ParityValid = (get_bit_by_position(packed, 0) == evenparity32(get_linear_field(packed, 1, 18))) && (get_bit_by_position(packed, 36) == oddparity32(get_linear_field(packed, 18, 18))); - return true; } @@ -420,59 +400,49 @@ bool Pack_H10304(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ if (card->FacilityCode > 0x0000FFFF) return false; // Can't encode FC. if (card->CardNumber > 0x0007FFFF) return false; // Can't encode CN. if (card->IssueLevel > 0) return false; // Not used in this format - + if (card->OEM > 0) return false; // Not used in this format packed->Length = 37; // Set number of bits packed->bot |= (card->CardNumber & 0x0007FFFF) << 1; packed->bot |= (card->FacilityCode & 0x00000FFF) << 20; packed->mid |= (card->FacilityCode & 0x0000F000) >> 12; - if (card->ParitySupported){ - packed->mid |= (evenparity32((packed->mid & 0x0000000F) ^ (packed->bot & 0xFFFC0000)) & 1) << 4; - packed->bot |= ( oddparity32(packed->bot & 0x0007FFFE) & 1); - } + packed->mid |= (evenparity32((packed->mid & 0x0000000F) ^ (packed->bot & 0xFFFC0000)) & 1) << 4; + packed->bot |= ( oddparity32(packed->bot & 0x0007FFFE) & 1); return add_HID_header(packed); } bool Unpack_H10304(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ memset(card, 0, sizeof(hidproxcard_t)); if (packed->Length != 37) return false; // Wrong length? Stop here. - card->CardNumber = (packed->bot >> 1) & 0x0007FFFF; card->FacilityCode = ((packed->mid & 0xF) << 12) | ((packed->bot >> 20)); - card->ParitySupported = true; card->ParityValid = (evenparity32((packed->mid & 0x0000000F) ^ (packed->bot & 0xFFFC0000)) == ((packed->mid >> 4) & 1)) && (oddparity32( packed->bot & 0x0007FFFE) == (packed->bot & 1)); return true; } - bool Pack_P10001(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ memset(packed, 0, sizeof(hidproxmessage_t)); if (card->FacilityCode > 0xFFF) return false; // Can't encode FC. if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. if (card->IssueLevel > 0) return false; // Not used in this format - + if (card->OEM > 0) return false; // Not used in this format packed->Length = 40; // Set number of bits set_linear_field(packed, 0xF, 0, 4); set_linear_field(packed, card->FacilityCode, 4, 12); set_linear_field(packed, card->CardNumber, 16, 16); - - if (card->ParitySupported){ - set_linear_field(packed, - get_linear_field(packed, 0, 8) ^ - get_linear_field(packed, 8, 8) ^ - get_linear_field(packed, 16, 8) ^ - get_linear_field(packed, 24, 8) - , 32, 8); - } + set_linear_field(packed, + get_linear_field(packed, 0, 8) ^ + get_linear_field(packed, 8, 8) ^ + get_linear_field(packed, 16, 8) ^ + get_linear_field(packed, 24, 8) + , 32, 8); return add_HID_header(packed); } bool Unpack_P10001(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ memset(card, 0, sizeof(hidproxcard_t)); if (packed->Length != 40) return false; // Wrong length? Stop here. - card->CardNumber = get_linear_field(packed, 16, 16); card->FacilityCode = get_linear_field(packed, 4, 12); - card->ParitySupported = true; card->ParityValid = ( get_linear_field(packed, 0, 8) ^ get_linear_field(packed, 8, 8) ^ @@ -482,31 +452,26 @@ bool Unpack_P10001(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ return true; } - bool Pack_C1k48s(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ memset(packed, 0, sizeof(hidproxmessage_t)); if (card->FacilityCode > 0x003FFFFF) return false; // Can't encode FC. if (card->CardNumber > 0x007FFFFF) return false; // Can't encode CN. if (card->IssueLevel > 0) return false; // Not used in this format - + if (card->OEM > 0) return false; // Not used in this format packed->Length = 48; // Set number of bits packed->bot |= (card->CardNumber & 0x007FFFFF) << 1; packed->bot |= (card->FacilityCode & 0x000000FF) << 24; packed->mid |= (card->FacilityCode & 0x003FFF00) >> 8; - if (card->ParitySupported){ - packed->mid |= (evenparity32((packed->mid & 0x00001B6D) ^ (packed->bot & 0xB6DB6DB6)) & 1) << 14; - packed->bot |= ( oddparity32((packed->mid & 0x000036DB) ^ (packed->bot & 0x6DB6DB6C)) & 1); - packed->mid |= ( oddparity32((packed->mid & 0x00007FFF) ^ (packed->bot & 0xFFFFFFFF)) & 1) << 15; - } + packed->mid |= (evenparity32((packed->mid & 0x00001B6D) ^ (packed->bot & 0xB6DB6DB6)) & 1) << 14; + packed->bot |= ( oddparity32((packed->mid & 0x000036DB) ^ (packed->bot & 0x6DB6DB6C)) & 1); + packed->mid |= ( oddparity32((packed->mid & 0x00007FFF) ^ (packed->bot & 0xFFFFFFFF)) & 1) << 15; return add_HID_header(packed); } bool Unpack_C1k48s(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ memset(card, 0, sizeof(hidproxcard_t)); if (packed->Length != 48) return false; // Wrong length? Stop here. - card->CardNumber = (packed->bot >> 1) & 0x007FFFFF; card->FacilityCode = ((packed->mid & 0x00003FFF) << 8) | ((packed->bot >> 24)); - card->ParitySupported = true; card->ParityValid = (evenparity32((packed->mid & 0x00001B6D) ^ (packed->bot & 0xB6DB6DB6)) == ((packed->mid >> 14) & 1)) && ( oddparity32((packed->mid & 0x000036DB) ^ (packed->bot & 0x6DB6DB6C)) == ((packed->bot >> 0) & 1)) && @@ -515,23 +480,24 @@ bool Unpack_C1k48s(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ } static const hidcardformat_t FormatTable[] = { - {"H10301", Pack_H10301, Unpack_H10301, "HID H10301 26-bit"}, // imported from old pack/unpack - {"Tecom27", Pack_Tecom27, Unpack_Tecom27, "Tecom 27-bit"}, // from cardinfo.barkweb.com.au - {"2804W", Pack_2804W, Unpack_2804W, "2804 Wiegand"}, // from cardinfo.barkweb.com.au - {"ATSW30", Pack_ATSW30, Unpack_ATSW30, "ATS Wiegand 30-bit"}, // from cardinfo.barkweb.com.au - {"ADT31", Pack_ADT31, Unpack_ADT31, "HID ADT 31-bit"}, // from cardinfo.barkweb.com.au - {"D10202", Pack_D10202, Unpack_D10202, "HID D10202 33-bit"}, // from cardinfo.barkweb.com.au - {"H10306", Pack_H10306, Unpack_H10306, "HID H10306 34-bit"}, // imported from old pack/unpack - {"N1002", Pack_N1002, Unpack_N1002, "HID N1002 34-bit"}, // from cardinfo.barkweb.com.au - {"C1k35s", Pack_C1k35s, Unpack_C1k35s, "HID Corporate 1000 35-bit standard layout"}, // imported from old pack/unpack - {"S12906", Pack_S12906, Unpack_S12906, "HID Simplex 36-bit"}, // from cardinfo.barkweb.com.au - {"Sie36", Pack_Sie36, Unpack_Sie36, "HID 36-bit Siemens"}, // from cardinfo.barkweb.com.au - {"H10320", Pack_H10320, Unpack_H10320, "HID H10320 36-bit BCD, Card num only"}, // from Proxmark forums - {"H10302", Pack_H10302, Unpack_H10302, "HID H10302 37-bit huge, Card num only"}, // from Proxmark forums - {"H10304", Pack_H10304, Unpack_H10304, "HID H10304 37-bit"}, // imported from old pack/unpack + {"H10301", Pack_H10301, Unpack_H10301, "HID H10301 26-bit", {1, 1, 0, 0, 1}}, // imported from old pack/unpack + {"Tecom27", Pack_Tecom27, Unpack_Tecom27, "Tecom 27-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au + {"2804W", Pack_2804W, Unpack_2804W, "2804 Wiegand", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au + {"ATSW30", Pack_ATSW30, Unpack_ATSW30, "ATS Wiegand 30-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au + {"ADT31", Pack_ADT31, Unpack_ADT31, "HID ADT 31-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au + {"D10202", Pack_D10202, Unpack_D10202, "HID D10202 33-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au + {"H10306", Pack_H10306, Unpack_H10306, "HID H10306 34-bit", {1, 1, 0, 0, 1}}, // imported from old pack/unpack + {"N10002", Pack_N10002, Unpack_N10002, "HID N10002 34-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au + {"C1k35s", Pack_C1k35s, Unpack_C1k35s, "HID Corporate 1000 35-bit standard layout", {1, 1, 0, 0, 1}}, // imported from old pack/unpack + {"C15001", Pack_C15001, Unpack_C15001, "HID KeySpan 36-bit", {1, 1, 0, 1, 1}}, // from Proxmark forums + {"S12906", Pack_S12906, Unpack_S12906, "HID Simplex 36-bit", {1, 1, 1, 0, 1}}, // from cardinfo.barkweb.com.au + {"Sie36", Pack_Sie36, Unpack_Sie36, "HID 36-bit Siemens", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au + {"H10320", Pack_H10320, Unpack_H10320, "HID H10320 36-bit BCD", {1, 0, 0, 0, 1}}, // from Proxmark forums + {"H10302", Pack_H10302, Unpack_H10302, "HID H10302 37-bit huge ID", {1, 0, 0, 0, 1}}, // from Proxmark forums + {"H10304", Pack_H10304, Unpack_H10304, "HID H10304 37-bit", {1, 1, 0, 0, 1}}, // imported from old pack/unpack {"P10001", Pack_P10001, Unpack_P10001, "HID P10001 Honeywell 40-bit"}, // from cardinfo.barkweb.com.au - {"C1k48s", Pack_C1k48s, Unpack_C1k48s, "HID Corporate 1000 48-bit standard layout"}, // imported from old pack/unpack - {NULL, NULL, NULL, NULL} // Must null terminate array + {"C1k48s", Pack_C1k48s, Unpack_C1k48s, "HID Corporate 1000 48-bit standard layout", {1, 1, 0, 0, 1}}, // imported from old pack/unpack + {NULL, NULL, NULL, NULL, {0, 0, 0, 0, 0}} // Must null terminate array }; void HIDListFormats(){ @@ -542,7 +508,7 @@ void HIDListFormats(){ PrintAndLog("------------------------------------------------------------"); while (FormatTable[i].Name) { - PrintAndLog("%-10s %s", FormatTable[i].Name, FormatTable[i].Descrp); + PrintAndLog("%-10s %-30s", FormatTable[i].Name, FormatTable[i].Descrp); ++i; } PrintAndLog(""); @@ -576,6 +542,20 @@ bool HIDPack(/* in */int FormatIndex, /* in */hidproxcard_t* card, /* out */hidp return FormatTable[FormatIndex].Pack(card, packed); } +void HIDDisplayUnpackedCard(hidproxcard_t* card, const hidcardformat_t format){ + PrintAndLog(" Format: %s (%s)", format.Name, format.Descrp); + if (format.Fields.hasFacilityCode) + PrintAndLog("Facility Code: %d",card->FacilityCode); + if (format.Fields.hasCardNumber) + PrintAndLog(" Card Number: %d",card->CardNumber); + if (format.Fields.hasIssueLevel) + PrintAndLog(" Issue Level: %d",card->IssueLevel); + if (format.Fields.hasOEMCode) + PrintAndLog(" OEM Code: %d",card->OEM); + if (format.Fields.hasParity) + PrintAndLog(" Parity: %s",card->ParityValid ? "Valid" : "Invalid"); +} + bool HIDTryUnpack(/* in */hidproxmessage_t* packed, /* in */bool ignoreParity){ if (FormatTable[0].Name == NULL) return false; @@ -587,15 +567,11 @@ bool HIDTryUnpack(/* in */hidproxmessage_t* packed, /* in */bool ignoreParity){ while (FormatTable[i].Name) { if (FormatTable[i].Unpack(packed, &card)){ - if (ignoreParity || !card.ParitySupported || card.ParityValid){ + if (ignoreParity || !FormatTable[i].Fields.hasParity || card.ParityValid){ + if (!result) PrintAndLog("--------------------------------------------------"); result = true; - PrintAndLog("%-16s FC: %u, Card: %"PRIu64", IL: %u, Parity %s", - FormatTable[i].Name, - card.FacilityCode, - card.CardNumber, - card.IssueLevel, - (card.ParitySupported) ? ((card.ParityValid) ? "valid" : "invalid") : "n/a" - ); + HIDDisplayUnpackedCard(&card, FormatTable[i]); + PrintAndLog("--------------------------------------------------"); } } ++i; diff --git a/client/hidcardformats.h b/client/hidcardformats.h index b8c6afb4..3019b823 100644 --- a/client/hidcardformats.h +++ b/client/hidcardformats.h @@ -15,12 +15,22 @@ #include #include "hidcardformatutils.h" + +typedef struct hidcardformatdescriptor_s{ + bool hasCardNumber; + bool hasFacilityCode; + bool hasIssueLevel; + bool hasOEMCode; + bool hasParity; +} hidcardformatdescriptor_t; + // Structure for defined HID card formats available for packing/unpacking typedef struct hidcardformat_s{ const char* Name; bool (*Pack)(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed); bool (*Unpack)(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card); const char* Descrp; + hidcardformatdescriptor_t Fields; } hidcardformat_t; void HIDListFormats(); diff --git a/client/hidcardformatutils.h b/client/hidcardformatutils.h index 821fc9ad..0ab2ae72 100644 --- a/client/hidcardformatutils.h +++ b/client/hidcardformatutils.h @@ -28,8 +28,8 @@ typedef struct hidproxmessage_s{ typedef struct hidproxcard_s{ uint32_t FacilityCode; uint64_t CardNumber; - uint8_t IssueLevel; - bool ParitySupported; + uint32_t IssueLevel; + uint32_t OEM; bool ParityValid; // Only valid for responses } hidproxcard_t; From f3c8131a0df4bd1d5cf39b886ba7c355efc2430c Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Thu, 6 Sep 2018 08:38:38 +0300 Subject: [PATCH 253/310] update travis config (#662) --- CI/.travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CI/.travis.yml b/CI/.travis.yml index 8e9289b1..f5fed7a6 100644 --- a/CI/.travis.yml +++ b/CI/.travis.yml @@ -12,9 +12,9 @@ matrix: - os: osx osx_image: xcode8.3 # OS X 10.12 - os: osx - osx_image: xcode9 # OS X 10.12 + osx_image: xcode9.4 # OS X 10.13 - os: osx - osx_image: xcode9.2 # OS X 10.12 + osx_image: xcode10 # OS X 10.13 - os: linux dist: trusty sudo: required From 6e3d8d671ac59e308c2ec83136890dc1af2edc65 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Thu, 6 Sep 2018 08:48:54 +0300 Subject: [PATCH 254/310] implement argtable in hf 14a apdu (#490) * added `hf 14a reader` to source and added functionality to exec empty commands * added `hf 14a raw` * added samples to command's help * added some help * added changelog * update to new argtable3 --- https://github.com/argtable/argtable3 * changed included getopt to `https://github.com/freebsd/freebsd/blob/master/include/getopt.h` (getopt from freebsd with simplified BSD license) --- CHANGELOG.md | 1 + client/Makefile | 2 + client/cliparser/README.md | 13 + client/cliparser/argtable3.c | 5005 ++++++++++++++++++++++++++++++++++ client/cliparser/argtable3.h | 305 +++ client/cliparser/cliparser.c | 167 ++ client/cliparser/cliparser.h | 32 + client/cmdhf14a.c | 267 +- client/obj/cliparser/.dummy | 0 9 files changed, 5619 insertions(+), 173 deletions(-) create mode 100644 client/cliparser/README.md create mode 100644 client/cliparser/argtable3.c create mode 100644 client/cliparser/argtable3.h create mode 100644 client/cliparser/cliparser.c create mode 100644 client/cliparser/cliparser.h create mode 100644 client/obj/cliparser/.dummy diff --git a/CHANGELOG.md b/CHANGELOG.md index da6463e9..ca13dc85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Changed `hf 14a raw` - works with LED's and some exchange logic (Merlok) - Changed TLV parser messages to more convenient (Merlok) - Rewritten Legic Prime reader (`hf legic reader`, `write` and `fill`) - it is using xcorrelation now (AntiCat) +- `hf 14a` commands works via argtable3 commandline parsing library (Merlok) ### Fixed - Changed start sequence in Qt mode (fix: short commands hangs main Qt thread) (Merlok) diff --git a/client/Makefile b/client/Makefile index c6ca1cf1..54a77a84 100644 --- a/client/Makefile +++ b/client/Makefile @@ -106,6 +106,8 @@ CMDSRCS = $(SRC_SMARTCARD) \ polarssl/bignum.c\ polarssl/rsa.c\ polarssl/sha1.c\ + cliparser/argtable3.c\ + cliparser/cliparser.c\ mfkey.c\ loclass/cipher.c \ loclass/cipherutils.c \ diff --git a/client/cliparser/README.md b/client/cliparser/README.md new file mode 100644 index 00000000..2b321946 --- /dev/null +++ b/client/cliparser/README.md @@ -0,0 +1,13 @@ +# Command line parser + +cliparser - librari for proxmark with command line parsing high level functions. + +## External libraries: + +### argtable + +Argtable3 is a single-file, ANSI C, command-line parsing library that parses GNU-style command-line options. + +You can download argtable3 from this repository https://github.com/argtable/argtable3 + +[argtable3 license](https://github.com/argtable/argtable3/blob/master/LICENSE) diff --git a/client/cliparser/argtable3.c b/client/cliparser/argtable3.c new file mode 100644 index 00000000..f70f68c7 --- /dev/null +++ b/client/cliparser/argtable3.c @@ -0,0 +1,5005 @@ +/******************************************************************************* + * This file is part of the argtable3 library. + * + * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of STEWART HEITMANN nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include "argtable3.h" + +// On Windows isspace crashes app in case of using Unicode character set and string to be above ASCII +// so you have to use _istspace instead of space +#ifdef UNICODE +#include + #define ISSPACE _istspace +#else + #define ISSPACE isspace +#endif + +/******************************************************************************* + * This file is part of the argtable3 library. + * + * Copyright (C) 2013 Tom G. Huang + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of STEWART HEITMANN nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#ifndef ARG_UTILS_H +#define ARG_UTILS_H + +#define ARG_ENABLE_TRACE 0 +#define ARG_ENABLE_LOG 1 + +#ifdef __cplusplus +extern "C" { +#endif + +enum +{ + EMINCOUNT = 1, + EMAXCOUNT, + EBADINT, + // The same name define EOVERFLOW in errno.h on windows platform +#ifdef __STDC_WANT_SECURE_LIB__ + EOVERFLOW_, +#else + EOVERFLOW, +#endif + EBADDOUBLE, + EBADDATE, + EREGNOMATCH +}; + + +#if defined(_MSC_VER) +#define ARG_TRACE(x) \ + __pragma(warning(push)) \ + __pragma(warning(disable:4127)) \ + do { if (ARG_ENABLE_TRACE) dbg_printf x; } while (0) \ + __pragma(warning(pop)) + +#define ARG_LOG(x) \ + __pragma(warning(push)) \ + __pragma(warning(disable:4127)) \ + do { if (ARG_ENABLE_LOG) dbg_printf x; } while (0) \ + __pragma(warning(pop)) +#else +#define ARG_TRACE(x) \ + do { if (ARG_ENABLE_TRACE) dbg_printf x; } while (0) + +#define ARG_LOG(x) \ + do { if (ARG_ENABLE_LOG) dbg_printf x; } while (0) +#endif + +extern void dbg_printf(const char *fmt, ...); + +#ifdef __cplusplus +} +#endif + +#endif + +/******************************************************************************* + * This file is part of the argtable3 library. + * + * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of STEWART HEITMANN nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include + + +void dbg_printf(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + +/* $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $ */ +/* $FreeBSD$ */ + +/*- + * SPDX-License-Identifier: BSD-2-Clause-NetBSD + * + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _GETOPT_H_ +#define _GETOPT_H_ + +#include + +/* + * GNU-like getopt_long()/getopt_long_only() with 4.4BSD optreset extension. + * getopt() is declared here too for GNU programs. + */ +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +struct option { + /* name of long option */ + const char *name; + /* + * one of no_argument, required_argument, and optional_argument: + * whether option takes an argument + */ + int has_arg; + /* if not NULL, set *flag to val when option found */ + int *flag; + /* if flag not NULL, value to set *flag to; else return value */ + int val; +}; + +__BEGIN_DECLS +int getopt_long(int, char * const *, const char *, + const struct option *, int *); +int getopt_long_only(int, char * const *, const char *, + const struct option *, int *); +#ifndef _GETOPT_DECLARED +#define _GETOPT_DECLARED +int getopt(int, char * const [], const char *); + +extern char *optarg; /* getopt(3) external variables */ +extern int optind, opterr, optopt; +#endif +#ifndef _OPTRESET_DECLARED +#define _OPTRESET_DECLARED +extern int optreset; /* getopt(3) external variable */ +#endif +__END_DECLS + +#endif /* !_GETOPT_H_ */ +/* $Id: getopt_long.c,v 1.1 2009/10/16 19:50:28 rodney Exp rodney $ */ +/* $OpenBSD: getopt_long.c,v 1.23 2007/10/31 12:34:57 chl Exp $ */ +/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ + +/* + * Copyright (c) 2002 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ + +// $Id: getopt_long.c,v 1.1 2009/10/16 19:50:28 rodney Exp rodney $" + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if 0 +#include +#endif +#include +#include +#include + + +#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */ + +#ifdef REPLACE_GETOPT +int opterr = 1; /* if error message should be printed */ +int optind = 1; /* index into parent argv vector */ +int optopt = '?'; /* character checked for validity */ +int optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ +#endif + +#define PRINT_ERROR ((opterr) && (*options != ':')) + +#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ +#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ +#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ + +/* return values */ +#define BADCH (int)'?' +#define BADARG ((*options == ':') ? (int)':' : (int)'?') +#define INORDER (int)1 + +#define EMSG "" + +static int getopt_internal(int, char * const *, const char *, + const struct option *, int *, int); +static int parse_long_options(char * const *, const char *, + const struct option *, int *, int); +static int gcd(int, int); +static void permute_args(int, int, int, char * const *); + +static char *place = EMSG; /* option letter processing */ + +/* XXX: set optreset to 1 rather than these two */ +static int nonopt_start = -1; /* first non option argument (for permute) */ +static int nonopt_end = -1; /* first option after non options (for permute) */ + +/* Error messages */ +static const char recargchar[] = "option requires an argument -- %c"; +static const char recargstring[] = "option requires an argument -- %s"; +static const char ambig[] = "ambiguous option -- %.*s"; +static const char noarg[] = "option doesn't take an argument -- %.*s"; +static const char illoptchar[] = "unknown option -- %c"; +static const char illoptstring[] = "unknown option -- %s"; + + + +#ifdef _WIN32 + +/* Windows needs warnx(). We change the definition though: + * 1. (another) global is defined, opterrmsg, which holds the error message + * 2. errors are always printed out on stderr w/o the program name + * Note that opterrmsg always gets set no matter what opterr is set to. The + * error message will not be printed if opterr is 0 as usual. + */ + +#include +#include + +#define MAX_OPTER_MSG_SIZE 128 + +extern char opterrmsg[MAX_OPTER_MSG_SIZE]; +char opterrmsg[MAX_OPTER_MSG_SIZE]; /* buffer for the last error message */ + +static void warnx(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + /* + Make sure opterrmsg is always zero-terminated despite the _vsnprintf() + implementation specifics and manually suppress the warning. + */ + memset(opterrmsg, 0, sizeof opterrmsg); + if (fmt != NULL) +#ifdef __STDC_WANT_SECURE_LIB__ + _vsnprintf_s(opterrmsg, MAX_OPTER_MSG_SIZE, sizeof(opterrmsg) - 1, fmt, ap); +#else + _vsnprintf(opterrmsg, sizeof(opterrmsg) - 1, fmt, ap); +#endif + va_end(ap); + +#pragma warning(suppress: 6053) + fprintf(stderr, "%s\n", opterrmsg); +} + +#else +#include +#endif /*_WIN32*/ + + +/* + * Compute the greatest common divisor of a and b. + */ +static int +gcd(int a, int b) +{ + int c; + + c = a % b; + while (c != 0) { + a = b; + b = c; + c = a % b; + } + + return (b); +} + +/* + * Exchange the block from nonopt_start to nonopt_end with the block + * from nonopt_end to opt_end (keeping the same order of arguments + * in each block). + */ +static void +permute_args(int panonopt_start, int panonopt_end, int opt_end, + char * const *nargv) +{ + int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; + char *swap; + + /* + * compute lengths of blocks and number and size of cycles + */ + nnonopts = panonopt_end - panonopt_start; + nopts = opt_end - panonopt_end; + ncycle = gcd(nnonopts, nopts); + cyclelen = (opt_end - panonopt_start) / ncycle; + + for (i = 0; i < ncycle; i++) { + cstart = panonopt_end+i; + pos = cstart; + for (j = 0; j < cyclelen; j++) { + if (pos >= panonopt_end) + pos -= nnonopts; + else + pos += nopts; + swap = nargv[pos]; + /* LINTED const cast */ + ((char **) nargv)[pos] = nargv[cstart]; + /* LINTED const cast */ + ((char **)nargv)[cstart] = swap; + } + } +} + +/* + * parse_long_options -- + * Parse long options in argc/argv argument vector. + * Returns -1 if short_too is set and the option does not match long_options. + */ +static int +parse_long_options(char * const *nargv, const char *options, + const struct option *long_options, int *idx, int short_too) +{ + char *current_argv, *has_equal; + size_t current_argv_len; + int i, match; + + current_argv = place; + match = -1; + + optind++; + + if ((has_equal = strchr(current_argv, '=')) != NULL) { + /* argument found (--option=arg) */ + current_argv_len = has_equal - current_argv; + has_equal++; + } else + current_argv_len = strlen(current_argv); + + for (i = 0; long_options[i].name; i++) { + /* find matching long option */ + if (strncmp(current_argv, long_options[i].name, + current_argv_len)) + continue; + + if (strlen(long_options[i].name) == current_argv_len) { + /* exact match */ + match = i; + break; + } + /* + * If this is a known short option, don't allow + * a partial match of a single character. + */ + if (short_too && current_argv_len == 1) + continue; + + if (match == -1) /* partial match */ + match = i; + else { + /* ambiguous abbreviation */ + if (PRINT_ERROR) + warnx(ambig, (int)current_argv_len, + current_argv); + optopt = 0; + return (BADCH); + } + } + if (match != -1) { /* option found */ + if (long_options[match].has_arg == no_argument + && has_equal) { + if (PRINT_ERROR) + warnx(noarg, (int)current_argv_len, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + return (BADARG); + } + if (long_options[match].has_arg == required_argument || + long_options[match].has_arg == optional_argument) { + if (has_equal) + optarg = has_equal; + else if (long_options[match].has_arg == + required_argument) { + /* + * optional argument doesn't use next nargv + */ + optarg = nargv[optind++]; + } + } + if ((long_options[match].has_arg == required_argument) + && (optarg == NULL)) { + /* + * Missing argument; leading ':' indicates no error + * should be generated. + */ + if (PRINT_ERROR) + warnx(recargstring, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + --optind; + return (BADARG); + } + } else { /* unknown option */ + if (short_too) { + --optind; + return (-1); + } + if (PRINT_ERROR) + warnx(illoptstring, current_argv); + optopt = 0; + return (BADCH); + } + if (idx) + *idx = match; + if (long_options[match].flag) { + *long_options[match].flag = long_options[match].val; + return (0); + } else + return (long_options[match].val); +} + +/* + * getopt_internal -- + * Parse argc/argv argument vector. Called by user level routines. + */ +static int +getopt_internal(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx, int flags) +{ + char *oli; /* option letter list index */ + int optchar, short_too; + static int posixly_correct = -1; +#ifdef __STDC_WANT_SECURE_LIB__ + char* buffer = NULL; + size_t buffer_size = 0; + errno_t err = 0; +#endif + + if (options == NULL) + return (-1); + + /* + * Disable GNU extensions if POSIXLY_CORRECT is set or options + * string begins with a '+'. + */ + +#ifdef __STDC_WANT_SECURE_LIB__ + if (posixly_correct == -1) { + err = _dupenv_s(&buffer, &buffer_size, "POSIXLY_CORRECT") == 0; + posixly_correct = buffer != NULL; + if(buffer != NULL && err == 0) { + free(buffer); + } + } +#else + if (posixly_correct == -1) + posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); +#endif + if (posixly_correct || *options == '+') + flags &= ~FLAG_PERMUTE; + else if (*options == '-') + flags |= FLAG_ALLARGS; + if (*options == '+' || *options == '-') + options++; + + /* + * XXX Some GNU programs (like cvs) set optind to 0 instead of + * XXX using optreset. Work around this braindamage. + */ + if (optind == 0) + optind = optreset = 1; + + optarg = NULL; + if (optreset) + nonopt_start = nonopt_end = -1; +start: + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc) { /* end of argument vector */ + place = EMSG; + if (nonopt_end != -1) { + /* do permutation, if we have to */ + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + else if (nonopt_start != -1) { + /* + * If we skipped non-options, set optind + * to the first of them. + */ + optind = nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + if (*(place = nargv[optind]) != '-' || + (place[1] == '\0' && strchr(options, '-') == NULL)) { + place = EMSG; /* found non-option */ + if (flags & FLAG_ALLARGS) { + /* + * GNU extension: + * return non-option as argument to option 1 + */ + optarg = nargv[optind++]; + return (INORDER); + } + if (!(flags & FLAG_PERMUTE)) { + /* + * If no permutation wanted, stop parsing + * at first non-option. + */ + return (-1); + } + /* do permutation */ + if (nonopt_start == -1) + nonopt_start = optind; + else if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + nonopt_start = optind - + (nonopt_end - nonopt_start); + nonopt_end = -1; + } + optind++; + /* process next argument */ + goto start; + } + if (nonopt_start != -1 && nonopt_end == -1) + nonopt_end = optind; + + /* + * If we have "-" do nothing, if "--" we are done. + */ + if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { + optind++; + place = EMSG; + /* + * We found an option (--), so if we skipped + * non-options, we have to permute. + */ + if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + } + + /* + * Check long options if: + * 1) we were passed some + * 2) the arg is not just "-" + * 3) either the arg starts with -- we are getopt_long_only() + */ + if (long_options != NULL && place != nargv[optind] && + (*place == '-' || (flags & FLAG_LONGONLY))) { + short_too = 0; + if (*place == '-') + place++; /* --foo long option */ + else if (*place != ':' && strchr(options, *place) != NULL) + short_too = 1; /* could be short option too */ + + optchar = parse_long_options(nargv, options, long_options, + idx, short_too); + if (optchar != -1) { + place = EMSG; + return (optchar); + } + } + + if ((optchar = (int)*place++) == (int)':' || + (optchar == (int)'-' && *place != '\0') || + (oli = strchr(options, optchar)) == NULL) { + /* + * If the user specified "-" and '-' isn't listed in + * options, return -1 (non-option) as per POSIX. + * Otherwise, it is an unknown option character (or ':'). + */ + if (optchar == (int)'-' && *place == '\0') + return (-1); + if (!*place) + ++optind; + if (PRINT_ERROR) + warnx(illoptchar, optchar); + optopt = optchar; + return (BADCH); + } + if (long_options != NULL && optchar == 'W' && oli[1] == ';') { + /* -W long-option */ + if (*place) /* no space */ + /* NOTHING */; + else if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else /* white space */ + place = nargv[optind]; + optchar = parse_long_options(nargv, options, long_options, + idx, 0); + place = EMSG; + return (optchar); + } + if (*++oli != ':') { /* doesn't take argument */ + if (!*place) + ++optind; + } else { /* takes (optional) argument */ + optarg = NULL; + if (*place) /* no white space */ + optarg = place; + else if (oli[1] != ':') { /* arg not optional */ + if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else + optarg = nargv[optind]; + } + place = EMSG; + ++optind; + } + /* dump back option letter */ + return (optchar); +} + +#ifdef REPLACE_GETOPT +/* + * getopt -- + * Parse argc/argv argument vector. + * + * [eventually this will replace the BSD getopt] + */ +int +getopt(int nargc, char * const *nargv, const char *options) +{ + + /* + * We don't pass FLAG_PERMUTE to getopt_internal() since + * the BSD getopt(3) (unlike GNU) has never done this. + * + * Furthermore, since many privileged programs call getopt() + * before dropping privileges it makes sense to keep things + * as simple (and bug-free) as possible. + */ + return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); +} +#endif /* REPLACE_GETOPT */ + +/* + * getopt_long -- + * Parse argc/argv argument vector. + */ +int +getopt_long(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE)); +} + +/* + * getopt_long_only -- + * Parse argc/argv argument vector. + */ +int +getopt_long_only(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE|FLAG_LONGONLY)); +} +/******************************************************************************* + * This file is part of the argtable3 library. + * + * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of STEWART HEITMANN nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include + +#include "argtable3.h" + + +char * arg_strptime(const char *buf, const char *fmt, struct tm *tm); + + +static void arg_date_resetfn(struct arg_date *parent) +{ + ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); + parent->count = 0; +} + + +static int arg_date_scanfn(struct arg_date *parent, const char *argval) +{ + int errorcode = 0; + + if (parent->count == parent->hdr.maxcount) + { + errorcode = EMAXCOUNT; + } + else if (!argval) + { + /* no argument value was given, leave parent->tmval[] unaltered but still count it */ + parent->count++; + } + else + { + const char *pend; + struct tm tm = parent->tmval[parent->count]; + + /* parse the given argument value, store result in parent->tmval[] */ + pend = arg_strptime(argval, parent->format, &tm); + if (pend && pend[0] == '\0') + parent->tmval[parent->count++] = tm; + else + errorcode = EBADDATE; + } + + ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode)); + return errorcode; +} + + +static int arg_date_checkfn(struct arg_date *parent) +{ + int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0; + + ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode)); + return errorcode; +} + + +static void arg_date_errorfn( + struct arg_date *parent, + FILE *fp, + int errorcode, + const char *argval, + const char *progname) +{ + const char *shortopts = parent->hdr.shortopts; + const char *longopts = parent->hdr.longopts; + const char *datatype = parent->hdr.datatype; + + /* make argval NULL safe */ + argval = argval ? argval : ""; + + fprintf(fp, "%s: ", progname); + switch(errorcode) + { + case EMINCOUNT: + fputs("missing option ", fp); + arg_print_option(fp, shortopts, longopts, datatype, "\n"); + break; + + case EMAXCOUNT: + fputs("excess option ", fp); + arg_print_option(fp, shortopts, longopts, argval, "\n"); + break; + + case EBADDATE: + { + struct tm tm; + char buff[200]; + + fprintf(fp, "illegal timestamp format \"%s\"\n", argval); + memset(&tm, 0, sizeof(tm)); + arg_strptime("1999-12-31 23:59:59", "%F %H:%M:%S", &tm); + strftime(buff, sizeof(buff), parent->format, &tm); + printf("correct format is \"%s\"\n", buff); + break; + } + } +} + + +struct arg_date * arg_date0( + const char * shortopts, + const char * longopts, + const char * format, + const char *datatype, + const char *glossary) +{ + return arg_daten(shortopts, longopts, format, datatype, 0, 1, glossary); +} + + +struct arg_date * arg_date1( + const char * shortopts, + const char * longopts, + const char * format, + const char *datatype, + const char *glossary) +{ + return arg_daten(shortopts, longopts, format, datatype, 1, 1, glossary); +} + + +struct arg_date * arg_daten( + const char * shortopts, + const char * longopts, + const char * format, + const char *datatype, + int mincount, + int maxcount, + const char *glossary) +{ + size_t nbytes; + struct arg_date *result; + + /* foolproof things by ensuring maxcount is not less than mincount */ + maxcount = (maxcount < mincount) ? mincount : maxcount; + + /* default time format is the national date format for the locale */ + if (!format) + format = "%x"; + + nbytes = sizeof(struct arg_date) /* storage for struct arg_date */ + + maxcount * sizeof(struct tm); /* storage for tmval[maxcount] array */ + + /* allocate storage for the arg_date struct + tmval[] array. */ + /* we use calloc because we want the tmval[] array zero filled. */ + result = (struct arg_date *)calloc(1, nbytes); + if (result) + { + /* init the arg_hdr struct */ + result->hdr.flag = ARG_HASVALUE; + result->hdr.shortopts = shortopts; + result->hdr.longopts = longopts; + result->hdr.datatype = datatype ? datatype : format; + result->hdr.glossary = glossary; + result->hdr.mincount = mincount; + result->hdr.maxcount = maxcount; + result->hdr.parent = result; + result->hdr.resetfn = (arg_resetfn *)arg_date_resetfn; + result->hdr.scanfn = (arg_scanfn *)arg_date_scanfn; + result->hdr.checkfn = (arg_checkfn *)arg_date_checkfn; + result->hdr.errorfn = (arg_errorfn *)arg_date_errorfn; + + /* store the tmval[maxcount] array immediately after the arg_date struct */ + result->tmval = (struct tm *)(result + 1); + + /* init the remaining arg_date member variables */ + result->count = 0; + result->format = format; + } + + ARG_TRACE(("arg_daten() returns %p\n", result)); + return result; +} + + +/*- + * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code was contributed to The NetBSD Foundation by Klaus Klein. + * Heavily optimised by David Laight + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +/* + * We do not implement alternate representations. However, we always + * check whether a given modifier is allowed for a certain conversion. + */ +#define ALT_E 0x01 +#define ALT_O 0x02 +#define LEGAL_ALT(x) { if (alt_format & ~(x)) return (0); } +#define TM_YEAR_BASE (1900) + +static int conv_num(const char * *, int *, int, int); + +static const char *day[7] = { + "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", + "Friday", "Saturday" +}; + +static const char *abday[7] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" +}; + +static const char *mon[12] = { + "January", "February", "March", "April", "May", "June", "July", + "August", "September", "October", "November", "December" +}; + +static const char *abmon[12] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +static const char *am_pm[2] = { + "AM", "PM" +}; + + +static int arg_strcasecmp(const char *s1, const char *s2) +{ + const unsigned char *us1 = (const unsigned char *)s1; + const unsigned char *us2 = (const unsigned char *)s2; + while (tolower(*us1) == tolower(*us2++)) + if (*us1++ == '\0') + return 0; + + return tolower(*us1) - tolower(*--us2); +} + + +static int arg_strncasecmp(const char *s1, const char *s2, size_t n) +{ + if (n != 0) + { + const unsigned char *us1 = (const unsigned char *)s1; + const unsigned char *us2 = (const unsigned char *)s2; + do + { + if (tolower(*us1) != tolower(*us2++)) + return tolower(*us1) - tolower(*--us2); + + if (*us1++ == '\0') + break; + } while (--n != 0); + } + + return 0; +} + + +char * arg_strptime(const char *buf, const char *fmt, struct tm *tm) +{ + char c; + const char *bp; + size_t len = 0; + int alt_format, i, split_year = 0; + + bp = buf; + + while ((c = *fmt) != '\0') { + /* Clear `alternate' modifier prior to new conversion. */ + alt_format = 0; + + /* Eat up white-space. */ + if (ISSPACE(c)) { + while (ISSPACE(*bp)) + bp++; + + fmt++; + continue; + } + + if ((c = *fmt++) != '%') + goto literal; + + +again: + switch (c = *fmt++) + { + case '%': /* "%%" is converted to "%". */ +literal: + if (c != *bp++) + return (0); + break; + + /* + * "Alternative" modifiers. Just set the appropriate flag + * and start over again. + */ + case 'E': /* "%E?" alternative conversion modifier. */ + LEGAL_ALT(0); + alt_format |= ALT_E; + goto again; + + case 'O': /* "%O?" alternative conversion modifier. */ + LEGAL_ALT(0); + alt_format |= ALT_O; + goto again; + + /* + * "Complex" conversion rules, implemented through recursion. + */ + case 'c': /* Date and time, using the locale's format. */ + LEGAL_ALT(ALT_E); + bp = arg_strptime(bp, "%x %X", tm); + if (!bp) + return (0); + break; + + case 'D': /* The date as "%m/%d/%y". */ + LEGAL_ALT(0); + bp = arg_strptime(bp, "%m/%d/%y", tm); + if (!bp) + return (0); + break; + + case 'R': /* The time as "%H:%M". */ + LEGAL_ALT(0); + bp = arg_strptime(bp, "%H:%M", tm); + if (!bp) + return (0); + break; + + case 'r': /* The time in 12-hour clock representation. */ + LEGAL_ALT(0); + bp = arg_strptime(bp, "%I:%M:%S %p", tm); + if (!bp) + return (0); + break; + + case 'T': /* The time as "%H:%M:%S". */ + LEGAL_ALT(0); + bp = arg_strptime(bp, "%H:%M:%S", tm); + if (!bp) + return (0); + break; + + case 'X': /* The time, using the locale's format. */ + LEGAL_ALT(ALT_E); + bp = arg_strptime(bp, "%H:%M:%S", tm); + if (!bp) + return (0); + break; + + case 'x': /* The date, using the locale's format. */ + LEGAL_ALT(ALT_E); + bp = arg_strptime(bp, "%m/%d/%y", tm); + if (!bp) + return (0); + break; + + /* + * "Elementary" conversion rules. + */ + case 'A': /* The day of week, using the locale's form. */ + case 'a': + LEGAL_ALT(0); + for (i = 0; i < 7; i++) { + /* Full name. */ + len = strlen(day[i]); + if (arg_strncasecmp(day[i], bp, len) == 0) + break; + + /* Abbreviated name. */ + len = strlen(abday[i]); + if (arg_strncasecmp(abday[i], bp, len) == 0) + break; + } + + /* Nothing matched. */ + if (i == 7) + return (0); + + tm->tm_wday = i; + bp += len; + break; + + case 'B': /* The month, using the locale's form. */ + case 'b': + case 'h': + LEGAL_ALT(0); + for (i = 0; i < 12; i++) { + /* Full name. */ + len = strlen(mon[i]); + if (arg_strncasecmp(mon[i], bp, len) == 0) + break; + + /* Abbreviated name. */ + len = strlen(abmon[i]); + if (arg_strncasecmp(abmon[i], bp, len) == 0) + break; + } + + /* Nothing matched. */ + if (i == 12) + return (0); + + tm->tm_mon = i; + bp += len; + break; + + case 'C': /* The century number. */ + LEGAL_ALT(ALT_E); + if (!(conv_num(&bp, &i, 0, 99))) + return (0); + + if (split_year) { + tm->tm_year = (tm->tm_year % 100) + (i * 100); + } else { + tm->tm_year = i * 100; + split_year = 1; + } + break; + + case 'd': /* The day of month. */ + case 'e': + LEGAL_ALT(ALT_O); + if (!(conv_num(&bp, &tm->tm_mday, 1, 31))) + return (0); + break; + + case 'k': /* The hour (24-hour clock representation). */ + LEGAL_ALT(0); + /* FALLTHROUGH */ + case 'H': + LEGAL_ALT(ALT_O); + if (!(conv_num(&bp, &tm->tm_hour, 0, 23))) + return (0); + break; + + case 'l': /* The hour (12-hour clock representation). */ + LEGAL_ALT(0); + /* FALLTHROUGH */ + case 'I': + LEGAL_ALT(ALT_O); + if (!(conv_num(&bp, &tm->tm_hour, 1, 12))) + return (0); + if (tm->tm_hour == 12) + tm->tm_hour = 0; + break; + + case 'j': /* The day of year. */ + LEGAL_ALT(0); + if (!(conv_num(&bp, &i, 1, 366))) + return (0); + tm->tm_yday = i - 1; + break; + + case 'M': /* The minute. */ + LEGAL_ALT(ALT_O); + if (!(conv_num(&bp, &tm->tm_min, 0, 59))) + return (0); + break; + + case 'm': /* The month. */ + LEGAL_ALT(ALT_O); + if (!(conv_num(&bp, &i, 1, 12))) + return (0); + tm->tm_mon = i - 1; + break; + + case 'p': /* The locale's equivalent of AM/PM. */ + LEGAL_ALT(0); + /* AM? */ + if (arg_strcasecmp(am_pm[0], bp) == 0) { + if (tm->tm_hour > 11) + return (0); + + bp += strlen(am_pm[0]); + break; + } + /* PM? */ + else if (arg_strcasecmp(am_pm[1], bp) == 0) { + if (tm->tm_hour > 11) + return (0); + + tm->tm_hour += 12; + bp += strlen(am_pm[1]); + break; + } + + /* Nothing matched. */ + return (0); + + case 'S': /* The seconds. */ + LEGAL_ALT(ALT_O); + if (!(conv_num(&bp, &tm->tm_sec, 0, 61))) + return (0); + break; + + case 'U': /* The week of year, beginning on sunday. */ + case 'W': /* The week of year, beginning on monday. */ + LEGAL_ALT(ALT_O); + /* + * XXX This is bogus, as we can not assume any valid + * information present in the tm structure at this + * point to calculate a real value, so just check the + * range for now. + */ + if (!(conv_num(&bp, &i, 0, 53))) + return (0); + break; + + case 'w': /* The day of week, beginning on sunday. */ + LEGAL_ALT(ALT_O); + if (!(conv_num(&bp, &tm->tm_wday, 0, 6))) + return (0); + break; + + case 'Y': /* The year. */ + LEGAL_ALT(ALT_E); + if (!(conv_num(&bp, &i, 0, 9999))) + return (0); + + tm->tm_year = i - TM_YEAR_BASE; + break; + + case 'y': /* The year within 100 years of the epoch. */ + LEGAL_ALT(ALT_E | ALT_O); + if (!(conv_num(&bp, &i, 0, 99))) + return (0); + + if (split_year) { + tm->tm_year = ((tm->tm_year / 100) * 100) + i; + break; + } + split_year = 1; + if (i <= 68) + tm->tm_year = i + 2000 - TM_YEAR_BASE; + else + tm->tm_year = i + 1900 - TM_YEAR_BASE; + break; + + /* + * Miscellaneous conversions. + */ + case 'n': /* Any kind of white-space. */ + case 't': + LEGAL_ALT(0); + while (ISSPACE(*bp)) + bp++; + break; + + + default: /* Unknown/unsupported conversion. */ + return (0); + } + + + } + + /* LINTED functional specification */ + return ((char *)bp); +} + + +static int conv_num(const char * *buf, int *dest, int llim, int ulim) +{ + int result = 0; + + /* The limit also determines the number of valid digits. */ + int rulim = ulim; + + if (**buf < '0' || **buf > '9') + return (0); + + do { + result *= 10; + result += *(*buf)++ - '0'; + rulim /= 10; + } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9'); + + if (result < llim || result > ulim) + return (0); + + *dest = result; + return (1); +} +/******************************************************************************* + * This file is part of the argtable3 library. + * + * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of STEWART HEITMANN nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include + +#include "argtable3.h" + + +static void arg_dbl_resetfn(struct arg_dbl *parent) +{ + ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); + parent->count = 0; +} + + +static int arg_dbl_scanfn(struct arg_dbl *parent, const char *argval) +{ + int errorcode = 0; + + if (parent->count == parent->hdr.maxcount) + { + /* maximum number of arguments exceeded */ + errorcode = EMAXCOUNT; + } + else if (!argval) + { + /* a valid argument with no argument value was given. */ + /* This happens when an optional argument value was invoked. */ + /* leave parent argument value unaltered but still count the argument. */ + parent->count++; + } + else + { + double val; + char *end; + + /* extract double from argval into val */ + val = strtod(argval, &end); + + /* if success then store result in parent->dval[] array otherwise return error*/ + if (*end == 0) + parent->dval[parent->count++] = val; + else + errorcode = EBADDOUBLE; + } + + ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode)); + return errorcode; +} + + +static int arg_dbl_checkfn(struct arg_dbl *parent) +{ + int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0; + + ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode)); + return errorcode; +} + + +static void arg_dbl_errorfn( + struct arg_dbl *parent, + FILE *fp, + int errorcode, + const char *argval, + const char *progname) +{ + const char *shortopts = parent->hdr.shortopts; + const char *longopts = parent->hdr.longopts; + const char *datatype = parent->hdr.datatype; + + /* make argval NULL safe */ + argval = argval ? argval : ""; + + fprintf(fp, "%s: ", progname); + switch(errorcode) + { + case EMINCOUNT: + fputs("missing option ", fp); + arg_print_option(fp, shortopts, longopts, datatype, "\n"); + break; + + case EMAXCOUNT: + fputs("excess option ", fp); + arg_print_option(fp, shortopts, longopts, argval, "\n"); + break; + + case EBADDOUBLE: + fprintf(fp, "invalid argument \"%s\" to option ", argval); + arg_print_option(fp, shortopts, longopts, datatype, "\n"); + break; + } +} + + +struct arg_dbl * arg_dbl0( + const char * shortopts, + const char * longopts, + const char *datatype, + const char *glossary) +{ + return arg_dbln(shortopts, longopts, datatype, 0, 1, glossary); +} + + +struct arg_dbl * arg_dbl1( + const char * shortopts, + const char * longopts, + const char *datatype, + const char *glossary) +{ + return arg_dbln(shortopts, longopts, datatype, 1, 1, glossary); +} + + +struct arg_dbl * arg_dbln( + const char * shortopts, + const char * longopts, + const char *datatype, + int mincount, + int maxcount, + const char *glossary) +{ + size_t nbytes; + struct arg_dbl *result; + + /* foolproof things by ensuring maxcount is not less than mincount */ + maxcount = (maxcount < mincount) ? mincount : maxcount; + + nbytes = sizeof(struct arg_dbl) /* storage for struct arg_dbl */ + + (maxcount + 1) * sizeof(double); /* storage for dval[maxcount] array plus one extra for padding to memory boundary */ + + result = (struct arg_dbl *)malloc(nbytes); + if (result) + { + size_t addr; + size_t rem; + + /* init the arg_hdr struct */ + result->hdr.flag = ARG_HASVALUE; + result->hdr.shortopts = shortopts; + result->hdr.longopts = longopts; + result->hdr.datatype = datatype ? datatype : ""; + result->hdr.glossary = glossary; + result->hdr.mincount = mincount; + result->hdr.maxcount = maxcount; + result->hdr.parent = result; + result->hdr.resetfn = (arg_resetfn *)arg_dbl_resetfn; + result->hdr.scanfn = (arg_scanfn *)arg_dbl_scanfn; + result->hdr.checkfn = (arg_checkfn *)arg_dbl_checkfn; + result->hdr.errorfn = (arg_errorfn *)arg_dbl_errorfn; + + /* Store the dval[maxcount] array on the first double boundary that + * immediately follows the arg_dbl struct. We do the memory alignment + * purely for SPARC and Motorola systems. They require floats and + * doubles to be aligned on natural boundaries. + */ + addr = (size_t)(result + 1); + rem = addr % sizeof(double); + result->dval = (double *)(addr + sizeof(double) - rem); + ARG_TRACE(("addr=%p, dval=%p, sizeof(double)=%d rem=%d\n", addr, result->dval, (int)sizeof(double), (int)rem)); + + result->count = 0; + } + + ARG_TRACE(("arg_dbln() returns %p\n", result)); + return result; +} +/******************************************************************************* + * This file is part of the argtable3 library. + * + * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of STEWART HEITMANN nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include + +#include "argtable3.h" + + +static void arg_end_resetfn(struct arg_end *parent) +{ + ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); + parent->count = 0; +} + +static void arg_end_errorfn( + void *parent, + FILE *fp, + int error, + const char *argval, + const char *progname) +{ + /* suppress unreferenced formal parameter warning */ + (void)parent; + + progname = progname ? progname : ""; + argval = argval ? argval : ""; + + fprintf(fp, "%s: ", progname); + switch(error) + { + case ARG_ELIMIT: + fputs("too many errors to display", fp); + break; + case ARG_EMALLOC: + fputs("insufficent memory", fp); + break; + case ARG_ENOMATCH: + fprintf(fp, "unexpected argument \"%s\"", argval); + break; + case ARG_EMISSARG: + fprintf(fp, "option \"%s\" requires an argument", argval); + break; + case ARG_ELONGOPT: + fprintf(fp, "invalid option \"%s\"", argval); + break; + default: + fprintf(fp, "invalid option \"-%c\"", error); + break; + } + + fputc('\n', fp); +} + + +struct arg_end * arg_end(int maxcount) +{ + size_t nbytes; + struct arg_end *result; + + nbytes = sizeof(struct arg_end) + + maxcount * sizeof(int) /* storage for int error[maxcount] array*/ + + maxcount * sizeof(void *) /* storage for void* parent[maxcount] array */ + + maxcount * sizeof(char *); /* storage for char* argval[maxcount] array */ + + result = (struct arg_end *)malloc(nbytes); + if (result) + { + /* init the arg_hdr struct */ + result->hdr.flag = ARG_TERMINATOR; + result->hdr.shortopts = NULL; + result->hdr.longopts = NULL; + result->hdr.datatype = NULL; + result->hdr.glossary = NULL; + result->hdr.mincount = 1; + result->hdr.maxcount = maxcount; + result->hdr.parent = result; + result->hdr.resetfn = (arg_resetfn *)arg_end_resetfn; + result->hdr.scanfn = NULL; + result->hdr.checkfn = NULL; + result->hdr.errorfn = (arg_errorfn *)arg_end_errorfn; + + /* store error[maxcount] array immediately after struct arg_end */ + result->error = (int *)(result + 1); + + /* store parent[maxcount] array immediately after error[] array */ + result->parent = (void * *)(result->error + maxcount ); + + /* store argval[maxcount] array immediately after parent[] array */ + result->argval = (const char * *)(result->parent + maxcount ); + } + + ARG_TRACE(("arg_end(%d) returns %p\n", maxcount, result)); + return result; +} + + +void arg_print_errors(FILE * fp, struct arg_end * end, const char * progname) +{ + int i; + ARG_TRACE(("arg_errors()\n")); + for (i = 0; i < end->count; i++) + { + struct arg_hdr *errorparent = (struct arg_hdr *)(end->parent[i]); + if (errorparent->errorfn) + errorparent->errorfn(end->parent[i], + fp, + end->error[i], + end->argval[i], + progname); + } +} +/******************************************************************************* + * This file is part of the argtable3 library. + * + * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of STEWART HEITMANN nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include + +#include "argtable3.h" + +#ifdef WIN32 +# define FILESEPARATOR1 '\\' +# define FILESEPARATOR2 '/' +#else +# define FILESEPARATOR1 '/' +# define FILESEPARATOR2 '/' +#endif + + +static void arg_file_resetfn(struct arg_file *parent) +{ + ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); + parent->count = 0; +} + + +/* Returns ptr to the base filename within *filename */ +static const char * arg_basename(const char *filename) +{ + const char *result = NULL, *result1, *result2; + + /* Find the last occurrence of eother file separator character. */ + /* Two alternative file separator chars are supported as legal */ + /* file separators but not both together in the same filename. */ + result1 = (filename ? strrchr(filename, FILESEPARATOR1) : NULL); + result2 = (filename ? strrchr(filename, FILESEPARATOR2) : NULL); + + if (result2) + result = result2 + 1; /* using FILESEPARATOR2 (the alternative file separator) */ + + if (result1) + result = result1 + 1; /* using FILESEPARATOR1 (the preferred file separator) */ + + if (!result) + result = filename; /* neither file separator was found so basename is the whole filename */ + + /* special cases of "." and ".." are not considered basenames */ + if (result && ( strcmp(".", result) == 0 || strcmp("..", result) == 0 )) + result = filename + strlen(filename); + + return result; +} + + +/* Returns ptr to the file extension within *basename */ +static const char * arg_extension(const char *basename) +{ + /* find the last occurrence of '.' in basename */ + const char *result = (basename ? strrchr(basename, '.') : NULL); + + /* if no '.' was found then return pointer to end of basename */ + if (basename && !result) + result = basename + strlen(basename); + + /* special case: basenames with a single leading dot (eg ".foo") are not considered as true extensions */ + if (basename && result == basename) + result = basename + strlen(basename); + + /* special case: empty extensions (eg "foo.","foo..") are not considered as true extensions */ + if (basename && result && result[1] == '\0') + result = basename + strlen(basename); + + return result; +} + + +static int arg_file_scanfn(struct arg_file *parent, const char *argval) +{ + int errorcode = 0; + + if (parent->count == parent->hdr.maxcount) + { + /* maximum number of arguments exceeded */ + errorcode = EMAXCOUNT; + } + else if (!argval) + { + /* a valid argument with no argument value was given. */ + /* This happens when an optional argument value was invoked. */ + /* leave parent arguiment value unaltered but still count the argument. */ + parent->count++; + } + else + { + parent->filename[parent->count] = argval; + parent->basename[parent->count] = arg_basename(argval); + parent->extension[parent->count] = + arg_extension(parent->basename[parent->count]); /* only seek extensions within the basename (not the file path)*/ + parent->count++; + } + + ARG_TRACE(("%s4:scanfn(%p) returns %d\n", __FILE__, parent, errorcode)); + return errorcode; +} + + +static int arg_file_checkfn(struct arg_file *parent) +{ + int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0; + + ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode)); + return errorcode; +} + + +static void arg_file_errorfn( + struct arg_file *parent, + FILE *fp, + int errorcode, + const char *argval, + const char *progname) +{ + const char *shortopts = parent->hdr.shortopts; + const char *longopts = parent->hdr.longopts; + const char *datatype = parent->hdr.datatype; + + /* make argval NULL safe */ + argval = argval ? argval : ""; + + fprintf(fp, "%s: ", progname); + switch(errorcode) + { + case EMINCOUNT: + fputs("missing option ", fp); + arg_print_option(fp, shortopts, longopts, datatype, "\n"); + break; + + case EMAXCOUNT: + fputs("excess option ", fp); + arg_print_option(fp, shortopts, longopts, argval, "\n"); + break; + + default: + fprintf(fp, "unknown error at \"%s\"\n", argval); + } +} + + +struct arg_file * arg_file0( + const char * shortopts, + const char * longopts, + const char *datatype, + const char *glossary) +{ + return arg_filen(shortopts, longopts, datatype, 0, 1, glossary); +} + + +struct arg_file * arg_file1( + const char * shortopts, + const char * longopts, + const char *datatype, + const char *glossary) +{ + return arg_filen(shortopts, longopts, datatype, 1, 1, glossary); +} + + +struct arg_file * arg_filen( + const char * shortopts, + const char * longopts, + const char *datatype, + int mincount, + int maxcount, + const char *glossary) +{ + size_t nbytes; + struct arg_file *result; + + /* foolproof things by ensuring maxcount is not less than mincount */ + maxcount = (maxcount < mincount) ? mincount : maxcount; + + nbytes = sizeof(struct arg_file) /* storage for struct arg_file */ + + sizeof(char *) * maxcount /* storage for filename[maxcount] array */ + + sizeof(char *) * maxcount /* storage for basename[maxcount] array */ + + sizeof(char *) * maxcount; /* storage for extension[maxcount] array */ + + result = (struct arg_file *)malloc(nbytes); + if (result) + { + int i; + + /* init the arg_hdr struct */ + result->hdr.flag = ARG_HASVALUE; + result->hdr.shortopts = shortopts; + result->hdr.longopts = longopts; + result->hdr.glossary = glossary; + result->hdr.datatype = datatype ? datatype : ""; + result->hdr.mincount = mincount; + result->hdr.maxcount = maxcount; + result->hdr.parent = result; + result->hdr.resetfn = (arg_resetfn *)arg_file_resetfn; + result->hdr.scanfn = (arg_scanfn *)arg_file_scanfn; + result->hdr.checkfn = (arg_checkfn *)arg_file_checkfn; + result->hdr.errorfn = (arg_errorfn *)arg_file_errorfn; + + /* store the filename,basename,extension arrays immediately after the arg_file struct */ + result->filename = (const char * *)(result + 1); + result->basename = result->filename + maxcount; + result->extension = result->basename + maxcount; + result->count = 0; + + /* foolproof the string pointers by initialising them with empty strings */ + for (i = 0; i < maxcount; i++) + { + result->filename[i] = ""; + result->basename[i] = ""; + result->extension[i] = ""; + } + } + + ARG_TRACE(("arg_filen() returns %p\n", result)); + return result; +} +/******************************************************************************* + * This file is part of the argtable3 library. + * + * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of STEWART HEITMANN nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include +#include + +#include "argtable3.h" + + +static void arg_int_resetfn(struct arg_int *parent) +{ + ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); + parent->count = 0; +} + + +/* strtol0x() is like strtol() except that the numeric string is */ +/* expected to be prefixed by "0X" where X is a user supplied char. */ +/* The string may optionally be prefixed by white space and + or - */ +/* as in +0X123 or -0X123. */ +/* Once the prefix has been scanned, the remainder of the numeric */ +/* string is converted using strtol() with the given base. */ +/* eg: to parse hex str="-0X12324", specify X='X' and base=16. */ +/* eg: to parse oct str="+0o12324", specify X='O' and base=8. */ +/* eg: to parse bin str="-0B01010", specify X='B' and base=2. */ +/* Failure of conversion is indicated by result where *endptr==str. */ +static long int strtol0X(const char * str, + const char * *endptr, + char X, + int base) +{ + long int val; /* stores result */ + int s = 1; /* sign is +1 or -1 */ + const char *ptr = str; /* ptr to current position in str */ + + /* skip leading whitespace */ + while (ISSPACE(*ptr)) + ptr++; + /* printf("1) %s\n",ptr); */ + + /* scan optional sign character */ + switch (*ptr) + { + case '+': + ptr++; + s = 1; + break; + case '-': + ptr++; + s = -1; + break; + default: + s = 1; + break; + } + /* printf("2) %s\n",ptr); */ + + /* '0X' prefix */ + if ((*ptr++) != '0') + { + /* printf("failed to detect '0'\n"); */ + *endptr = str; + return 0; + } + /* printf("3) %s\n",ptr); */ + if (toupper(*ptr++) != toupper(X)) + { + /* printf("failed to detect '%c'\n",X); */ + *endptr = str; + return 0; + } + /* printf("4) %s\n",ptr); */ + + /* attempt conversion on remainder of string using strtol() */ + val = strtol(ptr, (char * *)endptr, base); + if (*endptr == ptr) + { + /* conversion failed */ + *endptr = str; + return 0; + } + + /* success */ + return s * val; +} + + +/* Returns 1 if str matches suffix (case insensitive). */ +/* Str may contain trailing whitespace, but nothing else. */ +static int detectsuffix(const char *str, const char *suffix) +{ + /* scan pairwise through strings until mismatch detected */ + while( toupper(*str) == toupper(*suffix) ) + { + /* printf("'%c' '%c'\n", *str, *suffix); */ + + /* return 1 (success) if match persists until the string terminator */ + if (*str == '\0') + return 1; + + /* next chars */ + str++; + suffix++; + } + /* printf("'%c' '%c' mismatch\n", *str, *suffix); */ + + /* return 0 (fail) if the matching did not consume the entire suffix */ + if (*suffix != 0) + return 0; /* failed to consume entire suffix */ + + /* skip any remaining whitespace in str */ + while (ISSPACE(*str)) + str++; + + /* return 1 (success) if we have reached end of str else return 0 (fail) */ + return (*str == '\0') ? 1 : 0; +} + + +static int arg_int_scanfn(struct arg_int *parent, const char *argval) +{ + int errorcode = 0; + + if (parent->count == parent->hdr.maxcount) + { + /* maximum number of arguments exceeded */ + errorcode = EMAXCOUNT; + } + else if (!argval) + { + /* a valid argument with no argument value was given. */ + /* This happens when an optional argument value was invoked. */ + /* leave parent arguiment value unaltered but still count the argument. */ + parent->count++; + } + else + { + long int val; + const char *end; + + /* attempt to extract hex integer (eg: +0x123) from argval into val conversion */ + val = strtol0X(argval, &end, 'X', 16); + if (end == argval) + { + /* hex failed, attempt octal conversion (eg +0o123) */ + val = strtol0X(argval, &end, 'O', 8); + if (end == argval) + { + /* octal failed, attempt binary conversion (eg +0B101) */ + val = strtol0X(argval, &end, 'B', 2); + if (end == argval) + { + /* binary failed, attempt decimal conversion with no prefix (eg 1234) */ + val = strtol(argval, (char * *)&end, 10); + if (end == argval) + { + /* all supported number formats failed */ + return EBADINT; + } + } + } + } + + /* Safety check for integer overflow. WARNING: this check */ + /* achieves nothing on machines where size(int)==size(long). */ + if ( val > INT_MAX || val < INT_MIN ) +#ifdef __STDC_WANT_SECURE_LIB__ + errorcode = EOVERFLOW_; +#else + errorcode = EOVERFLOW; +#endif + + /* Detect any suffixes (KB,MB,GB) and multiply argument value appropriately. */ + /* We need to be mindful of integer overflows when using such big numbers. */ + if (detectsuffix(end, "KB")) /* kilobytes */ + { + if ( val > (INT_MAX / 1024) || val < (INT_MIN / 1024) ) +#ifdef __STDC_WANT_SECURE_LIB__ + errorcode = EOVERFLOW_; /* Overflow would occur if we proceed */ +#else + errorcode = EOVERFLOW; /* Overflow would occur if we proceed */ +#endif + else + val *= 1024; /* 1KB = 1024 */ + } + else if (detectsuffix(end, "MB")) /* megabytes */ + { + if ( val > (INT_MAX / 1048576) || val < (INT_MIN / 1048576) ) +#ifdef __STDC_WANT_SECURE_LIB__ + errorcode = EOVERFLOW_; /* Overflow would occur if we proceed */ +#else + errorcode = EOVERFLOW; /* Overflow would occur if we proceed */ +#endif + else + val *= 1048576; /* 1MB = 1024*1024 */ + } + else if (detectsuffix(end, "GB")) /* gigabytes */ + { + if ( val > (INT_MAX / 1073741824) || val < (INT_MIN / 1073741824) ) +#ifdef __STDC_WANT_SECURE_LIB__ + errorcode = EOVERFLOW_; /* Overflow would occur if we proceed */ +#else + errorcode = EOVERFLOW; /* Overflow would occur if we proceed */ +#endif + else + val *= 1073741824; /* 1GB = 1024*1024*1024 */ + } + else if (!detectsuffix(end, "")) + errorcode = EBADINT; /* invalid suffix detected */ + + /* if success then store result in parent->ival[] array */ + if (errorcode == 0) + parent->ival[parent->count++] = val; + } + + /* printf("%s:scanfn(%p,%p) returns %d\n",__FILE__,parent,argval,errorcode); */ + return errorcode; +} + + +static int arg_int_checkfn(struct arg_int *parent) +{ + int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0; + /*printf("%s:checkfn(%p) returns %d\n",__FILE__,parent,errorcode);*/ + return errorcode; +} + + +static void arg_int_errorfn( + struct arg_int *parent, + FILE *fp, + int errorcode, + const char *argval, + const char *progname) +{ + const char *shortopts = parent->hdr.shortopts; + const char *longopts = parent->hdr.longopts; + const char *datatype = parent->hdr.datatype; + + /* make argval NULL safe */ + argval = argval ? argval : ""; + + fprintf(fp, "%s: ", progname); + switch(errorcode) + { + case EMINCOUNT: + fputs("missing option ", fp); + arg_print_option(fp, shortopts, longopts, datatype, "\n"); + break; + + case EMAXCOUNT: + fputs("excess option ", fp); + arg_print_option(fp, shortopts, longopts, argval, "\n"); + break; + + case EBADINT: + fprintf(fp, "invalid argument \"%s\" to option ", argval); + arg_print_option(fp, shortopts, longopts, datatype, "\n"); + break; + +#ifdef __STDC_WANT_SECURE_LIB__ + case EOVERFLOW_: +#else + case EOVERFLOW: +#endif + fputs("integer overflow at option ", fp); + arg_print_option(fp, shortopts, longopts, datatype, " "); + fprintf(fp, "(%s is too large)\n", argval); + break; + } +} + + +struct arg_int * arg_int0( + const char *shortopts, + const char *longopts, + const char *datatype, + const char *glossary) +{ + return arg_intn(shortopts, longopts, datatype, 0, 1, glossary); +} + + +struct arg_int * arg_int1( + const char *shortopts, + const char *longopts, + const char *datatype, + const char *glossary) +{ + return arg_intn(shortopts, longopts, datatype, 1, 1, glossary); +} + + +struct arg_int * arg_intn( + const char *shortopts, + const char *longopts, + const char *datatype, + int mincount, + int maxcount, + const char *glossary) +{ + size_t nbytes; + struct arg_int *result; + + /* foolproof things by ensuring maxcount is not less than mincount */ + maxcount = (maxcount < mincount) ? mincount : maxcount; + + nbytes = sizeof(struct arg_int) /* storage for struct arg_int */ + + maxcount * sizeof(int); /* storage for ival[maxcount] array */ + + result = (struct arg_int *)malloc(nbytes); + if (result) + { + /* init the arg_hdr struct */ + result->hdr.flag = ARG_HASVALUE; + result->hdr.shortopts = shortopts; + result->hdr.longopts = longopts; + result->hdr.datatype = datatype ? datatype : ""; + result->hdr.glossary = glossary; + result->hdr.mincount = mincount; + result->hdr.maxcount = maxcount; + result->hdr.parent = result; + result->hdr.resetfn = (arg_resetfn *)arg_int_resetfn; + result->hdr.scanfn = (arg_scanfn *)arg_int_scanfn; + result->hdr.checkfn = (arg_checkfn *)arg_int_checkfn; + result->hdr.errorfn = (arg_errorfn *)arg_int_errorfn; + + /* store the ival[maxcount] array immediately after the arg_int struct */ + result->ival = (int *)(result + 1); + result->count = 0; + } + + ARG_TRACE(("arg_intn() returns %p\n", result)); + return result; +} +/******************************************************************************* + * This file is part of the argtable3 library. + * + * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of STEWART HEITMANN nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include + +#include "argtable3.h" + + +static void arg_lit_resetfn(struct arg_lit *parent) +{ + ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); + parent->count = 0; +} + + +static int arg_lit_scanfn(struct arg_lit *parent, const char *argval) +{ + int errorcode = 0; + if (parent->count < parent->hdr.maxcount ) + parent->count++; + else + errorcode = EMAXCOUNT; + + ARG_TRACE(("%s:scanfn(%p,%s) returns %d\n", __FILE__, parent, argval, + errorcode)); + return errorcode; +} + + +static int arg_lit_checkfn(struct arg_lit *parent) +{ + int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0; + ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode)); + return errorcode; +} + + +static void arg_lit_errorfn( + struct arg_lit *parent, + FILE *fp, + int errorcode, + const char *argval, + const char *progname) +{ + const char *shortopts = parent->hdr.shortopts; + const char *longopts = parent->hdr.longopts; + const char *datatype = parent->hdr.datatype; + + switch(errorcode) + { + case EMINCOUNT: + fprintf(fp, "%s: missing option ", progname); + arg_print_option(fp, shortopts, longopts, datatype, "\n"); + fprintf(fp, "\n"); + break; + + case EMAXCOUNT: + fprintf(fp, "%s: extraneous option ", progname); + arg_print_option(fp, shortopts, longopts, datatype, "\n"); + break; + } + + ARG_TRACE(("%s:errorfn(%p, %p, %d, %s, %s)\n", __FILE__, parent, fp, + errorcode, argval, progname)); +} + + +struct arg_lit * arg_lit0( + const char * shortopts, + const char * longopts, + const char * glossary) +{ + return arg_litn(shortopts, longopts, 0, 1, glossary); +} + + +struct arg_lit * arg_lit1( + const char *shortopts, + const char *longopts, + const char *glossary) +{ + return arg_litn(shortopts, longopts, 1, 1, glossary); +} + + +struct arg_lit * arg_litn( + const char *shortopts, + const char *longopts, + int mincount, + int maxcount, + const char *glossary) +{ + struct arg_lit *result; + + /* foolproof things by ensuring maxcount is not less than mincount */ + maxcount = (maxcount < mincount) ? mincount : maxcount; + + result = (struct arg_lit *)malloc(sizeof(struct arg_lit)); + if (result) + { + /* init the arg_hdr struct */ + result->hdr.flag = 0; + result->hdr.shortopts = shortopts; + result->hdr.longopts = longopts; + result->hdr.datatype = NULL; + result->hdr.glossary = glossary; + result->hdr.mincount = mincount; + result->hdr.maxcount = maxcount; + result->hdr.parent = result; + result->hdr.resetfn = (arg_resetfn *)arg_lit_resetfn; + result->hdr.scanfn = (arg_scanfn *)arg_lit_scanfn; + result->hdr.checkfn = (arg_checkfn *)arg_lit_checkfn; + result->hdr.errorfn = (arg_errorfn *)arg_lit_errorfn; + + /* init local variables */ + result->count = 0; + } + + ARG_TRACE(("arg_litn() returns %p\n", result)); + return result; +} +/******************************************************************************* + * This file is part of the argtable3 library. + * + * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of STEWART HEITMANN nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include + +#include "argtable3.h" + +struct arg_rem *arg_rem(const char *datatype, const char *glossary) +{ + struct arg_rem *result = (struct arg_rem *)malloc(sizeof(struct arg_rem)); + if (result) + { + result->hdr.flag = 0; + result->hdr.shortopts = NULL; + result->hdr.longopts = NULL; + result->hdr.datatype = datatype; + result->hdr.glossary = glossary; + result->hdr.mincount = 1; + result->hdr.maxcount = 1; + result->hdr.parent = result; + result->hdr.resetfn = NULL; + result->hdr.scanfn = NULL; + result->hdr.checkfn = NULL; + result->hdr.errorfn = NULL; + } + + ARG_TRACE(("arg_rem() returns %p\n", result)); + return result; +} + +/******************************************************************************* + * This file is part of the argtable3 library. + * + * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of STEWART HEITMANN nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include + +#include "argtable3.h" + + +#ifndef _TREX_H_ +#define _TREX_H_ +/*************************************************************** + T-Rex a tiny regular expression library + + Copyright (C) 2003-2006 Alberto Demichelis + + This software is provided 'as-is', without any express + or implied warranty. In no event will the authors be held + liable for any damages arising from the use of this software. + + Permission is granted to anyone to use this software for + any purpose, including commercial applications, and to alter + it and redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; + you must not claim that you wrote the original software. + If you use this software in a product, an acknowledgment + in the product documentation would be appreciated but + is not required. + + 2. Altered source versions must be plainly marked as such, + and must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any + source distribution. + +****************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _UNICODE +#define TRexChar unsigned short +#define MAX_CHAR 0xFFFF +#define _TREXC(c) L##c +#define trex_strlen wcslen +#define trex_printf wprintf +#else +#define TRexChar char +#define MAX_CHAR 0xFF +#define _TREXC(c) (c) +#define trex_strlen strlen +#define trex_printf printf +#endif + +#ifndef TREX_API +#define TREX_API extern +#endif + +#define TRex_True 1 +#define TRex_False 0 + +#define TREX_ICASE ARG_REX_ICASE + +typedef unsigned int TRexBool; +typedef struct TRex TRex; + +typedef struct { + const TRexChar *begin; + int len; +} TRexMatch; + +TREX_API TRex *trex_compile(const TRexChar *pattern, const TRexChar **error, int flags); +TREX_API void trex_free(TRex *exp); +TREX_API TRexBool trex_match(TRex* exp, const TRexChar* text); +TREX_API TRexBool trex_search(TRex* exp, const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end); +TREX_API TRexBool trex_searchrange(TRex* exp, const TRexChar* text_begin, const TRexChar* text_end, const TRexChar** out_begin, const TRexChar** out_end); +TREX_API int trex_getsubexpcount(TRex* exp); +TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp); + +#ifdef __cplusplus +} +#endif + +#endif + + + +struct privhdr +{ + const char *pattern; + int flags; +}; + + +static void arg_rex_resetfn(struct arg_rex *parent) +{ + ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); + parent->count = 0; +} + +static int arg_rex_scanfn(struct arg_rex *parent, const char *argval) +{ + int errorcode = 0; + const TRexChar *error = NULL; + TRex *rex = NULL; + TRexBool is_match = TRex_False; + + if (parent->count == parent->hdr.maxcount ) + { + /* maximum number of arguments exceeded */ + errorcode = EMAXCOUNT; + } + else if (!argval) + { + /* a valid argument with no argument value was given. */ + /* This happens when an optional argument value was invoked. */ + /* leave parent argument value unaltered but still count the argument. */ + parent->count++; + } + else + { + struct privhdr *priv = (struct privhdr *)parent->hdr.priv; + + /* test the current argument value for a match with the regular expression */ + /* if a match is detected, record the argument value in the arg_rex struct */ + + rex = trex_compile(priv->pattern, &error, priv->flags); + is_match = trex_match(rex, argval); + if (!is_match) + errorcode = EREGNOMATCH; + else + parent->sval[parent->count++] = argval; + + trex_free(rex); + } + + ARG_TRACE(("%s:scanfn(%p) returns %d\n",__FILE__,parent,errorcode)); + return errorcode; +} + +static int arg_rex_checkfn(struct arg_rex *parent) +{ + int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0; + //struct privhdr *priv = (struct privhdr*)parent->hdr.priv; + + /* free the regex "program" we constructed in resetfn */ + //regfree(&(priv->regex)); + + /*printf("%s:checkfn(%p) returns %d\n",__FILE__,parent,errorcode);*/ + return errorcode; +} + +static void arg_rex_errorfn(struct arg_rex *parent, + FILE *fp, + int errorcode, + const char *argval, + const char *progname) +{ + const char *shortopts = parent->hdr.shortopts; + const char *longopts = parent->hdr.longopts; + const char *datatype = parent->hdr.datatype; + + /* make argval NULL safe */ + argval = argval ? argval : ""; + + fprintf(fp, "%s: ", progname); + switch(errorcode) + { + case EMINCOUNT: + fputs("missing option ", fp); + arg_print_option(fp, shortopts, longopts, datatype, "\n"); + break; + + case EMAXCOUNT: + fputs("excess option ", fp); + arg_print_option(fp, shortopts, longopts, argval, "\n"); + break; + + case EREGNOMATCH: + fputs("illegal value ", fp); + arg_print_option(fp, shortopts, longopts, argval, "\n"); + break; + + default: + { + //char errbuff[256]; + //regerror(errorcode, NULL, errbuff, sizeof(errbuff)); + //printf("%s\n", errbuff); + } + break; + } +} + + +struct arg_rex * arg_rex0(const char * shortopts, + const char * longopts, + const char * pattern, + const char *datatype, + int flags, + const char *glossary) +{ + return arg_rexn(shortopts, + longopts, + pattern, + datatype, + 0, + 1, + flags, + glossary); +} + +struct arg_rex * arg_rex1(const char * shortopts, + const char * longopts, + const char * pattern, + const char *datatype, + int flags, + const char *glossary) +{ + return arg_rexn(shortopts, + longopts, + pattern, + datatype, + 1, + 1, + flags, + glossary); +} + + +struct arg_rex * arg_rexn(const char * shortopts, + const char * longopts, + const char * pattern, + const char *datatype, + int mincount, + int maxcount, + int flags, + const char *glossary) +{ + size_t nbytes; + struct arg_rex *result; + struct privhdr *priv; + int i; + const TRexChar *error = NULL; + TRex *rex = NULL; + + if (!pattern) + { + printf( + "argtable: ERROR - illegal regular expression pattern \"(NULL)\"\n"); + printf("argtable: Bad argument table.\n"); + return NULL; + } + + /* foolproof things by ensuring maxcount is not less than mincount */ + maxcount = (maxcount < mincount) ? mincount : maxcount; + + nbytes = sizeof(struct arg_rex) /* storage for struct arg_rex */ + + sizeof(struct privhdr) /* storage for private arg_rex data */ + + maxcount * sizeof(char *); /* storage for sval[maxcount] array */ + + result = (struct arg_rex *)malloc(nbytes); + if (result == NULL) + return result; + + /* init the arg_hdr struct */ + result->hdr.flag = ARG_HASVALUE; + result->hdr.shortopts = shortopts; + result->hdr.longopts = longopts; + result->hdr.datatype = datatype ? datatype : pattern; + result->hdr.glossary = glossary; + result->hdr.mincount = mincount; + result->hdr.maxcount = maxcount; + result->hdr.parent = result; + result->hdr.resetfn = (arg_resetfn *)arg_rex_resetfn; + result->hdr.scanfn = (arg_scanfn *)arg_rex_scanfn; + result->hdr.checkfn = (arg_checkfn *)arg_rex_checkfn; + result->hdr.errorfn = (arg_errorfn *)arg_rex_errorfn; + + /* store the arg_rex_priv struct immediately after the arg_rex struct */ + result->hdr.priv = result + 1; + priv = (struct privhdr *)(result->hdr.priv); + priv->pattern = pattern; + priv->flags = flags; + + /* store the sval[maxcount] array immediately after the arg_rex_priv struct */ + result->sval = (const char * *)(priv + 1); + result->count = 0; + + /* foolproof the string pointers by initializing them to reference empty strings */ + for (i = 0; i < maxcount; i++) + result->sval[i] = ""; + + /* here we construct and destroy a regex representation of the regular + * expression for no other reason than to force any regex errors to be + * trapped now rather than later. If we don't, then errors may go undetected + * until an argument is actually parsed. + */ + + rex = trex_compile(priv->pattern, &error, priv->flags); + if (rex == NULL) + { + ARG_LOG(("argtable: %s \"%s\"\n", error ? error : _TREXC("undefined"), priv->pattern)); + ARG_LOG(("argtable: Bad argument table.\n")); + } + + trex_free(rex); + + ARG_TRACE(("arg_rexn() returns %p\n", result)); + return result; +} + + + +/* see copyright notice in trex.h */ +#include +#include +#include +#include + +#ifdef _UINCODE +#define scisprint iswprint +#define scstrlen wcslen +#define scprintf wprintf +#define _SC(x) L(x) +#else +#define scisprint isprint +#define scstrlen strlen +#define scprintf printf +#define _SC(x) (x) +#endif + +#ifdef _DEBUG +#include + +static const TRexChar *g_nnames[] = +{ + _SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"), + _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"), + _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"), + _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB") +}; + +#endif +#define OP_GREEDY (MAX_CHAR+1) // * + ? {n} +#define OP_OR (MAX_CHAR+2) +#define OP_EXPR (MAX_CHAR+3) //parentesis () +#define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:) +#define OP_DOT (MAX_CHAR+5) +#define OP_CLASS (MAX_CHAR+6) +#define OP_CCLASS (MAX_CHAR+7) +#define OP_NCLASS (MAX_CHAR+8) //negates class the [^ +#define OP_RANGE (MAX_CHAR+9) +#define OP_CHAR (MAX_CHAR+10) +#define OP_EOL (MAX_CHAR+11) +#define OP_BOL (MAX_CHAR+12) +#define OP_WB (MAX_CHAR+13) + +#define TREX_SYMBOL_ANY_CHAR ('.') +#define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+') +#define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*') +#define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?') +#define TREX_SYMBOL_BRANCH ('|') +#define TREX_SYMBOL_END_OF_STRING ('$') +#define TREX_SYMBOL_BEGINNING_OF_STRING ('^') +#define TREX_SYMBOL_ESCAPE_CHAR ('\\') + + +typedef int TRexNodeType; + +typedef struct tagTRexNode{ + TRexNodeType type; + int left; + int right; + int next; +}TRexNode; + +struct TRex{ + const TRexChar *_eol; + const TRexChar *_bol; + const TRexChar *_p; + int _first; + int _op; + TRexNode *_nodes; + int _nallocated; + int _nsize; + int _nsubexpr; + TRexMatch *_matches; + int _currsubexp; + void *_jmpbuf; + const TRexChar **_error; + int _flags; +}; + +static int trex_list(TRex *exp); + +static int trex_newnode(TRex *exp, TRexNodeType type) +{ + TRexNode n; + int newid; + n.type = type; + n.next = n.right = n.left = -1; + if(type == OP_EXPR) + n.right = exp->_nsubexpr++; + if(exp->_nallocated < (exp->_nsize + 1)) { + exp->_nallocated *= 2; + exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode)); + } + exp->_nodes[exp->_nsize++] = n; + newid = exp->_nsize - 1; + return (int)newid; +} + +static void trex_error(TRex *exp,const TRexChar *error) +{ + if(exp->_error) *exp->_error = error; + longjmp(*((jmp_buf*)exp->_jmpbuf),-1); +} + +static void trex_expect(TRex *exp, int n){ + if((*exp->_p) != n) + trex_error(exp, _SC("expected paren")); + exp->_p++; +} + +static TRexChar trex_escapechar(TRex *exp) +{ + if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){ + exp->_p++; + switch(*exp->_p) { + case 'v': exp->_p++; return '\v'; + case 'n': exp->_p++; return '\n'; + case 't': exp->_p++; return '\t'; + case 'r': exp->_p++; return '\r'; + case 'f': exp->_p++; return '\f'; + default: return (*exp->_p++); + } + } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected")); + return (*exp->_p++); +} + +static int trex_charclass(TRex *exp,int classid) +{ + int n = trex_newnode(exp,OP_CCLASS); + exp->_nodes[n].left = classid; + return n; +} + +static int trex_charnode(TRex *exp,TRexBool isclass) +{ + TRexChar t; + if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) { + exp->_p++; + switch(*exp->_p) { + case 'n': exp->_p++; return trex_newnode(exp,'\n'); + case 't': exp->_p++; return trex_newnode(exp,'\t'); + case 'r': exp->_p++; return trex_newnode(exp,'\r'); + case 'f': exp->_p++; return trex_newnode(exp,'\f'); + case 'v': exp->_p++; return trex_newnode(exp,'\v'); + case 'a': case 'A': case 'w': case 'W': case 's': case 'S': + case 'd': case 'D': case 'x': case 'X': case 'c': case 'C': + case 'p': case 'P': case 'l': case 'u': + { + t = *exp->_p; exp->_p++; + return trex_charclass(exp,t); + } + case 'b': + case 'B': + if(!isclass) { + int node = trex_newnode(exp,OP_WB); + exp->_nodes[node].left = *exp->_p; + exp->_p++; + return node; + } //else default + default: + t = *exp->_p; exp->_p++; + return trex_newnode(exp,t); + } + } + else if(!scisprint(*exp->_p)) { + + trex_error(exp,_SC("letter expected")); + } + t = *exp->_p; exp->_p++; + return trex_newnode(exp,t); +} +static int trex_class(TRex *exp) +{ + int ret = -1; + int first = -1,chain; + if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){ + ret = trex_newnode(exp,OP_NCLASS); + exp->_p++; + }else ret = trex_newnode(exp,OP_CLASS); + + if(*exp->_p == ']') trex_error(exp,_SC("empty class")); + chain = ret; + while(*exp->_p != ']' && exp->_p != exp->_eol) { + if(*exp->_p == '-' && first != -1){ + int r,t; + if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range")); + r = trex_newnode(exp,OP_RANGE); + if(first>*exp->_p) trex_error(exp,_SC("invalid range")); + if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges")); + exp->_nodes[r].left = exp->_nodes[first].type; + t = trex_escapechar(exp); + exp->_nodes[r].right = t; + exp->_nodes[chain].next = r; + chain = r; + first = -1; + } + else{ + if(first!=-1){ + int c = first; + exp->_nodes[chain].next = c; + chain = c; + first = trex_charnode(exp,TRex_True); + } + else{ + first = trex_charnode(exp,TRex_True); + } + } + } + if(first!=-1){ + int c = first; + exp->_nodes[chain].next = c; + chain = c; + first = -1; + } + /* hack? */ + exp->_nodes[ret].left = exp->_nodes[ret].next; + exp->_nodes[ret].next = -1; + return ret; +} + +static int trex_parsenumber(TRex *exp) +{ + int ret = *exp->_p-'0'; + int positions = 10; + exp->_p++; + while(isdigit(*exp->_p)) { + ret = ret*10+(*exp->_p++-'0'); + if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant")); + positions *= 10; + }; + return ret; +} + +static int trex_element(TRex *exp) +{ + int ret = -1; + switch(*exp->_p) + { + case '(': { + int expr,newn; + exp->_p++; + + + if(*exp->_p =='?') { + exp->_p++; + trex_expect(exp,':'); + expr = trex_newnode(exp,OP_NOCAPEXPR); + } + else + expr = trex_newnode(exp,OP_EXPR); + newn = trex_list(exp); + exp->_nodes[expr].left = newn; + ret = expr; + trex_expect(exp,')'); + } + break; + case '[': + exp->_p++; + ret = trex_class(exp); + trex_expect(exp,']'); + break; + case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break; + case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break; + default: + ret = trex_charnode(exp,TRex_False); + break; + } + + { + TRexBool isgreedy = TRex_False; + unsigned short p0 = 0, p1 = 0; + switch(*exp->_p){ + case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break; + case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break; + case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break; + case '{': + exp->_p++; + if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected")); + p0 = (unsigned short)trex_parsenumber(exp); + /*******************************/ + switch(*exp->_p) { + case '}': + p1 = p0; exp->_p++; + break; + case ',': + exp->_p++; + p1 = 0xFFFF; + if(isdigit(*exp->_p)){ + p1 = (unsigned short)trex_parsenumber(exp); + } + trex_expect(exp,'}'); + break; + default: + trex_error(exp,_SC(", or } expected")); + } + /*******************************/ + isgreedy = TRex_True; + break; + + } + if(isgreedy) { + int nnode = trex_newnode(exp,OP_GREEDY); + exp->_nodes[nnode].left = ret; + exp->_nodes[nnode].right = ((p0)<<16)|p1; + ret = nnode; + } + } + if((*exp->_p != TREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != TREX_SYMBOL_GREEDY_ZERO_OR_MORE) && (*exp->_p != TREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) { + int nnode = trex_element(exp); + exp->_nodes[ret].next = nnode; + } + + return ret; +} + +static int trex_list(TRex *exp) +{ + int ret=-1,e; + if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) { + exp->_p++; + ret = trex_newnode(exp,OP_BOL); + } + e = trex_element(exp); + if(ret != -1) { + exp->_nodes[ret].next = e; + } + else ret = e; + + if(*exp->_p == TREX_SYMBOL_BRANCH) { + int temp,tright; + exp->_p++; + temp = trex_newnode(exp,OP_OR); + exp->_nodes[temp].left = ret; + tright = trex_list(exp); + exp->_nodes[temp].right = tright; + ret = temp; + } + return ret; +} + +static TRexBool trex_matchcclass(int cclass,TRexChar c) +{ + switch(cclass) { + case 'a': return isalpha(c)?TRex_True:TRex_False; + case 'A': return !isalpha(c)?TRex_True:TRex_False; + case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False; + case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False; + case 's': return ISSPACE(c)?TRex_True:TRex_False; + case 'S': return !ISSPACE(c)?TRex_True:TRex_False; + case 'd': return isdigit(c)?TRex_True:TRex_False; + case 'D': return !isdigit(c)?TRex_True:TRex_False; + case 'x': return isxdigit(c)?TRex_True:TRex_False; + case 'X': return !isxdigit(c)?TRex_True:TRex_False; + case 'c': return iscntrl(c)?TRex_True:TRex_False; + case 'C': return !iscntrl(c)?TRex_True:TRex_False; + case 'p': return ispunct(c)?TRex_True:TRex_False; + case 'P': return !ispunct(c)?TRex_True:TRex_False; + case 'l': return islower(c)?TRex_True:TRex_False; + case 'u': return isupper(c)?TRex_True:TRex_False; + } + return TRex_False; /*cannot happen*/ +} + +static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c) +{ + do { + switch(node->type) { + case OP_RANGE: + if (exp->_flags & TREX_ICASE) + { + if(c >= toupper(node->left) && c <= toupper(node->right)) return TRex_True; + if(c >= tolower(node->left) && c <= tolower(node->right)) return TRex_True; + } + else + { + if(c >= node->left && c <= node->right) return TRex_True; + } + break; + case OP_CCLASS: + if(trex_matchcclass(node->left,c)) return TRex_True; + break; + default: + if (exp->_flags & TREX_ICASE) + { + if (c == tolower(node->type) || c == toupper(node->type)) return TRex_True; + } + else + { + if(c == node->type)return TRex_True; + } + + } + } while((node->next != -1) && (node = &exp->_nodes[node->next])); + return TRex_False; +} + +static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next) +{ + + TRexNodeType type = node->type; + switch(type) { + case OP_GREEDY: { + //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL; + TRexNode *greedystop = NULL; + int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0; + const TRexChar *s=str, *good = str; + + if(node->next != -1) { + greedystop = &exp->_nodes[node->next]; + } + else { + greedystop = next; + } + + while((nmaches == 0xFFFF || nmaches < p1)) { + + const TRexChar *stop; + if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop))) + break; + nmaches++; + good=s; + if(greedystop) { + //checks that 0 matches satisfy the expression(if so skips) + //if not would always stop(for instance if is a '?') + if(greedystop->type != OP_GREEDY || + (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0)) + { + TRexNode *gnext = NULL; + if(greedystop->next != -1) { + gnext = &exp->_nodes[greedystop->next]; + }else if(next && next->next != -1){ + gnext = &exp->_nodes[next->next]; + } + stop = trex_matchnode(exp,greedystop,s,gnext); + if(stop) { + //if satisfied stop it + if(p0 == p1 && p0 == nmaches) break; + else if(nmaches >= p0 && p1 == 0xFFFF) break; + else if(nmaches >= p0 && nmaches <= p1) break; + } + } + } + + if(s >= exp->_eol) + break; + } + if(p0 == p1 && p0 == nmaches) return good; + else if(nmaches >= p0 && p1 == 0xFFFF) return good; + else if(nmaches >= p0 && nmaches <= p1) return good; + return NULL; + } + case OP_OR: { + const TRexChar *asd = str; + TRexNode *temp=&exp->_nodes[node->left]; + while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) { + if(temp->next != -1) + temp = &exp->_nodes[temp->next]; + else + return asd; + } + asd = str; + temp = &exp->_nodes[node->right]; + while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) { + if(temp->next != -1) + temp = &exp->_nodes[temp->next]; + else + return asd; + } + return NULL; + break; + } + case OP_EXPR: + case OP_NOCAPEXPR:{ + TRexNode *n = &exp->_nodes[node->left]; + const TRexChar *cur = str; + int capture = -1; + if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) { + capture = exp->_currsubexp; + exp->_matches[capture].begin = cur; + exp->_currsubexp++; + } + + do { + TRexNode *subnext = NULL; + if(n->next != -1) { + subnext = &exp->_nodes[n->next]; + }else { + subnext = next; + } + if(!(cur = trex_matchnode(exp,n,cur,subnext))) { + if(capture != -1){ + exp->_matches[capture].begin = 0; + exp->_matches[capture].len = 0; + } + return NULL; + } + } while((n->next != -1) && (n = &exp->_nodes[n->next])); + + if(capture != -1) + exp->_matches[capture].len = (int)(cur - exp->_matches[capture].begin); + return cur; + } + case OP_WB: + if((str == exp->_bol && !ISSPACE(*str)) + || ((str == exp->_eol && !ISSPACE(*(str-1)))) + || ((!ISSPACE(*str) && ISSPACE(*(str+1)))) + || ((ISSPACE(*str) && !ISSPACE(*(str+1)))) ) { + return (node->left == 'b')?str:NULL; + } + return (node->left == 'b')?NULL:str; + case OP_BOL: + if(str == exp->_bol) return str; + return NULL; + case OP_EOL: + if(str == exp->_eol) return str; + return NULL; + case OP_DOT: + str++; + return str; + case OP_NCLASS: + case OP_CLASS: + if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) { + str++; + return str; + } + return NULL; + case OP_CCLASS: + if(trex_matchcclass(node->left,*str)) { + str++; + return str; + } + return NULL; + default: /* char */ + if (exp->_flags & TREX_ICASE) + { + if(*str != tolower(node->type) && *str != toupper(node->type)) return NULL; + } + else + { + if (*str != node->type) return NULL; + } + str++; + return str; + } + return NULL; +} + +/* public api */ +TRex *trex_compile(const TRexChar *pattern,const TRexChar **error,int flags) +{ + TRex *exp = (TRex *)malloc(sizeof(TRex)); + exp->_eol = exp->_bol = NULL; + exp->_p = pattern; + exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar); + exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode)); + exp->_nsize = 0; + exp->_matches = 0; + exp->_nsubexpr = 0; + exp->_first = trex_newnode(exp,OP_EXPR); + exp->_error = error; + exp->_jmpbuf = malloc(sizeof(jmp_buf)); + exp->_flags = flags; + if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) { + int res = trex_list(exp); + exp->_nodes[exp->_first].left = res; + if(*exp->_p!='\0') + trex_error(exp,_SC("unexpected character")); +#ifdef _DEBUG + { + int nsize,i; + TRexNode *t; + nsize = exp->_nsize; + t = &exp->_nodes[0]; + scprintf(_SC("\n")); + for(i = 0;i < nsize; i++) { + if(exp->_nodes[i].type>MAX_CHAR) + scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]); + else + scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type); + scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next); + } + scprintf(_SC("\n")); + } +#endif + exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch)); + memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch)); + } + else{ + trex_free(exp); + return NULL; + } + return exp; +} + +void trex_free(TRex *exp) +{ + if(exp) { + if(exp->_nodes) free(exp->_nodes); + if(exp->_jmpbuf) free(exp->_jmpbuf); + if(exp->_matches) free(exp->_matches); + free(exp); + } +} + +TRexBool trex_match(TRex* exp,const TRexChar* text) +{ + const TRexChar* res = NULL; + exp->_bol = text; + exp->_eol = text + scstrlen(text); + exp->_currsubexp = 0; + res = trex_matchnode(exp,exp->_nodes,text,NULL); + if(res == NULL || res != exp->_eol) + return TRex_False; + return TRex_True; +} + +TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end) +{ + const TRexChar *cur = NULL; + int node = exp->_first; + if(text_begin >= text_end) return TRex_False; + exp->_bol = text_begin; + exp->_eol = text_end; + do { + cur = text_begin; + while(node != -1) { + exp->_currsubexp = 0; + cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL); + if(!cur) + break; + node = exp->_nodes[node].next; + } + text_begin++; + } while(cur == NULL && text_begin != text_end); + + if(cur == NULL) + return TRex_False; + + --text_begin; + + if(out_begin) *out_begin = text_begin; + if(out_end) *out_end = cur; + return TRex_True; +} + +TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end) +{ + return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end); +} + +int trex_getsubexpcount(TRex* exp) +{ + return exp->_nsubexpr; +} + +TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp) +{ + if( n<0 || n >= exp->_nsubexpr) return TRex_False; + *subexp = exp->_matches[n]; + return TRex_True; +} +/******************************************************************************* + * This file is part of the argtable3 library. + * + * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of STEWART HEITMANN nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include + +#include "argtable3.h" + + +static void arg_str_resetfn(struct arg_str *parent) +{ + ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); + parent->count = 0; +} + + +static int arg_str_scanfn(struct arg_str *parent, const char *argval) +{ + int errorcode = 0; + + if (parent->count == parent->hdr.maxcount) + { + /* maximum number of arguments exceeded */ + errorcode = EMAXCOUNT; + } + else if (!argval) + { + /* a valid argument with no argument value was given. */ + /* This happens when an optional argument value was invoked. */ + /* leave parent arguiment value unaltered but still count the argument. */ + parent->count++; + } + else + { + parent->sval[parent->count++] = argval; + } + + ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode)); + return errorcode; +} + + +static int arg_str_checkfn(struct arg_str *parent) +{ + int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0; + + ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode)); + return errorcode; +} + + +static void arg_str_errorfn( + struct arg_str *parent, + FILE *fp, + int errorcode, + const char *argval, + const char *progname) +{ + const char *shortopts = parent->hdr.shortopts; + const char *longopts = parent->hdr.longopts; + const char *datatype = parent->hdr.datatype; + + /* make argval NULL safe */ + argval = argval ? argval : ""; + + fprintf(fp, "%s: ", progname); + switch(errorcode) + { + case EMINCOUNT: + fputs("missing option ", fp); + arg_print_option(fp, shortopts, longopts, datatype, "\n"); + break; + + case EMAXCOUNT: + fputs("excess option ", fp); + arg_print_option(fp, shortopts, longopts, argval, "\n"); + break; + } +} + + +struct arg_str * arg_str0( + const char *shortopts, + const char *longopts, + const char *datatype, + const char *glossary) +{ + return arg_strn(shortopts, longopts, datatype, 0, 1, glossary); +} + + +struct arg_str * arg_str1( + const char *shortopts, + const char *longopts, + const char *datatype, + const char *glossary) +{ + return arg_strn(shortopts, longopts, datatype, 1, 1, glossary); +} + + +struct arg_str * arg_strn( + const char *shortopts, + const char *longopts, + const char *datatype, + int mincount, + int maxcount, + const char *glossary) +{ + size_t nbytes; + struct arg_str *result; + + /* should not allow this stupid error */ + /* we should return an error code warning this logic error */ + /* foolproof things by ensuring maxcount is not less than mincount */ + maxcount = (maxcount < mincount) ? mincount : maxcount; + + nbytes = sizeof(struct arg_str) /* storage for struct arg_str */ + + maxcount * sizeof(char *); /* storage for sval[maxcount] array */ + + result = (struct arg_str *)malloc(nbytes); + if (result) + { + int i; + + /* init the arg_hdr struct */ + result->hdr.flag = ARG_HASVALUE; + result->hdr.shortopts = shortopts; + result->hdr.longopts = longopts; + result->hdr.datatype = datatype ? datatype : ""; + result->hdr.glossary = glossary; + result->hdr.mincount = mincount; + result->hdr.maxcount = maxcount; + result->hdr.parent = result; + result->hdr.resetfn = (arg_resetfn *)arg_str_resetfn; + result->hdr.scanfn = (arg_scanfn *)arg_str_scanfn; + result->hdr.checkfn = (arg_checkfn *)arg_str_checkfn; + result->hdr.errorfn = (arg_errorfn *)arg_str_errorfn; + + /* store the sval[maxcount] array immediately after the arg_str struct */ + result->sval = (const char * *)(result + 1); + result->count = 0; + + /* foolproof the string pointers by initialising them to reference empty strings */ + for (i = 0; i < maxcount; i++) + result->sval[i] = ""; + } + + ARG_TRACE(("arg_strn() returns %p\n", result)); + return result; +} +/******************************************************************************* + * This file is part of the argtable3 library. + * + * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of STEWART HEITMANN nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include +#include +#include + +#include "argtable3.h" + +static +void arg_register_error(struct arg_end *end, + void *parent, + int error, + const char *argval) +{ + /* printf("arg_register_error(%p,%p,%d,%s)\n",end,parent,error,argval); */ + if (end->count < end->hdr.maxcount) + { + end->error[end->count] = error; + end->parent[end->count] = parent; + end->argval[end->count] = argval; + end->count++; + } + else + { + end->error[end->hdr.maxcount - 1] = ARG_ELIMIT; + end->parent[end->hdr.maxcount - 1] = end; + end->argval[end->hdr.maxcount - 1] = NULL; + } +} + + +/* + * Return index of first table entry with a matching short option + * or -1 if no match was found. + */ +static +int find_shortoption(struct arg_hdr * *table, char shortopt) +{ + int tabindex; + for(tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) + { + if (table[tabindex]->shortopts && + strchr(table[tabindex]->shortopts, shortopt)) + return tabindex; + } + return -1; +} + + +struct longoptions +{ + int getoptval; + int noptions; + struct option *options; +}; + +#if 0 +static +void dump_longoptions(struct longoptions * longoptions) +{ + int i; + printf("getoptval = %d\n", longoptions->getoptval); + printf("noptions = %d\n", longoptions->noptions); + for (i = 0; i < longoptions->noptions; i++) + { + printf("options[%d].name = \"%s\"\n", + i, + longoptions->options[i].name); + printf("options[%d].has_arg = %d\n", i, longoptions->options[i].has_arg); + printf("options[%d].flag = %p\n", i, longoptions->options[i].flag); + printf("options[%d].val = %d\n", i, longoptions->options[i].val); + } +} +#endif + +static +struct longoptions * alloc_longoptions(struct arg_hdr * *table) +{ + struct longoptions *result; + size_t nbytes; + int noptions = 1; + size_t longoptlen = 0; + int tabindex; + + /* + * Determine the total number of option structs required + * by counting the number of comma separated long options + * in all table entries and return the count in noptions. + * note: noptions starts at 1 not 0 because we getoptlong + * requires a NULL option entry to terminate the option array. + * While we are at it, count the number of chars required + * to store private copies of all the longoption strings + * and return that count in logoptlen. + */ + tabindex = 0; + do + { + const char *longopts = table[tabindex]->longopts; + longoptlen += (longopts ? strlen(longopts) : 0) + 1; + while (longopts) + { + noptions++; + longopts = strchr(longopts + 1, ','); + } + } while(!(table[tabindex++]->flag & ARG_TERMINATOR)); + /*printf("%d long options consuming %d chars in total\n",noptions,longoptlen);*/ + + + /* allocate storage for return data structure as: */ + /* (struct longoptions) + (struct options)[noptions] + char[longoptlen] */ + nbytes = sizeof(struct longoptions) + + sizeof(struct option) * noptions + + longoptlen; + result = (struct longoptions *)malloc(nbytes); + if (result) + { + int option_index = 0; + char *store; + + result->getoptval = 0; + result->noptions = noptions; + result->options = (struct option *)(result + 1); + store = (char *)(result->options + noptions); + + for(tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) + { + const char *longopts = table[tabindex]->longopts; + + while(longopts && *longopts) + { + char *storestart = store; + + /* copy progressive longopt strings into the store */ + while (*longopts != 0 && *longopts != ',') + *store++ = *longopts++; + *store++ = 0; + if (*longopts == ',') + longopts++; + /*fprintf(stderr,"storestart=\"%s\"\n",storestart);*/ + + result->options[option_index].name = storestart; + result->options[option_index].flag = &(result->getoptval); + result->options[option_index].val = tabindex; + if (table[tabindex]->flag & ARG_HASOPTVALUE) + result->options[option_index].has_arg = 2; + else if (table[tabindex]->flag & ARG_HASVALUE) + result->options[option_index].has_arg = 1; + else + result->options[option_index].has_arg = 0; + + option_index++; + } + } + /* terminate the options array with a zero-filled entry */ + result->options[option_index].name = 0; + result->options[option_index].has_arg = 0; + result->options[option_index].flag = 0; + result->options[option_index].val = 0; + } + + /*dump_longoptions(result);*/ + return result; +} + +static +char * alloc_shortoptions(struct arg_hdr * *table) +{ + char *result; + size_t len = 2; + int tabindex; + + /* determine the total number of option chars required */ + for(tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) + { + struct arg_hdr *hdr = table[tabindex]; + len += 3 * (hdr->shortopts ? strlen(hdr->shortopts) : 0); + } + + result = malloc(len); + if (result) + { + char *res = result; + + /* add a leading ':' so getopt return codes distinguish */ + /* unrecognised option and options missing argument values */ + *res++ = ':'; + + for(tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) + { + struct arg_hdr *hdr = table[tabindex]; + const char *shortopts = hdr->shortopts; + while(shortopts && *shortopts) + { + *res++ = *shortopts++; + if (hdr->flag & ARG_HASVALUE) + *res++ = ':'; + if (hdr->flag & ARG_HASOPTVALUE) + *res++ = ':'; + } + } + /* null terminate the string */ + *res = 0; + } + + /*printf("alloc_shortoptions() returns \"%s\"\n",(result?result:"NULL"));*/ + return result; +} + + +/* return index of the table terminator entry */ +static +int arg_endindex(struct arg_hdr * *table) +{ + int tabindex = 0; + while (!(table[tabindex]->flag & ARG_TERMINATOR)) + tabindex++; + return tabindex; +} + + +static +void arg_parse_tagged(int argc, + char * *argv, + struct arg_hdr * *table, + struct arg_end *endtable) +{ + struct longoptions *longoptions; + char *shortoptions; + int copt; + + /*printf("arg_parse_tagged(%d,%p,%p,%p)\n",argc,argv,table,endtable);*/ + + /* allocate short and long option arrays for the given opttable[]. */ + /* if the allocs fail then put an error msg in the last table entry. */ + longoptions = alloc_longoptions(table); + shortoptions = alloc_shortoptions(table); + if (!longoptions || !shortoptions) + { + /* one or both memory allocs failed */ + arg_register_error(endtable, endtable, ARG_EMALLOC, NULL); + /* free anything that was allocated (this is null safe) */ + free(shortoptions); + free(longoptions); + return; + } + + /*dump_longoptions(longoptions);*/ + + /* reset getopts internal option-index to zero, and disable error reporting */ + optind = 0; + opterr = 0; + + /* fetch and process args using getopt_long */ + while( (copt = + getopt_long(argc, argv, shortoptions, longoptions->options, + NULL)) != -1) + { + /* + printf("optarg='%s'\n",optarg); + printf("optind=%d\n",optind); + printf("copt=%c\n",(char)copt); + printf("optopt=%c (%d)\n",optopt, (int)(optopt)); + */ + switch(copt) + { + case 0: + { + int tabindex = longoptions->getoptval; + void *parent = table[tabindex]->parent; + /*printf("long option detected from argtable[%d]\n", tabindex);*/ + if (optarg && optarg[0] == 0 && + (table[tabindex]->flag & ARG_HASVALUE)) + { + /* printf(": long option %s requires an argument\n",argv[optind-1]); */ + arg_register_error(endtable, endtable, ARG_EMISSARG, + argv[optind - 1]); + /* continue to scan the (empty) argument value to enforce argument count checking */ + } + if (table[tabindex]->scanfn) + { + int errorcode = table[tabindex]->scanfn(parent, optarg); + if (errorcode != 0) + arg_register_error(endtable, parent, errorcode, optarg); + } + } + break; + + case '?': + /* + * getopt_long() found an unrecognised short option. + * if it was a short option its value is in optopt + * if it was a long option then optopt=0 + */ + switch (optopt) + { + case 0: + /*printf("?0 unrecognised long option %s\n",argv[optind-1]);*/ + arg_register_error(endtable, endtable, ARG_ELONGOPT, + argv[optind - 1]); + break; + default: + /*printf("?* unrecognised short option '%c'\n",optopt);*/ + arg_register_error(endtable, endtable, optopt, NULL); + break; + } + break; + + case ':': + /* + * getopt_long() found an option with its argument missing. + */ + /*printf(": option %s requires an argument\n",argv[optind-1]); */ + arg_register_error(endtable, endtable, ARG_EMISSARG, + argv[optind - 1]); + break; + + default: + { + /* getopt_long() found a valid short option */ + int tabindex = find_shortoption(table, (char)copt); + /*printf("short option detected from argtable[%d]\n", tabindex);*/ + if (tabindex == -1) + { + /* should never get here - but handle it just in case */ + /*printf("unrecognised short option %d\n",copt);*/ + arg_register_error(endtable, endtable, copt, NULL); + } + else + { + if (table[tabindex]->scanfn) + { + void *parent = table[tabindex]->parent; + int errorcode = table[tabindex]->scanfn(parent, optarg); + if (errorcode != 0) + arg_register_error(endtable, parent, errorcode, optarg); + } + } + break; + } + } + } + + free(shortoptions); + free(longoptions); +} + + +static +void arg_parse_untagged(int argc, + char * *argv, + struct arg_hdr * *table, + struct arg_end *endtable) +{ + int tabindex = 0; + int errorlast = 0; + const char *optarglast = NULL; + void *parentlast = NULL; + + /*printf("arg_parse_untagged(%d,%p,%p,%p)\n",argc,argv,table,endtable);*/ + while (!(table[tabindex]->flag & ARG_TERMINATOR)) + { + void *parent; + int errorcode; + + /* if we have exhausted our argv[optind] entries then we have finished */ + if (optind >= argc) + { + /*printf("arg_parse_untagged(): argv[] exhausted\n");*/ + return; + } + + /* skip table entries with non-null long or short options (they are not untagged entries) */ + if (table[tabindex]->longopts || table[tabindex]->shortopts) + { + /*printf("arg_parse_untagged(): skipping argtable[%d] (tagged argument)\n",tabindex);*/ + tabindex++; + continue; + } + + /* skip table entries with NULL scanfn */ + if (!(table[tabindex]->scanfn)) + { + /*printf("arg_parse_untagged(): skipping argtable[%d] (NULL scanfn)\n",tabindex);*/ + tabindex++; + continue; + } + + /* attempt to scan the current argv[optind] with the current */ + /* table[tabindex] entry. If it succeeds then keep it, otherwise */ + /* try again with the next table[] entry. */ + parent = table[tabindex]->parent; + errorcode = table[tabindex]->scanfn(parent, argv[optind]); + if (errorcode == 0) + { + /* success, move onto next argv[optind] but stay with same table[tabindex] */ + /*printf("arg_parse_untagged(): argtable[%d] successfully matched\n",tabindex);*/ + optind++; + + /* clear the last tentative error */ + errorlast = 0; + } + else + { + /* failure, try same argv[optind] with next table[tabindex] entry */ + /*printf("arg_parse_untagged(): argtable[%d] failed match\n",tabindex);*/ + tabindex++; + + /* remember this as a tentative error we may wish to reinstate later */ + errorlast = errorcode; + optarglast = argv[optind]; + parentlast = parent; + } + + } + + /* if a tenative error still remains at this point then register it as a proper error */ + if (errorlast) + { + arg_register_error(endtable, parentlast, errorlast, optarglast); + optind++; + } + + /* only get here when not all argv[] entries were consumed */ + /* register an error for each unused argv[] entry */ + while (optind < argc) + { + /*printf("arg_parse_untagged(): argv[%d]=\"%s\" not consumed\n",optind,argv[optind]);*/ + arg_register_error(endtable, endtable, ARG_ENOMATCH, argv[optind++]); + } + + return; +} + + +static +void arg_parse_check(struct arg_hdr * *table, struct arg_end *endtable) +{ + int tabindex = 0; + /* printf("arg_parse_check()\n"); */ + do + { + if (table[tabindex]->checkfn) + { + void *parent = table[tabindex]->parent; + int errorcode = table[tabindex]->checkfn(parent); + if (errorcode != 0) + arg_register_error(endtable, parent, errorcode, NULL); + } + } while(!(table[tabindex++]->flag & ARG_TERMINATOR)); +} + + +static +void arg_reset(void * *argtable) +{ + struct arg_hdr * *table = (struct arg_hdr * *)argtable; + int tabindex = 0; + /*printf("arg_reset(%p)\n",argtable);*/ + do + { + if (table[tabindex]->resetfn) + table[tabindex]->resetfn(table[tabindex]->parent); + } while(!(table[tabindex++]->flag & ARG_TERMINATOR)); +} + + +int arg_parse(int argc, char * *argv, void * *argtable) +{ + struct arg_hdr * *table = (struct arg_hdr * *)argtable; + struct arg_end *endtable; + int endindex; + char * *argvcopy = NULL; + + /*printf("arg_parse(%d,%p,%p)\n",argc,argv,argtable);*/ + + /* reset any argtable data from previous invocations */ + arg_reset(argtable); + + /* locate the first end-of-table marker within the array */ + endindex = arg_endindex(table); + endtable = (struct arg_end *)table[endindex]; + + /* Special case of argc==0. This can occur on Texas Instruments DSP. */ + /* Failure to trap this case results in an unwanted NULL result from */ + /* the malloc for argvcopy (next code block). */ + if (argc == 0) + { + /* We must still perform post-parse checks despite the absence of command line arguments */ + arg_parse_check(table, endtable); + + /* Now we are finished */ + return endtable->count; + } + + argvcopy = (char **)malloc(sizeof(char *) * (argc + 1)); + if (argvcopy) + { + int i; + + /* + Fill in the local copy of argv[]. We need a local copy + because getopt rearranges argv[] which adversely affects + susbsequent parsing attempts. + */ + for (i = 0; i < argc; i++) + argvcopy[i] = argv[i]; + + argvcopy[argc] = NULL; + + /* parse the command line (local copy) for tagged options */ + arg_parse_tagged(argc, argvcopy, table, endtable); + + /* parse the command line (local copy) for untagged options */ + arg_parse_untagged(argc, argvcopy, table, endtable); + + /* if no errors so far then perform post-parse checks otherwise dont bother */ + if (endtable->count == 0) + arg_parse_check(table, endtable); + + /* release the local copt of argv[] */ + free(argvcopy); + } + else + { + /* memory alloc failed */ + arg_register_error(endtable, endtable, ARG_EMALLOC, NULL); + } + + return endtable->count; +} + + +/* + * Concatenate contents of src[] string onto *pdest[] string. + * The *pdest pointer is altered to point to the end of the + * target string and *pndest is decremented by the same number + * of chars. + * Does not append more than *pndest chars into *pdest[] + * so as to prevent buffer overruns. + * Its something like strncat() but more efficient for repeated + * calls on the same destination string. + * Example of use: + * char dest[30] = "good" + * size_t ndest = sizeof(dest); + * char *pdest = dest; + * arg_char(&pdest,"bye ",&ndest); + * arg_char(&pdest,"cruel ",&ndest); + * arg_char(&pdest,"world!",&ndest); + * Results in: + * dest[] == "goodbye cruel world!" + * ndest == 10 + */ +static +void arg_cat(char * *pdest, const char *src, size_t *pndest) +{ + char *dest = *pdest; + char *end = dest + *pndest; + + /*locate null terminator of dest string */ + while(dest < end && *dest != 0) + dest++; + + /* concat src string to dest string */ + while(dest < end && *src != 0) + *dest++ = *src++; + + /* null terminate dest string */ + *dest = 0; + + /* update *pdest and *pndest */ + *pndest = end - dest; + *pdest = dest; +} + + +static +void arg_cat_option(char *dest, + size_t ndest, + const char *shortopts, + const char *longopts, + const char *datatype, + int optvalue) +{ + if (shortopts) + { + char option[3]; + + /* note: option array[] is initialiazed dynamically here to satisfy */ + /* a deficiency in the watcom compiler wrt static array initializers. */ + option[0] = '-'; + option[1] = shortopts[0]; + option[2] = 0; + + arg_cat(&dest, option, &ndest); + if (datatype) + { + arg_cat(&dest, " ", &ndest); + if (optvalue) + { + arg_cat(&dest, "[", &ndest); + arg_cat(&dest, datatype, &ndest); + arg_cat(&dest, "]", &ndest); + } + else + arg_cat(&dest, datatype, &ndest); + } + } + else if (longopts) + { + size_t ncspn; + + /* add "--" tag prefix */ + arg_cat(&dest, "--", &ndest); + + /* add comma separated option tag */ + ncspn = strcspn(longopts, ","); +#ifdef __STDC_WANT_SECURE_LIB__ + strncat_s(dest, ndest, longopts, (ncspn < ndest) ? ncspn : ndest); +#else + strncat(dest, longopts, (ncspn < ndest) ? ncspn : ndest); +#endif + + if (datatype) + { + arg_cat(&dest, "=", &ndest); + if (optvalue) + { + arg_cat(&dest, "[", &ndest); + arg_cat(&dest, datatype, &ndest); + arg_cat(&dest, "]", &ndest); + } + else + arg_cat(&dest, datatype, &ndest); + } + } + else if (datatype) + { + if (optvalue) + { + arg_cat(&dest, "[", &ndest); + arg_cat(&dest, datatype, &ndest); + arg_cat(&dest, "]", &ndest); + } + else + arg_cat(&dest, datatype, &ndest); + } +} + +static +void arg_cat_optionv(char *dest, + size_t ndest, + const char *shortopts, + const char *longopts, + const char *datatype, + int optvalue, + const char *separator) +{ + separator = separator ? separator : ""; + + if (shortopts) + { + const char *c = shortopts; + while(*c) + { + /* "-a|-b|-c" */ + char shortopt[3]; + + /* note: shortopt array[] is initialiazed dynamically here to satisfy */ + /* a deficiency in the watcom compiler wrt static array initializers. */ + shortopt[0] = '-'; + shortopt[1] = *c; + shortopt[2] = 0; + + arg_cat(&dest, shortopt, &ndest); + if (*++c) + arg_cat(&dest, separator, &ndest); + } + } + + /* put separator between long opts and short opts */ + if (shortopts && longopts) + arg_cat(&dest, separator, &ndest); + + if (longopts) + { + const char *c = longopts; + while(*c) + { + size_t ncspn; + + /* add "--" tag prefix */ + arg_cat(&dest, "--", &ndest); + + /* add comma separated option tag */ + ncspn = strcspn(c, ","); +#ifdef __STDC_WANT_SECURE_LIB__ + strncat_s(dest, ndest, c, (ncspn < ndest) ? ncspn : ndest); +#else + strncat(dest, c, (ncspn < ndest) ? ncspn : ndest); +#endif + c += ncspn; + + /* add given separator in place of comma */ + if (*c == ',') + { + arg_cat(&dest, separator, &ndest); + c++; + } + } + } + + if (datatype) + { + if (longopts) + arg_cat(&dest, "=", &ndest); + else if (shortopts) + arg_cat(&dest, " ", &ndest); + + if (optvalue) + { + arg_cat(&dest, "[", &ndest); + arg_cat(&dest, datatype, &ndest); + arg_cat(&dest, "]", &ndest); + } + else + arg_cat(&dest, datatype, &ndest); + } +} + + +/* this function should be deprecated because it doesnt consider optional argument values (ARG_HASOPTVALUE) */ +void arg_print_option(FILE *fp, + const char *shortopts, + const char *longopts, + const char *datatype, + const char *suffix) +{ + char syntax[200] = ""; + suffix = suffix ? suffix : ""; + + /* there is no way of passing the proper optvalue for optional argument values here, so we must ignore it */ + arg_cat_optionv(syntax, + sizeof(syntax), + shortopts, + longopts, + datatype, + 0, + "|"); + + fputs(syntax, fp); + fputs(suffix, fp); +} + + +/* + * Print a GNU style [OPTION] string in which all short options that + * do not take argument values are presented in abbreviated form, as + * in: -xvfsd, or -xvf[sd], or [-xvsfd] + */ +static +void arg_print_gnuswitch(FILE *fp, struct arg_hdr * *table) +{ + int tabindex; + char *format1 = " -%c"; + char *format2 = " [-%c"; + char *suffix = ""; + + /* print all mandatory switches that are without argument values */ + for(tabindex = 0; + table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR); + tabindex++) + { + /* skip optional options */ + if (table[tabindex]->mincount < 1) + continue; + + /* skip non-short options */ + if (table[tabindex]->shortopts == NULL) + continue; + + /* skip options that take argument values */ + if (table[tabindex]->flag & ARG_HASVALUE) + continue; + + /* print the short option (only the first short option char, ignore multiple choices)*/ + fprintf(fp, format1, table[tabindex]->shortopts[0]); + format1 = "%c"; + format2 = "[%c"; + } + + /* print all optional switches that are without argument values */ + for(tabindex = 0; + table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR); + tabindex++) + { + /* skip mandatory args */ + if (table[tabindex]->mincount > 0) + continue; + + /* skip args without short options */ + if (table[tabindex]->shortopts == NULL) + continue; + + /* skip args with values */ + if (table[tabindex]->flag & ARG_HASVALUE) + continue; + + /* print first short option */ + fprintf(fp, format2, table[tabindex]->shortopts[0]); + format2 = "%c"; + suffix = "]"; + } + + fprintf(fp, "%s", suffix); +} + + +void arg_print_syntax(FILE *fp, void * *argtable, const char *suffix) +{ + struct arg_hdr * *table = (struct arg_hdr * *)argtable; + int i, tabindex; + + /* print GNU style [OPTION] string */ + arg_print_gnuswitch(fp, table); + + /* print remaining options in abbreviated style */ + for(tabindex = 0; + table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR); + tabindex++) + { + char syntax[200] = ""; + const char *shortopts, *longopts, *datatype; + + /* skip short options without arg values (they were printed by arg_print_gnu_switch) */ + if (table[tabindex]->shortopts && + !(table[tabindex]->flag & ARG_HASVALUE)) + continue; + + shortopts = table[tabindex]->shortopts; + longopts = table[tabindex]->longopts; + datatype = table[tabindex]->datatype; + arg_cat_option(syntax, + sizeof(syntax), + shortopts, + longopts, + datatype, + table[tabindex]->flag & ARG_HASOPTVALUE); + + if (strlen(syntax) > 0) + { + /* print mandatory instances of this option */ + for (i = 0; i < table[tabindex]->mincount; i++) + fprintf(fp, " %s", syntax); + + /* print optional instances enclosed in "[..]" */ + switch ( table[tabindex]->maxcount - table[tabindex]->mincount ) + { + case 0: + break; + case 1: + fprintf(fp, " [%s]", syntax); + break; + case 2: + fprintf(fp, " [%s] [%s]", syntax, syntax); + break; + default: + fprintf(fp, " [%s]...", syntax); + break; + } + } + } + + if (suffix) + fprintf(fp, "%s", suffix); +} + + +void arg_print_syntaxv(FILE *fp, void * *argtable, const char *suffix) +{ + struct arg_hdr * *table = (struct arg_hdr * *)argtable; + int i, tabindex; + + /* print remaining options in abbreviated style */ + for(tabindex = 0; + table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR); + tabindex++) + { + char syntax[200] = ""; + const char *shortopts, *longopts, *datatype; + + shortopts = table[tabindex]->shortopts; + longopts = table[tabindex]->longopts; + datatype = table[tabindex]->datatype; + arg_cat_optionv(syntax, + sizeof(syntax), + shortopts, + longopts, + datatype, + table[tabindex]->flag & ARG_HASOPTVALUE, + "|"); + + /* print mandatory options */ + for (i = 0; i < table[tabindex]->mincount; i++) + fprintf(fp, " %s", syntax); + + /* print optional args enclosed in "[..]" */ + switch ( table[tabindex]->maxcount - table[tabindex]->mincount ) + { + case 0: + break; + case 1: + fprintf(fp, " [%s]", syntax); + break; + case 2: + fprintf(fp, " [%s] [%s]", syntax, syntax); + break; + default: + fprintf(fp, " [%s]...", syntax); + break; + } + } + + if (suffix) + fprintf(fp, "%s", suffix); +} + + +void arg_print_glossary(FILE *fp, void * *argtable, const char *format) +{ + struct arg_hdr * *table = (struct arg_hdr * *)argtable; + int tabindex; + + format = format ? format : " %-20s %s\n"; + for (tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) + { + if (table[tabindex]->glossary) + { + char syntax[200] = ""; + const char *shortopts = table[tabindex]->shortopts; + const char *longopts = table[tabindex]->longopts; + const char *datatype = table[tabindex]->datatype; + const char *glossary = table[tabindex]->glossary; + arg_cat_optionv(syntax, + sizeof(syntax), + shortopts, + longopts, + datatype, + table[tabindex]->flag & ARG_HASOPTVALUE, + ", "); + fprintf(fp, format, syntax, glossary); + } + } +} + + +/** + * Print a piece of text formatted, which means in a column with a + * left and a right margin. The lines are wrapped at whitspaces next + * to right margin. The function does not indent the first line, but + * only the following ones. + * + * Example: + * arg_print_formatted( fp, 0, 5, "Some text that doesn't fit." ) + * will result in the following output: + * + * Some + * text + * that + * doesn' + * t fit. + * + * Too long lines will be wrapped in the middle of a word. + * + * arg_print_formatted( fp, 2, 7, "Some text that doesn't fit." ) + * will result in the following output: + * + * Some + * text + * that + * doesn' + * t fit. + * + * As you see, the first line is not indented. This enables output of + * lines, which start in a line where output already happened. + * + * Author: Uli Fouquet + */ +static +void arg_print_formatted( FILE *fp, + const unsigned lmargin, + const unsigned rmargin, + const char *text ) +{ + const unsigned textlen = (unsigned)strlen( text ); + unsigned line_start = 0; + unsigned line_end = textlen + 1; + const unsigned colwidth = (rmargin - lmargin) + 1; + + /* Someone doesn't like us... */ + if ( line_end < line_start ) + { fprintf( fp, "%s\n", text ); } + + while (line_end - 1 > line_start ) + { + /* Eat leading whitespaces. This is essential because while + wrapping lines, there will often be a whitespace at beginning + of line */ + while ( ISSPACE(*(text + line_start)) ) + { line_start++; } + + if ((line_end - line_start) > colwidth ) + { line_end = line_start + colwidth; } + + /* Find last whitespace, that fits into line */ + while ( ( line_end > line_start ) + && ( line_end - line_start > colwidth ) + && !ISSPACE(*(text + line_end))) + { line_end--; } + + /* Do not print trailing whitespace. If this text + has got only one line, line_end now points to the + last char due to initialization. */ + line_end--; + + /* Output line of text */ + while ( line_start < line_end ) + { + fputc(*(text + line_start), fp ); + line_start++; + } + fputc( '\n', fp ); + + /* Initialize another line */ + if ( line_end + 1 < textlen ) + { + unsigned i; + + for (i = 0; i < lmargin; i++ ) + { fputc( ' ', fp ); } + + line_end = textlen; + } + + /* If we have to print another line, get also the last char. */ + line_end++; + + } /* lines of text */ +} + +/** + * Prints the glossary in strict GNU format. + * Differences to arg_print_glossary() are: + * - wraps lines after 80 chars + * - indents lines without shortops + * - does not accept formatstrings + * + * Contributed by Uli Fouquet + */ +void arg_print_glossary_gnu(FILE *fp, void * *argtable ) +{ + struct arg_hdr * *table = (struct arg_hdr * *)argtable; + int tabindex; + + for(tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) + { + if (table[tabindex]->glossary) + { + char syntax[200] = ""; + const char *shortopts = table[tabindex]->shortopts; + const char *longopts = table[tabindex]->longopts; + const char *datatype = table[tabindex]->datatype; + const char *glossary = table[tabindex]->glossary; + + if ( !shortopts && longopts ) + { + /* Indent trailing line by 4 spaces... */ + memset( syntax, ' ', 4 ); + *(syntax + 4) = '\0'; + } + + arg_cat_optionv(syntax, + sizeof(syntax), + shortopts, + longopts, + datatype, + table[tabindex]->flag & ARG_HASOPTVALUE, + ", "); + + /* If syntax fits not into column, print glossary in new line... */ + if ( strlen(syntax) > 25 ) + { + fprintf( fp, " %-25s %s\n", syntax, "" ); + *syntax = '\0'; + } + + fprintf( fp, " %-25s ", syntax ); + arg_print_formatted( fp, 28, 79, glossary ); + } + } /* for each table entry */ + + fputc( '\n', fp ); +} + + +/** + * Checks the argtable[] array for NULL entries and returns 1 + * if any are found, zero otherwise. + */ +int arg_nullcheck(void * *argtable) +{ + struct arg_hdr * *table = (struct arg_hdr * *)argtable; + int tabindex; + /*printf("arg_nullcheck(%p)\n",argtable);*/ + + if (!table) + return 1; + + tabindex = 0; + do + { + /*printf("argtable[%d]=%p\n",tabindex,argtable[tabindex]);*/ + if (!table[tabindex]) + return 1; + } while(!(table[tabindex++]->flag & ARG_TERMINATOR)); + + return 0; +} + + +/* + * arg_free() is deprecated in favour of arg_freetable() due to a flaw in its design. + * The flaw results in memory leak in the (very rare) case that an intermediate + * entry in the argtable array failed its memory allocation while others following + * that entry were still allocated ok. Those subsequent allocations will not be + * deallocated by arg_free(). + * Despite the unlikeliness of the problem occurring, and the even unlikelier event + * that it has any deliterious effect, it is fixed regardless by replacing arg_free() + * with the newer arg_freetable() function. + * We still keep arg_free() for backwards compatibility. + */ +void arg_free(void * *argtable) +{ + struct arg_hdr * *table = (struct arg_hdr * *)argtable; + int tabindex = 0; + int flag; + /*printf("arg_free(%p)\n",argtable);*/ + do + { + /* + if we encounter a NULL entry then somewhat incorrectly we presume + we have come to the end of the array. It isnt strictly true because + an intermediate entry could be NULL with other non-NULL entries to follow. + The subsequent argtable entries would then not be freed as they should. + */ + if (table[tabindex] == NULL) + break; + + flag = table[tabindex]->flag; + free(table[tabindex]); + table[tabindex++] = NULL; + + } while(!(flag & ARG_TERMINATOR)); +} + +/* frees each non-NULL element of argtable[], where n is the size of the number of entries in the array */ +void arg_freetable(void * *argtable, size_t n) +{ + struct arg_hdr * *table = (struct arg_hdr * *)argtable; + size_t tabindex = 0; + /*printf("arg_freetable(%p)\n",argtable);*/ + for (tabindex = 0; tabindex < n; tabindex++) + { + if (table[tabindex] == NULL) + continue; + + free(table[tabindex]); + table[tabindex] = NULL; + }; +} + diff --git a/client/cliparser/argtable3.h b/client/cliparser/argtable3.h new file mode 100644 index 00000000..1107de25 --- /dev/null +++ b/client/cliparser/argtable3.h @@ -0,0 +1,305 @@ +/******************************************************************************* + * This file is part of the argtable3 library. + * + * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of STEWART HEITMANN nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#ifndef ARGTABLE3 +#define ARGTABLE3 + +#include /* FILE */ +#include /* struct tm */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define ARG_REX_ICASE 1 + +/* bit masks for arg_hdr.flag */ +enum +{ + ARG_TERMINATOR=0x1, + ARG_HASVALUE=0x2, + ARG_HASOPTVALUE=0x4 +}; + +typedef void (arg_resetfn)(void *parent); +typedef int (arg_scanfn)(void *parent, const char *argval); +typedef int (arg_checkfn)(void *parent); +typedef void (arg_errorfn)(void *parent, FILE *fp, int error, const char *argval, const char *progname); + + +/* +* The arg_hdr struct defines properties that are common to all arg_xxx structs. +* The argtable library requires each arg_xxx struct to have an arg_hdr +* struct as its first data member. +* The argtable library functions then use this data to identify the +* properties of the command line option, such as its option tags, +* datatype string, and glossary strings, and so on. +* Moreover, the arg_hdr struct contains pointers to custom functions that +* are provided by each arg_xxx struct which perform the tasks of parsing +* that particular arg_xxx arguments, performing post-parse checks, and +* reporting errors. +* These functions are private to the individual arg_xxx source code +* and are the pointer to them are initiliased by that arg_xxx struct's +* constructor function. The user could alter them after construction +* if desired, but the original intention is for them to be set by the +* constructor and left unaltered. +*/ +struct arg_hdr +{ + char flag; /* Modifier flags: ARG_TERMINATOR, ARG_HASVALUE. */ + const char *shortopts; /* String defining the short options */ + const char *longopts; /* String defiing the long options */ + const char *datatype; /* Description of the argument data type */ + const char *glossary; /* Description of the option as shown by arg_print_glossary function */ + int mincount; /* Minimum number of occurences of this option accepted */ + int maxcount; /* Maximum number of occurences if this option accepted */ + void *parent; /* Pointer to parent arg_xxx struct */ + arg_resetfn *resetfn; /* Pointer to parent arg_xxx reset function */ + arg_scanfn *scanfn; /* Pointer to parent arg_xxx scan function */ + arg_checkfn *checkfn; /* Pointer to parent arg_xxx check function */ + arg_errorfn *errorfn; /* Pointer to parent arg_xxx error function */ + void *priv; /* Pointer to private header data for use by arg_xxx functions */ +}; + +struct arg_rem +{ + struct arg_hdr hdr; /* The mandatory argtable header struct */ +}; + +struct arg_lit +{ + struct arg_hdr hdr; /* The mandatory argtable header struct */ + int count; /* Number of matching command line args */ +}; + +struct arg_int +{ + struct arg_hdr hdr; /* The mandatory argtable header struct */ + int count; /* Number of matching command line args */ + int *ival; /* Array of parsed argument values */ +}; + +struct arg_dbl +{ + struct arg_hdr hdr; /* The mandatory argtable header struct */ + int count; /* Number of matching command line args */ + double *dval; /* Array of parsed argument values */ +}; + +struct arg_str +{ + struct arg_hdr hdr; /* The mandatory argtable header struct */ + int count; /* Number of matching command line args */ + const char **sval; /* Array of parsed argument values */ +}; + +struct arg_rex +{ + struct arg_hdr hdr; /* The mandatory argtable header struct */ + int count; /* Number of matching command line args */ + const char **sval; /* Array of parsed argument values */ +}; + +struct arg_file +{ + struct arg_hdr hdr; /* The mandatory argtable header struct */ + int count; /* Number of matching command line args*/ + const char **filename; /* Array of parsed filenames (eg: /home/foo.bar) */ + const char **basename; /* Array of parsed basenames (eg: foo.bar) */ + const char **extension; /* Array of parsed extensions (eg: .bar) */ +}; + +struct arg_date +{ + struct arg_hdr hdr; /* The mandatory argtable header struct */ + const char *format; /* strptime format string used to parse the date */ + int count; /* Number of matching command line args */ + struct tm *tmval; /* Array of parsed time values */ +}; + +enum {ARG_ELIMIT=1, ARG_EMALLOC, ARG_ENOMATCH, ARG_ELONGOPT, ARG_EMISSARG}; +struct arg_end +{ + struct arg_hdr hdr; /* The mandatory argtable header struct */ + int count; /* Number of errors encountered */ + int *error; /* Array of error codes */ + void **parent; /* Array of pointers to offending arg_xxx struct */ + const char **argval; /* Array of pointers to offending argv[] string */ +}; + + +/**** arg_xxx constructor functions *********************************/ + +struct arg_rem* arg_rem(const char* datatype, const char* glossary); + +struct arg_lit* arg_lit0(const char* shortopts, + const char* longopts, + const char* glossary); +struct arg_lit* arg_lit1(const char* shortopts, + const char* longopts, + const char *glossary); +struct arg_lit* arg_litn(const char* shortopts, + const char* longopts, + int mincount, + int maxcount, + const char *glossary); + +struct arg_key* arg_key0(const char* keyword, + int flags, + const char* glossary); +struct arg_key* arg_key1(const char* keyword, + int flags, + const char* glossary); +struct arg_key* arg_keyn(const char* keyword, + int flags, + int mincount, + int maxcount, + const char* glossary); + +struct arg_int* arg_int0(const char* shortopts, + const char* longopts, + const char* datatype, + const char* glossary); +struct arg_int* arg_int1(const char* shortopts, + const char* longopts, + const char* datatype, + const char *glossary); +struct arg_int* arg_intn(const char* shortopts, + const char* longopts, + const char *datatype, + int mincount, + int maxcount, + const char *glossary); + +struct arg_dbl* arg_dbl0(const char* shortopts, + const char* longopts, + const char* datatype, + const char* glossary); +struct arg_dbl* arg_dbl1(const char* shortopts, + const char* longopts, + const char* datatype, + const char *glossary); +struct arg_dbl* arg_dbln(const char* shortopts, + const char* longopts, + const char *datatype, + int mincount, + int maxcount, + const char *glossary); + +struct arg_str* arg_str0(const char* shortopts, + const char* longopts, + const char* datatype, + const char* glossary); +struct arg_str* arg_str1(const char* shortopts, + const char* longopts, + const char* datatype, + const char *glossary); +struct arg_str* arg_strn(const char* shortopts, + const char* longopts, + const char* datatype, + int mincount, + int maxcount, + const char *glossary); + +struct arg_rex* arg_rex0(const char* shortopts, + const char* longopts, + const char* pattern, + const char* datatype, + int flags, + const char* glossary); +struct arg_rex* arg_rex1(const char* shortopts, + const char* longopts, + const char* pattern, + const char* datatype, + int flags, + const char *glossary); +struct arg_rex* arg_rexn(const char* shortopts, + const char* longopts, + const char* pattern, + const char* datatype, + int mincount, + int maxcount, + int flags, + const char *glossary); + +struct arg_file* arg_file0(const char* shortopts, + const char* longopts, + const char* datatype, + const char* glossary); +struct arg_file* arg_file1(const char* shortopts, + const char* longopts, + const char* datatype, + const char *glossary); +struct arg_file* arg_filen(const char* shortopts, + const char* longopts, + const char* datatype, + int mincount, + int maxcount, + const char *glossary); + +struct arg_date* arg_date0(const char* shortopts, + const char* longopts, + const char* format, + const char* datatype, + const char* glossary); +struct arg_date* arg_date1(const char* shortopts, + const char* longopts, + const char* format, + const char* datatype, + const char *glossary); +struct arg_date* arg_daten(const char* shortopts, + const char* longopts, + const char* format, + const char* datatype, + int mincount, + int maxcount, + const char *glossary); + +struct arg_end* arg_end(int maxerrors); + + +/**** other functions *******************************************/ +int arg_nullcheck(void **argtable); +int arg_parse(int argc, char **argv, void **argtable); +void arg_print_option(FILE *fp, const char *shortopts, const char *longopts, const char *datatype, const char *suffix); +void arg_print_syntax(FILE *fp, void **argtable, const char *suffix); +void arg_print_syntaxv(FILE *fp, void **argtable, const char *suffix); +void arg_print_glossary(FILE *fp, void **argtable, const char *format); +void arg_print_glossary_gnu(FILE *fp, void **argtable); +void arg_print_errors(FILE* fp, struct arg_end* end, const char* progname); +void arg_freetable(void **argtable, size_t n); + +/**** deprecated functions, for back-compatibility only ********/ +void arg_free(void **argtable); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/client/cliparser/cliparser.c b/client/cliparser/cliparser.c new file mode 100644 index 00000000..0e70a630 --- /dev/null +++ b/client/cliparser/cliparser.c @@ -0,0 +1,167 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 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. +//----------------------------------------------------------------------------- +// Command line parser core commands +//----------------------------------------------------------------------------- + +#include "cliparser.h" +#include +#include + +void **argtable = NULL; +size_t argtableLen = 0; +char *programName = NULL; +char *programHint = NULL; +char *programHelp = NULL; +char buf[500] = {0}; + +int CLIParserInit(char *vprogramName, char *vprogramHint, char *vprogramHelp) { + argtable = NULL; + argtableLen = 0; + programName = vprogramName; + programHint = vprogramHint; + programHelp = vprogramHelp; + memset(buf, 0x00, 500); + + return 0; +} + +int CLIParserParseArg(int argc, char **argv, void* vargtable[], size_t vargtableLen, bool allowEmptyExec) { + int nerrors; + + argtable = vargtable; + argtableLen = vargtableLen; + + /* verify the argtable[] entries were allocated sucessfully */ + if (arg_nullcheck(argtable) != 0) { + /* NULL entries were detected, some allocations must have failed */ + printf("ERROR: Insufficient memory\n"); + return 2; + } + /* Parse the command line as defined by argtable[] */ + nerrors = arg_parse(argc, argv, argtable); + + /* special case: '--help' takes precedence over error reporting */ + if ((argc < 2 && !allowEmptyExec) ||((struct arg_lit *)argtable[0])->count > 0) { // help must be the first record + printf("Usage: %s", programName); + arg_print_syntaxv(stdout, argtable, "\n"); + if (programHint) + printf("%s\n\n", programHint); + arg_print_glossary(stdout, argtable, " %-20s %s\n"); + printf("\n"); + if (programHelp) + printf("%s \n", programHelp); + + return 1; + } + + /* If the parser returned any errors then display them and exit */ + if (nerrors > 0) { + /* Display the error details contained in the arg_end struct.*/ + arg_print_errors(stdout, ((struct arg_end *)argtable[vargtableLen - 1]), programName); + printf("Try '%s --help' for more information.\n", programName); + + return 3; + } + + return 0; +} + +enum ParserState { + PS_FIRST, + PS_ARGUMENT, + PS_OPTION, +}; + +#define isSpace(c)(c == ' ' || c == '\t') + +int CLIParserParseString(const char* str, void* vargtable[], size_t vargtableLen, bool allowEmptyExec) { + int argc = 0; + char *argv[200] = {NULL}; + + int len = strlen(str); + char *bufptr = buf; + char *spaceptr = NULL; + enum ParserState state = PS_FIRST; + + argv[argc++] = bufptr; + // param0 = program name + memcpy(buf, programName, strlen(programName) + 1); // with 0x00 + bufptr += strlen(programName) + 1; + if (len) + argv[argc++] = bufptr; + + // parse params + for (int i = 0; i < len; i++) { + switch(state){ + case PS_FIRST: // first char + if (str[i] == '-'){ // first char before space is '-' - next element - option + state = PS_OPTION; + + if (spaceptr) { + bufptr = spaceptr; + *bufptr = 0x00; + bufptr++; + argv[argc++] = bufptr; + } + } + spaceptr = NULL; + case PS_ARGUMENT: + if (state == PS_FIRST) + state = PS_ARGUMENT; + if (isSpace(str[i])) { + spaceptr = bufptr; + state = PS_FIRST; + } + *bufptr = str[i]; + bufptr++; + break; + case PS_OPTION: + if (isSpace(str[i])){ + state = PS_FIRST; + + *bufptr = 0x00; + bufptr++; + argv[argc++] = bufptr; + break; + } + + *bufptr = str[i]; + bufptr++; + break; + } + } + + return CLIParserParseArg(argc, argv, vargtable, vargtableLen, allowEmptyExec); +} + +void CLIParserFree() { + arg_freetable(argtable, argtableLen); + argtable = NULL; + + return; +} + +// convertors +int CLIParamHexToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen) { + switch(param_gethex_to_eol(argstr->sval[0], 0, data, maxdatalen, datalen)) { + case 1: + printf("Parameter error: Invalid HEX value.\n"); + return 1; + case 2: + printf("Parameter error: parameter too large.\n"); + return 2; + case 3: + printf("Parameter error: Hex string must have even number of digits.\n"); + return 3; + } + + return 0; +} + + + diff --git a/client/cliparser/cliparser.h b/client/cliparser/cliparser.h new file mode 100644 index 00000000..3f4fa4cc --- /dev/null +++ b/client/cliparser/cliparser.h @@ -0,0 +1,32 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 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. +//----------------------------------------------------------------------------- +// Command line parser core commands +//----------------------------------------------------------------------------- + +#include "argtable3.h" +#include "util.h" +#include + +#define arg_param_begin arg_lit0("hH", "help", "print this help and exit") +#define arg_param_end arg_end(20) + +#define arg_getsize(a) (sizeof(a) / sizeof(a[0])) +#define arg_get_lit(n)(((struct arg_lit*)argtable[n])->count) +#define arg_get_int(n)(((struct arg_int*)argtable[n])->ival[0]) +#define arg_get_str(n)((struct arg_str*)argtable[n]) + +#define CLIExecWithReturn(cmd, atbl, ifempty) if (CLIParserParseString(cmd, atbl, arg_getsize(atbl), ifempty)){CLIParserFree();return 0;} +#define CLIGetStrBLessWithReturn(paramnum, data, datalen, delta) if (CLIParamHexToBuf(arg_get_str(paramnum), data, sizeof(data) - (delta), datalen)) {CLIParserFree();return 1;} +#define CLIGetStrWithReturn(paramnum, data, datalen) if (CLIParamHexToBuf(arg_get_str(paramnum), data, sizeof(data), datalen)) {CLIParserFree();return 1;} + +extern int CLIParserInit(char *vprogramName, char *vprogramHint, char *vprogramHelp); +extern int CLIParserParseString(const char* str, void* argtable[], size_t vargtableLen, bool allowEmptyExec); +extern int CLIParserParseArg(int argc, char **argv, void* argtable[], size_t vargtableLen, bool allowEmptyExec); +extern void CLIParserFree(); + +extern int CLIParamHexToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen); diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 03ab0b5a..2d76f109 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -28,6 +28,7 @@ #include "mifare.h" #include "cmdhfmfu.h" #include "mifarehost.h" +#include "cliparser/cliparser.h" #include "emv/apduinfo.h" #include "emv/emvcore.h" @@ -137,38 +138,32 @@ int CmdHF14AList(const char *Cmd) int CmdHF14AReader(const char *Cmd) { uint32_t cm = ISO14A_CONNECT; - bool disconnectAfter = true; + bool leaveSignalON = false; - int cmdp = 0; - while(param_getchar(Cmd, cmdp) != 0x00) { - switch(param_getchar(Cmd, cmdp)) { - case 'h': - case 'H': - PrintAndLog("Usage: hf 14a reader [k|x] [3]"); - PrintAndLog(" k keep the field active after command executed"); - PrintAndLog(" x just drop the signal field"); - PrintAndLog(" 3 ISO14443-3 select only (skip RATS)"); - return 0; - case '3': - cm |= ISO14A_NO_RATS; - break; - case 'k': - case 'K': - disconnectAfter = false; - break; - case 'x': - case 'X': - cm &= ~ISO14A_CONNECT; - break; - default: - PrintAndLog("Unknown command."); - return 1; - } - - cmdp++; + CLIParserInit("hf 14a reader", "Executes ISO1443A anticollision-select group of commands.", NULL); + void* argtable[] = { + arg_param_begin, + arg_lit0("kK", "keep", "keep the field active after command executed"), + arg_lit0("xX", "drop", "just drop the signal field"), + arg_lit0("3", NULL, "ISO14443-3 select only (skip RATS)"), + arg_param_end + }; + if (CLIParserParseString(Cmd, argtable, arg_getsize(argtable), true)){ + CLIParserFree(); + return 0; } - - if (!disconnectAfter) + + leaveSignalON = arg_get_lit(1); + if (arg_get_lit(2)) { + cm = cm - ISO14A_CONNECT; + } + if (arg_get_lit(3)) { + cm |= ISO14A_NO_RATS; + } + + CLIParserFree(); + + if (leaveSignalON) cm |= ISO14A_NO_DISCONNECT; UsbCommand c = {CMD_READER_ISO_14443a, {cm, 0, 0}}; @@ -200,12 +195,12 @@ int CmdHF14AReader(const char *Cmd) { if(card.ats_len >= 3) { // a valid ATS consists of at least the length byte (TL) and 2 CRC bytes PrintAndLog(" ATS : %s", sprint_hex(card.ats, card.ats_len)); } - if (!disconnectAfter) { + if (leaveSignalON) { PrintAndLog("Card is selected. You can now start sending commands"); } } - if (disconnectAfter) { + if (!leaveSignalON) { PrintAndLog("Field dropped."); } @@ -737,59 +732,30 @@ int CmdHF14AAPDU(const char *cmd) { bool activateField = false; bool leaveSignalON = false; bool decodeTLV = false; + + CLIParserInit("hf 14a apdu", + "Sends an ISO 7816-4 APDU via ISO 14443-4 block transmission protocol (T=CL)", + "Sample:\n\thf 14a apdu -st 00A404000E325041592E5359532E444446303100\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("sS", "select", "activate field and select card"), + arg_lit0("kK", "keep", "leave the signal field ON after receive response"), + arg_lit0("tT", "tlv", "executes TLV decoder if it possible"), + arg_str1(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, false); - if (strlen(cmd) < 2) { - PrintAndLog("Usage: hf 14a apdu [-s] [-k] [-t] "); - PrintAndLog("Command sends an ISO 7816-4 APDU via ISO 14443-4 block transmission protocol (T=CL)"); - PrintAndLog(" -s activate field and select card"); - PrintAndLog(" -k leave the signal field ON after receive response"); - PrintAndLog(" -t executes TLV decoder if it possible. TODO!!!!"); - return 0; - } + activateField = arg_get_lit(1); + leaveSignalON = arg_get_lit(2); + decodeTLV = arg_get_lit(3); + // len = data + PCB(1b) + CRC(2b) + CLIGetStrBLessWithReturn(4, data, &datalen, 1 + 2); - int cmdp = 0; - while(param_getchar(cmd, cmdp) != 0x00) { - char c = param_getchar(cmd, cmdp); - if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) - switch (param_getchar_indx(cmd, 1, cmdp)) { - case 's': - case 'S': - activateField = true; - break; - case 'k': - case 'K': - leaveSignalON = true; - break; - case 't': - case 'T': - decodeTLV = true; - break; - default: - PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); - return 1; - } - - if (isxdigit((unsigned char)c)) { - // len = data + PCB(1b) + CRC(2b) - switch(param_gethex_to_eol(cmd, cmdp, data, sizeof(data) - 1 - 2, &datalen)) { - case 1: - PrintAndLog("Invalid HEX value."); - return 1; - case 2: - PrintAndLog("APDU too large."); - return 1; - case 3: - PrintAndLog("Hex must have even number of digits."); - return 1; - } - - // we get all the hex to end of line with spaces - break; - } - - cmdp++; - } + CLIParserFree(); +// PrintAndLog("---str [%d] %s", arg_get_str(4)->count, arg_get_str(4)->sval[0]); PrintAndLog(">>>>[%s%s%s] %s", activateField ? "sel ": "", leaveSignalON ? "keep ": "", decodeTLV ? "TLV": "", sprint_hex(data, datalen)); int res = ExchangeAPDU14a(data, datalen, activateField, leaveSignalON, data, USB_CMD_DATA_SIZE, &datalen); @@ -821,102 +787,57 @@ int CmdHF14ACmdRaw(const char *cmd) { bool bTimeout = false; uint32_t timeout = 0; bool topazmode = false; - char buf[5]=""; - int i = 0; uint8_t data[USB_CMD_DATA_SIZE]; - uint16_t datalen = 0; - uint32_t temp; + int datalen = 0; - if (strlen(cmd)<2) { - PrintAndLog("Usage: hf 14a raw [-r] [-c] [-p] [-f] [-b] [-t] <0A 0B 0C ... hex>"); - PrintAndLog(" -r do not read response"); - PrintAndLog(" -c calculate and append CRC"); - PrintAndLog(" -p leave the signal field ON after receive"); - PrintAndLog(" -a active signal field ON without select"); - PrintAndLog(" -s active signal field ON with select"); - PrintAndLog(" -b number of bits to send. Useful for send partial byte"); - PrintAndLog(" -t timeout in ms"); - PrintAndLog(" -T use Topaz protocol to send command"); - PrintAndLog(" -3 ISO14443-3 select only (skip RATS)"); + // extract parameters + CLIParserInit("hf 14a raw", "Send raw hex data to tag", + "Sample:\n"\ + "\thf 14a raw -pa -b7 -t1000 52 -- execute WUPA\n"\ + "\thf 14a raw -p 9320 -- anticollision\n"\ + "\thf 14a raw -psc 60 00 -- select and mifare AUTH\n"); + void* argtable[] = { + arg_param_begin, + arg_lit0("rR", "nreply", "do not read response"), + arg_lit0("cC", "crc", "calculate and append CRC"), + arg_lit0("pP", "power", "leave the signal field ON after receive"), + arg_lit0("aA", "active", "active signal field ON without select"), + arg_lit0("sS", "actives", "active signal field ON with select"), + arg_int0("bB", "bits", NULL, "number of bits to send. Useful for send partial byte"), + arg_int0("t", "timeout", NULL, "timeout in ms"), + arg_lit0("T", "topaz", "use Topaz protocol to send command"), + arg_lit0("3", NULL, "ISO14443-3 select only (skip RATS)"), + arg_str1(NULL, NULL, "", NULL), + arg_param_end + }; + // defaults + arg_get_int(6) = 0; + arg_get_int(7) = 0; + + if (CLIParserParseString(cmd, argtable, arg_getsize(argtable), false)){ + CLIParserFree(); return 0; } - - - // strip - while (*cmd==' ' || *cmd=='\t') cmd++; - - while (cmd[i]!='\0') { - if (cmd[i]==' ' || cmd[i]=='\t') { i++; continue; } - if (cmd[i]=='-') { - switch (cmd[i+1]) { - case 'r': - reply = false; - break; - case 'c': - crc = true; - break; - case 'p': - power = true; - break; - case 'a': - active = true; - break; - case 's': - active_select = true; - break; - case 'b': - sscanf(cmd+i+2,"%d",&temp); - numbits = temp & 0xFFFF; - i+=3; - while(cmd[i]!=' ' && cmd[i]!='\0') { i++; } - i-=2; - break; - case 't': - bTimeout = true; - sscanf(cmd+i+2,"%d",&temp); - timeout = temp; - i+=3; - while(cmd[i]!=' ' && cmd[i]!='\0') { i++; } - i-=2; - break; - case 'T': - topazmode = true; - break; - case '3': - no_rats = true; - break; - default: - PrintAndLog("Invalid option"); - return 0; - } - i+=2; - continue; - } - if ((cmd[i]>='0' && cmd[i]<='9') || - (cmd[i]>='a' && cmd[i]<='f') || - (cmd[i]>='A' && cmd[i]<='F') ) { - buf[strlen(buf)+1]=0; - buf[strlen(buf)]=cmd[i]; - i++; - - if (strlen(buf)>=2) { - sscanf(buf,"%x",&temp); - data[datalen]=(uint8_t)(temp & 0xff); - *buf=0; - if (datalen > sizeof(data)-1) { - if (crc) - PrintAndLog("Buffer is full, we can't add CRC to your data"); - break; - } else { - datalen++; - } - } - continue; - } - PrintAndLog("Invalid char on input"); - return 0; + + reply = !arg_get_lit(1); + crc = arg_get_lit(2); + power = arg_get_lit(3); + active = arg_get_lit(4); + active_select = arg_get_lit(5); + numbits = arg_get_int(6) & 0xFFFF; + timeout = arg_get_int(7); + bTimeout = (timeout > 0); + topazmode = arg_get_lit(8); + no_rats = arg_get_lit(9); + // len = data + CRC(2b) + if (CLIParamHexToBuf(arg_get_str(10), data, sizeof(data) -2, &datalen)) { + CLIParserFree(); + return 1; } - + + CLIParserFree(); + + // logic if(crc && datalen>0 && datalen Date: Sun, 9 Sep 2018 16:40:20 +0200 Subject: [PATCH 255/310] Legic Tag Simulator (#666) * FPGA Hi-Simulate: Formatted code * FPGA Hi-Simulate: Fixed documantation * FPGA Hi-Simulate: Freed up 4 LUTs * FPGA Hi-Simulate: Added 212kHz SSP-Clock option * Legic: Moved card simulator into separate file & cleaned interface. Reader and card simulation have almost no common code. Moreover the sim uses an SSP Clock at 212kHz for all timings to prevent any drifting from the PRNG. This clock speed is not available in reader simulation mode (SSP runs at up to 3.4MHz, and changes speed between TX and RX). For these reasons having the code in separate files makes it significantly cleaner. * Legic: Implemented RX and TX for card simulation * Legic: Implemented setup phase for card simulation * Legic: Implemented read command for card simulation * Legic: Implemented write command for card simulation --- armsrc/Makefile | 3 +- armsrc/appmain.c | 3 +- armsrc/legicrf.c | 427 +++------------------------------------- armsrc/legicrf.h | 2 +- armsrc/legicrfsim.c | 468 ++++++++++++++++++++++++++++++++++++++++++++ armsrc/legicrfsim.h | 19 ++ client/cmdhflegic.c | 8 +- fpga/fpga_hf.bit | Bin 42175 -> 42175 bytes fpga/hi_simulate.v | 44 ++--- 9 files changed, 539 insertions(+), 435 deletions(-) create mode 100644 armsrc/legicrfsim.c create mode 100644 armsrc/legicrfsim.h diff --git a/armsrc/Makefile b/armsrc/Makefile index d4b13c6b..046ad1bc 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -59,11 +59,12 @@ THUMBSRC = start.c \ # These are to be compiled in ARM mode ARMSRC = fpgaloader.c \ legicrf.c \ + legicrfsim.c \ + legic_prng.c \ $(SRC_ISO14443a) \ $(SRC_ISO14443b) \ $(SRC_CRAPTO1) \ $(SRC_CRC) \ - legic_prng.c \ iclass.c \ BigBuf.c \ optimized_cipher.c \ diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 4034788a..f7bcd620 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -21,6 +21,7 @@ #include "printf.h" #include "string.h" #include "legicrf.h" +#include "legicrfsim.h" #include "hitag2.h" #include "hitagS.h" #include "lfsampling.h" @@ -1090,7 +1091,7 @@ void UsbPacketReceived(uint8_t *packet, int len) #ifdef WITH_LEGICRF case CMD_SIMULATE_TAG_LEGIC_RF: - LegicRfSimulate(c->arg[0], c->arg[1], c->arg[2]); + LegicRfSimulate(c->arg[0]); break; case CMD_WRITER_LEGIC_RF: diff --git a/armsrc/legicrf.c b/armsrc/legicrf.c index 2a236b6f..c8a4829f 100644 --- a/armsrc/legicrf.c +++ b/armsrc/legicrf.c @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- // (c) 2009 Henryk Plötz // 2016 Iceman -// 2018 AntiCat (rwd rewritten) +// 2018 AntiCat // // 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 @@ -20,30 +20,8 @@ #include "legic.h" #include "crc.h" -static struct legic_frame { - int bits; - uint32_t data; -} current_frame; - -static enum { - STATE_DISCON, - STATE_IV, - STATE_CON, -} legic_state; - -static crc_t legic_crc; -static int legic_read_count; -static uint32_t legic_prng_bc; -static uint32_t legic_prng_iv; - -static int legic_phase_drift; -static int legic_frame_drift; -static int legic_reqresp_drift; - -AT91PS_TC timer; -AT91PS_TC prng_timer; - static legic_card_select_t card;/* metadata of currently selected card */ +static crc_t legic_crc; //----------------------------------------------------------------------------- // Frame timing and pseudorandom number generator @@ -71,11 +49,6 @@ static uint32_t last_frame_end; /* ts of last bit of previews rx or tx frame */ #define TAG_BIT_PERIOD 150 /* 100us */ #define TAG_WRITE_TIMEOUT 60 /* 40 * 100us (write should take at most 3.6ms) */ -#define SIM_DIVISOR 586 /* prng_time/DIV count prng needs to be forwared */ -#define SIM_SHIFT 900 /* prng_time+SHIFT shift of delayed start */ -#define RWD_TIME_FUZZ 20 /* rather generous 13us, since the peak detector - /+ hysteresis fuzz quite a bit */ - #define LEGIC_READ 0x01 /* Read Command */ #define LEGIC_WRITE 0x00 /* Write Command */ @@ -86,8 +59,6 @@ static uint32_t last_frame_end; /* ts of last bit of previews rx or tx frame */ #define INPUT_THRESHOLD 8 /* heuristically determined, lower values */ /* lead to detecting false ack during write */ -#define FUZZ_EQUAL(value, target, fuzz) ((value) > ((target)-(fuzz)) && (value) < ((target)+(fuzz))) - //----------------------------------------------------------------------------- // I/O interface abstraction (FPGA -> ARM) //----------------------------------------------------------------------------- @@ -125,8 +96,8 @@ static inline uint8_t rx_byte_from_fpga() { // also. // // The demedulated should be alligned to the bit periode by the caller. This is -// done in rx_bit_as_reader and rx_ack_as_reader. -static inline bool rx_bit_as_reader() { +// done in rx_bit and rx_ack. +static inline bool rx_bit() { int32_t cq = 0; int32_t ci = 0; @@ -158,7 +129,7 @@ static inline bool rx_bit_as_reader() { // be circumvented, but the adventage over bitbang would be little. //----------------------------------------------------------------------------- -static inline void tx_bit_as_reader(bool bit) { +static inline void tx_bit(bool bit) { // insert pause LOW(GPIO_SSC_DOUT); last_frame_end += RWD_TIME_PAUSE; @@ -180,7 +151,7 @@ static inline void tx_bit_as_reader(bool bit) { // present. //----------------------------------------------------------------------------- -static void tx_frame_as_reader(uint32_t frame, uint8_t len) { +static void tx_frame(uint32_t frame, uint8_t len) { FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); // wait for next tx timeslot @@ -190,7 +161,7 @@ static void tx_frame_as_reader(uint32_t frame, uint8_t len) { // transmit frame, MSB first for(uint8_t i = 0; i < len; ++i) { bool bit = (frame >> i) & 0x01; - tx_bit_as_reader(bit ^ legic_prng_get_bit()); + tx_bit(bit ^ legic_prng_get_bit()); legic_prng_forward(1); }; @@ -201,7 +172,7 @@ static void tx_frame_as_reader(uint32_t frame, uint8_t len) { HIGH(GPIO_SSC_DOUT); } -static uint32_t rx_frame_as_reader(uint8_t len) { +static uint32_t rx_frame(uint8_t len) { FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ | FPGA_HF_READER_RX_XCORR_QUARTER_FREQ); @@ -211,11 +182,11 @@ static uint32_t rx_frame_as_reader(uint8_t len) { while(GET_TICKS < last_frame_end) { }; uint32_t frame = 0; - for(uint8_t i = 0; i < len; i++) { - frame |= (rx_bit_as_reader() ^ legic_prng_get_bit()) << i; + for(uint8_t i = 0; i < len; ++i) { + frame |= (rx_bit() ^ legic_prng_get_bit()) << i; legic_prng_forward(1); - // rx_bit_as_reader runs only 95us, resync to TAG_BIT_PERIOD + // rx_bit runs only 95us, resync to TAG_BIT_PERIOD last_frame_end += TAG_BIT_PERIOD; while(GET_TICKS < last_frame_end) { }; } @@ -223,7 +194,7 @@ static uint32_t rx_frame_as_reader(uint8_t len) { return frame; } -static bool rx_ack_as_reader() { +static bool rx_ack() { // change fpga into rx mode FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ @@ -236,10 +207,10 @@ static bool rx_ack_as_reader() { uint32_t ack = 0; for(uint8_t i = 0; i < TAG_WRITE_TIMEOUT; ++i) { // sample bit - ack = rx_bit_as_reader(); + ack = rx_bit(); legic_prng_forward(1); - // rx_bit_as_reader runs only 95us, resync to TAG_BIT_PERIOD + // rx_bit runs only 95us, resync to TAG_BIT_PERIOD last_frame_end += TAG_BIT_PERIOD; while(GET_TICKS < last_frame_end) { }; @@ -256,7 +227,7 @@ static bool rx_ack_as_reader() { // Legic Reader //----------------------------------------------------------------------------- -int init_card(uint8_t cardtype, legic_card_select_t *p_card) { +static int init_card(uint8_t cardtype, legic_card_select_t *p_card) { p_card->tagtype = cardtype; switch(p_card->tagtype) { @@ -313,8 +284,8 @@ static void init_reader(bool clear_mem) { // The setup consists of a three way handshake: // - Transmit initialisation vector 7 bits // - Receive card type 6 bits -// - Acknowledge frame 6 bits -static uint32_t setup_phase_reader(uint8_t iv) { +// - Transmit Acknowledge 6 bits +static uint32_t setup_phase(uint8_t iv) { // init coordination timestamp last_frame_end = GET_TICKS; @@ -323,24 +294,24 @@ static uint32_t setup_phase_reader(uint8_t iv) { while(GET_TICKS < last_frame_end) { }; legic_prng_init(0); - tx_frame_as_reader(iv, 7); + tx_frame(iv, 7); - // configure iv + // configure prng legic_prng_init(iv); legic_prng_forward(2); // receive card type - int32_t card_type = rx_frame_as_reader(6); + int32_t card_type = rx_frame(6); legic_prng_forward(3); // send obsfuscated acknowledgment frame switch (card_type) { case 0x0D: - tx_frame_as_reader(0x19, 6); // MIM22 | READCMD = 0x18 | 0x01 + tx_frame(0x19, 6); // MIM22 | READCMD = 0x18 | 0x01 break; case 0x1D: case 0x3D: - tx_frame_as_reader(0x39, 6); // MIM256 | READCMD = 0x38 | 0x01 + tx_frame(0x39, 6); // MIM256 | READCMD = 0x38 | 0x01 break; } @@ -359,9 +330,9 @@ static int16_t read_byte(uint16_t index, uint8_t cmd_sz) { // read one byte LED_B_ON(); legic_prng_forward(2); - tx_frame_as_reader(cmd, cmd_sz); + tx_frame(cmd, cmd_sz); legic_prng_forward(2); - uint32_t frame = rx_frame_as_reader(12); + uint32_t frame = rx_frame(12); LED_B_OFF(); // split frame into data and crc @@ -391,12 +362,12 @@ bool write_byte(uint16_t index, uint8_t byte, uint8_t addr_sz) { // send write command LED_C_ON(); legic_prng_forward(2); - tx_frame_as_reader(cmd, addr_sz + 1 + 8 + 4); // sz = addr_sz + cmd + data + crc + tx_frame(cmd, addr_sz + 1 + 8 + 4); // sz = addr_sz + cmd + data + crc legic_prng_forward(3); LED_C_OFF(); // wait for ack - return rx_ack_as_reader(); + return rx_ack(); } //----------------------------------------------------------------------------- @@ -413,7 +384,7 @@ void LegicRfReader(int offset, int bytes) { // establish shared secret and detect card type DbpString("Reading card ..."); - uint8_t card_type = setup_phase_reader(SESSION_IV); + uint8_t card_type = setup_phase(SESSION_IV); if(init_card(card_type, &card) != 0) { Dbprintf("No or unknown card found, aborting"); goto OUT; @@ -463,7 +434,7 @@ void LegicRfWriter(int bytes, int offset) { // establish shared secret and detect card type Dbprintf("Writing 0x%02.2x - 0x%02.2x ...", offset, offset+bytes); - uint8_t card_type = setup_phase_reader(SESSION_IV); + uint8_t card_type = setup_phase(SESSION_IV); if(init_card(card_type, &card) != 0) { Dbprintf("No or unknown card found, aborting"); goto OUT; @@ -492,345 +463,3 @@ OUT: LED_D_OFF(); StopTicks(); } - -//----------------------------------------------------------------------------- -// Legic Simulator -//----------------------------------------------------------------------------- - -static void setup_timer(void) -{ - /* Set up Timer 1 to use for measuring time between pulses. Since we're bit-banging - * this it won't be terribly accurate but should be good enough. - */ - AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1); - timer = AT91C_BASE_TC1; - timer->TC_CCR = AT91C_TC_CLKDIS; - timer->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; - timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - - /* - * Set up Timer 2 to use for measuring time between frames in - * tag simulation mode. Runs 4x faster as Timer 1 - */ - AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC2); - prng_timer = AT91C_BASE_TC2; - prng_timer->TC_CCR = AT91C_TC_CLKDIS; - prng_timer->TC_CMR = AT91C_TC_CLKS_TIMER_DIV2_CLOCK; - prng_timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; -} - -/* Generate Keystream */ -static uint32_t get_key_stream(int skip, int count) -{ - uint32_t key=0; int i; - - /* Use int to enlarge timer tc to 32bit */ - legic_prng_bc += prng_timer->TC_CV; - prng_timer->TC_CCR = AT91C_TC_SWTRG; - - /* If skip == -1, forward prng time based */ - if(skip == -1) { - i = (legic_prng_bc+SIM_SHIFT)/SIM_DIVISOR; /* Calculate Cycles based on timer */ - i -= legic_prng_count(); /* substract cycles of finished frames */ - i -= count; /* substract current frame length, rewidn to bedinning */ - legic_prng_forward(i); - } else { - legic_prng_forward(skip); - } - - /* Write Time Data into LOG */ - uint8_t *BigBuf = BigBuf_get_addr(); - if(count == 6) { i = -1; } else { i = legic_read_count; } - BigBuf[OFFSET_LOG+128+i] = legic_prng_count(); - BigBuf[OFFSET_LOG+256+i*4] = (legic_prng_bc >> 0) & 0xff; - BigBuf[OFFSET_LOG+256+i*4+1] = (legic_prng_bc >> 8) & 0xff; - BigBuf[OFFSET_LOG+256+i*4+2] = (legic_prng_bc >>16) & 0xff; - BigBuf[OFFSET_LOG+256+i*4+3] = (legic_prng_bc >>24) & 0xff; - BigBuf[OFFSET_LOG+384+i] = count; - - /* Generate KeyStream */ - for(i=0; iPIO_CODR = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; - - /* Use time to crypt frame */ - if(crypt) { - legic_prng_forward(2); /* TAG_TIME_WAIT -> shift by 2 */ - int i; int key = 0; - for(i=0; iTC_CV < (TAG_FRAME_WAIT - 30)) ; - - int i; - for(i=0; iTC_CV + TAG_BIT_PERIOD; - int bit = response & 1; - response = response >> 1; - if(bit) { - AT91C_BASE_PIOA->PIO_SODR = GPIO_SSC_DOUT; - } else { - AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; - } - while(timer->TC_CV < nextbit) ; - } - AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; -} - -static void frame_append_bit(struct legic_frame * const f, int bit) -{ - if(f->bits >= 31) { - return; /* Overflow, won't happen */ - } - f->data |= (bit<bits); - f->bits++; -} - -static void frame_clean(struct legic_frame * const f) -{ - f->data = 0; - f->bits = 0; -} - -/* Handle (whether to respond) a frame in tag mode */ -static void frame_handle_tag(struct legic_frame const * const f) -{ - uint8_t *BigBuf = BigBuf_get_addr(); - - /* First Part of Handshake (IV) */ - if(f->bits == 7) { - if(f->data == SESSION_IV) { - LED_C_ON(); - prng_timer->TC_CCR = AT91C_TC_SWTRG; - legic_prng_init(f->data); - frame_send_tag(0x3d, 6, 1); /* 0x3d^0x26 = 0x1b */ - legic_state = STATE_IV; - legic_read_count = 0; - legic_prng_bc = 0; - legic_prng_iv = f->data; - - /* TIMEOUT */ - timer->TC_CCR = AT91C_TC_SWTRG; - while(timer->TC_CV > 1); - while(timer->TC_CV < 280); - return; - } else if((prng_timer->TC_CV % 50) > 40) { - legic_prng_init(f->data); - frame_send_tag(0x3d, 6, 1); - SpinDelay(20); - return; - } - } - - /* 0x19==??? */ - if(legic_state == STATE_IV) { - if((f->bits == 6) && (f->data == (0x19 ^ get_key_stream(1, 6)))) { - legic_state = STATE_CON; - - /* TIMEOUT */ - timer->TC_CCR = AT91C_TC_SWTRG; - while(timer->TC_CV > 1); - while(timer->TC_CV < 200); - return; - } else { - legic_state = STATE_DISCON; - LED_C_OFF(); - Dbprintf("0x19 - Frame: %03.3x", f->data); - return; - } - } - - /* Read */ - if(f->bits == 11) { - if(legic_state == STATE_CON) { - int key = get_key_stream(-1, 11); //legic_phase_drift, 11); - int addr = f->data ^ key; addr = addr >> 1; - int data = BigBuf[addr]; - int hash = calc_crc4(addr, data, 11) << 8; - BigBuf[OFFSET_LOG+legic_read_count] = (uint8_t)addr; - legic_read_count++; - - //Dbprintf("Data:%03.3x, key:%03.3x, addr: %03.3x, read_c:%u", f->data, key, addr, read_c); - legic_prng_forward(legic_reqresp_drift); - - frame_send_tag(hash | data, 12, 1); - - /* SHORT TIMEOUT */ - timer->TC_CCR = AT91C_TC_SWTRG; - while(timer->TC_CV > 1); - legic_prng_forward(legic_frame_drift); - while(timer->TC_CV < 180); - return; - } - } - - /* Write */ - if(f->bits == 23) { - int key = get_key_stream(-1, 23); //legic_frame_drift, 23); - int addr = f->data ^ key; addr = addr >> 1; addr = addr & 0x3ff; - int data = f->data ^ key; data = data >> 11; data = data & 0xff; - - /* write command */ - legic_state = STATE_DISCON; - LED_C_OFF(); - Dbprintf("write - addr: %x, data: %x", addr, data); - return; - } - - if(legic_state != STATE_DISCON) { - Dbprintf("Unexpected: sz:%u, Data:%03.3x, State:%u, Count:%u", f->bits, f->data, legic_state, legic_read_count); - int i; - Dbprintf("IV: %03.3x", legic_prng_iv); - for(i = 0; iPIO_ODR = GPIO_SSC_DIN; - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DIN; - - setup_timer(); - crc_init(&legic_crc, 4, 0x19 >> 1, 0x5, 0); - - int old_level = 0; - int active = 0; - legic_state = STATE_DISCON; - - LED_B_ON(); - DbpString("Starting Legic emulator, press button to end"); - while(!BUTTON_PRESS()) { - int level = !!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN); - int time = timer->TC_CV; - - if(level != old_level) { - if(level == 1) { - timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - if(FUZZ_EQUAL(time, RWD_TIME_1, RWD_TIME_FUZZ)) { - /* 1 bit */ - emit(1); - active = 1; - LED_A_ON(); - } else if(FUZZ_EQUAL(time, RWD_TIME_0, RWD_TIME_FUZZ)) { - /* 0 bit */ - emit(0); - active = 1; - LED_A_ON(); - } else if(active) { - /* invalid */ - emit(-1); - active = 0; - LED_A_OFF(); - } - } - } - - if(time >= (RWD_TIME_1+RWD_TIME_FUZZ) && active) { - /* Frame end */ - emit(-1); - active = 0; - LED_A_OFF(); - } - - if(time >= (20*RWD_TIME_1) && (timer->TC_SR & AT91C_TC_CLKSTA)) { - timer->TC_CCR = AT91C_TC_CLKDIS; - } - - old_level = level; - WDT_HIT(); - } - DbpString("Stopped"); - LED_B_OFF(); - LED_A_OFF(); - LED_C_OFF(); -} - diff --git a/armsrc/legicrf.h b/armsrc/legicrf.h index 46459856..9f8cf457 100644 --- a/armsrc/legicrf.h +++ b/armsrc/legicrf.h @@ -1,5 +1,6 @@ //----------------------------------------------------------------------------- // (c) 2009 Henryk Plötz +// 2018 AntiCat // // 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 @@ -11,7 +12,6 @@ #ifndef __LEGICRF_H #define __LEGICRF_H -extern void LegicRfSimulate(int phase, int frame, int reqresp); extern void LegicRfReader(int bytes, int offset); extern void LegicRfWriter(int bytes, int offset); diff --git a/armsrc/legicrfsim.c b/armsrc/legicrfsim.c new file mode 100644 index 00000000..07a0a62d --- /dev/null +++ b/armsrc/legicrfsim.c @@ -0,0 +1,468 @@ +//----------------------------------------------------------------------------- +// (c) 2009 Henryk Plötz +// 2016 Iceman +// 2018 AntiCat +// +// 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. +//----------------------------------------------------------------------------- +// LEGIC RF simulation code +//----------------------------------------------------------------------------- + +#include "proxmark3.h" +#include "apps.h" +#include "util.h" +#include "string.h" + +#include "legicrfsim.h" +#include "legic_prng.h" +#include "legic.h" +#include "crc.h" + +static uint8_t* legic_mem; /* card memory, used for sim */ +static legic_card_select_t card;/* metadata of currently selected card */ +static crc_t legic_crc; + +//----------------------------------------------------------------------------- +// Frame timing and pseudorandom number generator +// +// The Prng is forwarded every 99.1us (TAG_BIT_PERIOD), except when the reader is +// transmitting. In that case the prng has to be forwarded every bit transmitted: +// - 31.3us for a 0 (RWD_TIME_0) +// - 99.1us for a 1 (RWD_TIME_1) +// +// The data dependent timing makes writing comprehensible code significantly +// harder. The current aproach forwards the prng data based if there is data on +// air and time based, using GetCountSspClk(), during computational and wait +// periodes. SSP Clock is clocked by the FPGA at 212 kHz (subcarrier frequency). +// +// To not have the necessity to calculate/guess exection time dependend timeouts +// tx_frame and rx_frame use a shared timestamp to coordinate tx and rx timeslots. +//----------------------------------------------------------------------------- + +static uint32_t last_frame_end; /* ts of last bit of previews rx or tx frame */ + +#define TAG_FRAME_WAIT 70 /* 330us from READER frame end to TAG frame start */ +#define TAG_ACK_WAIT 758 /* 3.57ms from READER frame end to TAG write ACK */ +#define TAG_BIT_PERIOD 21 /* 99.1us */ + +#define RWD_TIME_PAUSE 4 /* 18.9us */ +#define RWD_TIME_1 21 /* RWD_TIME_PAUSE 18.9us off + 80.2us on = 99.1us */ +#define RWD_TIME_0 13 /* RWD_TIME_PAUSE 18.9us off + 42.4us on = 61.3us */ +#define RWD_CMD_TIMEOUT 40 /* 40 * 99.1us (arbitrary value) */ +#define RWD_MIN_FRAME_LEN 6 /* Shortest frame is 6 bits */ +#define RWD_MAX_FRAME_LEN 23 /* Longest frame is 23 bits */ + +#define RWD_PULSE 1 /* Pulse is signaled with GPIO_SSC_DIN high */ +#define RWD_PAUSE 0 /* Pause is signaled with GPIO_SSC_DIN low */ + +//----------------------------------------------------------------------------- +// Demodulation +//----------------------------------------------------------------------------- + +// Returns true if a pulse/pause is received within timeout +static inline bool wait_for(bool value, const uint32_t timeout) { + while((bool)(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN) != value) { + if(GetCountSspClk() > timeout) { + return false; + } + } + return true; +} + +// Returns a demedulated bit or -1 on code violation +// +// rx_bit decodes bits using a thresholds. rx_bit has to be called by as soon as +// a frame starts (first pause is received). rx_bit checks for a pause up to +// 18.9us followed by a pulse of 80.2us or 42.4us: +// - A bit length <18.9us is a code violation +// - A bit length >80.2us is a 1 +// - A bit length <80.2us is a 0 +// - A bit length >148.6us is a code violation +static inline int8_t rx_bit() { + // backup ts for threshold calculation + uint32_t bit_start = last_frame_end; + + // wait for pause to end + if(!wait_for(RWD_PULSE, bit_start + RWD_TIME_1*3/2)) { + return -1; + } + + // wait for next pause + if(!wait_for(RWD_PAUSE, bit_start + RWD_TIME_1*3/2)) { + return -1; + } + + // update bit and frame end + last_frame_end = GetCountSspClk(); + + // check for code violation (bit to short) + if(last_frame_end - bit_start < RWD_TIME_PAUSE) { + return -1; + } + + // apply threshold (average of RWD_TIME_0 and ) + return (last_frame_end - bit_start > (RWD_TIME_0 + RWD_TIME_1) / 2); +} + +//----------------------------------------------------------------------------- +// Modulation +// +// LEGIC RF uses a very basic load modulation from card to reader: +// - Subcarrier on for a 1 +// - Subcarrier off for for a 0 +// +// The 212kHz subcarrier is generated by the FPGA as well as a mathcing ssp clk. +// Each bit is transfered in a 99.1us slot and the first timeslot starts 330us +// after the final 20us pause generated by the reader. +//----------------------------------------------------------------------------- + +// Transmits a bit +// +// Note: The Subcarrier is not disabled during bits to prevent glitches. This is +// not mandatory but results in a cleaner signal. tx_frame will disable +// the subcarrier when the frame is done. +static inline void tx_bit(bool bit) { + LED_C_ON(); + + if(bit) { + // modulate subcarrier + HIGH(GPIO_SSC_DOUT); + } else { + // do not modulate subcarrier + LOW(GPIO_SSC_DOUT); + } + + // wait for tx timeslot to end + last_frame_end += TAG_BIT_PERIOD; + while(GetCountSspClk() < last_frame_end) { }; + LED_C_OFF(); +} + +//----------------------------------------------------------------------------- +// Frame Handling +// +// The LEGIC RF protocol from reader to card does not include explicit frame +// start/stop information or length information. The tag detects end of frame +// trough an extended pulse (>99.1us) without a pause. +// In reverse direction (card to reader) the number of bites is well known +// and depends only the command received (IV, ACK, READ or WRITE). +//----------------------------------------------------------------------------- + +static void tx_frame(uint32_t frame, uint8_t len) { + // wait for next tx timeslot + last_frame_end += TAG_FRAME_WAIT; + legic_prng_forward(TAG_FRAME_WAIT/TAG_BIT_PERIOD - 1); + while(GetCountSspClk() < last_frame_end) { }; + + // transmit frame, MSB first + for(uint8_t i = 0; i < len; ++i) { + bool bit = (frame >> i) & 0x01; + tx_bit(bit ^ legic_prng_get_bit()); + legic_prng_forward(1); + }; + + // disable subcarrier + LOW(GPIO_SSC_DOUT); +} + +static void tx_ack() { + // wait for ack timeslot + last_frame_end += TAG_ACK_WAIT; + legic_prng_forward(TAG_ACK_WAIT/TAG_BIT_PERIOD - 1); + while(GetCountSspClk() < last_frame_end) { }; + + // transmit ack (ack is not encrypted) + tx_bit(true); + legic_prng_forward(1); + + // disable subcarrier + LOW(GPIO_SSC_DOUT); +} + +// Returns a demedulated frame or -1 on code violation +// +// Since TX to RX delay is arbitrary rx_frame has to: +// - detect start of frame (first pause) +// - forward prng based on ts/TAG_BIT_PERIOD +// - receive the frame +// - detect end of frame (last pause) +static int32_t rx_frame(uint8_t *len) { + int32_t frame = 0; + + // add 2 SSP clock cycles (1 for tx and 1 for rx pipeline delay) + // those will be substracted at the end of the rx phase + last_frame_end -= 2; + + // wait for first pause (start of frame) + for(uint8_t i = 0; true; ++i) { + // increment prng every TAG_BIT_PERIOD + last_frame_end += TAG_BIT_PERIOD; + legic_prng_forward(1); + + // if start of frame was received exit delay loop + if(wait_for(RWD_PAUSE, last_frame_end)) { + last_frame_end = GetCountSspClk(); + break; + } + + // check for code violation + if(i > RWD_CMD_TIMEOUT) { + return -1; + } + } + + // receive frame + for(*len = 0; true; ++(*len)) { + // receive next bit + LED_D_ON(); + int8_t bit = rx_bit(); + LED_D_OFF(); + + // check for code violation and to short / long frame + if((bit < 0) && ((*len < RWD_MIN_FRAME_LEN) || (*len > RWD_MAX_FRAME_LEN))) { + return -1; + } + + // check for code violation caused by end of frame + if(bit < 0) { + break; + } + + // append bit + frame |= (bit ^ legic_prng_get_bit()) << (*len); + legic_prng_forward(1); + } + + // rx_bit sets coordination timestamp to start of pause, append pause duration + // and substract 2 SSP clock cycles (1 for rx and 1 for tx pipeline delay) to + // obtain exact end of frame. + last_frame_end += RWD_TIME_PAUSE - 2; + + return frame; +} + +//----------------------------------------------------------------------------- +// Legic Simulator +//----------------------------------------------------------------------------- + +static int32_t init_card(uint8_t cardtype, legic_card_select_t *p_card) { + p_card->tagtype = cardtype; + + switch(p_card->tagtype) { + case 0: + p_card->cmdsize = 6; + p_card->addrsize = 5; + p_card->cardsize = 22; + break; + case 1: + p_card->cmdsize = 9; + p_card->addrsize = 8; + p_card->cardsize = 256; + break; + case 2: + p_card->cmdsize = 11; + p_card->addrsize = 10; + p_card->cardsize = 1024; + break; + default: + p_card->cmdsize = 0; + p_card->addrsize = 0; + p_card->cardsize = 0; + return 2; + } + return 0; +} + +static void init_tag() { + // configure FPGA + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR + | FPGA_HF_SIMULATOR_MODULATE_212K); + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + + // configure SSC with defaults + FpgaSetupSsc(); + + // first pull output to low to prevent glitches then re-claim GPIO_SSC_DOUT + LOW(GPIO_SSC_DOUT); + AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; + AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; + + // reserve a cardmem, meaning we can use the tracelog function in bigbuff easier. + legic_mem = BigBuf_get_addr(); + + // init crc calculator + crc_init(&legic_crc, 4, 0x19 >> 1, 0x05, 0); + + // start 212kHz timer (running from SSP Clock) + StartCountSspClk(); +} + +// Setup reader to card connection +// +// The setup consists of a three way handshake: +// - Receive initialisation vector 7 bits +// - Transmit card type 6 bits +// - Receive Acknowledge 6 bits +static int32_t setup_phase(legic_card_select_t *p_card) { + uint8_t len = 0; + + // init coordination timestamp + last_frame_end = GetCountSspClk(); + + // reset prng + legic_prng_init(0); + + // wait for iv + int32_t iv = rx_frame(&len); + if((len != 7) || (iv < 0)) { + return -1; + } + + // configure prng + legic_prng_init(iv); + + // reply with card type + switch(p_card->tagtype) { + case 0: + tx_frame(0x0D, 6); + break; + case 1: + tx_frame(0x1D, 6); + break; + case 2: + tx_frame(0x3D, 6); + break; + } + + // wait for ack + int32_t ack = rx_frame(&len); + if((len != 6) || (ack < 0)) { + return -1; + } + + // validate data + switch(p_card->tagtype) { + case 0: + if(ack != 0x19) return -1; + break; + case 1: + if(ack != 0x39) return -1; + break; + case 2: + if(ack != 0x39) return -1; + break; + } + + // During rx the prng is clocked using the variable reader period. + // Since rx_frame detects end of frame by detecting a code violation, + // the prng is off by one bit period after each rx phase. Hence, tx + // code advances the prng by (TAG_FRAME_WAIT/TAG_BIT_PERIOD - 1). + // This is not possible for back to back rx, so this quirk reduces + // the gap by one period. + last_frame_end += TAG_BIT_PERIOD; + + return 0; +} + +static uint8_t calc_crc4(uint16_t cmd, uint8_t cmd_sz, uint8_t value) { + crc_clear(&legic_crc); + crc_update(&legic_crc, (value << cmd_sz) | cmd, 8 + cmd_sz); + return crc_finish(&legic_crc); +} + +static int32_t connected_phase(legic_card_select_t *p_card) { + uint8_t len = 0; + + // wait for command + int32_t cmd = rx_frame(&len); + if(cmd < 0) { + return -1; + } + + // check if command is LEGIC_READ + if(len == p_card->cmdsize) { + // prepare data + uint8_t byte = legic_mem[cmd >> 1]; + uint8_t crc = calc_crc4(cmd, p_card->cmdsize, byte); + + // transmit data + tx_frame((crc << 8) | byte, 12); + + return 0; + } + + // check if command is LEGIC_WRITE + if(len == p_card->cmdsize + 8 + 4) { + // decode data + uint16_t mask = (1 << p_card->addrsize) - 1; + uint16_t addr = (cmd >> 1) & mask; + uint8_t byte = (cmd >> p_card->cmdsize) & 0xff; + uint8_t crc = (cmd >> (p_card->cmdsize + 8)) & 0xf; + + // check received against calculated crc + uint8_t calc_crc = calc_crc4(addr << 1, p_card->cmdsize, byte); + if(calc_crc != crc) { + Dbprintf("!!! crc mismatch: %x != %x !!!", calc_crc, crc); + return -1; + } + + // store data + legic_mem[addr] = byte; + + // transmit ack + tx_ack(); + + return 0; + } + + return -1; +} + +//----------------------------------------------------------------------------- +// Command Line Interface +// +// Only this function is public / called from appmain.c +//----------------------------------------------------------------------------- + +void LegicRfSimulate(uint8_t cardtype) { + // configure ARM and FPGA + init_tag(); + + // verify command line input + if(init_card(cardtype, &card) != 0) { + DbpString("Unknown tagtype."); + goto OUT; + } + + LED_A_ON(); + DbpString("Starting Legic emulator, press button to end"); + while(!BUTTON_PRESS()) { + WDT_HIT(); + + // wait for carrier, restart after timeout + if(!wait_for(RWD_PULSE, GetCountSspClk() + TAG_BIT_PERIOD)) { + continue; + } + + // wait for connection, restart on error + if(setup_phase(&card)) { + continue; + } + + // conection is established, process commands until one fails + while(!connected_phase(&card)) { + WDT_HIT(); + } + } + +OUT: + DbpString("Stopped"); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_A_OFF(); + LED_C_OFF(); + LED_D_OFF(); + StopTicks(); +} diff --git a/armsrc/legicrfsim.h b/armsrc/legicrfsim.h new file mode 100644 index 00000000..c1c8a86e --- /dev/null +++ b/armsrc/legicrfsim.h @@ -0,0 +1,19 @@ +//----------------------------------------------------------------------------- +// (c) 2009 Henryk Plötz +// 2018 AntiCat +// +// 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. +//----------------------------------------------------------------------------- +// LEGIC RF emulation public interface +//----------------------------------------------------------------------------- + +#ifndef __LEGICRFSIM_H +#define __LEGICRFSIM_H + +#include "proxmark3.h" + +extern void LegicRfSimulate(uint8_t tagtype); + +#endif /* __LEGICRFSIM_H */ diff --git a/client/cmdhflegic.c b/client/cmdhflegic.c index 691e1978..8fbd4578 100644 --- a/client/cmdhflegic.c +++ b/client/cmdhflegic.c @@ -28,7 +28,7 @@ static command_t CommandTable[] = {"reader", CmdLegicRFRead, 0, "[offset [length]] -- read bytes from a LEGIC card"}, {"save", CmdLegicSave, 0, " [] -- Store samples"}, {"load", CmdLegicLoad, 0, " -- Restore samples"}, - {"sim", CmdLegicRfSim, 0, "[phase drift [frame drift [req/resp drift]]] Start tag simulator (use after load or read)"}, + {"sim", CmdLegicRfSim, 0, "[tagtype, 0:MIM22, 1:MIM256, 2:MIM1024] Start tag simulator (use after load or read)"}, {"write", CmdLegicRfWrite,0, " -- Write sample buffer (user after load or read)"}, {"fill", CmdLegicRfFill, 0, " -- Fill/Write tag with constant value"}, {NULL, NULL, 0, NULL} @@ -320,10 +320,8 @@ int CmdLegicSave(const char *Cmd) int CmdLegicRfSim(const char *Cmd) { UsbCommand c={CMD_SIMULATE_TAG_LEGIC_RF}; - c.arg[0] = 6; - c.arg[1] = 3; - c.arg[2] = 0; - sscanf(Cmd, " %" SCNi64 " %" SCNi64 " %" SCNi64, &c.arg[0], &c.arg[1], &c.arg[2]); + c.arg[0] = 1; + sscanf(Cmd, " %" SCNi64, &c.arg[0]); SendCommand(&c); return 0; } diff --git a/fpga/fpga_hf.bit b/fpga/fpga_hf.bit index 939ba93a54ce00e9425a1156d3900f1af58c4c8e..179e87eed22bc326f37e33f6925116916701c6bf 100644 GIT binary patch literal 42175 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 literal 42175 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 diff --git a/fpga/hi_simulate.v b/fpga/hi_simulate.v index 0768c29d..78650c4a 100644 --- a/fpga/hi_simulate.v +++ b/fpga/hi_simulate.v @@ -51,38 +51,29 @@ begin end -// Divide 13.56 MHz by 32 to produce the SSP_CLK -// The register is bigger to allow higher division factors of up to /128 +// Divide 13.56 MHz to produce various frequencies for SSP_CLK +// and modulation. 11 bits allow for factors of up to /128. reg [10:0] ssp_clk_divider; always @(posedge adc_clk) ssp_clk_divider <= (ssp_clk_divider + 1); reg ssp_clk; -reg ssp_frame; + always @(negedge adc_clk) begin - //If we're in 101, we only need a new bit every 8th carrier bit (53Hz). Otherwise, get next bit at 424Khz if(mod_type == 3'b101) - begin - if(ssp_clk_divider[7:0] == 8'b00000000) - ssp_clk <= 1'b0; - if(ssp_clk_divider[7:0] == 8'b10000000) - ssp_clk <= 1'b1; - - end + // Get bit every at 53KHz (every 8th carrier bit of 424kHz) + ssp_clk <= ssp_clk_divider[7]; + else if(mod_type == 3'b010) + // Get next bit at 212kHz + ssp_clk <= ssp_clk_divider[5]; else - begin - if(ssp_clk_divider[4:0] == 5'd0)//[4:0] == 5'b00000) - ssp_clk <= 1'b1; - if(ssp_clk_divider[4:0] == 5'd16) //[4:0] == 5'b10000) - ssp_clk <= 1'b0; - end + // Get next bit at 424Khz + ssp_clk <= ssp_clk_divider[4]; end -//assign ssp_clk = ssp_clk_divider[4]; - // Divide SSP_CLK by 8 to produce the byte framing signal; the phase of // this is arbitrary, because it's just a bitstream. // One nasty issue, though: I can't make it work with both rx and tx at @@ -96,19 +87,19 @@ always @(negedge ssp_clk) ssp_frame_divider_from_arm <= (ssp_frame_divider_from_arm + 1); - +reg ssp_frame; always @(ssp_frame_divider_to_arm or ssp_frame_divider_from_arm or mod_type) if(mod_type == 3'b000) // not modulating, so listening, to ARM ssp_frame = (ssp_frame_divider_to_arm == 3'b000); else - ssp_frame = (ssp_frame_divider_from_arm == 3'b000); + ssp_frame = (ssp_frame_divider_from_arm == 3'b000); // Synchronize up the after-hysteresis signal, to produce DIN. reg ssp_din; always @(posedge ssp_clk) ssp_din = after_hysteresis; -// Modulating carrier frequency is fc/16, reuse ssp_clk divider for that +// Modulating carrier frequency is fc/64 (212kHz) to fc/16 (848kHz). Reuse ssp_clk divider for that. reg modulating_carrier; always @(mod_type or ssp_clk or ssp_dout) if(mod_type == 3'b000) @@ -116,9 +107,9 @@ always @(mod_type or ssp_clk or ssp_dout) else if(mod_type == 3'b001) modulating_carrier <= ssp_dout ^ ssp_clk_divider[3]; // XOR means BPSK else if(mod_type == 3'b010) - modulating_carrier <= ssp_dout & ssp_clk_divider[5]; // switch 212kHz subcarrier on/off + modulating_carrier <= ssp_dout & ssp_clk_divider[5]; // switch 212kHz subcarrier on/off else if(mod_type == 3'b100 || mod_type == 3'b101) - modulating_carrier <= ssp_dout & ssp_clk_divider[4]; // switch 424kHz modulation on/off + modulating_carrier <= ssp_dout & ssp_clk_divider[4]; // switch 424kHz modulation on/off else modulating_carrier <= 1'b0; // yet unused @@ -133,9 +124,6 @@ assign pwr_oe4 = modulating_carrier; // This one is always on, so that we can watch the carrier. assign pwr_oe3 = 1'b0; -assign dbg = modulating_carrier; -//reg dbg; -//always @(ssp_dout) -// dbg <= ssp_dout; +assign dbg = ssp_din; endmodule From 53edb044c0dfe82fd9d0a0e0d3444a51ebe358f4 Mon Sep 17 00:00:00 2001 From: wllm-rbnt Date: Mon, 10 Sep 2018 18:19:31 +0200 Subject: [PATCH 256/310] Allow clean Legic simulation exit without button press (#668) --- armsrc/legicrfsim.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/armsrc/legicrfsim.c b/armsrc/legicrfsim.c index 07a0a62d..34633f36 100644 --- a/armsrc/legicrfsim.c +++ b/armsrc/legicrfsim.c @@ -19,6 +19,7 @@ #include "legic_prng.h" #include "legic.h" #include "crc.h" +#include "usb_cdc.h" // for usb_poll_validate_length static uint8_t* legic_mem; /* card memory, used for sim */ static legic_card_select_t card;/* metadata of currently selected card */ @@ -439,7 +440,7 @@ void LegicRfSimulate(uint8_t cardtype) { LED_A_ON(); DbpString("Starting Legic emulator, press button to end"); - while(!BUTTON_PRESS()) { + while(!BUTTON_PRESS() && !usb_poll_validate_length()) { WDT_HIT(); // wait for carrier, restart after timeout From 6d31653c440538916811b957769a89f24eb39d63 Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 11 Sep 2018 12:21:29 +0300 Subject: [PATCH 257/310] move `hf emv search` to argtable --- client/emv/cmdemv.c | 59 +++++++++++++++------------------------------ 1 file changed, 19 insertions(+), 40 deletions(-) diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index c53b02af..784c5943 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -11,6 +11,7 @@ #include #include "cmdemv.h" #include "test/cryptotest.h" +#include "cliparser/cliparser.h" int UsageCmdHFEMVSelect(void) { PrintAndLog("HELP : Executes select applet command:\n"); @@ -125,49 +126,27 @@ int UsageCmdHFEMVSearch(void) { int CmdHFEMVSearch(const char *cmd) { - bool activateField = false; - bool leaveSignalON = false; - bool decodeTLV = false; + CLIParserInit("hf 14a select", + "Tries to select all applets from applet list:\n", + "Usage:\n\thf emv search -s -> select card and search\n\thf emv search -s -t -> select card, search and show result in TLV\n"); - if (strlen(cmd) < 1) { - UsageCmdHFEMVSearch(); - return 0; - } + void* argtable[] = { + arg_param_begin, + arg_lit0("sS", "select", "activate field and select card"), + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results of selected applets"), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); - SetAPDULogging(false); + bool activateField = arg_get_lit(1); + bool leaveSignalON = arg_get_lit(2); + bool APDULogging = arg_get_lit(3); + bool decodeTLV = arg_get_lit(4); + + SetAPDULogging(APDULogging); - int cmdp = 0; - while(param_getchar(cmd, cmdp) != 0x00) { - char c = param_getchar(cmd, cmdp); - if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) - switch (param_getchar_indx(cmd, 1, cmdp)) { - case 'h': - case 'H': - UsageCmdHFEMVSearch(); - return 0; - case 's': - case 'S': - activateField = true; - break; - case 'k': - case 'K': - leaveSignalON = true; - break; - case 'a': - case 'A': - SetAPDULogging(true); - break; - case 't': - case 'T': - decodeTLV = true; - break; - default: - PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); - return 1; - } - cmdp++; - } - struct tlvdb *t = NULL; const char *al = "Applets list"; t = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); From 3668df05ec90178e7b10efe9790e7aed49099519 Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 11 Sep 2018 14:49:50 +0300 Subject: [PATCH 258/310] `hf emv select` --- client/cliparser/cliparser.h | 1 + client/emv/cmdemv.c | 96 +++++++++--------------------------- 2 files changed, 25 insertions(+), 72 deletions(-) diff --git a/client/cliparser/cliparser.h b/client/cliparser/cliparser.h index 3f4fa4cc..9d83a95f 100644 --- a/client/cliparser/cliparser.h +++ b/client/cliparser/cliparser.h @@ -19,6 +19,7 @@ #define arg_get_lit(n)(((struct arg_lit*)argtable[n])->count) #define arg_get_int(n)(((struct arg_int*)argtable[n])->ival[0]) #define arg_get_str(n)((struct arg_str*)argtable[n]) +#define arg_get_str_len(n)(strlen(((struct arg_int*)argtable[n])->ival[0])) #define CLIExecWithReturn(cmd, atbl, ifempty) if (CLIParserParseString(cmd, atbl, arg_getsize(atbl), ifempty)){CLIParserFree();return 0;} #define CLIGetStrBLessWithReturn(paramnum, data, datalen, delta) if (CLIParamHexToBuf(arg_get_str(paramnum), data, sizeof(data) - (delta), datalen)) {CLIParserFree();return 1;} diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index 784c5943..c40a9a69 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -30,67 +30,32 @@ int UsageCmdHFEMVSelect(void) { int CmdHFEMVSelect(const char *cmd) { uint8_t data[APDU_AID_LEN] = {0}; int datalen = 0; - bool activateField = false; - bool leaveSignalON = false; - bool decodeTLV = false; - if (strlen(cmd) < 1) { - UsageCmdHFEMVSelect(); - return 0; - } + + CLIParserInit("hf 14a select", + "Executes select applet command", + "Usage:\n\thf emv select -s a00000000101 -> select card, select applet\n\thf emv select -s -t a00000000101 -> select card, select applet, show result in TLV\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("sS", "select", "activate field and select card"), + arg_lit0("kK", "keep", "keep field for next command"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results"), + arg_str0(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); - SetAPDULogging(false); + bool activateField = arg_get_lit(1); + bool leaveSignalON = arg_get_lit(2); + bool APDULogging = arg_get_lit(3); + bool decodeTLV = arg_get_lit(4); + if (arg_get_str_len(5)) + CLIGetStrBLessWithReturn(5, data, &datalen, 0); + CLIParserFree(); - int cmdp = 0; - while(param_getchar(cmd, cmdp) != 0x00) { - char c = param_getchar(cmd, cmdp); - if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) - switch (param_getchar_indx(cmd, 1, cmdp)) { - case 'h': - case 'H': - UsageCmdHFEMVSelect(); - return 0; - case 's': - case 'S': - activateField = true; - break; - case 'k': - case 'K': - leaveSignalON = true; - break; - case 'a': - case 'A': - SetAPDULogging(true); - break; - case 't': - case 'T': - decodeTLV = true; - break; - default: - PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); - return 1; - } - - if (isxdigit((unsigned char)c)) { - switch(param_gethex_to_eol(cmd, cmdp, data, sizeof(data), &datalen)) { - case 1: - PrintAndLog("Invalid HEX value."); - return 1; - case 2: - PrintAndLog("AID too large."); - return 1; - case 3: - PrintAndLog("Hex must have even number of digits."); - return 1; - } - - // we get all the hex to end of line with spaces - break; - } - - - cmdp++; - } + SetAPDULogging(APDULogging); // exec uint8_t buf[APDU_RES_LEN] = {0}; @@ -110,20 +75,6 @@ int CmdHFEMVSelect(const char *cmd) { return 0; } -int UsageCmdHFEMVSearch(void) { - PrintAndLog("HELP : Tries to select all applets from applet list:\n"); - PrintAndLog("Usage: hf emv search [-s][-k][-a][-t]\n"); - PrintAndLog(" Options:"); - PrintAndLog(" -s : select card"); - PrintAndLog(" -k : keep field for next command"); - PrintAndLog(" -a : show APDU reqests and responses\n"); - PrintAndLog(" -t : TLV decode results of selected applets\n"); - PrintAndLog("Samples:"); - PrintAndLog(" hf emv search -s -> select card and search"); - PrintAndLog(" hf emv search -s -t -> select card, search and show result in TLV"); - return 0; -} - int CmdHFEMVSearch(const char *cmd) { CLIParserInit("hf 14a select", @@ -144,6 +95,7 @@ int CmdHFEMVSearch(const char *cmd) { bool leaveSignalON = arg_get_lit(2); bool APDULogging = arg_get_lit(3); bool decodeTLV = arg_get_lit(4); + CLIParserFree(); SetAPDULogging(APDULogging); From 7d4ba60e8e1b169822ff64a870b9f976585cb97c Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 11 Sep 2018 14:56:33 +0300 Subject: [PATCH 259/310] `hf emv select` some refactoring --- client/cliparser/cliparser.c | 4 ++++ client/emv/cmdemv.c | 3 +-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/client/cliparser/cliparser.c b/client/cliparser/cliparser.c index 0e70a630..87427db0 100644 --- a/client/cliparser/cliparser.c +++ b/client/cliparser/cliparser.c @@ -148,6 +148,10 @@ void CLIParserFree() { // convertors int CLIParamHexToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen) { + *datalen = 0; + if (!strlen(argstr->sval[0])) + return 0; + switch(param_gethex_to_eol(argstr->sval[0], 0, data, maxdatalen, datalen)) { case 1: printf("Parameter error: Invalid HEX value.\n"); diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index c40a9a69..33a519b1 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -51,8 +51,7 @@ int CmdHFEMVSelect(const char *cmd) { bool leaveSignalON = arg_get_lit(2); bool APDULogging = arg_get_lit(3); bool decodeTLV = arg_get_lit(4); - if (arg_get_str_len(5)) - CLIGetStrBLessWithReturn(5, data, &datalen, 0); + CLIGetStrBLessWithReturn(5, data, &datalen, 0); CLIParserFree(); SetAPDULogging(APDULogging); From 24e0d538d1cc9b71a20184ec9d1c97bed4ce2cc6 Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 11 Sep 2018 14:58:15 +0300 Subject: [PATCH 260/310] delete old help --- client/emv/cmdemv.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index 33a519b1..65a1639a 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -13,20 +13,6 @@ #include "test/cryptotest.h" #include "cliparser/cliparser.h" -int UsageCmdHFEMVSelect(void) { - PrintAndLog("HELP : Executes select applet command:\n"); - PrintAndLog("Usage: hf emv select [-s][-k][-a][-t] \n"); - PrintAndLog(" Options:"); - PrintAndLog(" -s : select card"); - PrintAndLog(" -k : keep field for next command"); - PrintAndLog(" -a : show APDU reqests and responses\n"); - PrintAndLog(" -t : TLV decode results\n"); - PrintAndLog("Samples:"); - PrintAndLog(" hf emv select -s a00000000101 -> select card, select applet"); - PrintAndLog(" hf emv select -s -t a00000000101 -> select card, select applet, show result in TLV"); - return 0; -} - int CmdHFEMVSelect(const char *cmd) { uint8_t data[APDU_AID_LEN] = {0}; int datalen = 0; From a4662ca9f9e8de6fb1e4dd07b59a9bba15731087 Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 11 Sep 2018 16:11:58 +0300 Subject: [PATCH 261/310] `hf emv pse` and sketch for the other commands --- client/emv/cmdemv.c | 117 +++++++++++++++++++------------------------- 1 file changed, 50 insertions(+), 67 deletions(-) diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index 65a1639a..e621038e 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -20,7 +20,7 @@ int CmdHFEMVSelect(const char *cmd) { CLIParserInit("hf 14a select", "Executes select applet command", - "Usage:\n\thf emv select -s a00000000101 -> select card, select applet\n\thf emv select -s -t a00000000101 -> select card, select applet, show result in TLV\n"); + "Usage:\n\thf emv select -s a00000000101 -> select card, select applet\n\thf emv select -st a00000000101 -> select card, select applet, show result in TLV\n"); void* argtable[] = { arg_param_begin, @@ -64,7 +64,7 @@ int CmdHFEMVSearch(const char *cmd) { CLIParserInit("hf 14a select", "Tries to select all applets from applet list:\n", - "Usage:\n\thf emv search -s -> select card and search\n\thf emv search -s -t -> select card, search and show result in TLV\n"); + "Usage:\n\thf emv search -s -> select card and search\n\thf emv search -st -> select card, search and show result in TLV\n"); void* argtable[] = { arg_param_begin, @@ -105,74 +105,37 @@ int CmdHFEMVSearch(const char *cmd) { return 0; } -int UsageCmdHFEMVPPSE(void) { - PrintAndLog("HELP : Executes PSE/PPSE select command. It returns list of applet on the card:\n"); - PrintAndLog("Usage: hf emv pse [-s][-k][-1][-2][-a][-t]\n"); - PrintAndLog(" Options:"); - PrintAndLog(" -s : select card"); - PrintAndLog(" -k : keep field for next command"); - PrintAndLog(" -1 : ppse (1PAY.SYS.DDF01)"); - PrintAndLog(" -2 : pse (2PAY.SYS.DDF01)"); - PrintAndLog(" -a : show APDU reqests and responses\n"); - PrintAndLog(" -t : TLV decode results\n"); - PrintAndLog("Samples:"); - PrintAndLog(" hf emv pse -s -1 -> select, get pse"); - PrintAndLog(" hf emv pse -s -k -2 -> select, get ppse, keep field"); - PrintAndLog(" hf emv pse -s -t -2 -> select, get ppse, show result in TLV"); - return 0; -} - int CmdHFEMVPPSE(const char *cmd) { - uint8_t PSENum = 2; - bool activateField = false; - bool leaveSignalON = false; - bool decodeTLV = false; - - if (strlen(cmd) < 1) { - UsageCmdHFEMVPPSE(); - return 0; - } - - SetAPDULogging(false); - int cmdp = 0; - while(param_getchar(cmd, cmdp) != 0x00) { - char c = param_getchar(cmd, cmdp); - if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) - switch (param_getchar_indx(cmd, 1, cmdp)) { - case 'h': - case 'H': - UsageCmdHFEMVPPSE(); - return 0; - case 's': - case 'S': - activateField = true; - break; - case 'k': - case 'K': - leaveSignalON = true; - break; - case 'a': - case 'A': - SetAPDULogging(true); - break; - case 't': - case 'T': - decodeTLV = true; - break; - case '1': - PSENum = 1; - break; - case '2': - PSENum = 2; - break; - default: - PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); - return 1; - } - cmdp++; - } + CLIParserInit("hf 14a pse", + "Executes PSE/PPSE select command. It returns list of applet on the card:\n", + "Usage:\n\thf emv pse -s1 -> select, get pse\n\thf emv pse -st2 -> select, get ppse, show result in TLV\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("sS", "select", "activate field and select card"), + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("1", "pse", "pse (1PAY.SYS.DDF01) mode"), + arg_lit0("2", "ppse", "ppse (2PAY.SYS.DDF01) mode (default mode)"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results of selected applets"), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool activateField = arg_get_lit(1); + bool leaveSignalON = arg_get_lit(2); + uint8_t PSENum = 2; + if (arg_get_lit(3)) + PSENum = 1; + if (arg_get_lit(4)) + PSENum = 2; + bool APDULogging = arg_get_lit(5); + bool decodeTLV = arg_get_lit(6); + CLIParserFree(); + + SetAPDULogging(APDULogging); // exec uint8_t buf[APDU_RES_LEN] = {0}; @@ -193,6 +156,26 @@ int CmdHFEMVPPSE(const char *cmd) { return 0; } +int CmdHFEMVGPO(const char *cmd) { + +} + +int CmdHFEMVReadRecord(const char *cmd) { + +} + +int CmdHFEMVAC(const char *cmd) { + +} + +int CmdHFEMVGenerateChallenge(const char *cmd) { + +} + +int CmdHFEMVInternalAuthenticate(const char *cmd) { + +} + int UsageCmdHFEMVExec(void) { PrintAndLog("HELP : Executes EMV contactless transaction:\n"); PrintAndLog("Usage: hf emv exec [-s][-a][-t][-f][-v][-c][-x][-g]\n"); From 626e650fbfba3cf2fb8adfa0a256466f9a2fd479 Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 11 Sep 2018 17:02:22 +0300 Subject: [PATCH 262/310] added new functions to `hf emv` --- client/emv/cmdemv.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index e621038e..a4018649 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -158,22 +158,27 @@ int CmdHFEMVPPSE(const char *cmd) { int CmdHFEMVGPO(const char *cmd) { + return 0; } int CmdHFEMVReadRecord(const char *cmd) { + return 0; } int CmdHFEMVAC(const char *cmd) { + return 0; } int CmdHFEMVGenerateChallenge(const char *cmd) { + return 0; } int CmdHFEMVInternalAuthenticate(const char *cmd) { + return 0; } int UsageCmdHFEMVExec(void) { @@ -733,12 +738,17 @@ int CmdHFEMVTest(const char *cmd) { int CmdHelp(const char *Cmd); static command_t CommandTable[] = { - {"help", CmdHelp, 1, "This help"}, - {"exec", CmdHFEMVExec, 0, "Executes EMV contactless transaction."}, - {"pse", CmdHFEMVPPSE, 0, "Execute PPSE. It selects 2PAY.SYS.DDF01 or 1PAY.SYS.DDF01 directory."}, - {"search", CmdHFEMVSearch, 0, "Try to select all applets from applets list and print installed applets."}, - {"select", CmdHFEMVSelect, 0, "Select applet."}, - {"test", CmdHFEMVTest, 0, "Crypto logic test."}, + {"help", CmdHelp, 1, "This help"}, + {"exec", CmdHFEMVExec, 0, "Executes EMV contactless transaction."}, + {"pse", CmdHFEMVPPSE, 0, "Execute PPSE. It selects 2PAY.SYS.DDF01 or 1PAY.SYS.DDF01 directory."}, + {"search", CmdHFEMVSearch, 0, "Try to select all applets from applets list and print installed applets."}, + {"select", CmdHFEMVSelect, 0, "Select applet."}, + {"gpo", CmdHFEMVGPO, 0, "Execute GetProcessingOptions."}, + {"readrec", CmdHFEMVReadRecord, 0, "Read files from card."}, + {"genac", CmdHFEMVAC, 0, "Generate ApplicationCryptogram."}, + {"challenge", CmdHFEMVGenerateChallenge, 0, "Generate challenge."}, + {"intauth", CmdHFEMVInternalAuthenticate, 0, "Internal authentication."}, + {"test", CmdHFEMVTest, 0, "Crypto logic test."}, {NULL, NULL, 0, NULL} }; From cd2f1acd1b4e1458422462f9584a06900f8f3719 Mon Sep 17 00:00:00 2001 From: merlokk Date: Wed, 12 Sep 2018 19:51:20 +0300 Subject: [PATCH 263/310] `hf emv gpo` works --- client/emv/cmdemv.c | 130 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 128 insertions(+), 2 deletions(-) diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index a4018649..496b3cb8 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -37,7 +37,7 @@ int CmdHFEMVSelect(const char *cmd) { bool leaveSignalON = arg_get_lit(2); bool APDULogging = arg_get_lit(3); bool decodeTLV = arg_get_lit(4); - CLIGetStrBLessWithReturn(5, data, &datalen, 0); + CLIGetStrWithReturn(5, data, &datalen); CLIParserFree(); SetAPDULogging(APDULogging); @@ -107,7 +107,6 @@ int CmdHFEMVSearch(const char *cmd) { int CmdHFEMVPPSE(const char *cmd) { - CLIParserInit("hf 14a pse", "Executes PSE/PPSE select command. It returns list of applet on the card:\n", "Usage:\n\thf emv pse -s1 -> select, get pse\n\thf emv pse -st2 -> select, get ppse, show result in TLV\n"); @@ -157,11 +156,138 @@ int CmdHFEMVPPSE(const char *cmd) { } int CmdHFEMVGPO(const char *cmd) { + uint8_t data[APDU_RES_LEN] = {0}; + int datalen = 0; + + CLIParserInit("hf 14a gpo", + "Executes Get Processing Options command. It returns data in TLV format (0x77 - format2) or plain (0x80 - format1) formats.\nNeeds a bank applet to be selected.\n", + "Usage:\n\thf emv gpo -k -> select, execute GPO\n\thf emv gpo -st 01020304 -> select, execute GPO with 4-byte PDOL data, show result in TLV\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("pP", "params", "load parameters for PDOL making from `emv/defparams.json` file (by default uses default parameters) (NOT WORK!!!)"), + arg_lit0("mM", "make", "make PDOLdata from PDOL (tag 9F38) and parameters (NOT WORK!!!)"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results of selected applets"), + arg_str0(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool leaveSignalON = arg_get_lit(1); + bool paramsLoadFromFile = arg_get_lit(2); + bool dataMakeFromPDOL = arg_get_lit(3); + bool APDULogging = arg_get_lit(4); + bool decodeTLV = arg_get_lit(5); + CLIGetStrWithReturn(6, data, &datalen); + CLIParserFree(); + + SetAPDULogging(APDULogging); + + // Init TLV tree + const char *alr = "Root terminal TLV tree"; + struct tlvdb *tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr); + + // calc PDOL + struct tlv *pdol_data_tlv = NULL; + struct tlv data_tlv = { + .tag = 0x83, //was 01 + .len = datalen, + .value = (uint8_t *)data, + }; + if (dataMakeFromPDOL) { + // TODO + PrintAndLog("Make PDOL data not implemented!"); + if (paramsLoadFromFile) { + }; +/* pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83); + if (!pdol_data_tlv){ + PrintAndLog("ERROR: can't create PDOL TLV."); + tlvdb_free(tlvRoot); + return 4; + }*/ + return 0; + } else { + pdol_data_tlv = &data_tlv; + } + + size_t pdol_data_tlv_data_len = 0; + unsigned char *pdol_data_tlv_data = tlv_encode(pdol_data_tlv, &pdol_data_tlv_data_len); + if (!pdol_data_tlv_data) { + PrintAndLog("ERROR: can't create PDOL data."); + tlvdb_free(tlvRoot); + return 4; + } + PrintAndLog("PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); + + // exec + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + int res = EMVGPO(leaveSignalON, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); + + free(pdol_data_tlv_data); + tlvdb_free(tlvRoot); + + if (sw) + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + + if (res) + return res; + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); return 0; } int CmdHFEMVReadRecord(const char *cmd) { + uint8_t data[APDU_RES_LEN] = {0}; + int datalen = 0; + + CLIParserInit("hf 14a readrec", + "Executes Read Record command. It returns data in TLV format.\nNeeds a bank applet to be selected and sometimes needs GPO to be executed.\n", + "Usage:\n\thf emv readrec -k -> select, get pse\n\thf emv readrec -st2 -> select, get ppse, show result in TLV\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results of selected applets"), + arg_str0(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool leaveSignalON = arg_get_lit(1); + bool APDULogging = arg_get_lit(2); + bool decodeTLV = arg_get_lit(3); + CLIGetStrWithReturn(4, data, &datalen); + CLIParserFree(); + + if (datalen != 2) { + PrintAndLog("ERROR: Command needs to have 2 bytes of data"); + return 1; + } + + SetAPDULogging(APDULogging); + + // exec + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + int res = EMVReadRecord(leaveSignalON, data[0], data[1], buf, sizeof(buf), &len, &sw, NULL); + + if (sw) + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + + if (res) + return res; + + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); return 0; } From ab2d91f86dbe81405a9a2e8600f326ebcaa9b0f8 Mon Sep 17 00:00:00 2001 From: merlokk Date: Wed, 12 Sep 2018 20:08:16 +0300 Subject: [PATCH 264/310] `hf emv readrec` works and some changes in help --- client/emv/cmdemv.c | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index 496b3cb8..030eaa0b 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -155,13 +155,16 @@ int CmdHFEMVPPSE(const char *cmd) { return 0; } +#define TLV_ADD(tag, value)( tlvdb_add(tlvRoot, tlvdb_fixed(tag, sizeof(value) - 1, (const unsigned char *)value)) ) + int CmdHFEMVGPO(const char *cmd) { uint8_t data[APDU_RES_LEN] = {0}; int datalen = 0; CLIParserInit("hf 14a gpo", - "Executes Get Processing Options command. It returns data in TLV format (0x77 - format2) or plain (0x80 - format1) formats.\nNeeds a bank applet to be selected.\n", - "Usage:\n\thf emv gpo -k -> select, execute GPO\n\thf emv gpo -st 01020304 -> select, execute GPO with 4-byte PDOL data, show result in TLV\n"); + "Executes Get Processing Options command. It returns data in TLV format (0x77 - format2) or plain format (0x80 - format1).\nNeeds a EMV applet to be selected.", + "Usage:\n\thf emv gpo -k -> execute GPO\n\thf emv gpo -st 01020304 -> execute GPO with 4-byte PDOL data, show result in TLV\n"); + // here need to add load params from file and gen pdol void* argtable[] = { arg_param_begin, @@ -199,6 +202,25 @@ int CmdHFEMVGPO(const char *cmd) { if (dataMakeFromPDOL) { // TODO PrintAndLog("Make PDOL data not implemented!"); + + //9F02:(Amount, authorized (Numeric)) len:6 + TLV_ADD(0x9F02, "\x00\x00\x00\x00\x01\x00"); + //9F1A:(Terminal Country Code) len:2 + TLV_ADD(0x9F1A, "ru"); + //5F2A:(Transaction Currency Code) len:2 + // USD 840, EUR 978, RUR 810, RUB 643, RUR 810(old), UAH 980, AZN 031, n/a 999 + TLV_ADD(0x5F2A, "\x09\x80"); + //9A:(Transaction Date) len:3 + TLV_ADD(0x9A, "\x00\x00\x00"); + //9C:(Transaction Type) len:1 | 00 => Goods and service #01 => Cash + TLV_ADD(0x9C, "\x00"); + // 9F37 Unpredictable Number len:4 + TLV_ADD(0x9F37, "\x01\x02\x03\x04"); + // 9F6A Unpredictable Number (MSD for UDOL) len:4 + TLV_ADD(0x9F6A, "\x01\x02\x03\x04"); + //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 + TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC + if (paramsLoadFromFile) { }; /* pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83); @@ -247,15 +269,15 @@ int CmdHFEMVReadRecord(const char *cmd) { int datalen = 0; CLIParserInit("hf 14a readrec", - "Executes Read Record command. It returns data in TLV format.\nNeeds a bank applet to be selected and sometimes needs GPO to be executed.\n", - "Usage:\n\thf emv readrec -k -> select, get pse\n\thf emv readrec -st2 -> select, get ppse, show result in TLV\n"); + "Executes Read Record command. It returns data in TLV format.\nNeeds a bank applet to be selected and sometimes needs GPO to be executed.", + "Usage:\n\thf emv readrec -k 0101 -> read file SFI=01, SFIrec=01\n\thf emv readrec -kt 0201-> read file 0201 and show result in TLV\n"); void* argtable[] = { arg_param_begin, arg_lit0("kK", "keep", "keep field ON for next command"), arg_lit0("aA", "apdu", "show APDU reqests and responses"), arg_lit0("tT", "tlv", "TLV decode results of selected applets"), - arg_str0(NULL, NULL, "", NULL), + arg_str1(NULL, NULL, "", NULL), arg_param_end }; CLIExecWithReturn(cmd, argtable, true); @@ -326,7 +348,6 @@ int UsageCmdHFEMVExec(void) { return 0; } -#define TLV_ADD(tag, value)( tlvdb_add(tlvRoot, tlvdb_fixed(tag, sizeof(value) - 1, (const unsigned char *)value)) ) #define dreturn(n) {free(pdol_data_tlv);tlvdb_free(tlvSelect);tlvdb_free(tlvRoot);DropField();return n;} int CmdHFEMVExec(const char *cmd) { From 8ffdccffa0e7ec45c785495116b77b3f0b85bd49 Mon Sep 17 00:00:00 2001 From: merlokk Date: Wed, 12 Sep 2018 20:20:27 +0300 Subject: [PATCH 265/310] `hf emv challenge` works --- client/emv/cmdemv.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index 030eaa0b..3114c46a 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -321,6 +321,41 @@ int CmdHFEMVAC(const char *cmd) { int CmdHFEMVGenerateChallenge(const char *cmd) { + CLIParserInit("hf 14a challenge", + "Executes Generate Challenge command. It returns 4-byte random number from card:\n", + "Usage:\n\thf emv challenge -> get challenge\n\thf emv challenge -k -> get challenge, keep fileld ON\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool leaveSignalON = arg_get_lit(1); + bool APDULogging = arg_get_lit(2); + CLIParserFree(); + + SetAPDULogging(APDULogging); + + // exec + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + int res = EMVGenerateChallenge(leaveSignalON, buf, sizeof(buf), &len, &sw, NULL); + + if (sw) + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + + if (res) + return res; + + PrintAndLog("Challenge: %s", sprint_hex(buf, len)); + + if (len != 4 && len != 8) + PrintAndLog("WARNING: length of challenge must be 4 or 8, but it %d", len); + return 0; } From ad23886a6f1c5088d4be2eeb97a029ca8db81b70 Mon Sep 17 00:00:00 2001 From: merlokk Date: Wed, 12 Sep 2018 20:22:24 +0300 Subject: [PATCH 266/310] small fix --- client/emv/cmdemv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index 3114c46a..eafea2c5 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -322,7 +322,7 @@ int CmdHFEMVAC(const char *cmd) { int CmdHFEMVGenerateChallenge(const char *cmd) { CLIParserInit("hf 14a challenge", - "Executes Generate Challenge command. It returns 4-byte random number from card:\n", + "Executes Generate Challenge command. It returns 4 or 8-byte random number from card:\n", "Usage:\n\thf emv challenge -> get challenge\n\thf emv challenge -k -> get challenge, keep fileld ON\n"); void* argtable[] = { From 53e2f2fad12bf3a270e19d0baad3c703c6f8ea64 Mon Sep 17 00:00:00 2001 From: grauerfuchs <42082416+grauerfuchs@users.noreply.github.com> Date: Thu, 13 Sep 2018 06:30:26 -0400 Subject: [PATCH 267/310] Correction to Samyrun offline mode update to Samyrun offline mode to support changes to HID ARM-side commands This code has not yet been tested --- armsrc/appmain.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 48620d71..5995df64 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -649,7 +649,7 @@ void SamyRun() StandAloneMode(); FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - int high[OPTS], low[OPTS]; + int tops[OPTS], high[OPTS], low[OPTS]; int selected = 0; int playing = 0; int cardRead = 0; @@ -683,8 +683,11 @@ void SamyRun() /* need this delay to prevent catching some weird data */ SpinDelay(500); - CmdHIDdemodFSK(1, &high[selected], &low[selected], 0); - Dbprintf("Recorded %x %x%08x", selected, high[selected], low[selected]); + CmdHIDdemodFSK(1, &tops[selected], &high[selected], &low[selected], 0); + if (tops[selected] > 0) + Dbprintf("Recorded %x %x%08x%08x", selected, tops[selected], high[selected], low[selected]); + else + Dbprintf("Recorded %x %x%08x", selected, high[selected], low[selected]); LEDsoff(); LED(selected + 1, 0); @@ -705,7 +708,10 @@ void SamyRun() LED(LED_ORANGE, 0); // record - Dbprintf("Cloning %x %x%08x", selected, high[selected], low[selected]); + if (tops[selected] > 0) + Dbprintf("Cloning %x %x%08x%08x", selected, tops[selected], high[selected], low[selected]); + else + Dbprintf("Cloning %x %x%08x", selected, high[selected], low[selected]); // wait for button to be released while(BUTTON_PRESS()) @@ -714,8 +720,11 @@ void SamyRun() /* need this delay to prevent catching some weird data */ SpinDelay(500); - CopyHIDtoT55x7(0, high[selected], low[selected], 0); - Dbprintf("Cloned %x %x%08x", selected, high[selected], low[selected]); + CopyHIDtoT55x7(tops[selected] & 0x000FFFFF, high[selected], low[selected], (tops[selected] != 0 && ((high[selected]& 0xFFFFFFC0) != 0))); + if (tops[selected] > 0) + Dbprintf("Cloned %x %x%08x%08x", selected, tops[selected], high[selected], low[selected]); + else + Dbprintf("Cloned %x %x%08x", selected, high[selected], low[selected]); LEDsoff(); LED(selected + 1, 0); @@ -748,8 +757,12 @@ void SamyRun() // wait for button to be released while(BUTTON_PRESS()) WDT_HIT(); - Dbprintf("%x %x%08x", selected, high[selected], low[selected]); - CmdHIDsimTAG(high[selected], low[selected], 0); + if (tops[selected] > 0) + Dbprintf("%x %x%08x%08x", selected, tops[selected], high[selected], low[selected]); + else + Dbprintf("%x %x%08x", selected, high[selected], low[selected]); + + CmdHIDsimTAG(tops[selected], high[selected], low[selected], 0); DbpString("Done playing"); if (BUTTON_HELD(1000) > 0) { From 11a78e048cf57efefe1ef504af347e34a54a0490 Mon Sep 17 00:00:00 2001 From: merlokk Date: Thu, 13 Sep 2018 18:38:03 +0300 Subject: [PATCH 268/310] added `intauth` and `genac` commands. works. --- client/emv/cmdemv.c | 112 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 107 insertions(+), 5 deletions(-) diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index eafea2c5..1d30fc83 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// Copyright (C) 2017 Merlok +// Copyright (C) 2017, 2018 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 @@ -195,7 +195,7 @@ int CmdHFEMVGPO(const char *cmd) { // calc PDOL struct tlv *pdol_data_tlv = NULL; struct tlv data_tlv = { - .tag = 0x83, //was 01 + .tag = 0x01, .len = datalen, .value = (uint8_t *)data, }; @@ -315,8 +315,67 @@ int CmdHFEMVReadRecord(const char *cmd) { } int CmdHFEMVAC(const char *cmd) { + uint8_t data[APDU_RES_LEN] = {0}; + int datalen = 0; - return 0; + CLIParserInit("hf 14a genac", + "Generate Application Cryptogram command. It returns data in TLV format .\nNeeds a EMV applet to be selected and GPO to be executed.", + "Usage:\n\thf emv genac -k 0102-> execute GPO with 2-byte CDOLdata and keep field ON after command\n\thf emv genac -t 01020304 -> execute GPO with 4-byte CDOL data, show result in TLV\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results of selected applets"), + arg_str1(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, false); + + bool leaveSignalON = arg_get_lit(1); + bool APDULogging = arg_get_lit(2); + bool decodeTLV = arg_get_lit(3); + CLIGetStrWithReturn(4, data, &datalen); + CLIParserFree(); + + SetAPDULogging(APDULogging); + + // Init TLV tree + const char *alr = "Root terminal TLV tree"; + struct tlvdb *tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr); + + // calc CDOL + struct tlv *cdol_data_tlv = NULL; +// struct tlv *cdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x8c, NULL), tlvRoot, 0x01); // 0x01 - dummy tag + struct tlv data_tlv = { + .tag = 0x01, + .len = datalen, + .value = (uint8_t *)data, + }; + cdol_data_tlv = &data_tlv; + PrintAndLog("CDOL data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len)); + + // exec + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; +// EMVAC_TC + EMVAC_CDAREQ --- to get SDAD +// res = EMVAC(true, (TrType == TT_CDA) ? EMVAC_TC + EMVAC_CDAREQ : EMVAC_TC, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); + int res = EMVAC(leaveSignalON, EMVAC_TC, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); + +// free(cdol_data_tlv); + tlvdb_free(tlvRoot); + + if (sw) + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + + if (res) + return res; + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + return 0; } int CmdHFEMVGenerateChallenge(const char *cmd) { @@ -360,8 +419,51 @@ int CmdHFEMVGenerateChallenge(const char *cmd) { } int CmdHFEMVInternalAuthenticate(const char *cmd) { + uint8_t data[APDU_RES_LEN] = {0}; + int datalen = 0; - return 0; + CLIParserInit("hf 14a intauth", + "Generate Internal Authenticate command. Usually needs 4-byte random number. It returns data in TLV format .\nNeeds a EMV applet to be selected and GPO to be executed.", + "Usage:\n\thf emv intauth -k 01020304 -> execute Internal Authenticate with 4-byte DDOLdata and keep field ON after command\n" + "\thf emv intauth -t 01020304 -> execute Internal Authenticate with 4-byte DDOL data, show result in TLV\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results of selected applets"), + arg_str1(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, false); + + bool leaveSignalON = arg_get_lit(1); + bool APDULogging = arg_get_lit(2); + bool decodeTLV = arg_get_lit(3); + CLIGetStrWithReturn(4, data, &datalen); + CLIParserFree(); + + SetAPDULogging(APDULogging); + + // DDOL + PrintAndLog("DDOL data[%d]: %s", datalen, sprint_hex(data, datalen)); + + // exec + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + int res = EMVInternalAuthenticate(leaveSignalON, data, datalen, buf, sizeof(buf), &len, &sw, NULL); + + if (sw) + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + + if (res) + return res; + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + return 0; } int UsageCmdHFEMVExec(void) { @@ -930,7 +1032,7 @@ static command_t CommandTable[] = { {"genac", CmdHFEMVAC, 0, "Generate ApplicationCryptogram."}, {"challenge", CmdHFEMVGenerateChallenge, 0, "Generate challenge."}, {"intauth", CmdHFEMVInternalAuthenticate, 0, "Internal authentication."}, - {"test", CmdHFEMVTest, 0, "Crypto logic test."}, + {"test", CmdHFEMVTest, 0, "Crypto logic test."}, {NULL, NULL, 0, NULL} }; From 48c32f043677bfb614f368617a3255ee976d161d Mon Sep 17 00:00:00 2001 From: merlokk Date: Thu, 13 Sep 2018 18:44:20 +0300 Subject: [PATCH 269/310] added CDA transaction to `hf emv genac` --- client/emv/cmdemv.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index 1d30fc83..bc908574 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -320,11 +320,13 @@ int CmdHFEMVAC(const char *cmd) { CLIParserInit("hf 14a genac", "Generate Application Cryptogram command. It returns data in TLV format .\nNeeds a EMV applet to be selected and GPO to be executed.", - "Usage:\n\thf emv genac -k 0102-> execute GPO with 2-byte CDOLdata and keep field ON after command\n\thf emv genac -t 01020304 -> execute GPO with 4-byte CDOL data, show result in TLV\n"); + "Usage:\n\thf emv genac -k 0102 -> execute GPO with 2-byte CDOLdata and keep field ON after command\n" + "\thf emv genac -t 01020304 -> execute GPO with 4-byte CDOL data, show result in TLV\n"); void* argtable[] = { arg_param_begin, arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("cC", "cda", "executes CDA transaction. Needs to get SDAD in results."), arg_lit0("aA", "apdu", "show APDU reqests and responses"), arg_lit0("tT", "tlv", "TLV decode results of selected applets"), arg_str1(NULL, NULL, "", NULL), @@ -333,9 +335,10 @@ int CmdHFEMVAC(const char *cmd) { CLIExecWithReturn(cmd, argtable, false); bool leaveSignalON = arg_get_lit(1); - bool APDULogging = arg_get_lit(2); - bool decodeTLV = arg_get_lit(3); - CLIGetStrWithReturn(4, data, &datalen); + bool trTypeCDA = arg_get_lit(2); + bool APDULogging = arg_get_lit(3); + bool decodeTLV = arg_get_lit(4); + CLIGetStrWithReturn(5, data, &datalen); CLIParserFree(); SetAPDULogging(APDULogging); @@ -359,9 +362,7 @@ int CmdHFEMVAC(const char *cmd) { uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; uint16_t sw = 0; -// EMVAC_TC + EMVAC_CDAREQ --- to get SDAD -// res = EMVAC(true, (TrType == TT_CDA) ? EMVAC_TC + EMVAC_CDAREQ : EMVAC_TC, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); - int res = EMVAC(leaveSignalON, EMVAC_TC, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); + int res = EMVAC(leaveSignalON, (trTypeCDA) ? EMVAC_TC + EMVAC_CDAREQ : EMVAC_TC, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); // free(cdol_data_tlv); tlvdb_free(tlvRoot); From 02ab5afa8701ecc9434e7d8c30850d0d4ec5903f Mon Sep 17 00:00:00 2001 From: merlokk Date: Thu, 13 Sep 2018 19:53:14 +0300 Subject: [PATCH 270/310] add terminal decision to `genac` and small fixes --- client/cliparser/cliparser.h | 2 +- client/emv/cmdemv.c | 58 ++++++++++++++++++++++++------------ 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/client/cliparser/cliparser.h b/client/cliparser/cliparser.h index 9d83a95f..169f102e 100644 --- a/client/cliparser/cliparser.h +++ b/client/cliparser/cliparser.h @@ -19,7 +19,7 @@ #define arg_get_lit(n)(((struct arg_lit*)argtable[n])->count) #define arg_get_int(n)(((struct arg_int*)argtable[n])->ival[0]) #define arg_get_str(n)((struct arg_str*)argtable[n]) -#define arg_get_str_len(n)(strlen(((struct arg_int*)argtable[n])->ival[0])) +#define arg_get_str_len(n)(strlen(((struct arg_str*)argtable[n])->sval[0])) #define CLIExecWithReturn(cmd, atbl, ifempty) if (CLIParserParseString(cmd, atbl, arg_getsize(atbl), ifempty)){CLIParserFree();return 0;} #define CLIGetStrBLessWithReturn(paramnum, data, datalen, delta) if (CLIParamHexToBuf(arg_get_str(paramnum), data, sizeof(data) - (delta), datalen)) {CLIParserFree();return 1;} diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index bc908574..571e1880 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -18,7 +18,7 @@ int CmdHFEMVSelect(const char *cmd) { int datalen = 0; - CLIParserInit("hf 14a select", + CLIParserInit("hf emv select", "Executes select applet command", "Usage:\n\thf emv select -s a00000000101 -> select card, select applet\n\thf emv select -st a00000000101 -> select card, select applet, show result in TLV\n"); @@ -62,7 +62,7 @@ int CmdHFEMVSelect(const char *cmd) { int CmdHFEMVSearch(const char *cmd) { - CLIParserInit("hf 14a select", + CLIParserInit("hf emv search", "Tries to select all applets from applet list:\n", "Usage:\n\thf emv search -s -> select card and search\n\thf emv search -st -> select card, search and show result in TLV\n"); @@ -107,7 +107,7 @@ int CmdHFEMVSearch(const char *cmd) { int CmdHFEMVPPSE(const char *cmd) { - CLIParserInit("hf 14a pse", + CLIParserInit("hf emv pse", "Executes PSE/PPSE select command. It returns list of applet on the card:\n", "Usage:\n\thf emv pse -s1 -> select, get pse\n\thf emv pse -st2 -> select, get ppse, show result in TLV\n"); @@ -161,7 +161,7 @@ int CmdHFEMVGPO(const char *cmd) { uint8_t data[APDU_RES_LEN] = {0}; int datalen = 0; - CLIParserInit("hf 14a gpo", + CLIParserInit("hf emv gpo", "Executes Get Processing Options command. It returns data in TLV format (0x77 - format2) or plain format (0x80 - format1).\nNeeds a EMV applet to be selected.", "Usage:\n\thf emv gpo -k -> execute GPO\n\thf emv gpo -st 01020304 -> execute GPO with 4-byte PDOL data, show result in TLV\n"); // here need to add load params from file and gen pdol @@ -268,7 +268,7 @@ int CmdHFEMVReadRecord(const char *cmd) { uint8_t data[APDU_RES_LEN] = {0}; int datalen = 0; - CLIParserInit("hf 14a readrec", + CLIParserInit("hf emv readrec", "Executes Read Record command. It returns data in TLV format.\nNeeds a bank applet to be selected and sometimes needs GPO to be executed.", "Usage:\n\thf emv readrec -k 0101 -> read file SFI=01, SFIrec=01\n\thf emv readrec -kt 0201-> read file 0201 and show result in TLV\n"); @@ -318,27 +318,47 @@ int CmdHFEMVAC(const char *cmd) { uint8_t data[APDU_RES_LEN] = {0}; int datalen = 0; - CLIParserInit("hf 14a genac", + CLIParserInit("hf emv genac", "Generate Application Cryptogram command. It returns data in TLV format .\nNeeds a EMV applet to be selected and GPO to be executed.", - "Usage:\n\thf emv genac -k 0102 -> execute GPO with 2-byte CDOLdata and keep field ON after command\n" - "\thf emv genac -t 01020304 -> execute GPO with 4-byte CDOL data, show result in TLV\n"); + "Usage:\n\thf emv genac -k 0102 -> generate AC with 2-byte CDOLdata and keep field ON after command\n" + "\thf emv genac -t 01020304 -> generate AC with 4-byte CDOL data, show result in TLV\n" + "\thf emv genac -Daac 01020304 -> generate AC with 4-byte CDOL data and terminal decision 'declined'\n"); void* argtable[] = { arg_param_begin, - arg_lit0("kK", "keep", "keep field ON for next command"), - arg_lit0("cC", "cda", "executes CDA transaction. Needs to get SDAD in results."), - arg_lit0("aA", "apdu", "show APDU reqests and responses"), - arg_lit0("tT", "tlv", "TLV decode results of selected applets"), - arg_str1(NULL, NULL, "", NULL), + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("cC", "cda", "executes CDA transaction. Needs to get SDAD in results."), + arg_str0("dD", "decision", "", "Terminal decision. aac - declined, tc - approved, arqc - online authorisation requested"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results of selected applets"), + arg_str1(NULL, NULL, "", NULL), arg_param_end }; CLIExecWithReturn(cmd, argtable, false); bool leaveSignalON = arg_get_lit(1); bool trTypeCDA = arg_get_lit(2); - bool APDULogging = arg_get_lit(3); - bool decodeTLV = arg_get_lit(4); - CLIGetStrWithReturn(5, data, &datalen); + uint8_t termDecision = 0xff; + if (arg_get_str_len(3)) { + if (!strncmp(arg_get_str(3)->sval[0], "aac", 4)) + termDecision = EMVAC_AAC; + if (!strncmp(arg_get_str(3)->sval[0], "tc", 4)) + termDecision = EMVAC_TC; + if (!strncmp(arg_get_str(3)->sval[0], "arqc", 4)) + termDecision = EMVAC_ARQC; + + if (termDecision == 0xff) { + PrintAndLog("ERROR: can't find terminal decision '%s'", arg_get_str(3)->sval[0]); + return 1; + } + } else { + termDecision = EMVAC_TC; + } + if (trTypeCDA) + termDecision = termDecision | EMVAC_CDAREQ; + bool APDULogging = arg_get_lit(4); + bool decodeTLV = arg_get_lit(5); + CLIGetStrWithReturn(6, data, &datalen); CLIParserFree(); SetAPDULogging(APDULogging); @@ -362,7 +382,7 @@ int CmdHFEMVAC(const char *cmd) { uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; uint16_t sw = 0; - int res = EMVAC(leaveSignalON, (trTypeCDA) ? EMVAC_TC + EMVAC_CDAREQ : EMVAC_TC, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); + int res = EMVAC(leaveSignalON, termDecision, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); // free(cdol_data_tlv); tlvdb_free(tlvRoot); @@ -381,7 +401,7 @@ int CmdHFEMVAC(const char *cmd) { int CmdHFEMVGenerateChallenge(const char *cmd) { - CLIParserInit("hf 14a challenge", + CLIParserInit("hf emv challenge", "Executes Generate Challenge command. It returns 4 or 8-byte random number from card:\n", "Usage:\n\thf emv challenge -> get challenge\n\thf emv challenge -k -> get challenge, keep fileld ON\n"); @@ -423,7 +443,7 @@ int CmdHFEMVInternalAuthenticate(const char *cmd) { uint8_t data[APDU_RES_LEN] = {0}; int datalen = 0; - CLIParserInit("hf 14a intauth", + CLIParserInit("hf emv intauth", "Generate Internal Authenticate command. Usually needs 4-byte random number. It returns data in TLV format .\nNeeds a EMV applet to be selected and GPO to be executed.", "Usage:\n\thf emv intauth -k 01020304 -> execute Internal Authenticate with 4-byte DDOLdata and keep field ON after command\n" "\thf emv intauth -t 01020304 -> execute Internal Authenticate with 4-byte DDOL data, show result in TLV\n"); From 0c4750c3ba006eebb8862ed5ae5a4a42983056d7 Mon Sep 17 00:00:00 2001 From: merlokk Date: Thu, 13 Sep 2018 19:58:26 +0300 Subject: [PATCH 271/310] small fixes in helps --- client/emv/cmdemv.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index 571e1880..d1ae2d45 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -163,8 +163,9 @@ int CmdHFEMVGPO(const char *cmd) { CLIParserInit("hf emv gpo", "Executes Get Processing Options command. It returns data in TLV format (0x77 - format2) or plain format (0x80 - format1).\nNeeds a EMV applet to be selected.", - "Usage:\n\thf emv gpo -k -> execute GPO\n\thf emv gpo -st 01020304 -> execute GPO with 4-byte PDOL data, show result in TLV\n"); - // here need to add load params from file and gen pdol + "Usage:\n\thf emv gpo -k -> execute GPO\n" + "\thf emv gpo -st 01020304 -> execute GPO with 4-byte PDOL data, show result in TLV\n"); + // here need to add load params from file and gen pdol void* argtable[] = { arg_param_begin, @@ -402,7 +403,7 @@ int CmdHFEMVAC(const char *cmd) { int CmdHFEMVGenerateChallenge(const char *cmd) { CLIParserInit("hf emv challenge", - "Executes Generate Challenge command. It returns 4 or 8-byte random number from card:\n", + "Executes Generate Challenge command. It returns 4 or 8-byte random number from card.\nNeeds a EMV applet to be selected and GPO to be executed.", "Usage:\n\thf emv challenge -> get challenge\n\thf emv challenge -k -> get challenge, keep fileld ON\n"); void* argtable[] = { From 8c2ae2176d35e3896f18b8dfe17257d1529116d5 Mon Sep 17 00:00:00 2001 From: merlokk Date: Thu, 13 Sep 2018 20:03:10 +0300 Subject: [PATCH 272/310] added changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca13dc85..edabfe00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac ### Fixed - Changed start sequence in Qt mode (fix: short commands hangs main Qt thread) (Merlok) - Changed driver file proxmark3.inf to support both old and new Product/Vendor IDs (piwi) +- Changed all command line parsers in `hf emv` commands to argtable (Merlok) ### Added - Added `sc` smartcard (contact card) commands - reader, info, raw, upgrade, setclock, list (hardware version RDV4.0 only) must turn option on in makefile options (Willok, Iceman, marshmellow) @@ -53,6 +54,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added to `hf emv exec` SDA, DDA, fast DDA, CDA calculations for VISA and Mastercard and some other compatible EMV cards (Merlok) - Added `hf emv test` - crypto tests for DES, AES, SHA, RSA, SDA, DDA, CDA and some other crypto functions (Merlok) - Added `hf list mf` - deciphers crypto1 stream and works with first authentication and weak nested authentications (Merlok) +- Added to `hf emv` commands: `gpo`, `readrec`, `genac`, `challenge`, `intauth` - commands working with EMV cards (Merlok) ## [3.0.1][2017-06-08] From fc7a78f2d180b51da57ddeb11570899abcd7191b Mon Sep 17 00:00:00 2001 From: grauerfuchs <42082416+grauerfuchs@users.noreply.github.com> Date: Thu, 13 Sep 2018 21:05:11 -0400 Subject: [PATCH 273/310] Changes requested by @marshmellow42 Added CHANGELOG details for new lf hid commands Restored basic ARM-side decoding of 26-bit and 35-bit cards --- CHANGELOG.md | 5 ++-- armsrc/lfops.c | 65 +++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da6463e9..3b454f64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Changed `hf 14a raw` - works with LED's and some exchange logic (Merlok) - Changed TLV parser messages to more convenient (Merlok) - Rewritten Legic Prime reader (`hf legic reader`, `write` and `fill`) - it is using xcorrelation now (AntiCat) +- HID LF operations on firmware updated for complete native support of long (>37 bit) HID tags (grauerfuchs) ### Fixed - Changed start sequence in Qt mode (fix: short commands hangs main Qt thread) (Merlok) @@ -52,6 +53,8 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added to `hf emv exec` SDA, DDA, fast DDA, CDA calculations for VISA and Mastercard and some other compatible EMV cards (Merlok) - Added `hf emv test` - crypto tests for DES, AES, SHA, RSA, SDA, DDA, CDA and some other crypto functions (Merlok) - Added `hf list mf` - deciphers crypto1 stream and works with first authentication and weak nested authentications (Merlok) +- Added `lf hid encode` and `lf hid decode` commands to translate printed HID card data to and from the packed data transmitted by a prox tag (grauerfuchs) +- Added `lf hid write` command, which operates as a macro for encode followed by clone operations (grauerfuchs) ## [3.0.1][2017-06-08] @@ -229,5 +232,3 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac ### Added - iClass functionality: full simulation of iclass tags, so tags can be simulated with data (not only CSN). Not yet support for write/update, but readers don't seem to enforce update. (holiman). - iClass decryption. Proxmark can now decrypt data on an iclass tag, but requires you to have the HID decryption key locally on your computer, as this is not bundled with the sourcecode. - - diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 7ad4dabe..c0134778 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -876,7 +876,6 @@ void CmdHIDdemodFSK(int findone, int *high2, int *high, int *low, int ledcontrol BigBuf_Clear_keep_EM(); while(!BUTTON_PRESS() && !usb_poll_validate_length()) { - WDT_HIT(); if (ledcontrol) LED_A_ON(); @@ -887,13 +886,67 @@ void CmdHIDdemodFSK(int findone, int *high2, int *high, int *low, int ledcontrol idx = HIDdemodFSK(dest, &size, &hi2, &hi, &lo, &dummyIdx); if (idx>0 && lo>0 && (size==96 || size==192)){ + uint8_t bitlen = 0; + uint32_t fc = 0; + uint32_t cardnum = 0; + bool decoded = false; + // go over previously decoded manchester data and decode into usable tag ID - if (hi2 != 0){ //extra large HID tags 88/192 bits - Dbprintf("TAG ID: %x%08x%08x (%d)", - (unsigned int) hi2, (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF); - } else { //standard HID tags 44/96 bits - Dbprintf("TAG ID: %x%08x (%d)",(unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF); //old print cmd + if ((hi2 & 0x000FFFF) != 0){ //extra large HID tags 88/192 bits + uint32_t bp = hi2 & 0x000FFFFF; + bitlen = 63; + while (bp > 0) { + bp = bp >> 1; + bitlen++; + } + } else if ((hi >> 6) > 0) { + uint32_t bp = hi; + bitlen = 31; + while (bp > 0) { + bp = bp >> 1; + bitlen++; + } + } else if (((hi >> 5) & 1) == 0) { + bitlen = 37; + } else if ((hi & 0x0000001F) > 0 ) { + uint32_t bp = (hi & 0x0000001F); + bitlen = 31; + while (bp > 0) { + bp = bp >> 1; + bitlen++; + } + } else { + uint32_t bp = lo; + bitlen = 0; + while (bp > 0) { + bp = bp >> 1; + bitlen++; + } } + switch (bitlen){ + case 26: + cardnum = (lo>>1)&0xFFFF; + fc = (lo>>17)&0xFF; + decoded = true; + break; + case 35: + cardnum = (lo>>1)&0xFFFFF; + fc = ((hi&1)<<11)|(lo>>21); + decoded = true; + break; + } + + if (hi2 != 0) //extra large HID tags 88/192 bits + Dbprintf("TAG ID: %x%08x%08x (%d)", + (unsigned int) hi2, (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF); + else + Dbprintf("TAG ID: %x%08x (%d)", + (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF); + + if (decoded) + Dbprintf("Format Len: %dbits - FC: %d - Card: %d", + (unsigned int) bitlen, (unsigned int) fc, (unsigned int) cardnum); + if (findone){ if (ledcontrol) LED_A_OFF(); *high2 = hi2; From 0d7ee55f9d217aff9944654c4bc1b38a9ee32b6d Mon Sep 17 00:00:00 2001 From: grauerfuchs <42082416+grauerfuchs@users.noreply.github.com> Date: Fri, 14 Sep 2018 06:32:29 -0400 Subject: [PATCH 274/310] Adding new Kastle 32-bit format from @xilni As approved by @0xFFFF on RfidResearchGroup/proxmark3 and provided by @xilni, this commit adds the format into the official repo as well. As I don't have any of the cards available for testing, I have been unable to personally verify the format encodes/decodes as intended. --- client/hidcardformats.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/client/hidcardformats.c b/client/hidcardformats.c index 8df5bdde..11d0558e 100644 --- a/client/hidcardformats.c +++ b/client/hidcardformats.c @@ -140,6 +140,34 @@ bool Unpack_ADT31(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ return true; } +bool Pack_Kastle(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_t)); + if (card->FacilityCode > 0x00FF) return false; // Can't encode FC. + if (card->CardNumber > 0x0000FFFF) return false; // Can't encode CN. + if (card->IssueLevel > 0x001F) return false; // IL is only 5 bits. + if (card->OEM > 0) return false; // Not used in this format + packed->Length = 32; // Set number of bits + set_bit_by_position(packed, 1, 1); // Always 1 + set_linear_field(packed, card->IssueLevel, 2, 5); + set_linear_field(packed, card->FacilityCode, 7, 8); + set_linear_field(packed, card->CardNumber, 15, 16); + set_bit_by_position(packed, evenparity32(get_linear_field(packed, 1, 16)), 0); + set_bit_by_position(packed, oddparity32(get_linear_field(packed, 14, 17)), 31); + return add_HID_header(packed); +} +bool Unpack_Kastle(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 32) return false; // Wrong length? Stop here. + if (get_bit_by_position(packed, 1) != 1) return false; // Always 1 in this format + card->IssueLevel = get_linear_field(packed, 2, 5); + card->FacilityCode = get_linear_field(packed, 7, 8); + card->CardNumber = get_linear_field(packed, 15, 16); + card->ParityValid = + (get_bit_by_position(packed, 0) == evenparity32(get_linear_field(packed, 1, 16))) && + (get_bit_by_position(packed, 31) == oddparity32(get_linear_field(packed, 14, 17))); + return true; +} + bool Pack_D10202(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ memset(packed, 0, sizeof(hidproxmessage_t)); if (card->FacilityCode > 0x007F) return false; // Can't encode FC. @@ -485,6 +513,7 @@ static const hidcardformat_t FormatTable[] = { {"2804W", Pack_2804W, Unpack_2804W, "2804 Wiegand", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au {"ATSW30", Pack_ATSW30, Unpack_ATSW30, "ATS Wiegand 30-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au {"ADT31", Pack_ADT31, Unpack_ADT31, "HID ADT 31-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au + {"Kastle", Pack_Kastle, Unpack_Kastle, "Kastle 32-bit", {1, 1, 1, 0, 1}}, // from @xilni; PR #23 on RfidResearchGroup/proxmark3 {"D10202", Pack_D10202, Unpack_D10202, "HID D10202 33-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au {"H10306", Pack_H10306, Unpack_H10306, "HID H10306 34-bit", {1, 1, 0, 0, 1}}, // imported from old pack/unpack {"N10002", Pack_N10002, Unpack_N10002, "HID N10002 34-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au From 6a5d4e17f4afe29f84308fe5ca78782233d7b611 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Sun, 16 Sep 2018 00:53:28 +0200 Subject: [PATCH 275/310] rework iso14443b device functions including FPGA I/Q signal transfer (#669) * rework iso14443b device functions * hf_read_rx_xcorr.v: transfer i/q pair in one 16bit frame * hi_read_tx.v: invert ssp_dout. When nothing is transferred (ssp_dout=0), this results in no modulation (carrier on) * adjust arm sources accordingly * iso14443b.c: switch off carrier after hf 14b sri512read and hf 14b srix4kread * iso14443b.c: fix DMA circular buffer handling --- armsrc/fpgaloader.c | 29 +++-- armsrc/fpgaloader.h | 2 +- armsrc/hfsnoop.c | 2 +- armsrc/iclass.c | 10 +- armsrc/iso14443a.c | 2 +- armsrc/iso14443b.c | 282 ++++++++++++++++------------------------ armsrc/iso15693.c | 151 +++++++-------------- armsrc/legicrf.c | 39 +++--- armsrc/legicrfsim.c | 2 +- armsrc/lfops.c | 2 +- armsrc/lfsampling.c | 2 +- fpga/fpga_hf.bit | Bin 42175 -> 42175 bytes fpga/hi_read_rx_xcorr.v | 6 +- fpga/hi_read_tx.v | 4 +- 14 files changed, 212 insertions(+), 321 deletions(-) diff --git a/armsrc/fpgaloader.c b/armsrc/fpgaloader.c index 77223bd0..a132b762 100644 --- a/armsrc/fpgaloader.c +++ b/armsrc/fpgaloader.c @@ -112,10 +112,10 @@ void SetupSpi(int mode) } //----------------------------------------------------------------------------- -// Set up the synchronous serial port, with the one set of options that we -// always use when we are talking to the FPGA. Both RX and TX are enabled. +// Set up the synchronous serial port with the set of options that fits +// the FPGA mode. Both RX and TX are always enabled. //----------------------------------------------------------------------------- -void FpgaSetupSsc(void) +void FpgaSetupSsc(uint8_t FPGA_mode) { // First configure the GPIOs, and get ourselves a clock. AT91C_BASE_PIOA->PIO_ASR = @@ -134,11 +134,15 @@ void FpgaSetupSsc(void) // on RX clock rising edge, sampled on falling edge AT91C_BASE_SSC->SSC_RCMR = SSC_CLOCK_MODE_SELECT(1) | SSC_CLOCK_MODE_START(1); - // 8 bits per transfer, no loopback, MSB first, 1 transfer per sync + // 8, 16 or 32 bits per transfer, no loopback, MSB first, 1 transfer per sync // pulse, no output sync - AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); + if ((FPGA_mode & 0xe0) == FPGA_MAJOR_MODE_HF_READER_RX_XCORR) { + AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(16) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); + } else { + AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); + } - // clock comes from TK pin, no clock output, outputs change on falling + // TX clock comes from TK pin, no clock output, outputs change on falling // edge of TK, sample on rising edge of TK, start on positive-going edge of sync AT91C_BASE_SSC->SSC_TCMR = SSC_CLOCK_MODE_SELECT(2) | SSC_CLOCK_MODE_START(5); @@ -154,16 +158,15 @@ void FpgaSetupSsc(void) // ourselves, not to another buffer). The stuff to manipulate those buffers // is in apps.h, because it should be inlined, for speed. //----------------------------------------------------------------------------- -bool FpgaSetupSscDma(uint8_t *buf, int len) +bool FpgaSetupSscDma(uint8_t *buf, uint16_t sample_count) { if (buf == NULL) return false; - AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; // Disable DMA Transfer - AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) buf; // transfer to this memory address - AT91C_BASE_PDC_SSC->PDC_RCR = len; // transfer this many bytes - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) buf; // next transfer to same memory address - AT91C_BASE_PDC_SSC->PDC_RNCR = len; // ... with same number of bytes - AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTEN; // go! + AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; // Disable DMA Transfer + AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) buf; // transfer to this memory address + AT91C_BASE_PDC_SSC->PDC_RCR = sample_count; // transfer this many samples + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) buf; // next transfer to same memory address + AT91C_BASE_PDC_SSC->PDC_RNCR = sample_count; // ... with same number of samples AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTEN; // go! return true; } diff --git a/armsrc/fpgaloader.h b/armsrc/fpgaloader.h index fa16771d..f75dfc81 100644 --- a/armsrc/fpgaloader.h +++ b/armsrc/fpgaloader.h @@ -19,7 +19,7 @@ void FpgaSendCommand(uint16_t cmd, uint16_t v); void FpgaWriteConfWord(uint8_t v); void FpgaDownloadAndGo(int bitstream_version); -void FpgaSetupSsc(void); +void FpgaSetupSsc(uint8_t mode); void SetupSpi(int mode); bool FpgaSetupSscDma(uint8_t *buf, int len); void Fpga_print_status(); diff --git a/armsrc/hfsnoop.c b/armsrc/hfsnoop.c index d06af443..e492c474 100644 --- a/armsrc/hfsnoop.c +++ b/armsrc/hfsnoop.c @@ -37,7 +37,7 @@ void HfSnoop(int samplesToSkip, int triggersToSkip) // Select correct configs FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Set up the synchronous serial port - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SNOOP); // connect Demodulated Signal to ADC: SetAdcMuxFor(GPIO_MUXSEL_HIPKD); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SNOOP); diff --git a/armsrc/iclass.c b/armsrc/iclass.c index c587f8ea..1591a062 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -676,7 +676,7 @@ void RAMFUNC SnoopIClass(void) Demod.state = DEMOD_UNSYNCD; // Setup for the DMA. - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_ISO14443A); upTo = dmaBuf; lastRxCounter = DMA_BUFFER_SIZE; FpgaSetupSscDma((uint8_t *)dmaBuf, DMA_BUFFER_SIZE); @@ -1163,7 +1163,7 @@ int doIClassSimulation( int simulationMode, uint8_t *reader_mac_buf) StartCountSspClk(); // We need to listen to the high-frequency, peak-detected path. SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_ISO14443A); // To control where we are in the protocol int cmdsRecvd = 0; @@ -1360,7 +1360,7 @@ static int SendIClassAnswer(uint8_t *resp, int respLen, int delay) FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR|FPGA_HF_SIMULATOR_MODULATE_424K_8BIT); AT91C_BASE_SSC->SSC_THR = 0x00; - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); while(!BUTTON_PRESS()) { if((AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)){ b = AT91C_BASE_SSC->SSC_RHR; (void) b; @@ -1398,7 +1398,7 @@ static void TransmitIClassCommand(const uint8_t *cmd, int len, int *samples, int int c; FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); AT91C_BASE_SSC->SSC_THR = 0x00; - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_ISO14443A); if (wait) { @@ -1586,7 +1586,7 @@ void setupIclassReader() clear_trace(); // Setup SSC - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_ISO14443A); // Start from off (no field generated) // Signal field is off with the appropriate LED LED_D_OFF(); diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index a8273e5e..0cacaed9 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1889,7 +1889,7 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u void iso14443a_setup(uint8_t fpga_minor_mode) { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Set up the synchronous serial port - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_ISO14443A); // connect Demodulated Signal to ADC: SetAdcMuxFor(GPIO_MUXSEL_HIPKD); diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index 75769859..cb8567fa 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -1,5 +1,6 @@ //----------------------------------------------------------------------------- // Jonathan Westhues, split Nov 2006 +// piwi 2018 // // 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 @@ -16,8 +17,8 @@ #include "iso14443crc.h" -#define RECEIVE_SAMPLES_TIMEOUT 2000 -#define ISO14443B_DMA_BUFFER_SIZE 256 +#define RECEIVE_SAMPLES_TIMEOUT 1000 // TR0 max is 256/fs = 256/(848kHz) = 302us or 64 samples from FPGA. 1000 seems to be much too high? +#define ISO14443B_DMA_BUFFER_SIZE 128 // PCB Block number for APDUs static uint8_t pcb_blocknum = 0; @@ -374,7 +375,7 @@ void SimulateIso14443bTag(void) // We need to listen to the high-frequency, peak-detected path. SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); cmdsRecvd = 0; @@ -440,7 +441,7 @@ void SimulateIso14443bTag(void) LED_D_OFF(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_BPSK); AT91C_BASE_SSC->SSC_THR = 0xff; - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); // Transmit the response. uint16_t i = 0; @@ -535,60 +536,11 @@ static RAMFUNC int Handle14443bSamplesDemod(int ci, int cq) #define SUBCARRIER_DETECT_THRESHOLD 8 -// Subcarrier amplitude v = sqrt(ci^2 + cq^2), approximated here by abs(ci) + abs(cq) -/* #define CHECK_FOR_SUBCARRIER() { \ - v = ci; \ - if(v < 0) v = -v; \ - if(cq > 0) { \ - v += cq; \ - } else { \ - v -= cq; \ - } \ - } - */ // Subcarrier amplitude v = sqrt(ci^2 + cq^2), approximated here by max(abs(ci),abs(cq)) + 1/2*min(abs(ci),abs(cq))) - - //note: couldn't we just use MAX(ABS(ci),ABS(cq)) + (MIN(ABS(ci),ABS(cq))/2) from common.h - marshmellow -#define CHECK_FOR_SUBCARRIER() { \ - v = MAX(ABS(ci),ABS(cq)) + (MIN(ABS(ci),ABS(cq))/2); \ - } - /* - if(ci < 0) { \ - if(cq < 0) { \ // ci < 0, cq < 0 - if (cq < ci) { \ - v = -cq - (ci >> 1); \ - } else { \ - v = -ci - (cq >> 1); \ - } \ - } else { \ // ci < 0, cq >= 0 - if (cq < -ci) { \ - v = -ci + (cq >> 1); \ - } else { \ - v = cq - (ci >> 1); \ - } \ - } \ - } else { \ - if(cq < 0) { \ // ci >= 0, cq < 0 - if (-cq < ci) { \ - v = ci - (cq >> 1); \ - } else { \ - v = -cq + (ci >> 1); \ - } \ - } else { \ // ci >= 0, cq >= 0 - if (cq < ci) { \ - v = ci + (cq >> 1); \ - } else { \ - v = cq + (ci >> 1); \ - } \ - } \ - } \ - } - */ - +#define AMPLITUDE(ci,cq) (MAX(ABS(ci),ABS(cq)) + (MIN(ABS(ci),ABS(cq))/2)) switch(Demod.state) { case DEMOD_UNSYNCD: - CHECK_FOR_SUBCARRIER(); - if(v > SUBCARRIER_DETECT_THRESHOLD) { // subcarrier detected + if(AMPLITUDE(ci,cq) > SUBCARRIER_DETECT_THRESHOLD) { // subcarrier detected Demod.state = DEMOD_PHASE_REF_TRAINING; Demod.sumI = ci; Demod.sumQ = cq; @@ -598,8 +550,7 @@ static RAMFUNC int Handle14443bSamplesDemod(int ci, int cq) case DEMOD_PHASE_REF_TRAINING: if(Demod.posCount < 8) { - CHECK_FOR_SUBCARRIER(); - if (v > SUBCARRIER_DETECT_THRESHOLD) { + if (AMPLITUDE(ci,cq) > SUBCARRIER_DETECT_THRESHOLD) { // set the reference phase (will code a logic '1') by averaging over 32 1/fs. // note: synchronization time > 80 1/fs Demod.sumI += ci; @@ -743,10 +694,11 @@ static void DemodInit(uint8_t *data) */ static void GetSamplesFor14443bDemod(int n, bool quiet) { - int max = 0; + int maxBehindBy = 0; bool gotFrame = false; - int lastRxCounter, ci, cq, samples = 0; - + int lastRxCounter, samples = 0; + int8_t ci, cq; + // Allocate memory from BigBuf for some buffers // free all previous allocations first BigBuf_free(); @@ -755,15 +707,19 @@ static void GetSamplesFor14443bDemod(int n, bool quiet) uint8_t *receivedResponse = BigBuf_malloc(MAX_FRAME_SIZE); // The DMA buffer, used to stream samples from the FPGA - int8_t *dmaBuf = (int8_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE); + uint16_t *dmaBuf = (uint16_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE * sizeof(uint16_t)); // Set up the demodulator for tag -> reader responses. DemodInit(receivedResponse); + // wait for last transfer to complete + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)) + // Setup and start DMA. + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); FpgaSetupSscDma((uint8_t*) dmaBuf, ISO14443B_DMA_BUFFER_SIZE); - int8_t *upTo = dmaBuf; + uint16_t *upTo = dmaBuf; lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; // Signal field is ON with the appropriate LED: @@ -772,39 +728,40 @@ static void GetSamplesFor14443bDemod(int n, bool quiet) FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ); for(;;) { - int behindBy = lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR; - if(behindBy > max) max = behindBy; - - while(((lastRxCounter-AT91C_BASE_PDC_SSC->PDC_RCR) & (ISO14443B_DMA_BUFFER_SIZE-1)) > 2) { - ci = upTo[0]; - cq = upTo[1]; - upTo += 2; - if(upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { - upTo = dmaBuf; - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) upTo; - AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; - } - lastRxCounter -= 2; - if(lastRxCounter <= 0) { - lastRxCounter += ISO14443B_DMA_BUFFER_SIZE; - } - - samples += 2; - - if(Handle14443bSamplesDemod(ci, cq)) { - gotFrame = true; - break; - } + int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & (ISO14443B_DMA_BUFFER_SIZE-1); + if(behindBy > maxBehindBy) { + maxBehindBy = behindBy; } - if(samples > n || gotFrame) { + if(behindBy < 1) continue; + + ci = *upTo >> 8; + cq = *upTo; + upTo++; + lastRxCounter--; + if(upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + upTo = dmaBuf; // start reading the circular buffer from the beginning + lastRxCounter += ISO14443B_DMA_BUFFER_SIZE; + } + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and + AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; // DMA Next Counter registers + } + samples++; + + if(Handle14443bSamplesDemod(ci, cq)) { + gotFrame = true; + break; + } + + if(samples > n) { break; } } - AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; + FpgaDisableSscDma(); - if (!quiet) Dbprintf("max behindby = %d, samples = %d, gotFrame = %d, Demod.len = %d, Demod.sumI = %d, Demod.sumQ = %d", max, samples, gotFrame, Demod.len, Demod.sumI, Demod.sumQ); + if (!quiet) Dbprintf("max behindby = %d, samples = %d, gotFrame = %d, Demod.len = %d, Demod.sumI = %d, Demod.sumQ = %d", maxBehindBy, samples, gotFrame, Demod.len, Demod.sumI, Demod.sumQ); //Tracing if (tracing && Demod.len > 0) { uint8_t parity[MAX_PARITY_SIZE]; @@ -820,11 +777,7 @@ static void TransmitFor14443b(void) { int c; - FpgaSetupSsc(); - - while(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0xff; - } + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_TX); // Signal field is ON with the appropriate Red LED LED_D_ON(); @@ -832,31 +785,15 @@ static void TransmitFor14443b(void) LED_B_ON(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); - for(c = 0; c < 10;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0xff; - c++; - } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; - (void)r; - } - WDT_HIT(); - } - c = 0; for(;;) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = ToSend[c]; + AT91C_BASE_SSC->SSC_THR = ~ToSend[c]; c++; if(c >= ToSendMax) { break; } } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; - (void)r; - } WDT_HIT(); } LED_B_OFF(); // Finished sending @@ -874,19 +811,14 @@ static void CodeIso14443bAsReader(const uint8_t *cmd, int len) ToSendReset(); - // Establish initial reference level - for(i = 0; i < 40; i++) { - ToSendStuffBit(1); - } // Send SOF for(i = 0; i < 10; i++) { ToSendStuffBit(0); } + ToSendStuffBit(1); + ToSendStuffBit(1); for(i = 0; i < len; i++) { - // Stop bits/EGT - ToSendStuffBit(1); - ToSendStuffBit(1); // Start bit ToSendStuffBit(0); // Data bits @@ -899,19 +831,18 @@ static void CodeIso14443bAsReader(const uint8_t *cmd, int len) } b >>= 1; } - } - // Send EOF - ToSendStuffBit(1); - for(i = 0; i < 10; i++) { - ToSendStuffBit(0); - } - for(i = 0; i < 8; i++) { + // Stop bit ToSendStuffBit(1); } - // And then a little more, to make sure that the last character makes - // it out before we switch to rx mode. - for(i = 0; i < 24; i++) { + // Send EOF + for(i = 0; i < 10; i++) { + ToSendStuffBit(0); + } + ToSendStuffBit(1); + + // ensure that last byte is filled up + for(i = 0; i < 8; i++) { ToSendStuffBit(1); } @@ -951,7 +882,7 @@ int iso14443b_apdu(uint8_t const *message, size_t message_length, uint8_t *respo // send CodeAndTransmit14443bAsReader(message_frame, message_length + 4); // get response - GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT*100, true); + GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, true); if(Demod.len < 3) { return 0; @@ -1011,7 +942,7 @@ int iso14443b_select_card() void iso14443b_setup() { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Set up the synchronous serial port - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_TX); // connect Demodulated Signal to ADC: SetAdcMuxFor(GPIO_MUXSEL_HIPKD); @@ -1019,9 +950,6 @@ void iso14443b_setup() { LED_D_ON(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); - // Start the timer - StartCountSspClk(); - DemodReset(); UartReset(); } @@ -1047,7 +975,7 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) SpinDelay(200); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); // Now give it time to spin up. // Signal field is on with the appropriate LED @@ -1065,6 +993,8 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) if (Demod.len == 0) { DbpString("No response from tag"); + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); return; } else { Dbprintf("Randomly generated Chip ID (+ 2 byte CRC): %02x %02x %02x", @@ -1080,17 +1010,23 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, true); if (Demod.len != 3) { Dbprintf("Expected 3 bytes from tag, got %d", Demod.len); + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); return; } // Check the CRC of the answer: ComputeCrc14443(CRC_14443_B, Demod.output, 1 , &cmd1[2], &cmd1[3]); if(cmd1[2] != Demod.output[1] || cmd1[3] != Demod.output[2]) { DbpString("CRC Error reading select response."); + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); return; } // Check response from the tag: should be the same UID as the command we just sent: if (cmd1[1] != Demod.output[0]) { Dbprintf("Bad response to SELECT from Tag, aborting: %02x %02x", cmd1[1], Demod.output[0]); + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); return; } @@ -1102,6 +1038,8 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, true); if (Demod.len != 10) { Dbprintf("Expected 10 bytes from tag, got %d", Demod.len); + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); return; } // The check the CRC of the answer (use cmd1 as temporary variable): @@ -1131,6 +1069,8 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, true); if (Demod.len != 6) { // Check if we got an answer from the tag DbpString("Expected 6 bytes from tag, got less..."); + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); return; } // The check the CRC of the answer (use cmd1 as temporary variable): @@ -1149,6 +1089,9 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) } i++; } + + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); } @@ -1171,11 +1114,6 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) */ void RAMFUNC SnoopIso14443b(void) { - // We won't start recording the frames that we acquire until we trigger; - // a good trigger condition to get started is probably when we see a - // response from the tag. - int triggered = true; // TODO: set and evaluate trigger condition - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); BigBuf_free(); @@ -1183,10 +1121,10 @@ void RAMFUNC SnoopIso14443b(void) set_tracing(true); // The DMA buffer, used to stream samples from the FPGA - int8_t *dmaBuf = (int8_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE); + uint16_t *dmaBuf = (uint16_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE * sizeof(uint16_t)); int lastRxCounter; - int8_t *upTo; - int ci, cq; + uint16_t *upTo; + int8_t ci, cq; int maxBehindBy = 0; // Count of samples received so far, so that we can include timing @@ -1211,7 +1149,7 @@ void RAMFUNC SnoopIso14443b(void) SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // Setup for the DMA. - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); upTo = dmaBuf; lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; FpgaSetupSscDma((uint8_t*) dmaBuf, ISO14443B_DMA_BUFFER_SIZE); @@ -1219,46 +1157,48 @@ void RAMFUNC SnoopIso14443b(void) bool TagIsActive = false; bool ReaderIsActive = false; + // We won't start recording the frames that we acquire until we trigger. + // A good trigger condition to get started is probably when we see a + // reader command + bool triggered = false; // And now we loop, receiving samples. for(;;) { - int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & - (ISO14443B_DMA_BUFFER_SIZE-1); + int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & (ISO14443B_DMA_BUFFER_SIZE-1); if(behindBy > maxBehindBy) { maxBehindBy = behindBy; } - if(behindBy < 2) continue; + if(behindBy < 1) continue; - ci = upTo[0]; - cq = upTo[1]; - upTo += 2; - lastRxCounter -= 2; - if(upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { - upTo = dmaBuf; + ci = *upTo>>8; + cq = *upTo; + upTo++; + lastRxCounter--; + if(upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + upTo = dmaBuf; // start reading the circular buffer from the beginning again lastRxCounter += ISO14443B_DMA_BUFFER_SIZE; - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; - AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; + if(behindBy > (9*ISO14443B_DMA_BUFFER_SIZE/10)) { + Dbprintf("About to blow circular buffer - aborted! behindBy=%d", behindBy); + break; + } + } + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and + AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; // DMA Next Counter registers WDT_HIT(); - if(behindBy > (9*ISO14443B_DMA_BUFFER_SIZE/10)) { // TODO: understand whether we can increase/decrease as we want or not? - Dbprintf("blew circular buffer! behindBy=%d", behindBy); - break; - } - if(!tracing) { - DbpString("Reached trace limit"); - break; - } if(BUTTON_PRESS()) { DbpString("cancelled"); break; } } - samples += 2; + samples++; if (!TagIsActive) { // no need to try decoding reader data if the tag is sending if(Handle14443bUartBit(ci & 0x01)) { - if(triggered && tracing) { + triggered = true; + if(tracing) { LogTrace(Uart.output, Uart.byteCnt, samples, samples, parity, true); } /* And ready to receive another command. */ @@ -1268,7 +1208,8 @@ void RAMFUNC SnoopIso14443b(void) DemodReset(); } if(Handle14443bUartBit(cq & 0x01)) { - if(triggered && tracing) { + triggered = true; + if(tracing) { LogTrace(Uart.output, Uart.byteCnt, samples, samples, parity, true); } /* And ready to receive another command. */ @@ -1280,8 +1221,8 @@ void RAMFUNC SnoopIso14443b(void) ReaderIsActive = (Uart.state > STATE_GOT_FALLING_EDGE_OF_SOF); } - if(!ReaderIsActive) { // no need to try decoding tag data if the reader is sending - and we cannot afford the time - if(Handle14443bSamplesDemod(ci | 0x01, cq | 0x01)) { + if(!ReaderIsActive && triggered) { // no need to try decoding tag data if the reader is sending or not yet triggered + if(Handle14443bSamplesDemod(ci/2, cq/2)) { //Use samples as a time measurement if(tracing) @@ -1289,8 +1230,6 @@ void RAMFUNC SnoopIso14443b(void) uint8_t parity[MAX_PARITY_SIZE]; LogTrace(Demod.output, Demod.len, samples, samples, parity, false); } - triggered = true; - // And ready to receive another response. DemodReset(); } @@ -1301,7 +1240,6 @@ void RAMFUNC SnoopIso14443b(void) FpgaDisableSscDma(); LEDsoff(); - AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; DbpString("Snoop statistics:"); Dbprintf(" Max behind by: %i", maxBehindBy); Dbprintf(" Uart State: %x", Uart.state); @@ -1327,7 +1265,11 @@ void SendRawCommand14443B(uint32_t datalen, uint32_t recv, uint8_t powerfield, u { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); + + // switch field on and give tag some time to power up + LED_D_ON(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_TX); + SpinDelay(10); if (datalen){ set_tracing(true); diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index ad6f5cfc..c092b383 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -227,26 +227,14 @@ static void TransmitTo15693Tag(const uint8_t *cmd, int len, int *samples, int *w { int c; -// FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_TX); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); if(*wait < 10) { *wait = 10; } -// for(c = 0; c < *wait;) { -// if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { -// AT91C_BASE_SSC->SSC_THR = 0x00; // For exact timing! -// c++; -// } -// if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { -// volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; -// (void)r; -// } -// WDT_HIT(); -// } - c = 0; for(;;) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = cmd[c]; + AT91C_BASE_SSC->SSC_THR = ~cmd[c]; c++; if(c >= len) { break; @@ -300,36 +288,25 @@ static int GetIso15693AnswerFromTag(uint8_t *receivedResponse, int maxLen, int * { int c = 0; uint8_t *dest = BigBuf_get_addr(); - int getNext = 0; - - int8_t prev = 0; // NOW READ RESPONSE + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); c = 0; - getNext = false; for(;;) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - int8_t b; - b = (int8_t)AT91C_BASE_SSC->SSC_RHR; - + uint16_t iq = AT91C_BASE_SSC->SSC_RHR; // The samples are correlations against I and Q versions of the - // tone that the tag AM-modulates, so every other sample is I, - // every other is Q. We just want power, so abs(I) + abs(Q) is - // close to what we want. - if(getNext) { - uint8_t r = AMPLITUDE(b, prev); + // tone that the tag AM-modulates. We just want power. + int8_t i = iq >> 8; + int8_t q = iq; + uint8_t r = AMPLITUDE(i, q); - dest[c++] = r; - - if(c >= 4000) { - break; - } - } else { - prev = b; + dest[c++] = r; + + if(c >= 4000) { + break; } - - getNext = !getNext; } } @@ -441,36 +418,26 @@ static int GetIso15693AnswerFromSniff(uint8_t *receivedResponse, int maxLen, int { int c = 0; uint8_t *dest = BigBuf_get_addr(); - int getNext = 0; - - int8_t prev = 0; // NOW READ RESPONSE FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); //spindelay(60); // greg - experiment to get rid of some of the 0 byte/failed reads c = 0; - getNext = false; for(;;) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - int8_t b = (int8_t)AT91C_BASE_SSC->SSC_RHR; - + uint16_t iq = AT91C_BASE_SSC->SSC_RHR; // The samples are correlations against I and Q versions of the - // tone that the tag AM-modulates, so every other sample is I, - // every other is Q. We just want power, so abs(I) + abs(Q) is - // close to what we want. - if(getNext) { - uint8_t r = AMPLITUDE(b, prev); + // tone that the tag AM-modulates. We just want power, + // so abs(I) + abs(Q) is close to what we want. + int8_t i = iq >> 8; + int8_t q = iq; + uint8_t r = AMPLITUDE(i, q); - dest[c++] = r; + dest[c++] = r; - if(c >= BIGBUF_SIZE) { - break; - } - } else { - prev = b; + if(c >= BIGBUF_SIZE) { + break; } - - getNext = !getNext; } } @@ -585,8 +552,6 @@ void AcquireRawAdcSamplesIso15693(void) uint8_t *dest = BigBuf_get_addr(); int c = 0; - int getNext = 0; - int8_t prev = 0; FpgaDownloadAndGo(FPGA_BITSTREAM_HF); BuildIdentifyRequest(); @@ -598,13 +563,13 @@ void AcquireRawAdcSamplesIso15693(void) SpinDelay(100); // Now send the command - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_TX); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); c = 0; for(;;) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = ToSend[c]; + AT91C_BASE_SSC->SSC_THR = ~ToSend[c]; c++; if(c == ToSendMax+3) { break; @@ -613,32 +578,25 @@ void AcquireRawAdcSamplesIso15693(void) WDT_HIT(); } + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); c = 0; - getNext = false; for(;;) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - int8_t b; - b = (int8_t)AT91C_BASE_SSC->SSC_RHR; - + uint16_t iq = AT91C_BASE_SSC->SSC_RHR; // The samples are correlations against I and Q versions of the - // tone that the tag AM-modulates, so every other sample is I, - // every other is Q. We just want power, so abs(I) + abs(Q) is - // close to what we want. - if(getNext) { - uint8_t r = AMPLITUDE(b, prev); + // tone that the tag AM-modulates. We just want power, + // so abs(I) + abs(Q) is close to what we want. + int8_t i = iq >> 8; + int8_t q = iq; + uint8_t r = AMPLITUDE(i, q); - dest[c++] = r; + dest[c++] = r; - if(c >= 4000) { - break; - } - } else { - prev = b; + if(c >= 4000) { + break; } - - getNext = !getNext; } } } @@ -649,16 +607,14 @@ void RecordRawAdcSamplesIso15693(void) uint8_t *dest = BigBuf_get_addr(); int c = 0; - int getNext = 0; - int8_t prev = 0; FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Setup SSC - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); // Start from off (no field generated) - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(200); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + SpinDelay(200); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); @@ -667,33 +623,24 @@ void RecordRawAdcSamplesIso15693(void) FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); c = 0; - getNext = false; for(;;) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - int8_t b; - b = (int8_t)AT91C_BASE_SSC->SSC_RHR; - + uint16_t iq = AT91C_BASE_SSC->SSC_RHR; // The samples are correlations against I and Q versions of the - // tone that the tag AM-modulates, so every other sample is I, - // every other is Q. We just want power, so abs(I) + abs(Q) is - // close to what we want. - if(getNext) { - uint8_t r = AMPLITUDE(b, prev); + // tone that the tag AM-modulates. We just want power, + // so abs(I) + abs(Q) is close to what we want. + int8_t i = iq >> 8; + int8_t q = iq; + uint8_t r = AMPLITUDE(i, q); - dest[c++] = r; + dest[c++] = r; - if(c >= 14000) { - break; - } - } else { - prev = b; + if(c >= 14000) { + break; } - - getNext = !getNext; - WDT_HIT(); } } - Dbprintf("fin record"); + Dbprintf("finished recording"); } @@ -714,7 +661,7 @@ void Iso15693InitReader() { SpinDelay(10); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); // Give the tags time to energize FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); @@ -973,7 +920,7 @@ void ReaderIso15693(uint32_t parameter) SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // Setup SSC - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); // Start from off (no field generated) FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); @@ -1075,7 +1022,7 @@ void SimTagIso15693(uint32_t parameter, uint8_t *uid) memset(buf, 0x00, 100); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); // Start from off (no field generated) FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); diff --git a/armsrc/legicrf.c b/armsrc/legicrf.c index c8a4829f..d3fd35d1 100644 --- a/armsrc/legicrf.c +++ b/armsrc/legicrf.c @@ -63,11 +63,11 @@ static uint32_t last_frame_end; /* ts of last bit of previews rx or tx frame */ // I/O interface abstraction (FPGA -> ARM) //----------------------------------------------------------------------------- -static inline uint8_t rx_byte_from_fpga() { +static inline uint16_t rx_frame_from_fpga() { for(;;) { WDT_HIT(); - // wait for byte be become available in rx holding register + // wait for frame be become available in rx holding register if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { return AT91C_BASE_SSC->SSC_RHR; } @@ -88,33 +88,32 @@ static inline uint8_t rx_byte_from_fpga() { // To reduce CPU time the amplitude is approximated by using linear functions: // am = MAX(ABS(i),ABS(q)) + 1/2*MIN(ABS(i),ABSq)) // -// Note: The SSC receiver is never synchronized the calculation my be performed -// on a I/Q pair from two subsequent correlations, but does not matter. -// // The bit time is 99.1us (21 I/Q pairs). The receiver skips the first 5 samples // and averages the next (most stable) 8 samples. The final 8 samples are dropped // also. // -// The demedulated should be alligned to the bit periode by the caller. This is +// The demodulated should be alligned to the bit period by the caller. This is // done in rx_bit and rx_ack. static inline bool rx_bit() { - int32_t cq = 0; - int32_t ci = 0; + int32_t sum_cq = 0; + int32_t sum_ci = 0; // skip first 5 I/Q pairs for(size_t i = 0; i<5; ++i) { - (int8_t)rx_byte_from_fpga(); - (int8_t)rx_byte_from_fpga(); + (void)rx_frame_from_fpga(); } // sample next 8 I/Q pairs for(size_t i = 0; i<8; ++i) { - cq += (int8_t)rx_byte_from_fpga(); - ci += (int8_t)rx_byte_from_fpga(); + uint16_t iq = rx_frame_from_fpga(); + int8_t ci = (int8_t)(iq >> 8); + int8_t cq = (int8_t)(iq & 0xff); + sum_ci += ci; + sum_cq += cq; } // calculate power - int32_t power = (MAX(ABS(ci), ABS(cq)) + (MIN(ABS(ci), ABS(cq)) >> 1)); + int32_t power = (MAX(ABS(sum_ci), ABS(sum_cq)) + MIN(ABS(sum_ci), ABS(sum_cq))/2); // compare average (power / 8) to threshold return ((power >> 3) > INPUT_THRESHOLD); @@ -131,12 +130,12 @@ static inline bool rx_bit() { static inline void tx_bit(bool bit) { // insert pause - LOW(GPIO_SSC_DOUT); + HIGH(GPIO_SSC_DOUT); last_frame_end += RWD_TIME_PAUSE; while(GET_TICKS < last_frame_end) { }; - HIGH(GPIO_SSC_DOUT); - // return to high, wait for bit periode to end + // return to carrier on, wait for bit periode to end + LOW(GPIO_SSC_DOUT); last_frame_end += (bit ? RWD_TIME_1 : RWD_TIME_0) - RWD_TIME_PAUSE; while(GET_TICKS < last_frame_end) { }; } @@ -166,10 +165,10 @@ static void tx_frame(uint32_t frame, uint8_t len) { }; // add pause to mark end of the frame - LOW(GPIO_SSC_DOUT); + HIGH(GPIO_SSC_DOUT); last_frame_end += RWD_TIME_PAUSE; while(GET_TICKS < last_frame_end) { }; - HIGH(GPIO_SSC_DOUT); + LOW(GPIO_SSC_DOUT); } static uint32_t rx_frame(uint8_t len) { @@ -265,12 +264,12 @@ static void init_reader(bool clear_mem) { LED_D_ON(); // configure SSC with defaults - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); // re-claim GPIO_SSC_DOUT as GPIO and enable output AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; - HIGH(GPIO_SSC_DOUT); + LOW(GPIO_SSC_DOUT); // init crc calculator crc_init(&legic_crc, 4, 0x19 >> 1, 0x05, 0); diff --git a/armsrc/legicrfsim.c b/armsrc/legicrfsim.c index 34633f36..a149e0f9 100644 --- a/armsrc/legicrfsim.c +++ b/armsrc/legicrfsim.c @@ -284,7 +284,7 @@ static void init_tag() { SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // configure SSC with defaults - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); // first pull output to low to prevent glitches then re-claim GPIO_SSC_DOUT LOW(GPIO_SSC_DOUT); diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 911ba8da..2fe49a80 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -1818,7 +1818,7 @@ void Cotag(uint32_t arg0) { SetAdcMuxFor(GPIO_MUXSEL_LOPKD); // Now set up the SSC to get the ADC samples that are now streaming at us. - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_LF_ADC); // start clock - 1.5ticks is 1us StartTicks(); diff --git a/armsrc/lfsampling.c b/armsrc/lfsampling.c index 3b076265..ffbc0da8 100644 --- a/armsrc/lfsampling.c +++ b/armsrc/lfsampling.c @@ -101,7 +101,7 @@ void LFSetupFPGAForADC(int divisor, bool lf_field) // Give it a bit of time for the resonant antenna to settle. SpinDelay(50); // Now set up the SSC to get the ADC samples that are now streaming at us. - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_LF_ADC); } /** diff --git a/fpga/fpga_hf.bit b/fpga/fpga_hf.bit index 179e87eed22bc326f37e33f6925116916701c6bf..bd296174f0978d3214bae003d25a7063bff60021 100644 GIT binary patch literal 42175 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 diff --git a/fpga/hi_read_rx_xcorr.v b/fpga/hi_read_rx_xcorr.v index f637abf2..8233960f 100644 --- a/fpga/hi_read_rx_xcorr.v +++ b/fpga/hi_read_rx_xcorr.v @@ -197,9 +197,9 @@ begin end end - // set ssp_frame signal for corr_i_cnt = 0..3 and corr_i_cnt = 32..35 - // (send two frames with 8 Bits each) - if(corr_i_cnt[5:2] == 4'b0000 || corr_i_cnt[5:2] == 4'b1000) + // set ssp_frame signal for corr_i_cnt = 0..3 + // (send one frame with 16 Bits) + if(corr_i_cnt[5:2] == 4'b0000) ssp_frame = 1'b1; else ssp_frame = 1'b0; diff --git a/fpga/hi_read_tx.v b/fpga/hi_read_tx.v index 756683cd..819f1697 100644 --- a/fpga/hi_read_tx.v +++ b/fpga/hi_read_tx.v @@ -42,11 +42,11 @@ begin pwr_hi <= ck_1356megb; pwr_oe1 <= 1'b0; pwr_oe3 <= 1'b0; - pwr_oe4 <= ~ssp_dout; + pwr_oe4 <= ssp_dout; end else begin - pwr_hi <= ck_1356megb & ssp_dout; + pwr_hi <= ck_1356megb & ~ssp_dout; pwr_oe1 <= 1'b0; pwr_oe3 <= 1'b0; pwr_oe4 <= 1'b0; From b4ba1eeabeb2bd91302d4536f4406ba6c52ef46b Mon Sep 17 00:00:00 2001 From: uhei Date: Mon, 17 Sep 2018 07:55:51 +0200 Subject: [PATCH 276/310] fpgaloader: fix function accordingly to #669 (#673) FpgaSetupSscDma uses uint16_t and not int with #669 --- armsrc/fpgaloader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/armsrc/fpgaloader.h b/armsrc/fpgaloader.h index f75dfc81..5aff6505 100644 --- a/armsrc/fpgaloader.h +++ b/armsrc/fpgaloader.h @@ -21,7 +21,7 @@ void FpgaWriteConfWord(uint8_t v); void FpgaDownloadAndGo(int bitstream_version); void FpgaSetupSsc(uint8_t mode); void SetupSpi(int mode); -bool FpgaSetupSscDma(uint8_t *buf, int len); +bool FpgaSetupSscDma(uint8_t *buf, uint16_t sample_count); void Fpga_print_status(); int FpgaGetCurrent(); #define FpgaDisableSscDma(void) AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; From 2dedd5eaeca6d2014d6e406ccaf286f3d32c5a56 Mon Sep 17 00:00:00 2001 From: AntiCat Date: Mon, 17 Sep 2018 07:56:53 +0200 Subject: [PATCH 277/310] Update changelog and fix build error (#674) * Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca13dc85..7f5c4542 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Changed TLV parser messages to more convenient (Merlok) - Rewritten Legic Prime reader (`hf legic reader`, `write` and `fill`) - it is using xcorrelation now (AntiCat) - `hf 14a` commands works via argtable3 commandline parsing library (Merlok) +- Changed Legic Prime tag simulator (`hf legic sim`) to run from 212 kHz SSP clock for better reliability (AntiCat) ### Fixed - Changed start sequence in Qt mode (fix: short commands hangs main Qt thread) (Merlok) From 82258709f6c66a06c4f09dc902128c2d1eb41389 Mon Sep 17 00:00:00 2001 From: Michael Farrell Date: Mon, 17 Sep 2018 16:35:45 +1000 Subject: [PATCH 278/310] Remove unused functions le32toh, le24toh, hextobinstring, binarraytobinstring, print_hex, print_hex_break, sprint_hex_ascii, sprint_ascii, SwapEndian64ex (#667) * Remove unused method le32toh. * Delete unused function le24toh * Remove unused functions hextobinstring, binarraytobinstring * Remove unused functions print_hex, print_hex_break * Delete unused functions sprint_hex_ascii, sprint_ascii * Remove unused function SwapEndian64ex --- armsrc/util.c | 5 --- armsrc/util.h | 1 - client/util.c | 90 --------------------------------------------------- client/util.h | 8 ----- 4 files changed, 104 deletions(-) diff --git a/armsrc/util.c b/armsrc/util.c index 8a8d6657..a1b0f151 100644 --- a/armsrc/util.c +++ b/armsrc/util.c @@ -81,11 +81,6 @@ void lsl (uint8_t *data, size_t len) { data[len - 1] <<= 1; } -int32_t le24toh (uint8_t data[3]) -{ - return (data[2] << 16) | (data[1] << 8) | data[0]; -} - void LEDsoff() { LED_A_OFF(); diff --git a/armsrc/util.h b/armsrc/util.h index 58dcdd52..fb50ecc8 100644 --- a/armsrc/util.h +++ b/armsrc/util.h @@ -34,7 +34,6 @@ void num_to_bytes(uint64_t n, size_t len, uint8_t* dest); uint64_t bytes_to_num(uint8_t* src, size_t len); void rol(uint8_t *data, const size_t len); void lsl (uint8_t *data, size_t len); -int32_t le24toh (uint8_t data[3]); void LED(int led, int ms); void LEDsoff(); diff --git a/client/util.c b/client/util.c index 2be1e46b..d7c824d9 100644 --- a/client/util.c +++ b/client/util.c @@ -138,33 +138,6 @@ void hex_to_buffer(const uint8_t *buf, const uint8_t *hex_data, const size_t hex // printing and converting functions -void print_hex(const uint8_t * data, const size_t len) -{ - size_t i; - - for (i=0; i < len; i++) - printf("%02x ", data[i]); - - printf("\n"); -} - -void print_hex_break(const uint8_t *data, const size_t len, uint8_t breaks) { - - int rownum = 0; - printf("[%02d] | ", rownum); - for (int i = 0; i < len; ++i) { - - printf("%02X ", data[i]); - - // check if a line break is needed - if ( breaks > 0 && !((i+1) % breaks) && (i+1 < len) ) { - ++rownum; - printf("\n[%02d] | ", rownum); - } - } - printf("\n"); -} - char *sprint_hex(const uint8_t *data, const size_t len) { static char buf[1025] = {0}; @@ -220,27 +193,6 @@ char *sprint_bin(const uint8_t *data, const size_t len) { return sprint_bin_break(data, len, 0); } -char *sprint_hex_ascii(const uint8_t *data, const size_t len) { - static char buf[1024]; - char *tmp = buf; - memset(buf, 0x00, 1024); - size_t max_len = (len > 255) ? 255 : len; - // max 255 bytes * 3 + 2 characters = 767 in buffer - sprintf(tmp, "%.765s| ", sprint_hex(data, max_len) ); - - size_t i = 0; - size_t pos = (max_len * 3)+2; - // add another 255 characters ascii = 1020 characters of buffer used - while(i < max_len) { - char c = data[i]; - if ( (c < 32) || (c == 127)) - c = '.'; - sprintf(tmp+pos+i, "%c", c); - ++i; - } - return buf; -} - char *sprint_ascii_ex(const uint8_t *data, const size_t len, const size_t min_str_len) { static char buf[1024]; char *tmp = buf; @@ -260,10 +212,6 @@ char *sprint_ascii_ex(const uint8_t *data, const size_t len, const size_t min_st return buf; } -char *sprint_ascii(const uint8_t *data, const size_t len) { - return sprint_ascii_ex(data, len, 0); -} - void num_to_bytes(uint64_t n, size_t len, uint8_t* dest) { while (len--) { @@ -324,16 +272,6 @@ uint8_t *SwapEndian64(const uint8_t *src, const size_t len, const uint8_t blockS return tmp; } -// takes a uint8_t src array, for len items and reverses the byte order in blocksizes (8,16,32,64), -// returns: the dest array contains the reordered src array. -void SwapEndian64ex(const uint8_t *src, const size_t len, const uint8_t blockSize, uint8_t *dest){ - for (uint8_t block=0; block < (uint8_t)(len/blockSize); block++){ - for (size_t i = 0; i < blockSize; i++){ - dest[i+(blockSize*block)] = src[(blockSize-1-i)+(blockSize*block)]; - } - } -} - //assumes little endian char * printBits(size_t const size, void const * const ptr) { @@ -648,17 +586,6 @@ int hextobinarray(char *target, char *source) return count; } -// convert hex to human readable binary string -int hextobinstring(char *target, char *source) -{ - int length; - - if(!(length= hextobinarray(target, source))) - return 0; - binarraytobinstring(target, target, length); - return length; -} - // convert binary array of 0x00/0x01 values to hex (safe to do in place as target will always be shorter than source) // return number of bits converted int binarraytohex(char *target,char *source, int length) @@ -681,16 +608,6 @@ int binarraytohex(char *target,char *source, int length) return length; } -// convert binary array to human readable binary -void binarraytobinstring(char *target, char *source, int length) -{ - int i; - - for(i= 0 ; i < length ; ++i) - *(target++)= *(source++) + '0'; - *target= '\0'; -} - // return parity bit required to match type uint8_t GetParity( uint8_t *bits, uint8_t type, int length) { @@ -718,13 +635,6 @@ void xor(unsigned char *dst, unsigned char *src, size_t len) { *dst ^= *src; } -int32_t le24toh (uint8_t data[3]) { - return (data[2] << 16) | (data[1] << 8) | data[0]; -} -uint32_t le32toh (uint8_t *data) { - return (uint32_t)( (data[3]<<24) | (data[2]<<16) | (data[1]<<8) | data[0]); -} - // RotateLeft - Ultralight, Desfire, works on byte level // 00-01-02 >> 01-02-00 void rol(uint8_t *data, const size_t len){ diff --git a/client/util.h b/client/util.h index 878938f4..31ad29fb 100644 --- a/client/util.h +++ b/client/util.h @@ -46,14 +46,11 @@ extern void FillFileNameByUID(char *fileName, uint8_t * uid, char *ext, int byte extern void hex_to_buffer(const uint8_t *buf, const uint8_t *hex_data, const size_t hex_len, const size_t hex_max_len, const size_t min_str_len, const size_t spaces_between, bool uppercase); -extern void print_hex(const uint8_t * data, const size_t len); extern char *sprint_hex(const uint8_t * data, const size_t len); extern char *sprint_hex_inrow(const uint8_t *data, const size_t len); extern char *sprint_hex_inrow_ex(const uint8_t *data, const size_t len, const size_t min_str_len); extern char *sprint_bin(const uint8_t * data, const size_t len); extern char *sprint_bin_break(const uint8_t *data, const size_t len, const uint8_t breaks); -extern char *sprint_hex_ascii(const uint8_t *data, const size_t len); -extern char *sprint_ascii(const uint8_t *data, const size_t len); extern char *sprint_ascii_ex(const uint8_t *data, const size_t len, const size_t min_str_len); extern void num_to_bytes(uint64_t n, size_t len, uint8_t* dest); @@ -64,7 +61,6 @@ extern char *printBits(size_t const size, void const * const ptr); extern char * printBitsPar(const uint8_t *b, size_t len); extern uint32_t SwapBits(uint32_t value, int nrbits); extern uint8_t *SwapEndian64(const uint8_t *src, const size_t len, const uint8_t blockSize); -extern void SwapEndian64ex(const uint8_t *src, const size_t len, const uint8_t blockSize, uint8_t *dest); extern int param_getlength(const char *line, int paramnum); extern char param_getchar(const char *line, int paramnum); @@ -82,15 +78,11 @@ extern int param_gethex_to_eol(const char *line, int paramnum, uint8_t * data, i extern int param_getstr(const char *line, int paramnum, char * str, size_t buffersize); extern int hextobinarray( char *target, char *source); -extern int hextobinstring( char *target, char *source); extern int binarraytohex( char *target, char *source, int length); -extern void binarraytobinstring(char *target, char *source, int length); extern uint8_t GetParity( uint8_t *string, uint8_t type, int length); extern void wiegand_add_parity(uint8_t *target, uint8_t *source, uint8_t length); extern void xor(unsigned char *dst, unsigned char *src, size_t len); -extern int32_t le24toh(uint8_t data[3]); -extern uint32_t le32toh (uint8_t *data); extern void rol(uint8_t *data, const size_t len); extern void clean_ascii(unsigned char *buf, size_t len); From 556826b5d52152450259311c613695ee5adcac04 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Tue, 18 Sep 2018 08:15:34 +0300 Subject: [PATCH 279/310] Added loading EMV transaction parameters from json file (#659) * added default params file * added jansson * added test function. * added tlv tree to function * add params from json works * added to `hf emv exec`: load params from json and some refactoring * added `scan` command --- client/Makefile | 13 +- client/emv/cmdemv.c | 212 ++++- client/emv/defparams.json | 58 ++ client/emv/tlv.c | 64 ++ client/emv/tlv.h | 2 + client/jansson/Makefile | 63 ++ client/jansson/dump.c | 504 ++++++++++++ client/jansson/error.c | 66 ++ client/jansson/hashtable.c | 356 +++++++++ client/jansson/hashtable.h | 176 +++++ client/jansson/hashtable_seed.c | 277 +++++++ client/jansson/jansson.def | 75 ++ client/jansson/jansson.h | 362 +++++++++ client/jansson/jansson_config.h | 51 ++ client/jansson/jansson_config.h.in | 51 ++ client/jansson/jansson_private.h | 112 +++ client/jansson/load.c | 1155 ++++++++++++++++++++++++++++ client/jansson/lookup3.h | 381 +++++++++ client/jansson/memory.c | 69 ++ client/jansson/pack_unpack.c | 909 ++++++++++++++++++++++ client/jansson/strbuffer.c | 111 +++ client/jansson/strbuffer.h | 34 + client/jansson/strconv.c | 145 ++++ client/jansson/utf.c | 187 +++++ client/jansson/utf.h | 27 + client/jansson/value.c | 1078 ++++++++++++++++++++++++++ 26 files changed, 6515 insertions(+), 23 deletions(-) create mode 100644 client/emv/defparams.json create mode 100644 client/jansson/Makefile create mode 100644 client/jansson/dump.c create mode 100644 client/jansson/error.c create mode 100644 client/jansson/hashtable.c create mode 100644 client/jansson/hashtable.h create mode 100644 client/jansson/hashtable_seed.c create mode 100644 client/jansson/jansson.def create mode 100644 client/jansson/jansson.h create mode 100644 client/jansson/jansson_config.h create mode 100644 client/jansson/jansson_config.h.in create mode 100644 client/jansson/jansson_private.h create mode 100644 client/jansson/load.c create mode 100644 client/jansson/lookup3.h create mode 100644 client/jansson/memory.c create mode 100644 client/jansson/pack_unpack.c create mode 100644 client/jansson/strbuffer.c create mode 100644 client/jansson/strbuffer.h create mode 100644 client/jansson/strconv.c create mode 100644 client/jansson/utf.c create mode 100644 client/jansson/utf.h create mode 100644 client/jansson/value.c diff --git a/client/Makefile b/client/Makefile index 54a77a84..d223a921 100644 --- a/client/Makefile +++ b/client/Makefile @@ -19,8 +19,10 @@ OBJDIR = obj LDLIBS = -L/opt/local/lib -L/usr/local/lib -lreadline -lpthread -lm LUALIB = ../liblua/liblua.a +JANSSONLIBPATH = ./jansson +JANSSONLIB = $(JANSSONLIBPATH)/libjansson.a LDFLAGS = $(ENV_LDFLAGS) -CFLAGS = $(ENV_CFLAGS) -std=c99 -D_ISOC99_SOURCE -I. -I../include -I../common -I../common/polarssl -I../zlib -I../uart -I/opt/local/include -I../liblua -Wall -g -O3 +CFLAGS = $(ENV_CFLAGS) -std=c99 -D_ISOC99_SOURCE -I. -I../include -I../common -I../common/polarssl -I../zlib -I../uart -I/opt/local/include -I../liblua -I$(JANSSONLIBPATH) -Wall -g -O3 CXXFLAGS = -I../include -Wall -O3 APP_CFLAGS = @@ -234,12 +236,12 @@ WINBINS = $(patsubst %, %.exe, $(BINS)) CLEAN = $(BINS) $(WINBINS) $(COREOBJS) $(CMDOBJS) $(ZLIBOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(OBJDIR)/*.o *.moc.cpp ui/ui_overlays.h # need to assign dependancies to build these first... -all: lua_build $(BINS) +all: lua_build jansson_build $(BINS) all-static: LDLIBS:=-static $(LDLIBS) all-static: proxmark3 flasher fpga_compress -proxmark3: LDLIBS+=$(LUALIB) $(QTLDLIBS) +proxmark3: LDLIBS+=$(LUALIB) $(JANSSONLIB) $(QTLDLIBS) proxmark3: $(OBJDIR)/proxmark3.o $(COREOBJS) $(CMDOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(ZLIBOBJS) lualibs/usb_cmd.lua $(LD) $(LDFLAGS) $(OBJDIR)/proxmark3.o $(COREOBJS) $(CMDOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(ZLIBOBJS) $(LDLIBS) -o $@ @@ -263,6 +265,7 @@ lualibs/usb_cmd.lua: ../include/usb_cmd.h clean: $(RM) $(CLEAN) cd ../liblua && make clean + cd ./jansson && make clean tarbin: $(BINS) $(TAR) $(TARFLAGS) ../proxmark3-$(platform)-bin.tar $(BINS:%=client/%) $(WINBINS:%=client/%) @@ -270,6 +273,10 @@ tarbin: $(BINS) lua_build: @echo Compiling liblua, using platform $(LUAPLATFORM) cd ../liblua && make $(LUAPLATFORM) + +jansson_build: + @echo Compiling jansson + cd ./jansson && make all .PHONY: all clean diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index c53b02af..541f505a 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -11,6 +11,7 @@ #include #include "cmdemv.h" #include "test/cryptotest.h" +#include int UsageCmdHFEMVSelect(void) { PrintAndLog("HELP : Executes select applet command:\n"); @@ -279,11 +280,12 @@ int CmdHFEMVPPSE(const char *cmd) { int UsageCmdHFEMVExec(void) { PrintAndLog("HELP : Executes EMV contactless transaction:\n"); - PrintAndLog("Usage: hf emv exec [-s][-a][-t][-f][-v][-c][-x][-g]\n"); + PrintAndLog("Usage: hf emv exec [-s][-a][-t][-j][-f][-v][-c][-x][-g]\n"); PrintAndLog(" Options:"); PrintAndLog(" -s : select card"); PrintAndLog(" -a : show APDU reqests and responses\n"); PrintAndLog(" -t : TLV decode results\n"); + PrintAndLog(" -j : load transaction parameters from `emv/defparams.json` file\n"); PrintAndLog(" -f : force search AID. Search AID instead of execute PPSE.\n"); PrintAndLog(" -v : transaction type - qVSDC or M/Chip.\n"); PrintAndLog(" -c : transaction type - qVSDC or M/Chip plus CDA (SDAD generation).\n"); @@ -296,9 +298,159 @@ int UsageCmdHFEMVExec(void) { return 0; } -#define TLV_ADD(tag, value)( tlvdb_add(tlvRoot, tlvdb_fixed(tag, sizeof(value) - 1, (const unsigned char *)value)) ) +#define TLV_ADD(tag, value)( tlvdb_change_or_add_node(tlvRoot, tag, sizeof(value) - 1, (const unsigned char *)value) ) #define dreturn(n) {free(pdol_data_tlv);tlvdb_free(tlvSelect);tlvdb_free(tlvRoot);DropField();return n;} +bool HexToBuffer(const char *errormsg, const char *hexvalue, uint8_t * buffer, size_t maxbufferlen, size_t *bufferlen) { + int buflen = 0; + + switch(param_gethex_to_eol(hexvalue, 0, buffer, maxbufferlen, &buflen)) { + case 1: + PrintAndLog("%s Invalid HEX value.", errormsg); + return false; + case 2: + PrintAndLog("%s Hex value too large.", errormsg); + return false; + case 3: + PrintAndLog("%s Hex value must have even number of digits.", errormsg); + return false; + } + + if (buflen > maxbufferlen) { + PrintAndLog("%s HEX length (%d) more than %d", errormsg, *bufferlen, maxbufferlen); + return false; + } + + *bufferlen = buflen; + + return true; +} + +bool ParamLoadFromJson(struct tlvdb *tlv) { + json_t *root; + json_error_t error; + + if (!tlv) { + PrintAndLog("ERROR load params: tlv tree is NULL."); + return false; + } + + // current path + file name + const char *relfname = "emv/defparams.json"; + char fname[strlen(get_my_executable_directory()) + strlen(relfname) + 1]; + strcpy(fname, get_my_executable_directory()); + strcat(fname, relfname); + + root = json_load_file(fname, 0, &error); + if (!root) { + PrintAndLog("Load params: json error on line %d: %s", error.line, error.text); + return false; + } + + if (!json_is_array(root)) { + PrintAndLog("Load params: Invalid json format. root must be array."); + return false; + } + + PrintAndLog("Load params: json OK"); + + for(int i = 0; i < json_array_size(root); i++) { + json_t *data, *jtype, *jlength, *jvalue; + + data = json_array_get(root, i); + if(!json_is_object(data)) + { + PrintAndLog("Load params: data [%d] is not an object", i + 1); + json_decref(root); + return false; + } + + jtype = json_object_get(data, "type"); + if(!json_is_string(jtype)) + { + PrintAndLog("Load params: data [%d] type is not a string", i + 1); + json_decref(root); + return false; + } + const char *tlvType = json_string_value(jtype); + + jvalue = json_object_get(data, "value"); + if(!json_is_string(jvalue)) + { + PrintAndLog("Load params: data [%d] value is not a string", i + 1); + json_decref(root); + return false; + } + const char *tlvValue = json_string_value(jvalue); + + jlength = json_object_get(data, "length"); + if(!json_is_number(jlength)) + { + PrintAndLog("Load params: data [%d] length is not a number", i + 1); + json_decref(root); + return false; + } + + int tlvLength = json_integer_value(jlength); + if (tlvLength > 250) { + PrintAndLog("Load params: data [%d] length more than 250", i + 1); + json_decref(root); + return false; + } + + PrintAndLog("TLV param: %s[%d]=%s", tlvType, tlvLength, tlvValue); + uint8_t buf[251] = {0}; + size_t buflen = 0; + + // here max length must be 4, but now tlv_tag_t is 2-byte var. so let it be 2 by now... TODO: needs refactoring tlv_tag_t... + if (!HexToBuffer("TLV Error type:", tlvType, buf, 2, &buflen)) { + json_decref(root); + return false; + } + tlv_tag_t tag = 0; + for (int i = 0; i < buflen; i++) { + tag = (tag << 8) + buf[i]; + } + + if (!HexToBuffer("TLV Error value:", tlvValue, buf, sizeof(buf) - 1, &buflen)) { + json_decref(root); + return false; + } + + if (buflen != tlvLength) { + PrintAndLog("Load params: data [%d] length of HEX must(%d) be identical to length in TLV param(%d)", i + 1, buflen, tlvLength); + json_decref(root); + return false; + } + + tlvdb_change_or_add_node(tlv, tag, tlvLength, (const unsigned char *)buf); + } + + json_decref(root); + + return true; +} + +void ParamLoadDefaults(struct tlvdb *tlvRoot) { + //9F02:(Amount, authorized (Numeric)) len:6 + TLV_ADD(0x9F02, "\x00\x00\x00\x00\x01\x00"); + //9F1A:(Terminal Country Code) len:2 + TLV_ADD(0x9F1A, "ru"); + //5F2A:(Transaction Currency Code) len:2 + // USD 840, EUR 978, RUR 810, RUB 643, RUR 810(old), UAH 980, AZN 031, n/a 999 + TLV_ADD(0x5F2A, "\x09\x80"); + //9A:(Transaction Date) len:3 + TLV_ADD(0x9A, "\x00\x00\x00"); + //9C:(Transaction Type) len:1 | 00 => Goods and service #01 => Cash + TLV_ADD(0x9C, "\x00"); + // 9F37 Unpredictable Number len:4 + TLV_ADD(0x9F37, "\x01\x02\x03\x04"); + // 9F6A Unpredictable Number (MSD for UDOL) len:4 + TLV_ADD(0x9F6A, "\x01\x02\x03\x04"); + //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 + TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC +} + int CmdHFEMVExec(const char *cmd) { bool activateField = false; bool showAPDU = false; @@ -306,6 +458,7 @@ int CmdHFEMVExec(const char *cmd) { bool forceSearch = false; enum TransactionType TrType = TT_MSD; bool GenACGPO = false; + bool paramLoadJSON = false; uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; @@ -367,6 +520,10 @@ int CmdHFEMVExec(const char *cmd) { case 'G': GenACGPO = true; break; + case 'j': + case 'J': + paramLoadJSON = true; + break; default: PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); return 1; @@ -433,7 +590,14 @@ int CmdHFEMVExec(const char *cmd) { PrintAndLog("\n* Init transaction parameters."); - //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 + ParamLoadDefaults(tlvRoot); + + if (paramLoadJSON) { + PrintAndLog("* * Transaction parameters loading from JSON..."); + ParamLoadFromJson(tlvRoot); + } + + //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 char *qVSDC = "\x26\x00\x00\x00"; if (GenACGPO) { qVSDC = "\x26\x80\x00\x00"; @@ -453,26 +617,9 @@ int CmdHFEMVExec(const char *cmd) { TLV_ADD(0x9F66, qVSDC); // qVSDC (VISA CDA not enabled) break; default: - TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC break; } - //9F02:(Amount, authorized (Numeric)) len:6 - TLV_ADD(0x9F02, "\x00\x00\x00\x00\x01\x00"); - //9F1A:(Terminal Country Code) len:2 - TLV_ADD(0x9F1A, "ru"); - //5F2A:(Transaction Currency Code) len:2 - // USD 840, EUR 978, RUR 810, RUB 643, RUR 810(old), UAH 980, AZN 031, n/a 999 - TLV_ADD(0x5F2A, "\x09\x80"); - //9A:(Transaction Date) len:3 - TLV_ADD(0x9A, "\x00\x00\x00"); - //9C:(Transaction Type) len:1 | 00 => Goods and service #01 => Cash - TLV_ADD(0x9C, "\x00"); - // 9F37 Unpredictable Number len:4 - TLV_ADD(0x9F37, "\x01\x02\x03\x04"); - // 9F6A Unpredictable Number (MSD for UDOL) len:4 - TLV_ADD(0x9F6A, "\x01\x02\x03\x04"); - TLVPrintFromTLV(tlvRoot); // TODO delete!!! PrintAndLog("\n* Calc PDOL."); @@ -828,6 +975,30 @@ int CmdHFEMVExec(const char *cmd) { return 0; } +int UsageCmdHFEMVScan(void) { + PrintAndLog("HELP : Scan EMV card and save it contents to a file. \n"); + PrintAndLog(" It executes EMV contactless transaction and saves result to a file which can be used for emulation.\n"); + PrintAndLog("Usage: hf emv scan [-a][-t][-v][-c][-x][-g] \n"); + PrintAndLog(" Options:"); + PrintAndLog(" -a : show APDU reqests and responses\n"); + PrintAndLog(" -t : TLV decode results\n"); + PrintAndLog(" -v : transaction type - qVSDC or M/Chip.\n"); + PrintAndLog(" -c : transaction type - qVSDC or M/Chip plus CDA (SDAD generation).\n"); + PrintAndLog(" -x : transaction type - VSDC. For test only. Not a standart behavior.\n"); + PrintAndLog(" -g : VISA. generate AC from GPO\n"); + PrintAndLog("By default : transaction type - MSD.\n"); + PrintAndLog("Samples:"); + PrintAndLog(" hf emv scan -a -t -> scan MSD transaction mode"); + PrintAndLog(" hf emv scan -a -t -c -> scan CDA transaction mode"); + return 0; +} + +int CmdHFEMVScan(const char *cmd) { + UsageCmdHFEMVScan(); + + return 0; +} + int CmdHFEMVTest(const char *cmd) { return ExecuteCryptoTests(true); } @@ -839,6 +1010,7 @@ static command_t CommandTable[] = { {"pse", CmdHFEMVPPSE, 0, "Execute PPSE. It selects 2PAY.SYS.DDF01 or 1PAY.SYS.DDF01 directory."}, {"search", CmdHFEMVSearch, 0, "Try to select all applets from applets list and print installed applets."}, {"select", CmdHFEMVSelect, 0, "Select applet."}, +// {"scan", CmdHFEMVScan, 0, "Scan EMV card and save it contents to json file for emulator."}, {"test", CmdHFEMVTest, 0, "Crypto logic test."}, {NULL, NULL, 0, NULL} }; diff --git a/client/emv/defparams.json b/client/emv/defparams.json new file mode 100644 index 00000000..47f1c8a1 --- /dev/null +++ b/client/emv/defparams.json @@ -0,0 +1,58 @@ +[ + { + "name": "Transaction Date", + "type": "9A", + "value": "00 00 00", + "length": 3, + "hint": "format: YYMMDD" + }, + { + "name": "Transaction Type", + "type": "9C", + "value": "00", + "length": 1, + "hint": "00: Goods and service, 01: Cash" + }, + { + "name": "Amount, authorized", + "type": "9F 02", + "value": "00 00 00 00 01 00", + "length": 6, + "hint": "amount (numberic) in cents" + }, + { + "name": "Transaction Currency Code", + "type": "5F 2A", + "value": "09 80", + "length": 2, + "hint": "USD 840, EUR 978, RUB 643, RUR 810(old), UAH 980, AZN 031, n/a 999" + }, + { + "name": "Terminal Country Code", + "type": "9F 1A", + "value": "72 75", + "length": 2, + "hint": "ISO3166: de, en (65 6e), uk(75 6b), ru (72 75), us, ua" + }, + { + "name": "Terminal Transaction Qualifiers (TTQ)", + "type": "9F 66", + "value": "26 00 00 00", + "length": 4, + "hint": "qVSDC 26 00 00 00, gen AC from GPO 26 80 00 00, MSD 86 00 00 00, VSDC 46 00 00 00" + }, + { + "name": "Unpredictable Number", + "type": "9F 37", + "value": "01 02 03 04", + "length": 4, + "hint": "4 byte random number" + }, + { + "name": "Unpredictable Number (MSD for UDOL)", + "type": "9F 6A", + "value": "01 02 03 05", + "length": 4, + "hint": "4 byte random number" + } +] diff --git a/client/emv/tlv.c b/client/emv/tlv.c index 1be91777..776fdeed 100644 --- a/client/emv/tlv.c +++ b/client/emv/tlv.c @@ -318,6 +318,24 @@ struct tlvdb *tlvdb_find(struct tlvdb *tlvdb, tlv_tag_t tag) { return NULL; } +struct tlvdb *tlvdb_find_full(struct tlvdb *tlvdb, tlv_tag_t tag) { + if (!tlvdb) + return NULL; + + for (; tlvdb; tlvdb = tlvdb->next) { + if (tlvdb->tag.tag == tag) + return tlvdb; + + if (tlvdb->children) { + struct tlvdb * ch = tlvdb_find_full(tlvdb->children, tag); + if (ch) + return ch; + } + } + + return NULL; +} + struct tlvdb *tlvdb_find_path(struct tlvdb *tlvdb, tlv_tag_t tag[]) { int i = 0; struct tlvdb *tnext = tlvdb; @@ -342,6 +360,52 @@ void tlvdb_add(struct tlvdb *tlvdb, struct tlvdb *other) tlvdb->next = other; } +void tlvdb_change_or_add_node(struct tlvdb *tlvdb, tlv_tag_t tag, size_t len, const unsigned char *value) +{ + struct tlvdb *telm = tlvdb_find_full(tlvdb, tag); + if (telm == NULL) { + // new tlv element + tlvdb_add(tlvdb, tlvdb_fixed(tag, len, value)); + } else { + // the same tlv structure + if (telm->tag.tag == tag && telm->tag.len == len && !memcmp(telm->tag.value, value, len)) + return; + + // replace tlv element + struct tlvdb *tnewelm = tlvdb_fixed(tag, len, value); + tnewelm->next = telm->next; + tnewelm->parent = telm->parent; + + // if telm stayed first in children chain + if (telm->parent && telm->parent->children == telm) { + telm->parent->children = tnewelm; + } + + // if telm have previous element + if (telm != tlvdb) { + // elm in root + struct tlvdb *celm = tlvdb; + // elm in child list of node + if (telm->parent && telm->parent->children) + celm = telm->parent->children; + + // find previous element + for (; celm; celm = celm->next) { + if (celm->next == telm) { + celm->next = tnewelm; + break; + } + } + } + + // free old element with childrens + telm->next = NULL; + tlvdb_free(telm); + } + + return; +} + void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data, int level) { struct tlvdb *next = NULL; diff --git a/client/emv/tlv.h b/client/emv/tlv.h index 5b8b6825..963abb2a 100644 --- a/client/emv/tlv.h +++ b/client/emv/tlv.h @@ -39,11 +39,13 @@ struct tlvdb *tlvdb_parse(const unsigned char *buf, size_t len); struct tlvdb *tlvdb_parse_multi(const unsigned char *buf, size_t len); void tlvdb_free(struct tlvdb *tlvdb); +struct tlvdb *tlvdb_find_full(struct tlvdb *tlvdb, tlv_tag_t tag); // search also in childrens struct tlvdb *tlvdb_find(struct tlvdb *tlvdb, tlv_tag_t tag); struct tlvdb *tlvdb_find_next(struct tlvdb *tlvdb, tlv_tag_t tag); struct tlvdb *tlvdb_find_path(struct tlvdb *tlvdb, tlv_tag_t tag[]); void tlvdb_add(struct tlvdb *tlvdb, struct tlvdb *other); +void tlvdb_change_or_add_node(struct tlvdb *tlvdb, tlv_tag_t tag, size_t len, const unsigned char *value); void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data, int level); const struct tlv *tlvdb_get(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev); diff --git a/client/jansson/Makefile b/client/jansson/Makefile new file mode 100644 index 00000000..7a262f8b --- /dev/null +++ b/client/jansson/Makefile @@ -0,0 +1,63 @@ + +include_HEADERS = jansson.h +nodist_include_HEADERS = jansson_config.h + +LIB_A = libjansson.a +libjansson_la_SOURCES = \ + dump.c \ + error.c \ + hashtable.c \ + hashtable.h \ + hashtable_seed.c \ + jansson_private.h \ + load.c \ + lookup3.h \ + memory.c \ + pack_unpack.c \ + strbuffer.c \ + strbuffer.h \ + strconv.c \ + utf.c \ + utf.h \ + value.c +libjansson_la_LDFLAGS = \ + -no-undefined \ + -export-symbols-regex '^json_' \ + -version-info 15:0:11 + + +CFILES = $(filter %.c, $(libjansson_la_SOURCES)) +CMDOBJS = $(CFILES:%.c=%.o) +CLEAN = $(CMDOBJS) + +CC= gcc +CFLAGS= -O2 -Wall -Wno-unused-variable -Wno-unused-function +LDFLAGS= $(SYSLDFLAGS) $(libjansson_la_LDFLAGS) +LIBS= -lm $(SYSLIBS) $(MYLIBS) +DEFAULT_INCLUDES = -I. +DEFS = -DHAVE_STDINT_H + +AR= ar rcs +RANLIB= ranlib +RM= rm -f +TST= echo + +SYSLDFLAGS= +SYSLIBS= + +MYLIBS= +MYOBJS= + +all: $(CMDOBJS) + $(AR) $(LIB_A) $(CMDOBJS) + $(RANLIB) $(LIB_A) + +clean: + $(RM) $(CLEAN) + $(RM) $(LIB_A) + +%.o: %.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(CFLAGS) -c -o $@ $< $(LIBS) + +.PHONY: all clean + diff --git a/client/jansson/dump.c b/client/jansson/dump.c new file mode 100644 index 00000000..8e725c93 --- /dev/null +++ b/client/jansson/dump.c @@ -0,0 +1,504 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include "jansson_private.h" + +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "jansson.h" +#include "strbuffer.h" +#include "utf.h" + +#define MAX_INTEGER_STR_LENGTH 100 +#define MAX_REAL_STR_LENGTH 100 + +#define FLAGS_TO_INDENT(f) ((f) & 0x1F) +#define FLAGS_TO_PRECISION(f) (((f) >> 11) & 0x1F) + +struct buffer { + const size_t size; + size_t used; + char *data; +}; + +static int dump_to_strbuffer(const char *buffer, size_t size, void *data) +{ + return strbuffer_append_bytes((strbuffer_t *)data, buffer, size); +} + +static int dump_to_buffer(const char *buffer, size_t size, void *data) +{ + struct buffer *buf = (struct buffer *)data; + + if(buf->used + size <= buf->size) + memcpy(&buf->data[buf->used], buffer, size); + + buf->used += size; + return 0; +} + +static int dump_to_file(const char *buffer, size_t size, void *data) +{ + FILE *dest = (FILE *)data; + if(fwrite(buffer, size, 1, dest) != 1) + return -1; + return 0; +} + +static int dump_to_fd(const char *buffer, size_t size, void *data) +{ + int *dest = (int *)data; +#ifdef HAVE_UNISTD_H + if(write(*dest, buffer, size) == (ssize_t)size) + return 0; +#endif + return -1; +} + +/* 32 spaces (the maximum indentation size) */ +static const char whitespace[] = " "; + +static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, void *data) +{ + if(FLAGS_TO_INDENT(flags) > 0) + { + unsigned int ws_count = FLAGS_TO_INDENT(flags), n_spaces = depth * ws_count; + + if(dump("\n", 1, data)) + return -1; + + while(n_spaces > 0) + { + int cur_n = n_spaces < sizeof whitespace - 1 ? n_spaces : sizeof whitespace - 1; + + if(dump(whitespace, cur_n, data)) + return -1; + + n_spaces -= cur_n; + } + } + else if(space && !(flags & JSON_COMPACT)) + { + return dump(" ", 1, data); + } + return 0; +} + +static int dump_string(const char *str, size_t len, json_dump_callback_t dump, void *data, size_t flags) +{ + const char *pos, *end, *lim; + int32_t codepoint; + + if(dump("\"", 1, data)) + return -1; + + end = pos = str; + lim = str + len; + while(1) + { + const char *text; + char seq[13]; + int length; + + while(end < lim) + { + end = utf8_iterate(pos, lim - pos, &codepoint); + if(!end) + return -1; + + /* mandatory escape or control char */ + if(codepoint == '\\' || codepoint == '"' || codepoint < 0x20) + break; + + /* slash */ + if((flags & JSON_ESCAPE_SLASH) && codepoint == '/') + break; + + /* non-ASCII */ + if((flags & JSON_ENSURE_ASCII) && codepoint > 0x7F) + break; + + pos = end; + } + + if(pos != str) { + if(dump(str, pos - str, data)) + return -1; + } + + if(end == pos) + break; + + /* handle \, /, ", and control codes */ + length = 2; + switch(codepoint) + { + case '\\': text = "\\\\"; break; + case '\"': text = "\\\""; break; + case '\b': text = "\\b"; break; + case '\f': text = "\\f"; break; + case '\n': text = "\\n"; break; + case '\r': text = "\\r"; break; + case '\t': text = "\\t"; break; + case '/': text = "\\/"; break; + default: + { + /* codepoint is in BMP */ + if(codepoint < 0x10000) + { + snprintf(seq, sizeof(seq), "\\u%04X", (unsigned int)codepoint); + length = 6; + } + + /* not in BMP -> construct a UTF-16 surrogate pair */ + else + { + int32_t first, last; + + codepoint -= 0x10000; + first = 0xD800 | ((codepoint & 0xffc00) >> 10); + last = 0xDC00 | (codepoint & 0x003ff); + + snprintf(seq, sizeof(seq), "\\u%04X\\u%04X", (unsigned int)first, (unsigned int)last); + length = 12; + } + + text = seq; + break; + } + } + + if(dump(text, length, data)) + return -1; + + str = pos = end; + } + + return dump("\"", 1, data); +} + +static int compare_keys(const void *key1, const void *key2) +{ + return strcmp(*(const char **)key1, *(const char **)key2); +} + +static int loop_check(hashtable_t *parents, const json_t *json, char *key, size_t key_size) +{ + snprintf(key, key_size, "%p", json); + if (hashtable_get(parents, key)) + return -1; + + return hashtable_set(parents, key, json_null()); +} + +static int do_dump(const json_t *json, size_t flags, int depth, + hashtable_t *parents, json_dump_callback_t dump, void *data) +{ + int embed = flags & JSON_EMBED; + + flags &= ~JSON_EMBED; + + if(!json) + return -1; + + switch(json_typeof(json)) { + case JSON_NULL: + return dump("null", 4, data); + + case JSON_TRUE: + return dump("true", 4, data); + + case JSON_FALSE: + return dump("false", 5, data); + + case JSON_INTEGER: + { + char buffer[MAX_INTEGER_STR_LENGTH]; + int size; + + size = snprintf(buffer, MAX_INTEGER_STR_LENGTH, + "%" JSON_INTEGER_FORMAT, + json_integer_value(json)); + if(size < 0 || size >= MAX_INTEGER_STR_LENGTH) + return -1; + + return dump(buffer, size, data); + } + + case JSON_REAL: + { + char buffer[MAX_REAL_STR_LENGTH]; + int size; + double value = json_real_value(json); + + size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value, + FLAGS_TO_PRECISION(flags)); + if(size < 0) + return -1; + + return dump(buffer, size, data); + } + + case JSON_STRING: + return dump_string(json_string_value(json), json_string_length(json), dump, data, flags); + + case JSON_ARRAY: + { + size_t n; + size_t i; + /* Space for "0x", double the sizeof a pointer for the hex and a terminator. */ + char key[2 + (sizeof(json) * 2) + 1]; + + /* detect circular references */ + if (loop_check(parents, json, key, sizeof(key))) + return -1; + + n = json_array_size(json); + + if(!embed && dump("[", 1, data)) + return -1; + if(n == 0) { + hashtable_del(parents, key); + return embed ? 0 : dump("]", 1, data); + } + if(dump_indent(flags, depth + 1, 0, dump, data)) + return -1; + + for(i = 0; i < n; ++i) { + if(do_dump(json_array_get(json, i), flags, depth + 1, + parents, dump, data)) + return -1; + + if(i < n - 1) + { + if(dump(",", 1, data) || + dump_indent(flags, depth + 1, 1, dump, data)) + return -1; + } + else + { + if(dump_indent(flags, depth, 0, dump, data)) + return -1; + } + } + + hashtable_del(parents, key); + return embed ? 0 : dump("]", 1, data); + } + + case JSON_OBJECT: + { + void *iter; + const char *separator; + int separator_length; + /* Space for "0x", double the sizeof a pointer for the hex and a terminator. */ + char key[2 + (sizeof(json) * 2) + 1]; + + if(flags & JSON_COMPACT) { + separator = ":"; + separator_length = 1; + } + else { + separator = ": "; + separator_length = 2; + } + + /* detect circular references */ + if (loop_check(parents, json, key, sizeof(key))) + return -1; + + iter = json_object_iter((json_t *)json); + + if(!embed && dump("{", 1, data)) + return -1; + if(!iter) { + hashtable_del(parents, key); + return embed ? 0 : dump("}", 1, data); + } + if(dump_indent(flags, depth + 1, 0, dump, data)) + return -1; + + if(flags & JSON_SORT_KEYS) + { + const char **keys; + size_t size, i; + + size = json_object_size(json); + keys = jsonp_malloc(size * sizeof(const char *)); + if(!keys) + return -1; + + i = 0; + while(iter) + { + keys[i] = json_object_iter_key(iter); + iter = json_object_iter_next((json_t *)json, iter); + i++; + } + assert(i == size); + + qsort(keys, size, sizeof(const char *), compare_keys); + + for(i = 0; i < size; i++) + { + const char *key; + json_t *value; + + key = keys[i]; + value = json_object_get(json, key); + assert(value); + + dump_string(key, strlen(key), dump, data, flags); + if(dump(separator, separator_length, data) || + do_dump(value, flags, depth + 1, parents, dump, data)) + { + jsonp_free(keys); + return -1; + } + + if(i < size - 1) + { + if(dump(",", 1, data) || + dump_indent(flags, depth + 1, 1, dump, data)) + { + jsonp_free(keys); + return -1; + } + } + else + { + if(dump_indent(flags, depth, 0, dump, data)) + { + jsonp_free(keys); + return -1; + } + } + } + + jsonp_free(keys); + } + else + { + /* Don't sort keys */ + + while(iter) + { + void *next = json_object_iter_next((json_t *)json, iter); + const char *key = json_object_iter_key(iter); + + dump_string(key, strlen(key), dump, data, flags); + if(dump(separator, separator_length, data) || + do_dump(json_object_iter_value(iter), flags, depth + 1, + parents, dump, data)) + return -1; + + if(next) + { + if(dump(",", 1, data) || + dump_indent(flags, depth + 1, 1, dump, data)) + return -1; + } + else + { + if(dump_indent(flags, depth, 0, dump, data)) + return -1; + } + + iter = next; + } + } + + hashtable_del(parents, key); + return embed ? 0 : dump("}", 1, data); + } + + default: + /* not reached */ + return -1; + } +} + +char *json_dumps(const json_t *json, size_t flags) +{ + strbuffer_t strbuff; + char *result; + + if(strbuffer_init(&strbuff)) + return NULL; + + if(json_dump_callback(json, dump_to_strbuffer, (void *)&strbuff, flags)) + result = NULL; + else + result = jsonp_strdup(strbuffer_value(&strbuff)); + + strbuffer_close(&strbuff); + return result; +} + +size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags) +{ + struct buffer buf = { size, 0, buffer }; + + if(json_dump_callback(json, dump_to_buffer, (void *)&buf, flags)) + return 0; + + return buf.used; +} + +int json_dumpf(const json_t *json, FILE *output, size_t flags) +{ + return json_dump_callback(json, dump_to_file, (void *)output, flags); +} + +int json_dumpfd(const json_t *json, int output, size_t flags) +{ + return json_dump_callback(json, dump_to_fd, (void *)&output, flags); +} + +int json_dump_file(const json_t *json, const char *path, size_t flags) +{ + int result; + + FILE *output = fopen(path, "w"); + if(!output) + return -1; + + result = json_dumpf(json, output, flags); + + if(fclose(output) != 0) + return -1; + + return result; +} + +int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags) +{ + int res; + hashtable_t parents_set; + + if(!(flags & JSON_ENCODE_ANY)) { + if(!json_is_array(json) && !json_is_object(json)) + return -1; + } + + if (hashtable_init(&parents_set)) + return -1; + res = do_dump(json, flags, 0, &parents_set, callback, data); + hashtable_close(&parents_set); + + return res; +} diff --git a/client/jansson/error.c b/client/jansson/error.c new file mode 100644 index 00000000..cbd50d7e --- /dev/null +++ b/client/jansson/error.c @@ -0,0 +1,66 @@ +#include +#include "jansson_private.h" + +void jsonp_error_init(json_error_t *error, const char *source) +{ + if(error) + { + error->text[0] = '\0'; + error->line = -1; + error->column = -1; + error->position = 0; + if(source) + jsonp_error_set_source(error, source); + else + error->source[0] = '\0'; + } +} + +void jsonp_error_set_source(json_error_t *error, const char *source) +{ + size_t length; + + if(!error || !source) + return; + + length = strlen(source); + if(length < JSON_ERROR_SOURCE_LENGTH) + strncpy(error->source, source, length + 1); + else { + size_t extra = length - JSON_ERROR_SOURCE_LENGTH + 4; + strncpy(error->source, "...", 3); + strncpy(error->source + 3, source + extra, length - extra + 1); + } +} + +void jsonp_error_set(json_error_t *error, int line, int column, + size_t position, enum json_error_code code, + const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + jsonp_error_vset(error, line, column, position, code, msg, ap); + va_end(ap); +} + +void jsonp_error_vset(json_error_t *error, int line, int column, + size_t position, enum json_error_code code, + const char *msg, va_list ap) +{ + if(!error) + return; + + if(error->text[0] != '\0') { + /* error already set */ + return; + } + + error->line = line; + error->column = column; + error->position = (int)position; + + vsnprintf(error->text, JSON_ERROR_TEXT_LENGTH - 1, msg, ap); + error->text[JSON_ERROR_TEXT_LENGTH - 2] = '\0'; + error->text[JSON_ERROR_TEXT_LENGTH - 1] = code; +} diff --git a/client/jansson/hashtable.c b/client/jansson/hashtable.c new file mode 100644 index 00000000..c819319a --- /dev/null +++ b/client/jansson/hashtable.c @@ -0,0 +1,356 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#if HAVE_CONFIG_H +#include +#endif + +#include +#include + +#if HAVE_STDINT_H +#include +#endif + +#include /* for JSON_INLINE */ +#include "jansson_private.h" /* for container_of() */ +#include "hashtable.h" + +#ifndef INITIAL_HASHTABLE_ORDER +#define INITIAL_HASHTABLE_ORDER 3 +#endif + +typedef struct hashtable_list list_t; +typedef struct hashtable_pair pair_t; +typedef struct hashtable_bucket bucket_t; + +extern volatile uint32_t hashtable_seed; + +/* Implementation of the hash function */ +#include "lookup3.h" + +#define list_to_pair(list_) container_of(list_, pair_t, list) +#define ordered_list_to_pair(list_) container_of(list_, pair_t, ordered_list) +#define hash_str(key) ((size_t)hashlittle((key), strlen(key), hashtable_seed)) + +static JSON_INLINE void list_init(list_t *list) +{ + list->next = list; + list->prev = list; +} + +static JSON_INLINE void list_insert(list_t *list, list_t *node) +{ + node->next = list; + node->prev = list->prev; + list->prev->next = node; + list->prev = node; +} + +static JSON_INLINE void list_remove(list_t *list) +{ + list->prev->next = list->next; + list->next->prev = list->prev; +} + +static JSON_INLINE int bucket_is_empty(hashtable_t *hashtable, bucket_t *bucket) +{ + return bucket->first == &hashtable->list && bucket->first == bucket->last; +} + +static void insert_to_bucket(hashtable_t *hashtable, bucket_t *bucket, + list_t *list) +{ + if(bucket_is_empty(hashtable, bucket)) + { + list_insert(&hashtable->list, list); + bucket->first = bucket->last = list; + } + else + { + list_insert(bucket->first, list); + bucket->first = list; + } +} + +static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket, + const char *key, size_t hash) +{ + list_t *list; + pair_t *pair; + + if(bucket_is_empty(hashtable, bucket)) + return NULL; + + list = bucket->first; + while(1) + { + pair = list_to_pair(list); + if(pair->hash == hash && strcmp(pair->key, key) == 0) + return pair; + + if(list == bucket->last) + break; + + list = list->next; + } + + return NULL; +} + +/* returns 0 on success, -1 if key was not found */ +static int hashtable_do_del(hashtable_t *hashtable, + const char *key, size_t hash) +{ + pair_t *pair; + bucket_t *bucket; + size_t index; + + index = hash & hashmask(hashtable->order); + bucket = &hashtable->buckets[index]; + + pair = hashtable_find_pair(hashtable, bucket, key, hash); + if(!pair) + return -1; + + if(&pair->list == bucket->first && &pair->list == bucket->last) + bucket->first = bucket->last = &hashtable->list; + + else if(&pair->list == bucket->first) + bucket->first = pair->list.next; + + else if(&pair->list == bucket->last) + bucket->last = pair->list.prev; + + list_remove(&pair->list); + list_remove(&pair->ordered_list); + json_decref(pair->value); + + jsonp_free(pair); + hashtable->size--; + + return 0; +} + +static void hashtable_do_clear(hashtable_t *hashtable) +{ + list_t *list, *next; + pair_t *pair; + + for(list = hashtable->list.next; list != &hashtable->list; list = next) + { + next = list->next; + pair = list_to_pair(list); + json_decref(pair->value); + jsonp_free(pair); + } +} + +static int hashtable_do_rehash(hashtable_t *hashtable) +{ + list_t *list, *next; + pair_t *pair; + size_t i, index, new_size, new_order; + struct hashtable_bucket *new_buckets; + + new_order = hashtable->order + 1; + new_size = hashsize(new_order); + + new_buckets = jsonp_malloc(new_size * sizeof(bucket_t)); + if(!new_buckets) + return -1; + + jsonp_free(hashtable->buckets); + hashtable->buckets = new_buckets; + hashtable->order = new_order; + + for(i = 0; i < hashsize(hashtable->order); i++) + { + hashtable->buckets[i].first = hashtable->buckets[i].last = + &hashtable->list; + } + + list = hashtable->list.next; + list_init(&hashtable->list); + + for(; list != &hashtable->list; list = next) { + next = list->next; + pair = list_to_pair(list); + index = pair->hash % new_size; + insert_to_bucket(hashtable, &hashtable->buckets[index], &pair->list); + } + + return 0; +} + + +int hashtable_init(hashtable_t *hashtable) +{ + size_t i; + + hashtable->size = 0; + hashtable->order = INITIAL_HASHTABLE_ORDER; + hashtable->buckets = jsonp_malloc(hashsize(hashtable->order) * sizeof(bucket_t)); + if(!hashtable->buckets) + return -1; + + list_init(&hashtable->list); + list_init(&hashtable->ordered_list); + + for(i = 0; i < hashsize(hashtable->order); i++) + { + hashtable->buckets[i].first = hashtable->buckets[i].last = + &hashtable->list; + } + + return 0; +} + +void hashtable_close(hashtable_t *hashtable) +{ + hashtable_do_clear(hashtable); + jsonp_free(hashtable->buckets); +} + +int hashtable_set(hashtable_t *hashtable, const char *key, json_t *value) +{ + pair_t *pair; + bucket_t *bucket; + size_t hash, index; + + /* rehash if the load ratio exceeds 1 */ + if(hashtable->size >= hashsize(hashtable->order)) + if(hashtable_do_rehash(hashtable)) + return -1; + + hash = hash_str(key); + index = hash & hashmask(hashtable->order); + bucket = &hashtable->buckets[index]; + pair = hashtable_find_pair(hashtable, bucket, key, hash); + + if(pair) + { + json_decref(pair->value); + pair->value = value; + } + else + { + /* offsetof(...) returns the size of pair_t without the last, + flexible member. This way, the correct amount is + allocated. */ + + size_t len = strlen(key); + if(len >= (size_t)-1 - offsetof(pair_t, key)) { + /* Avoid an overflow if the key is very long */ + return -1; + } + + pair = jsonp_malloc(offsetof(pair_t, key) + len + 1); + if(!pair) + return -1; + + pair->hash = hash; + strncpy(pair->key, key, len + 1); + pair->value = value; + list_init(&pair->list); + list_init(&pair->ordered_list); + + insert_to_bucket(hashtable, bucket, &pair->list); + list_insert(&hashtable->ordered_list, &pair->ordered_list); + + hashtable->size++; + } + return 0; +} + +void *hashtable_get(hashtable_t *hashtable, const char *key) +{ + pair_t *pair; + size_t hash; + bucket_t *bucket; + + hash = hash_str(key); + bucket = &hashtable->buckets[hash & hashmask(hashtable->order)]; + + pair = hashtable_find_pair(hashtable, bucket, key, hash); + if(!pair) + return NULL; + + return pair->value; +} + +int hashtable_del(hashtable_t *hashtable, const char *key) +{ + size_t hash = hash_str(key); + return hashtable_do_del(hashtable, key, hash); +} + +void hashtable_clear(hashtable_t *hashtable) +{ + size_t i; + + hashtable_do_clear(hashtable); + + for(i = 0; i < hashsize(hashtable->order); i++) + { + hashtable->buckets[i].first = hashtable->buckets[i].last = + &hashtable->list; + } + + list_init(&hashtable->list); + list_init(&hashtable->ordered_list); + hashtable->size = 0; +} + +void *hashtable_iter(hashtable_t *hashtable) +{ + return hashtable_iter_next(hashtable, &hashtable->ordered_list); +} + +void *hashtable_iter_at(hashtable_t *hashtable, const char *key) +{ + pair_t *pair; + size_t hash; + bucket_t *bucket; + + hash = hash_str(key); + bucket = &hashtable->buckets[hash & hashmask(hashtable->order)]; + + pair = hashtable_find_pair(hashtable, bucket, key, hash); + if(!pair) + return NULL; + + return &pair->ordered_list; +} + +void *hashtable_iter_next(hashtable_t *hashtable, void *iter) +{ + list_t *list = (list_t *)iter; + if(list->next == &hashtable->ordered_list) + return NULL; + return list->next; +} + +void *hashtable_iter_key(void *iter) +{ + pair_t *pair = ordered_list_to_pair((list_t *)iter); + return pair->key; +} + +void *hashtable_iter_value(void *iter) +{ + pair_t *pair = ordered_list_to_pair((list_t *)iter); + return pair->value; +} + +void hashtable_iter_set(void *iter, json_t *value) +{ + pair_t *pair = ordered_list_to_pair((list_t *)iter); + + json_decref(pair->value); + pair->value = value; +} diff --git a/client/jansson/hashtable.h b/client/jansson/hashtable.h new file mode 100644 index 00000000..d4c32ae0 --- /dev/null +++ b/client/jansson/hashtable.h @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef HASHTABLE_H +#define HASHTABLE_H + +#include +#include "jansson.h" + +struct hashtable_list { + struct hashtable_list *prev; + struct hashtable_list *next; +}; + +/* "pair" may be a bit confusing a name, but think of it as a + key-value pair. In this case, it just encodes some extra data, + too */ +struct hashtable_pair { + struct hashtable_list list; + struct hashtable_list ordered_list; + size_t hash; + json_t *value; + char key[1]; +}; + +struct hashtable_bucket { + struct hashtable_list *first; + struct hashtable_list *last; +}; + +typedef struct hashtable { + size_t size; + struct hashtable_bucket *buckets; + size_t order; /* hashtable has pow(2, order) buckets */ + struct hashtable_list list; + struct hashtable_list ordered_list; +} hashtable_t; + + +#define hashtable_key_to_iter(key_) \ + (&(container_of(key_, struct hashtable_pair, key)->ordered_list)) + + +/** + * hashtable_init - Initialize a hashtable object + * + * @hashtable: The (statically allocated) hashtable object + * + * Initializes a statically allocated hashtable object. The object + * should be cleared with hashtable_close when it's no longer used. + * + * Returns 0 on success, -1 on error (out of memory). + */ +int hashtable_init(hashtable_t *hashtable); + +/** + * hashtable_close - Release all resources used by a hashtable object + * + * @hashtable: The hashtable + * + * Destroys a statically allocated hashtable object. + */ +void hashtable_close(hashtable_t *hashtable); + +/** + * hashtable_set - Add/modify value in hashtable + * + * @hashtable: The hashtable object + * @key: The key + * @serial: For addition order of keys + * @value: The value + * + * If a value with the given key already exists, its value is replaced + * with the new value. Value is "stealed" in the sense that hashtable + * doesn't increment its refcount but decreases the refcount when the + * value is no longer needed. + * + * Returns 0 on success, -1 on failure (out of memory). + */ +int hashtable_set(hashtable_t *hashtable, const char *key, json_t *value); + +/** + * hashtable_get - Get a value associated with a key + * + * @hashtable: The hashtable object + * @key: The key + * + * Returns value if it is found, or NULL otherwise. + */ +void *hashtable_get(hashtable_t *hashtable, const char *key); + +/** + * hashtable_del - Remove a value from the hashtable + * + * @hashtable: The hashtable object + * @key: The key + * + * Returns 0 on success, or -1 if the key was not found. + */ +int hashtable_del(hashtable_t *hashtable, const char *key); + +/** + * hashtable_clear - Clear hashtable + * + * @hashtable: The hashtable object + * + * Removes all items from the hashtable. + */ +void hashtable_clear(hashtable_t *hashtable); + +/** + * hashtable_iter - Iterate over hashtable + * + * @hashtable: The hashtable object + * + * Returns an opaque iterator to the first element in the hashtable. + * The iterator should be passed to hashtable_iter_* functions. + * The hashtable items are not iterated over in any particular order. + * + * There's no need to free the iterator in any way. The iterator is + * valid as long as the item that is referenced by the iterator is not + * deleted. Other values may be added or deleted. In particular, + * hashtable_iter_next() may be called on an iterator, and after that + * the key/value pair pointed by the old iterator may be deleted. + */ +void *hashtable_iter(hashtable_t *hashtable); + +/** + * hashtable_iter_at - Return an iterator at a specific key + * + * @hashtable: The hashtable object + * @key: The key that the iterator should point to + * + * Like hashtable_iter() but returns an iterator pointing to a + * specific key. + */ +void *hashtable_iter_at(hashtable_t *hashtable, const char *key); + +/** + * hashtable_iter_next - Advance an iterator + * + * @hashtable: The hashtable object + * @iter: The iterator + * + * Returns a new iterator pointing to the next element in the + * hashtable or NULL if the whole hastable has been iterated over. + */ +void *hashtable_iter_next(hashtable_t *hashtable, void *iter); + +/** + * hashtable_iter_key - Retrieve the key pointed by an iterator + * + * @iter: The iterator + */ +void *hashtable_iter_key(void *iter); + +/** + * hashtable_iter_value - Retrieve the value pointed by an iterator + * + * @iter: The iterator + */ +void *hashtable_iter_value(void *iter); + +/** + * hashtable_iter_set - Set the value pointed by an iterator + * + * @iter: The iterator + * @value: The value to set + */ +void hashtable_iter_set(void *iter, json_t *value); + +#endif diff --git a/client/jansson/hashtable_seed.c b/client/jansson/hashtable_seed.c new file mode 100644 index 00000000..8aed5406 --- /dev/null +++ b/client/jansson/hashtable_seed.c @@ -0,0 +1,277 @@ +/* Generate sizeof(uint32_t) bytes of as random data as possible to seed + the hash function. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#ifdef HAVE_STDINT_H +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_SCHED_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#if defined(_WIN32) +/* For GetModuleHandle(), GetProcAddress() and GetCurrentProcessId() */ +#include +#endif + +#include "jansson.h" + + +static uint32_t buf_to_uint32(char *data) { + size_t i; + uint32_t result = 0; + + for (i = 0; i < sizeof(uint32_t); i++) + result = (result << 8) | (unsigned char)data[i]; + + return result; +} + + + +/* /dev/urandom */ +#if !defined(_WIN32) && defined(USE_URANDOM) +static int seed_from_urandom(uint32_t *seed) { + /* Use unbuffered I/O if we have open(), close() and read(). Otherwise + fall back to fopen() */ + + char data[sizeof(uint32_t)]; + int ok; + +#if defined(HAVE_OPEN) && defined(HAVE_CLOSE) && defined(HAVE_READ) + int urandom; + urandom = open("/dev/urandom", O_RDONLY); + if (urandom == -1) + return 1; + + ok = read(urandom, data, sizeof(uint32_t)) == sizeof(uint32_t); + close(urandom); +#else + FILE *urandom; + + urandom = fopen("/dev/urandom", "rb"); + if (!urandom) + return 1; + + ok = fread(data, 1, sizeof(uint32_t), urandom) == sizeof(uint32_t); + fclose(urandom); +#endif + + if (!ok) + return 1; + + *seed = buf_to_uint32(data); + return 0; +} +#endif + +/* Windows Crypto API */ +#if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI) +#include + +typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv, LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType, DWORD dwFlags); +typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen, BYTE *pbBuffer); +typedef BOOL (WINAPI *CRYPTRELEASECONTEXT)(HCRYPTPROV hProv, DWORD dwFlags); + +static int seed_from_windows_cryptoapi(uint32_t *seed) +{ + HINSTANCE hAdvAPI32 = NULL; + CRYPTACQUIRECONTEXTA pCryptAcquireContext = NULL; + CRYPTGENRANDOM pCryptGenRandom = NULL; + CRYPTRELEASECONTEXT pCryptReleaseContext = NULL; + HCRYPTPROV hCryptProv = 0; + BYTE data[sizeof(uint32_t)]; + int ok; + + hAdvAPI32 = GetModuleHandle(TEXT("advapi32.dll")); + if(hAdvAPI32 == NULL) + return 1; + + pCryptAcquireContext = (CRYPTACQUIRECONTEXTA)GetProcAddress(hAdvAPI32, "CryptAcquireContextA"); + if (!pCryptAcquireContext) + return 1; + + pCryptGenRandom = (CRYPTGENRANDOM)GetProcAddress(hAdvAPI32, "CryptGenRandom"); + if (!pCryptGenRandom) + return 1; + + pCryptReleaseContext = (CRYPTRELEASECONTEXT)GetProcAddress(hAdvAPI32, "CryptReleaseContext"); + if (!pCryptReleaseContext) + return 1; + + if (!pCryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) + return 1; + + ok = pCryptGenRandom(hCryptProv, sizeof(uint32_t), data); + pCryptReleaseContext(hCryptProv, 0); + + if (!ok) + return 1; + + *seed = buf_to_uint32((char *)data); + return 0; +} +#endif + +/* gettimeofday() and getpid() */ +static int seed_from_timestamp_and_pid(uint32_t *seed) { +#ifdef HAVE_GETTIMEOFDAY + /* XOR of seconds and microseconds */ + struct timeval tv; + gettimeofday(&tv, NULL); + *seed = (uint32_t)tv.tv_sec ^ (uint32_t)tv.tv_usec; +#else + /* Seconds only */ + *seed = (uint32_t)time(NULL); +#endif + + /* XOR with PID for more randomness */ +#if defined(_WIN32) + *seed ^= (uint32_t)GetCurrentProcessId(); +#elif defined(HAVE_GETPID) + *seed ^= (uint32_t)getpid(); +#endif + + return 0; +} + +static uint32_t generate_seed() { + uint32_t seed; + int done = 0; + +#if !defined(_WIN32) && defined(USE_URANDOM) + if (seed_from_urandom(&seed) == 0) + done = 1; +#endif + +#if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI) + if (seed_from_windows_cryptoapi(&seed) == 0) + done = 1; +#endif + + if (!done) { + /* Fall back to timestamp and PID if no better randomness is + available */ + seed_from_timestamp_and_pid(&seed); + } + + /* Make sure the seed is never zero */ + if (seed == 0) + seed = 1; + + return seed; +} + + +volatile uint32_t hashtable_seed = 0; + +#if defined(HAVE_ATOMIC_BUILTINS) && (defined(HAVE_SCHED_YIELD) || !defined(_WIN32)) +static volatile char seed_initialized = 0; + +void json_object_seed(size_t seed) { + uint32_t new_seed = (uint32_t)seed; + + if (hashtable_seed == 0) { + if (__atomic_test_and_set(&seed_initialized, __ATOMIC_RELAXED) == 0) { + /* Do the seeding ourselves */ + if (new_seed == 0) + new_seed = generate_seed(); + + __atomic_store_n(&hashtable_seed, new_seed, __ATOMIC_RELEASE); + } else { + /* Wait for another thread to do the seeding */ + do { +#ifdef HAVE_SCHED_YIELD + sched_yield(); +#endif + } while(__atomic_load_n(&hashtable_seed, __ATOMIC_ACQUIRE) == 0); + } + } +} +#elif defined(HAVE_SYNC_BUILTINS) && (defined(HAVE_SCHED_YIELD) || !defined(_WIN32)) +void json_object_seed(size_t seed) { + uint32_t new_seed = (uint32_t)seed; + + if (hashtable_seed == 0) { + if (new_seed == 0) { + /* Explicit synchronization fences are not supported by the + __sync builtins, so every thread getting here has to + generate the seed value. + */ + new_seed = generate_seed(); + } + + do { + if (__sync_bool_compare_and_swap(&hashtable_seed, 0, new_seed)) { + /* We were the first to seed */ + break; + } else { + /* Wait for another thread to do the seeding */ +#ifdef HAVE_SCHED_YIELD + sched_yield(); +#endif + } + } while(hashtable_seed == 0); + } +} +#elif defined(_WIN32) +static long seed_initialized = 0; +void json_object_seed(size_t seed) { + uint32_t new_seed = (uint32_t)seed; + + if (hashtable_seed == 0) { + if (InterlockedIncrement(&seed_initialized) == 1) { + /* Do the seeding ourselves */ + if (new_seed == 0) + new_seed = generate_seed(); + + hashtable_seed = new_seed; + } else { + /* Wait for another thread to do the seeding */ + do { + SwitchToThread(); + } while (hashtable_seed == 0); + } + } +} +#else +/* Fall back to a thread-unsafe version */ +void json_object_seed(size_t seed) { + uint32_t new_seed = (uint32_t)seed; + + if (hashtable_seed == 0) { + if (new_seed == 0) + new_seed = generate_seed(); + + hashtable_seed = new_seed; + } +} +#endif diff --git a/client/jansson/jansson.def b/client/jansson/jansson.def new file mode 100644 index 00000000..15f35c95 --- /dev/null +++ b/client/jansson/jansson.def @@ -0,0 +1,75 @@ +EXPORTS + json_delete + json_true + json_false + json_null + json_sprintf + json_vsprintf + json_string + json_stringn + json_string_nocheck + json_stringn_nocheck + json_string_value + json_string_length + json_string_set + json_string_setn + json_string_set_nocheck + json_string_setn_nocheck + json_integer + json_integer_value + json_integer_set + json_real + json_real_value + json_real_set + json_number_value + json_array + json_array_size + json_array_get + json_array_set_new + json_array_append_new + json_array_insert_new + json_array_remove + json_array_clear + json_array_extend + json_object + json_object_size + json_object_get + json_object_set_new + json_object_set_new_nocheck + json_object_del + json_object_clear + json_object_update + json_object_update_existing + json_object_update_missing + json_object_iter + json_object_iter_at + json_object_iter_next + json_object_iter_key + json_object_iter_value + json_object_iter_set_new + json_object_key_to_iter + json_object_seed + json_dumps + json_dumpb + json_dumpf + json_dumpfd + json_dump_file + json_dump_callback + json_loads + json_loadb + json_loadf + json_loadfd + json_load_file + json_load_callback + json_equal + json_copy + json_deep_copy + json_pack + json_pack_ex + json_vpack_ex + json_unpack + json_unpack_ex + json_vunpack_ex + json_set_alloc_funcs + json_get_alloc_funcs + diff --git a/client/jansson/jansson.h b/client/jansson/jansson.h new file mode 100644 index 00000000..43332895 --- /dev/null +++ b/client/jansson/jansson.h @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef JANSSON_H +#define JANSSON_H + +#include +#include +#include /* for size_t */ +#include + +#include "jansson_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* version */ + +#define JANSSON_MAJOR_VERSION 2 +#define JANSSON_MINOR_VERSION 11 +#define JANSSON_MICRO_VERSION 0 + +/* Micro version is omitted if it's 0 */ +#define JANSSON_VERSION "2.11" + +/* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this + for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */ +#define JANSSON_VERSION_HEX ((JANSSON_MAJOR_VERSION << 16) | \ + (JANSSON_MINOR_VERSION << 8) | \ + (JANSSON_MICRO_VERSION << 0)) + +/* If __atomic or __sync builtins are available the library is thread + * safe for all read-only functions plus reference counting. */ +#if JSON_HAVE_ATOMIC_BUILTINS || JSON_HAVE_SYNC_BUILTINS +#define JANSSON_THREAD_SAFE_REFCOUNT 1 +#endif + +/* types */ + +typedef enum { + JSON_OBJECT, + JSON_ARRAY, + JSON_STRING, + JSON_INTEGER, + JSON_REAL, + JSON_TRUE, + JSON_FALSE, + JSON_NULL +} json_type; + +typedef struct json_t { + json_type type; + volatile size_t refcount; +} json_t; + +#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */ +#if JSON_INTEGER_IS_LONG_LONG +#ifdef _WIN32 +#define JSON_INTEGER_FORMAT "I64d" +#else +#define JSON_INTEGER_FORMAT "lld" +#endif +typedef long long json_int_t; +#else +#define JSON_INTEGER_FORMAT "ld" +typedef long json_int_t; +#endif /* JSON_INTEGER_IS_LONG_LONG */ +#endif + +#define json_typeof(json) ((json)->type) +#define json_is_object(json) ((json) && json_typeof(json) == JSON_OBJECT) +#define json_is_array(json) ((json) && json_typeof(json) == JSON_ARRAY) +#define json_is_string(json) ((json) && json_typeof(json) == JSON_STRING) +#define json_is_integer(json) ((json) && json_typeof(json) == JSON_INTEGER) +#define json_is_real(json) ((json) && json_typeof(json) == JSON_REAL) +#define json_is_number(json) (json_is_integer(json) || json_is_real(json)) +#define json_is_true(json) ((json) && json_typeof(json) == JSON_TRUE) +#define json_is_false(json) ((json) && json_typeof(json) == JSON_FALSE) +#define json_boolean_value json_is_true +#define json_is_boolean(json) (json_is_true(json) || json_is_false(json)) +#define json_is_null(json) ((json) && json_typeof(json) == JSON_NULL) + +/* construction, destruction, reference counting */ + +json_t *json_object(void); +json_t *json_array(void); +json_t *json_string(const char *value); +json_t *json_stringn(const char *value, size_t len); +json_t *json_string_nocheck(const char *value); +json_t *json_stringn_nocheck(const char *value, size_t len); +json_t *json_integer(json_int_t value); +json_t *json_real(double value); +json_t *json_true(void); +json_t *json_false(void); +#define json_boolean(val) ((val) ? json_true() : json_false()) +json_t *json_null(void); + +/* do not call JSON_INTERNAL_INCREF or JSON_INTERNAL_DECREF directly */ +#if JSON_HAVE_ATOMIC_BUILTINS +#define JSON_INTERNAL_INCREF(json) __atomic_add_fetch(&json->refcount, 1, __ATOMIC_ACQUIRE) +#define JSON_INTERNAL_DECREF(json) __atomic_sub_fetch(&json->refcount, 1, __ATOMIC_RELEASE) +#elif JSON_HAVE_SYNC_BUILTINS +#define JSON_INTERNAL_INCREF(json) __sync_add_and_fetch(&json->refcount, 1) +#define JSON_INTERNAL_DECREF(json) __sync_sub_and_fetch(&json->refcount, 1) +#else +#define JSON_INTERNAL_INCREF(json) (++json->refcount) +#define JSON_INTERNAL_DECREF(json) (--json->refcount) +#endif + +static JSON_INLINE +json_t *json_incref(json_t *json) +{ + if(json && json->refcount != (size_t)-1) + JSON_INTERNAL_INCREF(json); + return json; +} + +/* do not call json_delete directly */ +void json_delete(json_t *json); + +static JSON_INLINE +void json_decref(json_t *json) +{ + if(json && json->refcount != (size_t)-1 && JSON_INTERNAL_DECREF(json) == 0) + json_delete(json); +} + +#if defined(__GNUC__) || defined(__clang__) +static JSON_INLINE +void json_decrefp(json_t **json) +{ + if(json) { + json_decref(*json); + *json = NULL; + } +} + +#define json_auto_t json_t __attribute__((cleanup(json_decrefp))) +#endif + + +/* error reporting */ + +#define JSON_ERROR_TEXT_LENGTH 160 +#define JSON_ERROR_SOURCE_LENGTH 80 + +typedef struct json_error_t { + int line; + int column; + int position; + char source[JSON_ERROR_SOURCE_LENGTH]; + char text[JSON_ERROR_TEXT_LENGTH]; +} json_error_t; + +enum json_error_code { + json_error_unknown, + json_error_out_of_memory, + json_error_stack_overflow, + json_error_cannot_open_file, + json_error_invalid_argument, + json_error_invalid_utf8, + json_error_premature_end_of_input, + json_error_end_of_input_expected, + json_error_invalid_syntax, + json_error_invalid_format, + json_error_wrong_type, + json_error_null_character, + json_error_null_value, + json_error_null_byte_in_key, + json_error_duplicate_key, + json_error_numeric_overflow, + json_error_item_not_found, + json_error_index_out_of_range +}; + +static JSON_INLINE enum json_error_code json_error_code(const json_error_t *e) { + return (enum json_error_code)e->text[JSON_ERROR_TEXT_LENGTH - 1]; +} + +/* getters, setters, manipulation */ + +void json_object_seed(size_t seed); +size_t json_object_size(const json_t *object); +json_t *json_object_get(const json_t *object, const char *key); +int json_object_set_new(json_t *object, const char *key, json_t *value); +int json_object_set_new_nocheck(json_t *object, const char *key, json_t *value); +int json_object_del(json_t *object, const char *key); +int json_object_clear(json_t *object); +int json_object_update(json_t *object, json_t *other); +int json_object_update_existing(json_t *object, json_t *other); +int json_object_update_missing(json_t *object, json_t *other); +void *json_object_iter(json_t *object); +void *json_object_iter_at(json_t *object, const char *key); +void *json_object_key_to_iter(const char *key); +void *json_object_iter_next(json_t *object, void *iter); +const char *json_object_iter_key(void *iter); +json_t *json_object_iter_value(void *iter); +int json_object_iter_set_new(json_t *object, void *iter, json_t *value); + +#define json_object_foreach(object, key, value) \ + for(key = json_object_iter_key(json_object_iter(object)); \ + key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ + key = json_object_iter_key(json_object_iter_next(object, json_object_key_to_iter(key)))) + +#define json_object_foreach_safe(object, n, key, value) \ + for(key = json_object_iter_key(json_object_iter(object)), \ + n = json_object_iter_next(object, json_object_key_to_iter(key)); \ + key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ + key = json_object_iter_key(n), \ + n = json_object_iter_next(object, json_object_key_to_iter(key))) + +#define json_array_foreach(array, index, value) \ + for(index = 0; \ + index < json_array_size(array) && (value = json_array_get(array, index)); \ + index++) + +static JSON_INLINE +int json_object_set(json_t *object, const char *key, json_t *value) +{ + return json_object_set_new(object, key, json_incref(value)); +} + +static JSON_INLINE +int json_object_set_nocheck(json_t *object, const char *key, json_t *value) +{ + return json_object_set_new_nocheck(object, key, json_incref(value)); +} + +static JSON_INLINE +int json_object_iter_set(json_t *object, void *iter, json_t *value) +{ + return json_object_iter_set_new(object, iter, json_incref(value)); +} + +size_t json_array_size(const json_t *array); +json_t *json_array_get(const json_t *array, size_t index); +int json_array_set_new(json_t *array, size_t index, json_t *value); +int json_array_append_new(json_t *array, json_t *value); +int json_array_insert_new(json_t *array, size_t index, json_t *value); +int json_array_remove(json_t *array, size_t index); +int json_array_clear(json_t *array); +int json_array_extend(json_t *array, json_t *other); + +static JSON_INLINE +int json_array_set(json_t *array, size_t ind, json_t *value) +{ + return json_array_set_new(array, ind, json_incref(value)); +} + +static JSON_INLINE +int json_array_append(json_t *array, json_t *value) +{ + return json_array_append_new(array, json_incref(value)); +} + +static JSON_INLINE +int json_array_insert(json_t *array, size_t ind, json_t *value) +{ + return json_array_insert_new(array, ind, json_incref(value)); +} + +const char *json_string_value(const json_t *string); +size_t json_string_length(const json_t *string); +json_int_t json_integer_value(const json_t *integer); +double json_real_value(const json_t *real); +double json_number_value(const json_t *json); + +int json_string_set(json_t *string, const char *value); +int json_string_setn(json_t *string, const char *value, size_t len); +int json_string_set_nocheck(json_t *string, const char *value); +int json_string_setn_nocheck(json_t *string, const char *value, size_t len); +int json_integer_set(json_t *integer, json_int_t value); +int json_real_set(json_t *real, double value); + +/* pack, unpack */ + +json_t *json_pack(const char *fmt, ...); +json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...); +json_t *json_vpack_ex(json_error_t *error, size_t flags, const char *fmt, va_list ap); + +#define JSON_VALIDATE_ONLY 0x1 +#define JSON_STRICT 0x2 + +int json_unpack(json_t *root, const char *fmt, ...); +int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...); +int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, va_list ap); + +/* sprintf */ + +json_t *json_sprintf(const char *fmt, ...); +json_t *json_vsprintf(const char *fmt, va_list ap); + + +/* equality */ + +int json_equal(const json_t *value1, const json_t *value2); + + +/* copying */ + +json_t *json_copy(json_t *value); +json_t *json_deep_copy(const json_t *value); + + +/* decoding */ + +#define JSON_REJECT_DUPLICATES 0x1 +#define JSON_DISABLE_EOF_CHECK 0x2 +#define JSON_DECODE_ANY 0x4 +#define JSON_DECODE_INT_AS_REAL 0x8 +#define JSON_ALLOW_NUL 0x10 + +typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data); + +json_t *json_loads(const char *input, size_t flags, json_error_t *error); +json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error); +json_t *json_loadf(FILE *input, size_t flags, json_error_t *error); +json_t *json_loadfd(int input, size_t flags, json_error_t *error); +json_t *json_load_file(const char *path, size_t flags, json_error_t *error); +json_t *json_load_callback(json_load_callback_t callback, void *data, size_t flags, json_error_t *error); + + +/* encoding */ + +#define JSON_MAX_INDENT 0x1F +#define JSON_INDENT(n) ((n) & JSON_MAX_INDENT) +#define JSON_COMPACT 0x20 +#define JSON_ENSURE_ASCII 0x40 +#define JSON_SORT_KEYS 0x80 +#define JSON_PRESERVE_ORDER 0x100 +#define JSON_ENCODE_ANY 0x200 +#define JSON_ESCAPE_SLASH 0x400 +#define JSON_REAL_PRECISION(n) (((n) & 0x1F) << 11) +#define JSON_EMBED 0x10000 + +typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data); + +char *json_dumps(const json_t *json, size_t flags); +size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags); +int json_dumpf(const json_t *json, FILE *output, size_t flags); +int json_dumpfd(const json_t *json, int output, size_t flags); +int json_dump_file(const json_t *json, const char *path, size_t flags); +int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags); + +/* custom memory allocation */ + +typedef void *(*json_malloc_t)(size_t); +typedef void (*json_free_t)(void *); + +void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn); +void json_get_alloc_funcs(json_malloc_t *malloc_fn, json_free_t *free_fn); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/client/jansson/jansson_config.h b/client/jansson/jansson_config.h new file mode 100644 index 00000000..b34baa55 --- /dev/null +++ b/client/jansson/jansson_config.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2010-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + * + * + * This file specifies a part of the site-specific configuration for + * Jansson, namely those things that affect the public API in + * jansson.h. + * + * The configure script copies this file to jansson_config.h and + * replaces @var@ substitutions by values that fit your system. If you + * cannot run the configure script, you can do the value substitution + * by hand. + */ + +#ifndef JANSSON_CONFIG_H +#define JANSSON_CONFIG_H + +/* If your compiler supports the inline keyword in C, JSON_INLINE is + defined to `inline', otherwise empty. In C++, the inline is always + supported. */ +#ifdef __cplusplus +#define JSON_INLINE inline +#else +#define JSON_INLINE inline +#endif + +/* If your compiler supports the `long long` type and the strtoll() + library function, JSON_INTEGER_IS_LONG_LONG is defined to 1, + otherwise to 0. */ +#define JSON_INTEGER_IS_LONG_LONG 1 + +/* If locale.h and localeconv() are available, define to 1, + otherwise to 0. */ +#define JSON_HAVE_LOCALECONV 1 + +/* If __atomic builtins are available they will be used to manage + reference counts of json_t. */ +#define JSON_HAVE_ATOMIC_BUILTINS 1 + +/* If __atomic builtins are not available we try using __sync builtins + to manage reference counts of json_t. */ +#define JSON_HAVE_SYNC_BUILTINS 1 + +/* Maximum recursion depth for parsing JSON input. + This limits the depth of e.g. array-within-array constructions. */ +#define JSON_PARSER_MAX_DEPTH 2048 + +#endif diff --git a/client/jansson/jansson_config.h.in b/client/jansson/jansson_config.h.in new file mode 100644 index 00000000..fe692ab4 --- /dev/null +++ b/client/jansson/jansson_config.h.in @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2010-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + * + * + * This file specifies a part of the site-specific configuration for + * Jansson, namely those things that affect the public API in + * jansson.h. + * + * The configure script copies this file to jansson_config.h and + * replaces @var@ substitutions by values that fit your system. If you + * cannot run the configure script, you can do the value substitution + * by hand. + */ + +#ifndef JANSSON_CONFIG_H +#define JANSSON_CONFIG_H + +/* If your compiler supports the inline keyword in C, JSON_INLINE is + defined to `inline', otherwise empty. In C++, the inline is always + supported. */ +#ifdef __cplusplus +#define JSON_INLINE inline +#else +#define JSON_INLINE @json_inline@ +#endif + +/* If your compiler supports the `long long` type and the strtoll() + library function, JSON_INTEGER_IS_LONG_LONG is defined to 1, + otherwise to 0. */ +#define JSON_INTEGER_IS_LONG_LONG @json_have_long_long@ + +/* If locale.h and localeconv() are available, define to 1, + otherwise to 0. */ +#define JSON_HAVE_LOCALECONV @json_have_localeconv@ + +/* If __atomic builtins are available they will be used to manage + reference counts of json_t. */ +#define JSON_HAVE_ATOMIC_BUILTINS @json_have_atomic_builtins@ + +/* If __atomic builtins are not available we try using __sync builtins + to manage reference counts of json_t. */ +#define JSON_HAVE_SYNC_BUILTINS @json_have_sync_builtins@ + +/* Maximum recursion depth for parsing JSON input. + This limits the depth of e.g. array-within-array constructions. */ +#define JSON_PARSER_MAX_DEPTH 2048 + +#endif diff --git a/client/jansson/jansson_private.h b/client/jansson/jansson_private.h new file mode 100644 index 00000000..7b0985aa --- /dev/null +++ b/client/jansson/jansson_private.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef JANSSON_PRIVATE_H +#define JANSSON_PRIVATE_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include "jansson.h" +#include "hashtable.h" +#include "strbuffer.h" + +#define container_of(ptr_, type_, member_) \ + ((type_ *)((char *)ptr_ - offsetof(type_, member_))) + +/* On some platforms, max() may already be defined */ +#ifndef max +#define max(a, b) ((a) > (b) ? (a) : (b)) +#endif + +/* va_copy is a C99 feature. In C89 implementations, it's sometimes + available as __va_copy. If not, memcpy() should do the trick. */ +#ifndef va_copy +#ifdef __va_copy +#define va_copy __va_copy +#else +#define va_copy(a, b) memcpy(&(a), &(b), sizeof(va_list)) +#endif +#endif + +typedef struct { + json_t json; + hashtable_t hashtable; +} json_object_t; + +typedef struct { + json_t json; + size_t size; + size_t entries; + json_t **table; +} json_array_t; + +typedef struct { + json_t json; + char *value; + size_t length; +} json_string_t; + +typedef struct { + json_t json; + double value; +} json_real_t; + +typedef struct { + json_t json; + json_int_t value; +} json_integer_t; + +#define json_to_object(json_) container_of(json_, json_object_t, json) +#define json_to_array(json_) container_of(json_, json_array_t, json) +#define json_to_string(json_) container_of(json_, json_string_t, json) +#define json_to_real(json_) container_of(json_, json_real_t, json) +#define json_to_integer(json_) container_of(json_, json_integer_t, json) + +/* Create a string by taking ownership of an existing buffer */ +json_t *jsonp_stringn_nocheck_own(const char *value, size_t len); + +/* Error message formatting */ +void jsonp_error_init(json_error_t *error, const char *source); +void jsonp_error_set_source(json_error_t *error, const char *source); +void jsonp_error_set(json_error_t *error, int line, int column, + size_t position, enum json_error_code code, + const char *msg, ...); +void jsonp_error_vset(json_error_t *error, int line, int column, + size_t position, enum json_error_code code, + const char *msg, va_list ap); + +/* Locale independent string<->double conversions */ +int jsonp_strtod(strbuffer_t *strbuffer, double *out); +int jsonp_dtostr(char *buffer, size_t size, double value, int prec); + +/* Wrappers for custom memory functions */ +void* jsonp_malloc(size_t size); +void jsonp_free(void *ptr); +char *jsonp_strndup(const char *str, size_t length); +char *jsonp_strdup(const char *str); +char *jsonp_strndup(const char *str, size_t len); + + +/* Windows compatibility */ +#if defined(_WIN32) || defined(WIN32) +# if defined(_MSC_VER) /* MS compiller */ +# if (_MSC_VER < 1900) && !defined(snprintf) /* snprintf not defined yet & not introduced */ +# define snprintf _snprintf +# endif +# if (_MSC_VER < 1500) && !defined(vsnprintf) /* vsnprintf not defined yet & not introduced */ +# define vsnprintf(b,c,f,a) _vsnprintf(b,c,f,a) +# endif +# else /* Other Windows compiller, old definition */ +# define snprintf _snprintf +# define vsnprintf _vsnprintf +# endif +#endif + +#endif diff --git a/client/jansson/load.c b/client/jansson/load.c new file mode 100644 index 00000000..deb36f32 --- /dev/null +++ b/client/jansson/load.c @@ -0,0 +1,1155 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include "jansson_private.h" + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "jansson.h" +#include "strbuffer.h" +#include "utf.h" + +#define STREAM_STATE_OK 0 +#define STREAM_STATE_EOF -1 +#define STREAM_STATE_ERROR -2 + +#define TOKEN_INVALID -1 +#define TOKEN_EOF 0 +#define TOKEN_STRING 256 +#define TOKEN_INTEGER 257 +#define TOKEN_REAL 258 +#define TOKEN_TRUE 259 +#define TOKEN_FALSE 260 +#define TOKEN_NULL 261 + +/* Locale independent versions of isxxx() functions */ +#define l_isupper(c) ('A' <= (c) && (c) <= 'Z') +#define l_islower(c) ('a' <= (c) && (c) <= 'z') +#define l_isalpha(c) (l_isupper(c) || l_islower(c)) +#define l_isdigit(c) ('0' <= (c) && (c) <= '9') +#define l_isxdigit(c) \ + (l_isdigit(c) || ('A' <= (c) && (c) <= 'F') || ('a' <= (c) && (c) <= 'f')) + +/* Read one byte from stream, convert to unsigned char, then int, and + return. return EOF on end of file. This corresponds to the + behaviour of fgetc(). */ +typedef int (*get_func)(void *data); + +typedef struct { + get_func get; + void *data; + char buffer[5]; + size_t buffer_pos; + int state; + int line; + int column, last_column; + size_t position; +} stream_t; + +typedef struct { + stream_t stream; + strbuffer_t saved_text; + size_t flags; + size_t depth; + int token; + union { + struct { + char *val; + size_t len; + } string; + json_int_t integer; + double real; + } value; +} lex_t; + +#define stream_to_lex(stream) container_of(stream, lex_t, stream) + + +/*** error reporting ***/ + +static void error_set(json_error_t *error, const lex_t *lex, + enum json_error_code code, + const char *msg, ...) +{ + va_list ap; + char msg_text[JSON_ERROR_TEXT_LENGTH]; + char msg_with_context[JSON_ERROR_TEXT_LENGTH]; + + int line = -1, col = -1; + size_t pos = 0; + const char *result = msg_text; + + if(!error) + return; + + va_start(ap, msg); + vsnprintf(msg_text, JSON_ERROR_TEXT_LENGTH, msg, ap); + msg_text[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; + va_end(ap); + + if(lex) + { + const char *saved_text = strbuffer_value(&lex->saved_text); + + line = lex->stream.line; + col = lex->stream.column; + pos = lex->stream.position; + + if(saved_text && saved_text[0]) + { + if(lex->saved_text.length <= 20) { + snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH, + "%s near '%s'", msg_text, saved_text); + msg_with_context[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; + result = msg_with_context; + } + } + else + { + if(code == json_error_invalid_syntax) { + /* More specific error code for premature end of file. */ + code = json_error_premature_end_of_input; + } + if(lex->stream.state == STREAM_STATE_ERROR) { + /* No context for UTF-8 decoding errors */ + result = msg_text; + } + else { + snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH, + "%s near end of file", msg_text); + msg_with_context[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; + result = msg_with_context; + } + } + } + + jsonp_error_set(error, line, col, pos, code, "%s", result); +} + + +/*** lexical analyzer ***/ + +static void +stream_init(stream_t *stream, get_func get, void *data) +{ + stream->get = get; + stream->data = data; + stream->buffer[0] = '\0'; + stream->buffer_pos = 0; + + stream->state = STREAM_STATE_OK; + stream->line = 1; + stream->column = 0; + stream->position = 0; +} + +static int stream_get(stream_t *stream, json_error_t *error) +{ + int c; + + if(stream->state != STREAM_STATE_OK) + return stream->state; + + if(!stream->buffer[stream->buffer_pos]) + { + c = stream->get(stream->data); + if(c == EOF) { + stream->state = STREAM_STATE_EOF; + return STREAM_STATE_EOF; + } + + stream->buffer[0] = c; + stream->buffer_pos = 0; + + if(0x80 <= c && c <= 0xFF) + { + /* multi-byte UTF-8 sequence */ + size_t i, count; + + count = utf8_check_first(c); + if(!count) + goto out; + + assert(count >= 2); + + for(i = 1; i < count; i++) + stream->buffer[i] = stream->get(stream->data); + + if(!utf8_check_full(stream->buffer, count, NULL)) + goto out; + + stream->buffer[count] = '\0'; + } + else + stream->buffer[1] = '\0'; + } + + c = stream->buffer[stream->buffer_pos++]; + + stream->position++; + if(c == '\n') { + stream->line++; + stream->last_column = stream->column; + stream->column = 0; + } + else if(utf8_check_first(c)) { + /* track the Unicode character column, so increment only if + this is the first character of a UTF-8 sequence */ + stream->column++; + } + + return c; + +out: + stream->state = STREAM_STATE_ERROR; + error_set(error, stream_to_lex(stream), json_error_invalid_utf8, "unable to decode byte 0x%x", c); + return STREAM_STATE_ERROR; +} + +static void stream_unget(stream_t *stream, int c) +{ + if(c == STREAM_STATE_EOF || c == STREAM_STATE_ERROR) + return; + + stream->position--; + if(c == '\n') { + stream->line--; + stream->column = stream->last_column; + } + else if(utf8_check_first(c)) + stream->column--; + + assert(stream->buffer_pos > 0); + stream->buffer_pos--; + assert(stream->buffer[stream->buffer_pos] == c); +} + + +static int lex_get(lex_t *lex, json_error_t *error) +{ + return stream_get(&lex->stream, error); +} + +static void lex_save(lex_t *lex, int c) +{ + strbuffer_append_byte(&lex->saved_text, c); +} + +static int lex_get_save(lex_t *lex, json_error_t *error) +{ + int c = stream_get(&lex->stream, error); + if(c != STREAM_STATE_EOF && c != STREAM_STATE_ERROR) + lex_save(lex, c); + return c; +} + +static void lex_unget(lex_t *lex, int c) +{ + stream_unget(&lex->stream, c); +} + +static void lex_unget_unsave(lex_t *lex, int c) +{ + if(c != STREAM_STATE_EOF && c != STREAM_STATE_ERROR) { + /* Since we treat warnings as errors, when assertions are turned + * off the "d" variable would be set but never used. Which is + * treated as an error by GCC. + */ + #ifndef NDEBUG + char d; + #endif + stream_unget(&lex->stream, c); + #ifndef NDEBUG + d = + #endif + strbuffer_pop(&lex->saved_text); + assert(c == d); + } +} + +static void lex_save_cached(lex_t *lex) +{ + while(lex->stream.buffer[lex->stream.buffer_pos] != '\0') + { + lex_save(lex, lex->stream.buffer[lex->stream.buffer_pos]); + lex->stream.buffer_pos++; + lex->stream.position++; + } +} + +static void lex_free_string(lex_t *lex) +{ + jsonp_free(lex->value.string.val); + lex->value.string.val = NULL; + lex->value.string.len = 0; +} + +/* assumes that str points to 'u' plus at least 4 valid hex digits */ +static int32_t decode_unicode_escape(const char *str) +{ + int i; + int32_t value = 0; + + assert(str[0] == 'u'); + + for(i = 1; i <= 4; i++) { + char c = str[i]; + value <<= 4; + if(l_isdigit(c)) + value += c - '0'; + else if(l_islower(c)) + value += c - 'a' + 10; + else if(l_isupper(c)) + value += c - 'A' + 10; + else + return -1; + } + + return value; +} + +static void lex_scan_string(lex_t *lex, json_error_t *error) +{ + int c; + const char *p; + char *t; + int i; + + lex->value.string.val = NULL; + lex->token = TOKEN_INVALID; + + c = lex_get_save(lex, error); + + while(c != '"') { + if(c == STREAM_STATE_ERROR) + goto out; + + else if(c == STREAM_STATE_EOF) { + error_set(error, lex, json_error_premature_end_of_input, "premature end of input"); + goto out; + } + + else if(0 <= c && c <= 0x1F) { + /* control character */ + lex_unget_unsave(lex, c); + if(c == '\n') + error_set(error, lex, json_error_invalid_syntax, "unexpected newline"); + else + error_set(error, lex, json_error_invalid_syntax, "control character 0x%x", c); + goto out; + } + + else if(c == '\\') { + c = lex_get_save(lex, error); + if(c == 'u') { + c = lex_get_save(lex, error); + for(i = 0; i < 4; i++) { + if(!l_isxdigit(c)) { + error_set(error, lex, json_error_invalid_syntax, "invalid escape"); + goto out; + } + c = lex_get_save(lex, error); + } + } + else if(c == '"' || c == '\\' || c == '/' || c == 'b' || + c == 'f' || c == 'n' || c == 'r' || c == 't') + c = lex_get_save(lex, error); + else { + error_set(error, lex, json_error_invalid_syntax, "invalid escape"); + goto out; + } + } + else + c = lex_get_save(lex, error); + } + + /* the actual value is at most of the same length as the source + string, because: + - shortcut escapes (e.g. "\t") (length 2) are converted to 1 byte + - a single \uXXXX escape (length 6) is converted to at most 3 bytes + - two \uXXXX escapes (length 12) forming an UTF-16 surrogate pair + are converted to 4 bytes + */ + t = jsonp_malloc(lex->saved_text.length + 1); + if(!t) { + /* this is not very nice, since TOKEN_INVALID is returned */ + goto out; + } + lex->value.string.val = t; + + /* + 1 to skip the " */ + p = strbuffer_value(&lex->saved_text) + 1; + + while(*p != '"') { + if(*p == '\\') { + p++; + if(*p == 'u') { + size_t length; + int32_t value; + + value = decode_unicode_escape(p); + if(value < 0) { + error_set(error, lex, json_error_invalid_syntax, "invalid Unicode escape '%.6s'", p - 1); + goto out; + } + p += 5; + + if(0xD800 <= value && value <= 0xDBFF) { + /* surrogate pair */ + if(*p == '\\' && *(p + 1) == 'u') { + int32_t value2 = decode_unicode_escape(++p); + if(value2 < 0) { + error_set(error, lex, json_error_invalid_syntax, "invalid Unicode escape '%.6s'", p - 1); + goto out; + } + p += 5; + + if(0xDC00 <= value2 && value2 <= 0xDFFF) { + /* valid second surrogate */ + value = + ((value - 0xD800) << 10) + + (value2 - 0xDC00) + + 0x10000; + } + else { + /* invalid second surrogate */ + error_set(error, lex, + json_error_invalid_syntax, + "invalid Unicode '\\u%04X\\u%04X'", + value, value2); + goto out; + } + } + else { + /* no second surrogate */ + error_set(error, lex, json_error_invalid_syntax, "invalid Unicode '\\u%04X'", + value); + goto out; + } + } + else if(0xDC00 <= value && value <= 0xDFFF) { + error_set(error, lex, json_error_invalid_syntax, "invalid Unicode '\\u%04X'", value); + goto out; + } + + if(utf8_encode(value, t, &length)) + assert(0); + t += length; + } + else { + switch(*p) { + case '"': case '\\': case '/': + *t = *p; break; + case 'b': *t = '\b'; break; + case 'f': *t = '\f'; break; + case 'n': *t = '\n'; break; + case 'r': *t = '\r'; break; + case 't': *t = '\t'; break; + default: assert(0); + } + t++; + p++; + } + } + else + *(t++) = *(p++); + } + *t = '\0'; + lex->value.string.len = t - lex->value.string.val; + lex->token = TOKEN_STRING; + return; + +out: + lex_free_string(lex); +} + +#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */ +#if JSON_INTEGER_IS_LONG_LONG +#ifdef _MSC_VER /* Microsoft Visual Studio */ +#define json_strtoint _strtoi64 +#else +#define json_strtoint strtoll +#endif +#else +#define json_strtoint strtol +#endif +#endif + +static int lex_scan_number(lex_t *lex, int c, json_error_t *error) +{ + const char *saved_text; + char *end; + double doubleval; + + lex->token = TOKEN_INVALID; + + if(c == '-') + c = lex_get_save(lex, error); + + if(c == '0') { + c = lex_get_save(lex, error); + if(l_isdigit(c)) { + lex_unget_unsave(lex, c); + goto out; + } + } + else if(l_isdigit(c)) { + do + c = lex_get_save(lex, error); + while(l_isdigit(c)); + } + else { + lex_unget_unsave(lex, c); + goto out; + } + + if(!(lex->flags & JSON_DECODE_INT_AS_REAL) && + c != '.' && c != 'E' && c != 'e') + { + json_int_t intval; + + lex_unget_unsave(lex, c); + + saved_text = strbuffer_value(&lex->saved_text); + + errno = 0; + intval = json_strtoint(saved_text, &end, 10); + if(errno == ERANGE) { + if(intval < 0) + error_set(error, lex, json_error_numeric_overflow, "too big negative integer"); + else + error_set(error, lex, json_error_numeric_overflow, "too big integer"); + goto out; + } + + assert(end == saved_text + lex->saved_text.length); + + lex->token = TOKEN_INTEGER; + lex->value.integer = intval; + return 0; + } + + if(c == '.') { + c = lex_get(lex, error); + if(!l_isdigit(c)) { + lex_unget(lex, c); + goto out; + } + lex_save(lex, c); + + do + c = lex_get_save(lex, error); + while(l_isdigit(c)); + } + + if(c == 'E' || c == 'e') { + c = lex_get_save(lex, error); + if(c == '+' || c == '-') + c = lex_get_save(lex, error); + + if(!l_isdigit(c)) { + lex_unget_unsave(lex, c); + goto out; + } + + do + c = lex_get_save(lex, error); + while(l_isdigit(c)); + } + + lex_unget_unsave(lex, c); + + if(jsonp_strtod(&lex->saved_text, &doubleval)) { + error_set(error, lex, json_error_numeric_overflow, "real number overflow"); + goto out; + } + + lex->token = TOKEN_REAL; + lex->value.real = doubleval; + return 0; + +out: + return -1; +} + +static int lex_scan(lex_t *lex, json_error_t *error) +{ + int c; + + strbuffer_clear(&lex->saved_text); + + if(lex->token == TOKEN_STRING) + lex_free_string(lex); + + do + c = lex_get(lex, error); + while(c == ' ' || c == '\t' || c == '\n' || c == '\r'); + + if(c == STREAM_STATE_EOF) { + lex->token = TOKEN_EOF; + goto out; + } + + if(c == STREAM_STATE_ERROR) { + lex->token = TOKEN_INVALID; + goto out; + } + + lex_save(lex, c); + + if(c == '{' || c == '}' || c == '[' || c == ']' || c == ':' || c == ',') + lex->token = c; + + else if(c == '"') + lex_scan_string(lex, error); + + else if(l_isdigit(c) || c == '-') { + if(lex_scan_number(lex, c, error)) + goto out; + } + + else if(l_isalpha(c)) { + /* eat up the whole identifier for clearer error messages */ + const char *saved_text; + + do + c = lex_get_save(lex, error); + while(l_isalpha(c)); + lex_unget_unsave(lex, c); + + saved_text = strbuffer_value(&lex->saved_text); + + if(strcmp(saved_text, "true") == 0) + lex->token = TOKEN_TRUE; + else if(strcmp(saved_text, "false") == 0) + lex->token = TOKEN_FALSE; + else if(strcmp(saved_text, "null") == 0) + lex->token = TOKEN_NULL; + else + lex->token = TOKEN_INVALID; + } + + else { + /* save the rest of the input UTF-8 sequence to get an error + message of valid UTF-8 */ + lex_save_cached(lex); + lex->token = TOKEN_INVALID; + } + +out: + return lex->token; +} + +static char *lex_steal_string(lex_t *lex, size_t *out_len) +{ + char *result = NULL; + if(lex->token == TOKEN_STRING) { + result = lex->value.string.val; + *out_len = lex->value.string.len; + lex->value.string.val = NULL; + lex->value.string.len = 0; + } + return result; +} + +static int lex_init(lex_t *lex, get_func get, size_t flags, void *data) +{ + stream_init(&lex->stream, get, data); + if(strbuffer_init(&lex->saved_text)) + return -1; + + lex->flags = flags; + lex->token = TOKEN_INVALID; + return 0; +} + +static void lex_close(lex_t *lex) +{ + if(lex->token == TOKEN_STRING) + lex_free_string(lex); + strbuffer_close(&lex->saved_text); +} + + +/*** parser ***/ + +static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error); + +static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error) +{ + json_t *object = json_object(); + if(!object) + return NULL; + + lex_scan(lex, error); + if(lex->token == '}') + return object; + + while(1) { + char *key; + size_t len; + json_t *value; + + if(lex->token != TOKEN_STRING) { + error_set(error, lex, json_error_invalid_syntax, "string or '}' expected"); + goto error; + } + + key = lex_steal_string(lex, &len); + if(!key) + return NULL; + if (memchr(key, '\0', len)) { + jsonp_free(key); + error_set(error, lex, json_error_null_byte_in_key, "NUL byte in object key not supported"); + goto error; + } + + if(flags & JSON_REJECT_DUPLICATES) { + if(json_object_get(object, key)) { + jsonp_free(key); + error_set(error, lex, json_error_duplicate_key, "duplicate object key"); + goto error; + } + } + + lex_scan(lex, error); + if(lex->token != ':') { + jsonp_free(key); + error_set(error, lex, json_error_invalid_syntax, "':' expected"); + goto error; + } + + lex_scan(lex, error); + value = parse_value(lex, flags, error); + if(!value) { + jsonp_free(key); + goto error; + } + + if(json_object_set_new_nocheck(object, key, value)) { + jsonp_free(key); + goto error; + } + + jsonp_free(key); + + lex_scan(lex, error); + if(lex->token != ',') + break; + + lex_scan(lex, error); + } + + if(lex->token != '}') { + error_set(error, lex, json_error_invalid_syntax, "'}' expected"); + goto error; + } + + return object; + +error: + json_decref(object); + return NULL; +} + +static json_t *parse_array(lex_t *lex, size_t flags, json_error_t *error) +{ + json_t *array = json_array(); + if(!array) + return NULL; + + lex_scan(lex, error); + if(lex->token == ']') + return array; + + while(lex->token) { + json_t *elem = parse_value(lex, flags, error); + if(!elem) + goto error; + + if(json_array_append_new(array, elem)) { + goto error; + } + + lex_scan(lex, error); + if(lex->token != ',') + break; + + lex_scan(lex, error); + } + + if(lex->token != ']') { + error_set(error, lex, json_error_invalid_syntax, "']' expected"); + goto error; + } + + return array; + +error: + json_decref(array); + return NULL; +} + +static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error) +{ + json_t *json; + + lex->depth++; + if(lex->depth > JSON_PARSER_MAX_DEPTH) { + error_set(error, lex, json_error_stack_overflow, "maximum parsing depth reached"); + return NULL; + } + + switch(lex->token) { + case TOKEN_STRING: { + const char *value = lex->value.string.val; + size_t len = lex->value.string.len; + + if(!(flags & JSON_ALLOW_NUL)) { + if(memchr(value, '\0', len)) { + error_set(error, lex, json_error_null_character, "\\u0000 is not allowed without JSON_ALLOW_NUL"); + return NULL; + } + } + + json = jsonp_stringn_nocheck_own(value, len); + if(json) { + lex->value.string.val = NULL; + lex->value.string.len = 0; + } + break; + } + + case TOKEN_INTEGER: { + json = json_integer(lex->value.integer); + break; + } + + case TOKEN_REAL: { + json = json_real(lex->value.real); + break; + } + + case TOKEN_TRUE: + json = json_true(); + break; + + case TOKEN_FALSE: + json = json_false(); + break; + + case TOKEN_NULL: + json = json_null(); + break; + + case '{': + json = parse_object(lex, flags, error); + break; + + case '[': + json = parse_array(lex, flags, error); + break; + + case TOKEN_INVALID: + error_set(error, lex, json_error_invalid_syntax, "invalid token"); + return NULL; + + default: + error_set(error, lex, json_error_invalid_syntax, "unexpected token"); + return NULL; + } + + if(!json) + return NULL; + + lex->depth--; + return json; +} + +static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error) +{ + json_t *result; + + lex->depth = 0; + + lex_scan(lex, error); + if(!(flags & JSON_DECODE_ANY)) { + if(lex->token != '[' && lex->token != '{') { + error_set(error, lex, json_error_invalid_syntax, "'[' or '{' expected"); + return NULL; + } + } + + result = parse_value(lex, flags, error); + if(!result) + return NULL; + + if(!(flags & JSON_DISABLE_EOF_CHECK)) { + lex_scan(lex, error); + if(lex->token != TOKEN_EOF) { + error_set(error, lex, json_error_end_of_input_expected, "end of file expected"); + json_decref(result); + return NULL; + } + } + + if(error) { + /* Save the position even though there was no error */ + error->position = (int)lex->stream.position; + } + + return result; +} + +typedef struct +{ + const char *data; + size_t pos; +} string_data_t; + +static int string_get(void *data) +{ + char c; + string_data_t *stream = (string_data_t *)data; + c = stream->data[stream->pos]; + if(c == '\0') + return EOF; + else + { + stream->pos++; + return (unsigned char)c; + } +} + +json_t *json_loads(const char *string, size_t flags, json_error_t *error) +{ + lex_t lex; + json_t *result; + string_data_t stream_data; + + jsonp_error_init(error, ""); + + if (string == NULL) { + error_set(error, NULL, json_error_invalid_argument, "wrong arguments"); + return NULL; + } + + stream_data.data = string; + stream_data.pos = 0; + + if(lex_init(&lex, string_get, flags, (void *)&stream_data)) + return NULL; + + result = parse_json(&lex, flags, error); + + lex_close(&lex); + return result; +} + +typedef struct +{ + const char *data; + size_t len; + size_t pos; +} buffer_data_t; + +static int buffer_get(void *data) +{ + char c; + buffer_data_t *stream = data; + if(stream->pos >= stream->len) + return EOF; + + c = stream->data[stream->pos]; + stream->pos++; + return (unsigned char)c; +} + +json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error) +{ + lex_t lex; + json_t *result; + buffer_data_t stream_data; + + jsonp_error_init(error, ""); + + if (buffer == NULL) { + error_set(error, NULL, json_error_invalid_argument, "wrong arguments"); + return NULL; + } + + stream_data.data = buffer; + stream_data.pos = 0; + stream_data.len = buflen; + + if(lex_init(&lex, buffer_get, flags, (void *)&stream_data)) + return NULL; + + result = parse_json(&lex, flags, error); + + lex_close(&lex); + return result; +} + +json_t *json_loadf(FILE *input, size_t flags, json_error_t *error) +{ + lex_t lex; + const char *source; + json_t *result; + + if(input == stdin) + source = ""; + else + source = ""; + + jsonp_error_init(error, source); + + if (input == NULL) { + error_set(error, NULL, json_error_invalid_argument, "wrong arguments"); + return NULL; + } + + if(lex_init(&lex, (get_func)fgetc, flags, input)) + return NULL; + + result = parse_json(&lex, flags, error); + + lex_close(&lex); + return result; +} + +static int fd_get_func(int *fd) +{ + uint8_t c; +#ifdef HAVE_UNISTD_H + if (read(*fd, &c, 1) == 1) + return c; +#endif + return EOF; +} + +json_t *json_loadfd(int input, size_t flags, json_error_t *error) +{ + lex_t lex; + const char *source; + json_t *result; + +#ifdef HAVE_UNISTD_H + if(input == STDIN_FILENO) + source = ""; + else +#endif + source = ""; + + jsonp_error_init(error, source); + + if (input < 0) { + error_set(error, NULL, json_error_invalid_argument, "wrong arguments"); + return NULL; + } + + if(lex_init(&lex, (get_func)fd_get_func, flags, &input)) + return NULL; + + result = parse_json(&lex, flags, error); + + lex_close(&lex); + return result; +} + +json_t *json_load_file(const char *path, size_t flags, json_error_t *error) +{ + json_t *result; + FILE *fp; + + jsonp_error_init(error, path); + + if (path == NULL) { + error_set(error, NULL, json_error_invalid_argument, "wrong arguments"); + return NULL; + } + + fp = fopen(path, "rb"); + if(!fp) + { + error_set(error, NULL, json_error_cannot_open_file, "unable to open %s: %s", + path, strerror(errno)); + return NULL; + } + + result = json_loadf(fp, flags, error); + + fclose(fp); + return result; +} + +#define MAX_BUF_LEN 1024 + +typedef struct +{ + char data[MAX_BUF_LEN]; + size_t len; + size_t pos; + json_load_callback_t callback; + void *arg; +} callback_data_t; + +static int callback_get(void *data) +{ + char c; + callback_data_t *stream = data; + + if(stream->pos >= stream->len) { + stream->pos = 0; + stream->len = stream->callback(stream->data, MAX_BUF_LEN, stream->arg); + if(stream->len == 0 || stream->len == (size_t)-1) + return EOF; + } + + c = stream->data[stream->pos]; + stream->pos++; + return (unsigned char)c; +} + +json_t *json_load_callback(json_load_callback_t callback, void *arg, size_t flags, json_error_t *error) +{ + lex_t lex; + json_t *result; + + callback_data_t stream_data; + + memset(&stream_data, 0, sizeof(stream_data)); + stream_data.callback = callback; + stream_data.arg = arg; + + jsonp_error_init(error, ""); + + if (callback == NULL) { + error_set(error, NULL, json_error_invalid_argument, "wrong arguments"); + return NULL; + } + + if(lex_init(&lex, (get_func)callback_get, flags, &stream_data)) + return NULL; + + result = parse_json(&lex, flags, error); + + lex_close(&lex); + return result; +} diff --git a/client/jansson/lookup3.h b/client/jansson/lookup3.h new file mode 100644 index 00000000..2fe4c258 --- /dev/null +++ b/client/jansson/lookup3.h @@ -0,0 +1,381 @@ +/* +------------------------------------------------------------------------------- +lookup3.c, by Bob Jenkins, May 2006, Public Domain. + +These are functions for producing 32-bit hashes for hash table lookup. +hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() +are externally useful functions. Routines to test the hash are included +if SELF_TEST is defined. You can use this free for any purpose. It's in +the public domain. It has no warranty. + +You probably want to use hashlittle(). hashlittle() and hashbig() +hash byte arrays. hashlittle() is is faster than hashbig() on +little-endian machines. Intel and AMD are little-endian machines. +On second thought, you probably want hashlittle2(), which is identical to +hashlittle() except it returns two 32-bit hashes for the price of one. +You could implement hashbig2() if you wanted but I haven't bothered here. + +If you want to find a hash of, say, exactly 7 integers, do + a = i1; b = i2; c = i3; + mix(a,b,c); + a += i4; b += i5; c += i6; + mix(a,b,c); + a += i7; + final(a,b,c); +then use c as the hash value. If you have a variable length array of +4-byte integers to hash, use hashword(). If you have a byte array (like +a character string), use hashlittle(). If you have several byte arrays, or +a mix of things, see the comments above hashlittle(). + +Why is this so big? I read 12 bytes at a time into 3 4-byte integers, +then mix those integers. This is fast (you can do a lot more thorough +mixing with 12*3 instructions on 3 integers than you can with 3 instructions +on 1 byte), but shoehorning those bytes into integers efficiently is messy. +------------------------------------------------------------------------------- +*/ + +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_STDINT_H +#include /* defines uint32_t etc */ +#endif + +#ifdef HAVE_SYS_PARAM_H +#include /* attempt to define endianness */ +#endif + +#ifdef HAVE_ENDIAN_H +# include /* attempt to define endianness */ +#endif + +/* + * My best guess at if you are big-endian or little-endian. This may + * need adjustment. + */ +#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ + __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(i386) || defined(__i386__) || defined(__i486__) || \ + defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL)) +# define HASH_LITTLE_ENDIAN 1 +# define HASH_BIG_ENDIAN 0 +#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ + __BYTE_ORDER == __BIG_ENDIAN) || \ + (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel)) +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 1 +#else +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 0 +#endif + +#define hashsize(n) ((uint32_t)1<<(n)) +#define hashmask(n) (hashsize(n)-1) +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + +/* +------------------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. + +This is reversible, so any information in (a,b,c) before mix() is +still in (a,b,c) after mix(). + +If four pairs of (a,b,c) inputs are run through mix(), or through +mix() in reverse, there are at least 32 bits of the output that +are sometimes the same for one pair and different for another pair. +This was tested for: +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that +satisfy this are + 4 6 8 16 19 4 + 9 15 3 18 27 15 + 14 9 3 7 17 3 +Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing +for "differ" defined as + with a one-bit base and a two-bit delta. I +used http://burtleburtle.net/bob/hash/avalanche.html to choose +the operations, constants, and arrangements of the variables. + +This does not achieve avalanche. There are input bits of (a,b,c) +that fail to affect some output bits of (a,b,c), especially of a. The +most thoroughly mixed value is c, but it doesn't really even achieve +avalanche in c. + +This allows some parallelism. Read-after-writes are good at doubling +the number of bits affected, so the goal of mixing pulls in the opposite +direction as the goal of parallelism. I did what I could. Rotates +seem to cost as much as shifts on every machine I could lay my hands +on, and rotates are much kinder to the top and bottom bits, so I used +rotates. +------------------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ +{ \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} + +/* +------------------------------------------------------------------------------- +final -- final mixing of 3 32-bit values (a,b,c) into c + +Pairs of (a,b,c) values differing in only a few bits will usually +produce values of c that look totally different. This was tested for +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +These constants passed: + 14 11 25 16 4 14 24 + 12 14 25 16 4 14 24 +and these came close: + 4 8 15 26 3 22 24 + 10 8 15 26 3 22 24 + 11 8 15 26 3 22 24 +------------------------------------------------------------------------------- +*/ +#define final(a,b,c) \ +{ \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} + +/* +------------------------------------------------------------------------------- +hashlittle() -- hash a variable-length key into a 32-bit value + k : the key (the unaligned variable-length array of bytes) + length : the length of the key, counting by bytes + initval : can be any 4-byte value +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Two keys differing by one or two bits will have +totally different hash values. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (uint8_t **)k, do it like this: + for (i=0, h=0; i 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef NO_MASKING_TRICK + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : return c; + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : return c; /* zero length requires no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((uint32_t)k[11])<<24; /* fall through */ + case 11: c+=((uint32_t)k[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k[9])<<8; /* fall through */ + case 9 : c+=k[8]; /* fall through */ + case 8 : b+=((uint32_t)k[7])<<24; /* fall through */ + case 7 : b+=((uint32_t)k[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k[5])<<8; /* fall through */ + case 5 : b+=k[4]; /* fall through */ + case 4 : a+=((uint32_t)k[3])<<24; /* fall through */ + case 3 : a+=((uint32_t)k[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k[1])<<8; /* fall through */ + case 1 : a+=k[0]; + break; + case 0 : return c; + } + } + + final(a,b,c); + return c; +} diff --git a/client/jansson/memory.c b/client/jansson/memory.c new file mode 100644 index 00000000..a2be5d23 --- /dev/null +++ b/client/jansson/memory.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * Copyright (c) 2011-2012 Basile Starynkevitch + * + * Jansson is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See LICENSE for details. + */ + +#include +#include + +#include "jansson.h" +#include "jansson_private.h" + +/* C89 allows these to be macros */ +#undef malloc +#undef free + +/* memory function pointers */ +static json_malloc_t do_malloc = malloc; +static json_free_t do_free = free; + +void *jsonp_malloc(size_t size) +{ + if(!size) + return NULL; + + return (*do_malloc)(size); +} + +void jsonp_free(void *ptr) +{ + if(!ptr) + return; + + (*do_free)(ptr); +} + +char *jsonp_strdup(const char *str) +{ + return jsonp_strndup(str, strlen(str)); +} + +char *jsonp_strndup(const char *str, size_t len) +{ + char *new_str; + + new_str = jsonp_malloc(len + 1); + if(!new_str) + return NULL; + + memcpy(new_str, str, len); + new_str[len] = '\0'; + return new_str; +} + +void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn) +{ + do_malloc = malloc_fn; + do_free = free_fn; +} + +void json_get_alloc_funcs(json_malloc_t *malloc_fn, json_free_t *free_fn) +{ + if (malloc_fn) + *malloc_fn = do_malloc; + if (free_fn) + *free_fn = do_free; +} diff --git a/client/jansson/pack_unpack.c b/client/jansson/pack_unpack.c new file mode 100644 index 00000000..153f64d1 --- /dev/null +++ b/client/jansson/pack_unpack.c @@ -0,0 +1,909 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * Copyright (c) 2011-2012 Graeme Smecher + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include +#include "jansson.h" +#include "jansson_private.h" +#include "utf.h" + +typedef struct { + int line; + int column; + size_t pos; + char token; +} token_t; + +typedef struct { + const char *start; + const char *fmt; + token_t prev_token; + token_t token; + token_t next_token; + json_error_t *error; + size_t flags; + int line; + int column; + size_t pos; + int has_error; +} scanner_t; + +#define token(scanner) ((scanner)->token.token) + +static const char * const type_names[] = { + "object", + "array", + "string", + "integer", + "real", + "true", + "false", + "null" +}; + +#define type_name(x) type_names[json_typeof(x)] + +static const char unpack_value_starters[] = "{[siIbfFOon"; + +static void scanner_init(scanner_t *s, json_error_t *error, + size_t flags, const char *fmt) +{ + s->error = error; + s->flags = flags; + s->fmt = s->start = fmt; + memset(&s->prev_token, 0, sizeof(token_t)); + memset(&s->token, 0, sizeof(token_t)); + memset(&s->next_token, 0, sizeof(token_t)); + s->line = 1; + s->column = 0; + s->pos = 0; + s->has_error = 0; +} + +static void next_token(scanner_t *s) +{ + const char *t; + s->prev_token = s->token; + + if(s->next_token.line) { + s->token = s->next_token; + s->next_token.line = 0; + return; + } + + t = s->fmt; + s->column++; + s->pos++; + + /* skip space and ignored chars */ + while(*t == ' ' || *t == '\t' || *t == '\n' || *t == ',' || *t == ':') { + if(*t == '\n') { + s->line++; + s->column = 1; + } + else + s->column++; + + s->pos++; + t++; + } + + s->token.token = *t; + s->token.line = s->line; + s->token.column = s->column; + s->token.pos = s->pos; + + t++; + s->fmt = t; +} + +static void prev_token(scanner_t *s) +{ + s->next_token = s->token; + s->token = s->prev_token; +} + +static void set_error(scanner_t *s, const char *source, enum json_error_code code, + const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + jsonp_error_vset(s->error, s->token.line, s->token.column, s->token.pos, + code, fmt, ap); + + jsonp_error_set_source(s->error, source); + + va_end(ap); +} + +static json_t *pack(scanner_t *s, va_list *ap); + + +/* ours will be set to 1 if jsonp_free() must be called for the result + afterwards */ +static char *read_string(scanner_t *s, va_list *ap, + const char *purpose, size_t *out_len, int *ours) +{ + char t; + strbuffer_t strbuff; + const char *str; + size_t length; + + next_token(s); + t = token(s); + prev_token(s); + + *ours = 0; + if(t != '#' && t != '%' && t != '+') { + /* Optimize the simple case */ + str = va_arg(*ap, const char *); + + if(!str) { + set_error(s, "", json_error_null_value, "NULL string argument"); + return NULL; + } + + length = strlen(str); + + if(!utf8_check_string(str, length)) { + set_error(s, "", json_error_invalid_utf8, "Invalid UTF-8 %s", purpose); + return NULL; + } + + *out_len = length; + return (char *)str; + } + + strbuffer_init(&strbuff); + + while(1) { + str = va_arg(*ap, const char *); + if(!str) { + set_error(s, "", json_error_null_value, "NULL string argument"); + s->has_error = 1; + } + + next_token(s); + + if(token(s) == '#') { + length = va_arg(*ap, int); + } + else if(token(s) == '%') { + length = va_arg(*ap, size_t); + } + else { + prev_token(s); + length = s->has_error ? 0 : strlen(str); + } + + if(!s->has_error && strbuffer_append_bytes(&strbuff, str, length) == -1) { + set_error(s, "", json_error_out_of_memory, "Out of memory"); + s->has_error = 1; + } + + next_token(s); + if(token(s) != '+') { + prev_token(s); + break; + } + } + + if(s->has_error) { + strbuffer_close(&strbuff); + return NULL; + } + + if(!utf8_check_string(strbuff.value, strbuff.length)) { + set_error(s, "", json_error_invalid_utf8, "Invalid UTF-8 %s", purpose); + strbuffer_close(&strbuff); + s->has_error = 1; + return NULL; + } + + *out_len = strbuff.length; + *ours = 1; + return strbuffer_steal_value(&strbuff); +} + +static json_t *pack_object(scanner_t *s, va_list *ap) +{ + json_t *object = json_object(); + next_token(s); + + while(token(s) != '}') { + char *key; + size_t len; + int ours; + json_t *value; + + if(!token(s)) { + set_error(s, "", json_error_invalid_format, "Unexpected end of format string"); + goto error; + } + + if(token(s) != 's') { + set_error(s, "", json_error_invalid_format, "Expected format 's', got '%c'", token(s)); + goto error; + } + + key = read_string(s, ap, "object key", &len, &ours); + if (!key) + s->has_error = 1; + + next_token(s); + + value = pack(s, ap); + if(!value) { + if(ours) + jsonp_free(key); + + if(strchr("soO", token(s)) && s->next_token.token == '*') { + next_token(s); + } else { + s->has_error = 1; + } + + next_token(s); + continue; + } + + if(s->has_error) + json_decref(value); + + if(!s->has_error && json_object_set_new_nocheck(object, key, value)) { + set_error(s, "", json_error_out_of_memory, "Unable to add key \"%s\"", key); + s->has_error = 1; + } + + if(ours) + jsonp_free(key); + + if(strchr("soO", token(s)) && s->next_token.token == '*') + next_token(s); + next_token(s); + } + + if(!s->has_error) + return object; + +error: + json_decref(object); + return NULL; +} + +static json_t *pack_array(scanner_t *s, va_list *ap) +{ + json_t *array = json_array(); + next_token(s); + + while(token(s) != ']') { + json_t *value; + + if(!token(s)) { + set_error(s, "", json_error_invalid_format, "Unexpected end of format string"); + /* Format string errors are unrecoverable. */ + goto error; + } + + value = pack(s, ap); + if(!value) { + if(strchr("soO", token(s)) && s->next_token.token == '*') { + next_token(s); + } else { + s->has_error = 1; + } + + next_token(s); + continue; + } + + if(s->has_error) + json_decref(value); + + if(!s->has_error && json_array_append_new(array, value)) { + set_error(s, "", json_error_out_of_memory, "Unable to append to array"); + s->has_error = 1; + } + + if(strchr("soO", token(s)) && s->next_token.token == '*') + next_token(s); + next_token(s); + } + + if(!s->has_error) + return array; + +error: + json_decref(array); + return NULL; +} + +static json_t *pack_string(scanner_t *s, va_list *ap) +{ + char *str; + size_t len; + int ours; + int nullable; + + next_token(s); + nullable = token(s) == '?'; + if (!nullable) + prev_token(s); + + str = read_string(s, ap, "string", &len, &ours); + if (!str) { + return nullable ? json_null() : NULL; + } else if (ours) { + return jsonp_stringn_nocheck_own(str, len); + } else { + return json_stringn_nocheck(str, len); + } +} + +static json_t *pack(scanner_t *s, va_list *ap) +{ + switch(token(s)) { + case '{': + return pack_object(s, ap); + + case '[': + return pack_array(s, ap); + + case 's': /* string */ + return pack_string(s, ap); + + case 'n': /* null */ + return json_null(); + + case 'b': /* boolean */ + return va_arg(*ap, int) ? json_true() : json_false(); + + case 'i': /* integer from int */ + return json_integer(va_arg(*ap, int)); + + case 'I': /* integer from json_int_t */ + return json_integer(va_arg(*ap, json_int_t)); + + case 'f': /* real */ + return json_real(va_arg(*ap, double)); + + case 'O': /* a json_t object; increments refcount */ + { + int nullable; + json_t *json; + + next_token(s); + nullable = token(s) == '?'; + if (!nullable) + prev_token(s); + + json = va_arg(*ap, json_t *); + if (!json && nullable) { + return json_null(); + } else { + return json_incref(json); + } + } + + case 'o': /* a json_t object; doesn't increment refcount */ + { + int nullable; + json_t *json; + + next_token(s); + nullable = token(s) == '?'; + if (!nullable) + prev_token(s); + + json = va_arg(*ap, json_t *); + if (!json && nullable) { + return json_null(); + } else { + return json; + } + } + + default: + set_error(s, "", json_error_invalid_format, "Unexpected format character '%c'", + token(s)); + s->has_error = 1; + return NULL; + } +} + +static int unpack(scanner_t *s, json_t *root, va_list *ap); + +static int unpack_object(scanner_t *s, json_t *root, va_list *ap) +{ + int ret = -1; + int strict = 0; + int gotopt = 0; + + /* Use a set (emulated by a hashtable) to check that all object + keys are accessed. Checking that the correct number of keys + were accessed is not enough, as the same key can be unpacked + multiple times. + */ + hashtable_t key_set; + + if(hashtable_init(&key_set)) { + set_error(s, "", json_error_out_of_memory, "Out of memory"); + return -1; + } + + if(root && !json_is_object(root)) { + set_error(s, "", json_error_wrong_type, "Expected object, got %s", + type_name(root)); + goto out; + } + next_token(s); + + while(token(s) != '}') { + const char *key; + json_t *value; + int opt = 0; + + if(strict != 0) { + set_error(s, "", json_error_invalid_format, "Expected '}' after '%c', got '%c'", + (strict == 1 ? '!' : '*'), token(s)); + goto out; + } + + if(!token(s)) { + set_error(s, "", json_error_invalid_format, "Unexpected end of format string"); + goto out; + } + + if(token(s) == '!' || token(s) == '*') { + strict = (token(s) == '!' ? 1 : -1); + next_token(s); + continue; + } + + if(token(s) != 's') { + set_error(s, "", json_error_invalid_format, "Expected format 's', got '%c'", token(s)); + goto out; + } + + key = va_arg(*ap, const char *); + if(!key) { + set_error(s, "", json_error_null_value, "NULL object key"); + goto out; + } + + next_token(s); + + if(token(s) == '?') { + opt = gotopt = 1; + next_token(s); + } + + if(!root) { + /* skipping */ + value = NULL; + } + else { + value = json_object_get(root, key); + if(!value && !opt) { + set_error(s, "", json_error_item_not_found, "Object item not found: %s", key); + goto out; + } + } + + if(unpack(s, value, ap)) + goto out; + + hashtable_set(&key_set, key, json_null()); + next_token(s); + } + + if(strict == 0 && (s->flags & JSON_STRICT)) + strict = 1; + + if(root && strict == 1) { + /* We need to check that all non optional items have been parsed */ + const char *key; + int have_unrecognized_keys = 0; + strbuffer_t unrecognized_keys; + json_t *value; + long unpacked = 0; + if (gotopt) { + /* We have optional keys, we need to iter on each key */ + json_object_foreach(root, key, value) { + if(!hashtable_get(&key_set, key)) { + unpacked++; + + /* Save unrecognized keys for the error message */ + if (!have_unrecognized_keys) { + strbuffer_init(&unrecognized_keys); + have_unrecognized_keys = 1; + } else { + strbuffer_append_bytes(&unrecognized_keys, ", ", 2); + } + strbuffer_append_bytes(&unrecognized_keys, key, strlen(key)); + } + } + } else { + /* No optional keys, we can just compare the number of items */ + unpacked = (long)json_object_size(root) - (long)key_set.size; + } + if (unpacked) { + if (!gotopt) { + /* Save unrecognized keys for the error message */ + json_object_foreach(root, key, value) { + if(!hashtable_get(&key_set, key)) { + if (!have_unrecognized_keys) { + strbuffer_init(&unrecognized_keys); + have_unrecognized_keys = 1; + } else { + strbuffer_append_bytes(&unrecognized_keys, ", ", 2); + } + strbuffer_append_bytes(&unrecognized_keys, key, strlen(key)); + } + } + } + set_error(s, "", json_error_end_of_input_expected, + "%li object item(s) left unpacked: %s", + unpacked, strbuffer_value(&unrecognized_keys)); + strbuffer_close(&unrecognized_keys); + goto out; + } + } + + ret = 0; + +out: + hashtable_close(&key_set); + return ret; +} + +static int unpack_array(scanner_t *s, json_t *root, va_list *ap) +{ + size_t i = 0; + int strict = 0; + + if(root && !json_is_array(root)) { + set_error(s, "", json_error_wrong_type, "Expected array, got %s", type_name(root)); + return -1; + } + next_token(s); + + while(token(s) != ']') { + json_t *value; + + if(strict != 0) { + set_error(s, "", json_error_invalid_format, "Expected ']' after '%c', got '%c'", + (strict == 1 ? '!' : '*'), + token(s)); + return -1; + } + + if(!token(s)) { + set_error(s, "", json_error_invalid_format, "Unexpected end of format string"); + return -1; + } + + if(token(s) == '!' || token(s) == '*') { + strict = (token(s) == '!' ? 1 : -1); + next_token(s); + continue; + } + + if(!strchr(unpack_value_starters, token(s))) { + set_error(s, "", json_error_invalid_format, "Unexpected format character '%c'", + token(s)); + return -1; + } + + if(!root) { + /* skipping */ + value = NULL; + } + else { + value = json_array_get(root, i); + if(!value) { + set_error(s, "", json_error_index_out_of_range, "Array index %lu out of range", + (unsigned long)i); + return -1; + } + } + + if(unpack(s, value, ap)) + return -1; + + next_token(s); + i++; + } + + if(strict == 0 && (s->flags & JSON_STRICT)) + strict = 1; + + if(root && strict == 1 && i != json_array_size(root)) { + long diff = (long)json_array_size(root) - (long)i; + set_error(s, "", json_error_end_of_input_expected, "%li array item(s) left unpacked", diff); + return -1; + } + + return 0; +} + +static int unpack(scanner_t *s, json_t *root, va_list *ap) +{ + switch(token(s)) + { + case '{': + return unpack_object(s, root, ap); + + case '[': + return unpack_array(s, root, ap); + + case 's': + if(root && !json_is_string(root)) { + set_error(s, "", json_error_wrong_type, "Expected string, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + const char **str_target; + size_t *len_target = NULL; + + str_target = va_arg(*ap, const char **); + if(!str_target) { + set_error(s, "", json_error_null_value, "NULL string argument"); + return -1; + } + + next_token(s); + + if(token(s) == '%') { + len_target = va_arg(*ap, size_t *); + if(!len_target) { + set_error(s, "", json_error_null_value, "NULL string length argument"); + return -1; + } + } + else + prev_token(s); + + if(root) { + *str_target = json_string_value(root); + if(len_target) + *len_target = json_string_length(root); + } + } + return 0; + + case 'i': + if(root && !json_is_integer(root)) { + set_error(s, "", json_error_wrong_type, "Expected integer, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + int *target = va_arg(*ap, int*); + if(root) + *target = (int)json_integer_value(root); + } + + return 0; + + case 'I': + if(root && !json_is_integer(root)) { + set_error(s, "", json_error_wrong_type, "Expected integer, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + json_int_t *target = va_arg(*ap, json_int_t*); + if(root) + *target = json_integer_value(root); + } + + return 0; + + case 'b': + if(root && !json_is_boolean(root)) { + set_error(s, "", json_error_wrong_type, "Expected true or false, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + int *target = va_arg(*ap, int*); + if(root) + *target = json_is_true(root); + } + + return 0; + + case 'f': + if(root && !json_is_real(root)) { + set_error(s, "", json_error_wrong_type, "Expected real, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + double *target = va_arg(*ap, double*); + if(root) + *target = json_real_value(root); + } + + return 0; + + case 'F': + if(root && !json_is_number(root)) { + set_error(s, "", json_error_wrong_type, "Expected real or integer, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + double *target = va_arg(*ap, double*); + if(root) + *target = json_number_value(root); + } + + return 0; + + case 'O': + if(root && !(s->flags & JSON_VALIDATE_ONLY)) + json_incref(root); + /* Fall through */ + + case 'o': + if(!(s->flags & JSON_VALIDATE_ONLY)) { + json_t **target = va_arg(*ap, json_t**); + if(root) + *target = root; + } + + return 0; + + case 'n': + /* Never assign, just validate */ + if(root && !json_is_null(root)) { + set_error(s, "", json_error_wrong_type, "Expected null, got %s", + type_name(root)); + return -1; + } + return 0; + + default: + set_error(s, "", json_error_invalid_format, "Unexpected format character '%c'", + token(s)); + return -1; + } +} + +json_t *json_vpack_ex(json_error_t *error, size_t flags, + const char *fmt, va_list ap) +{ + scanner_t s; + va_list ap_copy; + json_t *value; + + if(!fmt || !*fmt) { + jsonp_error_init(error, ""); + jsonp_error_set(error, -1, -1, 0, json_error_invalid_argument, "NULL or empty format string"); + return NULL; + } + jsonp_error_init(error, NULL); + + scanner_init(&s, error, flags, fmt); + next_token(&s); + + va_copy(ap_copy, ap); + value = pack(&s, &ap_copy); + va_end(ap_copy); + + if(!value) + return NULL; + + next_token(&s); + if(token(&s)) { + json_decref(value); + set_error(&s, "", json_error_invalid_format, "Garbage after format string"); + return NULL; + } + if(s.has_error) { + json_decref(value); + return NULL; + } + + return value; +} + +json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...) +{ + json_t *value; + va_list ap; + + va_start(ap, fmt); + value = json_vpack_ex(error, flags, fmt, ap); + va_end(ap); + + return value; +} + +json_t *json_pack(const char *fmt, ...) +{ + json_t *value; + va_list ap; + + va_start(ap, fmt); + value = json_vpack_ex(NULL, 0, fmt, ap); + va_end(ap); + + return value; +} + +int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, + const char *fmt, va_list ap) +{ + scanner_t s; + va_list ap_copy; + + if(!root) { + jsonp_error_init(error, ""); + jsonp_error_set(error, -1, -1, 0, json_error_null_value, "NULL root value"); + return -1; + } + + if(!fmt || !*fmt) { + jsonp_error_init(error, ""); + jsonp_error_set(error, -1, -1, 0, json_error_invalid_argument, "NULL or empty format string"); + return -1; + } + jsonp_error_init(error, NULL); + + scanner_init(&s, error, flags, fmt); + next_token(&s); + + va_copy(ap_copy, ap); + if(unpack(&s, root, &ap_copy)) { + va_end(ap_copy); + return -1; + } + va_end(ap_copy); + + next_token(&s); + if(token(&s)) { + set_error(&s, "", json_error_invalid_format, "Garbage after format string"); + return -1; + } + + return 0; +} + +int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = json_vunpack_ex(root, error, flags, fmt, ap); + va_end(ap); + + return ret; +} + +int json_unpack(json_t *root, const char *fmt, ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = json_vunpack_ex(root, NULL, 0, fmt, ap); + va_end(ap); + + return ret; +} diff --git a/client/jansson/strbuffer.c b/client/jansson/strbuffer.c new file mode 100644 index 00000000..5e8c0039 --- /dev/null +++ b/client/jansson/strbuffer.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include "jansson_private.h" +#include "strbuffer.h" + +#define STRBUFFER_MIN_SIZE 16 +#define STRBUFFER_FACTOR 2 +#define STRBUFFER_SIZE_MAX ((size_t)-1) + +int strbuffer_init(strbuffer_t *strbuff) +{ + strbuff->size = STRBUFFER_MIN_SIZE; + strbuff->length = 0; + + strbuff->value = jsonp_malloc(strbuff->size); + if(!strbuff->value) + return -1; + + /* initialize to empty */ + strbuff->value[0] = '\0'; + return 0; +} + +void strbuffer_close(strbuffer_t *strbuff) +{ + if(strbuff->value) + jsonp_free(strbuff->value); + + strbuff->size = 0; + strbuff->length = 0; + strbuff->value = NULL; +} + +void strbuffer_clear(strbuffer_t *strbuff) +{ + strbuff->length = 0; + strbuff->value[0] = '\0'; +} + +const char *strbuffer_value(const strbuffer_t *strbuff) +{ + return strbuff->value; +} + +char *strbuffer_steal_value(strbuffer_t *strbuff) +{ + char *result = strbuff->value; + strbuff->value = NULL; + return result; +} + +int strbuffer_append_byte(strbuffer_t *strbuff, char byte) +{ + return strbuffer_append_bytes(strbuff, &byte, 1); +} + +int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size) +{ + if(size >= strbuff->size - strbuff->length) + { + size_t new_size; + char *new_value; + + /* avoid integer overflow */ + if (strbuff->size > STRBUFFER_SIZE_MAX / STRBUFFER_FACTOR + || size > STRBUFFER_SIZE_MAX - 1 + || strbuff->length > STRBUFFER_SIZE_MAX - 1 - size) + return -1; + + new_size = max(strbuff->size * STRBUFFER_FACTOR, + strbuff->length + size + 1); + + new_value = jsonp_malloc(new_size); + if(!new_value) + return -1; + + memcpy(new_value, strbuff->value, strbuff->length); + + jsonp_free(strbuff->value); + strbuff->value = new_value; + strbuff->size = new_size; + } + + memcpy(strbuff->value + strbuff->length, data, size); + strbuff->length += size; + strbuff->value[strbuff->length] = '\0'; + + return 0; +} + +char strbuffer_pop(strbuffer_t *strbuff) +{ + if(strbuff->length > 0) { + char c = strbuff->value[--strbuff->length]; + strbuff->value[strbuff->length] = '\0'; + return c; + } + else + return '\0'; +} diff --git a/client/jansson/strbuffer.h b/client/jansson/strbuffer.h new file mode 100644 index 00000000..615b7f5f --- /dev/null +++ b/client/jansson/strbuffer.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef STRBUFFER_H +#define STRBUFFER_H + +#include + +typedef struct { + char *value; + size_t length; /* bytes used */ + size_t size; /* bytes allocated */ +} strbuffer_t; + +int strbuffer_init(strbuffer_t *strbuff); +void strbuffer_close(strbuffer_t *strbuff); + +void strbuffer_clear(strbuffer_t *strbuff); + +const char *strbuffer_value(const strbuffer_t *strbuff); + +/* Steal the value and close the strbuffer */ +char *strbuffer_steal_value(strbuffer_t *strbuff); + +int strbuffer_append_byte(strbuffer_t *strbuff, char byte); +int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size); + +char strbuffer_pop(strbuffer_t *strbuff); + +#endif diff --git a/client/jansson/strconv.c b/client/jansson/strconv.c new file mode 100644 index 00000000..8075481e --- /dev/null +++ b/client/jansson/strconv.c @@ -0,0 +1,145 @@ +#include +#include +#include +#include +#include +#ifdef __MINGW32__ +#undef __NO_ISOCEXT /* ensure stdlib.h will declare prototypes for mingw own 'strtod' replacement, called '__strtod' */ +#endif +#include "jansson_private.h" +#include "strbuffer.h" + +/* need jansson_private_config.h to get the correct snprintf */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef __MINGW32__ +#define strtod __strtod +#endif + +#if JSON_HAVE_LOCALECONV +#include + +/* + - This code assumes that the decimal separator is exactly one + character. + + - If setlocale() is called by another thread between the call to + localeconv() and the call to sprintf() or strtod(), the result may + be wrong. setlocale() is not thread-safe and should not be used + this way. Multi-threaded programs should use uselocale() instead. +*/ + +static void to_locale(strbuffer_t *strbuffer) +{ + const char *point; + char *pos; + + point = localeconv()->decimal_point; + if(*point == '.') { + /* No conversion needed */ + return; + } + + pos = strchr(strbuffer->value, '.'); + if(pos) + *pos = *point; +} + +static void from_locale(char *buffer) +{ + const char *point; + char *pos; + + point = localeconv()->decimal_point; + if(*point == '.') { + /* No conversion needed */ + return; + } + + pos = strchr(buffer, *point); + if(pos) + *pos = '.'; +} +#endif + +int jsonp_strtod(strbuffer_t *strbuffer, double *out) +{ + double value; + char *end; + +#if JSON_HAVE_LOCALECONV + to_locale(strbuffer); +#endif + + errno = 0; + value = strtod(strbuffer->value, &end); + assert(end == strbuffer->value + strbuffer->length); + + if((value == HUGE_VAL || value == -HUGE_VAL) && errno == ERANGE) { + /* Overflow */ + return -1; + } + + *out = value; + return 0; +} + +int jsonp_dtostr(char *buffer, size_t size, double value, int precision) +{ + int ret; + char *start, *end; + size_t length; + + if (precision == 0) + precision = 17; + + ret = snprintf(buffer, size, "%.*g", precision, value); + if(ret < 0) + return -1; + + length = (size_t)ret; + if(length >= size) + return -1; + +#if JSON_HAVE_LOCALECONV + from_locale(buffer); +#endif + + /* Make sure there's a dot or 'e' in the output. Otherwise + a real is converted to an integer when decoding */ + if(strchr(buffer, '.') == NULL && + strchr(buffer, 'e') == NULL) + { + if(length + 3 >= size) { + /* No space to append ".0" */ + return -1; + } + buffer[length] = '.'; + buffer[length + 1] = '0'; + buffer[length + 2] = '\0'; + length += 2; + } + + /* Remove leading '+' from positive exponent. Also remove leading + zeros from exponents (added by some printf() implementations) */ + start = strchr(buffer, 'e'); + if(start) { + start++; + end = start + 1; + + if(*start == '-') + start++; + + while(*end == '0') + end++; + + if(end != start) { + memmove(start, end, length - (size_t)(end - buffer)); + length -= (size_t)(end - start); + } + } + + return (int)length; +} diff --git a/client/jansson/utf.c b/client/jansson/utf.c new file mode 100644 index 00000000..be966cbd --- /dev/null +++ b/client/jansson/utf.c @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include +#include "utf.h" + +int utf8_encode(int32_t codepoint, char *buffer, size_t *size) +{ + if(codepoint < 0) + return -1; + else if(codepoint < 0x80) + { + buffer[0] = (char)codepoint; + *size = 1; + } + else if(codepoint < 0x800) + { + buffer[0] = 0xC0 + ((codepoint & 0x7C0) >> 6); + buffer[1] = 0x80 + ((codepoint & 0x03F)); + *size = 2; + } + else if(codepoint < 0x10000) + { + buffer[0] = 0xE0 + ((codepoint & 0xF000) >> 12); + buffer[1] = 0x80 + ((codepoint & 0x0FC0) >> 6); + buffer[2] = 0x80 + ((codepoint & 0x003F)); + *size = 3; + } + else if(codepoint <= 0x10FFFF) + { + buffer[0] = 0xF0 + ((codepoint & 0x1C0000) >> 18); + buffer[1] = 0x80 + ((codepoint & 0x03F000) >> 12); + buffer[2] = 0x80 + ((codepoint & 0x000FC0) >> 6); + buffer[3] = 0x80 + ((codepoint & 0x00003F)); + *size = 4; + } + else + return -1; + + return 0; +} + +size_t utf8_check_first(char byte) +{ + unsigned char u = (unsigned char)byte; + + if(u < 0x80) + return 1; + + if(0x80 <= u && u <= 0xBF) { + /* second, third or fourth byte of a multi-byte + sequence, i.e. a "continuation byte" */ + return 0; + } + else if(u == 0xC0 || u == 0xC1) { + /* overlong encoding of an ASCII byte */ + return 0; + } + else if(0xC2 <= u && u <= 0xDF) { + /* 2-byte sequence */ + return 2; + } + + else if(0xE0 <= u && u <= 0xEF) { + /* 3-byte sequence */ + return 3; + } + else if(0xF0 <= u && u <= 0xF4) { + /* 4-byte sequence */ + return 4; + } + else { /* u >= 0xF5 */ + /* Restricted (start of 4-, 5- or 6-byte sequence) or invalid + UTF-8 */ + return 0; + } +} + +size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint) +{ + size_t i; + int32_t value = 0; + unsigned char u = (unsigned char)buffer[0]; + + if(size == 2) + { + value = u & 0x1F; + } + else if(size == 3) + { + value = u & 0xF; + } + else if(size == 4) + { + value = u & 0x7; + } + else + return 0; + + for(i = 1; i < size; i++) + { + u = (unsigned char)buffer[i]; + + if(u < 0x80 || u > 0xBF) { + /* not a continuation byte */ + return 0; + } + + value = (value << 6) + (u & 0x3F); + } + + if(value > 0x10FFFF) { + /* not in Unicode range */ + return 0; + } + + else if(0xD800 <= value && value <= 0xDFFF) { + /* invalid code point (UTF-16 surrogate halves) */ + return 0; + } + + else if((size == 2 && value < 0x80) || + (size == 3 && value < 0x800) || + (size == 4 && value < 0x10000)) { + /* overlong encoding */ + return 0; + } + + if(codepoint) + *codepoint = value; + + return 1; +} + +const char *utf8_iterate(const char *buffer, size_t bufsize, int32_t *codepoint) +{ + size_t count; + int32_t value; + + if(!bufsize) + return buffer; + + count = utf8_check_first(buffer[0]); + if(count <= 0) + return NULL; + + if(count == 1) + value = (unsigned char)buffer[0]; + else + { + if(count > bufsize || !utf8_check_full(buffer, count, &value)) + return NULL; + } + + if(codepoint) + *codepoint = value; + + return buffer + count; +} + +int utf8_check_string(const char *string, size_t length) +{ + size_t i; + + for(i = 0; i < length; i++) + { + size_t count = utf8_check_first(string[i]); + if(count == 0) + return 0; + else if(count > 1) + { + if(count > length - i) + return 0; + + if(!utf8_check_full(&string[i], count, NULL)) + return 0; + + i += count - 1; + } + } + + return 1; +} diff --git a/client/jansson/utf.h b/client/jansson/utf.h new file mode 100644 index 00000000..e182df78 --- /dev/null +++ b/client/jansson/utf.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef UTF_H +#define UTF_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_STDINT_H +#include +#endif + +int utf8_encode(int32_t codepoint, char *buffer, size_t *size); + +size_t utf8_check_first(char byte); +size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint); +const char *utf8_iterate(const char *buffer, size_t size, int32_t *codepoint); + +int utf8_check_string(const char *string, size_t length); + +#endif diff --git a/client/jansson/value.c b/client/jansson/value.c new file mode 100644 index 00000000..b3b31412 --- /dev/null +++ b/client/jansson/value.c @@ -0,0 +1,1078 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#ifdef HAVE_STDINT_H +#include +#endif + +#include "jansson.h" +#include "hashtable.h" +#include "jansson_private.h" +#include "utf.h" + +/* Work around nonstandard isnan() and isinf() implementations */ +#ifndef isnan +#ifndef __sun +static JSON_INLINE int isnan(double x) { return x != x; } +#endif +#endif +#ifndef isinf +static JSON_INLINE int isinf(double x) { return !isnan(x) && isnan(x - x); } +#endif + +static JSON_INLINE void json_init(json_t *json, json_type type) +{ + json->type = type; + json->refcount = 1; +} + + +/*** object ***/ + +extern volatile uint32_t hashtable_seed; + +json_t *json_object(void) +{ + json_object_t *object = jsonp_malloc(sizeof(json_object_t)); + if(!object) + return NULL; + + if (!hashtable_seed) { + /* Autoseed */ + json_object_seed(0); + } + + json_init(&object->json, JSON_OBJECT); + + if(hashtable_init(&object->hashtable)) + { + jsonp_free(object); + return NULL; + } + + return &object->json; +} + +static void json_delete_object(json_object_t *object) +{ + hashtable_close(&object->hashtable); + jsonp_free(object); +} + +size_t json_object_size(const json_t *json) +{ + json_object_t *object; + + if(!json_is_object(json)) + return 0; + + object = json_to_object(json); + return object->hashtable.size; +} + +json_t *json_object_get(const json_t *json, const char *key) +{ + json_object_t *object; + + if(!key || !json_is_object(json)) + return NULL; + + object = json_to_object(json); + return hashtable_get(&object->hashtable, key); +} + +int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value) +{ + json_object_t *object; + + if(!value) + return -1; + + if(!key || !json_is_object(json) || json == value) + { + json_decref(value); + return -1; + } + object = json_to_object(json); + + if(hashtable_set(&object->hashtable, key, value)) + { + json_decref(value); + return -1; + } + + return 0; +} + +int json_object_set_new(json_t *json, const char *key, json_t *value) +{ + if(!key || !utf8_check_string(key, strlen(key))) + { + json_decref(value); + return -1; + } + + return json_object_set_new_nocheck(json, key, value); +} + +int json_object_del(json_t *json, const char *key) +{ + json_object_t *object; + + if(!key || !json_is_object(json)) + return -1; + + object = json_to_object(json); + return hashtable_del(&object->hashtable, key); +} + +int json_object_clear(json_t *json) +{ + json_object_t *object; + + if(!json_is_object(json)) + return -1; + + object = json_to_object(json); + hashtable_clear(&object->hashtable); + + return 0; +} + +int json_object_update(json_t *object, json_t *other) +{ + const char *key; + json_t *value; + + if(!json_is_object(object) || !json_is_object(other)) + return -1; + + json_object_foreach(other, key, value) { + if(json_object_set_nocheck(object, key, value)) + return -1; + } + + return 0; +} + +int json_object_update_existing(json_t *object, json_t *other) +{ + const char *key; + json_t *value; + + if(!json_is_object(object) || !json_is_object(other)) + return -1; + + json_object_foreach(other, key, value) { + if(json_object_get(object, key)) + json_object_set_nocheck(object, key, value); + } + + return 0; +} + +int json_object_update_missing(json_t *object, json_t *other) +{ + const char *key; + json_t *value; + + if(!json_is_object(object) || !json_is_object(other)) + return -1; + + json_object_foreach(other, key, value) { + if(!json_object_get(object, key)) + json_object_set_nocheck(object, key, value); + } + + return 0; +} + +void *json_object_iter(json_t *json) +{ + json_object_t *object; + + if(!json_is_object(json)) + return NULL; + + object = json_to_object(json); + return hashtable_iter(&object->hashtable); +} + +void *json_object_iter_at(json_t *json, const char *key) +{ + json_object_t *object; + + if(!key || !json_is_object(json)) + return NULL; + + object = json_to_object(json); + return hashtable_iter_at(&object->hashtable, key); +} + +void *json_object_iter_next(json_t *json, void *iter) +{ + json_object_t *object; + + if(!json_is_object(json) || iter == NULL) + return NULL; + + object = json_to_object(json); + return hashtable_iter_next(&object->hashtable, iter); +} + +const char *json_object_iter_key(void *iter) +{ + if(!iter) + return NULL; + + return hashtable_iter_key(iter); +} + +json_t *json_object_iter_value(void *iter) +{ + if(!iter) + return NULL; + + return (json_t *)hashtable_iter_value(iter); +} + +int json_object_iter_set_new(json_t *json, void *iter, json_t *value) +{ + if(!json_is_object(json) || !iter || !value) + { + json_decref(value); + return -1; + } + + hashtable_iter_set(iter, value); + return 0; +} + +void *json_object_key_to_iter(const char *key) +{ + if(!key) + return NULL; + + return hashtable_key_to_iter(key); +} + +static int json_object_equal(const json_t *object1, const json_t *object2) +{ + const char *key; + const json_t *value1, *value2; + + if(json_object_size(object1) != json_object_size(object2)) + return 0; + + json_object_foreach((json_t *)object1, key, value1) { + value2 = json_object_get(object2, key); + + if(!json_equal(value1, value2)) + return 0; + } + + return 1; +} + +static json_t *json_object_copy(json_t *object) +{ + json_t *result; + + const char *key; + json_t *value; + + result = json_object(); + if(!result) + return NULL; + + json_object_foreach(object, key, value) + json_object_set_nocheck(result, key, value); + + return result; +} + +static json_t *json_object_deep_copy(const json_t *object) +{ + json_t *result; + void *iter; + + result = json_object(); + if(!result) + return NULL; + + /* Cannot use json_object_foreach because object has to be cast + non-const */ + iter = json_object_iter((json_t *)object); + while(iter) { + const char *key; + const json_t *value; + key = json_object_iter_key(iter); + value = json_object_iter_value(iter); + + json_object_set_new_nocheck(result, key, json_deep_copy(value)); + iter = json_object_iter_next((json_t *)object, iter); + } + + return result; +} + + +/*** array ***/ + +json_t *json_array(void) +{ + json_array_t *array = jsonp_malloc(sizeof(json_array_t)); + if(!array) + return NULL; + json_init(&array->json, JSON_ARRAY); + + array->entries = 0; + array->size = 8; + + array->table = jsonp_malloc(array->size * sizeof(json_t *)); + if(!array->table) { + jsonp_free(array); + return NULL; + } + + return &array->json; +} + +static void json_delete_array(json_array_t *array) +{ + size_t i; + + for(i = 0; i < array->entries; i++) + json_decref(array->table[i]); + + jsonp_free(array->table); + jsonp_free(array); +} + +size_t json_array_size(const json_t *json) +{ + if(!json_is_array(json)) + return 0; + + return json_to_array(json)->entries; +} + +json_t *json_array_get(const json_t *json, size_t index) +{ + json_array_t *array; + if(!json_is_array(json)) + return NULL; + array = json_to_array(json); + + if(index >= array->entries) + return NULL; + + return array->table[index]; +} + +int json_array_set_new(json_t *json, size_t index, json_t *value) +{ + json_array_t *array; + + if(!value) + return -1; + + if(!json_is_array(json) || json == value) + { + json_decref(value); + return -1; + } + array = json_to_array(json); + + if(index >= array->entries) + { + json_decref(value); + return -1; + } + + json_decref(array->table[index]); + array->table[index] = value; + + return 0; +} + +static void array_move(json_array_t *array, size_t dest, + size_t src, size_t count) +{ + memmove(&array->table[dest], &array->table[src], count * sizeof(json_t *)); +} + +static void array_copy(json_t **dest, size_t dpos, + json_t **src, size_t spos, + size_t count) +{ + memcpy(&dest[dpos], &src[spos], count * sizeof(json_t *)); +} + +static json_t **json_array_grow(json_array_t *array, + size_t amount, + int copy) +{ + size_t new_size; + json_t **old_table, **new_table; + + if(array->entries + amount <= array->size) + return array->table; + + old_table = array->table; + + new_size = max(array->size + amount, array->size * 2); + new_table = jsonp_malloc(new_size * sizeof(json_t *)); + if(!new_table) + return NULL; + + array->size = new_size; + array->table = new_table; + + if(copy) { + array_copy(array->table, 0, old_table, 0, array->entries); + jsonp_free(old_table); + return array->table; + } + + return old_table; +} + +int json_array_append_new(json_t *json, json_t *value) +{ + json_array_t *array; + + if(!value) + return -1; + + if(!json_is_array(json) || json == value) + { + json_decref(value); + return -1; + } + array = json_to_array(json); + + if(!json_array_grow(array, 1, 1)) { + json_decref(value); + return -1; + } + + array->table[array->entries] = value; + array->entries++; + + return 0; +} + +int json_array_insert_new(json_t *json, size_t index, json_t *value) +{ + json_array_t *array; + json_t **old_table; + + if(!value) + return -1; + + if(!json_is_array(json) || json == value) { + json_decref(value); + return -1; + } + array = json_to_array(json); + + if(index > array->entries) { + json_decref(value); + return -1; + } + + old_table = json_array_grow(array, 1, 0); + if(!old_table) { + json_decref(value); + return -1; + } + + if(old_table != array->table) { + array_copy(array->table, 0, old_table, 0, index); + array_copy(array->table, index + 1, old_table, index, + array->entries - index); + jsonp_free(old_table); + } + else + array_move(array, index + 1, index, array->entries - index); + + array->table[index] = value; + array->entries++; + + return 0; +} + +int json_array_remove(json_t *json, size_t index) +{ + json_array_t *array; + + if(!json_is_array(json)) + return -1; + array = json_to_array(json); + + if(index >= array->entries) + return -1; + + json_decref(array->table[index]); + + /* If we're removing the last element, nothing has to be moved */ + if(index < array->entries - 1) + array_move(array, index, index + 1, array->entries - index - 1); + + array->entries--; + + return 0; +} + +int json_array_clear(json_t *json) +{ + json_array_t *array; + size_t i; + + if(!json_is_array(json)) + return -1; + array = json_to_array(json); + + for(i = 0; i < array->entries; i++) + json_decref(array->table[i]); + + array->entries = 0; + return 0; +} + +int json_array_extend(json_t *json, json_t *other_json) +{ + json_array_t *array, *other; + size_t i; + + if(!json_is_array(json) || !json_is_array(other_json)) + return -1; + array = json_to_array(json); + other = json_to_array(other_json); + + if(!json_array_grow(array, other->entries, 1)) + return -1; + + for(i = 0; i < other->entries; i++) + json_incref(other->table[i]); + + array_copy(array->table, array->entries, other->table, 0, other->entries); + + array->entries += other->entries; + return 0; +} + +static int json_array_equal(const json_t *array1, const json_t *array2) +{ + size_t i, size; + + size = json_array_size(array1); + if(size != json_array_size(array2)) + return 0; + + for(i = 0; i < size; i++) + { + json_t *value1, *value2; + + value1 = json_array_get(array1, i); + value2 = json_array_get(array2, i); + + if(!json_equal(value1, value2)) + return 0; + } + + return 1; +} + +static json_t *json_array_copy(json_t *array) +{ + json_t *result; + size_t i; + + result = json_array(); + if(!result) + return NULL; + + for(i = 0; i < json_array_size(array); i++) + json_array_append(result, json_array_get(array, i)); + + return result; +} + +static json_t *json_array_deep_copy(const json_t *array) +{ + json_t *result; + size_t i; + + result = json_array(); + if(!result) + return NULL; + + for(i = 0; i < json_array_size(array); i++) + json_array_append_new(result, json_deep_copy(json_array_get(array, i))); + + return result; +} + +/*** string ***/ + +static json_t *string_create(const char *value, size_t len, int own) +{ + char *v; + json_string_t *string; + + if(!value) + return NULL; + + if(own) + v = (char *)value; + else { + v = jsonp_strndup(value, len); + if(!v) + return NULL; + } + + string = jsonp_malloc(sizeof(json_string_t)); + if(!string) { + if(!own) + jsonp_free(v); + return NULL; + } + json_init(&string->json, JSON_STRING); + string->value = v; + string->length = len; + + return &string->json; +} + +json_t *json_string_nocheck(const char *value) +{ + if(!value) + return NULL; + + return string_create(value, strlen(value), 0); +} + +json_t *json_stringn_nocheck(const char *value, size_t len) +{ + return string_create(value, len, 0); +} + +/* this is private; "steal" is not a public API concept */ +json_t *jsonp_stringn_nocheck_own(const char *value, size_t len) +{ + return string_create(value, len, 1); +} + +json_t *json_string(const char *value) +{ + if(!value) + return NULL; + + return json_stringn(value, strlen(value)); +} + +json_t *json_stringn(const char *value, size_t len) +{ + if(!value || !utf8_check_string(value, len)) + return NULL; + + return json_stringn_nocheck(value, len); +} + +const char *json_string_value(const json_t *json) +{ + if(!json_is_string(json)) + return NULL; + + return json_to_string(json)->value; +} + +size_t json_string_length(const json_t *json) +{ + if(!json_is_string(json)) + return 0; + + return json_to_string(json)->length; +} + +int json_string_set_nocheck(json_t *json, const char *value) +{ + if(!value) + return -1; + + return json_string_setn_nocheck(json, value, strlen(value)); +} + +int json_string_setn_nocheck(json_t *json, const char *value, size_t len) +{ + char *dup; + json_string_t *string; + + if(!json_is_string(json) || !value) + return -1; + + dup = jsonp_strndup(value, len); + if(!dup) + return -1; + + string = json_to_string(json); + jsonp_free(string->value); + string->value = dup; + string->length = len; + + return 0; +} + +int json_string_set(json_t *json, const char *value) +{ + if(!value) + return -1; + + return json_string_setn(json, value, strlen(value)); +} + +int json_string_setn(json_t *json, const char *value, size_t len) +{ + if(!value || !utf8_check_string(value, len)) + return -1; + + return json_string_setn_nocheck(json, value, len); +} + +static void json_delete_string(json_string_t *string) +{ + jsonp_free(string->value); + jsonp_free(string); +} + +static int json_string_equal(const json_t *string1, const json_t *string2) +{ + json_string_t *s1, *s2; + + if(!json_is_string(string1) || !json_is_string(string2)) + return 0; + + s1 = json_to_string(string1); + s2 = json_to_string(string2); + return s1->length == s2->length && !memcmp(s1->value, s2->value, s1->length); +} + +static json_t *json_string_copy(const json_t *string) +{ + json_string_t *s; + + if(!json_is_string(string)) + return NULL; + + s = json_to_string(string); + return json_stringn_nocheck(s->value, s->length); +} + +json_t *json_vsprintf(const char *fmt, va_list ap) { + int length; + char *buf; + va_list aq; + va_copy(aq, ap); + + length = vsnprintf(NULL, 0, fmt, ap); + if (length == 0) + return json_string(""); + + buf = jsonp_malloc(length + 1); + if (!buf) + return NULL; + + vsnprintf(buf, length + 1, fmt, aq); + if (!utf8_check_string(buf, length)) { + jsonp_free(buf); + return NULL; + } + + return jsonp_stringn_nocheck_own(buf, length); +} + +json_t *json_sprintf(const char *fmt, ...) { + json_t *result; + va_list ap; + + va_start(ap, fmt); + result = json_vsprintf(fmt, ap); + va_end(ap); + + return result; +} + + +/*** integer ***/ + +json_t *json_integer(json_int_t value) +{ + json_integer_t *integer = jsonp_malloc(sizeof(json_integer_t)); + if(!integer) + return NULL; + json_init(&integer->json, JSON_INTEGER); + + integer->value = value; + return &integer->json; +} + +json_int_t json_integer_value(const json_t *json) +{ + if(!json_is_integer(json)) + return 0; + + return json_to_integer(json)->value; +} + +int json_integer_set(json_t *json, json_int_t value) +{ + if(!json_is_integer(json)) + return -1; + + json_to_integer(json)->value = value; + + return 0; +} + +static void json_delete_integer(json_integer_t *integer) +{ + jsonp_free(integer); +} + +static int json_integer_equal(const json_t *integer1, const json_t *integer2) +{ + return json_integer_value(integer1) == json_integer_value(integer2); +} + +static json_t *json_integer_copy(const json_t *integer) +{ + return json_integer(json_integer_value(integer)); +} + + +/*** real ***/ + +json_t *json_real(double value) +{ + json_real_t *real; + + if(isnan(value) || isinf(value)) + return NULL; + + real = jsonp_malloc(sizeof(json_real_t)); + if(!real) + return NULL; + json_init(&real->json, JSON_REAL); + + real->value = value; + return &real->json; +} + +double json_real_value(const json_t *json) +{ + if(!json_is_real(json)) + return 0; + + return json_to_real(json)->value; +} + +int json_real_set(json_t *json, double value) +{ + if(!json_is_real(json) || isnan(value) || isinf(value)) + return -1; + + json_to_real(json)->value = value; + + return 0; +} + +static void json_delete_real(json_real_t *real) +{ + jsonp_free(real); +} + +static int json_real_equal(const json_t *real1, const json_t *real2) +{ + return json_real_value(real1) == json_real_value(real2); +} + +static json_t *json_real_copy(const json_t *real) +{ + return json_real(json_real_value(real)); +} + + +/*** number ***/ + +double json_number_value(const json_t *json) +{ + if(json_is_integer(json)) + return (double)json_integer_value(json); + else if(json_is_real(json)) + return json_real_value(json); + else + return 0.0; +} + + +/*** simple values ***/ + +json_t *json_true(void) +{ + static json_t the_true = {JSON_TRUE, (size_t)-1}; + return &the_true; +} + + +json_t *json_false(void) +{ + static json_t the_false = {JSON_FALSE, (size_t)-1}; + return &the_false; +} + + +json_t *json_null(void) +{ + static json_t the_null = {JSON_NULL, (size_t)-1}; + return &the_null; +} + + +/*** deletion ***/ + +void json_delete(json_t *json) +{ + if (!json) + return; + + switch(json_typeof(json)) { + case JSON_OBJECT: + json_delete_object(json_to_object(json)); + break; + case JSON_ARRAY: + json_delete_array(json_to_array(json)); + break; + case JSON_STRING: + json_delete_string(json_to_string(json)); + break; + case JSON_INTEGER: + json_delete_integer(json_to_integer(json)); + break; + case JSON_REAL: + json_delete_real(json_to_real(json)); + break; + default: + return; + } + + /* json_delete is not called for true, false or null */ +} + + +/*** equality ***/ + +int json_equal(const json_t *json1, const json_t *json2) +{ + if(!json1 || !json2) + return 0; + + if(json_typeof(json1) != json_typeof(json2)) + return 0; + + /* this covers true, false and null as they are singletons */ + if(json1 == json2) + return 1; + + switch(json_typeof(json1)) { + case JSON_OBJECT: + return json_object_equal(json1, json2); + case JSON_ARRAY: + return json_array_equal(json1, json2); + case JSON_STRING: + return json_string_equal(json1, json2); + case JSON_INTEGER: + return json_integer_equal(json1, json2); + case JSON_REAL: + return json_real_equal(json1, json2); + default: + return 0; + } +} + + +/*** copying ***/ + +json_t *json_copy(json_t *json) +{ + if(!json) + return NULL; + + switch(json_typeof(json)) { + case JSON_OBJECT: + return json_object_copy(json); + case JSON_ARRAY: + return json_array_copy(json); + case JSON_STRING: + return json_string_copy(json); + case JSON_INTEGER: + return json_integer_copy(json); + case JSON_REAL: + return json_real_copy(json); + case JSON_TRUE: + case JSON_FALSE: + case JSON_NULL: + return json; + default: + return NULL; + } + + return NULL; +} + +json_t *json_deep_copy(const json_t *json) +{ + if(!json) + return NULL; + + switch(json_typeof(json)) { + case JSON_OBJECT: + return json_object_deep_copy(json); + case JSON_ARRAY: + return json_array_deep_copy(json); + /* for the rest of the types, deep copying doesn't differ from + shallow copying */ + case JSON_STRING: + return json_string_copy(json); + case JSON_INTEGER: + return json_integer_copy(json); + case JSON_REAL: + return json_real_copy(json); + case JSON_TRUE: + case JSON_FALSE: + case JSON_NULL: + return (json_t *)json; + default: + return NULL; + } + + return NULL; +} From 61ad070f56bfbd29e1a1c928812c3d3634c6bac7 Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 18 Sep 2018 18:36:03 +0300 Subject: [PATCH 280/310] delete unused procedure after merge --- client/emv/cmdemv.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index dceade79..84630ea7 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -14,20 +14,6 @@ #include "cliparser/cliparser.h" #include -int UsageCmdHFEMVSelect(void) { - PrintAndLog("HELP : Executes select applet command:\n"); - PrintAndLog("Usage: hf emv select [-s][-k][-a][-t] \n"); - PrintAndLog(" Options:"); - PrintAndLog(" -s : select card"); - PrintAndLog(" -k : keep field for next command"); - PrintAndLog(" -a : show APDU reqests and responses\n"); - PrintAndLog(" -t : TLV decode results\n"); - PrintAndLog("Samples:"); - PrintAndLog(" hf emv select -s a00000000101 -> select card, select applet"); - PrintAndLog(" hf emv select -s -t a00000000101 -> select card, select applet, show result in TLV"); - return 0; -} - int CmdHFEMVSelect(const char *cmd) { uint8_t data[APDU_AID_LEN] = {0}; int datalen = 0; From 57548f275a2d1400cac8b30d235a015bee652213 Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 18 Sep 2018 19:06:59 +0300 Subject: [PATCH 281/310] `hf emv exec` works with argtable --- client/emv/cmdemv.c | 184 +++++++++++++++----------------------------- 1 file changed, 60 insertions(+), 124 deletions(-) diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index 84630ea7..93635f85 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -14,11 +14,31 @@ #include "cliparser/cliparser.h" #include +#define TLV_ADD(tag, value)( tlvdb_change_or_add_node(tlvRoot, tag, sizeof(value) - 1, (const unsigned char *)value) ) +void ParamLoadDefaults(struct tlvdb *tlvRoot) { + //9F02:(Amount, authorized (Numeric)) len:6 + TLV_ADD(0x9F02, "\x00\x00\x00\x00\x01\x00"); + //9F1A:(Terminal Country Code) len:2 + TLV_ADD(0x9F1A, "ru"); + //5F2A:(Transaction Currency Code) len:2 + // USD 840, EUR 978, RUR 810, RUB 643, RUR 810(old), UAH 980, AZN 031, n/a 999 + TLV_ADD(0x5F2A, "\x09\x80"); + //9A:(Transaction Date) len:3 + TLV_ADD(0x9A, "\x00\x00\x00"); + //9C:(Transaction Type) len:1 | 00 => Goods and service #01 => Cash + TLV_ADD(0x9C, "\x00"); + // 9F37 Unpredictable Number len:4 + TLV_ADD(0x9F37, "\x01\x02\x03\x04"); + // 9F6A Unpredictable Number (MSD for UDOL) len:4 + TLV_ADD(0x9F6A, "\x01\x02\x03\x04"); + //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 + TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC +} + int CmdHFEMVSelect(const char *cmd) { uint8_t data[APDU_AID_LEN] = {0}; int datalen = 0; - CLIParserInit("hf emv select", "Executes select applet command", "Usage:\n\thf emv select -s a00000000101 -> select card, select applet\n\thf emv select -st a00000000101 -> select card, select applet, show result in TLV\n"); @@ -156,8 +176,6 @@ int CmdHFEMVPPSE(const char *cmd) { return 0; } -#define TLV_ADD(tag, value)( tlvdb_change_or_add_node(tlvRoot, tag, sizeof(value) - 1, (const unsigned char *)value) ) - int CmdHFEMVGPO(const char *cmd) { uint8_t data[APDU_RES_LEN] = {0}; int datalen = 0; @@ -205,23 +223,7 @@ int CmdHFEMVGPO(const char *cmd) { // TODO PrintAndLog("Make PDOL data not implemented!"); - //9F02:(Amount, authorized (Numeric)) len:6 - TLV_ADD(0x9F02, "\x00\x00\x00\x00\x01\x00"); - //9F1A:(Terminal Country Code) len:2 - TLV_ADD(0x9F1A, "ru"); - //5F2A:(Transaction Currency Code) len:2 - // USD 840, EUR 978, RUR 810, RUB 643, RUR 810(old), UAH 980, AZN 031, n/a 999 - TLV_ADD(0x5F2A, "\x09\x80"); - //9A:(Transaction Date) len:3 - TLV_ADD(0x9A, "\x00\x00\x00"); - //9C:(Transaction Type) len:1 | 00 => Goods and service #01 => Cash - TLV_ADD(0x9C, "\x00"); - // 9F37 Unpredictable Number len:4 - TLV_ADD(0x9F37, "\x01\x02\x03\x04"); - // 9F6A Unpredictable Number (MSD for UDOL) len:4 - TLV_ADD(0x9F6A, "\x01\x02\x03\x04"); - //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 - TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC + ParamLoadDefaults(tlvRoot); if (paramsLoadFromFile) { }; @@ -489,26 +491,6 @@ int CmdHFEMVInternalAuthenticate(const char *cmd) { return 0; } -int UsageCmdHFEMVExec(void) { - PrintAndLog("HELP : Executes EMV contactless transaction:\n"); - PrintAndLog("Usage: hf emv exec [-s][-a][-t][-j][-f][-v][-c][-x][-g]\n"); - PrintAndLog(" Options:"); - PrintAndLog(" -s : select card"); - PrintAndLog(" -a : show APDU reqests and responses\n"); - PrintAndLog(" -t : TLV decode results\n"); - PrintAndLog(" -j : load transaction parameters from `emv/defparams.json` file\n"); - PrintAndLog(" -f : force search AID. Search AID instead of execute PPSE.\n"); - PrintAndLog(" -v : transaction type - qVSDC or M/Chip.\n"); - PrintAndLog(" -c : transaction type - qVSDC or M/Chip plus CDA (SDAD generation).\n"); - PrintAndLog(" -x : transaction type - VSDC. For test only. Not a standart behavior.\n"); - PrintAndLog(" -g : VISA. generate AC from GPO\n"); - PrintAndLog("By default : transaction type - MSD.\n"); - PrintAndLog("Samples:"); - PrintAndLog(" hf emv exec -s -a -t -> execute MSD transaction"); - PrintAndLog(" hf emv exec -s -a -t -c -> execute CDA transaction"); - return 0; -} - #define dreturn(n) {free(pdol_data_tlv);tlvdb_free(tlvSelect);tlvdb_free(tlvRoot);DropField();return n;} bool HexToBuffer(const char *errormsg, const char *hexvalue, uint8_t * buffer, size_t maxbufferlen, size_t *bufferlen) { @@ -641,35 +623,7 @@ bool ParamLoadFromJson(struct tlvdb *tlv) { return true; } -void ParamLoadDefaults(struct tlvdb *tlvRoot) { - //9F02:(Amount, authorized (Numeric)) len:6 - TLV_ADD(0x9F02, "\x00\x00\x00\x00\x01\x00"); - //9F1A:(Terminal Country Code) len:2 - TLV_ADD(0x9F1A, "ru"); - //5F2A:(Transaction Currency Code) len:2 - // USD 840, EUR 978, RUR 810, RUB 643, RUR 810(old), UAH 980, AZN 031, n/a 999 - TLV_ADD(0x5F2A, "\x09\x80"); - //9A:(Transaction Date) len:3 - TLV_ADD(0x9A, "\x00\x00\x00"); - //9C:(Transaction Type) len:1 | 00 => Goods and service #01 => Cash - TLV_ADD(0x9C, "\x00"); - // 9F37 Unpredictable Number len:4 - TLV_ADD(0x9F37, "\x01\x02\x03\x04"); - // 9F6A Unpredictable Number (MSD for UDOL) len:4 - TLV_ADD(0x9F6A, "\x01\x02\x03\x04"); - //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 - TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC -} - int CmdHFEMVExec(const char *cmd) { - bool activateField = false; - bool showAPDU = false; - bool decodeTLV = false; - bool forceSearch = false; - enum TransactionType TrType = TT_MSD; - bool GenACGPO = false; - bool paramLoadJSON = false; - uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; uint16_t sw = 0; @@ -684,63 +638,45 @@ int CmdHFEMVExec(const char *cmd) { struct tlvdb *tlvRoot = NULL; struct tlv *pdol_data_tlv = NULL; - if (strlen(cmd) < 1) { - UsageCmdHFEMVExec(); - return 0; - } - - int cmdp = 0; - while(param_getchar(cmd, cmdp) != 0x00) { - char c = param_getchar(cmd, cmdp); - if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) - switch (param_getchar_indx(cmd, 1, cmdp)) { - case 'h': - case 'H': - UsageCmdHFEMVExec(); - return 0; - case 's': - case 'S': - activateField = true; - break; - case 'a': - case 'A': - showAPDU = true; - break; - case 't': - case 'T': - decodeTLV = true; - break; - case 'f': - case 'F': - forceSearch = true; - break; - case 'x': - case 'X': - TrType = TT_VSDC; - break; - case 'v': - case 'V': - TrType = TT_QVSDCMCHIP; - break; - case 'c': - case 'C': - TrType = TT_CDA; - break; - case 'g': - case 'G': - GenACGPO = true; - break; - case 'j': - case 'J': - paramLoadJSON = true; - break; - default: - PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); - return 1; - } - cmdp++; - } + CLIParserInit("hf emv exec", + "Executes EMV contactless transaction", + "Usage:\n\thf emv exec -sat -> select card, execute MSD transaction, show APDU and TLV\n" + "\thf emv exec -satc -> select card, execute CDA transaction, show APDU and TLV\n"); + void* argtable[] = { + arg_param_begin, + arg_lit0("sS", "select", "activate field and select card."), + arg_lit0("aA", "apdu", "show APDU reqests and responses."), + arg_lit0("tT", "tlv", "TLV decode results."), + arg_lit0("jJ", "jload", "Load transaction parameters from `emv/defparams.json` file."), + arg_lit0("fF", "forceaid", "Force search AID. Search AID instead of execute PPSE."), + arg_rem("By default:", "Transaction type - MSD"), + arg_lit0("vV", "qvsdc", "Transaction type - qVSDC or M/Chip."), + arg_lit0("cC", "qvsdccda", "Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)."), + arg_lit0("xX", "vsdc", "Transaction type - VSDC. For test only. Not a standart behavior."), + arg_lit0("gG", "acgpo", "VISA. generate AC from GPO."), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool activateField = arg_get_lit(1); + bool showAPDU = arg_get_lit(2); + bool decodeTLV = arg_get_lit(3); + bool paramLoadJSON = arg_get_lit(4); + bool forceSearch = arg_get_lit(5); + + enum TransactionType TrType = TT_MSD; + if (arg_get_lit(6)) + TrType = TT_QVSDCMCHIP; + if (arg_get_lit(7)) + TrType = TT_CDA; + if (arg_get_lit(8)) + TrType = TT_VSDC; + + bool GenACGPO = arg_get_lit(9); + CLIParserFree(); + + SetAPDULogging(showAPDU); // init applets list tree const char *al = "Applets list"; From 696e8630782aa1707c23c2ce4b0e8394689835ac Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Tue, 18 Sep 2018 19:52:41 +0300 Subject: [PATCH 282/310] added EMV tag #9F6E (#676) --- client/emv/emv_tags.c | 1 + 1 file changed, 1 insertion(+) diff --git a/client/emv/emv_tags.c b/client/emv/emv_tags.c index cdd57cb5..02b039f8 100644 --- a/client/emv/emv_tags.c +++ b/client/emv/emv_tags.c @@ -272,6 +272,7 @@ static const struct emv_tag emv_tags[] = { { 0x9f6a, "Unpredictable Number", EMV_TAG_NUMERIC }, { 0x9f6b, "Track 2 Data" }, { 0x9f6c, "Card Transaction Qualifiers (CTQ)", EMV_TAG_BITMASK, &EMV_CTQ }, + { 0x9f6e, "Form Factor Indicator" }, { 0xa5 , "File Control Information (FCI) Proprietary Template" }, { 0xbf0c, "File Control Information (FCI) Issuer Discretionary Data" }, { 0xdf20, "Issuer Proprietary Bitmap (IPB)" }, From 050aa18b13084b0d57ffcee352bf7a30b828b8f5 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Fri, 21 Sep 2018 08:27:35 +0200 Subject: [PATCH 283/310] RDV40 compatibility fixes (#678) * detect and use RDV40 higher voltage ADC channel for hw tune, hf tune, hw detectreader * fix mode switching in hw detectreader * detect Smartcard Slot in hw version * i2c changes from https://github.com/RfidResearchGroup/proxmark3 * some formatting in proxmark3.h --- armsrc/Makefile | 7 +- armsrc/appmain.c | 95 ++++++++----- armsrc/apps.h | 5 +- armsrc/i2c.c | 339 +++++++++++++++++++++++++++++--------------- armsrc/i2c.h | 33 +---- armsrc/iso14443a.c | 8 +- armsrc/mifaresim.c | 2 +- include/proxmark3.h | 107 +++++++------- 8 files changed, 352 insertions(+), 244 deletions(-) diff --git a/armsrc/Makefile b/armsrc/Makefile index 046ad1bc..d230cda1 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -26,11 +26,8 @@ SRC_ISO14443a = epa.c iso14443a.c mifareutil.c mifarecmd.c mifaresniff.c mifares SRC_ISO14443b = iso14443b.c SRC_CRAPTO1 = crypto1.c des.c SRC_CRC = iso14443crc.c crc.c crc16.c crc32.c parity.c -ifneq (,$(findstring WITH_SMARTCARD,$(APP_CFLAGS))) - SRC_SMARTCARD = i2c.c -else - SRC_SMARTCARD = -endif +SRC_SMARTCARD = i2c.c + #the FPGA bitstream files. Note: order matters! FPGA_BITSTREAMS = fpga_lf.bit fpga_hf.bit diff --git a/armsrc/appmain.c b/armsrc/appmain.c index e6d40abc..573a3a71 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -28,12 +28,10 @@ #include "BigBuf.h" #include "mifareutil.h" #include "pcf7931.h" +#include "i2c.h" #ifdef WITH_LCD #include "LCD.h" #endif -#ifdef WITH_SMARTCARD - #include "i2c.h" -#endif // Craig Young - 14a stand-alone code @@ -143,7 +141,7 @@ void Dbhexdump(int len, uint8_t *d, bool bAsci) { static int ReadAdc(int ch) { // Note: ADC_MODE_PRESCALE and ADC_MODE_SAMPLE_HOLD_TIME are set to the maximum allowed value. - // AMPL_HI is are high impedance (10MOhm || 1MOhm) output, the input capacitance of the ADC is 12pF (typical). This results in a time constant + // AMPL_HI is a high impedance (10MOhm || 1MOhm) output, the input capacitance of the ADC is 12pF (typical). This results in a time constant // of RC = (0.91MOhm) * 12pF = 10.9us. Even after the maximum configurable sample&hold time of 40us the input capacitor will not be fully charged. // // The maths are: @@ -162,7 +160,7 @@ static int ReadAdc(int ch) while(!(AT91C_BASE_ADC->ADC_SR & ADC_END_OF_CONVERSION(ch))) {}; - return AT91C_BASE_ADC->ADC_CDR[ch]; + return AT91C_BASE_ADC->ADC_CDR[ch] & 0x3ff; } int AvgAdc(int ch) // was static - merlok @@ -177,6 +175,26 @@ int AvgAdc(int ch) // was static - merlok return (a + 15) >> 5; } +static int AvgAdc_Voltage_HF(void) +{ + int AvgAdc_Voltage_Low, AvgAdc_Voltage_High; + + AvgAdc_Voltage_Low= (MAX_ADC_HF_VOLTAGE_LOW * AvgAdc(ADC_CHAN_HF_LOW)) >> 10; + // if voltage range is about to be exceeded, use high voltage ADC channel if available (RDV40 only) + if (AvgAdc_Voltage_Low > MAX_ADC_HF_VOLTAGE_LOW - 300) { + AvgAdc_Voltage_High = (MAX_ADC_HF_VOLTAGE_HIGH * AvgAdc(ADC_CHAN_HF_HIGH)) >> 10; + if (AvgAdc_Voltage_High >= AvgAdc_Voltage_Low) { + return AvgAdc_Voltage_High; + } + } + return AvgAdc_Voltage_Low; +} + +static int AvgAdc_Voltage_LF(void) +{ + return (MAX_ADC_LF_VOLTAGE * AvgAdc(ADC_CHAN_LF)) >> 10; +} + void MeasureAntennaTuningLfOnly(int *vLf125, int *vLf134, int *peakf, int *peakv, uint8_t LF_Results[]) { int i, adcval = 0, peak = 0; @@ -198,7 +216,7 @@ void MeasureAntennaTuningLfOnly(int *vLf125, int *vLf134, int *peakf, int *peakv WDT_HIT(); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, i); SpinDelay(20); - adcval = ((MAX_ADC_LF_VOLTAGE * AvgAdc(ADC_CHAN_LF)) >> 10); + adcval = AvgAdc_Voltage_LF(); if (i==95) *vLf125 = adcval; // voltage at 125Khz if (i==89) *vLf134 = adcval; // voltage at 134Khz @@ -223,9 +241,8 @@ void MeasureAntennaTuningHfOnly(int *vHf) FpgaDownloadAndGo(FPGA_BITSTREAM_HF); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); SpinDelay(20); - *vHf = (MAX_ADC_HF_VOLTAGE * AvgAdc(ADC_CHAN_HF)) >> 10; + *vHf = AvgAdc_Voltage_HF(); LED_A_OFF(); - return; } @@ -267,8 +284,8 @@ void MeasureAntennaTuningHf(void) FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); for (;;) { - SpinDelay(20); - vHf = (MAX_ADC_HF_VOLTAGE * AvgAdc(ADC_CHAN_HF)) >> 10; + SpinDelay(500); + vHf = AvgAdc_Voltage_HF(); Dbprintf("%d mV",vHf); if (BUTTON_PRESS()) break; @@ -293,6 +310,7 @@ extern struct version_information version_information; /* bootrom version information is pointed to from _bootphase1_version_pointer */ extern char *_bootphase1_version_pointer, _flash_start, _flash_end, _bootrom_start, _bootrom_end, __data_src_start__; + void SendVersion(void) { char temp[USB_CMD_DATA_SIZE]; /* Limited data payload in USB packets */ @@ -315,11 +333,16 @@ void SendVersion(void) for (int i = 0; i < fpga_bitstream_num; i++) { strncat(VersionString, fpga_version_information[i], sizeof(VersionString) - strlen(VersionString) - 1); - if (i < fpga_bitstream_num - 1) { - strncat(VersionString, "\n", sizeof(VersionString) - strlen(VersionString) - 1); - } + strncat(VersionString, "\n", sizeof(VersionString) - strlen(VersionString) - 1); } - + + // test availability of SmartCard slot + if (I2C_is_available()) { + strncat(VersionString, "SmartCard Slot: available\n", sizeof(VersionString) - strlen(VersionString) - 1); + } else { + strncat(VersionString, "SmartCard Slot: not available\n", sizeof(VersionString) - strlen(VersionString) - 1); + } + // Send Chip ID and used flash memory uint32_t text_and_rodata_section_size = (uint32_t)&__data_src_start__ - (uint32_t)&_flash_start; uint32_t compressed_data_section_size = common_area.arg1; @@ -828,13 +851,15 @@ static const int LIGHT_LEN = sizeof(LIGHT_SCHEME)/sizeof(LIGHT_SCHEME[0]); void ListenReaderField(int limit) { - int lf_av, lf_av_new, lf_baseline= 0, lf_max; - int hf_av, hf_av_new, hf_baseline= 0, hf_max; + int lf_av, lf_av_new=0, lf_baseline= 0, lf_max; + int hf_av, hf_av_new=0, hf_baseline= 0, hf_max; int mode=1, display_val, display_max, i; -#define LF_ONLY 1 -#define HF_ONLY 2 -#define REPORT_CHANGE 10 // report new values only if they have changed at least by REPORT_CHANGE +#define LF_ONLY 1 +#define HF_ONLY 2 +#define REPORT_CHANGE_PERCENT 5 // report new values only if they have changed at least by REPORT_CHANGE_PERCENT +#define MIN_HF_FIELD 300 // in mode 1 signal HF field greater than MIN_HF_FIELD above baseline +#define MIN_LF_FIELD 1200 // in mode 1 signal LF field greater than MIN_LF_FIELD above baseline // switch off FPGA - we don't want to measure our own signal @@ -843,23 +868,23 @@ void ListenReaderField(int limit) LEDsoff(); - lf_av = lf_max = AvgAdc(ADC_CHAN_LF); + lf_av = lf_max = AvgAdc_Voltage_LF(); if(limit != HF_ONLY) { - Dbprintf("LF 125/134kHz Baseline: %dmV", (MAX_ADC_LF_VOLTAGE * lf_av) >> 10); + Dbprintf("LF 125/134kHz Baseline: %dmV", lf_av); lf_baseline = lf_av; } - hf_av = hf_max = AvgAdc(ADC_CHAN_HF); - + hf_av = hf_max = AvgAdc_Voltage_HF(); + if (limit != LF_ONLY) { - Dbprintf("HF 13.56MHz Baseline: %dmV", (MAX_ADC_HF_VOLTAGE * hf_av) >> 10); + Dbprintf("HF 13.56MHz Baseline: %dmV", hf_av); hf_baseline = hf_av; } for(;;) { + SpinDelay(500); if (BUTTON_PRESS()) { - SpinDelay(500); switch (mode) { case 1: mode=2; @@ -872,21 +897,22 @@ void ListenReaderField(int limit) return; break; } + while (BUTTON_PRESS()); } WDT_HIT(); if (limit != HF_ONLY) { if(mode == 1) { - if (ABS(lf_av - lf_baseline) > REPORT_CHANGE) + if (lf_av - lf_baseline > MIN_LF_FIELD) LED_D_ON(); else LED_D_OFF(); } - lf_av_new = AvgAdc(ADC_CHAN_LF); + lf_av_new = AvgAdc_Voltage_LF(); // see if there's a significant change - if(ABS(lf_av - lf_av_new) > REPORT_CHANGE) { - Dbprintf("LF 125/134kHz Field Change: %5dmV", (MAX_ADC_LF_VOLTAGE * lf_av_new) >> 10); + if (ABS((lf_av - lf_av_new)*100/(lf_av?lf_av:1)) > REPORT_CHANGE_PERCENT) { + Dbprintf("LF 125/134kHz Field Change: %5dmV", lf_av_new); lf_av = lf_av_new; if (lf_av > lf_max) lf_max = lf_av; @@ -895,16 +921,17 @@ void ListenReaderField(int limit) if (limit != LF_ONLY) { if (mode == 1){ - if (ABS(hf_av - hf_baseline) > REPORT_CHANGE) + if (hf_av - hf_baseline > MIN_HF_FIELD) LED_B_ON(); else LED_B_OFF(); } - hf_av_new = AvgAdc(ADC_CHAN_HF); + hf_av_new = AvgAdc_Voltage_HF(); + // see if there's a significant change - if(ABS(hf_av - hf_av_new) > REPORT_CHANGE) { - Dbprintf("HF 13.56MHz Field Change: %5dmV", (MAX_ADC_HF_VOLTAGE * hf_av_new) >> 10); + if (ABS((hf_av - hf_av_new)*100/(hf_av?hf_av:1)) > REPORT_CHANGE_PERCENT) { + Dbprintf("HF 13.56MHz Field Change: %5dmV", hf_av_new); hf_av = hf_av_new; if (hf_av > hf_max) hf_max = hf_av; @@ -1436,7 +1463,7 @@ void __attribute__((noreturn)) AppMain(void) LED_A_OFF(); // Init USB device - usb_enable(); + usb_enable(); // The FPGA gets its clock from us from PCK0 output, so set that up. AT91C_BASE_PIOA->PIO_BSR = GPIO_PCK0; diff --git a/armsrc/apps.h b/armsrc/apps.h index 5b981f4f..6f728c61 100644 --- a/armsrc/apps.h +++ b/armsrc/apps.h @@ -42,9 +42,10 @@ void Dbprintf(const char *fmt, ...); void Dbhexdump(int len, uint8_t *d, bool bAsci); // ADC Vref = 3300mV, and an (10M+1M):1M voltage divider on the HF input can measure voltages up to 36300 mV -#define MAX_ADC_HF_VOLTAGE 36300 +#define MAX_ADC_HF_VOLTAGE_LOW 36300 // ADC Vref = 3300mV, and an (10000k+240k):240k voltage divider on the LF input can measure voltages up to 140800 mV -#define MAX_ADC_LF_VOLTAGE 140800 +#define MAX_ADC_HF_VOLTAGE_HIGH 140800 +#define MAX_ADC_LF_VOLTAGE 140800 int AvgAdc(int ch); void ToSendStuffBit(int b); diff --git a/armsrc/i2c.c b/armsrc/i2c.c index 721b4b2e..7efb1fd3 100644 --- a/armsrc/i2c.c +++ b/armsrc/i2c.c @@ -8,9 +8,21 @@ //----------------------------------------------------------------------------- // The main i2c code, for communications with smart card module //----------------------------------------------------------------------------- + #include "i2c.h" -#include "mifareutil.h" //for mf_dbglevel + +#include +#include #include "string.h" //for memset memcmp +#include "proxmark3.h" +#include "mifareutil.h" // for MF_DBGLEVEL +#include "BigBuf.h" +#include "apps.h" + +#ifdef WITH_SMARTCARD +#include "smartcard.h" +#endif + // ¶¨ÒåÁ¬½ÓÒý½Å #define GPIO_RST AT91C_PIO_PA1 @@ -27,13 +39,13 @@ #define I2C_ERROR "I2C_WaitAck Error" -volatile unsigned long c; +static volatile unsigned long c; // Ö±½ÓʹÓÃÑ­»·À´ÑÓʱ£¬Ò»¸öÑ­»· 6 ÌõÖ¸Á48M£¬ Delay=1 ´ó¸ÅΪ 200kbps // timer. // I2CSpinDelayClk(4) = 12.31us // I2CSpinDelayClk(1) = 3.07us -void __attribute__((optimize("O0"))) I2CSpinDelayClk(uint16_t delay) { +static void __attribute__((optimize("O0"))) I2CSpinDelayClk(uint16_t delay) { for (c = delay * 2; c; c--) {}; } @@ -45,23 +57,19 @@ void __attribute__((optimize("O0"))) I2CSpinDelayClk(uint16_t delay) { #define ISO7618_MAX_FRAME 255 -void I2C_init(void) { - // ÅäÖø´Î»Òý½Å£¬¹Ø±ÕÉÏÀ­£¬ÍÆÍìÊä³ö£¬Ä¬ÈÏ¸ß - // Configure reset pin, close up pull up, push-pull output, default high - AT91C_BASE_PIOA->PIO_PPUDR = GPIO_RST; - AT91C_BASE_PIOA->PIO_MDDR = GPIO_RST; +static void I2C_init(void) { + // Configure reset pin + AT91C_BASE_PIOA->PIO_PPUDR = GPIO_RST; // disable pull up resistor + AT91C_BASE_PIOA->PIO_MDDR = GPIO_RST; // push-pull output (multidriver disabled) - // ÅäÖà I2C Òý½Å£¬¿ªÆôÉÏÀ­£¬¿ªÂ©Êä³ö - // Configure I2C pin, open up, open leakage - AT91C_BASE_PIOA->PIO_PPUER |= (GPIO_SCL | GPIO_SDA); // ´ò¿ªÉÏÀ­ Open up the pull up - AT91C_BASE_PIOA->PIO_MDER |= (GPIO_SCL | GPIO_SDA); + // Configure SCL and SDA pins + AT91C_BASE_PIOA->PIO_PPUER |= (GPIO_SCL | GPIO_SDA); // enable pull up resistor + AT91C_BASE_PIOA->PIO_MDER |= (GPIO_SCL | GPIO_SDA); // open drain output (multidriver enabled) - requires external pull up resistor - // ĬÈÏÈý¸ùÏßÈ«²¿À­¸ß - // default three lines all pull up + // set all three outputs to high AT91C_BASE_PIOA->PIO_SODR |= (GPIO_SCL | GPIO_SDA | GPIO_RST); - // ÔÊÐíÊä³ö - // allow output + // configure all three pins as output, controlled by PIOA AT91C_BASE_PIOA->PIO_OER |= (GPIO_SCL | GPIO_SDA | GPIO_RST); AT91C_BASE_PIOA->PIO_PER |= (GPIO_SCL | GPIO_SDA | GPIO_RST); } @@ -69,7 +77,7 @@ void I2C_init(void) { // ÉèÖø´Î»×´Ì¬ // set the reset state -void I2C_SetResetStatus(uint8_t LineRST, uint8_t LineSCK, uint8_t LineSDA) { +static void I2C_SetResetStatus(uint8_t LineRST, uint8_t LineSCK, uint8_t LineSDA) { if (LineRST) HIGH(GPIO_RST); else @@ -89,7 +97,7 @@ void I2C_SetResetStatus(uint8_t LineRST, uint8_t LineSCK, uint8_t LineSDA) { // ¸´Î»½øÈëÖ÷³ÌÐò // Reset the SIM_Adapter, then enter the main program // Note: the SIM_Adapter will not enter the main program after power up. Please run this function before use SIM_Adapter. -void I2C_Reset_EnterMainProgram(void) { +static void I2C_Reset_EnterMainProgram(void) { I2C_SetResetStatus(0, 0, 0); // À­µÍ¸´Î»Ïß SpinDelay(30); I2C_SetResetStatus(1, 0, 0); // ½â³ý¸´Î» @@ -98,19 +106,9 @@ void I2C_Reset_EnterMainProgram(void) { SpinDelay(10); } -// ¸´Î»½øÈëÒýµ¼Ä£Ê½ -// Reset the SIM_Adapter, then enter the bootloader program -// Reserve£ºFor firmware update. -void I2C_Reset_EnterBootloader(void) { - I2C_SetResetStatus(0, 1, 1); // À­µÍ¸´Î»Ïß - SpinDelay(100); - I2C_SetResetStatus(1, 1, 1); // ½â³ý¸´Î» - SpinDelay(10); -} - // µÈ´ýʱÖÓ±ä¸ß // Wait for the clock to go High. -bool WaitSCL_H_delay(uint32_t delay) { +static bool WaitSCL_H_delay(uint32_t delay) { while (delay--) { if (SCL_read) { return true; @@ -120,26 +118,26 @@ bool WaitSCL_H_delay(uint32_t delay) { return false; } -// 5000 * 3.07us = 15350us. 15.35ms -bool WaitSCL_H(void) { - return WaitSCL_H_delay(5000); +// 15000 * 3.07us = 46050us. 46.05ms +static bool WaitSCL_H(void) { + return WaitSCL_H_delay(15000); } -// Wait max 300ms or until SCL goes LOW. -// Which ever comes first -bool WaitSCL_L_300ms(void) { - volatile uint16_t delay = 300; - while ( delay-- ) { - // exit on SCL LOW - if (!SCL_read) +bool WaitSCL_L_delay(uint32_t delay) { + while (delay--) { + if (!SCL_read) { return true; - - SpinDelay(1); + } + I2C_DELAY_1CLK; } - return (delay == 0); + return false; } -bool I2C_Start(void) { +bool WaitSCL_L(void) { + return WaitSCL_L_delay(15000); +} + +static bool I2C_Start(void) { I2C_DELAY_XCLK(4); SDA_H; I2C_DELAY_1CLK; @@ -155,22 +153,8 @@ bool I2C_Start(void) { return true; } -bool I2C_WaitForSim() { - // variable delay here. - if (!WaitSCL_L_300ms()) - return false; - - // 8051 speaks with smart card. - // 1000*50*3.07 = 153.5ms - // 1byte transfer == 1ms - if (!WaitSCL_H_delay(2000*50) ) - return false; - - return true; -} - // send i2c STOP -void I2C_Stop(void) { +static void I2C_Stop(void) { SCL_L; I2C_DELAY_2CLK; SDA_L; I2C_DELAY_2CLK; SCL_H; I2C_DELAY_2CLK; @@ -179,29 +163,14 @@ void I2C_Stop(void) { I2C_DELAY_XCLK(8); } -// Send i2c ACK -void I2C_Ack(void) { - SCL_L; I2C_DELAY_2CLK; - SDA_L; I2C_DELAY_2CLK; - SCL_H; I2C_DELAY_2CLK; - SCL_L; I2C_DELAY_2CLK; -} - -// Send i2c NACK -void I2C_NoAck(void) { - SCL_L; I2C_DELAY_2CLK; - SDA_H; I2C_DELAY_2CLK; - SCL_H; I2C_DELAY_2CLK; - SCL_L; I2C_DELAY_2CLK; -} - -bool I2C_WaitAck(void) { +static bool I2C_WaitAck(void) { SCL_L; I2C_DELAY_1CLK; SDA_H; I2C_DELAY_1CLK; SCL_H; if (!WaitSCL_H()) return false; + I2C_DELAY_2CLK; I2C_DELAY_2CLK; if (SDA_read) { SCL_L; @@ -211,10 +180,10 @@ bool I2C_WaitAck(void) { return true; } -void I2C_SendByte(uint8_t data) { - uint8_t i = 8; +static void I2C_SendByte(uint8_t data) { + uint8_t bits = 8; - while (i--) { + while (bits--) { SCL_L; I2C_DELAY_1CLK; if (data & 0x80) @@ -223,6 +192,7 @@ void I2C_SendByte(uint8_t data) { SDA_L; data <<= 1; + I2C_DELAY_1CLK; SCL_H; @@ -234,18 +204,92 @@ void I2C_SendByte(uint8_t data) { SCL_L; } -uint8_t I2C_ReadByte(void) { - uint8_t i = 8, b = 0; +bool I2C_is_available(void) { + I2C_init(); + I2C_Reset_EnterMainProgram(); + if (!I2C_Start()) // some other device is active on the bus + return true; + I2C_SendByte(I2C_DEVICE_ADDRESS_MAIN & 0xFE); + if (!I2C_WaitAck()) { // no response from smartcard reader + I2C_Stop(); + return false; + } + I2C_Stop(); + return true; +} + +#ifdef WITH_SMARTCARD +// ¸´Î»½øÈëÒýµ¼Ä£Ê½ +// Reset the SIM_Adapter, then enter the bootloader program +// Reserve£ºFor firmware update. +static void I2C_Reset_EnterBootloader(void) { + I2C_SetResetStatus(0, 1, 1); // À­µÍ¸´Î»Ïß + SpinDelay(100); + I2C_SetResetStatus(1, 1, 1); // ½â³ý¸´Î» + SpinDelay(10); +} + +// Wait max 300ms or until SCL goes LOW. +// Which ever comes first +static bool WaitSCL_L_300ms(void) { + volatile uint16_t delay = 310; + while ( delay-- ) { + // exit on SCL LOW + if (!SCL_read) + return true; + + SpinDelay(1); + } + return (delay == 0); +} + +static bool I2C_WaitForSim() { + // variable delay here. + if (!WaitSCL_L_300ms()) + return false; + + // 8051 speaks with smart card. + // 1000*50*3.07 = 153.5ms + // 1byte transfer == 1ms with max frame being 256bytes + if (!WaitSCL_H_delay(10 * 1000 * 50)) + return false; + + return true; +} + +// Send i2c ACK +static void I2C_Ack(void) { + SCL_L; I2C_DELAY_2CLK; + SDA_L; I2C_DELAY_2CLK; + SCL_H; I2C_DELAY_2CLK; + if (!WaitSCL_H()) return; + SCL_L; I2C_DELAY_2CLK; +} + +// Send i2c NACK +static void I2C_NoAck(void) { + SCL_L; I2C_DELAY_2CLK; + SDA_H; I2C_DELAY_2CLK; + SCL_H; I2C_DELAY_2CLK; + if (!WaitSCL_H()) return; + SCL_L; I2C_DELAY_2CLK; +} + +static int16_t I2C_ReadByte(void) { + uint8_t bits = 8, b = 0; SDA_H; - while (i--) { + while (bits--) { b <<= 1; - SCL_L; I2C_DELAY_2CLK; - SCL_H; - if (!WaitSCL_H()) - return 0; + SCL_L; + if (!WaitSCL_L()) return -2; + + I2C_DELAY_1CLK; - I2C_DELAY_2CLK; + SCL_H; + if (!WaitSCL_H()) return -1; + + I2C_DELAY_1CLK; if (SDA_read) b |= 0x01; } @@ -254,7 +298,7 @@ uint8_t I2C_ReadByte(void) { } // Sends one byte ( command to be written, SlaveDevice address) -bool I2C_WriteCmd(uint8_t device_cmd, uint8_t device_address) { +static bool I2C_WriteCmd(uint8_t device_cmd, uint8_t device_address) { bool bBreak = true; do { if (!I2C_Start()) @@ -281,7 +325,7 @@ bool I2C_WriteCmd(uint8_t device_cmd, uint8_t device_address) { // дÈë1×Ö½ÚÊý¾Ý £¨´ýдÈëÊý¾Ý£¬´ýдÈëµØÖ·£¬Æ÷¼þÀàÐÍ£© // Sends 1 byte data (Data to be written, command to be written , SlaveDevice address ). -bool I2C_WriteByte(uint8_t data, uint8_t device_cmd, uint8_t device_address) { +static bool I2C_WriteByte(uint8_t data, uint8_t device_cmd, uint8_t device_address) { bool bBreak = true; do { if (!I2C_Start()) @@ -313,7 +357,7 @@ bool I2C_WriteByte(uint8_t data, uint8_t device_cmd, uint8_t device_address) { // дÈë1´®Êý¾Ý£¨´ýдÈëÊý×鵨ַ£¬´ýдÈ볤¶È£¬´ýдÈëµØÖ·£¬Æ÷¼þÀàÐÍ£© //Sends a string of data (Array, length, command to be written , SlaveDevice address ). // len = uint8 (max buffer to write 256bytes) -bool I2C_BufferWrite(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address) { +static bool I2C_BufferWrite(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address) { bool bBreak = true; do { if (!I2C_Start()) @@ -352,16 +396,16 @@ bool I2C_BufferWrite(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t dev // ¶Á³ö1´®Êý¾Ý£¨´æ·Å¶Á³öÊý¾Ý£¬´ý¶Á³ö³¤¶È£¬´ø¶Á³öµØÖ·£¬Æ÷¼þÀàÐÍ£© // read 1 strings of data (Data array, Readout length, command to be written , SlaveDevice address ). // len = uint8 (max buffer to read 256bytes) -uint8_t I2C_BufferRead(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address) { +static int16_t I2C_BufferRead(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address) { if ( !data || len == 0 ) return 0; // extra wait 500us (514us measured) // 200us (xx measured) - SpinDelayUs(200); + SpinDelayUs(600); bool bBreak = true; - uint8_t readcount = 0; + uint16_t readcount = 0; do { if (!I2C_Start()) @@ -391,11 +435,14 @@ uint8_t I2C_BufferRead(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t d return 0; } - // reading while (len) { - *data = I2C_ReadByte(); - + int16_t tmp = I2C_ReadByte(); + if ( tmp < 0 ) + return tmp; + + *data = (uint8_t)tmp & 0xFF; + len--; // ¶ÁÈ¡µÄµÚÒ»¸ö×Ö½ÚΪºóÐø³¤¶È @@ -416,10 +463,10 @@ uint8_t I2C_BufferRead(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t d I2C_Stop(); // return bytecount - first byte (which is length byte) - return (readcount) ? --readcount : 0; + return --readcount; } -uint8_t I2C_ReadFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address) { +static int16_t I2C_ReadFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address) { //START, 0xB0, 0x00, 0x00, START, 0xB1, xx, yy, zz, ......, STOP bool bBreak = true; uint8_t readcount = 0; @@ -461,8 +508,13 @@ uint8_t I2C_ReadFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t // reading while (len) { - *data = I2C_ReadByte(); - + + int16_t tmp = I2C_ReadByte(); + if ( tmp < 0 ) + return tmp; + + *data = (uint8_t)tmp & 0xFF; + data++; readcount++; len--; @@ -478,7 +530,7 @@ uint8_t I2C_ReadFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t return readcount; } -bool I2C_WriteFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address) { +static bool I2C_WriteFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address) { //START, 0xB0, 0x00, 0x00, xx, yy, zz, ......, STOP bool bBreak = true; @@ -534,13 +586,40 @@ void I2C_print_status(void) { DbpString(" version.................FAILED"); } -bool GetATR(smart_card_atr_t *card_ptr) { - - // clear - if ( card_ptr ) { - card_ptr->atr_len = 0; - memset(card_ptr->atr, 0, sizeof(card_ptr->atr)); +// Will read response from smart card module, retries 3 times to get the data. +static bool sc_rx_bytes(uint8_t* dest, uint8_t *destlen) { + uint8_t i = 3; + int16_t len = 0; + while (i--) { + + I2C_WaitForSim(); + + len = I2C_BufferRead(dest, *destlen, I2C_DEVICE_CMD_READ, I2C_DEVICE_ADDRESS_MAIN); + + if ( len > 1 ){ + break; + } else if ( len == 1 ) { + continue; + } else if ( len <= 0 ) { + return false; + } } + // after three + if ( len <= 1 ) + return false; + + *destlen = (uint8_t)len & 0xFF; + return true; +} + +static bool GetATR(smart_card_atr_t *card_ptr) { + + if ( !card_ptr ) { + return false; + } + + card_ptr->atr_len = 0; + memset(card_ptr->atr, 0, sizeof(card_ptr->atr)); // Send ATR // start [C0 01] stop start C1 len aa bb cc stop] @@ -548,16 +627,38 @@ bool GetATR(smart_card_atr_t *card_ptr) { uint8_t cmd[1] = {1}; LogTrace(cmd, 1, 0, 0, NULL, true); - //wait for sim card to answer. + // wait for sim card to answer. + // 1byte = 1ms, max frame 256bytes. Should wait 256ms at least just in case. if (!I2C_WaitForSim()) return false; - // read answer - uint8_t len = I2C_BufferRead(card_ptr->atr, sizeof(card_ptr->atr), I2C_DEVICE_CMD_READ, I2C_DEVICE_ADDRESS_MAIN); - - if ( len == 0 ) + // read bytes from module + uint8_t len = sizeof(card_ptr->atr); + if ( !sc_rx_bytes(card_ptr->atr, &len) ) return false; + uint8_t pos_td = 1; + if ( (card_ptr->atr[1] & 0x10) == 0x10) pos_td++; + if ( (card_ptr->atr[1] & 0x20) == 0x20) pos_td++; + if ( (card_ptr->atr[1] & 0x40) == 0x40) pos_td++; + + // T0 indicate presence T=0 vs T=1. T=1 has checksum TCK + if ( (card_ptr->atr[1] & 0x80) == 0x80) { + + pos_td++; + + // 1 == T1 , presence of checksum TCK + if ( (card_ptr->atr[pos_td] & 0x01) == 0x01) { + uint8_t chksum = 0; + // xor property. will be zero when xored with chksum. + for (uint8_t i = 1; i < len; ++i) + chksum ^= card_ptr->atr[i]; + if ( chksum ) { + if ( MF_DBGLEVEL > 2) DbpString("Wrong ATR checksum"); + } + } + } + // for some reason we only get first byte of atr, if that is so, send dummy command to retrieve the rest of the atr if (len == 1) { @@ -571,10 +672,8 @@ bool GetATR(smart_card_atr_t *card_ptr) { len = len + len2; } - if ( card_ptr ) { - card_ptr->atr_len = len; - LogTrace(card_ptr->atr, card_ptr->atr_len, 0, 0, NULL, false); - } + card_ptr->atr_len = len; + LogTrace(card_ptr->atr, card_ptr->atr_len, 0, 0, NULL, false); return true; } @@ -631,7 +730,9 @@ void SmartCardRaw( uint64_t arg0, uint64_t arg1, uint8_t *data ) { if ( !I2C_WaitForSim() ) goto OUT; - len = I2C_BufferRead(resp, ISO7618_MAX_FRAME, I2C_DEVICE_CMD_READ, I2C_DEVICE_ADDRESS_MAIN); + // read bytes from module + len = ISO7618_MAX_FRAME; + sc_rx_bytes(resp, &len); LogTrace(resp, len, 0, 0, NULL, false); } OUT: @@ -652,7 +753,7 @@ void SmartCardUpgrade(uint64_t arg0) { I2C_Reset_EnterBootloader(); bool isOK = true; - uint8_t res = 0; + int16_t res = 0; uint16_t length = arg0; uint16_t pos = 0; uint8_t *fwdata = BigBuf_get_addr(); @@ -680,7 +781,7 @@ void SmartCardUpgrade(uint64_t arg0) { // read res = I2C_ReadFW(verfiydata, size, msb, lsb, I2C_DEVICE_ADDRESS_BOOT); - if ( res == 0) { + if ( res <= 0) { DbpString("Reading back failed"); isOK = false; break; @@ -718,3 +819,5 @@ void SmartCardSetClock(uint64_t arg0) { set_tracing(false); LEDsoff(); } + +#endif \ No newline at end of file diff --git a/armsrc/i2c.h b/armsrc/i2c.h index 4c5c5228..a10ac74f 100644 --- a/armsrc/i2c.h +++ b/armsrc/i2c.h @@ -11,12 +11,8 @@ #ifndef __I2C_H #define __I2C_H -#include -#include "proxmark3.h" -#include "apps.h" -#include "util.h" -#include "BigBuf.h" -#include "smartcard.h" +#include +#include #define I2C_DEVICE_ADDRESS_BOOT 0xB0 #define I2C_DEVICE_ADDRESS_MAIN 0xC0 @@ -28,31 +24,14 @@ #define I2C_DEVICE_CMD_SIM_CLC 0x05 #define I2C_DEVICE_CMD_GETVERSION 0x06 +bool I2C_is_available(void); -void I2C_init(void); -void I2C_Reset(void); -void I2C_SetResetStatus(uint8_t LineRST, uint8_t LineSCK, uint8_t LineSDA); - -void I2C_Reset_EnterMainProgram(void); -void I2C_Reset_EnterBootloader(void); - -bool I2C_WriteCmd(uint8_t device_cmd, uint8_t device_address); - -bool I2C_WriteByte(uint8_t data, uint8_t device_cmd, uint8_t device_address); -bool I2C_BufferWrite(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address); -uint8_t I2C_BufferRead(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address); - -// for firmware -uint8_t I2C_ReadFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address); -bool I2C_WriteFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address); - -bool GetATR(smart_card_atr_t *card_ptr); - -// generic functions +#ifdef WITH_SMARTCARD void SmartCardAtr(void); void SmartCardRaw(uint64_t arg0, uint64_t arg1, uint8_t *data); void SmartCardUpgrade(uint64_t arg0); -//void SmartCardSetBaud(uint64_t arg0); void SmartCardSetClock(uint64_t arg0); void I2C_print_status(void); #endif + +#endif // __I2C_H diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 0cacaed9..059db71e 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1405,7 +1405,7 @@ int EmGetCmd(uint8_t *received, uint16_t *len, uint8_t *parity) ADC_MODE_PRESCALE(63) | ADC_MODE_STARTUP_TIME(1) | ADC_MODE_SAMPLE_HOLD_TIME(15); - AT91C_BASE_ADC->ADC_CHER = ADC_CHANNEL(ADC_CHAN_HF); + AT91C_BASE_ADC->ADC_CHER = ADC_CHANNEL(ADC_CHAN_HF_LOW); // start ADC AT91C_BASE_ADC->ADC_CR = AT91C_ADC_START; @@ -1432,12 +1432,12 @@ int EmGetCmd(uint8_t *received, uint16_t *len, uint8_t *parity) if (BUTTON_PRESS()) return 1; // test if the field exists - if (AT91C_BASE_ADC->ADC_SR & ADC_END_OF_CONVERSION(ADC_CHAN_HF)) { + if (AT91C_BASE_ADC->ADC_SR & ADC_END_OF_CONVERSION(ADC_CHAN_HF_LOW)) { analogCnt++; - analogAVG += AT91C_BASE_ADC->ADC_CDR[ADC_CHAN_HF]; + analogAVG += AT91C_BASE_ADC->ADC_CDR[ADC_CHAN_HF_LOW]; AT91C_BASE_ADC->ADC_CR = AT91C_ADC_START; if (analogCnt >= 32) { - if ((MAX_ADC_HF_VOLTAGE * (analogAVG / analogCnt) >> 10) < MF_MINFIELDV) { + if ((MAX_ADC_HF_VOLTAGE_LOW * (analogAVG / analogCnt) >> 10) < MF_MINFIELDV) { vtime = GetTickCount(); if (!timer) timer = vtime; // 50ms no field --> card to idle state diff --git a/armsrc/mifaresim.c b/armsrc/mifaresim.c index 1fdf99d6..c9264836 100644 --- a/armsrc/mifaresim.c +++ b/armsrc/mifaresim.c @@ -347,7 +347,7 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t * // find reader field if (cardSTATE == MFEMUL_NOFIELD) { - int vHf = (MAX_ADC_HF_VOLTAGE * AvgAdc(ADC_CHAN_HF)) >> 10; + int vHf = (MAX_ADC_HF_VOLTAGE_LOW * AvgAdc(ADC_CHAN_HF_LOW)) >> 10; if (vHf > MF_MINFIELDV) { LED_A_ON(); cardSTATE_TO_IDLE(); diff --git a/include/proxmark3.h b/include/proxmark3.h index 6fb6624f..fec7415a 100644 --- a/include/proxmark3.h +++ b/include/proxmark3.h @@ -16,70 +16,71 @@ #include "config_gpio.h" #include "usb_cmd.h" -#define WDT_HIT() AT91C_BASE_WDTC->WDTC_WDCR = 0xa5000001 +#define WDT_HIT() AT91C_BASE_WDTC->WDTC_WDCR = 0xa5000001 -#define PWM_CH_MODE_PRESCALER(x) ((x)<<0) -#define PWM_CHANNEL(x) (1<<(x)) +#define PWM_CH_MODE_PRESCALER(x) ((x)<<0) +#define PWM_CHANNEL(x) (1<<(x)) -#define ADC_CHAN_LF 4 -#define ADC_CHAN_HF 5 -#define ADC_MODE_PRESCALE(x) ((x)<<8) -#define ADC_MODE_STARTUP_TIME(x) ((x)<<16) -#define ADC_MODE_SAMPLE_HOLD_TIME(x) ((x)<<24) -#define ADC_CHANNEL(x) (1<<(x)) -#define ADC_END_OF_CONVERSION(x) (1<<(x)) +#define ADC_CHAN_LF 4 +#define ADC_CHAN_HF_LOW 5 +#define ADC_CHAN_HF_HIGH 7 +#define ADC_MODE_PRESCALE(x) ((x)<<8) +#define ADC_MODE_STARTUP_TIME(x) ((x)<<16) +#define ADC_MODE_SAMPLE_HOLD_TIME(x) ((x)<<24) +#define ADC_CHANNEL(x) (1<<(x)) +#define ADC_END_OF_CONVERSION(x) (1<<(x)) -#define SSC_CLOCK_MODE_START(x) ((x)<<8) -#define SSC_FRAME_MODE_WORDS_PER_TRANSFER(x) ((x)<<8) -#define SSC_CLOCK_MODE_SELECT(x) ((x)<<0) -#define SSC_FRAME_MODE_BITS_IN_WORD(x) (((x)-1)<<0) +#define SSC_CLOCK_MODE_START(x) ((x)<<8) +#define SSC_FRAME_MODE_WORDS_PER_TRANSFER(x) ((x)<<8) +#define SSC_CLOCK_MODE_SELECT(x) ((x)<<0) +#define SSC_FRAME_MODE_BITS_IN_WORD(x) (((x)-1)<<0) -#define MC_FLASH_COMMAND_KEY ((0x5a)<<24) -#define MC_FLASH_MODE_FLASH_WAIT_STATES(x) ((x)<<8) -#define MC_FLASH_MODE_MASTER_CLK_IN_MHZ(x) (((x)+((x)/2))<<16) -#define MC_FLASH_COMMAND_PAGEN(x) ((x)<<8) +#define MC_FLASH_COMMAND_KEY ((0x5a)<<24) +#define MC_FLASH_MODE_FLASH_WAIT_STATES(x) ((x)<<8) +#define MC_FLASH_MODE_MASTER_CLK_IN_MHZ(x) (((x)+((x)/2))<<16) +#define MC_FLASH_COMMAND_PAGEN(x) ((x)<<8) -#define RST_CONTROL_KEY (0xa5<<24) +#define RST_CONTROL_KEY (0xa5<<24) -#define PMC_MAIN_OSC_STARTUP_DELAY(x) ((x)<<8) -#define PMC_PLL_DIVISOR(x) (x) -#define PMC_PLL_MULTIPLIER(x) (((x)-1)<<16) -#define PMC_PLL_COUNT_BEFORE_LOCK(x) ((x)<<8) -#define PMC_PLL_FREQUENCY_RANGE(x) ((x)<<14) -#define PMC_PLL_USB_DIVISOR(x) ((x)<<28) +#define PMC_MAIN_OSC_STARTUP_DELAY(x) ((x)<<8) +#define PMC_PLL_DIVISOR(x) (x) +#define PMC_PLL_MULTIPLIER(x) (((x)-1)<<16) +#define PMC_PLL_COUNT_BEFORE_LOCK(x) ((x)<<8) +#define PMC_PLL_FREQUENCY_RANGE(x) ((x)<<14) +#define PMC_PLL_USB_DIVISOR(x) ((x)<<28) -#define UDP_INTERRUPT_ENDPOINT(x) (1<<(x)) -#define UDP_CSR_BYTES_RECEIVED(x) (((x) >> 16) & 0x7ff) +#define UDP_INTERRUPT_ENDPOINT(x) (1<<(x)) +#define UDP_CSR_BYTES_RECEIVED(x) (((x) >> 16) & 0x7ff) //************************************************************** -#define LOW(x) AT91C_BASE_PIOA->PIO_CODR = (x) -#define HIGH(x) AT91C_BASE_PIOA->PIO_SODR = (x) -#define GETBIT(x) (AT91C_BASE_PIOA->PIO_ODSR & (x)) ? 1:0 -#define SETBIT(x, y) (y) ? (HIGH(x)):(LOW(x)) -#define INVBIT(x) SETBIT((x), !(GETBIT(x))) +#define LOW(x) AT91C_BASE_PIOA->PIO_CODR = (x) +#define HIGH(x) AT91C_BASE_PIOA->PIO_SODR = (x) +#define GETBIT(x) (AT91C_BASE_PIOA->PIO_ODSR & (x)) ? 1:0 +#define SETBIT(x, y) (y) ? (HIGH(x)):(LOW(x)) +#define INVBIT(x) SETBIT((x), !(GETBIT(x))) -#define SPI_FPGA_MODE 0 -#define SPI_LCD_MODE 1 +#define SPI_FPGA_MODE 0 +#define SPI_LCD_MODE 1 //#define PACKED __attribute__((__packed__)) -#define LED_A_ON() HIGH(GPIO_LED_A) -#define LED_A_OFF() LOW(GPIO_LED_A) -#define LED_A_INV() INVBIT(GPIO_LED_A) -#define LED_B_ON() HIGH(GPIO_LED_B) -#define LED_B_OFF() LOW(GPIO_LED_B) -#define LED_B_INV() INVBIT(GPIO_LED_B) -#define LED_C_ON() HIGH(GPIO_LED_C) -#define LED_C_OFF() LOW(GPIO_LED_C) -#define LED_C_INV() INVBIT(GPIO_LED_C) -#define LED_D_ON() HIGH(GPIO_LED_D) -#define LED_D_OFF() LOW(GPIO_LED_D) -#define LED_D_INV() INVBIT(GPIO_LED_D) -#define RELAY_ON() HIGH(GPIO_RELAY) -#define RELAY_OFF() LOW(GPIO_RELAY) -#define BUTTON_PRESS() !(AT91C_BASE_PIOA->PIO_PDSR & GPIO_BUTTON) +#define LED_A_ON() HIGH(GPIO_LED_A) +#define LED_A_OFF() LOW(GPIO_LED_A) +#define LED_A_INV() INVBIT(GPIO_LED_A) +#define LED_B_ON() HIGH(GPIO_LED_B) +#define LED_B_OFF() LOW(GPIO_LED_B) +#define LED_B_INV() INVBIT(GPIO_LED_B) +#define LED_C_ON() HIGH(GPIO_LED_C) +#define LED_C_OFF() LOW(GPIO_LED_C) +#define LED_C_INV() INVBIT(GPIO_LED_C) +#define LED_D_ON() HIGH(GPIO_LED_D) +#define LED_D_OFF() LOW(GPIO_LED_D) +#define LED_D_INV() INVBIT(GPIO_LED_D) +#define RELAY_ON() HIGH(GPIO_RELAY) +#define RELAY_OFF() LOW(GPIO_RELAY) +#define BUTTON_PRESS() !(AT91C_BASE_PIOA->PIO_PDSR & GPIO_BUTTON) -#define VERSION_INFORMATION_MAGIC 0x56334d50 +#define VERSION_INFORMATION_MAGIC 0x56334d50 struct version_information { int magic; /* Magic sequence to identify this as a correct version information structure. Must be VERSION_INFORMATION_MAGIC */ char versionversion; /* Must be 1 */ @@ -89,9 +90,9 @@ struct version_information { char buildtime[30]; /* string with the build time */ } __attribute__((packed)); -#define COMMON_AREA_MAGIC 0x43334d50 -#define COMMON_AREA_COMMAND_NONE 0 -#define COMMON_AREA_COMMAND_ENTER_FLASH_MODE 1 +#define COMMON_AREA_MAGIC 0x43334d50 +#define COMMON_AREA_COMMAND_NONE 0 +#define COMMON_AREA_COMMAND_ENTER_FLASH_MODE 1 struct common_area { int magic; /* Magic sequence, to distinguish against random uninitialized memory */ char version; /* Must be 1 */ From a7e1b46d5119220dba8481a782b70e35cf124afc Mon Sep 17 00:00:00 2001 From: Fl0-0 Date: Sat, 22 Sep 2018 17:51:13 +0200 Subject: [PATCH 284/310] Jansson gcc8 fix (#679) * Fix error in error.c * Fix error in load.c --- client/jansson/error.c | 4 ++-- client/jansson/load.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client/jansson/error.c b/client/jansson/error.c index cbd50d7e..b94b3a3a 100644 --- a/client/jansson/error.c +++ b/client/jansson/error.c @@ -25,10 +25,10 @@ void jsonp_error_set_source(json_error_t *error, const char *source) length = strlen(source); if(length < JSON_ERROR_SOURCE_LENGTH) - strncpy(error->source, source, length + 1); + strncpy(error->source, source, JSON_ERROR_SOURCE_LENGTH); else { size_t extra = length - JSON_ERROR_SOURCE_LENGTH + 4; - strncpy(error->source, "...", 3); + memcpy(error->source, "...", 3); strncpy(error->source + 3, source + extra, length - extra + 1); } } diff --git a/client/jansson/load.c b/client/jansson/load.c index deb36f32..4cf3855a 100644 --- a/client/jansson/load.c +++ b/client/jansson/load.c @@ -89,7 +89,7 @@ static void error_set(json_error_t *error, const lex_t *lex, { va_list ap; char msg_text[JSON_ERROR_TEXT_LENGTH]; - char msg_with_context[JSON_ERROR_TEXT_LENGTH]; + char msg_with_context[JSON_ERROR_TEXT_LENGTH + 28]; int line = -1, col = -1; size_t pos = 0; @@ -114,7 +114,7 @@ static void error_set(json_error_t *error, const lex_t *lex, if(saved_text && saved_text[0]) { if(lex->saved_text.length <= 20) { - snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH, + snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH + 28, "%s near '%s'", msg_text, saved_text); msg_with_context[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; result = msg_with_context; @@ -131,7 +131,7 @@ static void error_set(json_error_t *error, const lex_t *lex, result = msg_text; } else { - snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH, + snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH + 17, "%s near end of file", msg_text); msg_with_context[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; result = msg_with_context; From 2d4db38754e059eeb9af601f0f02c5608792af77 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Mon, 24 Sep 2018 13:48:07 +0300 Subject: [PATCH 285/310] FIX: APDUCodeTable has more items than 100 (#682) https://github.com/RfidResearchGroup/proxmark3/commit/4fa0835d5f7823b9b850be7c7162a98f9a3973a2 --- client/emv/apduinfo.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/client/emv/apduinfo.c b/client/emv/apduinfo.c index a631c614..d14e3ff6 100644 --- a/client/emv/apduinfo.c +++ b/client/emv/apduinfo.c @@ -255,7 +255,7 @@ const APDUCode APDUCodeTable[] = { {"9F00", APDUCODE_TYPE_NONE, "PIN blocked and Unblock Try Counter is 3"}, {"9F04", APDUCODE_TYPE_NONE, "PIN not succesfully verified, PIN blocked and Unblock Try Counter is 3"}, {"9FXX", APDUCODE_TYPE_NONE, "Command successfully executed; 'xx' bytes of data are available and can be requested using GET RESPONSE."}, - {"9xXX", APDUCODE_TYPE_NONE, "Application related status, (ISO 7816-3)"} + {"9XXX", APDUCODE_TYPE_NONE, "Application related status, (ISO 7816-3)"} }; const size_t APDUCodeTableLen = sizeof(APDUCodeTable)/sizeof(APDUCode); @@ -280,11 +280,10 @@ int CodeCmp(const char *code1, const char *code2) { const APDUCode* const GetAPDUCode(uint8_t sw1, uint8_t sw2) { char buf[5] = {0}; int res; - int mineq = 100; + int mineq = APDUCodeTableLen; int mineqindx = 0; - sprintf(&buf[0], "%02X", sw1); - sprintf(&buf[2], "%02X", sw2); + sprintf(buf, "%02X%02X", sw1, sw2); for (int i = 0; i < APDUCodeTableLen; i++) { res = CodeCmp(APDUCodeTable[i].ID, buf); @@ -302,7 +301,7 @@ const APDUCode* const GetAPDUCode(uint8_t sw1, uint8_t sw2) { } // if we have not equal, but with some 'X' - if (mineqindx < 100) { + if (mineqindx < APDUCodeTableLen) { return &APDUCodeTable[mineqindx]; } From c13afca1f5278a4aa9bad0bcbfc4f82248fc8e4d Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Mon, 24 Sep 2018 13:03:13 +0200 Subject: [PATCH 286/310] Fix: accidentially deleted line in fpgaloader.c --- armsrc/fpgaloader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/armsrc/fpgaloader.c b/armsrc/fpgaloader.c index a132b762..45294d60 100644 --- a/armsrc/fpgaloader.c +++ b/armsrc/fpgaloader.c @@ -167,7 +167,7 @@ bool FpgaSetupSscDma(uint8_t *buf, uint16_t sample_count) AT91C_BASE_PDC_SSC->PDC_RCR = sample_count; // transfer this many samples AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) buf; // next transfer to same memory address AT91C_BASE_PDC_SSC->PDC_RNCR = sample_count; // ... with same number of samples AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTEN; // go! - + AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTEN; // go! return true; } From f23565c38b5a77ec3f5b02fc9f8368b77730b3ab Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Fri, 28 Sep 2018 19:03:21 +0300 Subject: [PATCH 287/310] Added to `hf emv` commands: `gpo`, `readrec`, `genac`, `challenge`, `intauth` (#671) * move `hf emv search` to argtable * `hf emv select` * delete old help * `hf emv pse` and sketch for the other commands * `hf emv gpo` * `hf emv readrec` * `hf emv challenge` works * added `intauth` and `genac` commands. * added CDA transaction to `hf emv genac` * add terminal decision to `genac` * added changelog --- CHANGELOG.md | 2 + client/cliparser/cliparser.c | 4 + client/cliparser/cliparser.h | 1 + client/emv/cmdemv.c | 745 +++++++++++++++++++++-------------- 4 files changed, 455 insertions(+), 297 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c9f29d9..73306b1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac ### Fixed - Changed start sequence in Qt mode (fix: short commands hangs main Qt thread) (Merlok) - Changed driver file proxmark3.inf to support both old and new Product/Vendor IDs (piwi) +- Changed all command line parsers in `hf emv` commands to argtable (Merlok) ### Added - Added `sc` smartcard (contact card) commands - reader, info, raw, upgrade, setclock, list (hardware version RDV4.0 only) must turn option on in makefile options (Willok, Iceman, marshmellow) @@ -55,6 +56,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added to `hf emv exec` SDA, DDA, fast DDA, CDA calculations for VISA and Mastercard and some other compatible EMV cards (Merlok) - Added `hf emv test` - crypto tests for DES, AES, SHA, RSA, SDA, DDA, CDA and some other crypto functions (Merlok) - Added `hf list mf` - deciphers crypto1 stream and works with first authentication and weak nested authentications (Merlok) +- Added to `hf emv` commands: `gpo`, `readrec`, `genac`, `challenge`, `intauth` - commands working with EMV cards (Merlok) - Added `lf hid encode` and `lf hid decode` commands to translate printed HID card data to and from the packed data transmitted by a prox tag (grauerfuchs) - Added `lf hid write` command, which operates as a macro for encode followed by clone operations (grauerfuchs) diff --git a/client/cliparser/cliparser.c b/client/cliparser/cliparser.c index 0e70a630..87427db0 100644 --- a/client/cliparser/cliparser.c +++ b/client/cliparser/cliparser.c @@ -148,6 +148,10 @@ void CLIParserFree() { // convertors int CLIParamHexToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen) { + *datalen = 0; + if (!strlen(argstr->sval[0])) + return 0; + switch(param_gethex_to_eol(argstr->sval[0], 0, data, maxdatalen, datalen)) { case 1: printf("Parameter error: Invalid HEX value.\n"); diff --git a/client/cliparser/cliparser.h b/client/cliparser/cliparser.h index 3f4fa4cc..169f102e 100644 --- a/client/cliparser/cliparser.h +++ b/client/cliparser/cliparser.h @@ -19,6 +19,7 @@ #define arg_get_lit(n)(((struct arg_lit*)argtable[n])->count) #define arg_get_int(n)(((struct arg_int*)argtable[n])->ival[0]) #define arg_get_str(n)((struct arg_str*)argtable[n]) +#define arg_get_str_len(n)(strlen(((struct arg_str*)argtable[n])->sval[0])) #define CLIExecWithReturn(cmd, atbl, ifempty) if (CLIParserParseString(cmd, atbl, arg_getsize(atbl), ifempty)){CLIParserFree();return 0;} #define CLIGetStrBLessWithReturn(paramnum, data, datalen, delta) if (CLIParamHexToBuf(arg_get_str(paramnum), data, sizeof(data) - (delta), datalen)) {CLIParserFree();return 1;} diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index 541f505a..93635f85 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// Copyright (C) 2017 Merlok +// Copyright (C) 2017, 2018 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 @@ -11,86 +11,57 @@ #include #include "cmdemv.h" #include "test/cryptotest.h" +#include "cliparser/cliparser.h" #include -int UsageCmdHFEMVSelect(void) { - PrintAndLog("HELP : Executes select applet command:\n"); - PrintAndLog("Usage: hf emv select [-s][-k][-a][-t] \n"); - PrintAndLog(" Options:"); - PrintAndLog(" -s : select card"); - PrintAndLog(" -k : keep field for next command"); - PrintAndLog(" -a : show APDU reqests and responses\n"); - PrintAndLog(" -t : TLV decode results\n"); - PrintAndLog("Samples:"); - PrintAndLog(" hf emv select -s a00000000101 -> select card, select applet"); - PrintAndLog(" hf emv select -s -t a00000000101 -> select card, select applet, show result in TLV"); - return 0; +#define TLV_ADD(tag, value)( tlvdb_change_or_add_node(tlvRoot, tag, sizeof(value) - 1, (const unsigned char *)value) ) +void ParamLoadDefaults(struct tlvdb *tlvRoot) { + //9F02:(Amount, authorized (Numeric)) len:6 + TLV_ADD(0x9F02, "\x00\x00\x00\x00\x01\x00"); + //9F1A:(Terminal Country Code) len:2 + TLV_ADD(0x9F1A, "ru"); + //5F2A:(Transaction Currency Code) len:2 + // USD 840, EUR 978, RUR 810, RUB 643, RUR 810(old), UAH 980, AZN 031, n/a 999 + TLV_ADD(0x5F2A, "\x09\x80"); + //9A:(Transaction Date) len:3 + TLV_ADD(0x9A, "\x00\x00\x00"); + //9C:(Transaction Type) len:1 | 00 => Goods and service #01 => Cash + TLV_ADD(0x9C, "\x00"); + // 9F37 Unpredictable Number len:4 + TLV_ADD(0x9F37, "\x01\x02\x03\x04"); + // 9F6A Unpredictable Number (MSD for UDOL) len:4 + TLV_ADD(0x9F6A, "\x01\x02\x03\x04"); + //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 + TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC } int CmdHFEMVSelect(const char *cmd) { uint8_t data[APDU_AID_LEN] = {0}; int datalen = 0; - bool activateField = false; - bool leaveSignalON = false; - bool decodeTLV = false; - if (strlen(cmd) < 1) { - UsageCmdHFEMVSelect(); - return 0; - } + CLIParserInit("hf emv select", + "Executes select applet command", + "Usage:\n\thf emv select -s a00000000101 -> select card, select applet\n\thf emv select -st a00000000101 -> select card, select applet, show result in TLV\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("sS", "select", "activate field and select card"), + arg_lit0("kK", "keep", "keep field for next command"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results"), + arg_str0(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); - SetAPDULogging(false); + bool activateField = arg_get_lit(1); + bool leaveSignalON = arg_get_lit(2); + bool APDULogging = arg_get_lit(3); + bool decodeTLV = arg_get_lit(4); + CLIGetStrWithReturn(5, data, &datalen); + CLIParserFree(); - int cmdp = 0; - while(param_getchar(cmd, cmdp) != 0x00) { - char c = param_getchar(cmd, cmdp); - if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) - switch (param_getchar_indx(cmd, 1, cmdp)) { - case 'h': - case 'H': - UsageCmdHFEMVSelect(); - return 0; - case 's': - case 'S': - activateField = true; - break; - case 'k': - case 'K': - leaveSignalON = true; - break; - case 'a': - case 'A': - SetAPDULogging(true); - break; - case 't': - case 'T': - decodeTLV = true; - break; - default: - PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); - return 1; - } - - if (isxdigit((unsigned char)c)) { - switch(param_gethex_to_eol(cmd, cmdp, data, sizeof(data), &datalen)) { - case 1: - PrintAndLog("Invalid HEX value."); - return 1; - case 2: - PrintAndLog("AID too large."); - return 1; - case 3: - PrintAndLog("Hex must have even number of digits."); - return 1; - } - - // we get all the hex to end of line with spaces - break; - } - - - cmdp++; - } + SetAPDULogging(APDULogging); // exec uint8_t buf[APDU_RES_LEN] = {0}; @@ -110,65 +81,30 @@ int CmdHFEMVSelect(const char *cmd) { return 0; } -int UsageCmdHFEMVSearch(void) { - PrintAndLog("HELP : Tries to select all applets from applet list:\n"); - PrintAndLog("Usage: hf emv search [-s][-k][-a][-t]\n"); - PrintAndLog(" Options:"); - PrintAndLog(" -s : select card"); - PrintAndLog(" -k : keep field for next command"); - PrintAndLog(" -a : show APDU reqests and responses\n"); - PrintAndLog(" -t : TLV decode results of selected applets\n"); - PrintAndLog("Samples:"); - PrintAndLog(" hf emv search -s -> select card and search"); - PrintAndLog(" hf emv search -s -t -> select card, search and show result in TLV"); - return 0; -} - int CmdHFEMVSearch(const char *cmd) { - bool activateField = false; - bool leaveSignalON = false; - bool decodeTLV = false; + CLIParserInit("hf emv search", + "Tries to select all applets from applet list:\n", + "Usage:\n\thf emv search -s -> select card and search\n\thf emv search -st -> select card, search and show result in TLV\n"); - if (strlen(cmd) < 1) { - UsageCmdHFEMVSearch(); - return 0; - } + void* argtable[] = { + arg_param_begin, + arg_lit0("sS", "select", "activate field and select card"), + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results of selected applets"), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); - SetAPDULogging(false); + bool activateField = arg_get_lit(1); + bool leaveSignalON = arg_get_lit(2); + bool APDULogging = arg_get_lit(3); + bool decodeTLV = arg_get_lit(4); + CLIParserFree(); + + SetAPDULogging(APDULogging); - int cmdp = 0; - while(param_getchar(cmd, cmdp) != 0x00) { - char c = param_getchar(cmd, cmdp); - if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) - switch (param_getchar_indx(cmd, 1, cmdp)) { - case 'h': - case 'H': - UsageCmdHFEMVSearch(); - return 0; - case 's': - case 'S': - activateField = true; - break; - case 'k': - case 'K': - leaveSignalON = true; - break; - case 'a': - case 'A': - SetAPDULogging(true); - break; - case 't': - case 'T': - decodeTLV = true; - break; - default: - PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); - return 1; - } - cmdp++; - } - struct tlvdb *t = NULL; const char *al = "Applets list"; t = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); @@ -190,74 +126,36 @@ int CmdHFEMVSearch(const char *cmd) { return 0; } -int UsageCmdHFEMVPPSE(void) { - PrintAndLog("HELP : Executes PSE/PPSE select command. It returns list of applet on the card:\n"); - PrintAndLog("Usage: hf emv pse [-s][-k][-1][-2][-a][-t]\n"); - PrintAndLog(" Options:"); - PrintAndLog(" -s : select card"); - PrintAndLog(" -k : keep field for next command"); - PrintAndLog(" -1 : ppse (1PAY.SYS.DDF01)"); - PrintAndLog(" -2 : pse (2PAY.SYS.DDF01)"); - PrintAndLog(" -a : show APDU reqests and responses\n"); - PrintAndLog(" -t : TLV decode results\n"); - PrintAndLog("Samples:"); - PrintAndLog(" hf emv pse -s -1 -> select, get pse"); - PrintAndLog(" hf emv pse -s -k -2 -> select, get ppse, keep field"); - PrintAndLog(" hf emv pse -s -t -2 -> select, get ppse, show result in TLV"); - return 0; -} - int CmdHFEMVPPSE(const char *cmd) { - uint8_t PSENum = 2; - bool activateField = false; - bool leaveSignalON = false; - bool decodeTLV = false; + CLIParserInit("hf emv pse", + "Executes PSE/PPSE select command. It returns list of applet on the card:\n", + "Usage:\n\thf emv pse -s1 -> select, get pse\n\thf emv pse -st2 -> select, get ppse, show result in TLV\n"); - if (strlen(cmd) < 1) { - UsageCmdHFEMVPPSE(); - return 0; - } - - SetAPDULogging(false); + void* argtable[] = { + arg_param_begin, + arg_lit0("sS", "select", "activate field and select card"), + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("1", "pse", "pse (1PAY.SYS.DDF01) mode"), + arg_lit0("2", "ppse", "ppse (2PAY.SYS.DDF01) mode (default mode)"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results of selected applets"), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); - int cmdp = 0; - while(param_getchar(cmd, cmdp) != 0x00) { - char c = param_getchar(cmd, cmdp); - if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) - switch (param_getchar_indx(cmd, 1, cmdp)) { - case 'h': - case 'H': - UsageCmdHFEMVPPSE(); - return 0; - case 's': - case 'S': - activateField = true; - break; - case 'k': - case 'K': - leaveSignalON = true; - break; - case 'a': - case 'A': - SetAPDULogging(true); - break; - case 't': - case 'T': - decodeTLV = true; - break; - case '1': - PSENum = 1; - break; - case '2': - PSENum = 2; - break; - default: - PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); - return 1; - } - cmdp++; - } + bool activateField = arg_get_lit(1); + bool leaveSignalON = arg_get_lit(2); + uint8_t PSENum = 2; + if (arg_get_lit(3)) + PSENum = 1; + if (arg_get_lit(4)) + PSENum = 2; + bool APDULogging = arg_get_lit(5); + bool decodeTLV = arg_get_lit(6); + CLIParserFree(); + + SetAPDULogging(APDULogging); // exec uint8_t buf[APDU_RES_LEN] = {0}; @@ -278,27 +176,321 @@ int CmdHFEMVPPSE(const char *cmd) { return 0; } -int UsageCmdHFEMVExec(void) { - PrintAndLog("HELP : Executes EMV contactless transaction:\n"); - PrintAndLog("Usage: hf emv exec [-s][-a][-t][-j][-f][-v][-c][-x][-g]\n"); - PrintAndLog(" Options:"); - PrintAndLog(" -s : select card"); - PrintAndLog(" -a : show APDU reqests and responses\n"); - PrintAndLog(" -t : TLV decode results\n"); - PrintAndLog(" -j : load transaction parameters from `emv/defparams.json` file\n"); - PrintAndLog(" -f : force search AID. Search AID instead of execute PPSE.\n"); - PrintAndLog(" -v : transaction type - qVSDC or M/Chip.\n"); - PrintAndLog(" -c : transaction type - qVSDC or M/Chip plus CDA (SDAD generation).\n"); - PrintAndLog(" -x : transaction type - VSDC. For test only. Not a standart behavior.\n"); - PrintAndLog(" -g : VISA. generate AC from GPO\n"); - PrintAndLog("By default : transaction type - MSD.\n"); - PrintAndLog("Samples:"); - PrintAndLog(" hf emv exec -s -a -t -> execute MSD transaction"); - PrintAndLog(" hf emv exec -s -a -t -c -> execute CDA transaction"); +int CmdHFEMVGPO(const char *cmd) { + uint8_t data[APDU_RES_LEN] = {0}; + int datalen = 0; + + CLIParserInit("hf emv gpo", + "Executes Get Processing Options command. It returns data in TLV format (0x77 - format2) or plain format (0x80 - format1).\nNeeds a EMV applet to be selected.", + "Usage:\n\thf emv gpo -k -> execute GPO\n" + "\thf emv gpo -st 01020304 -> execute GPO with 4-byte PDOL data, show result in TLV\n"); + // here need to add load params from file and gen pdol + + void* argtable[] = { + arg_param_begin, + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("pP", "params", "load parameters for PDOL making from `emv/defparams.json` file (by default uses default parameters) (NOT WORK!!!)"), + arg_lit0("mM", "make", "make PDOLdata from PDOL (tag 9F38) and parameters (NOT WORK!!!)"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results of selected applets"), + arg_str0(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool leaveSignalON = arg_get_lit(1); + bool paramsLoadFromFile = arg_get_lit(2); + bool dataMakeFromPDOL = arg_get_lit(3); + bool APDULogging = arg_get_lit(4); + bool decodeTLV = arg_get_lit(5); + CLIGetStrWithReturn(6, data, &datalen); + CLIParserFree(); + + SetAPDULogging(APDULogging); + + // Init TLV tree + const char *alr = "Root terminal TLV tree"; + struct tlvdb *tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr); + + // calc PDOL + struct tlv *pdol_data_tlv = NULL; + struct tlv data_tlv = { + .tag = 0x01, + .len = datalen, + .value = (uint8_t *)data, + }; + if (dataMakeFromPDOL) { + // TODO + PrintAndLog("Make PDOL data not implemented!"); + + ParamLoadDefaults(tlvRoot); + + if (paramsLoadFromFile) { + }; +/* pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83); + if (!pdol_data_tlv){ + PrintAndLog("ERROR: can't create PDOL TLV."); + tlvdb_free(tlvRoot); + return 4; + }*/ + return 0; + } else { + pdol_data_tlv = &data_tlv; + } + + size_t pdol_data_tlv_data_len = 0; + unsigned char *pdol_data_tlv_data = tlv_encode(pdol_data_tlv, &pdol_data_tlv_data_len); + if (!pdol_data_tlv_data) { + PrintAndLog("ERROR: can't create PDOL data."); + tlvdb_free(tlvRoot); + return 4; + } + PrintAndLog("PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); + + // exec + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + int res = EMVGPO(leaveSignalON, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); + + free(pdol_data_tlv_data); + tlvdb_free(tlvRoot); + + if (sw) + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + + if (res) + return res; + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + return 0; } -#define TLV_ADD(tag, value)( tlvdb_change_or_add_node(tlvRoot, tag, sizeof(value) - 1, (const unsigned char *)value) ) +int CmdHFEMVReadRecord(const char *cmd) { + uint8_t data[APDU_RES_LEN] = {0}; + int datalen = 0; + + CLIParserInit("hf emv readrec", + "Executes Read Record command. It returns data in TLV format.\nNeeds a bank applet to be selected and sometimes needs GPO to be executed.", + "Usage:\n\thf emv readrec -k 0101 -> read file SFI=01, SFIrec=01\n\thf emv readrec -kt 0201-> read file 0201 and show result in TLV\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results of selected applets"), + arg_str1(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool leaveSignalON = arg_get_lit(1); + bool APDULogging = arg_get_lit(2); + bool decodeTLV = arg_get_lit(3); + CLIGetStrWithReturn(4, data, &datalen); + CLIParserFree(); + + if (datalen != 2) { + PrintAndLog("ERROR: Command needs to have 2 bytes of data"); + return 1; + } + + SetAPDULogging(APDULogging); + + // exec + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + int res = EMVReadRecord(leaveSignalON, data[0], data[1], buf, sizeof(buf), &len, &sw, NULL); + + if (sw) + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + + if (res) + return res; + + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + return 0; +} + +int CmdHFEMVAC(const char *cmd) { + uint8_t data[APDU_RES_LEN] = {0}; + int datalen = 0; + + CLIParserInit("hf emv genac", + "Generate Application Cryptogram command. It returns data in TLV format .\nNeeds a EMV applet to be selected and GPO to be executed.", + "Usage:\n\thf emv genac -k 0102 -> generate AC with 2-byte CDOLdata and keep field ON after command\n" + "\thf emv genac -t 01020304 -> generate AC with 4-byte CDOL data, show result in TLV\n" + "\thf emv genac -Daac 01020304 -> generate AC with 4-byte CDOL data and terminal decision 'declined'\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("cC", "cda", "executes CDA transaction. Needs to get SDAD in results."), + arg_str0("dD", "decision", "", "Terminal decision. aac - declined, tc - approved, arqc - online authorisation requested"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results of selected applets"), + arg_str1(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, false); + + bool leaveSignalON = arg_get_lit(1); + bool trTypeCDA = arg_get_lit(2); + uint8_t termDecision = 0xff; + if (arg_get_str_len(3)) { + if (!strncmp(arg_get_str(3)->sval[0], "aac", 4)) + termDecision = EMVAC_AAC; + if (!strncmp(arg_get_str(3)->sval[0], "tc", 4)) + termDecision = EMVAC_TC; + if (!strncmp(arg_get_str(3)->sval[0], "arqc", 4)) + termDecision = EMVAC_ARQC; + + if (termDecision == 0xff) { + PrintAndLog("ERROR: can't find terminal decision '%s'", arg_get_str(3)->sval[0]); + return 1; + } + } else { + termDecision = EMVAC_TC; + } + if (trTypeCDA) + termDecision = termDecision | EMVAC_CDAREQ; + bool APDULogging = arg_get_lit(4); + bool decodeTLV = arg_get_lit(5); + CLIGetStrWithReturn(6, data, &datalen); + CLIParserFree(); + + SetAPDULogging(APDULogging); + + // Init TLV tree + const char *alr = "Root terminal TLV tree"; + struct tlvdb *tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr); + + // calc CDOL + struct tlv *cdol_data_tlv = NULL; +// struct tlv *cdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x8c, NULL), tlvRoot, 0x01); // 0x01 - dummy tag + struct tlv data_tlv = { + .tag = 0x01, + .len = datalen, + .value = (uint8_t *)data, + }; + cdol_data_tlv = &data_tlv; + PrintAndLog("CDOL data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len)); + + // exec + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + int res = EMVAC(leaveSignalON, termDecision, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); + +// free(cdol_data_tlv); + tlvdb_free(tlvRoot); + + if (sw) + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + + if (res) + return res; + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + return 0; +} + +int CmdHFEMVGenerateChallenge(const char *cmd) { + + CLIParserInit("hf emv challenge", + "Executes Generate Challenge command. It returns 4 or 8-byte random number from card.\nNeeds a EMV applet to be selected and GPO to be executed.", + "Usage:\n\thf emv challenge -> get challenge\n\thf emv challenge -k -> get challenge, keep fileld ON\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool leaveSignalON = arg_get_lit(1); + bool APDULogging = arg_get_lit(2); + CLIParserFree(); + + SetAPDULogging(APDULogging); + + // exec + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + int res = EMVGenerateChallenge(leaveSignalON, buf, sizeof(buf), &len, &sw, NULL); + + if (sw) + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + + if (res) + return res; + + PrintAndLog("Challenge: %s", sprint_hex(buf, len)); + + if (len != 4 && len != 8) + PrintAndLog("WARNING: length of challenge must be 4 or 8, but it %d", len); + + return 0; +} + +int CmdHFEMVInternalAuthenticate(const char *cmd) { + uint8_t data[APDU_RES_LEN] = {0}; + int datalen = 0; + + CLIParserInit("hf emv intauth", + "Generate Internal Authenticate command. Usually needs 4-byte random number. It returns data in TLV format .\nNeeds a EMV applet to be selected and GPO to be executed.", + "Usage:\n\thf emv intauth -k 01020304 -> execute Internal Authenticate with 4-byte DDOLdata and keep field ON after command\n" + "\thf emv intauth -t 01020304 -> execute Internal Authenticate with 4-byte DDOL data, show result in TLV\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results of selected applets"), + arg_str1(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, false); + + bool leaveSignalON = arg_get_lit(1); + bool APDULogging = arg_get_lit(2); + bool decodeTLV = arg_get_lit(3); + CLIGetStrWithReturn(4, data, &datalen); + CLIParserFree(); + + SetAPDULogging(APDULogging); + + // DDOL + PrintAndLog("DDOL data[%d]: %s", datalen, sprint_hex(data, datalen)); + + // exec + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + int res = EMVInternalAuthenticate(leaveSignalON, data, datalen, buf, sizeof(buf), &len, &sw, NULL); + + if (sw) + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + + if (res) + return res; + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + return 0; +} + #define dreturn(n) {free(pdol_data_tlv);tlvdb_free(tlvSelect);tlvdb_free(tlvRoot);DropField();return n;} bool HexToBuffer(const char *errormsg, const char *hexvalue, uint8_t * buffer, size_t maxbufferlen, size_t *bufferlen) { @@ -431,35 +623,7 @@ bool ParamLoadFromJson(struct tlvdb *tlv) { return true; } -void ParamLoadDefaults(struct tlvdb *tlvRoot) { - //9F02:(Amount, authorized (Numeric)) len:6 - TLV_ADD(0x9F02, "\x00\x00\x00\x00\x01\x00"); - //9F1A:(Terminal Country Code) len:2 - TLV_ADD(0x9F1A, "ru"); - //5F2A:(Transaction Currency Code) len:2 - // USD 840, EUR 978, RUR 810, RUB 643, RUR 810(old), UAH 980, AZN 031, n/a 999 - TLV_ADD(0x5F2A, "\x09\x80"); - //9A:(Transaction Date) len:3 - TLV_ADD(0x9A, "\x00\x00\x00"); - //9C:(Transaction Type) len:1 | 00 => Goods and service #01 => Cash - TLV_ADD(0x9C, "\x00"); - // 9F37 Unpredictable Number len:4 - TLV_ADD(0x9F37, "\x01\x02\x03\x04"); - // 9F6A Unpredictable Number (MSD for UDOL) len:4 - TLV_ADD(0x9F6A, "\x01\x02\x03\x04"); - //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 - TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC -} - int CmdHFEMVExec(const char *cmd) { - bool activateField = false; - bool showAPDU = false; - bool decodeTLV = false; - bool forceSearch = false; - enum TransactionType TrType = TT_MSD; - bool GenACGPO = false; - bool paramLoadJSON = false; - uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; uint16_t sw = 0; @@ -474,63 +638,45 @@ int CmdHFEMVExec(const char *cmd) { struct tlvdb *tlvRoot = NULL; struct tlv *pdol_data_tlv = NULL; - if (strlen(cmd) < 1) { - UsageCmdHFEMVExec(); - return 0; - } - - int cmdp = 0; - while(param_getchar(cmd, cmdp) != 0x00) { - char c = param_getchar(cmd, cmdp); - if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) - switch (param_getchar_indx(cmd, 1, cmdp)) { - case 'h': - case 'H': - UsageCmdHFEMVExec(); - return 0; - case 's': - case 'S': - activateField = true; - break; - case 'a': - case 'A': - showAPDU = true; - break; - case 't': - case 'T': - decodeTLV = true; - break; - case 'f': - case 'F': - forceSearch = true; - break; - case 'x': - case 'X': - TrType = TT_VSDC; - break; - case 'v': - case 'V': - TrType = TT_QVSDCMCHIP; - break; - case 'c': - case 'C': - TrType = TT_CDA; - break; - case 'g': - case 'G': - GenACGPO = true; - break; - case 'j': - case 'J': - paramLoadJSON = true; - break; - default: - PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); - return 1; - } - cmdp++; - } + CLIParserInit("hf emv exec", + "Executes EMV contactless transaction", + "Usage:\n\thf emv exec -sat -> select card, execute MSD transaction, show APDU and TLV\n" + "\thf emv exec -satc -> select card, execute CDA transaction, show APDU and TLV\n"); + void* argtable[] = { + arg_param_begin, + arg_lit0("sS", "select", "activate field and select card."), + arg_lit0("aA", "apdu", "show APDU reqests and responses."), + arg_lit0("tT", "tlv", "TLV decode results."), + arg_lit0("jJ", "jload", "Load transaction parameters from `emv/defparams.json` file."), + arg_lit0("fF", "forceaid", "Force search AID. Search AID instead of execute PPSE."), + arg_rem("By default:", "Transaction type - MSD"), + arg_lit0("vV", "qvsdc", "Transaction type - qVSDC or M/Chip."), + arg_lit0("cC", "qvsdccda", "Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)."), + arg_lit0("xX", "vsdc", "Transaction type - VSDC. For test only. Not a standart behavior."), + arg_lit0("gG", "acgpo", "VISA. generate AC from GPO."), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool activateField = arg_get_lit(1); + bool showAPDU = arg_get_lit(2); + bool decodeTLV = arg_get_lit(3); + bool paramLoadJSON = arg_get_lit(4); + bool forceSearch = arg_get_lit(5); + + enum TransactionType TrType = TT_MSD; + if (arg_get_lit(6)) + TrType = TT_QVSDCMCHIP; + if (arg_get_lit(7)) + TrType = TT_CDA; + if (arg_get_lit(8)) + TrType = TT_VSDC; + + bool GenACGPO = arg_get_lit(9); + CLIParserFree(); + + SetAPDULogging(showAPDU); // init applets list tree const char *al = "Applets list"; @@ -1005,13 +1151,18 @@ int CmdHFEMVTest(const char *cmd) { int CmdHelp(const char *Cmd); static command_t CommandTable[] = { - {"help", CmdHelp, 1, "This help"}, - {"exec", CmdHFEMVExec, 0, "Executes EMV contactless transaction."}, - {"pse", CmdHFEMVPPSE, 0, "Execute PPSE. It selects 2PAY.SYS.DDF01 or 1PAY.SYS.DDF01 directory."}, - {"search", CmdHFEMVSearch, 0, "Try to select all applets from applets list and print installed applets."}, - {"select", CmdHFEMVSelect, 0, "Select applet."}, + {"help", CmdHelp, 1, "This help"}, + {"exec", CmdHFEMVExec, 0, "Executes EMV contactless transaction."}, + {"pse", CmdHFEMVPPSE, 0, "Execute PPSE. It selects 2PAY.SYS.DDF01 or 1PAY.SYS.DDF01 directory."}, + {"search", CmdHFEMVSearch, 0, "Try to select all applets from applets list and print installed applets."}, + {"select", CmdHFEMVSelect, 0, "Select applet."}, + {"gpo", CmdHFEMVGPO, 0, "Execute GetProcessingOptions."}, + {"readrec", CmdHFEMVReadRecord, 0, "Read files from card."}, + {"genac", CmdHFEMVAC, 0, "Generate ApplicationCryptogram."}, + {"challenge", CmdHFEMVGenerateChallenge, 0, "Generate challenge."}, + {"intauth", CmdHFEMVInternalAuthenticate, 0, "Internal authentication."}, // {"scan", CmdHFEMVScan, 0, "Scan EMV card and save it contents to json file for emulator."}, - {"test", CmdHFEMVTest, 0, "Crypto logic test."}, + {"test", CmdHFEMVTest, 0, "Crypto logic test."}, {NULL, NULL, 0, NULL} }; From 11146fc1e1722ad8780d0589fc2f6a66fa198108 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Mon, 1 Oct 2018 21:12:14 +0300 Subject: [PATCH 288/310] modify argtable parser to parse ints with spaces (#683) * modify argtable parser to parse ints with spaces * added arg_strx1 and arg_strx0 for x str arguments in one * added option to clue data in arg parser * add new argtable logic to emv commands and small fix * small fix in GPO help * small GPO fix --- client/cliparser/cliparser.c | 23 ++++++++++++++++++++--- client/cliparser/cliparser.h | 4 ++++ client/cmdhf14a.c | 4 ++-- client/emv/cmdemv.c | 14 +++++++------- 4 files changed, 33 insertions(+), 12 deletions(-) diff --git a/client/cliparser/cliparser.c b/client/cliparser/cliparser.c index 87427db0..56be2ca6 100644 --- a/client/cliparser/cliparser.c +++ b/client/cliparser/cliparser.c @@ -80,6 +80,10 @@ enum ParserState { #define isSpace(c)(c == ' ' || c == '\t') int CLIParserParseString(const char* str, void* vargtable[], size_t vargtableLen, bool allowEmptyExec) { + return CLIParserParseStringEx(str, vargtable, vargtableLen, allowEmptyExec, false); +} + +int CLIParserParseStringEx(const char* str, void* vargtable[], size_t vargtableLen, bool allowEmptyExec, bool clueData) { int argc = 0; char *argv[200] = {NULL}; @@ -99,7 +103,7 @@ int CLIParserParseString(const char* str, void* vargtable[], size_t vargtableLen for (int i = 0; i < len; i++) { switch(state){ case PS_FIRST: // first char - if (str[i] == '-'){ // first char before space is '-' - next element - option + if (!clueData || str[i] == '-'){ // first char before space is '-' - next element - option OR not "clueData" for not-option fields state = PS_OPTION; if (spaceptr) { @@ -149,10 +153,23 @@ void CLIParserFree() { // convertors int CLIParamHexToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen) { *datalen = 0; - if (!strlen(argstr->sval[0])) + if (!argstr->count) return 0; - switch(param_gethex_to_eol(argstr->sval[0], 0, data, maxdatalen, datalen)) { + char buf[256] = {0}; + int ibuf = 0; + + for (int i = 0; i < argstr->count; i++) { + int len = strlen(argstr->sval[i]); + memcpy(&buf[ibuf], argstr->sval[i], len); + ibuf += len; + } + buf[ibuf] = 0; + + if (!ibuf) + return 0; + + switch(param_gethex_to_eol(buf, 0, data, maxdatalen, datalen)) { case 1: printf("Parameter error: Invalid HEX value.\n"); return 1; diff --git a/client/cliparser/cliparser.h b/client/cliparser/cliparser.h index 169f102e..7c1ced20 100644 --- a/client/cliparser/cliparser.h +++ b/client/cliparser/cliparser.h @@ -21,12 +21,16 @@ #define arg_get_str(n)((struct arg_str*)argtable[n]) #define arg_get_str_len(n)(strlen(((struct arg_str*)argtable[n])->sval[0])) +#define arg_strx1(shortopts, longopts, datatype, glossary) (arg_strn((shortopts), (longopts), (datatype), 1, 250, (glossary))) +#define arg_strx0(shortopts, longopts, datatype, glossary) (arg_strn((shortopts), (longopts), (datatype), 0, 250, (glossary))) + #define CLIExecWithReturn(cmd, atbl, ifempty) if (CLIParserParseString(cmd, atbl, arg_getsize(atbl), ifempty)){CLIParserFree();return 0;} #define CLIGetStrBLessWithReturn(paramnum, data, datalen, delta) if (CLIParamHexToBuf(arg_get_str(paramnum), data, sizeof(data) - (delta), datalen)) {CLIParserFree();return 1;} #define CLIGetStrWithReturn(paramnum, data, datalen) if (CLIParamHexToBuf(arg_get_str(paramnum), data, sizeof(data), datalen)) {CLIParserFree();return 1;} extern int CLIParserInit(char *vprogramName, char *vprogramHint, char *vprogramHelp); extern int CLIParserParseString(const char* str, void* argtable[], size_t vargtableLen, bool allowEmptyExec); +extern int CLIParserParseStringEx(const char* str, void* vargtable[], size_t vargtableLen, bool allowEmptyExec, bool clueData); extern int CLIParserParseArg(int argc, char **argv, void* argtable[], size_t vargtableLen, bool allowEmptyExec); extern void CLIParserFree(); diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 2d76f109..94eb8ff3 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -742,7 +742,7 @@ int CmdHF14AAPDU(const char *cmd) { arg_lit0("sS", "select", "activate field and select card"), arg_lit0("kK", "keep", "leave the signal field ON after receive response"), arg_lit0("tT", "tlv", "executes TLV decoder if it possible"), - arg_str1(NULL, NULL, "", NULL), + arg_strx1(NULL, NULL, "", NULL), arg_param_end }; CLIExecWithReturn(cmd, argtable, false); @@ -807,7 +807,7 @@ int CmdHF14ACmdRaw(const char *cmd) { arg_int0("t", "timeout", NULL, "timeout in ms"), arg_lit0("T", "topaz", "use Topaz protocol to send command"), arg_lit0("3", NULL, "ISO14443-3 select only (skip RATS)"), - arg_str1(NULL, NULL, "", NULL), + arg_strx1(NULL, NULL, "", NULL), arg_param_end }; // defaults diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index 93635f85..544632ef 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -49,7 +49,7 @@ int CmdHFEMVSelect(const char *cmd) { arg_lit0("kK", "keep", "keep field for next command"), arg_lit0("aA", "apdu", "show APDU reqests and responses"), arg_lit0("tT", "tlv", "TLV decode results"), - arg_str0(NULL, NULL, "", NULL), + arg_strx0(NULL, NULL, "", NULL), arg_param_end }; CLIExecWithReturn(cmd, argtable, true); @@ -183,7 +183,7 @@ int CmdHFEMVGPO(const char *cmd) { CLIParserInit("hf emv gpo", "Executes Get Processing Options command. It returns data in TLV format (0x77 - format2) or plain format (0x80 - format1).\nNeeds a EMV applet to be selected.", "Usage:\n\thf emv gpo -k -> execute GPO\n" - "\thf emv gpo -st 01020304 -> execute GPO with 4-byte PDOL data, show result in TLV\n"); + "\thf emv gpo -t 01020304 -> execute GPO with 4-byte PDOL data, show result in TLV\n"); // here need to add load params from file and gen pdol void* argtable[] = { @@ -193,7 +193,7 @@ int CmdHFEMVGPO(const char *cmd) { arg_lit0("mM", "make", "make PDOLdata from PDOL (tag 9F38) and parameters (NOT WORK!!!)"), arg_lit0("aA", "apdu", "show APDU reqests and responses"), arg_lit0("tT", "tlv", "TLV decode results of selected applets"), - arg_str0(NULL, NULL, "", NULL), + arg_strx0(NULL, NULL, "", NULL), arg_param_end }; CLIExecWithReturn(cmd, argtable, true); @@ -215,7 +215,7 @@ int CmdHFEMVGPO(const char *cmd) { // calc PDOL struct tlv *pdol_data_tlv = NULL; struct tlv data_tlv = { - .tag = 0x01, + .tag = 0x83, .len = datalen, .value = (uint8_t *)data, }; @@ -281,7 +281,7 @@ int CmdHFEMVReadRecord(const char *cmd) { arg_lit0("kK", "keep", "keep field ON for next command"), arg_lit0("aA", "apdu", "show APDU reqests and responses"), arg_lit0("tT", "tlv", "TLV decode results of selected applets"), - arg_str1(NULL, NULL, "", NULL), + arg_strx1(NULL, NULL, "", NULL), arg_param_end }; CLIExecWithReturn(cmd, argtable, true); @@ -335,7 +335,7 @@ int CmdHFEMVAC(const char *cmd) { arg_str0("dD", "decision", "", "Terminal decision. aac - declined, tc - approved, arqc - online authorisation requested"), arg_lit0("aA", "apdu", "show APDU reqests and responses"), arg_lit0("tT", "tlv", "TLV decode results of selected applets"), - arg_str1(NULL, NULL, "", NULL), + arg_strx1(NULL, NULL, "", NULL), arg_param_end }; CLIExecWithReturn(cmd, argtable, false); @@ -457,7 +457,7 @@ int CmdHFEMVInternalAuthenticate(const char *cmd) { arg_lit0("kK", "keep", "keep field ON for next command"), arg_lit0("aA", "apdu", "show APDU reqests and responses"), arg_lit0("tT", "tlv", "TLV decode results of selected applets"), - arg_str1(NULL, NULL, "", NULL), + arg_strx1(NULL, NULL, "", NULL), arg_param_end }; CLIExecWithReturn(cmd, argtable, false); From 7b2cd970ec5f158a24c5ab20db80fbfa3cb88dda Mon Sep 17 00:00:00 2001 From: Michael Farrell Date: Sun, 7 Oct 2018 01:33:44 +1000 Subject: [PATCH 289/310] FIX: crash on Bionic libc if CloseProxmark is called twice. (#672) In Android O and later, if an invalid pthread_t is passed to pthread_join, it calls fatal(). https://github.com/aosp-mirror/platform_bionic/blob/ed16b344e75f422fb36fbfd91fb30de339475880/libc/bionic/pthread_internal.cpp#L116-L128 This patch addresses it by: 1. Always memset(0) on USB_communications_thread at the end of CloseProxmark. 2. On Bionic, only call pthread_join on USB_communications_thread if it is not equal to 0. --- client/comms.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/client/comms.c b/client/comms.c index 2030f8f3..86dca3ed 100644 --- a/client/comms.c +++ b/client/comms.c @@ -333,7 +333,20 @@ bool OpenProxmark(void *port, bool wait_for_port, int timeout, bool flash_mode) void CloseProxmark(void) { conn.run = false; + +#ifdef __BIONIC__ + // In Android O and later, if an invalid pthread_t is passed to pthread_join, it calls fatal(). + // https://github.com/aosp-mirror/platform_bionic/blob/ed16b344e75f422fb36fbfd91fb30de339475880/libc/bionic/pthread_internal.cpp#L116-L128 + // + // In Bionic libc, pthread_t is an integer. + + if (USB_communication_thread != 0) { + pthread_join(USB_communication_thread, NULL); + } +#else + // pthread_t is a struct on other libc, treat as an opaque memory reference pthread_join(USB_communication_thread, NULL); +#endif if (sp) { uart_close(sp); @@ -351,6 +364,7 @@ void CloseProxmark(void) { // Clean up our state sp = NULL; serial_port_name = NULL; + memset(&USB_communication_thread, 0, sizeof(pthread_t)); } From eed83b910ca8687f7928b5d3743ab9bcd7ede705 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Sat, 6 Oct 2018 17:48:43 +0200 Subject: [PATCH 290/310] Add another #ifdef for the __BIONIC__ fix (the effect of setting p_thread to 0 is undefined for other libs) --- client/comms.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/comms.c b/client/comms.c index 86dca3ed..0c4efa73 100644 --- a/client/comms.c +++ b/client/comms.c @@ -364,7 +364,9 @@ void CloseProxmark(void) { // Clean up our state sp = NULL; serial_port_name = NULL; +#ifdef __BIONIC__ memset(&USB_communication_thread, 0, sizeof(pthread_t)); +#endif } From 351d6d17b3fa82a82b2205c69dcc024884d0b146 Mon Sep 17 00:00:00 2001 From: AntiCat Date: Sun, 7 Oct 2018 17:30:47 +0200 Subject: [PATCH 291/310] Fix: ControlWidget placement (#690) * ui: code cleanup * ui: move control widget below plot --- client/proxguiqt.cpp | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/client/proxguiqt.cpp b/client/proxguiqt.cpp index ab0976cc..8fc1f990 100644 --- a/client/proxguiqt.cpp +++ b/client/proxguiqt.cpp @@ -181,8 +181,7 @@ ProxWidget::ProxWidget(QWidget *parent, ProxGuiQT *master) : QWidget(parent) this->master = master; resize(800,500); - /** Setup the controller widget **/ - + // Setup the controller widget controlWidget = new QWidget(); opsController = new Ui::Form(); opsController->setupUi(controlWidget); @@ -204,23 +203,17 @@ ProxWidget::ProxWidget(QWidget *parent, ProxGuiQT *master) : QWidget(parent) QObject::connect(opsController->horizontalSlider_dirthr_down, SIGNAL(valueChanged(int)), this, SLOT(vchange_dthr_down(int))); QObject::connect(opsController->horizontalSlider_askedge, SIGNAL(valueChanged(int)), this, SLOT(vchange_askedge(int))); - controlWidget->show(); - // Set up the plot widget, which does the actual plotting - plot = new Plot(this); - /* - QSlider* slider = new QSlider(Qt::Horizontal); - slider->setFocusPolicy(Qt::StrongFocus); - slider->setTickPosition(QSlider::TicksBothSides); - slider->setTickInterval(10); - slider->setSingleStep(1); - */ QVBoxLayout *layout = new QVBoxLayout; - //layout->addWidget(slider); layout->addWidget(plot); setLayout(layout); - //printf("Proxwidget Constructor just set layout\r\n"); + show(); // places the window on the screen. + + // Move controller widget below plot + controlWidget->move(x(),y()+frameSize().height()); + controlWidget->resize(size().width(), controlWidget->size().height()); + controlWidget->show(); } // not 100% sure what i need in this block From 70b2fc0abdb9efe03c3eb29c8b9d661ba4c3c90e Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Sun, 7 Oct 2018 17:32:03 +0200 Subject: [PATCH 292/310] start fixing hf 15: (#684) * implement a real time Decoder for tag responses (will be required for sniffing) * switch off field after each command (protect rdv40) * correctly signal field status with LED D --- armsrc/iso15693.c | 664 ++++++++++++++++++++++++++-------------------- client/cmdhf15.c | 11 +- 2 files changed, 386 insertions(+), 289 deletions(-) diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index c092b383..9b0ab29e 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -67,6 +67,8 @@ #define arraylen(x) (sizeof(x)/sizeof((x)[0])) +static int DEBUG = 0; + /////////////////////////////////////////////////////////////////////// // ISO 15693 Part 2 - Air Interface // This section basicly contains transmission and receiving of bits @@ -82,10 +84,10 @@ #define sprintUID(target,uid) Iso15693sprintUID(target,uid) // approximate amplitude=sqrt(ci^2+cq^2) -#define AMPLITUDE(ci, cq) (MAX(ABS(ci), ABS(cq)) + (MIN(ABS(ci), ABS(cq))>>1)) - -static int DEBUG = 0; +#define AMPLITUDE(ci, cq) (MAX(ABS(ci), ABS(cq)) + MIN(ABS(ci), ABS(cq))/2) +// DMA buffer +#define ISO15693_DMA_BUFFER_SIZE 128 // --------------------------- // Signal Processing @@ -168,13 +170,15 @@ static void CodeIso15693AsReader(uint8_t *cmd, int n) ToSendStuffBit(0); ToSendStuffBit(1); - // And slack at the end, too. - for(i = 0; i < 24; i++) { + // Fill remainder of last byte with 1 + for(i = 0; i < 4; i++) { ToSendStuffBit(1); } + + ToSendMax++; } -// encode data using "1 out of 256" sheme +// encode data using "1 out of 256" scheme // data rate is 1,66 kbit/s (fc/8192) // is designed for more robust communication over longer distances static void CodeIso15693AsReader256(uint8_t *cmd, int n) @@ -222,224 +226,349 @@ static void CodeIso15693AsReader256(uint8_t *cmd, int n) } -// Transmit the command (to the tag) that was placed in ToSend[]. -static void TransmitTo15693Tag(const uint8_t *cmd, int len, int *samples, int *wait) +// Transmit the command (to the tag) that was placed in cmd[]. +static void TransmitTo15693Tag(const uint8_t *cmd, int len) { - int c; - FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_TX); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); - if(*wait < 10) { *wait = 10; } - c = 0; - for(;;) { + LED_B_ON(); + for(int c = 0; c < len; ) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { AT91C_BASE_SSC->SSC_THR = ~cmd[c]; c++; - if(c >= len) { - break; - } - } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; - (void)r; } WDT_HIT(); } - *samples = (c + *wait) << 3; + LED_B_OFF(); } //----------------------------------------------------------------------------- -// Transmit the command (to the reader) that was placed in ToSend[]. +// Transmit the command (to the reader) that was placed in cmd[]. //----------------------------------------------------------------------------- -static void TransmitTo15693Reader(const uint8_t *cmd, int len, int *samples, int *wait) +static void TransmitTo15693Reader(const uint8_t *cmd, int len) { - int c = 0; - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR|FPGA_HF_SIMULATOR_MODULATE_424K); - if(*wait < 10) { *wait = 10; } + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_424K); - for(;;) { + LED_C_ON(); + for(int c = 0; c < len; ) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { AT91C_BASE_SSC->SSC_THR = cmd[c]; c++; - if(c >= len) { - break; - } - } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; - (void)r; } WDT_HIT(); } - *samples = (c + *wait) << 3; + LED_C_OFF(); } -// Read from Tag -// Parameters: -// receivedResponse -// maxLen -// samples -// elapsed -// returns: -// number of decoded bytes -static int GetIso15693AnswerFromTag(uint8_t *receivedResponse, int maxLen, int *samples, int *elapsed) +//============================================================================= +// An ISO 15693 demodulator (one subcarrier only). Uses cross correlation to +// identify the SOF, each bit, and EOF. +// This function is called 8 times per bit (every 2 subcarrier cycles). +// Subcarrier frequency fs is 424kHz, 1/fs = 2,36us, +// i.e. function is called every 4,72us +// LED handling: +// LED C -> ON once we have received the SOF and are expecting the rest. +// LED C -> OFF once we have received EOF or are unsynced +// +// Returns: true if we received a EOF +// false if we are still waiting for some more +//============================================================================= + +#define SUBCARRIER_DETECT_THRESHOLD 2 +#define SOF_CORRELATOR_LEN (1<<5) + +typedef struct Demod { + enum { + DEMOD_UNSYNCD, + DEMOD_AWAIT_SOF_1, + DEMOD_AWAIT_SOF_2, + DEMOD_RECEIVING_DATA, + DEMOD_AWAIT_EOF + } state; + int bitCount; + int posCount; + enum { + LOGIC0, + LOGIC1, + SOF_PART1, + SOF_PART2 + } lastBit; + uint16_t shiftReg; + uint8_t *output; + int len; + int sum1, sum2; + uint8_t SOF_low; + uint8_t SOF_high; + uint8_t SOF_last; + int32_t SOF_corr; + int32_t SOF_corr_prev; + uint8_t SOF_correlator[SOF_CORRELATOR_LEN]; +} Demod_t; + +static RAMFUNC int Handle15693SamplesDemod(int8_t ci, int8_t cq, Demod_t *Demod) { - int c = 0; - uint8_t *dest = BigBuf_get_addr(); - -// NOW READ RESPONSE - FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); - c = 0; - for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - uint16_t iq = AT91C_BASE_SSC->SSC_RHR; - // The samples are correlations against I and Q versions of the - // tone that the tag AM-modulates. We just want power. - int8_t i = iq >> 8; - int8_t q = iq; - uint8_t r = AMPLITUDE(i, q); - - dest[c++] = r; + switch(Demod->state) { + case DEMOD_UNSYNCD: + // initialize SOF correlator. We are looking for 12 samples low and 12 samples high. + Demod->SOF_low = 0; + Demod->SOF_high = 12; + Demod->SOF_last = 23; + memset(Demod->SOF_correlator, 0x00, Demod->SOF_last + 1); + Demod->SOF_correlator[Demod->SOF_last] = AMPLITUDE(ci,cq); + Demod->SOF_corr = Demod->SOF_correlator[Demod->SOF_last]; + Demod->SOF_corr_prev = Demod->SOF_corr; + // initialize Demodulator + Demod->posCount = 0; + Demod->bitCount = 0; + Demod->len = 0; + Demod->state = DEMOD_AWAIT_SOF_1; + break; - if(c >= 4000) { - break; - } - } - } + case DEMOD_AWAIT_SOF_1: + // calculate the correlation in real time. Look at differences only. + Demod->SOF_corr += Demod->SOF_correlator[Demod->SOF_low++]; + Demod->SOF_corr -= 2*Demod->SOF_correlator[Demod->SOF_high++]; + Demod->SOF_last++; + Demod->SOF_low &= (SOF_CORRELATOR_LEN-1); + Demod->SOF_high &= (SOF_CORRELATOR_LEN-1); + Demod->SOF_last &= (SOF_CORRELATOR_LEN-1); + Demod->SOF_correlator[Demod->SOF_last] = AMPLITUDE(ci,cq); + Demod->SOF_corr += Demod->SOF_correlator[Demod->SOF_last]; - ////////////////////////////////////////// - /////////// DEMODULATE /////////////////// - ////////////////////////////////////////// - - int i, j; - int max = 0, maxPos=0; - - int skip = 2; - - // First, correlate for SOF - for(i = 0; i < 200; i++) { // usually, SOF is found around i = 60 - int corr = 0; - for(j = 0; j < arraylen(FrameSOF); j += skip) { - corr += FrameSOF[j]*dest[i+(j/skip)]; - } - if(corr > max) { - max = corr; - maxPos = i; - } - } - if (DEBUG) Dbprintf("SOF at %d, correlation %d", maxPos, max/(arraylen(FrameSOF)/skip)); - - int k = 0; // this will be our return value - - // greg - If correlation is less than 1 then there's little point in continuing - if ((max/(arraylen(FrameSOF)/skip)) >= 1) - { - - i = maxPos + arraylen(FrameSOF)/skip; - - uint8_t outBuf[20]; - memset(outBuf, 0, sizeof(outBuf)); - uint8_t mask = 0x01; - for(;;) { - int corr0 = 0, corr00 = 0, corr01 = 0, corr1 = 0, corrEOF = 0; - for(j = 0; j < arraylen(Logic0); j += skip) { - corr0 += Logic0[j]*dest[i+(j/skip)]; - } - corr01 = corr00 = corr0; - for(j = 0; j < arraylen(Logic0); j += skip) { - corr00 += Logic0[j]*dest[i+arraylen(Logic0)/skip+(j/skip)]; - corr01 += Logic1[j]*dest[i+arraylen(Logic0)/skip+(j/skip)]; - } - for(j = 0; j < arraylen(Logic1); j += skip) { - corr1 += Logic1[j]*dest[i+(j/skip)]; - } - for(j = 0; j < arraylen(FrameEOF); j += skip) { - corrEOF += FrameEOF[j]*dest[i+(j/skip)]; - } - // Even things out by the length of the target waveform. - corr00 *= 2; - corr01 *= 2; - corr0 *= 4; - corr1 *= 4; - - if(corrEOF > corr1 && corrEOF > corr00 && corrEOF > corr01) { - if (DEBUG) Dbprintf("EOF at %d, correlation %d (corr01: %d, corr00: %d, corr1: %d, corr0: %d)", - i, corrEOF, corr01, corr00, corr1, corr0); - break; - } else if(corr1 > corr0) { - i += arraylen(Logic1)/skip; - outBuf[k] |= mask; + // if correlation increases for 10 consecutive samples, we are close to maximum correlation + if (Demod->SOF_corr > Demod->SOF_corr_prev + SUBCARRIER_DETECT_THRESHOLD) { + Demod->posCount++; } else { - i += arraylen(Logic0)/skip; + Demod->posCount = 0; } - mask <<= 1; - if(mask == 0) { - k++; - mask = 0x01; - } - if((i+(int)arraylen(FrameEOF)/skip) >= 4000) { - DbpString("ran off end!"); - break; - } - } - if(mask != 0x01) { // this happens, when we miss the EOF - // TODO: for some reason this happens quite often - if (DEBUG) Dbprintf("error, uneven octet! (extra bits!) mask=%02x", mask); - if (mask<0x08) k--; // discard the last uneven octet; - // 0x08 is an assumption - but works quite often - } - // uint8_t str1 [8]; - // itoa(k,str1); - // strncat(str1," octets read",8); - - // DbpString( str1); // DbpString("%d octets", k); - - // for(i = 0; i < k; i+=3) { - // //DbpString("# %2d: %02x ", i, outBuf[i]); - // DbpIntegers(outBuf[i],outBuf[i+1],outBuf[i+2]); - // } - - for(i = 0; i < k; i++) { - receivedResponse[i] = outBuf[i]; - } - } // "end if correlation > 0" (max/(arraylen(FrameSOF)/skip)) - return k; // return the number of bytes demodulated -/// DbpString("CRC=%04x", Iso15693Crc(outBuf, k-2)); + if (Demod->posCount == 10) { // correlation increased 10 times + Demod->state = DEMOD_AWAIT_SOF_2; + } + + Demod->SOF_corr_prev = Demod->SOF_corr; + + break; + case DEMOD_AWAIT_SOF_2: + // calculate the correlation in real time. Look at differences only. + Demod->SOF_corr += Demod->SOF_correlator[Demod->SOF_low++]; + Demod->SOF_corr -= 2*Demod->SOF_correlator[Demod->SOF_high++]; + Demod->SOF_last++; + Demod->SOF_low &= (SOF_CORRELATOR_LEN-1); + Demod->SOF_high &= (SOF_CORRELATOR_LEN-1); + Demod->SOF_last &= (SOF_CORRELATOR_LEN-1); + Demod->SOF_correlator[Demod->SOF_last] = AMPLITUDE(ci,cq); + Demod->SOF_corr += Demod->SOF_correlator[Demod->SOF_last]; + + if (Demod->SOF_corr >= Demod->SOF_corr_prev) { // we are looking for the maximum correlation + Demod->SOF_corr_prev = Demod->SOF_corr; + } else { + Demod->lastBit = SOF_PART1; // detected 1st part of SOF + Demod->sum1 = Demod->SOF_correlator[Demod->SOF_last]; + Demod->sum2 = 0; + Demod->posCount = 2; + Demod->state = DEMOD_RECEIVING_DATA; + LED_C_ON(); + } + + break; + + case DEMOD_RECEIVING_DATA: + if (Demod->posCount == 1) { + Demod->sum1 = 0; + Demod->sum2 = 0; + } + + if (Demod->posCount <= 4) { + Demod->sum1 += AMPLITUDE(ci, cq); + } else { + Demod->sum2 += AMPLITUDE(ci, cq); + } + + if (Demod->posCount == 8) { + int16_t corr_1 = (Demod->sum2 - Demod->sum1) / 4; + int16_t corr_0 = (Demod->sum1 - Demod->sum2) / 4; + int16_t corr_EOF = (Demod->sum1 + Demod->sum2) / 8; + if (corr_EOF > corr_0 && corr_EOF > corr_1) { + Demod->state = DEMOD_AWAIT_EOF; + } else if (corr_1 > corr_0) { + // logic 1 + if (Demod->lastBit == SOF_PART1) { // still part of SOF + Demod->lastBit = SOF_PART2; + } else { + Demod->lastBit = LOGIC1; + Demod->shiftReg >>= 1; + Demod->shiftReg |= 0x80; + Demod->bitCount++; + if (Demod->bitCount == 8) { + Demod->output[Demod->len] = Demod->shiftReg; + Demod->len++; + Demod->bitCount = 0; + Demod->shiftReg = 0; + } + } + } else { + // logic 0 + if (Demod->lastBit == SOF_PART1) { // incomplete SOF + Demod->state = DEMOD_UNSYNCD; + LED_C_OFF(); + } else { + Demod->lastBit = LOGIC0; + Demod->shiftReg >>= 1; + Demod->bitCount++; + if (Demod->bitCount == 8) { + Demod->output[Demod->len] = Demod->shiftReg; + Demod->len++; + Demod->bitCount = 0; + Demod->shiftReg = 0; + } + } + } + Demod->posCount = 0; + } + Demod->posCount++; + break; + + case DEMOD_AWAIT_EOF: + if (Demod->lastBit == LOGIC0) { // this was already part of EOF + LED_C_OFF(); + return true; + } else { + Demod->state = DEMOD_UNSYNCD; + LED_C_OFF(); + } + break; + + default: + Demod->state = DEMOD_UNSYNCD; + LED_C_OFF(); + break; + } + + return false; +} + + +static void DemodInit(Demod_t* Demod, uint8_t* data) +{ + Demod->output = data; + Demod->state = DEMOD_UNSYNCD; +} + + +/* + * Demodulate the samples we received from the tag, also log to tracebuffer + */ +static int GetIso15693AnswerFromTag(uint8_t* response, int timeout) +{ + int maxBehindBy = 0; + int lastRxCounter, samples = 0; + int8_t ci, cq; + bool gotFrame = false; + + // Allocate memory from BigBuf for some buffers + // free all previous allocations first + BigBuf_free(); + + // The DMA buffer, used to stream samples from the FPGA + uint16_t* dmaBuf = (uint16_t*) BigBuf_malloc(ISO15693_DMA_BUFFER_SIZE * sizeof(uint16_t)); + + // the Demodulatur data structure + Demod_t* Demod = (Demod_t*) BigBuf_malloc(sizeof(Demod_t)); + + // Set up the demodulator for tag -> reader responses. + DemodInit(Demod, response); + + // wait for last transfer to complete + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)); + + // And put the FPGA in the appropriate mode + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + + // Setup and start DMA. + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + FpgaSetupSscDma((uint8_t*) dmaBuf, ISO15693_DMA_BUFFER_SIZE); + + uint16_t *upTo = dmaBuf; + lastRxCounter = ISO15693_DMA_BUFFER_SIZE; + + for(;;) { + int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & (ISO15693_DMA_BUFFER_SIZE-1); + if(behindBy > maxBehindBy) { + maxBehindBy = behindBy; + } + + if (behindBy < 1) continue; + + ci = (int8_t)(*upTo >> 8); + cq = (int8_t)(*upTo & 0xff); + + upTo++; + lastRxCounter--; + if(upTo >= dmaBuf + ISO15693_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + upTo = dmaBuf; // start reading the circular buffer from the beginning + lastRxCounter += ISO15693_DMA_BUFFER_SIZE; + } + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and + AT91C_BASE_PDC_SSC->PDC_RNCR = ISO15693_DMA_BUFFER_SIZE; // DMA Next Counter registers + } + samples++; + + if (Handle15693SamplesDemod(ci, cq, Demod)) { + gotFrame = true; + break; + } + + if(samples > timeout && Demod->state < DEMOD_RECEIVING_DATA) { + Demod->len = 0; + break; + } + } + + FpgaDisableSscDma(); + + if (DEBUG) Dbprintf("max behindby = %d, samples = %d, gotFrame = %d, Demod.state = %d, Demod.len = %d, Demod.bitCount = %d, Demod.posCount = %d", + maxBehindBy, samples, gotFrame, Demod->state, Demod->len, Demod->bitCount, Demod->posCount); + + if (tracing && Demod->len > 0) { + uint8_t parity[MAX_PARITY_SIZE]; + LogTrace(Demod->output, Demod->len, 0, 0, parity, false); + } + + return Demod->len; } // Now the GetISO15693 message from sniffing command +// TODO: fix it. This cannot work for several reasons: +// 1. Carrier is switched on during sniffing? +// 2. We most probable miss the next reader command when demodulating static int GetIso15693AnswerFromSniff(uint8_t *receivedResponse, int maxLen, int *samples, int *elapsed) { - int c = 0; uint8_t *dest = BigBuf_get_addr(); // NOW READ RESPONSE + LED_D_ON(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); //spindelay(60); // greg - experiment to get rid of some of the 0 byte/failed reads - c = 0; - for(;;) { + for(int c = 0; c < BIGBUF_SIZE; ) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { uint16_t iq = AT91C_BASE_SSC->SSC_RHR; // The samples are correlations against I and Q versions of the // tone that the tag AM-modulates. We just want power, // so abs(I) + abs(Q) is close to what we want. - int8_t i = iq >> 8; - int8_t q = iq; + int8_t i = (int8_t)(iq >> 8); + int8_t q = (int8_t)(iq & 0xff); uint8_t r = AMPLITUDE(i, q); - dest[c++] = r; - - if(c >= BIGBUF_SIZE) { - break; - } } } + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_D_OFF(); ////////////////////////////////////////// /////////// DEMODULATE /////////////////// @@ -461,6 +590,7 @@ static int GetIso15693AnswerFromSniff(uint8_t *receivedResponse, int maxLen, int maxPos = i; } } + if (DEBUG) Dbprintf("SOF at %d, correlation %d", maxPos,max/(arraylen(FrameSOF)/skip)); int k = 0; // this will be our return value @@ -498,7 +628,7 @@ static int GetIso15693AnswerFromSniff(uint8_t *receivedResponse, int maxLen, int if(corrEOF > corr1 && corrEOF > corr00 && corrEOF > corr01) { if (DEBUG) Dbprintf("EOF at %d, correlation %d (corr01: %d, corr00: %d, corr1: %d, corr0: %d)", - i, corrEOF, corr01, corr00, corr1, corr0); + i, corrEOF, corr01, corr00, corr1, corr0); break; } else if(corr1 > corr0) { i += arraylen(Logic1)/skip; @@ -549,16 +679,18 @@ static void BuildIdentifyRequest(void); //----------------------------------------------------------------------------- void AcquireRawAdcSamplesIso15693(void) { + LEDsoff(); + LED_A_ON(); + uint8_t *dest = BigBuf_get_addr(); - int c = 0; - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); BuildIdentifyRequest(); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // Give the tags time to energize + LED_D_ON(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); SpinDelay(100); @@ -566,48 +698,51 @@ void AcquireRawAdcSamplesIso15693(void) FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_TX); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); - c = 0; - for(;;) { + LED_B_ON(); + for(int c = 0; c < ToSendMax; ) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { AT91C_BASE_SSC->SSC_THR = ~ToSend[c]; c++; - if(c == ToSendMax+3) { - break; - } } WDT_HIT(); } + LED_B_OFF(); + + // wait for last transfer to complete + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)); FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); - c = 0; - for(;;) { + for(int c = 0; c < 4000; ) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { uint16_t iq = AT91C_BASE_SSC->SSC_RHR; // The samples are correlations against I and Q versions of the // tone that the tag AM-modulates. We just want power, // so abs(I) + abs(Q) is close to what we want. - int8_t i = iq >> 8; - int8_t q = iq; + int8_t i = (int8_t)(iq >> 8); + int8_t q = (int8_t)(iq & 0xff); uint8_t r = AMPLITUDE(i, q); - dest[c++] = r; - - if(c >= 4000) { - break; - } } } + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); } +// TODO: there is no trigger condition. The 14000 samples represent a time frame of 66ms. +// It is unlikely that we get something meaningful. +// TODO: Currently we only record tag answers. Add tracing of reader commands. +// TODO: would we get something at all? The carrier is switched on... void RecordRawAdcSamplesIso15693(void) { + LEDsoff(); + LED_A_ON(); + uint8_t *dest = BigBuf_get_addr(); - int c = 0; - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Setup SSC FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); @@ -620,43 +755,38 @@ void RecordRawAdcSamplesIso15693(void) SpinDelay(100); + LED_D_ON(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); - c = 0; - for(;;) { + for(int c = 0; c < 14000;) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { uint16_t iq = AT91C_BASE_SSC->SSC_RHR; // The samples are correlations against I and Q versions of the // tone that the tag AM-modulates. We just want power, // so abs(I) + abs(Q) is close to what we want. - int8_t i = iq >> 8; - int8_t q = iq; + int8_t i = (int8_t)(iq >> 8); + int8_t q = (int8_t)(iq & 0xff); uint8_t r = AMPLITUDE(i, q); - dest[c++] = r; - - if(c >= 14000) { - break; - } } } + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_D_OFF(); Dbprintf("finished recording"); + LED_A_OFF(); } // Initialize the proxmark as iso15k reader // (this might produces glitches that confuse some tags -void Iso15693InitReader() { - LED_A_ON(); - LED_B_ON(); - LED_C_OFF(); - LED_D_OFF(); - +static void Iso15693InitReader() { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Setup SSC // FpgaSetupSsc(); // Start from off (no field generated) + LED_D_OFF(); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); SpinDelay(10); @@ -664,18 +794,14 @@ void Iso15693InitReader() { FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); // Give the tags time to energize + LED_D_ON(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); SpinDelay(250); - - LED_A_ON(); - LED_B_OFF(); - LED_C_OFF(); - LED_D_OFF(); } /////////////////////////////////////////////////////////////////////// // ISO 15693 Part 3 - Air Interface -// This section basicly contains transmission and receiving of bits +// This section basically contains transmission and receiving of bits /////////////////////////////////////////////////////////////////////// // Encode (into the ToSend buffers) an identify request, which is the first @@ -732,8 +858,9 @@ static void BuildReadBlockRequest(uint8_t *uid, uint8_t blockNumber ) CodeIso15693AsReader(cmd, sizeof(cmd)); } + // Now the VICC>VCD responses when we are simulating a tag - static void BuildInventoryResponse( uint8_t *uid) +static void BuildInventoryResponse( uint8_t *uid) { uint8_t cmd[12]; @@ -766,17 +893,11 @@ static void BuildReadBlockRequest(uint8_t *uid, uint8_t blockNumber ) // **recv will return you a pointer to the received data // If you do not need the answer use NULL for *recv[] // return: lenght of received data -int SendDataTag(uint8_t *send, int sendlen, int init, int speed, uint8_t **recv) { +int SendDataTag(uint8_t *send, int sendlen, bool init, int speed, uint8_t **recv) { - int samples = 0; - int tsamples = 0; - int wait = 0; - int elapsed = 0; - LED_A_ON(); - LED_B_ON(); + LED_B_OFF(); LED_C_OFF(); - LED_D_OFF(); if (init) Iso15693InitReader(); @@ -792,22 +913,14 @@ int SendDataTag(uint8_t *send, int sendlen, int init, int speed, uint8_t **recv) CodeIso15693AsReader(send, sendlen); } - LED_A_ON(); - LED_B_OFF(); - - TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait); + TransmitTo15693Tag(ToSend,ToSendMax); // Now wait for a response if (recv!=NULL) { - LED_A_OFF(); - LED_B_ON(); - answerLen = GetIso15693AnswerFromTag(answer, 100, &samples, &elapsed) ; + answerLen = GetIso15693AnswerFromTag(answer, 100); *recv=answer; } LED_A_OFF(); - LED_B_OFF(); - LED_C_OFF(); - LED_D_OFF(); return answerLen; } @@ -887,35 +1000,21 @@ void SetDebugIso15693(uint32_t debug) { return; } - - //----------------------------------------------------------------------------- // Simulate an ISO15693 reader, perform anti-collision and then attempt to read a sector // all demodulation performed in arm rather than host. - greg //----------------------------------------------------------------------------- void ReaderIso15693(uint32_t parameter) { + LEDsoff(); LED_A_ON(); - LED_B_ON(); - LED_C_OFF(); - LED_D_OFF(); int answerLen1 = 0; - int answerLen2 = 0; - // int answerLen3 = 0; - int i = 0; - int samples = 0; - int tsamples = 0; - int wait = 0; - int elapsed = 0; uint8_t TagUID[8] = {0x00}; FpgaDownloadAndGo(FPGA_BITSTREAM_HF); uint8_t *answer1 = BigBuf_get_addr() + 4000; - uint8_t *answer2 = BigBuf_get_addr() + 4100; - // uint8_t *answer3 = BigBuf_get_addr() + 4200; - // Blank arrays memset(answer1, 0x00, 200); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); @@ -927,24 +1026,20 @@ void ReaderIso15693(uint32_t parameter) SpinDelay(200); // Give the tags time to energize + LED_D_ON(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); SpinDelay(200); - LED_A_ON(); - LED_B_OFF(); - LED_C_OFF(); - LED_D_OFF(); - // FIRST WE RUN AN INVENTORY TO GET THE TAG UID // THIS MEANS WE CAN PRE-BUILD REQUESTS TO SAVE CPU TIME // Now send the IDENTIFY command BuildIdentifyRequest(); - TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait); + TransmitTo15693Tag(ToSend,ToSendMax); // Now wait for a response - answerLen1 = GetIso15693AnswerFromTag(answer1, 100, &samples, &elapsed) ; + answerLen1 = GetIso15693AnswerFromTag(answer1, 100) ; if (answerLen1 >=12) // we should do a better check than this { @@ -964,7 +1059,7 @@ void ReaderIso15693(uint32_t parameter) Dbhexdump(answerLen1,answer1,true); // UID is reverse - if (answerLen1>=12) + if (answerLen1 >= 12) Dbprintf("UID = %02hX%02hX%02hX%02hX%02hX%02hX%02hX%02hX", TagUID[7],TagUID[6],TagUID[5],TagUID[4], TagUID[3],TagUID[2],TagUID[1],TagUID[0]); @@ -979,12 +1074,13 @@ void ReaderIso15693(uint32_t parameter) // Dbhexdump(answerLen3,answer3,true); // read all pages - if (answerLen1>=12 && DEBUG) { - i=0; + if (answerLen1 >= 12 && DEBUG) { + uint8_t *answer2 = BigBuf_get_addr() + 4100; + int i=0; while (i<32) { // sanity check, assume max 32 pages BuildReadBlockRequest(TagUID,i); - TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait); - answerLen2 = GetIso15693AnswerFromTag(answer2, 100, &samples, &elapsed); + TransmitTo15693Tag(ToSend,ToSendMax); + int answerLen2 = GetIso15693AnswerFromTag(answer2, 100); if (answerLen2>0) { Dbprintf("READ SINGLE BLOCK %d returned %d octets:",i,answerLen2); DbdecodeIso15693Answer(answerLen2,answer2); @@ -995,25 +1091,23 @@ void ReaderIso15693(uint32_t parameter) } } - LED_A_OFF(); - LED_B_OFF(); - LED_C_OFF(); + // for the time being, switch field off to protect rdv4.0 + // note: this prevents using hf 15 cmd with s option - which isn't implemented yet anyway + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); + + LED_A_OFF(); } // Simulate an ISO15693 TAG, perform anti-collision and then print any reader commands // all demodulation performed in arm rather than host. - greg void SimTagIso15693(uint32_t parameter, uint8_t *uid) { + LEDsoff(); LED_A_ON(); - LED_B_ON(); - LED_C_OFF(); - LED_D_OFF(); int answerLen1 = 0; int samples = 0; - int tsamples = 0; - int wait = 0; int elapsed = 0; FpgaDownloadAndGo(FPGA_BITSTREAM_HF); @@ -1028,11 +1122,6 @@ void SimTagIso15693(uint32_t parameter, uint8_t *uid) FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); SpinDelay(200); - LED_A_OFF(); - LED_B_OFF(); - LED_C_ON(); - LED_D_OFF(); - // Listen to reader answerLen1 = GetIso15693AnswerFromSniff(buf, 100, &samples, &elapsed) ; @@ -1043,7 +1132,7 @@ void SimTagIso15693(uint32_t parameter, uint8_t *uid) BuildInventoryResponse(uid); - TransmitTo15693Reader(ToSend,ToSendMax, &tsamples, &wait); + TransmitTo15693Reader(ToSend,ToSendMax); } Dbprintf("%d octets read from reader command: %x %x %x %x %x %x %x %x %x", answerLen1, @@ -1054,10 +1143,7 @@ void SimTagIso15693(uint32_t parameter, uint8_t *uid) uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6], uid[7]); - LED_A_OFF(); - LED_B_OFF(); - LED_C_OFF(); - LED_D_OFF(); + LEDsoff(); } @@ -1065,6 +1151,9 @@ void SimTagIso15693(uint32_t parameter, uint8_t *uid) // (some manufactures offer a way to read the AFI, though) void BruteforceIso15693Afi(uint32_t speed) { + LEDsoff(); + LED_A_ON(); + uint8_t data[20]; uint8_t *recv=data; int datalen=0, recvlen=0; @@ -1079,7 +1168,7 @@ void BruteforceIso15693Afi(uint32_t speed) data[1]=ISO15_CMD_INVENTORY; data[2]=0; // mask length datalen=AddCrc(data,3); - recvlen=SendDataTag(data,datalen,0,speed,&recv); + recvlen=SendDataTag(data, datalen, false, speed, &recv); WDT_HIT(); if (recvlen>=12) { Dbprintf("NoAFI UID=%s",sprintUID(NULL,&recv[2])); @@ -1096,7 +1185,7 @@ void BruteforceIso15693Afi(uint32_t speed) for (int i=0;i<256;i++) { data[2]=i & 0xFF; datalen=AddCrc(data,4); - recvlen=SendDataTag(data,datalen,0,speed,&recv); + recvlen=SendDataTag(data, datalen, false, speed, &recv); WDT_HIT(); if (recvlen>=12) { Dbprintf("AFI=%i UID=%s",i,sprintUID(NULL,&recv[2])); @@ -1104,26 +1193,27 @@ void BruteforceIso15693Afi(uint32_t speed) } Dbprintf("AFI Bruteforcing done."); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); } // Allows to directly send commands to the tag via the client -void DirectTag15693Command(uint32_t datalen,uint32_t speed, uint32_t recv, uint8_t data[]) { +void DirectTag15693Command(uint32_t datalen, uint32_t speed, uint32_t recv, uint8_t data[]) { int recvlen=0; uint8_t *recvbuf = BigBuf_get_addr(); -// UsbCommand n; + + LED_A_ON(); if (DEBUG) { Dbprintf("SEND"); Dbhexdump(datalen,data,true); } - recvlen=SendDataTag(data,datalen,1,speed,(recv?&recvbuf:NULL)); + recvlen = SendDataTag(data, datalen, true, speed, (recv?&recvbuf:NULL)); if (recv) { - LED_B_ON(); - cmd_send(CMD_ACK,recvlen>48?48:recvlen,0,0,recvbuf,48); - LED_B_OFF(); + cmd_send(CMD_ACK, recvlen>48?48:recvlen, 0, 0, recvbuf, 48); if (DEBUG) { Dbprintf("RECV"); @@ -1132,6 +1222,12 @@ void DirectTag15693Command(uint32_t datalen,uint32_t speed, uint32_t recv, uint8 } } + // for the time being, switch field off to protect rdv4.0 + // note: this prevents using hf 15 cmd with s option - which isn't implemented yet anyway + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_D_OFF(); + + LED_A_OFF(); } diff --git a/client/cmdhf15.c b/client/cmdhf15.c index b6a84c1c..c116b001 100644 --- a/client/cmdhf15.c +++ b/client/cmdhf15.c @@ -373,7 +373,8 @@ int CmdHF15Read(const char *Cmd) return 0; } -// Record Activity without enabeling carrier +// Record Activity without enabling carrier +// TODO: currently it DOES enable the carrier int CmdHF15Record(const char *Cmd) { UsbCommand c = {CMD_RECORD_RAW_ADC_SAMPLES_ISO_15693}; @@ -519,7 +520,7 @@ static command_t CommandTable15[] = {"cmd", CmdHF15Cmd, 0, "Send direct commands to ISO15693 tag"}, {"findafi", CmdHF15Afi, 0, "Brute force AFI of an ISO15693 tag"}, {"dumpmemory", CmdHF15DumpMem, 0, "Read all memory pages of an ISO15693 tag"}, - {NULL, NULL, 0, NULL} + {NULL, NULL, 0, NULL} }; int CmdHF15(const char *Cmd) @@ -572,9 +573,9 @@ int CmdHF15CmdInquiry(const char *Cmd) // Turns debugging on(1)/off(0) int CmdHF15CmdDebug( const char *cmd) { - int debug=atoi(cmd); - if (strlen(cmd)<1) { - PrintAndLog("Usage: hf 15 cmd debug <0|1>"); + int debug = atoi(cmd); + if (strlen(cmd) < 1) { + PrintAndLog("Usage: hf 15 debug <0|1>"); PrintAndLog(" 0 no debugging"); PrintAndLog(" 1 turn debugging on"); return 0; From 2a537311063d3e49479ff4cc8b94287a10c37266 Mon Sep 17 00:00:00 2001 From: AntiCat Date: Mon, 8 Oct 2018 07:15:29 +0200 Subject: [PATCH 293/310] osx: disable app-nap during serial comm (#687) Apple introduced app-nap with OS X 10.10. This feature saves power by throttling apps running in background. However, it also less accurate timers in systemcalls. In our case a 30ms select timeout would take up to 10s. This patch uses an API also added with 10.10 to disable app-nap as long as the serial port is polled. --- CHANGELOG.md | 1 + client/Makefile | 18 +++++++++++++----- client/comms.c | 9 +++++++++ client/util_darwin.h | 17 +++++++++++++++++ client/util_darwin.m | 40 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 80 insertions(+), 5 deletions(-) create mode 100644 client/util_darwin.h create mode 100644 client/util_darwin.m diff --git a/CHANGELOG.md b/CHANGELOG.md index 73306b1f..993dd636 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Changed start sequence in Qt mode (fix: short commands hangs main Qt thread) (Merlok) - Changed driver file proxmark3.inf to support both old and new Product/Vendor IDs (piwi) - Changed all command line parsers in `hf emv` commands to argtable (Merlok) +- Implemented AppNap API, fixing #283 and #627 OSX USB comm issues (AntiCat) ### Added - Added `sc` smartcard (contact card) commands - reader, info, raw, upgrade, setclock, list (hardware version RDV4.0 only) must turn option on in makefile options (Willok, Iceman, marshmellow) diff --git a/client/Makefile b/client/Makefile index 90b16830..96f610cd 100644 --- a/client/Makefile +++ b/client/Makefile @@ -37,10 +37,12 @@ endif LUAPLATFORM = generic platform = $(shell uname) ifneq (,$(findstring MINGW,$(platform))) - LUAPLATFORM = mingw + LUAPLATFORM = mingw else ifeq ($(platform),Darwin) LUAPLATFORM = macosx + OBJCSRCS = util_darwin.m + LDFLAGS += -framework Foundation else LUALIB += -ldl LDLIBS += -ltermcap -lncurses @@ -210,6 +212,7 @@ QTGUISRCS = proxgui.cpp proxguiqt.cpp proxguiqt.moc.cpp guidummy.cpp COREOBJS = $(CORESRCS:%.c=$(OBJDIR)/%.o) CMDOBJS = $(CMDSRCS:%.c=$(OBJDIR)/%.o) +OBJCOBJS = $(OBJCSRCS:%.m=$(OBJDIR)/%.o) ZLIBOBJS = $(ZLIBSRCS:%.c=$(OBJDIR)/%.o) MULTIARCHOBJS = $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_NOSIMD.o) \ $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_MMX.o) \ @@ -235,7 +238,7 @@ endif BINS = proxmark3 flasher fpga_compress WINBINS = $(patsubst %, %.exe, $(BINS)) -CLEAN = $(BINS) $(WINBINS) $(COREOBJS) $(CMDOBJS) $(ZLIBOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(OBJDIR)/*.o *.moc.cpp ui/ui_overlays.h +CLEAN = $(BINS) $(WINBINS) $(COREOBJS) $(CMDOBJS) $(OBJCOBJS) $(ZLIBOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(OBJDIR)/*.o *.moc.cpp ui/ui_overlays.h # need to assign dependancies to build these first... all: lua_build jansson_build $(BINS) @@ -244,10 +247,10 @@ all-static: LDLIBS:=-static $(LDLIBS) all-static: proxmark3 flasher fpga_compress proxmark3: LDLIBS+=$(LUALIB) $(JANSSONLIB) $(QTLDLIBS) -proxmark3: $(OBJDIR)/proxmark3.o $(COREOBJS) $(CMDOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(ZLIBOBJS) lualibs/usb_cmd.lua - $(LD) $(LDFLAGS) $(OBJDIR)/proxmark3.o $(COREOBJS) $(CMDOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(ZLIBOBJS) $(LDLIBS) -o $@ +proxmark3: $(OBJDIR)/proxmark3.o $(COREOBJS) $(CMDOBJS) $(OBJCOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(ZLIBOBJS) lualibs/usb_cmd.lua + $(LD) $(LDFLAGS) $(OBJDIR)/proxmark3.o $(COREOBJS) $(CMDOBJS) $(OBJCOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(ZLIBOBJS) $(LDLIBS) -o $@ -flasher: $(OBJDIR)/flash.o $(OBJDIR)/flasher.o $(COREOBJS) +flasher: $(OBJDIR)/flash.o $(OBJDIR)/flasher.o $(COREOBJS) $(OBJCOBJS) $(LD) $(LDFLAGS) $^ $(LDLIBS) -o $@ fpga_compress: $(OBJDIR)/fpga_compress.o $(ZLIBOBJS) @@ -310,6 +313,10 @@ $(OBJDIR)/%.o : %.cpp $(OBJDIR)/%.d $(CXX) $(DEPFLAGS) $(CXXFLAGS) $(QTINCLUDES) -c -o $@ $< $(POSTCOMPILE) +%.o: %.m +$(OBJDIR)/%.o : %.m $(OBJDIR)/%.d + $(CC) $(DEPFLAGS) $(CFLAGS) -c -o $@ $< + $(POSTCOMPILE) #$(CMDOBJS) $(COREOBJS): $(notdir $(%.c)) %.d # $(CC) $(DEPFLAGS) $(CFLAGS) -c -o $@ $< @@ -325,6 +332,7 @@ $(OBJDIR)/%.o : %.cpp $(OBJDIR)/%.d DEPENDENCY_FILES = $(patsubst %.c, $(OBJDIR)/%.d, $(CORESRCS) $(CMDSRCS) $(ZLIBSRCS) $(MULTIARCHSRCS)) \ $(patsubst %.cpp, $(OBJDIR)/%.d, $(QTGUISRCS)) \ + $(patsubst %.m, $(OBJDIR)/%.d, $(OBJCSRCS)) \ $(OBJDIR)/proxmark3.d $(OBJDIR)/flash.d $(OBJDIR)/flasher.d $(OBJDIR)/fpga_compress.d $(DEPENDENCY_FILES): ; diff --git a/client/comms.c b/client/comms.c index 0c4efa73..190b9110 100644 --- a/client/comms.c +++ b/client/comms.c @@ -18,6 +18,7 @@ #include "uart.h" #include "ui.h" #include "common.h" +#include "util_darwin.h" #include "util_posix.h" @@ -198,6 +199,10 @@ __attribute__((force_align_arg_pointer)) UsbCommand rx; UsbCommand *prx = ℞ +#if defined(__MACH__) && defined(__APPLE__) + disableAppNap("Proxmark3 polling UART"); +#endif + while (conn->run) { rxlen = 0; bool ACK_received = false; @@ -236,6 +241,10 @@ __attribute__((force_align_arg_pointer)) pthread_mutex_unlock(&txBufferMutex); } +#if defined(__MACH__) && defined(__APPLE__) + enableAppNap(); +#endif + pthread_exit(NULL); return NULL; } diff --git a/client/util_darwin.h b/client/util_darwin.h new file mode 100644 index 00000000..923a4dfc --- /dev/null +++ b/client/util_darwin.h @@ -0,0 +1,17 @@ +//----------------------------------------------------------------------------- +// (c) 2018 AntiCat +// +// 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. +//----------------------------------------------------------------------------- +// macOS framework bindings +//----------------------------------------------------------------------------- + +#ifndef UTIL_DARWIN_H__ +#define UTIL_DARWIN_H__ + +void disableAppNap(const char* reason); +void enableAppNap(); + +#endif \ No newline at end of file diff --git a/client/util_darwin.m b/client/util_darwin.m new file mode 100644 index 00000000..d07ebebc --- /dev/null +++ b/client/util_darwin.m @@ -0,0 +1,40 @@ +//----------------------------------------------------------------------------- +// (c) 2018 AntiCat +// +// 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. +//----------------------------------------------------------------------------- +// macOS framework bindings +//----------------------------------------------------------------------------- + +#import "util_darwin.h" + +#import +#import + +static id activity = nil; + +//OS X Version 10.10 is defined in OS X 10.10 and later +#if defined(MAC_OS_X_VERSION_10_10) +void disableAppNap(const char* reason) { + if(activity == nil) { + //NSLog(@"disableAppNap: %@", @(reason)); + activity = [[NSProcessInfo processInfo] beginActivityWithOptions:NSActivityBackground reason:@(reason)]; + [activity retain]; + } +} + +void enableAppNap() { + if(activity != nil) { + //NSLog(@"enableAppNap"); + [[NSProcessInfo processInfo] endActivity:activity]; + [activity release]; + activity = nil; + } +} + +#else +void disableAppNap(const char* reason) { } +void enableAppNap() { } +#endif From b742ab8cc3fa259990551f2d4265c09681ba705f Mon Sep 17 00:00:00 2001 From: AntiCat Date: Mon, 8 Oct 2018 07:20:21 +0200 Subject: [PATCH 294/310] osx: fix annoying focus behaviour (#689) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OS X has a global menu bar and a per app dock icon. Therefore, all GUI applications launched from a terminal will become focused - even if they don’t show any windows. Thereby the terminal loses focus. Since is it very annoying to re-focus the terminal after each proxmark client launch, this change makes the client unfocusable during launch and restores the regular behaviour when a window is created. --- client/Makefile | 2 +- client/proxguiqt.cpp | 14 ++++++++++++++ client/util_darwin.h | 3 +++ client/util_darwin.m | 15 +++++++++++++++ 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/client/Makefile b/client/Makefile index 96f610cd..9e5bde30 100644 --- a/client/Makefile +++ b/client/Makefile @@ -42,7 +42,7 @@ else ifeq ($(platform),Darwin) LUAPLATFORM = macosx OBJCSRCS = util_darwin.m - LDFLAGS += -framework Foundation + LDFLAGS += -framework Foundation -framework AppKit else LUALIB += -ldl LDLIBS += -ltermcap -lncurses diff --git a/client/proxguiqt.cpp b/client/proxguiqt.cpp index 8fc1f990..30e4c8de 100644 --- a/client/proxguiqt.cpp +++ b/client/proxguiqt.cpp @@ -26,6 +26,10 @@ #include #include "proxgui.h" #include + +extern "C" { +#include "util_darwin.h" +} //#include bool g_useOverlays = false; @@ -60,7 +64,12 @@ void ProxGuiQT::_ShowGraphWindow(void) return; if (!plotwidget) + { +#if defined(__MACH__) && defined(__APPLE__) + makeFocusable(); +#endif plotwidget = new ProxWidget(); + } plotwidget->show(); } @@ -108,6 +117,11 @@ void ProxGuiQT::MainLoop() //start proxmark thread after starting event loop QTimer::singleShot(200, this, SLOT(_StartProxmarkThread())); +#if defined(__MACH__) && defined(__APPLE__) + //Prevent the terminal from loosing focus during launch by making the client unfocusable + makeUnfocusable(); +#endif + plotapp->exec(); } diff --git a/client/util_darwin.h b/client/util_darwin.h index 923a4dfc..dd159ec5 100644 --- a/client/util_darwin.h +++ b/client/util_darwin.h @@ -14,4 +14,7 @@ void disableAppNap(const char* reason); void enableAppNap(); +void makeUnfocusable(); +void makeFocusable(); + #endif \ No newline at end of file diff --git a/client/util_darwin.m b/client/util_darwin.m index d07ebebc..c0f69aa9 100644 --- a/client/util_darwin.m +++ b/client/util_darwin.m @@ -12,6 +12,7 @@ #import #import +#import static id activity = nil; @@ -38,3 +39,17 @@ void enableAppNap() { void disableAppNap(const char* reason) { } void enableAppNap() { } #endif + +//OS X Version 10.6 is defined in OS X 10.6 and later +#if defined(MAC_OS_X_VERSION_10_6) +void makeUnfocusable() { + [NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited]; +} + +void makeFocusable() { + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; +} +#else +void makeUnfocusable() { } +void makeFocusable() { } +#endif From 92479429c2e3526fce39a09d19ca0856bb041a09 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Mon, 8 Oct 2018 08:48:37 +0300 Subject: [PATCH 295/310] Added loading parameters from json to several emv commands (#686) * added loading params from GPO * `hf emv pdol` added help and warning * started `hf emv ac` * dol calculation implemented in genac and intauth * help fix --- client/emv/cmdemv.c | 386 ++++++++++++++++++++++++++------------------ 1 file changed, 228 insertions(+), 158 deletions(-) diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index 544632ef..6bac90d6 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -14,6 +14,31 @@ #include "cliparser/cliparser.h" #include +bool HexToBuffer(const char *errormsg, const char *hexvalue, uint8_t * buffer, size_t maxbufferlen, size_t *bufferlen) { + int buflen = 0; + + switch(param_gethex_to_eol(hexvalue, 0, buffer, maxbufferlen, &buflen)) { + case 1: + PrintAndLog("%s Invalid HEX value.", errormsg); + return false; + case 2: + PrintAndLog("%s Hex value too large.", errormsg); + return false; + case 3: + PrintAndLog("%s Hex value must have even number of digits.", errormsg); + return false; + } + + if (buflen > maxbufferlen) { + PrintAndLog("%s HEX length (%d) more than %d", errormsg, *bufferlen, maxbufferlen); + return false; + } + + *bufferlen = buflen; + + return true; +} + #define TLV_ADD(tag, value)( tlvdb_change_or_add_node(tlvRoot, tag, sizeof(value) - 1, (const unsigned char *)value) ) void ParamLoadDefaults(struct tlvdb *tlvRoot) { //9F02:(Amount, authorized (Numeric)) len:6 @@ -35,6 +60,111 @@ void ParamLoadDefaults(struct tlvdb *tlvRoot) { TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC } +bool ParamLoadFromJson(struct tlvdb *tlv) { + json_t *root; + json_error_t error; + + if (!tlv) { + PrintAndLog("ERROR load params: tlv tree is NULL."); + return false; + } + + // current path + file name + const char *relfname = "emv/defparams.json"; + char fname[strlen(get_my_executable_directory()) + strlen(relfname) + 1]; + strcpy(fname, get_my_executable_directory()); + strcat(fname, relfname); + + root = json_load_file(fname, 0, &error); + if (!root) { + PrintAndLog("Load params: json error on line %d: %s", error.line, error.text); + return false; + } + + if (!json_is_array(root)) { + PrintAndLog("Load params: Invalid json format. root must be array."); + return false; + } + + PrintAndLog("Load params: json OK"); + + for(int i = 0; i < json_array_size(root); i++) { + json_t *data, *jtype, *jlength, *jvalue; + + data = json_array_get(root, i); + if(!json_is_object(data)) + { + PrintAndLog("Load params: data [%d] is not an object", i + 1); + json_decref(root); + return false; + } + + jtype = json_object_get(data, "type"); + if(!json_is_string(jtype)) + { + PrintAndLog("Load params: data [%d] type is not a string", i + 1); + json_decref(root); + return false; + } + const char *tlvType = json_string_value(jtype); + + jvalue = json_object_get(data, "value"); + if(!json_is_string(jvalue)) + { + PrintAndLog("Load params: data [%d] value is not a string", i + 1); + json_decref(root); + return false; + } + const char *tlvValue = json_string_value(jvalue); + + jlength = json_object_get(data, "length"); + if(!json_is_number(jlength)) + { + PrintAndLog("Load params: data [%d] length is not a number", i + 1); + json_decref(root); + return false; + } + + int tlvLength = json_integer_value(jlength); + if (tlvLength > 250) { + PrintAndLog("Load params: data [%d] length more than 250", i + 1); + json_decref(root); + return false; + } + + PrintAndLog("TLV param: %s[%d]=%s", tlvType, tlvLength, tlvValue); + uint8_t buf[251] = {0}; + size_t buflen = 0; + + // here max length must be 4, but now tlv_tag_t is 2-byte var. so let it be 2 by now... TODO: needs refactoring tlv_tag_t... + if (!HexToBuffer("TLV Error type:", tlvType, buf, 2, &buflen)) { + json_decref(root); + return false; + } + tlv_tag_t tag = 0; + for (int i = 0; i < buflen; i++) { + tag = (tag << 8) + buf[i]; + } + + if (!HexToBuffer("TLV Error value:", tlvValue, buf, sizeof(buf) - 1, &buflen)) { + json_decref(root); + return false; + } + + if (buflen != tlvLength) { + PrintAndLog("Load params: data [%d] length of HEX must(%d) be identical to length in TLV param(%d)", i + 1, buflen, tlvLength); + json_decref(root); + return false; + } + + tlvdb_change_or_add_node(tlv, tag, tlvLength, (const unsigned char *)buf); + } + + json_decref(root); + + return true; +} + int CmdHFEMVSelect(const char *cmd) { uint8_t data[APDU_AID_LEN] = {0}; int datalen = 0; @@ -183,14 +313,14 @@ int CmdHFEMVGPO(const char *cmd) { CLIParserInit("hf emv gpo", "Executes Get Processing Options command. It returns data in TLV format (0x77 - format2) or plain format (0x80 - format1).\nNeeds a EMV applet to be selected.", "Usage:\n\thf emv gpo -k -> execute GPO\n" - "\thf emv gpo -t 01020304 -> execute GPO with 4-byte PDOL data, show result in TLV\n"); - // here need to add load params from file and gen pdol + "\thf emv gpo -t 01020304 -> execute GPO with 4-byte PDOL data, show result in TLV\n" + "\thf emv gpo -pmt 9F 37 04 -> load params from file, make PDOL data from PDOL, execute GPO with PDOL, show result in TLV\n"); void* argtable[] = { arg_param_begin, arg_lit0("kK", "keep", "keep field ON for next command"), - arg_lit0("pP", "params", "load parameters for PDOL making from `emv/defparams.json` file (by default uses default parameters) (NOT WORK!!!)"), - arg_lit0("mM", "make", "make PDOLdata from PDOL (tag 9F38) and parameters (NOT WORK!!!)"), + arg_lit0("pP", "params", "load parameters from `emv/defparams.json` file for PDOLdata making from PDOL and parameters"), + arg_lit0("mM", "make", "make PDOLdata from PDOL (tag 9F38) and parameters (by default uses default parameters)"), arg_lit0("aA", "apdu", "show APDU reqests and responses"), arg_lit0("tT", "tlv", "TLV decode results of selected applets"), arg_strx0(NULL, NULL, "", NULL), @@ -220,21 +350,23 @@ int CmdHFEMVGPO(const char *cmd) { .value = (uint8_t *)data, }; if (dataMakeFromPDOL) { - // TODO - PrintAndLog("Make PDOL data not implemented!"); - ParamLoadDefaults(tlvRoot); if (paramsLoadFromFile) { + PrintAndLog("Params loading from file..."); + ParamLoadFromJson(tlvRoot); }; -/* pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83); + + pdol_data_tlv = dol_process((const struct tlv *)tlvdb_external(0x9f38, datalen, data), tlvRoot, 0x83); if (!pdol_data_tlv){ PrintAndLog("ERROR: can't create PDOL TLV."); tlvdb_free(tlvRoot); return 4; - }*/ - return 0; + } } else { + if (paramsLoadFromFile) { + PrintAndLog("WARNING: don't need to load parameters. Sending plain PDOL data..."); + } pdol_data_tlv = &data_tlv; } @@ -253,7 +385,8 @@ int CmdHFEMVGPO(const char *cmd) { uint16_t sw = 0; int res = EMVGPO(leaveSignalON, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); - free(pdol_data_tlv_data); + if (pdol_data_tlv != &data_tlv) + free(pdol_data_tlv); tlvdb_free(tlvRoot); if (sw) @@ -326,16 +459,19 @@ int CmdHFEMVAC(const char *cmd) { "Generate Application Cryptogram command. It returns data in TLV format .\nNeeds a EMV applet to be selected and GPO to be executed.", "Usage:\n\thf emv genac -k 0102 -> generate AC with 2-byte CDOLdata and keep field ON after command\n" "\thf emv genac -t 01020304 -> generate AC with 4-byte CDOL data, show result in TLV\n" - "\thf emv genac -Daac 01020304 -> generate AC with 4-byte CDOL data and terminal decision 'declined'\n"); + "\thf emv genac -Daac 01020304 -> generate AC with 4-byte CDOL data and terminal decision 'declined'\n" + "\thf emv genac -pmt 9F 37 04 -> load params from file, make CDOL data from CDOL, generate AC with CDOL, show result in TLV"); void* argtable[] = { arg_param_begin, arg_lit0("kK", "keep", "keep field ON for next command"), arg_lit0("cC", "cda", "executes CDA transaction. Needs to get SDAD in results."), arg_str0("dD", "decision", "", "Terminal decision. aac - declined, tc - approved, arqc - online authorisation requested"), + arg_lit0("pP", "params", "load parameters from `emv/defparams.json` file for CDOLdata making from CDOL and parameters"), + arg_lit0("mM", "make", "make CDOLdata from CDOL (tag 8C and 8D) and parameters (by default uses default parameters)"), arg_lit0("aA", "apdu", "show APDU reqests and responses"), arg_lit0("tT", "tlv", "TLV decode results of selected applets"), - arg_strx1(NULL, NULL, "", NULL), + arg_strx1(NULL, NULL, "", NULL), arg_param_end }; CLIExecWithReturn(cmd, argtable, false); @@ -360,9 +496,11 @@ int CmdHFEMVAC(const char *cmd) { } if (trTypeCDA) termDecision = termDecision | EMVAC_CDAREQ; - bool APDULogging = arg_get_lit(4); - bool decodeTLV = arg_get_lit(5); - CLIGetStrWithReturn(6, data, &datalen); + bool paramsLoadFromFile = arg_get_lit(4); + bool dataMakeFromCDOL = arg_get_lit(5); + bool APDULogging = arg_get_lit(6); + bool decodeTLV = arg_get_lit(7); + CLIGetStrWithReturn(8, data, &datalen); CLIParserFree(); SetAPDULogging(APDULogging); @@ -373,22 +511,43 @@ int CmdHFEMVAC(const char *cmd) { // calc CDOL struct tlv *cdol_data_tlv = NULL; -// struct tlv *cdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x8c, NULL), tlvRoot, 0x01); // 0x01 - dummy tag struct tlv data_tlv = { .tag = 0x01, .len = datalen, .value = (uint8_t *)data, - }; - cdol_data_tlv = &data_tlv; - PrintAndLog("CDOL data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len)); + }; + if (dataMakeFromCDOL) { + ParamLoadDefaults(tlvRoot); + + if (paramsLoadFromFile) { + PrintAndLog("Params loading from file..."); + ParamLoadFromJson(tlvRoot); + }; + + cdol_data_tlv = dol_process((const struct tlv *)tlvdb_external(0x8c, datalen, data), tlvRoot, 0x01); // 0x01 - dummy tag + if (!cdol_data_tlv){ + PrintAndLog("ERROR: can't create CDOL TLV."); + tlvdb_free(tlvRoot); + return 4; + } + } else { + if (paramsLoadFromFile) { + PrintAndLog("WARNING: don't need to load parameters. Sending plain CDOL data..."); + } + cdol_data_tlv = &data_tlv; + } + + PrintAndLog("CDOL data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len)); + // exec uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; uint16_t sw = 0; int res = EMVAC(leaveSignalON, termDecision, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); -// free(cdol_data_tlv); + if (cdol_data_tlv != &data_tlv) + free(cdol_data_tlv); tlvdb_free(tlvRoot); if (sw) @@ -450,28 +609,65 @@ int CmdHFEMVInternalAuthenticate(const char *cmd) { CLIParserInit("hf emv intauth", "Generate Internal Authenticate command. Usually needs 4-byte random number. It returns data in TLV format .\nNeeds a EMV applet to be selected and GPO to be executed.", "Usage:\n\thf emv intauth -k 01020304 -> execute Internal Authenticate with 4-byte DDOLdata and keep field ON after command\n" - "\thf emv intauth -t 01020304 -> execute Internal Authenticate with 4-byte DDOL data, show result in TLV\n"); + "\thf emv intauth -t 01020304 -> execute Internal Authenticate with 4-byte DDOL data, show result in TLV\n" + "\thf emv intauth -pmt 9F 37 04 -> load params from file, make DDOL data from DDOL, Internal Authenticate with DDOL, show result in TLV"); void* argtable[] = { arg_param_begin, arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("pP", "params", "load parameters from `emv/defparams.json` file for DDOLdata making from DDOL and parameters"), + arg_lit0("mM", "make", "make DDOLdata from DDOL (tag 9F49) and parameters (by default uses default parameters)"), arg_lit0("aA", "apdu", "show APDU reqests and responses"), arg_lit0("tT", "tlv", "TLV decode results of selected applets"), - arg_strx1(NULL, NULL, "", NULL), + arg_strx1(NULL, NULL, "", NULL), arg_param_end }; CLIExecWithReturn(cmd, argtable, false); bool leaveSignalON = arg_get_lit(1); - bool APDULogging = arg_get_lit(2); - bool decodeTLV = arg_get_lit(3); - CLIGetStrWithReturn(4, data, &datalen); + bool paramsLoadFromFile = arg_get_lit(2); + bool dataMakeFromDDOL = arg_get_lit(3); + bool APDULogging = arg_get_lit(4); + bool decodeTLV = arg_get_lit(5); + CLIGetStrWithReturn(6, data, &datalen); CLIParserFree(); SetAPDULogging(APDULogging); + + // Init TLV tree + const char *alr = "Root terminal TLV tree"; + struct tlvdb *tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr); - // DDOL - PrintAndLog("DDOL data[%d]: %s", datalen, sprint_hex(data, datalen)); + // calc DDOL + struct tlv *ddol_data_tlv = NULL; + struct tlv data_tlv = { + .tag = 0x01, + .len = datalen, + .value = (uint8_t *)data, + }; + + if (dataMakeFromDDOL) { + ParamLoadDefaults(tlvRoot); + + if (paramsLoadFromFile) { + PrintAndLog("Params loading from file..."); + ParamLoadFromJson(tlvRoot); + }; + + ddol_data_tlv = dol_process((const struct tlv *)tlvdb_external(0x9f49, datalen, data), tlvRoot, 0x01); // 0x01 - dummy tag + if (!ddol_data_tlv){ + PrintAndLog("ERROR: can't create DDOL TLV."); + tlvdb_free(tlvRoot); + return 4; + } + } else { + if (paramsLoadFromFile) { + PrintAndLog("WARNING: don't need to load parameters. Sending plain DDOL data..."); + } + ddol_data_tlv = &data_tlv; + } + + PrintAndLog("DDOL data[%d]: %s", ddol_data_tlv->len, sprint_hex(ddol_data_tlv->value, ddol_data_tlv->len)); // exec uint8_t buf[APDU_RES_LEN] = {0}; @@ -479,6 +675,10 @@ int CmdHFEMVInternalAuthenticate(const char *cmd) { uint16_t sw = 0; int res = EMVInternalAuthenticate(leaveSignalON, data, datalen, buf, sizeof(buf), &len, &sw, NULL); + if (ddol_data_tlv != &data_tlv) + free(ddol_data_tlv); + tlvdb_free(tlvRoot); + if (sw) PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); @@ -493,136 +693,6 @@ int CmdHFEMVInternalAuthenticate(const char *cmd) { #define dreturn(n) {free(pdol_data_tlv);tlvdb_free(tlvSelect);tlvdb_free(tlvRoot);DropField();return n;} -bool HexToBuffer(const char *errormsg, const char *hexvalue, uint8_t * buffer, size_t maxbufferlen, size_t *bufferlen) { - int buflen = 0; - - switch(param_gethex_to_eol(hexvalue, 0, buffer, maxbufferlen, &buflen)) { - case 1: - PrintAndLog("%s Invalid HEX value.", errormsg); - return false; - case 2: - PrintAndLog("%s Hex value too large.", errormsg); - return false; - case 3: - PrintAndLog("%s Hex value must have even number of digits.", errormsg); - return false; - } - - if (buflen > maxbufferlen) { - PrintAndLog("%s HEX length (%d) more than %d", errormsg, *bufferlen, maxbufferlen); - return false; - } - - *bufferlen = buflen; - - return true; -} - -bool ParamLoadFromJson(struct tlvdb *tlv) { - json_t *root; - json_error_t error; - - if (!tlv) { - PrintAndLog("ERROR load params: tlv tree is NULL."); - return false; - } - - // current path + file name - const char *relfname = "emv/defparams.json"; - char fname[strlen(get_my_executable_directory()) + strlen(relfname) + 1]; - strcpy(fname, get_my_executable_directory()); - strcat(fname, relfname); - - root = json_load_file(fname, 0, &error); - if (!root) { - PrintAndLog("Load params: json error on line %d: %s", error.line, error.text); - return false; - } - - if (!json_is_array(root)) { - PrintAndLog("Load params: Invalid json format. root must be array."); - return false; - } - - PrintAndLog("Load params: json OK"); - - for(int i = 0; i < json_array_size(root); i++) { - json_t *data, *jtype, *jlength, *jvalue; - - data = json_array_get(root, i); - if(!json_is_object(data)) - { - PrintAndLog("Load params: data [%d] is not an object", i + 1); - json_decref(root); - return false; - } - - jtype = json_object_get(data, "type"); - if(!json_is_string(jtype)) - { - PrintAndLog("Load params: data [%d] type is not a string", i + 1); - json_decref(root); - return false; - } - const char *tlvType = json_string_value(jtype); - - jvalue = json_object_get(data, "value"); - if(!json_is_string(jvalue)) - { - PrintAndLog("Load params: data [%d] value is not a string", i + 1); - json_decref(root); - return false; - } - const char *tlvValue = json_string_value(jvalue); - - jlength = json_object_get(data, "length"); - if(!json_is_number(jlength)) - { - PrintAndLog("Load params: data [%d] length is not a number", i + 1); - json_decref(root); - return false; - } - - int tlvLength = json_integer_value(jlength); - if (tlvLength > 250) { - PrintAndLog("Load params: data [%d] length more than 250", i + 1); - json_decref(root); - return false; - } - - PrintAndLog("TLV param: %s[%d]=%s", tlvType, tlvLength, tlvValue); - uint8_t buf[251] = {0}; - size_t buflen = 0; - - // here max length must be 4, but now tlv_tag_t is 2-byte var. so let it be 2 by now... TODO: needs refactoring tlv_tag_t... - if (!HexToBuffer("TLV Error type:", tlvType, buf, 2, &buflen)) { - json_decref(root); - return false; - } - tlv_tag_t tag = 0; - for (int i = 0; i < buflen; i++) { - tag = (tag << 8) + buf[i]; - } - - if (!HexToBuffer("TLV Error value:", tlvValue, buf, sizeof(buf) - 1, &buflen)) { - json_decref(root); - return false; - } - - if (buflen != tlvLength) { - PrintAndLog("Load params: data [%d] length of HEX must(%d) be identical to length in TLV param(%d)", i + 1, buflen, tlvLength); - json_decref(root); - return false; - } - - tlvdb_change_or_add_node(tlv, tag, tlvLength, (const unsigned char *)buf); - } - - json_decref(root); - - return true; -} - int CmdHFEMVExec(const char *cmd) { uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; From 9f3839139568c064633102c3da240be65d8c6205 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Wed, 10 Oct 2018 08:13:40 +0200 Subject: [PATCH 296/310] Update CHANGELOG for 3.1.0 release --- CHANGELOG.md | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 993dd636..fbc40cda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,19 +2,26 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... - ## [unreleased][unreleased] +### Changed + +### Fixed + +### Added + +## [v3.1.0][2018-10-10] + ### Changed - Adjusted `lf cmdread` to respond to client when complete and the client will then automatically call `data samples` -- Improved backdoor detection missbehaving magic s50/1k tag (Fl0-0) +- Improved backdoor detection misbehaving magic s50/1k tag (Fl0-0) - Deleted wipe functionality from `hf mf csetuid` (Merlok) - Changed `hf mf nested` logic (Merlok) - Added `hf mf nested` mode: autosearch keys for attack (from well known keys) (Merlok) - `hf mf nested` Check keys after they have found (Merlok) - `hf mf chk` Move main cycle to arm (Merlok) - Changed proxmark command line parameter `flush` to `-f` or `-flush` (Merlok) -- Changed `hf 14a reader` to just reqest-anticilission-select sequence (Merlok) +- Changed `hf 14a reader` to just request-anticolission-select sequence (Merlok) - Changed `hf 14a raw` - works with LED's and some exchange logic (Merlok) - Changed TLV parser messages to more convenient (Merlok) - Rewritten Legic Prime reader (`hf legic reader`, `write` and `fill`) - it is using xcorrelation now (AntiCat) @@ -33,14 +40,14 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added a bitbang mode to `lf cmdread` if delay is 0 the cmd bits turn off and on the antenna with 0 and 1 respectively (marshmellow) - Added PAC/Stanley detection to lf search (marshmellow) - Added lf pac demod and lf pac read - extracts the raw blocks from a PAC/Stanley tag (marshmellow) -- Added hf mf c* commands compatibity for 4k and gen1b backdoor (Fl0-0) +- Added hf mf c* commands compatibility for 4k and gen1b backdoor (Fl0-0) - Added backdoor detection for gen1b magic s70/4k tag (Fl0-0) - Added data fsktonrz, a fsk cleaning/demodulating routine for weak fsk signal. Note: follow this up with a `data rawdemod nr` to finish demoding your signal. (marshmellow) - Added lf em 410xbrute, LF EM410x reader bruteforce attack by simulating UIDs from a file (Fl0-0) - Added `hf mf cwipe` command. It wipes "magic Chinese" card. For 1a generation it uses card's "wipe" command. For gen1a and gen1b it uses a write command. (Merlok) - Added to `hf mf nested` source key check before attack (Merlok) - Added to `hf mf nested` after attack it checks all found keys on non-open sectors (Merlok) -- `hf mf chk` Added setings to set iso14443a operations timeout. default timeout set to 500us (Merlok) +- `hf mf chk` Added settings to set iso14443a operations timeout. default timeout set to 500us (Merlok) - Added to `hf mf nested` parameters `s` and `ss` for checking slow cards (Merlok) - Added to proxmark command line parameters `w` - wait 20s for serial port (Merlok) - Added to proxmark command line parameters `c` and `l` - execute command and lua script from command line (Merlok) From 7dadcc959fb7009b6e8bbde4a644aa2f7f1b7a98 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Wed, 10 Oct 2018 23:34:04 +0300 Subject: [PATCH 297/310] auth 14443-4 (#692) * AES authentication --- client/cmdhf14a.c | 89 ++++++++++++++++++++++++++++ client/cmdhf14a.h | 1 + client/cmdhfmf.c | 147 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 237 insertions(+) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 94eb8ff3..a5de2e2a 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -648,6 +648,95 @@ void DropField() { SendCommand(&c); } +int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { + uint16_t cmdc = 0; + *dataoutlen = 0; + + if (activateField) { + UsbCommand resp; + + // Anticollision + SELECT card + UsbCommand ca = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_DISCONNECT | ISO14A_CLEAR_TRACE, 0, 0}}; + SendCommand(&ca); + if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + PrintAndLog("14aRAW ERROR: Proxmark connection timeout."); + return 1; + } + + // check result + if (resp.arg[0] == 0) { + PrintAndLog("14aRAW ERROR: No card in field."); + return 1; + } + + if (resp.arg[0] != 1 && resp.arg[0] != 2) { + PrintAndLog("14aRAW ERROR: card not in iso14443-4. res=%d.", resp.arg[0]); + return 1; + } + + if (resp.arg[0] == 2) { // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision + // get ATS + UsbCommand cr = {CMD_READER_ISO_14443a, {ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, 2, 0}}; + uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0 + memcpy(cr.d.asBytes, rats, 2); + SendCommand(&cr); + if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + PrintAndLog("14aRAW ERROR: Proxmark connection timeout."); + return 1; + } + + if (resp.arg[0] <= 0) { // ats_len + PrintAndLog("14aRAW ERROR: Can't get ATS."); + return 1; + } + } + } + + if (leaveSignalON) + cmdc |= ISO14A_NO_DISCONNECT; + + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_RAW | ISO14A_APPEND_CRC | cmdc, (datainlen & 0xFFFF), 0}}; + memcpy(c.d.asBytes, datain, datainlen); + SendCommand(&c); + + uint8_t *recv; + UsbCommand resp; + + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + recv = resp.d.asBytes; + int iLen = resp.arg[0]; + + *dataoutlen = iLen - 2; + if (*dataoutlen < 0) + *dataoutlen = 0; + + if (maxdataoutlen && *dataoutlen > maxdataoutlen) { + PrintAndLog("14aRAW ERROR: Buffer too small(%d). Needs %d bytes", *dataoutlen, maxdataoutlen); + return 2; + } + + memcpy(dataout, recv, *dataoutlen); + + if(!iLen) { + PrintAndLog("14aRAW ERROR: No card response."); + return 1; + } + + // CRC Check + if (iLen == -1) { + PrintAndLog("14aRAW ERROR: ISO 14443A CRC error."); + return 3; + } + + + } else { + PrintAndLog("14aRAW ERROR: Reply timeout."); + return 4; + } + + return 0; +} + int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { uint16_t cmdc = 0; diff --git a/client/cmdhf14a.h b/client/cmdhf14a.h index 71007f95..bbfd9b29 100644 --- a/client/cmdhf14a.h +++ b/client/cmdhf14a.h @@ -25,6 +25,7 @@ int CmdHF14ASnoop(const char *Cmd); char* getTagInfo(uint8_t uid); extern void DropField(); +extern int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); extern int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); #endif diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index e2a4ba1e..028bbf7a 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -27,6 +27,9 @@ #include "mifare.h" #include "mfkey.h" #include "hardnested/hardnested_bf_core.h" +#include "cliparser/cliparser.h" +#include "cmdhf14a.h" +#include #define NESTED_SECTOR_RETRY 10 // how often we try mfested() until we give up @@ -2634,6 +2637,149 @@ int CmdDecryptTraceCmds(const char *Cmd){ return tryDecryptWord(param_get32ex(Cmd,0,0,16),param_get32ex(Cmd,1,0,16),param_get32ex(Cmd,2,0,16),data,len/2); } +int aes_encode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length){ + uint8_t iiv[16] = {0}; + if (iv) + memcpy(iiv, iv, 16); + + aes_context aes; + aes_init(&aes); + if (aes_setkey_enc(&aes, key, 128)) + return 1; + if (aes_crypt_cbc(&aes, AES_ENCRYPT, length, iiv, input, output)) + return 2; + aes_free(&aes); + + return 0; +} + +int aes_decode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length){ + uint8_t iiv[16] = {0}; + if (iv) + memcpy(iiv, iv, 16); + + aes_context aes; + aes_init(&aes); + if (aes_setkey_dec(&aes, key, 128)) + return 1; + if (aes_crypt_cbc(&aes, AES_DECRYPT, length, iiv, input, output)) + return 2; + aes_free(&aes); + + return 0; +} + +int CmdHF14AMfAuth4(const char *cmd) { + uint8_t keyn[20] = {0}; + int keynlen = 0; + uint8_t key[16] = {0}; + int keylen = 0; + 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}; + + + CLIParserInit("hf mf auth4", + "Executes AES authentication command in ISO14443-4", + "Usage:\n\thf mf auth4 4000 000102030405060708090a0b0c0d0e0f -> executes authentication\n" + "\thf mf auth4 9003 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> executes authentication\n"); + + void* argtable[] = { + arg_param_begin, + arg_str1(NULL, NULL, "", NULL), + arg_str1(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + CLIGetStrWithReturn(1, keyn, &keynlen); + CLIGetStrWithReturn(2, key, &keylen); + CLIParserFree(); + + if (keynlen != 2) { + PrintAndLog("ERROR: must be 2 bytes long instead of: %d", keynlen); + return 1; + } + + if (keylen != 16) { + PrintAndLog("ERROR: must be 16 bytes long instead of: %d", keylen); + return 1; + } + + uint8_t cmd1[] = {0x0a, 0x00, 0x70, keyn[1], keyn[0], 0x00}; + int res = ExchangeRAW14a(cmd1, sizeof(cmd1), true, true, data, sizeof(data), &datalen); + if (res) { + PrintAndLog("ERROR exchande raw error: %d", res); + return 2; + } + + PrintAndLog("phase2: %s", sprint_hex(cmd2, 35)); + + res = ExchangeRAW14a(cmd2, sizeof(cmd2), false, false, data, sizeof(data), &datalen); + if (res) { + PrintAndLog("ERROR exchande raw error: %d", res); + DropField(); + return 4; + } + + PrintAndLog(" Date: Thu, 11 Oct 2018 20:28:29 +0300 Subject: [PATCH 298/310] small fix in auth (#693) --- client/cmdhfmf.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 028bbf7a..5efb4a41 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -2712,6 +2712,7 @@ int CmdHF14AMfAuth4(const char *cmd) { int res = ExchangeRAW14a(cmd1, sizeof(cmd1), true, true, data, sizeof(data), &datalen); if (res) { PrintAndLog("ERROR exchande raw error: %d", res); + DropField(); return 2; } @@ -2719,21 +2720,25 @@ int CmdHF14AMfAuth4(const char *cmd) { if (datalen < 3) { PrintAndLog("ERROR: card response length: %d", datalen); + DropField(); return 3; } if (data[0] != 0x0a || data[1] != 0x00) { PrintAndLog("ERROR: card response. Framing error. :%s", sprint_hex(data, 2)); + DropField(); return 3; } if (data[2] != 0x90) { PrintAndLog("ERROR: card response error: %02x", data[2]); + DropField(); return 3; } if (datalen != 19) { PrintAndLog("ERROR: card response must be 16 bytes long instead of: %d", datalen); + DropField(); return 3; } From 54e3cfcb74fbc02f7d1d16be8cd5338529acd58e Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Fri, 12 Oct 2018 15:13:58 +0300 Subject: [PATCH 299/310] small improvements in auth (#694) --- client/cmdhf14a.c | 16 +++++++++++++--- client/cmdhfmf.c | 30 +++++++++++------------------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index a5de2e2a..63b1cda6 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -649,10 +649,12 @@ void DropField() { } int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { + static bool responseNum = false; uint16_t cmdc = 0; *dataoutlen = 0; if (activateField) { + responseNum = false; UsbCommand resp; // Anticollision + SELECT card @@ -695,8 +697,11 @@ int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leav if (leaveSignalON) cmdc |= ISO14A_NO_DISCONNECT; - UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_RAW | ISO14A_APPEND_CRC | cmdc, (datainlen & 0xFFFF), 0}}; - memcpy(c.d.asBytes, datain, datainlen); + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_RAW | ISO14A_APPEND_CRC | cmdc, (datainlen & 0xFFFF) + 2, 0}}; + uint8_t header[] = {0x0a | responseNum, 0x00}; + responseNum ^= 1; + memcpy(c.d.asBytes, header, 2); + memcpy(&c.d.asBytes[2], datain, datainlen); SendCommand(&c); uint8_t *recv; @@ -715,7 +720,12 @@ int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leav return 2; } - memcpy(dataout, recv, *dataoutlen); + if (recv[0] != header[0]) { + PrintAndLog("14aRAW ERROR: iso14443-4 framing error. Card send %2x must be %2x", dataout[0], header[0]); + return 2; + } + + memcpy(dataout, &recv[2], *dataoutlen); if(!iLen) { PrintAndLog("14aRAW ERROR: No card response."); diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 5efb4a41..eb85b8c5 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -2708,7 +2708,7 @@ int CmdHF14AMfAuth4(const char *cmd) { return 1; } - uint8_t cmd1[] = {0x0a, 0x00, 0x70, keyn[1], keyn[0], 0x00}; + uint8_t cmd1[] = {0x70, keyn[1], keyn[0], 0x00}; int res = ExchangeRAW14a(cmd1, sizeof(cmd1), true, true, data, sizeof(data), &datalen); if (res) { PrintAndLog("ERROR exchande raw error: %d", res); @@ -2718,45 +2718,37 @@ int CmdHF14AMfAuth4(const char *cmd) { PrintAndLog("phase2: %s", sprint_hex(cmd2, 35)); + aes_encode(NULL, key, raw, &cmd2[1], 32); + PrintAndLog(">phase2: %s", sprint_hex(cmd2, 33)); res = ExchangeRAW14a(cmd2, sizeof(cmd2), false, false, data, sizeof(data), &datalen); if (res) { @@ -2767,7 +2759,7 @@ int CmdHF14AMfAuth4(const char *cmd) { PrintAndLog(" Date: Wed, 17 Oct 2018 21:53:34 +0300 Subject: [PATCH 300/310] Emv scan (#691) * added `hf emv scan` command and options * add tlv tag save to json * added tlv tree navigation * added kernel id and moved some parts of code in ppse * save gpo result * added read records * extract childs from tlv works * added application data list * added work with application data section * flag --extract works * refactoring: move json functions to emvjson.h/c * added path.c to jansson * refactoring: move ParamLoadFromJson * refactoring: move defparams.json to tag-name-value structure * refactoring and add key recovering * added some codes to appdata list * refactoring: process response format 1 from GPO * added save mode * added RID to app data * add file name handling and small refactoring in argtable string processing * added finalization logic to `emv scan` and option to remove hash checking in key recovery --- client/Makefile | 1 + client/cliparser/cliparser.c | 45 ++- client/cliparser/cliparser.h | 6 +- client/cmdhf14a.c | 41 ++- client/cmdhf14a.h | 3 + client/emv/cmdemv.c | 608 +++++++++++++++++++++++------------ client/emv/defparams.json | 16 +- client/emv/emv_pki.c | 14 +- client/emv/emv_pki.h | 2 + client/emv/emv_tags.c | 14 + client/emv/emv_tags.h | 1 + client/emv/emvcore.c | 68 ++++ client/emv/emvcore.h | 4 + client/emv/emvjson.c | 355 ++++++++++++++++++++ client/emv/emvjson.h | 35 ++ client/emv/tlv.c | 19 ++ client/emv/tlv.h | 5 + client/jansson/Makefile | 1 + client/jansson/jansson.h | 10 + client/jansson/path.c | 202 ++++++++++++ 20 files changed, 1209 insertions(+), 241 deletions(-) create mode 100644 client/emv/emvjson.c create mode 100644 client/emv/emvjson.h create mode 100644 client/jansson/path.c diff --git a/client/Makefile b/client/Makefile index 9e5bde30..f1007b89 100644 --- a/client/Makefile +++ b/client/Makefile @@ -140,6 +140,7 @@ CMDSRCS = $(SRC_SMARTCARD) \ emv/tlv.c\ emv/emv_tags.c\ emv/dol.c\ + emv/emvjson.c\ emv/emvcore.c\ emv/test/crypto_test.c\ emv/test/sda_test.c\ diff --git a/client/cliparser/cliparser.c b/client/cliparser/cliparser.c index 56be2ca6..931d68cd 100644 --- a/client/cliparser/cliparser.c +++ b/client/cliparser/cliparser.c @@ -153,23 +153,14 @@ void CLIParserFree() { // convertors int CLIParamHexToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen) { *datalen = 0; - if (!argstr->count) - return 0; - char buf[256] = {0}; int ibuf = 0; + uint8_t buf[256] = {0}; + int res = CLIParamStrToBuf(argstr, buf, maxdatalen, &ibuf); + if (res || !ibuf) + return res; - for (int i = 0; i < argstr->count; i++) { - int len = strlen(argstr->sval[i]); - memcpy(&buf[ibuf], argstr->sval[i], len); - ibuf += len; - } - buf[ibuf] = 0; - - if (!ibuf) - return 0; - - switch(param_gethex_to_eol(buf, 0, data, maxdatalen, datalen)) { + switch(param_gethex_to_eol((char *)buf, 0, data, maxdatalen, datalen)) { case 1: printf("Parameter error: Invalid HEX value.\n"); return 1; @@ -184,5 +175,31 @@ int CLIParamHexToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int return 0; } +int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen) { + *datalen = 0; + if (!argstr->count) + return 0; + + uint8_t buf[256] = {0}; + int ibuf = 0; + + for (int i = 0; i < argstr->count; i++) { + int len = strlen(argstr->sval[i]); + memcpy(&buf[ibuf], argstr->sval[i], len); + ibuf += len; + } + buf[ibuf] = 0; + + if (!ibuf) + return 0; + + if (ibuf > maxdatalen) + return 2; + + memcpy(data, buf, ibuf); + *datalen = ibuf; + + return 0; +} diff --git a/client/cliparser/cliparser.h b/client/cliparser/cliparser.h index 7c1ced20..2f5ac317 100644 --- a/client/cliparser/cliparser.h +++ b/client/cliparser/cliparser.h @@ -25,8 +25,9 @@ #define arg_strx0(shortopts, longopts, datatype, glossary) (arg_strn((shortopts), (longopts), (datatype), 0, 250, (glossary))) #define CLIExecWithReturn(cmd, atbl, ifempty) if (CLIParserParseString(cmd, atbl, arg_getsize(atbl), ifempty)){CLIParserFree();return 0;} -#define CLIGetStrBLessWithReturn(paramnum, data, datalen, delta) if (CLIParamHexToBuf(arg_get_str(paramnum), data, sizeof(data) - (delta), datalen)) {CLIParserFree();return 1;} -#define CLIGetStrWithReturn(paramnum, data, datalen) if (CLIParamHexToBuf(arg_get_str(paramnum), data, sizeof(data), datalen)) {CLIParserFree();return 1;} +#define CLIGetHexBLessWithReturn(paramnum, data, datalen, delta) if (CLIParamHexToBuf(arg_get_str(paramnum), data, sizeof(data) - (delta), datalen)) {CLIParserFree();return 1;} +#define CLIGetHexWithReturn(paramnum, data, datalen) if (CLIParamHexToBuf(arg_get_str(paramnum), data, sizeof(data), datalen)) {CLIParserFree();return 1;} +#define CLIGetStrWithReturn(paramnum, data, datalen) if (CLIParamStrToBuf(arg_get_str(paramnum), data, sizeof(data), datalen)) {CLIParserFree();return 1;} extern int CLIParserInit(char *vprogramName, char *vprogramHint, char *vprogramHelp); extern int CLIParserParseString(const char* str, void* argtable[], size_t vargtableLen, bool allowEmptyExec); @@ -35,3 +36,4 @@ extern int CLIParserParseArg(int argc, char **argv, void* argtable[], size_t var extern void CLIParserFree(); extern int CLIParamHexToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen); +extern int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen); diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 63b1cda6..31654188 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -136,6 +136,45 @@ int CmdHF14AList(const char *Cmd) return 0; } +int Hf14443_4aGetCardData(iso14a_card_select_t * card) { + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT, 0, 0}}; + SendCommand(&c); + + UsbCommand resp; + WaitForResponse(CMD_ACK,&resp); + + memcpy(card, (iso14a_card_select_t *)resp.d.asBytes, sizeof(iso14a_card_select_t)); + + uint64_t select_status = resp.arg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision + + if(select_status == 0) { + PrintAndLog("E->iso14443a card select failed"); + return 1; + } + + if(select_status == 2) { + PrintAndLog("E->Card doesn't support iso14443-4 mode"); + return 1; + } + + if(select_status == 3) { + PrintAndLog("E->Card doesn't support standard iso14443-3 anticollision"); + PrintAndLog("\tATQA : %02x %02x", card->atqa[1], card->atqa[0]); + return 1; + } + + PrintAndLog(" UID: %s", sprint_hex(card->uid, card->uidlen)); + PrintAndLog("ATQA: %02x %02x", card->atqa[1], card->atqa[0]); + PrintAndLog(" SAK: %02x [%" PRIu64 "]", card->sak, resp.arg[0]); + if(card->ats_len < 3) { // a valid ATS consists of at least the length byte (TL) and 2 CRC bytes + PrintAndLog("E-> Error ATS length(%d) : %s", card->ats_len, sprint_hex(card->ats, card->ats_len)); + return 1; + } + PrintAndLog(" ATS: %s", sprint_hex(card->ats, card->ats_len)); + + return 0; +} + int CmdHF14AReader(const char *Cmd) { uint32_t cm = ISO14A_CONNECT; bool leaveSignalON = false; @@ -850,7 +889,7 @@ int CmdHF14AAPDU(const char *cmd) { leaveSignalON = arg_get_lit(2); decodeTLV = arg_get_lit(3); // len = data + PCB(1b) + CRC(2b) - CLIGetStrBLessWithReturn(4, data, &datalen, 1 + 2); + CLIGetHexBLessWithReturn(4, data, &datalen, 1 + 2); CLIParserFree(); diff --git a/client/cmdhf14a.h b/client/cmdhf14a.h index bbfd9b29..d961d570 100644 --- a/client/cmdhf14a.h +++ b/client/cmdhf14a.h @@ -14,6 +14,7 @@ #include #include +#include "mifare.h" int CmdHF14A(const char *Cmd); int CmdHF14AList(const char *Cmd); @@ -25,6 +26,8 @@ int CmdHF14ASnoop(const char *Cmd); char* getTagInfo(uint8_t uid); extern void DropField(); + +extern int Hf14443_4aGetCardData(iso14a_card_select_t * card); extern int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); extern int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index 6bac90d6..d886834d 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -9,36 +9,14 @@ //----------------------------------------------------------------------------- #include +#include "mifare.h" #include "cmdemv.h" +#include "emvjson.h" +#include "emv_pki.h" #include "test/cryptotest.h" #include "cliparser/cliparser.h" #include -bool HexToBuffer(const char *errormsg, const char *hexvalue, uint8_t * buffer, size_t maxbufferlen, size_t *bufferlen) { - int buflen = 0; - - switch(param_gethex_to_eol(hexvalue, 0, buffer, maxbufferlen, &buflen)) { - case 1: - PrintAndLog("%s Invalid HEX value.", errormsg); - return false; - case 2: - PrintAndLog("%s Hex value too large.", errormsg); - return false; - case 3: - PrintAndLog("%s Hex value must have even number of digits.", errormsg); - return false; - } - - if (buflen > maxbufferlen) { - PrintAndLog("%s HEX length (%d) more than %d", errormsg, *bufferlen, maxbufferlen); - return false; - } - - *bufferlen = buflen; - - return true; -} - #define TLV_ADD(tag, value)( tlvdb_change_or_add_node(tlvRoot, tag, sizeof(value) - 1, (const unsigned char *)value) ) void ParamLoadDefaults(struct tlvdb *tlvRoot) { //9F02:(Amount, authorized (Numeric)) len:6 @@ -60,111 +38,6 @@ void ParamLoadDefaults(struct tlvdb *tlvRoot) { TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC } -bool ParamLoadFromJson(struct tlvdb *tlv) { - json_t *root; - json_error_t error; - - if (!tlv) { - PrintAndLog("ERROR load params: tlv tree is NULL."); - return false; - } - - // current path + file name - const char *relfname = "emv/defparams.json"; - char fname[strlen(get_my_executable_directory()) + strlen(relfname) + 1]; - strcpy(fname, get_my_executable_directory()); - strcat(fname, relfname); - - root = json_load_file(fname, 0, &error); - if (!root) { - PrintAndLog("Load params: json error on line %d: %s", error.line, error.text); - return false; - } - - if (!json_is_array(root)) { - PrintAndLog("Load params: Invalid json format. root must be array."); - return false; - } - - PrintAndLog("Load params: json OK"); - - for(int i = 0; i < json_array_size(root); i++) { - json_t *data, *jtype, *jlength, *jvalue; - - data = json_array_get(root, i); - if(!json_is_object(data)) - { - PrintAndLog("Load params: data [%d] is not an object", i + 1); - json_decref(root); - return false; - } - - jtype = json_object_get(data, "type"); - if(!json_is_string(jtype)) - { - PrintAndLog("Load params: data [%d] type is not a string", i + 1); - json_decref(root); - return false; - } - const char *tlvType = json_string_value(jtype); - - jvalue = json_object_get(data, "value"); - if(!json_is_string(jvalue)) - { - PrintAndLog("Load params: data [%d] value is not a string", i + 1); - json_decref(root); - return false; - } - const char *tlvValue = json_string_value(jvalue); - - jlength = json_object_get(data, "length"); - if(!json_is_number(jlength)) - { - PrintAndLog("Load params: data [%d] length is not a number", i + 1); - json_decref(root); - return false; - } - - int tlvLength = json_integer_value(jlength); - if (tlvLength > 250) { - PrintAndLog("Load params: data [%d] length more than 250", i + 1); - json_decref(root); - return false; - } - - PrintAndLog("TLV param: %s[%d]=%s", tlvType, tlvLength, tlvValue); - uint8_t buf[251] = {0}; - size_t buflen = 0; - - // here max length must be 4, but now tlv_tag_t is 2-byte var. so let it be 2 by now... TODO: needs refactoring tlv_tag_t... - if (!HexToBuffer("TLV Error type:", tlvType, buf, 2, &buflen)) { - json_decref(root); - return false; - } - tlv_tag_t tag = 0; - for (int i = 0; i < buflen; i++) { - tag = (tag << 8) + buf[i]; - } - - if (!HexToBuffer("TLV Error value:", tlvValue, buf, sizeof(buf) - 1, &buflen)) { - json_decref(root); - return false; - } - - if (buflen != tlvLength) { - PrintAndLog("Load params: data [%d] length of HEX must(%d) be identical to length in TLV param(%d)", i + 1, buflen, tlvLength); - json_decref(root); - return false; - } - - tlvdb_change_or_add_node(tlv, tag, tlvLength, (const unsigned char *)buf); - } - - json_decref(root); - - return true; -} - int CmdHFEMVSelect(const char *cmd) { uint8_t data[APDU_AID_LEN] = {0}; int datalen = 0; @@ -188,7 +61,7 @@ int CmdHFEMVSelect(const char *cmd) { bool leaveSignalON = arg_get_lit(2); bool APDULogging = arg_get_lit(3); bool decodeTLV = arg_get_lit(4); - CLIGetStrWithReturn(5, data, &datalen); + CLIGetHexWithReturn(5, data, &datalen); CLIParserFree(); SetAPDULogging(APDULogging); @@ -333,7 +206,7 @@ int CmdHFEMVGPO(const char *cmd) { bool dataMakeFromPDOL = arg_get_lit(3); bool APDULogging = arg_get_lit(4); bool decodeTLV = arg_get_lit(5); - CLIGetStrWithReturn(6, data, &datalen); + CLIGetHexWithReturn(6, data, &datalen); CLIParserFree(); SetAPDULogging(APDULogging); @@ -422,7 +295,7 @@ int CmdHFEMVReadRecord(const char *cmd) { bool leaveSignalON = arg_get_lit(1); bool APDULogging = arg_get_lit(2); bool decodeTLV = arg_get_lit(3); - CLIGetStrWithReturn(4, data, &datalen); + CLIGetHexWithReturn(4, data, &datalen); CLIParserFree(); if (datalen != 2) { @@ -500,7 +373,7 @@ int CmdHFEMVAC(const char *cmd) { bool dataMakeFromCDOL = arg_get_lit(5); bool APDULogging = arg_get_lit(6); bool decodeTLV = arg_get_lit(7); - CLIGetStrWithReturn(8, data, &datalen); + CLIGetHexWithReturn(8, data, &datalen); CLIParserFree(); SetAPDULogging(APDULogging); @@ -629,7 +502,7 @@ int CmdHFEMVInternalAuthenticate(const char *cmd) { bool dataMakeFromDDOL = arg_get_lit(3); bool APDULogging = arg_get_lit(4); bool decodeTLV = arg_get_lit(5); - CLIGetStrWithReturn(6, data, &datalen); + CLIGetHexWithReturn(6, data, &datalen); CLIParserFree(); SetAPDULogging(APDULogging); @@ -693,6 +566,69 @@ int CmdHFEMVInternalAuthenticate(const char *cmd) { #define dreturn(n) {free(pdol_data_tlv);tlvdb_free(tlvSelect);tlvdb_free(tlvRoot);DropField();return n;} +void InitTransactionParameters(struct tlvdb *tlvRoot, bool paramLoadJSON, enum TransactionType TrType, bool GenACGPO) { + + ParamLoadDefaults(tlvRoot); + + if (paramLoadJSON) { + PrintAndLog("* * Transaction parameters loading from JSON..."); + ParamLoadFromJson(tlvRoot); + } + + //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 + char *qVSDC = "\x26\x00\x00\x00"; + if (GenACGPO) { + qVSDC = "\x26\x80\x00\x00"; + } + switch(TrType) { + case TT_MSD: + TLV_ADD(0x9F66, "\x86\x00\x00\x00"); // MSD + break; + // not standard for contactless. just for test. + case TT_VSDC: + TLV_ADD(0x9F66, "\x46\x00\x00\x00"); // VSDC + break; + case TT_QVSDCMCHIP: + TLV_ADD(0x9F66, qVSDC); // qVSDC + break; + case TT_CDA: + TLV_ADD(0x9F66, qVSDC); // qVSDC (VISA CDA not enabled) + break; + default: + break; + } +} + +void ProcessGPOResponseFormat1(struct tlvdb *tlvRoot, uint8_t *buf, size_t len, bool decodeTLV) { + if (buf[0] == 0x80) { + if (decodeTLV){ + PrintAndLog("GPO response format1:"); + TLVPrintFromBuffer(buf, len); + } + + if (len < 4 || (len - 4) % 4) { + PrintAndLog("ERROR: GPO response format1 parsing error. length=%d", len); + } else { + // AIP + struct tlvdb * f1AIP = tlvdb_fixed(0x82, 2, buf + 2); + tlvdb_add(tlvRoot, f1AIP); + if (decodeTLV){ + PrintAndLog("\n* * Decode response format 1 (0x80) AIP and AFL:"); + TLVPrintFromTLV(f1AIP); + } + + // AFL + struct tlvdb * f1AFL = tlvdb_fixed(0x94, len - 4, buf + 2 + 2); + tlvdb_add(tlvRoot, f1AFL); + if (decodeTLV) + TLVPrintFromTLV(f1AFL); + } + } else { + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + } +} + int CmdHFEMVExec(const char *cmd) { uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; @@ -805,37 +741,7 @@ int CmdHFEMVExec(const char *cmd) { PrintAndLog("* Selected."); PrintAndLog("\n* Init transaction parameters."); - - ParamLoadDefaults(tlvRoot); - - if (paramLoadJSON) { - PrintAndLog("* * Transaction parameters loading from JSON..."); - ParamLoadFromJson(tlvRoot); - } - - //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 - char *qVSDC = "\x26\x00\x00\x00"; - if (GenACGPO) { - qVSDC = "\x26\x80\x00\x00"; - } - switch(TrType) { - case TT_MSD: - TLV_ADD(0x9F66, "\x86\x00\x00\x00"); // MSD - break; - // not standard for contactless. just for test. - case TT_VSDC: - TLV_ADD(0x9F66, "\x46\x00\x00\x00"); // VSDC - break; - case TT_QVSDCMCHIP: - TLV_ADD(0x9F66, qVSDC); // qVSDC - break; - case TT_CDA: - TLV_ADD(0x9F66, qVSDC); // qVSDC (VISA CDA not enabled) - break; - default: - break; - } - + InitTransactionParameters(tlvRoot, paramLoadJSON, TrType, GenACGPO); TLVPrintFromTLV(tlvRoot); // TODO delete!!! PrintAndLog("\n* Calc PDOL."); @@ -865,33 +771,7 @@ int CmdHFEMVExec(const char *cmd) { } // process response template format 1 [id:80 2b AIP + x4b AFL] and format 2 [id:77 TLV] - if (buf[0] == 0x80) { - if (decodeTLV){ - PrintAndLog("GPO response format1:"); - TLVPrintFromBuffer(buf, len); - } - - if (len < 4 || (len - 4) % 4) { - PrintAndLog("ERROR: GPO response format1 parsing error. length=%d", len); - } else { - // AIP - struct tlvdb * f1AIP = tlvdb_fixed(0x82, 2, buf + 2); - tlvdb_add(tlvRoot, f1AIP); - if (decodeTLV){ - PrintAndLog("\n* * Decode response format 1 (0x80) AIP and AFL:"); - TLVPrintFromTLV(f1AIP); - } - - // AFL - struct tlvdb * f1AFL = tlvdb_fixed(0x94, len - 4, buf + 2 + 2); - tlvdb_add(tlvRoot, f1AFL); - if (decodeTLV) - TLVPrintFromTLV(f1AFL); - } - } else { - if (decodeTLV) - TLVPrintFromBuffer(buf, len); - } + ProcessGPOResponseFormat1(tlvRoot, buf, len, decodeTLV); // extract PAN from track2 { @@ -1191,26 +1071,328 @@ int CmdHFEMVExec(const char *cmd) { return 0; } -int UsageCmdHFEMVScan(void) { - PrintAndLog("HELP : Scan EMV card and save it contents to a file. \n"); - PrintAndLog(" It executes EMV contactless transaction and saves result to a file which can be used for emulation.\n"); - PrintAndLog("Usage: hf emv scan [-a][-t][-v][-c][-x][-g] \n"); - PrintAndLog(" Options:"); - PrintAndLog(" -a : show APDU reqests and responses\n"); - PrintAndLog(" -t : TLV decode results\n"); - PrintAndLog(" -v : transaction type - qVSDC or M/Chip.\n"); - PrintAndLog(" -c : transaction type - qVSDC or M/Chip plus CDA (SDAD generation).\n"); - PrintAndLog(" -x : transaction type - VSDC. For test only. Not a standart behavior.\n"); - PrintAndLog(" -g : VISA. generate AC from GPO\n"); - PrintAndLog("By default : transaction type - MSD.\n"); - PrintAndLog("Samples:"); - PrintAndLog(" hf emv scan -a -t -> scan MSD transaction mode"); - PrintAndLog(" hf emv scan -a -t -c -> scan CDA transaction mode"); - return 0; -} - int CmdHFEMVScan(const char *cmd) { - UsageCmdHFEMVScan(); + uint8_t AID[APDU_AID_LEN] = {0}; + size_t AIDlen = 0; + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + int res; + json_t *root; + json_error_t error; + + CLIParserInit("hf emv scan", + "Scan EMV card and save it contents to a file.", + "It executes EMV contactless transaction and saves result to a file which can be used for emulation\n" + "Usage:\n\thf emv scan -at -> scan MSD transaction mode and show APDU and TLV\n" + "\thf emv scan -c -> scan CDA transaction mode\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("aA", "apdu", "show APDU reqests and responses."), + arg_lit0("tT", "tlv", "TLV decode results."), + arg_lit0("eE", "extract", "Extract TLV elements and fill Application Data"), + arg_lit0("jJ", "jload", "Load transaction parameters from `emv/defparams.json` file."), + arg_rem("By default:", "Transaction type - MSD"), + arg_lit0("vV", "qvsdc", "Transaction type - qVSDC or M/Chip."), + arg_lit0("cC", "qvsdccda", "Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)."), + arg_lit0("xX", "vsdc", "Transaction type - VSDC. For test only. Not a standart behavior."), + arg_lit0("gG", "acgpo", "VISA. generate AC from GPO."), + arg_lit0("mM", "merge", "Merge output file with card's data. (warning: the file may be corrupted!)"), + arg_str1(NULL, NULL, "output.json", "JSON output file name"), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool showAPDU = arg_get_lit(1); + bool decodeTLV = arg_get_lit(2); + bool extractTLVElements = arg_get_lit(3); + bool paramLoadJSON = arg_get_lit(4); + + enum TransactionType TrType = TT_MSD; + if (arg_get_lit(6)) + TrType = TT_QVSDCMCHIP; + if (arg_get_lit(7)) + TrType = TT_CDA; + if (arg_get_lit(8)) + TrType = TT_VSDC; + + bool GenACGPO = arg_get_lit(9); + bool MergeJSON = arg_get_lit(10); + uint8_t relfname[250] ={0}; + char *crelfname = (char *)relfname; + int relfnamelen = 0; + CLIGetStrWithReturn(11, relfname, &relfnamelen); + CLIParserFree(); + + SetAPDULogging(showAPDU); + + // current path + file name + if (!strstr(crelfname, ".json")) + strcat(crelfname, ".json"); + char fname[strlen(get_my_executable_directory()) + strlen(crelfname) + 1]; + strcpy(fname, get_my_executable_directory()); + strcat(fname, crelfname); + + if (MergeJSON) { + root = json_load_file(fname, 0, &error); + if (!root) { + PrintAndLog("ERROR: json error on line %d: %s", error.line, error.text); + return 1; + } + + if (!json_is_object(root)) { + PrintAndLog("ERROR: Invalid json format. root must be an object."); + return 1; + } + } else { + root = json_object(); + } + + // drop field at start + DropField(); + + // iso 14443 select + PrintAndLog("--> GET UID, ATS."); + + iso14a_card_select_t card; + if (Hf14443_4aGetCardData(&card)) { + return 2; + } + + JsonSaveStr(root, "$.File.Created", "proxmark3 `hf emv scan`"); + + JsonSaveStr(root, "$.Card.Communication", "iso14443-4a"); + JsonSaveBufAsHex(root, "$.Card.UID", (uint8_t *)&card.uid, card.uidlen); + JsonSaveHex(root, "$.Card.ATQA", card.atqa[0] + (card.atqa[1] << 2), 2); + JsonSaveHex(root, "$.Card.SAK", card.sak, 0); + JsonSaveBufAsHex(root, "$.Card.ATS", (uint8_t *)card.ats, card.ats_len); + + // init applets list tree + const char *al = "Applets list"; + struct tlvdb *tlvSelect = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); + + // EMV PPSE + PrintAndLog("--> PPSE."); + res = EMVSelectPSE(true, true, 2, buf, sizeof(buf), &len, &sw); + + if (!res && sw == 0x9000){ + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + JsonSaveBufAsHex(root, "$.PPSE.AID", (uint8_t *)"2PAY.SYS.DDF01", 14); + + struct tlvdb *fci = tlvdb_parse_multi(buf, len); + if (extractTLVElements) + JsonSaveTLVTree(root, root, "$.PPSE.FCITemplate", fci); + else + JsonSaveTLVTreeElm(root, "$.PPSE.FCITemplate", fci, true, true, false); + JsonSaveTLVValue(root, "$.Application.KernelID", tlvdb_find_full(fci, 0x9f2a)); + tlvdb_free(fci); + } + + res = EMVSearchPSE(false, true, decodeTLV, tlvSelect); + + // check PPSE and select application id + if (!res) { + TLVPrintAIDlistFromSelectTLV(tlvSelect); + } else { + // EMV SEARCH with AID list + SetAPDULogging(false); + PrintAndLog("--> AID search."); + if (EMVSearch(false, true, decodeTLV, tlvSelect)) { + PrintAndLog("E->Can't found any of EMV AID. Exit..."); + tlvdb_free(tlvSelect); + DropField(); + return 3; + } + + // check search and select application id + TLVPrintAIDlistFromSelectTLV(tlvSelect); + } + + // EMV SELECT application + SetAPDULogging(showAPDU); + EMVSelectApplication(tlvSelect, AID, &AIDlen); + + tlvdb_free(tlvSelect); + + if (!AIDlen) { + PrintAndLog("Can't select AID. EMV AID not found. Exit..."); + DropField(); + return 4; + } + + JsonSaveBufAsHex(root, "$.Application.AID", AID, AIDlen); + + // Init TLV tree + const char *alr = "Root terminal TLV tree"; + struct tlvdb *tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr); + + // EMV SELECT applet + + PrintAndLog("\n-->Selecting AID:%s.", sprint_hex_inrow(AID, AIDlen)); + SetAPDULogging(showAPDU); + res = EMVSelect(false, true, AID, AIDlen, buf, sizeof(buf), &len, &sw, tlvRoot); + + if (res) { + PrintAndLog("E->Can't select AID (%d). Exit...", res); + tlvdb_free(tlvRoot); + DropField(); + return 5; + } + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + // save mode + if (tlvdb_get(tlvRoot, 0x9f38, NULL)) { + JsonSaveStr(root, "$.Application.Mode", TransactionTypeStr[TrType]); + } + + struct tlvdb *fci = tlvdb_parse_multi(buf, len); + if (extractTLVElements) + JsonSaveTLVTree(root, root, "$.Application.FCITemplate", fci); + else + JsonSaveTLVTreeElm(root, "$.Application.FCITemplate", fci, true, true, false); + tlvdb_free(fci); + + // create transaction parameters + PrintAndLog("-->Init transaction parameters."); + InitTransactionParameters(tlvRoot, paramLoadJSON, TrType, GenACGPO); + + PrintAndLog("-->Calc PDOL."); + struct tlv *pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83); + if (!pdol_data_tlv){ + PrintAndLog("E->Can't create PDOL TLV."); + tlvdb_free(tlvRoot); + DropField(); + return 6; + } + + size_t pdol_data_tlv_data_len; + unsigned char *pdol_data_tlv_data = tlv_encode(pdol_data_tlv, &pdol_data_tlv_data_len); + if (!pdol_data_tlv_data) { + PrintAndLog("E->Can't create PDOL data."); + tlvdb_free(tlvRoot); + DropField(); + return 6; + } + PrintAndLog("PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); + + PrintAndLog("-->GPO."); + res = EMVGPO(true, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); + + free(pdol_data_tlv_data); + free(pdol_data_tlv); + + if (res) { + PrintAndLog("GPO error(%d): %4x. Exit...", res, sw); + tlvdb_free(tlvRoot); + DropField(); + return 7; + } + ProcessGPOResponseFormat1(tlvRoot, buf, len, decodeTLV); + + struct tlvdb *gpofci = tlvdb_parse_multi(buf, len); + if (extractTLVElements) + JsonSaveTLVTree(root, root, "$.Application.GPO", gpofci); + else + JsonSaveTLVTreeElm(root, "$.Application.GPO", gpofci, true, true, false); + + JsonSaveTLVValue(root, "$.ApplicationData.AIP", tlvdb_find_full(gpofci, 0x82)); + JsonSaveTLVValue(root, "$.ApplicationData.AFL", tlvdb_find_full(gpofci, 0x94)); + + tlvdb_free(gpofci); + + PrintAndLog("-->Read records from AFL."); + const struct tlv *AFL = tlvdb_get(tlvRoot, 0x94, NULL); + + while(AFL && AFL->len) { + if (AFL->len % 4) { + PrintAndLog("E->Wrong AFL length: %d", AFL->len); + break; + } + + json_t *sfijson = json_path_get(root, "$.Application.Records"); + if (!sfijson) { + json_t *app = json_path_get(root, "$.Application"); + json_object_set_new(app, "Records", json_array()); + + sfijson = json_path_get(root, "$.Application.Records"); + } + if (!json_is_array(sfijson)) { + PrintAndLog("E->Internal logic error. `$.Application.Records` is not an array."); + break; + } + for (int i = 0; i < AFL->len / 4; i++) { + uint8_t SFI = AFL->value[i * 4 + 0] >> 3; + uint8_t SFIstart = AFL->value[i * 4 + 1]; + uint8_t SFIend = AFL->value[i * 4 + 2]; + uint8_t SFIoffline = AFL->value[i * 4 + 3]; + + PrintAndLog("--->SFI[%02x] start:%02x end:%02x offline:%02x", SFI, SFIstart, SFIend, SFIoffline); + if (SFI == 0 || SFI == 31 || SFIstart == 0 || SFIstart > SFIend) { + PrintAndLog("SFI ERROR! Skipped..."); + continue; + } + + for(int n = SFIstart; n <= SFIend; n++) { + PrintAndLog("---->SFI[%02x] %d", SFI, n); + + res = EMVReadRecord(true, SFI, n, buf, sizeof(buf), &len, &sw, tlvRoot); + if (res) { + PrintAndLog("E->SFI[%02x]. APDU error %4x", SFI, sw); + continue; + } + + if (decodeTLV) { + TLVPrintFromBuffer(buf, len); + PrintAndLog(""); + } + + json_t *jsonelm = json_object(); + json_array_append_new(sfijson, jsonelm); + + JsonSaveHex(jsonelm, "SFI", SFI, 1); + JsonSaveHex(jsonelm, "RecordNum", n, 1); + JsonSaveHex(jsonelm, "Offline", SFIoffline, 1); + + struct tlvdb *rsfi = tlvdb_parse_multi(buf, len); + if (extractTLVElements) + JsonSaveTLVTree(root, jsonelm, "$.Data", rsfi); + else + JsonSaveTLVTreeElm(jsonelm, "$.Data", rsfi, true, true, false); + tlvdb_free(rsfi); + } + } + + break; + } + + // getting certificates + if (tlvdb_get(tlvRoot, 0x90, NULL)) { + PrintAndLog("-->Recovering certificates."); + PKISetStrictExecution(false); + RecoveryCertificates(tlvRoot, root); + PKISetStrictExecution(true); + } + + // free tlv object + tlvdb_free(tlvRoot); + + // DropField + DropField(); + + res = json_dump_file(root, fname, JSON_INDENT(2)); + if (res) { + PrintAndLog("ERROR: can't save the file: %s", fname); + return 200; + } + PrintAndLog("File `%s` saved.", fname); + + // free json object + json_decref(root); return 0; } @@ -1231,7 +1413,7 @@ static command_t CommandTable[] = { {"genac", CmdHFEMVAC, 0, "Generate ApplicationCryptogram."}, {"challenge", CmdHFEMVGenerateChallenge, 0, "Generate challenge."}, {"intauth", CmdHFEMVInternalAuthenticate, 0, "Internal authentication."}, -// {"scan", CmdHFEMVScan, 0, "Scan EMV card and save it contents to json file for emulator."}, + {"scan", CmdHFEMVScan, 0, "Scan EMV card and save it contents to json file for emulator."}, {"test", CmdHFEMVTest, 0, "Crypto logic test."}, {NULL, NULL, 0, NULL} }; diff --git a/client/emv/defparams.json b/client/emv/defparams.json index 47f1c8a1..e615d323 100644 --- a/client/emv/defparams.json +++ b/client/emv/defparams.json @@ -1,56 +1,56 @@ [ { "name": "Transaction Date", - "type": "9A", + "tag": "9A", "value": "00 00 00", "length": 3, "hint": "format: YYMMDD" }, { "name": "Transaction Type", - "type": "9C", + "tag": "9C", "value": "00", "length": 1, "hint": "00: Goods and service, 01: Cash" }, { "name": "Amount, authorized", - "type": "9F 02", + "tag": "9F 02", "value": "00 00 00 00 01 00", "length": 6, "hint": "amount (numberic) in cents" }, { "name": "Transaction Currency Code", - "type": "5F 2A", + "tag": "5F 2A", "value": "09 80", "length": 2, "hint": "USD 840, EUR 978, RUB 643, RUR 810(old), UAH 980, AZN 031, n/a 999" }, { "name": "Terminal Country Code", - "type": "9F 1A", + "tag": "9F 1A", "value": "72 75", "length": 2, "hint": "ISO3166: de, en (65 6e), uk(75 6b), ru (72 75), us, ua" }, { "name": "Terminal Transaction Qualifiers (TTQ)", - "type": "9F 66", + "tag": "9F 66", "value": "26 00 00 00", "length": 4, "hint": "qVSDC 26 00 00 00, gen AC from GPO 26 80 00 00, MSD 86 00 00 00, VSDC 46 00 00 00" }, { "name": "Unpredictable Number", - "type": "9F 37", + "tag": "9F 37", "value": "01 02 03 04", "length": 4, "hint": "4 byte random number" }, { "name": "Unpredictable Number (MSD for UDOL)", - "type": "9F 6A", + "tag": "9F 6A", "value": "01 02 03 05", "length": 4, "hint": "4 byte random number" diff --git a/client/emv/emv_pki.c b/client/emv/emv_pki.c index f79e3045..da102291 100644 --- a/client/emv/emv_pki.c +++ b/client/emv/emv_pki.c @@ -27,6 +27,11 @@ #include #include +static bool strictExecution = true; +void PKISetStrictExecution(bool se) { + strictExecution = se; +} + static const unsigned char empty_tlv_value[] = {}; static const struct tlv empty_tlv = {.tag = 0x0, .len = 0, .value = empty_tlv_value}; @@ -108,9 +113,12 @@ static unsigned char *emv_pki_decode_message(const struct emv_pk *enc_pk, printf("ERROR: Calculated wrong hash\n"); printf("decoded: %s\n",sprint_hex(data + data_len - 1 - hash_len, hash_len)); printf("calculated: %s\n",sprint_hex(crypto_hash_read(ch), hash_len)); - crypto_hash_close(ch); - free(data); - return NULL; + + if (strictExecution) { + crypto_hash_close(ch); + free(data); + return NULL; + } } crypto_hash_close(ch); diff --git a/client/emv/emv_pki.h b/client/emv/emv_pki.h index e37e3c7d..6fa7b12e 100644 --- a/client/emv/emv_pki.h +++ b/client/emv/emv_pki.h @@ -21,6 +21,8 @@ #include +extern void PKISetStrictExecution(bool se); + struct emv_pk *emv_pki_recover_issuer_cert(const struct emv_pk *pk, struct tlvdb *db); struct emv_pk *emv_pki_recover_icc_cert(const struct emv_pk *pk, struct tlvdb *db, const struct tlv *sda_tlv); struct emv_pk *emv_pki_recover_icc_pe_cert(const struct emv_pk *pk, struct tlvdb *db); diff --git a/client/emv/emv_tags.c b/client/emv/emv_tags.c index 02b039f8..eed77e76 100644 --- a/client/emv/emv_tags.c +++ b/client/emv/emv_tags.c @@ -686,3 +686,17 @@ bool emv_tag_dump(const struct tlv *tlv, FILE *f, int level) return true; } + +char *emv_get_tag_name(const struct tlv *tlv) +{ + static char *defstr = ""; + + if (!tlv) + return defstr; + + const struct emv_tag *tag = emv_get_tag(tlv); + if (tag) + return tag->name; + + return defstr; +} diff --git a/client/emv/emv_tags.h b/client/emv/emv_tags.h index f6d44f01..246fc72d 100644 --- a/client/emv/emv_tags.h +++ b/client/emv/emv_tags.h @@ -31,5 +31,6 @@ # define EMVCID_REASON_MASK 0x07 bool emv_tag_dump(const struct tlv *tlv, FILE *f, int level); +char *emv_get_tag_name(const struct tlv *tlv); #endif diff --git a/client/emv/emvcore.c b/client/emv/emvcore.c index 36f6f8eb..f9dd0cbe 100644 --- a/client/emv/emvcore.c +++ b/client/emv/emvcore.c @@ -9,6 +9,7 @@ //----------------------------------------------------------------------------- #include "emvcore.h" +#include "emvjson.h" // Got from here. Thanks) // https://eftlab.co.uk/index.php/site-map/knowledge-base/211-emv-aid-rid-pix @@ -18,6 +19,13 @@ static const char *PSElist [] = { }; //static const size_t PSElistLen = sizeof(PSElist)/sizeof(char*); +char *TransactionTypeStr[] = { + "MSD", + "VSDC", + "qVCDCMCHIP", + "CDA" +}; + typedef struct { enum CardPSVendor vendor; const char* aid; @@ -849,3 +857,63 @@ int trCDA(struct tlvdb *tlv, struct tlvdb *ac_tlv, struct tlv *pdol_data_tlv, st emv_pk_free(icc_pk); return 0; } + +int RecoveryCertificates(struct tlvdb *tlvRoot, json_t *root) { + + struct emv_pk *pk = get_ca_pk(tlvRoot); + if (!pk) { + PrintAndLog("ERROR: Key not found. Exit."); + return 1; + } + + struct emv_pk *issuer_pk = emv_pki_recover_issuer_cert(pk, tlvRoot); + if (!issuer_pk) { + emv_pk_free(pk); + PrintAndLog("WARNING: Issuer certificate not found. Exit."); + return 2; + } + PrintAndLog("Issuer PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx", + issuer_pk->rid[0], + issuer_pk->rid[1], + issuer_pk->rid[2], + issuer_pk->rid[3], + issuer_pk->rid[4], + issuer_pk->index, + issuer_pk->serial[0], + issuer_pk->serial[1], + issuer_pk->serial[2] + ); + + JsonSaveBufAsHex(root, "$.ApplicationData.RID", issuer_pk->rid, 5); + + char *issuer_pk_c = emv_pk_dump_pk(issuer_pk); + JsonSaveStr(root, "$.ApplicationData.IssuerPublicKeyDec", issuer_pk_c); + JsonSaveBufAsHex(root, "$.ApplicationData.IssuerPublicKeyModulus", issuer_pk->modulus, issuer_pk->mlen); + free(issuer_pk_c); + + struct emv_pk *icc_pk = emv_pki_recover_icc_cert(issuer_pk, tlvRoot, NULL); + if (!icc_pk) { + emv_pk_free(pk); + emv_pk_free(issuer_pk); + PrintAndLog("WARNING: ICC certificate not found. Exit."); + return 2; + } + printf("ICC PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx\n", + icc_pk->rid[0], + icc_pk->rid[1], + icc_pk->rid[2], + icc_pk->rid[3], + icc_pk->rid[4], + icc_pk->index, + icc_pk->serial[0], + icc_pk->serial[1], + icc_pk->serial[2] + ); + + char *icc_pk_c = emv_pk_dump_pk(icc_pk); + JsonSaveStr(root, "$.ApplicationData.ICCPublicKeyDec", icc_pk_c); + JsonSaveBufAsHex(root, "$.ApplicationData.ICCPublicKeyModulus", icc_pk->modulus, icc_pk->mlen); + free(issuer_pk_c); + + return 0; +} diff --git a/client/emv/emvcore.h b/client/emv/emvcore.h index 94be4fcf..ece7324a 100644 --- a/client/emv/emvcore.h +++ b/client/emv/emvcore.h @@ -16,6 +16,7 @@ #include #include #include +#include #include "util.h" #include "common.h" #include "ui.h" @@ -37,6 +38,7 @@ enum TransactionType { TT_QVSDCMCHIP, TT_CDA, }; +extern char *TransactionTypeStr[]; typedef struct { uint8_t CLA; @@ -90,6 +92,8 @@ extern int trSDA(struct tlvdb *tlv); extern int trDDA(bool decodeTLV, struct tlvdb *tlv); extern int trCDA(struct tlvdb *tlv, struct tlvdb *ac_tlv, struct tlv *pdol_data_tlv, struct tlv *ac_data_tlv); +extern int RecoveryCertificates(struct tlvdb *tlvRoot, json_t *root); + #endif diff --git a/client/emv/emvjson.c b/client/emv/emvjson.c new file mode 100644 index 00000000..02297435 --- /dev/null +++ b/client/emv/emvjson.c @@ -0,0 +1,355 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 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. +//----------------------------------------------------------------------------- +// EMV json logic +//----------------------------------------------------------------------------- + +#include "emvjson.h" +#include +#include +#include +#include +#include +#include "util.h" +#include "ui.h" +#include "proxmark3.h" +#include "emv_tags.h" + +static const ApplicationDataElm ApplicationData[] = { +{0x82, "AIP"}, +{0x94, "AFL"}, + +{0x5A, "PAN"}, +{0x5F34, "PANSeqNo"}, +{0x5F24, "ExpirationDate"}, +{0x5F25, "EffectiveDate"}, +{0x5F28, "IssuerCountryCode"}, + +{0x50, "ApplicationLabel"}, +{0x9F08, "VersionNumber"}, +{0x9F42, "CurrencyCode"}, +{0x5F2D, "LanguagePreference"}, +{0x87, "PriorityIndicator"}, +{0x9F36, "ATC"}, //Application Transaction Counter + +{0x5F20, "CardholderName"}, + +{0x9F38, "PDOL"}, +{0x8C, "CDOL1"}, +{0x8D, "CDOL2"}, + +{0x9F07, "AUC"}, // Application Usage Control +{0x9F6C, "CTQ"}, +{0x8E, "CVMList"}, +{0x9F0D, "IACDefault"}, +{0x9F0E, "IACDeny"}, +{0x9F0F, "IACOnline"}, + +{0x8F, "CertificationAuthorityPublicKeyIndex"}, +{0x9F32, "IssuerPublicKeyExponent"}, +{0x92, "IssuerPublicKeyRemainder"}, +{0x90, "IssuerPublicKeyCertificate"}, +{0x9F47, "ICCPublicKeyExponent"}, +{0x9F46, "ICCPublicKeyCertificate"}, + +{0x00, "end..."} +}; +int ApplicationDataLen = sizeof(ApplicationData) / sizeof(ApplicationDataElm); + +char* GetApplicationDataName(tlv_tag_t tag) { + for (int i = 0; i < ApplicationDataLen; i++) + if (ApplicationData[i].Tag == tag) + return ApplicationData[i].Name; + + return NULL; +} + +int JsonSaveStr(json_t *root, char *path, char *value) { + json_error_t error; + + if (strlen(path) < 1) + return 1; + + if (path[0] == '$') { + if (json_path_set(root, path, json_string(value), 0, &error)) { + PrintAndLog("ERROR: can't set json path: ", error.text); + return 2; + } else { + return 0; + } + } else { + return json_object_set_new(root, path, json_string(value)); + } +}; + +int JsonSaveBufAsHex(json_t *elm, char *path, uint8_t *data, size_t datalen) { + char * msg = sprint_hex(data, datalen); + if (msg && strlen(msg) && msg[strlen(msg) - 1] == ' ') + msg[strlen(msg) - 1] = '\0'; + + return JsonSaveStr(elm, path, msg); +} + +int JsonSaveHex(json_t *elm, char *path, uint64_t data, int datalen) { + uint8_t bdata[8] = {0}; + int len = 0; + if (!datalen) { + for (uint64_t u = 0xffffffffffffffff; u; u = u << 8) { + if (!(data & u)) { + break; + } + len++; + } + if (!len) + len = 1; + } else { + len = datalen; + } + num_to_bytes(data, len, bdata); + + return JsonSaveBufAsHex(elm, path, bdata, len); +} + +int JsonSaveTLVValue(json_t *root, char *path, struct tlvdb *tlvdbelm) { + const struct tlv *tlvelm = tlvdb_get_tlv(tlvdbelm); + if (tlvelm) + return JsonSaveBufAsHex(root, path, (uint8_t *)tlvelm->value, tlvelm->len); + else + return 1; +} + +int JsonSaveTLVElm(json_t *elm, char *path, struct tlv *tlvelm, bool saveName, bool saveValue, bool saveAppDataLink) { + json_error_t error; + + if (strlen(path) < 1 || !tlvelm) + return 1; + + if (path[0] == '$') { + + json_t *obj = json_path_get(elm, path); + if (!obj) { + obj = json_object(); + + if (json_is_array(elm)) { + if (json_array_append_new(elm, obj)) { + PrintAndLog("ERROR: can't append array: %s", path); + return 2; + } + } else { + if (json_path_set(elm, path, obj, 0, &error)) { + PrintAndLog("ERROR: can't set json path: ", error.text); + return 2; + } + } + } + + if (saveAppDataLink) { + char * AppDataName = GetApplicationDataName(tlvelm->tag); + if (AppDataName) + JsonSaveStr(obj, "appdata", AppDataName); + } else { + char * name = emv_get_tag_name(tlvelm); + if (saveName && name && strlen(name) > 0 && strncmp(name, "Unknown", 7)) + JsonSaveStr(obj, "name", emv_get_tag_name(tlvelm)); + JsonSaveHex(obj, "tag", tlvelm->tag, 0); + if (saveValue) { + JsonSaveHex(obj, "length", tlvelm->len, 0); + JsonSaveBufAsHex(obj, "value", (uint8_t *)tlvelm->value, tlvelm->len); + }; + } + } + + return 0; +} + +int JsonSaveTLVTreeElm(json_t *elm, char *path, struct tlvdb *tlvdbelm, bool saveName, bool saveValue, bool saveAppDataLink) { + return JsonSaveTLVElm(elm, path, (struct tlv *)tlvdb_get_tlv(tlvdbelm), saveName, saveValue, saveAppDataLink); +} + +int JsonSaveTLVTree(json_t *root, json_t *elm, char *path, struct tlvdb *tlvdbelm) { + struct tlvdb *tlvp = tlvdbelm; + while (tlvp) { + const struct tlv * tlvpelm = tlvdb_get_tlv(tlvp); + char * AppDataName = NULL; + if (tlvpelm) + AppDataName = GetApplicationDataName(tlvpelm->tag); + + if (AppDataName) { + char appdatalink[200] = {0}; + sprintf(appdatalink, "$.ApplicationData.%s", AppDataName); + JsonSaveBufAsHex(root, appdatalink, (uint8_t *)tlvpelm->value, tlvpelm->len); + } + + json_t *pelm = json_path_get(elm, path); + if (pelm && json_is_array(pelm)) { + json_t *appendelm = json_object(); + json_array_append_new(pelm, appendelm); + JsonSaveTLVTreeElm(appendelm, "$", tlvp, !AppDataName, !tlvdb_elm_get_children(tlvp), AppDataName); + pelm = appendelm; + } else { + JsonSaveTLVTreeElm(elm, path, tlvp, !AppDataName, !tlvdb_elm_get_children(tlvp), AppDataName); + pelm = json_path_get(elm, path); + } + + if (tlvdb_elm_get_children(tlvp)) { + // get path element + if(!pelm) + return 1; + + // check childs element and add it if not found + json_t *chjson = json_path_get(pelm, "$.Childs"); + if (!chjson) { + json_object_set_new(pelm, "Childs", json_array()); + + chjson = json_path_get(pelm, "$.Childs"); + } + + // check + if (!json_is_array(chjson)) { + PrintAndLog("E->Internal logic error. `$.Childs` is not an array."); + break; + } + + // Recursion + JsonSaveTLVTree(root, chjson, "$", tlvdb_elm_get_children(tlvp)); + } + + tlvp = tlvdb_elm_get_next(tlvp); + } + return 0; +} + +bool HexToBuffer(const char *errormsg, const char *hexvalue, uint8_t * buffer, size_t maxbufferlen, size_t *bufferlen) { + int buflen = 0; + + switch(param_gethex_to_eol(hexvalue, 0, buffer, maxbufferlen, &buflen)) { + case 1: + PrintAndLog("%s Invalid HEX value.", errormsg); + return false; + case 2: + PrintAndLog("%s Hex value too large.", errormsg); + return false; + case 3: + PrintAndLog("%s Hex value must have even number of digits.", errormsg); + return false; + } + + if (buflen > maxbufferlen) { + PrintAndLog("%s HEX length (%d) more than %d", errormsg, *bufferlen, maxbufferlen); + return false; + } + + *bufferlen = buflen; + + return true; +} + +bool ParamLoadFromJson(struct tlvdb *tlv) { + json_t *root; + json_error_t error; + + if (!tlv) { + PrintAndLog("ERROR load params: tlv tree is NULL."); + return false; + } + + // current path + file name + const char *relfname = "emv/defparams.json"; + char fname[strlen(get_my_executable_directory()) + strlen(relfname) + 1]; + strcpy(fname, get_my_executable_directory()); + strcat(fname, relfname); + + root = json_load_file(fname, 0, &error); + if (!root) { + PrintAndLog("Load params: json error on line %d: %s", error.line, error.text); + return false; + } + + if (!json_is_array(root)) { + PrintAndLog("Load params: Invalid json format. root must be array."); + return false; + } + + PrintAndLog("Load params: json(%d) OK", json_array_size(root)); + + for(int i = 0; i < json_array_size(root); i++) { + json_t *data, *jtag, *jlength, *jvalue; + + data = json_array_get(root, i); + if(!json_is_object(data)) + { + PrintAndLog("Load params: data [%d] is not an object", i + 1); + json_decref(root); + return false; + } + + jtag = json_object_get(data, "tag"); + if(!json_is_string(jtag)) + { + PrintAndLog("Load params: data [%d] tag is not a string", i + 1); + json_decref(root); + return false; + } + const char *tlvTag = json_string_value(jtag); + + jvalue = json_object_get(data, "value"); + if(!json_is_string(jvalue)) + { + PrintAndLog("Load params: data [%d] value is not a string", i + 1); + json_decref(root); + return false; + } + const char *tlvValue = json_string_value(jvalue); + + jlength = json_object_get(data, "length"); + if(!json_is_number(jlength)) + { + PrintAndLog("Load params: data [%d] length is not a number", i + 1); + json_decref(root); + return false; + } + + int tlvLength = json_integer_value(jlength); + if (tlvLength > 250) { + PrintAndLog("Load params: data [%d] length more than 250", i + 1); + json_decref(root); + return false; + } + + PrintAndLog("TLV param: %s[%d]=%s", tlvTag, tlvLength, tlvValue); + uint8_t buf[251] = {0}; + size_t buflen = 0; + + // here max length must be 4, but now tlv_tag_t is 2-byte var. so let it be 2 by now... TODO: needs refactoring tlv_tag_t... + if (!HexToBuffer("TLV Error type:", tlvTag, buf, 2, &buflen)) { + json_decref(root); + return false; + } + tlv_tag_t tag = 0; + for (int i = 0; i < buflen; i++) { + tag = (tag << 8) + buf[i]; + } + + if (!HexToBuffer("TLV Error value:", tlvValue, buf, sizeof(buf) - 1, &buflen)) { + json_decref(root); + return false; + } + + if (buflen != tlvLength) { + PrintAndLog("Load params: data [%d] length of HEX must(%d) be identical to length in TLV param(%d)", i + 1, buflen, tlvLength); + json_decref(root); + return false; + } + + tlvdb_change_or_add_node(tlv, tag, tlvLength, (const unsigned char *)buf); + } + + json_decref(root); + + return true; +} + diff --git a/client/emv/emvjson.h b/client/emv/emvjson.h new file mode 100644 index 00000000..a518d7b9 --- /dev/null +++ b/client/emv/emvjson.h @@ -0,0 +1,35 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 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. +//----------------------------------------------------------------------------- +// EMV json logic +//----------------------------------------------------------------------------- +#ifndef EMVJSON_H__ +#define EMVJSON_H__ + +#include +#include "tlv.h" + +typedef struct { + tlv_tag_t Tag; + char *Name; +} ApplicationDataElm; + +extern char* GetApplicationDataName(tlv_tag_t tag); + +extern int JsonSaveStr(json_t *root, char *path, char *value); +extern int JsonSaveBufAsHex(json_t *elm, char *path, uint8_t *data, size_t datalen); +extern int JsonSaveHex(json_t *elm, char *path, uint64_t data, int datalen); + +extern int JsonSaveTLVValue(json_t *root, char *path, struct tlvdb *tlvdbelm); +extern int JsonSaveTLVElm(json_t *elm, char *path, struct tlv *tlvelm, bool saveName, bool saveValue, bool saveAppDataLink); +extern int JsonSaveTLVTreeElm(json_t *elm, char *path, struct tlvdb *tlvdbelm, bool saveName, bool saveValue, bool saveAppDataLink); + +extern int JsonSaveTLVTree(json_t *root, json_t *elm, char *path, struct tlvdb *tlvdbelm); + +extern bool ParamLoadFromJson(struct tlvdb *tlv); + +#endif \ No newline at end of file diff --git a/client/emv/tlv.c b/client/emv/tlv.c index 776fdeed..540c33e4 100644 --- a/client/emv/tlv.c +++ b/client/emv/tlv.c @@ -458,6 +458,10 @@ const struct tlv *tlvdb_get_inchild(const struct tlvdb *tlvdb, tlv_tag_t tag, co return tlvdb_get(tlvdb, tag, prev); } +const struct tlv *tlvdb_get_tlv(const struct tlvdb *tlvdb) { + return &tlvdb->tag; +} + unsigned char *tlv_encode(const struct tlv *tlv, size_t *len) { size_t size = tlv->len; @@ -516,3 +520,18 @@ bool tlv_equal(const struct tlv *a, const struct tlv *b) return a->tag == b->tag && a->len == b->len && !memcmp(a->value, b->value, a->len); } + +struct tlvdb *tlvdb_elm_get_next(struct tlvdb *tlvdb) +{ + return tlvdb->next; +} + +struct tlvdb *tlvdb_elm_get_children(struct tlvdb *tlvdb) +{ + return tlvdb->children; +} + +struct tlvdb *tlvdb_elm_get_parent(struct tlvdb *tlvdb) +{ + return tlvdb->parent; +} diff --git a/client/emv/tlv.h b/client/emv/tlv.h index 963abb2a..b25b51de 100644 --- a/client/emv/tlv.h +++ b/client/emv/tlv.h @@ -39,6 +39,10 @@ struct tlvdb *tlvdb_parse(const unsigned char *buf, size_t len); struct tlvdb *tlvdb_parse_multi(const unsigned char *buf, size_t len); void tlvdb_free(struct tlvdb *tlvdb); +struct tlvdb *tlvdb_elm_get_next(struct tlvdb *tlvdb); +struct tlvdb *tlvdb_elm_get_children(struct tlvdb *tlvdb); +struct tlvdb *tlvdb_elm_get_parent(struct tlvdb *tlvdb); + struct tlvdb *tlvdb_find_full(struct tlvdb *tlvdb, tlv_tag_t tag); // search also in childrens struct tlvdb *tlvdb_find(struct tlvdb *tlvdb, tlv_tag_t tag); struct tlvdb *tlvdb_find_next(struct tlvdb *tlvdb, tlv_tag_t tag); @@ -50,6 +54,7 @@ void tlvdb_change_or_add_node(struct tlvdb *tlvdb, tlv_tag_t tag, size_t len, co void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data, int level); const struct tlv *tlvdb_get(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev); const struct tlv *tlvdb_get_inchild(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev); +const struct tlv *tlvdb_get_tlv(const struct tlvdb *tlvdb); bool tlv_parse_tl(const unsigned char **buf, size_t *len, struct tlv *tlv); unsigned char *tlv_encode(const struct tlv *tlv, size_t *len); diff --git a/client/jansson/Makefile b/client/jansson/Makefile index 7a262f8b..e608d7c0 100644 --- a/client/jansson/Makefile +++ b/client/jansson/Makefile @@ -19,6 +19,7 @@ libjansson_la_SOURCES = \ strconv.c \ utf.c \ utf.h \ + path.c \ value.c libjansson_la_LDFLAGS = \ -no-undefined \ diff --git a/client/jansson/jansson.h b/client/jansson/jansson.h index 43332895..86f23d17 100644 --- a/client/jansson/jansson.h +++ b/client/jansson/jansson.h @@ -306,6 +306,16 @@ int json_equal(const json_t *value1, const json_t *value2); json_t *json_copy(json_t *value); json_t *json_deep_copy(const json_t *value); +/* path */ + +json_t *json_path_get(const json_t *json, const char *path); +int json_path_set_new(json_t *json, const char *path, json_t *value, size_t flags, json_error_t *error); + +static JSON_INLINE +int json_path_set(json_t *json, const char *path, json_t *value, size_t flags, json_error_t *error) +{ + return json_path_set_new(json, path, json_incref(value), flags, error); +} /* decoding */ diff --git a/client/jansson/path.c b/client/jansson/path.c new file mode 100644 index 00000000..08f2da9f --- /dev/null +++ b/client/jansson/path.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2012 Rogerz Zhang + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + * + * source here https://github.com/rogerz/jansson/blob/json_path/src/path.c + */ + +#include +#include + +#include +#include "jansson_private.h" + + +json_t *json_path_get(const json_t *json, const char *path) +{ + static const char root_chr = '$', array_open = '['; + static const char *path_delims = ".[", *array_close = "]"; + const json_t *cursor; + char *token, *buf, *peek, *endptr, delim = '\0'; + const char *expect; + + if (!json || !path || path[0] != root_chr) + return NULL; + else + buf = jsonp_strdup(path); + + peek = buf + 1; + cursor = json; + token = NULL; + expect = path_delims; + + while (peek && *peek && cursor) + { + char *last_peek = peek; + peek = strpbrk(peek, expect); + if (peek) { + if (!token && peek != last_peek) + goto fail; + delim = *peek; + *peek++ = '\0'; + } else if (expect != path_delims || !token) { + goto fail; + } + + if (expect == path_delims) { + if (token) { + cursor = json_object_get(cursor, token); + } + expect = (delim == array_open ? array_close : path_delims); + token = peek; + } else if (expect == array_close) { + size_t index = strtol(token, &endptr, 0); + if (*endptr) + goto fail; + cursor = json_array_get(cursor, index); + token = NULL; + expect = path_delims; + } else { + goto fail; + } + } + + jsonp_free(buf); + return (json_t *)cursor; +fail: + jsonp_free(buf); + return NULL; +} + +int json_path_set_new(json_t *json, const char *path, json_t *value, size_t flags, json_error_t *error) +{ + static const char root_chr = '$', array_open = '[', object_delim = '.'; + static const char * const path_delims = ".[", *array_close = "]"; + + json_t *cursor, *parent = NULL; + char *token, *buf = NULL, *peek, delim = '\0'; + const char *expect; + int index_saved = -1; + + jsonp_error_init(error, ""); + + if (!json || !path || flags || !value) { + jsonp_error_set(error, -1, -1, 0, json_error_invalid_argument, "invalid argument"); + goto fail; + } else { + buf = jsonp_strdup(path); + } + + if (buf[0] != root_chr) { + jsonp_error_set(error, -1, -1, 0, json_error_invalid_format, "path should start with $"); + goto fail; + } + + peek = buf + 1; + cursor = json; + token = NULL; + expect = path_delims; + + while (peek && *peek && cursor) + { + char *last_peek = peek; + peek = strpbrk(last_peek, expect); + + if (peek) { + if (!token && peek != last_peek) { + jsonp_error_set(error, -1, -1, last_peek - buf, json_error_invalid_format, "unexpected trailing chars"); + goto fail; + } + delim = *peek; + *peek++ = '\0'; + } else { // end of path + if (expect == path_delims) { + break; + } else { + jsonp_error_set(error, -1, -1, peek - buf, json_error_invalid_format, "missing ']'?"); + goto fail; + } + } + + if (expect == path_delims) { + if (token) { + if (token[0] == '\0') { + jsonp_error_set(error, -1, -1, peek - buf, json_error_invalid_format, "empty token"); + goto fail; + } + + parent = cursor; + cursor = json_object_get(parent, token); + + if (!cursor) { + if (!json_is_object(parent)) { + jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "object expected"); + goto fail; + } + if (delim == object_delim) { + cursor = json_object(); + json_object_set_new(parent, token, cursor); + } else { + jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "new array is not allowed"); + goto fail; + } + } + } + expect = (delim == array_open ? array_close : path_delims); + token = peek; + } else if (expect == array_close) { + char *endptr; + size_t index; + + parent = cursor; + if (!json_is_array(parent)) { + jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "array expected"); + goto fail; + } + index = strtol(token, &endptr, 0); + if (*endptr) { + jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "invalid array index"); + goto fail; + } + cursor = json_array_get(parent, index); + if (!cursor) { + jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "array index out of bound"); + goto fail; + } + index_saved = index; + token = NULL; + expect = path_delims; + } else { + assert(1); + jsonp_error_set(error, -1, -1, peek - buf, json_error_unknown, "unexpected error in path move"); + goto fail; + } + } + + if (token) { + if (json_is_object(cursor)) { + json_object_set(cursor, token, value); + } else { + jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "object expected"); + goto fail; + } + cursor = json_object_get(cursor, token); + } else if (index_saved != -1 && json_is_array(parent)) { + json_array_set(parent, index_saved, value); + cursor = json_array_get(parent, index_saved); + } else { + jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "invalid path"); + goto fail; + } + + json_decref(value); + jsonp_free(buf); + return 0; + +fail: + json_decref(value); + jsonp_free(buf); + return -1; +} From dc3e2acf33a1f62b530d3a199073c1b9bfebf9b5 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko <807634+merlokk@users.noreply.github.com> Date: Wed, 17 Oct 2018 21:55:04 +0300 Subject: [PATCH 301/310] mf plus info with detect sl mode (#695) --- client/Makefile | 1 + client/cmdhf.c | 2 + client/cmdhfmfp.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++ client/cmdhfmfp.h | 18 +++++++ 4 files changed, 143 insertions(+) create mode 100644 client/cmdhfmfp.c create mode 100644 client/cmdhfmfp.h diff --git a/client/Makefile b/client/Makefile index f1007b89..c0a319a4 100644 --- a/client/Makefile +++ b/client/Makefile @@ -156,6 +156,7 @@ CMDSRCS = $(SRC_SMARTCARD) \ cmdhflegic.c \ cmdhficlass.c \ cmdhfmf.c \ + cmdhfmfp.c \ cmdhfmfu.c \ cmdhfmfhard.c \ hardnested/hardnested_bruteforce.c \ diff --git a/client/cmdhf.c b/client/cmdhf.c index b973354d..762cc791 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -28,6 +28,7 @@ #include "cmdhflegic.h" #include "cmdhficlass.h" #include "cmdhfmf.h" +#include "cmdhfmfp.h" #include "cmdhfmfu.h" #include "cmdhftopaz.h" #include "protocols.h" @@ -595,6 +596,7 @@ static command_t CommandTable[] = {"iclass", CmdHFiClass, 1, "{ ICLASS RFIDs... }"}, {"mf", CmdHFMF, 1, "{ MIFARE RFIDs... }"}, {"mfu", CmdHFMFUltra, 1, "{ MIFARE Ultralight RFIDs... }"}, + {"mfp", CmdHFMFP, 1, "{ MIFARE Plus RFIDs... }"}, {"topaz", CmdHFTopaz, 1, "{ TOPAZ (NFC Type 1) RFIDs... }"}, {"tune", CmdHFTune, 0, "Continuously measure HF antenna tuning"}, {"list", CmdHFList, 1, "List protocol data in trace buffer"}, diff --git a/client/cmdhfmfp.c b/client/cmdhfmfp.c new file mode 100644 index 00000000..34abbed0 --- /dev/null +++ b/client/cmdhfmfp.c @@ -0,0 +1,122 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 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. +//----------------------------------------------------------------------------- +// High frequency MIFARE Plus commands +//----------------------------------------------------------------------------- + +#include "cmdhfmfp.h" + +#include +#include +#include +#include +#include +#include "comms.h" +#include "cmdmain.h" +#include "util.h" +#include "ui.h" +#include "cmdhf14a.h" +#include "mifare.h" + +static int CmdHelp(const char *Cmd); + +int CmdHFMFPInfo(const char *cmd) { + + if (cmd && strlen(cmd) > 0) + PrintAndLog("WARNING: command don't have any parameters.\n"); + + // info about 14a part + CmdHF14AInfo(""); + + // Mifare Plus info + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0}}; + SendCommand(&c); + + UsbCommand resp; + WaitForResponse(CMD_ACK,&resp); + + iso14a_card_select_t card; + memcpy(&card, (iso14a_card_select_t *)resp.d.asBytes, sizeof(iso14a_card_select_t)); + + uint64_t select_status = resp.arg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision + + if (select_status == 1 || select_status == 2) { + PrintAndLog("----------------------------------------------"); + PrintAndLog("Mifare Plus info:"); + + // MIFARE Type Identification Procedure + // https://www.nxp.com/docs/en/application-note/AN10833.pdf + uint16_t ATQA = card.atqa[0] + (card.atqa[1] << 8); + if (ATQA == 0x0004) PrintAndLog("ATQA: Mifare Plus 2k 4bUID"); + if (ATQA == 0x0002) PrintAndLog("ATQA: Mifare Plus 4k 4bUID"); + if (ATQA == 0x0044) PrintAndLog("ATQA: Mifare Plus 2k 7bUID"); + if (ATQA == 0x0042) PrintAndLog("ATQA: Mifare Plus 4k 7bUID"); + + uint8_t SLmode = 0xff; + if (card.sak == 0x08) { + PrintAndLog("SAK: Mifare Plus 2k 7bUID"); + if (select_status == 2) SLmode = 1; + } + if (card.sak == 0x18) { + PrintAndLog("SAK: Mifare Plus 4k 7bUID"); + if (select_status == 2) SLmode = 1; + } + if (card.sak == 0x10) { + PrintAndLog("SAK: Mifare Plus 2k"); + if (select_status == 2) SLmode = 2; + } + if (card.sak == 0x11) { + PrintAndLog("SAK: Mifare Plus 4k"); + if (select_status == 2) SLmode = 2; + } + if (card.sak == 0x20) { + PrintAndLog("SAK: Mifare Plus SL0/SL3 or Mifare desfire"); + if (card.ats_len > 0) { + SLmode = 3; + + // check SL0 + uint8_t data[250] = {0}; + int datalen = 0; + // https://github.com/Proxmark/proxmark3/blob/master/client/scripts/mifarePlus.lua#L161 + uint8_t cmd[3 + 16] = {0xa8, 0x90, 0x90, 0x00}; + int res = ExchangeRAW14a(cmd, sizeof(cmd), false, false, data, sizeof(data), &datalen); + if (!res && datalen > 1 && data[0] == 0x09) { + SLmode = 0; + } + } + } + + if (SLmode != 0xff) + PrintAndLog("Mifare Plus SL mode: SL%d", SLmode); + else + PrintAndLog("Mifare Plus SL mode: unknown("); + } else { + PrintAndLog("Mifare Plus info not available."); + } + + DropField(); + + return 0; +} + +static command_t CommandTable[] = +{ + {"help", CmdHelp, 1, "This help"}, + {"info", CmdHFMFPInfo, 0, "Info about Mifare Plus tag"}, + {NULL, NULL, 0, NULL} +}; + +int CmdHFMFP(const char *Cmd) { + (void)WaitForResponseTimeout(CMD_ACK,NULL,100); + CmdsParse(CommandTable, Cmd); + return 0; +} + +int CmdHelp(const char *Cmd) { + CmdsHelp(CommandTable); + return 0; +} diff --git a/client/cmdhfmfp.h b/client/cmdhfmfp.h new file mode 100644 index 00000000..b1ac7c34 --- /dev/null +++ b/client/cmdhfmfp.h @@ -0,0 +1,18 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 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. +//----------------------------------------------------------------------------- +// High frequency MIFARE Plus commands +//----------------------------------------------------------------------------- +#ifndef CMDHFMFP_H__ +#define CMDHFMFP_H__ + +#include "mifaredefault.h" + +extern int CmdHFMFP(const char *Cmd); + + +#endif \ No newline at end of file From 666fa6e1cfdc81acd1525ff4153c077535aa205a Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko <807634+merlokk@users.noreply.github.com> Date: Mon, 22 Oct 2018 23:27:37 +0300 Subject: [PATCH 302/310] deploy in msys2 and proxspace (#702) --- CI/appveyor.yml | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/CI/appveyor.yml b/CI/appveyor.yml index f5ae2c70..1c22c92e 100644 --- a/CI/appveyor.yml +++ b/CI/appveyor.yml @@ -66,9 +66,20 @@ clone_script: Write-Host "[ OK ]" -ForegroundColor Green - Write-Host "Fill msys\etc\fstab file..." -NoNewLine + Write-Host "Fill msys2\etc\fstab file..." -NoNewLine - New-Item c:\ProxSpace\msys\etc\fstab -type file -force -value "#Win32_Path Mount_Point`nc:\ProxSpace\devkitARM /devkitARM`nc:\ProxSpace\Qt\5.6 /qt `nc:\ProxSpace\pm3 /pm3`n" + New-Item c:\ProxSpace\msys2\etc\fstab -type file -force -value "# For a description of the file format, see the Users Guide`n# http://cygwin.com/cygwin-ug-net/using.html#mount-table`nnone / cygdrive binary,posix=0,noacl,user 0 0 `nC:\ProxSpace\pm3 /pm3 ntfs noacl 0 0 `nC:\ProxSpace\gcc-arm-none-eabi /gcc-arm-none-eabi ntfs noacl 0 0 `n" + + Write-Host "[ OK ]" -ForegroundColor Green + + + Write-Host "Update msys2 packages..." -NoNewLine + + $env:Path = "C:\ProxSpace\msys2\usr\bin;C:\ProxSpace\msys2\mingw32\bin;C:\ProxSpace\gcc-arm-none-eabi\bin;$env:Path" + + C:\ProxSpace\msys2\msys2_shell.cmd -mingw32 -defterm -no-start /dev/null 1> msys1.txt 2>&1 + + C:\ProxSpace\msys2\msys2_shell.cmd -mingw32 -defterm -no-start /dev/null 1> msys1.txt 2>&1 Write-Host "[ OK ]" -ForegroundColor Green install: @@ -84,12 +95,25 @@ install: } build_script: - ps: >- - $env:Path = "C:\ProxSpace\msys\bin;$env:Path" + "C:\ProxSpace\msys2\usr\bin;C:\ProxSpace\msys2\mingw32\bin;C:\ProxSpace\gcc-arm-none-eabi\bin;$env:Path" + + + $env:MINGW_HOME="C:\ProxSpace\msys2\mingw32" + + $env:MSYS_HOME="C:\ProxSpace\msys2" + + $env:MSYSTEM="MINGW32" + + $env:MINGW_PREFIX="/mingw32" + + $env:SHELL="/bin/bash" + + $env:MSYSTEM_CHOST="i686-w64-mingw32" #make - bash -lc -i "pwd;make all" + bash -c -i 'pwd;make clean;make all' #some checks @@ -268,19 +292,19 @@ test_script: #proxmark logic tests - ExecTest "proxmark help" "proxmark3 -h" {bash -lc 'cd ~/client;proxmark3 -h | grep -q Execute && echo Passed || echo Failed'} + ExecTest "proxmark help" "proxmark3 -h" {bash -lc 'cd ~/client;./proxmark3 -h | grep -q Execute && echo Passed || echo Failed'} - ExecTest "proxmark help hardnested" "proxmark3 -h" {bash -lc 'cd ~/client;proxmark3 -h | grep -q hardnested && echo Passed || echo Failed'} + ExecTest "proxmark help hardnested" "proxmark3 -h" {bash -lc 'cd ~/client;./proxmark3 -h | grep -q hardnested && echo Passed || echo Failed'} - ExecTest "hf mf offline text" "hf mf" {bash -lc "cd ~/client;proxmark3 comx -c 'hf mf'"} "at_enc" + ExecTest "hf mf offline text" "hf mf" {bash -lc "cd ~/client;./proxmark3 comx -c 'hf mf'"} "at_enc" - ExecTest "hf mf hardnested" "hf mf hardnested" {bash -lc "cd ~/client;proxmark3 comx -c 'hf mf hardnested t 1 000000000000'"} "found:" + ExecTest "hf mf hardnested" "hf mf hardnested" {bash -lc "cd ~/client;./proxmark3 comx -c 'hf mf hardnested t 1 000000000000'"} "found:" #proxmark crypto tests - ExecTest "hf emv test" "hf emv test" {bash -lc "cd ~/client;proxmark3 comx -c 'hf emv test'"} "Tests ?OK" + ExecTest "hf emv test" "hf emv test" {bash -lc "cd ~/client;./proxmark3 comx -c 'hf emv test'"} "Tests ?OK" if ($global:TestsPassed) { From 8c6cca0ba1424200dcb872eb96918281c9e62784 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Tue, 23 Oct 2018 08:22:13 +0200 Subject: [PATCH 303/310] Fix hf 15 sim (#696) * added ISO15693 coding for tag messages (CodeIso15693AsTag()) * added ISO15693 decoding for reader commands (Handle15693SampleFromReader()) * send tag inventory response in either high or low speed * some refactoring and formatting --- armsrc/iso15693.c | 1059 +++++++++++++++++++++++++--------------- armsrc/util.c | 24 +- client/cmdhf15.c | 103 ++-- common/iso15693tools.h | 64 +-- common/protocols.h | 60 ++- fpga/hi_simulate.v | 45 +- 6 files changed, 789 insertions(+), 566 deletions(-) diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index 9b0ab29e..9479c3c0 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -2,33 +2,31 @@ // Jonathan Westhues, split Nov 2006 // Modified by Greg Jones, Jan 2009 // Modified by Adrian Dabrowski "atrox", Mar-Sept 2010,Oct 2011 +// Modified by piwi, Oct 2018 // // 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. //----------------------------------------------------------------------------- // Routines to support ISO 15693. This includes both the reader software and -// the `fake tag' modes, but at the moment I've implemented only the reader -// stuff, and that barely. -// Modified to perform modulation onboard in arm rather than on PC -// Also added additional reader commands (SELECT, READ etc.) +// the `fake tag' modes. //----------------------------------------------------------------------------- -// The ISO 15693 describes two transmission modes from reader to tag, and 4 -// transmission modes from tag to reader. As of Mar 2010 this code only -// supports one of each: "1of4" mode from reader to tag, and the highspeed -// variant with one subcarrier from card to reader. -// As long, as the card fully support ISO 15693 this is no problem, since the -// reader chooses both data rates, but some non-standard tags do not. Further for -// the simulation to work, we will need to support all data rates. + +// The ISO 15693 describes two transmission modes from reader to tag, and four +// transmission modes from tag to reader. As of Oct 2018 this code supports +// both reader modes and the high speed variant with one subcarrier from card to reader. +// As long as the card fully support ISO 15693 this is no problem, since the +// reader chooses both data rates, but some non-standard tags do not. +// For card simulation, the code supports both high and low speed modes with one subcarrier. // // VCD (reader) -> VICC (tag) // 1 out of 256: -// data rate: 1,66 kbit/s (fc/8192) +// data rate: 1,66 kbit/s (fc/8192) // used for long range // 1 out of 4: // data rate: 26,48 kbit/s (fc/512) // used for short range, high speed -// +// // VICC (tag) -> VCD (reader) // Modulation: // ASK / one subcarrier (423,75 khz) @@ -39,22 +37,17 @@ // high ASK: 26,48 kbit/s // high FSK: 26,69 kbit/s //----------------------------------------------------------------------------- -// added "1 out of 256" mode (for VCD->PICC) - atrox 20100911 // Random Remarks: // *) UID is always used "transmission order" (LSB), which is reverse to display order // TODO / BUGS / ISSUES: -// *) writing to tags takes longer: we miss the answer from the tag in most cases -// -> tweak the read-timeout times -// *) signal decoding from the card is still a bit shaky. -// *) signal decoding is unable to detect collissions. -// *) add anti-collission support for inventory-commands +// *) signal decoding is unable to detect collisions. +// *) add anti-collision support for inventory-commands // *) read security status of a block -// *) sniffing and simulation do only support one transmission mode. need to support -// all 8 transmission combinations -// *) remove or refactor code under "depricated" +// *) sniffing and simulation do not support two subcarrier modes. +// *) remove or refactor code under "depricated" // *) document all the functions @@ -63,6 +56,7 @@ #include "apps.h" #include "string.h" #include "iso15693tools.h" +#include "protocols.h" #include "cmd.h" #define arraylen(x) (sizeof(x)/sizeof((x)[0])) @@ -83,18 +77,25 @@ static int DEBUG = 0; #define AddCrc(data,datalen) Iso15693AddCrc(data,datalen) #define sprintUID(target,uid) Iso15693sprintUID(target,uid) -// approximate amplitude=sqrt(ci^2+cq^2) +// approximate amplitude=sqrt(ci^2+cq^2) by amplitude = max(|ci|,|cq|) + 1/2*min(|ci|,|cq|) #define AMPLITUDE(ci, cq) (MAX(ABS(ci), ABS(cq)) + MIN(ABS(ci), ABS(cq))/2) -// DMA buffer -#define ISO15693_DMA_BUFFER_SIZE 128 +// buffers +#define ISO15693_DMA_BUFFER_SIZE 128 +#define ISO15693_MAX_RESPONSE_LENGTH 36 // allows read single block with the maximum block size of 256bits. Read multiple blocks not supported yet +#define ISO15693_MAX_COMMAND_LENGTH 45 // allows write single block with the maximum block size of 256bits. Write multiple blocks not supported yet + +// timing. Delays in SSP_CLK ticks. +#define DELAY_READER_TO_ARM 8 +#define DELAY_ARM_TO_READER 1 +#define DELAY_ISO15693_VCD_TO_VICC 132 // 132/423.75kHz = 311.5us from end of EOF to start of tag response // --------------------------- -// Signal Processing +// Signal Processing // --------------------------- // prepare data using "1 out of 4" code for later transmission -// resulting data rate is 26,48 kbit/s (fc/512) +// resulting data rate is 26.48 kbit/s (fc/512) // cmd ... data // n ... length of data static void CodeIso15693AsReader(uint8_t *cmd, int n) @@ -174,12 +175,10 @@ static void CodeIso15693AsReader(uint8_t *cmd, int n) for(i = 0; i < 4; i++) { ToSendStuffBit(1); } - - ToSendMax++; } // encode data using "1 out of 256" scheme -// data rate is 1,66 kbit/s (fc/8192) +// data rate is 1,66 kbit/s (fc/8192) // is designed for more robust communication over longer distances static void CodeIso15693AsReader256(uint8_t *cmd, int n) { @@ -201,7 +200,7 @@ static void CodeIso15693AsReader256(uint8_t *cmd, int n) ToSendStuffBit(1); ToSendStuffBit(1); ToSendStuffBit(0); - + for(i = 0; i < n; i++) { for (j = 0; j<=255; j++) { if (cmd[i]==j) { @@ -210,8 +209,8 @@ static void CodeIso15693AsReader256(uint8_t *cmd, int n) } else { ToSendStuffBit(1); ToSendStuffBit(1); - } - } + } + } } // EOF ToSendStuffBit(1); @@ -219,10 +218,53 @@ static void CodeIso15693AsReader256(uint8_t *cmd, int n) ToSendStuffBit(0); ToSendStuffBit(1); - // And slack at the end, too. - for(i = 0; i < 24; i++) { + // Fill remainder of last byte with 1 + for(i = 0; i < 4; i++) { ToSendStuffBit(1); } + + ToSendMax++; +} + + +static void CodeIso15693AsTag(uint8_t *cmd, int n) +{ + ToSendReset(); + + // SOF + ToSendStuffBit(0); + ToSendStuffBit(0); + ToSendStuffBit(0); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(0); + ToSendStuffBit(1); + + // data + for(int i = 0; i < n; i++) { + for(int j = 0; j < 8; j++) { + if ((cmd[i] >> j) & 0x01) { + ToSendStuffBit(0); + ToSendStuffBit(1); + } else { + ToSendStuffBit(1); + ToSendStuffBit(0); + } + } + } + + // EOF + ToSendStuffBit(1); + ToSendStuffBit(0); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(0); + ToSendStuffBit(0); + ToSendStuffBit(0); + + ToSendMax++; } @@ -244,29 +286,50 @@ static void TransmitTo15693Tag(const uint8_t *cmd, int len) } //----------------------------------------------------------------------------- -// Transmit the command (to the reader) that was placed in cmd[]. +// Transmit the tag response (to the reader) that was placed in cmd[]. //----------------------------------------------------------------------------- -static void TransmitTo15693Reader(const uint8_t *cmd, int len) +static void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t start_time, bool slow) { + // don't use the FPGA_HF_SIMULATOR_MODULATE_424K_8BIT minor mode. It would spoil GetCountSspClk() FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_424K); + uint8_t shift_delay = start_time & 0x00000007; + uint8_t bitmask = 0x00; + for (int i = 0; i < shift_delay; i++) { + bitmask |= (0x01 << i); + } + + while (GetCountSspClk() < (start_time & 0xfffffff8)) ; + AT91C_BASE_SSC->SSC_THR = 0x00; // clear TXRDY + LED_C_ON(); - for(int c = 0; c < len; ) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = cmd[c]; - c++; + uint8_t bits_to_shift = 0x00; + for(size_t c = 0; c <= len; c++) { + uint8_t bits_to_send = bits_to_shift << (8 - shift_delay) | (c==len?0x00:cmd[c]) >> shift_delay; + bits_to_shift = cmd[c] & bitmask; + for (int i = 7; i >= 0; i--) { + for (int j = 0; j < (slow?4:1); ) { + if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { + if (bits_to_send >> i & 0x01) { + AT91C_BASE_SSC->SSC_THR = 0xff; + } else { + AT91C_BASE_SSC->SSC_THR = 0x00; + } + j++; + } + WDT_HIT(); + } } - WDT_HIT(); } LED_C_OFF(); } //============================================================================= -// An ISO 15693 demodulator (one subcarrier only). Uses cross correlation to -// identify the SOF, each bit, and EOF. +// An ISO 15693 decoder for tag responses (one subcarrier only). +// Uses cross correlation to identify the SOF, each bit, and EOF. // This function is called 8 times per bit (every 2 subcarrier cycles). -// Subcarrier frequency fs is 424kHz, 1/fs = 2,36us, +// Subcarrier frequency fs is 424kHz, 1/fs = 2,36us, // i.e. function is called every 4,72us // LED handling: // LED C -> ON once we have received the SOF and are expecting the rest. @@ -279,13 +342,13 @@ static void TransmitTo15693Reader(const uint8_t *cmd, int len) #define SUBCARRIER_DETECT_THRESHOLD 2 #define SOF_CORRELATOR_LEN (1<<5) -typedef struct Demod { +typedef struct DecodeTag { enum { - DEMOD_UNSYNCD, - DEMOD_AWAIT_SOF_1, - DEMOD_AWAIT_SOF_2, - DEMOD_RECEIVING_DATA, - DEMOD_AWAIT_EOF + STATE_TAG_UNSYNCD, + STATE_TAG_AWAIT_SOF_1, + STATE_TAG_AWAIT_SOF_2, + STATE_TAG_RECEIVING_DATA, + STATE_TAG_AWAIT_EOF } state; int bitCount; int posCount; @@ -305,145 +368,145 @@ typedef struct Demod { int32_t SOF_corr; int32_t SOF_corr_prev; uint8_t SOF_correlator[SOF_CORRELATOR_LEN]; -} Demod_t; +} DecodeTag_t; -static RAMFUNC int Handle15693SamplesDemod(int8_t ci, int8_t cq, Demod_t *Demod) +static int Handle15693SamplesFromTag(int8_t ci, int8_t cq, DecodeTag_t *DecodeTag) { - switch(Demod->state) { - case DEMOD_UNSYNCD: + switch(DecodeTag->state) { + case STATE_TAG_UNSYNCD: // initialize SOF correlator. We are looking for 12 samples low and 12 samples high. - Demod->SOF_low = 0; - Demod->SOF_high = 12; - Demod->SOF_last = 23; - memset(Demod->SOF_correlator, 0x00, Demod->SOF_last + 1); - Demod->SOF_correlator[Demod->SOF_last] = AMPLITUDE(ci,cq); - Demod->SOF_corr = Demod->SOF_correlator[Demod->SOF_last]; - Demod->SOF_corr_prev = Demod->SOF_corr; - // initialize Demodulator - Demod->posCount = 0; - Demod->bitCount = 0; - Demod->len = 0; - Demod->state = DEMOD_AWAIT_SOF_1; + DecodeTag->SOF_low = 0; + DecodeTag->SOF_high = 12; + DecodeTag->SOF_last = 23; + memset(DecodeTag->SOF_correlator, 0x00, DecodeTag->SOF_last + 1); + DecodeTag->SOF_correlator[DecodeTag->SOF_last] = AMPLITUDE(ci,cq); + DecodeTag->SOF_corr = DecodeTag->SOF_correlator[DecodeTag->SOF_last]; + DecodeTag->SOF_corr_prev = DecodeTag->SOF_corr; + // initialize Decoder + DecodeTag->posCount = 0; + DecodeTag->bitCount = 0; + DecodeTag->len = 0; + DecodeTag->state = STATE_TAG_AWAIT_SOF_1; break; - - case DEMOD_AWAIT_SOF_1: + + case STATE_TAG_AWAIT_SOF_1: // calculate the correlation in real time. Look at differences only. - Demod->SOF_corr += Demod->SOF_correlator[Demod->SOF_low++]; - Demod->SOF_corr -= 2*Demod->SOF_correlator[Demod->SOF_high++]; - Demod->SOF_last++; - Demod->SOF_low &= (SOF_CORRELATOR_LEN-1); - Demod->SOF_high &= (SOF_CORRELATOR_LEN-1); - Demod->SOF_last &= (SOF_CORRELATOR_LEN-1); - Demod->SOF_correlator[Demod->SOF_last] = AMPLITUDE(ci,cq); - Demod->SOF_corr += Demod->SOF_correlator[Demod->SOF_last]; + DecodeTag->SOF_corr += DecodeTag->SOF_correlator[DecodeTag->SOF_low++]; + DecodeTag->SOF_corr -= 2*DecodeTag->SOF_correlator[DecodeTag->SOF_high++]; + DecodeTag->SOF_last++; + DecodeTag->SOF_low &= (SOF_CORRELATOR_LEN-1); + DecodeTag->SOF_high &= (SOF_CORRELATOR_LEN-1); + DecodeTag->SOF_last &= (SOF_CORRELATOR_LEN-1); + DecodeTag->SOF_correlator[DecodeTag->SOF_last] = AMPLITUDE(ci,cq); + DecodeTag->SOF_corr += DecodeTag->SOF_correlator[DecodeTag->SOF_last]; // if correlation increases for 10 consecutive samples, we are close to maximum correlation - if (Demod->SOF_corr > Demod->SOF_corr_prev + SUBCARRIER_DETECT_THRESHOLD) { - Demod->posCount++; + if (DecodeTag->SOF_corr > DecodeTag->SOF_corr_prev + SUBCARRIER_DETECT_THRESHOLD) { + DecodeTag->posCount++; } else { - Demod->posCount = 0; + DecodeTag->posCount = 0; } - if (Demod->posCount == 10) { // correlation increased 10 times - Demod->state = DEMOD_AWAIT_SOF_2; + if (DecodeTag->posCount == 10) { // correlation increased 10 times + DecodeTag->state = STATE_TAG_AWAIT_SOF_2; } - - Demod->SOF_corr_prev = Demod->SOF_corr; - + + DecodeTag->SOF_corr_prev = DecodeTag->SOF_corr; + break; - case DEMOD_AWAIT_SOF_2: + case STATE_TAG_AWAIT_SOF_2: // calculate the correlation in real time. Look at differences only. - Demod->SOF_corr += Demod->SOF_correlator[Demod->SOF_low++]; - Demod->SOF_corr -= 2*Demod->SOF_correlator[Demod->SOF_high++]; - Demod->SOF_last++; - Demod->SOF_low &= (SOF_CORRELATOR_LEN-1); - Demod->SOF_high &= (SOF_CORRELATOR_LEN-1); - Demod->SOF_last &= (SOF_CORRELATOR_LEN-1); - Demod->SOF_correlator[Demod->SOF_last] = AMPLITUDE(ci,cq); - Demod->SOF_corr += Demod->SOF_correlator[Demod->SOF_last]; + DecodeTag->SOF_corr += DecodeTag->SOF_correlator[DecodeTag->SOF_low++]; + DecodeTag->SOF_corr -= 2*DecodeTag->SOF_correlator[DecodeTag->SOF_high++]; + DecodeTag->SOF_last++; + DecodeTag->SOF_low &= (SOF_CORRELATOR_LEN-1); + DecodeTag->SOF_high &= (SOF_CORRELATOR_LEN-1); + DecodeTag->SOF_last &= (SOF_CORRELATOR_LEN-1); + DecodeTag->SOF_correlator[DecodeTag->SOF_last] = AMPLITUDE(ci,cq); + DecodeTag->SOF_corr += DecodeTag->SOF_correlator[DecodeTag->SOF_last]; - if (Demod->SOF_corr >= Demod->SOF_corr_prev) { // we are looking for the maximum correlation - Demod->SOF_corr_prev = Demod->SOF_corr; + if (DecodeTag->SOF_corr >= DecodeTag->SOF_corr_prev) { // we are looking for the maximum correlation + DecodeTag->SOF_corr_prev = DecodeTag->SOF_corr; } else { - Demod->lastBit = SOF_PART1; // detected 1st part of SOF - Demod->sum1 = Demod->SOF_correlator[Demod->SOF_last]; - Demod->sum2 = 0; - Demod->posCount = 2; - Demod->state = DEMOD_RECEIVING_DATA; + DecodeTag->lastBit = SOF_PART1; // detected 1st part of SOF + DecodeTag->sum1 = DecodeTag->SOF_correlator[DecodeTag->SOF_last]; + DecodeTag->sum2 = 0; + DecodeTag->posCount = 2; + DecodeTag->state = STATE_TAG_RECEIVING_DATA; LED_C_ON(); } - + break; - case DEMOD_RECEIVING_DATA: - if (Demod->posCount == 1) { - Demod->sum1 = 0; - Demod->sum2 = 0; + case STATE_TAG_RECEIVING_DATA: + if (DecodeTag->posCount == 1) { + DecodeTag->sum1 = 0; + DecodeTag->sum2 = 0; } - if (Demod->posCount <= 4) { - Demod->sum1 += AMPLITUDE(ci, cq); + if (DecodeTag->posCount <= 4) { + DecodeTag->sum1 += AMPLITUDE(ci, cq); } else { - Demod->sum2 += AMPLITUDE(ci, cq); + DecodeTag->sum2 += AMPLITUDE(ci, cq); } - if (Demod->posCount == 8) { - int16_t corr_1 = (Demod->sum2 - Demod->sum1) / 4; - int16_t corr_0 = (Demod->sum1 - Demod->sum2) / 4; - int16_t corr_EOF = (Demod->sum1 + Demod->sum2) / 8; + if (DecodeTag->posCount == 8) { + int16_t corr_1 = (DecodeTag->sum2 - DecodeTag->sum1) / 4; + int16_t corr_0 = (DecodeTag->sum1 - DecodeTag->sum2) / 4; + int16_t corr_EOF = (DecodeTag->sum1 + DecodeTag->sum2) / 8; if (corr_EOF > corr_0 && corr_EOF > corr_1) { - Demod->state = DEMOD_AWAIT_EOF; + DecodeTag->state = STATE_TAG_AWAIT_EOF; } else if (corr_1 > corr_0) { // logic 1 - if (Demod->lastBit == SOF_PART1) { // still part of SOF - Demod->lastBit = SOF_PART2; + if (DecodeTag->lastBit == SOF_PART1) { // still part of SOF + DecodeTag->lastBit = SOF_PART2; } else { - Demod->lastBit = LOGIC1; - Demod->shiftReg >>= 1; - Demod->shiftReg |= 0x80; - Demod->bitCount++; - if (Demod->bitCount == 8) { - Demod->output[Demod->len] = Demod->shiftReg; - Demod->len++; - Demod->bitCount = 0; - Demod->shiftReg = 0; + DecodeTag->lastBit = LOGIC1; + DecodeTag->shiftReg >>= 1; + DecodeTag->shiftReg |= 0x80; + DecodeTag->bitCount++; + if (DecodeTag->bitCount == 8) { + DecodeTag->output[DecodeTag->len] = DecodeTag->shiftReg; + DecodeTag->len++; + DecodeTag->bitCount = 0; + DecodeTag->shiftReg = 0; } } } else { // logic 0 - if (Demod->lastBit == SOF_PART1) { // incomplete SOF - Demod->state = DEMOD_UNSYNCD; + if (DecodeTag->lastBit == SOF_PART1) { // incomplete SOF + DecodeTag->state = STATE_TAG_UNSYNCD; LED_C_OFF(); } else { - Demod->lastBit = LOGIC0; - Demod->shiftReg >>= 1; - Demod->bitCount++; - if (Demod->bitCount == 8) { - Demod->output[Demod->len] = Demod->shiftReg; - Demod->len++; - Demod->bitCount = 0; - Demod->shiftReg = 0; + DecodeTag->lastBit = LOGIC0; + DecodeTag->shiftReg >>= 1; + DecodeTag->bitCount++; + if (DecodeTag->bitCount == 8) { + DecodeTag->output[DecodeTag->len] = DecodeTag->shiftReg; + DecodeTag->len++; + DecodeTag->bitCount = 0; + DecodeTag->shiftReg = 0; } } } - Demod->posCount = 0; + DecodeTag->posCount = 0; } - Demod->posCount++; + DecodeTag->posCount++; break; - - case DEMOD_AWAIT_EOF: - if (Demod->lastBit == LOGIC0) { // this was already part of EOF + + case STATE_TAG_AWAIT_EOF: + if (DecodeTag->lastBit == LOGIC0) { // this was already part of EOF LED_C_OFF(); return true; } else { - Demod->state = DEMOD_UNSYNCD; + DecodeTag->state = STATE_TAG_UNSYNCD; LED_C_OFF(); } break; default: - Demod->state = DEMOD_UNSYNCD; + DecodeTag->state = STATE_TAG_UNSYNCD; LED_C_OFF(); break; } @@ -452,15 +515,14 @@ static RAMFUNC int Handle15693SamplesDemod(int8_t ci, int8_t cq, Demod_t *Demod) } -static void DemodInit(Demod_t* Demod, uint8_t* data) +static void DecodeTagInit(DecodeTag_t *DecodeTag, uint8_t *data) { - Demod->output = data; - Demod->state = DEMOD_UNSYNCD; + DecodeTag->output = data; + DecodeTag->state = STATE_TAG_UNSYNCD; } - /* - * Demodulate the samples we received from the tag, also log to tracebuffer + * Receive and decode the tag response, also log to tracebuffer */ static int GetIso15693AnswerFromTag(uint8_t* response, int timeout) { @@ -468,19 +530,12 @@ static int GetIso15693AnswerFromTag(uint8_t* response, int timeout) int lastRxCounter, samples = 0; int8_t ci, cq; bool gotFrame = false; - - // Allocate memory from BigBuf for some buffers - // free all previous allocations first - BigBuf_free(); - // The DMA buffer, used to stream samples from the FPGA - uint16_t* dmaBuf = (uint16_t*) BigBuf_malloc(ISO15693_DMA_BUFFER_SIZE * sizeof(uint16_t)); + uint16_t dmaBuf[ISO15693_DMA_BUFFER_SIZE]; - // the Demodulatur data structure - Demod_t* Demod = (Demod_t*) BigBuf_malloc(sizeof(Demod_t)); - - // Set up the demodulator for tag -> reader responses. - DemodInit(Demod, response); + // the Decoder data structure + DecodeTag_t DecodeTag; + DecodeTagInit(&DecodeTag, response); // wait for last transfer to complete while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)); @@ -491,7 +546,6 @@ static int GetIso15693AnswerFromTag(uint8_t* response, int timeout) // Setup and start DMA. FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); FpgaSetupSscDma((uint8_t*) dmaBuf, ISO15693_DMA_BUFFER_SIZE); - uint16_t *upTo = dmaBuf; lastRxCounter = ISO15693_DMA_BUFFER_SIZE; @@ -502,7 +556,7 @@ static int GetIso15693AnswerFromTag(uint8_t* response, int timeout) } if (behindBy < 1) continue; - + ci = (int8_t)(*upTo >> 8); cq = (int8_t)(*upTo & 0xff); @@ -518,156 +572,367 @@ static int GetIso15693AnswerFromTag(uint8_t* response, int timeout) } samples++; - if (Handle15693SamplesDemod(ci, cq, Demod)) { + if (Handle15693SamplesFromTag(ci, cq, &DecodeTag)) { gotFrame = true; break; } - if(samples > timeout && Demod->state < DEMOD_RECEIVING_DATA) { - Demod->len = 0; + if(samples > timeout && DecodeTag.state < STATE_TAG_RECEIVING_DATA) { + DecodeTag.len = 0; break; } + } FpgaDisableSscDma(); - if (DEBUG) Dbprintf("max behindby = %d, samples = %d, gotFrame = %d, Demod.state = %d, Demod.len = %d, Demod.bitCount = %d, Demod.posCount = %d", - maxBehindBy, samples, gotFrame, Demod->state, Demod->len, Demod->bitCount, Demod->posCount); + if (DEBUG) Dbprintf("max behindby = %d, samples = %d, gotFrame = %d, Decoder: state = %d, len = %d, bitCount = %d, posCount = %d", + maxBehindBy, samples, gotFrame, DecodeTag.state, DecodeTag.len, DecodeTag.bitCount, DecodeTag.posCount); - if (tracing && Demod->len > 0) { - uint8_t parity[MAX_PARITY_SIZE]; - LogTrace(Demod->output, Demod->len, 0, 0, parity, false); + if (tracing && DecodeTag.len > 0) { + LogTrace(DecodeTag.output, DecodeTag.len, 0, 0, NULL, false); } - return Demod->len; + return DecodeTag.len; } -// Now the GetISO15693 message from sniffing command -// TODO: fix it. This cannot work for several reasons: -// 1. Carrier is switched on during sniffing? -// 2. We most probable miss the next reader command when demodulating -static int GetIso15693AnswerFromSniff(uint8_t *receivedResponse, int maxLen, int *samples, int *elapsed) +//============================================================================= +// An ISO15693 decoder for reader commands. +// +// This function is called 4 times per bit (every 2 subcarrier cycles). +// Subcarrier frequency fs is 848kHz, 1/fs = 1,18us, i.e. function is called every 2,36us +// LED handling: +// LED B -> ON once we have received the SOF and are expecting the rest. +// LED B -> OFF once we have received EOF or are in error state or unsynced +// +// Returns: true if we received a EOF +// false if we are still waiting for some more +//============================================================================= + +typedef struct DecodeReader { + enum { + STATE_READER_UNSYNCD, + STATE_READER_AWAIT_1ST_RISING_EDGE_OF_SOF, + STATE_READER_AWAIT_2ND_FALLING_EDGE_OF_SOF, + STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF, + STATE_READER_AWAIT_END_OF_SOF_1_OUT_OF_4, + STATE_READER_RECEIVE_DATA_1_OUT_OF_4, + STATE_READER_RECEIVE_DATA_1_OUT_OF_256 + } state; + enum { + CODING_1_OUT_OF_4, + CODING_1_OUT_OF_256 + } Coding; + uint8_t shiftReg; + uint8_t bitCount; + int byteCount; + int byteCountMax; + int posCount; + int sum1, sum2; + uint8_t *output; +} DecodeReader_t; + + +static int Handle15693SampleFromReader(uint8_t bit, DecodeReader_t* DecodeReader) { - uint8_t *dest = BigBuf_get_addr(); - -// NOW READ RESPONSE - LED_D_ON(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); - //spindelay(60); // greg - experiment to get rid of some of the 0 byte/failed reads - for(int c = 0; c < BIGBUF_SIZE; ) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - uint16_t iq = AT91C_BASE_SSC->SSC_RHR; - // The samples are correlations against I and Q versions of the - // tone that the tag AM-modulates. We just want power, - // so abs(I) + abs(Q) is close to what we want. - int8_t i = (int8_t)(iq >> 8); - int8_t q = (int8_t)(iq & 0xff); - uint8_t r = AMPLITUDE(i, q); - dest[c++] = r; - } - } - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LED_D_OFF(); - - ////////////////////////////////////////// - /////////// DEMODULATE /////////////////// - ////////////////////////////////////////// - - int i, j; - int max = 0, maxPos=0; - - int skip = 2; - - // First, correlate for SOF - for(i = 0; i < 38000; i++) { - int corr = 0; - for(j = 0; j < arraylen(FrameSOF); j += skip) { - corr += FrameSOF[j]*dest[i+(j/skip)]; - } - if(corr > max) { - max = corr; - maxPos = i; - } - } - - if (DEBUG) Dbprintf("SOF at %d, correlation %d", maxPos,max/(arraylen(FrameSOF)/skip)); - - int k = 0; // this will be our return value - - // greg - If correlation is less than 1 then there's little point in continuing - if ((max/(arraylen(FrameSOF)/skip)) >= 1) // THIS SHOULD BE 1 - { - - i = maxPos + arraylen(FrameSOF)/skip; - - uint8_t outBuf[20]; - memset(outBuf, 0, sizeof(outBuf)); - uint8_t mask = 0x01; - for(;;) { - int corr0 = 0, corr00 = 0, corr01 = 0, corr1 = 0, corrEOF = 0; - for(j = 0; j < arraylen(Logic0); j += skip) { - corr0 += Logic0[j]*dest[i+(j/skip)]; + switch(DecodeReader->state) { + case STATE_READER_UNSYNCD: + if(!bit) { + // we went low, so this could be the beginning of a SOF + DecodeReader->state = STATE_READER_AWAIT_1ST_RISING_EDGE_OF_SOF; + DecodeReader->posCount = 1; } - corr01 = corr00 = corr0; - for(j = 0; j < arraylen(Logic0); j += skip) { - corr00 += Logic0[j]*dest[i+arraylen(Logic0)/skip+(j/skip)]; - corr01 += Logic1[j]*dest[i+arraylen(Logic0)/skip+(j/skip)]; - } - for(j = 0; j < arraylen(Logic1); j += skip) { - corr1 += Logic1[j]*dest[i+(j/skip)]; - } - for(j = 0; j < arraylen(FrameEOF); j += skip) { - corrEOF += FrameEOF[j]*dest[i+(j/skip)]; - } - // Even things out by the length of the target waveform. - corr00 *= 2; - corr01 *= 2; - corr0 *= 4; - corr1 *= 4; - - if(corrEOF > corr1 && corrEOF > corr00 && corrEOF > corr01) { - if (DEBUG) Dbprintf("EOF at %d, correlation %d (corr01: %d, corr00: %d, corr1: %d, corr0: %d)", - i, corrEOF, corr01, corr00, corr1, corr0); - break; - } else if(corr1 > corr0) { - i += arraylen(Logic1)/skip; - outBuf[k] |= mask; + break; + + case STATE_READER_AWAIT_1ST_RISING_EDGE_OF_SOF: + DecodeReader->posCount++; + if(bit) { // detected rising edge + if(DecodeReader->posCount < 4) { // rising edge too early (nominally expected at 5) + DecodeReader->state = STATE_READER_UNSYNCD; + } else { // SOF + DecodeReader->state = STATE_READER_AWAIT_2ND_FALLING_EDGE_OF_SOF; + } } else { - i += arraylen(Logic0)/skip; + if(DecodeReader->posCount > 5) { // stayed low for too long + DecodeReader->state = STATE_READER_UNSYNCD; + } else { + // do nothing, keep waiting + } } - mask <<= 1; - if(mask == 0) { - k++; - mask = 0x01; + break; + + case STATE_READER_AWAIT_2ND_FALLING_EDGE_OF_SOF: + DecodeReader->posCount++; + if(!bit) { // detected a falling edge + if (DecodeReader->posCount < 20) { // falling edge too early (nominally expected at 21 earliest) + DecodeReader->state = STATE_READER_UNSYNCD; + } else if (DecodeReader->posCount < 23) { // SOF for 1 out of 4 coding + DecodeReader->Coding = CODING_1_OUT_OF_4; + DecodeReader->state = STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF; + } else if (DecodeReader->posCount < 28) { // falling edge too early (nominally expected at 29 latest) + DecodeReader->state = STATE_READER_UNSYNCD; + } else { // SOF for 1 out of 4 coding + DecodeReader->Coding = CODING_1_OUT_OF_256; + DecodeReader->state = STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF; + } + } else { + if(DecodeReader->posCount > 29) { // stayed high for too long + DecodeReader->state = STATE_READER_UNSYNCD; + } else { + // do nothing, keep waiting + } } - if((i+(int)arraylen(FrameEOF)/skip) >= BIGBUF_SIZE) { - DbpString("ran off end!"); + break; + + case STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF: + DecodeReader->posCount++; + if (bit) { // detected rising edge + if (DecodeReader->Coding == CODING_1_OUT_OF_256) { + if (DecodeReader->posCount < 32) { // rising edge too early (nominally expected at 33) + DecodeReader->state = STATE_READER_UNSYNCD; + } else { + DecodeReader->posCount = 1; + DecodeReader->bitCount = 0; + DecodeReader->byteCount = 0; + DecodeReader->sum1 = 1; + DecodeReader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_256; + LED_B_ON(); + } + } else { // CODING_1_OUT_OF_4 + if (DecodeReader->posCount < 24) { // rising edge too early (nominally expected at 25) + DecodeReader->state = STATE_READER_UNSYNCD; + } else { + DecodeReader->state = STATE_READER_AWAIT_END_OF_SOF_1_OUT_OF_4; + } + } + } else { + if (DecodeReader->Coding == CODING_1_OUT_OF_256) { + if (DecodeReader->posCount > 34) { // signal stayed low for too long + DecodeReader->state = STATE_READER_UNSYNCD; + } else { + // do nothing, keep waiting + } + } else { // CODING_1_OUT_OF_4 + if (DecodeReader->posCount > 26) { // signal stayed low for too long + DecodeReader->state = STATE_READER_UNSYNCD; + } else { + // do nothing, keep waiting + } + } + } + break; + + case STATE_READER_AWAIT_END_OF_SOF_1_OUT_OF_4: + DecodeReader->posCount++; + if (bit) { + if (DecodeReader->posCount == 33) { + DecodeReader->posCount = 1; + DecodeReader->bitCount = 0; + DecodeReader->byteCount = 0; + DecodeReader->sum1 = 1; + DecodeReader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_4; + LED_B_ON(); + } else { + // do nothing, keep waiting + } + } else { // unexpected falling edge + DecodeReader->state = STATE_READER_UNSYNCD; + } + break; + + case STATE_READER_RECEIVE_DATA_1_OUT_OF_4: + DecodeReader->posCount++; + if (DecodeReader->posCount == 1) { + DecodeReader->sum1 = bit; + } else if (DecodeReader->posCount <= 4) { + DecodeReader->sum1 += bit; + } else if (DecodeReader->posCount == 5) { + DecodeReader->sum2 = bit; + } else { + DecodeReader->sum2 += bit; + } + if (DecodeReader->posCount == 8) { + DecodeReader->posCount = 0; + int corr10 = DecodeReader->sum1 - DecodeReader->sum2; + int corr01 = DecodeReader->sum2 - DecodeReader->sum1; + int corr11 = (DecodeReader->sum1 + DecodeReader->sum2) / 2; + if (corr01 > corr11 && corr01 > corr10) { // EOF + LED_B_OFF(); // Finished receiving + DecodeReader->state = STATE_READER_UNSYNCD; + if (DecodeReader->byteCount != 0) { + return true; + } + } + if (corr10 > corr11) { // detected a 2bit position + DecodeReader->shiftReg >>= 2; + DecodeReader->shiftReg |= (DecodeReader->bitCount << 6); + } + if (DecodeReader->bitCount == 15) { // we have a full byte + DecodeReader->output[DecodeReader->byteCount++] = DecodeReader->shiftReg; + if (DecodeReader->byteCount > DecodeReader->byteCountMax) { + // buffer overflow, give up + LED_B_OFF(); + DecodeReader->state = STATE_READER_UNSYNCD; + } + DecodeReader->bitCount = 0; + } else { + DecodeReader->bitCount++; + } + } + break; + + case STATE_READER_RECEIVE_DATA_1_OUT_OF_256: + DecodeReader->posCount++; + if (DecodeReader->posCount == 1) { + DecodeReader->sum1 = bit; + } else if (DecodeReader->posCount <= 4) { + DecodeReader->sum1 += bit; + } else if (DecodeReader->posCount == 5) { + DecodeReader->sum2 = bit; + } else { + DecodeReader->sum2 += bit; + } + if (DecodeReader->posCount == 8) { + DecodeReader->posCount = 0; + int corr10 = DecodeReader->sum1 - DecodeReader->sum2; + int corr01 = DecodeReader->sum2 - DecodeReader->sum1; + int corr11 = (DecodeReader->sum1 + DecodeReader->sum2) / 2; + if (corr01 > corr11 && corr01 > corr10) { // EOF + LED_B_OFF(); // Finished receiving + DecodeReader->state = STATE_READER_UNSYNCD; + if (DecodeReader->byteCount != 0) { + return true; + } + } + if (corr10 > corr11) { // detected the bit position + DecodeReader->shiftReg = DecodeReader->bitCount; + } + if (DecodeReader->bitCount == 255) { // we have a full byte + DecodeReader->output[DecodeReader->byteCount++] = DecodeReader->shiftReg; + if (DecodeReader->byteCount > DecodeReader->byteCountMax) { + // buffer overflow, give up + LED_B_OFF(); + DecodeReader->state = STATE_READER_UNSYNCD; + } + } + DecodeReader->bitCount++; + } + break; + + default: + LED_B_OFF(); + DecodeReader->state = STATE_READER_UNSYNCD; + break; + } + + return false; +} + + +static void DecodeReaderInit(uint8_t *data, uint16_t max_len, DecodeReader_t* DecodeReader) +{ + DecodeReader->output = data; + DecodeReader->byteCountMax = max_len; + DecodeReader->state = STATE_READER_UNSYNCD; + DecodeReader->byteCount = 0; + DecodeReader->bitCount = 0; + DecodeReader->shiftReg = 0; +} + + +//----------------------------------------------------------------------------- +// Receive a command (from the reader to us, where we are the simulated tag), +// and store it in the given buffer, up to the given maximum length. Keeps +// spinning, waiting for a well-framed command, until either we get one +// (returns true) or someone presses the pushbutton on the board (false). +// +// Assume that we're called with the SSC (to the FPGA) and ADC path set +// correctly. +//----------------------------------------------------------------------------- + +static int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eof_time) +{ + int maxBehindBy = 0; + int lastRxCounter, samples = 0; + bool gotFrame = false; + uint8_t b; + + uint8_t dmaBuf[ISO15693_DMA_BUFFER_SIZE]; + + // the decoder data structure + DecodeReader_t DecodeReader; + DecodeReaderInit(received, max_len, &DecodeReader); + + // wait for last transfer to complete + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)); + + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_NO_MODULATION); + + // clear receive register and wait for next transfer + uint32_t temp = AT91C_BASE_SSC->SSC_RHR; + (void) temp; + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)) ; + + uint32_t bit_time = GetCountSspClk() & 0xfffffff8; + + // Setup and start DMA. + FpgaSetupSscDma(dmaBuf, ISO15693_DMA_BUFFER_SIZE); + uint8_t *upTo = dmaBuf; + lastRxCounter = ISO15693_DMA_BUFFER_SIZE; + + for(;;) { + int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & (ISO15693_DMA_BUFFER_SIZE-1); + if(behindBy > maxBehindBy) { + maxBehindBy = behindBy; + } + + if (behindBy < 1) continue; + + b = *upTo++; + lastRxCounter--; + if(upTo >= dmaBuf + ISO15693_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + upTo = dmaBuf; // start reading the circular buffer from the beginning + lastRxCounter += ISO15693_DMA_BUFFER_SIZE; + } + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and + AT91C_BASE_PDC_SSC->PDC_RNCR = ISO15693_DMA_BUFFER_SIZE; // DMA Next Counter registers + } + + for (int i = 7; i >= 0; i--) { + if (Handle15693SampleFromReader((b >> i) & 0x01, &DecodeReader)) { + *eof_time = bit_time + samples - DELAY_READER_TO_ARM; // end of EOF + gotFrame = true; break; } + samples++; } - if(mask != 0x01) { - DbpString("sniff: error, uneven octet! (discard extra bits!)"); - /// DbpString(" mask=%02x", mask); - } - // uint8_t str1 [8]; - // itoa(k,str1); - // strncat(str1," octets read",8); - - // DbpString( str1); // DbpString("%d octets", k); - - // for(i = 0; i < k; i+=3) { - // //DbpString("# %2d: %02x ", i, outBuf[i]); - // DbpIntegers(outBuf[i],outBuf[i+1],outBuf[i+2]); - // } - - for(i = 0; i < k; i++) { - receivedResponse[i] = outBuf[i]; - } - } // "end if correlation > 0" (max/(arraylen(FrameSOF)/skip)) - return k; // return the number of bytes demodulated -/// DbpString("CRC=%04x", Iso15693Crc(outBuf, k-2)); + if (gotFrame) { + break; + } + + if (BUTTON_PRESS()) { + DecodeReader.byteCount = 0; + break; + } + + WDT_HIT(); + } + + + FpgaDisableSscDma(); + + if (DEBUG) Dbprintf("max behindby = %d, samples = %d, gotFrame = %d, Decoder: state = %d, len = %d, bitCount = %d, posCount = %d", + maxBehindBy, samples, gotFrame, DecodeReader.state, DecodeReader.byteCount, DecodeReader.bitCount, DecodeReader.posCount); + + if (tracing && DecodeReader.byteCount > 0) { + LogTrace(DecodeReader.output, DecodeReader.byteCount, 0, 0, NULL, true); + } + + return DecodeReader.byteCount; } @@ -681,7 +946,7 @@ void AcquireRawAdcSamplesIso15693(void) { LEDsoff(); LED_A_ON(); - + uint8_t *dest = BigBuf_get_addr(); FpgaDownloadAndGo(FPGA_BITSTREAM_HF); @@ -718,7 +983,7 @@ void AcquireRawAdcSamplesIso15693(void) if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { uint16_t iq = AT91C_BASE_SSC->SSC_RHR; // The samples are correlations against I and Q versions of the - // tone that the tag AM-modulates. We just want power, + // tone that the tag AM-modulates. We just want power, // so abs(I) + abs(Q) is close to what we want. int8_t i = (int8_t)(iq >> 8); int8_t q = (int8_t)(iq & 0xff); @@ -732,7 +997,7 @@ void AcquireRawAdcSamplesIso15693(void) } -// TODO: there is no trigger condition. The 14000 samples represent a time frame of 66ms. +// TODO: there is no trigger condition. The 14000 samples represent a time frame of 66ms. // It is unlikely that we get something meaningful. // TODO: Currently we only record tag answers. Add tracing of reader commands. // TODO: would we get something at all? The carrier is switched on... @@ -740,7 +1005,7 @@ void RecordRawAdcSamplesIso15693(void) { LEDsoff(); LED_A_ON(); - + uint8_t *dest = BigBuf_get_addr(); FpgaDownloadAndGo(FPGA_BITSTREAM_HF); @@ -762,7 +1027,7 @@ void RecordRawAdcSamplesIso15693(void) if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { uint16_t iq = AT91C_BASE_SSC->SSC_RHR; // The samples are correlations against I and Q versions of the - // tone that the tag AM-modulates. We just want power, + // tone that the tag AM-modulates. We just want power, // so abs(I) + abs(Q) is close to what we want. int8_t i = (int8_t)(iq >> 8); int8_t q = (int8_t)(iq & 0xff); @@ -778,7 +1043,7 @@ void RecordRawAdcSamplesIso15693(void) } -// Initialize the proxmark as iso15k reader +// Initialize the proxmark as iso15k reader // (this might produces glitches that confuse some tags static void Iso15693InitReader() { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); @@ -851,7 +1116,7 @@ static void BuildReadBlockRequest(uint8_t *uid, uint8_t blockNumber ) // Block number to read cmd[10] = blockNumber;//0x00; //Now the CRC - crc = Crc(cmd, 11); // the crc needs to be calculated over 12 bytes + crc = Crc(cmd, 11); // the crc needs to be calculated over 11 bytes cmd[11] = crc & 0xff; cmd[12] = crc >> 8; @@ -860,15 +1125,13 @@ static void BuildReadBlockRequest(uint8_t *uid, uint8_t blockNumber ) // Now the VICC>VCD responses when we are simulating a tag -static void BuildInventoryResponse( uint8_t *uid) +static void BuildInventoryResponse(uint8_t *uid) { uint8_t cmd[12]; uint16_t crc; - // one sub-carrier, inventory, 1 slot, fast rate - // AFI is at bit 5 (1<<4) when doing an INVENTORY - //(1 << 2) | (1 << 5) | (1 << 1); - cmd[0] = 0; // + + cmd[0] = 0; // No error, no protocol format extension cmd[1] = 0; // DSFID (data storage format identifier). 0x00 = not supported // 64-bit UID cmd[2] = uid[7]; //0x32; @@ -884,21 +1147,21 @@ static void BuildInventoryResponse( uint8_t *uid) cmd[10] = crc & 0xff; cmd[11] = crc >> 8; - CodeIso15693AsReader(cmd, sizeof(cmd)); + CodeIso15693AsTag(cmd, sizeof(cmd)); } // Universal Method for sending to and recv bytes from a tag // init ... should we initialize the reader? -// speed ... 0 low speed, 1 hi speed +// speed ... 0 low speed, 1 hi speed // **recv will return you a pointer to the received data -// If you do not need the answer use NULL for *recv[] +// If you do not need the answer use NULL for *recv[] // return: lenght of received data int SendDataTag(uint8_t *send, int sendlen, bool init, int speed, uint8_t **recv) { LED_A_ON(); LED_B_OFF(); LED_C_OFF(); - + if (init) Iso15693InitReader(); int answerLen=0; @@ -912,22 +1175,22 @@ int SendDataTag(uint8_t *send, int sendlen, bool init, int speed, uint8_t **recv // high speed (1 out of 4) CodeIso15693AsReader(send, sendlen); } - - TransmitTo15693Tag(ToSend,ToSendMax); + + TransmitTo15693Tag(ToSend,ToSendMax); // Now wait for a response if (recv!=NULL) { - answerLen = GetIso15693AnswerFromTag(answer, 100); + answerLen = GetIso15693AnswerFromTag(answer, 100); *recv=answer; } LED_A_OFF(); - + return answerLen; } // -------------------------------------------------------------------- -// Debug Functions +// Debug Functions // -------------------------------------------------------------------- // Decodes a message from a tag and displays its metadata and content @@ -937,37 +1200,37 @@ void DbdecodeIso15693Answer(int len, uint8_t *d) { uint16_t crc; if (len>3) { - if (d[0]&(1<<3)) + if (d[0]&(1<<3)) strncat(status,"ProtExt ",DBD15STATLEN); - if (d[0]&1) { + if (d[0]&1) { // error strncat(status,"Error ",DBD15STATLEN); switch (d[1]) { - case 0x01: + case 0x01: strncat(status,"01:notSupp",DBD15STATLEN); break; - case 0x02: + case 0x02: strncat(status,"02:notRecog",DBD15STATLEN); break; - case 0x03: + case 0x03: strncat(status,"03:optNotSupp",DBD15STATLEN); break; - case 0x0f: + case 0x0f: strncat(status,"0f:noInfo",DBD15STATLEN); break; - case 0x10: + case 0x10: strncat(status,"10:dontExist",DBD15STATLEN); break; - case 0x11: + case 0x11: strncat(status,"11:lockAgain",DBD15STATLEN); break; - case 0x12: + case 0x12: strncat(status,"12:locked",DBD15STATLEN); break; - case 0x13: + case 0x13: strncat(status,"13:progErr",DBD15STATLEN); break; - case 0x14: + case 0x14: strncat(status,"14:lockErr",DBD15STATLEN); break; default: @@ -977,12 +1240,12 @@ void DbdecodeIso15693Answer(int len, uint8_t *d) { } else { strncat(status,"NoErr ",DBD15STATLEN); } - + crc=Crc(d,len-2); - if ( (( crc & 0xff ) == d[len-2]) && (( crc >> 8 ) == d[len-1]) ) + if ( (( crc & 0xff ) == d[len-2]) && (( crc >> 8 ) == d[len-1]) ) strncat(status,"CrcOK",DBD15STATLEN); else - strncat(status,"CrcFail!",DBD15STATLEN); + strncat(status,"CrcFail!",DBD15STATLEN); Dbprintf("%s",status); } @@ -1035,9 +1298,9 @@ void ReaderIso15693(uint32_t parameter) // Now send the IDENTIFY command BuildIdentifyRequest(); - + TransmitTo15693Tag(ToSend,ToSendMax); - + // Now wait for a response answerLen1 = GetIso15693AnswerFromTag(answer1, 100) ; @@ -1055,11 +1318,11 @@ void ReaderIso15693(uint32_t parameter) } Dbprintf("%d octets read from IDENTIFY request:", answerLen1); - DbdecodeIso15693Answer(answerLen1,answer1); - Dbhexdump(answerLen1,answer1,true); + DbdecodeIso15693Answer(answerLen1, answer1); + Dbhexdump(answerLen1, answer1, false); // UID is reverse - if (answerLen1 >= 12) + if (answerLen1 >= 12) Dbprintf("UID = %02hX%02hX%02hX%02hX%02hX%02hX%02hX%02hX", TagUID[7],TagUID[6],TagUID[5],TagUID[4], TagUID[3],TagUID[2],TagUID[1],TagUID[0]); @@ -1076,123 +1339,113 @@ void ReaderIso15693(uint32_t parameter) // read all pages if (answerLen1 >= 12 && DEBUG) { uint8_t *answer2 = BigBuf_get_addr() + 4100; - int i=0; - while (i<32) { // sanity check, assume max 32 pages - BuildReadBlockRequest(TagUID,i); - TransmitTo15693Tag(ToSend,ToSendMax); + int i = 0; + while (i < 32) { // sanity check, assume max 32 pages + BuildReadBlockRequest(TagUID, i); + TransmitTo15693Tag(ToSend, ToSendMax); int answerLen2 = GetIso15693AnswerFromTag(answer2, 100); - if (answerLen2>0) { - Dbprintf("READ SINGLE BLOCK %d returned %d octets:",i,answerLen2); - DbdecodeIso15693Answer(answerLen2,answer2); - Dbhexdump(answerLen2,answer2,true); - if ( *((uint32_t*) answer2) == 0x07160101 ) break; // exit on NoPageErr - } + if (answerLen2 > 0) { + Dbprintf("READ SINGLE BLOCK %d returned %d octets:", i, answerLen2); + DbdecodeIso15693Answer(answerLen2, answer2); + Dbhexdump(answerLen2, answer2, false); + if ( *((uint32_t*) answer2) == 0x07160101 ) break; // exit on NoPageErr + } i++; - } + } } - // for the time being, switch field off to protect rdv4.0 + // for the time being, switch field off to protect rdv4.0 // note: this prevents using hf 15 cmd with s option - which isn't implemented yet anyway FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); - + LED_A_OFF(); } -// Simulate an ISO15693 TAG, perform anti-collision and then print any reader commands -// all demodulation performed in arm rather than host. - greg + +// Simulate an ISO15693 TAG. +// For Inventory command: print command and send Inventory Response with given UID +// TODO: interpret other reader commands and send appropriate response void SimTagIso15693(uint32_t parameter, uint8_t *uid) { LEDsoff(); LED_A_ON(); - int answerLen1 = 0; - int samples = 0; - int elapsed = 0; - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - - uint8_t *buf = BigBuf_get_addr() + 4000; - memset(buf, 0x00, 100); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_NO_MODULATION); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); - // Start from off (no field generated) - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(200); + StartCountSspClk(); + + uint8_t cmd[ISO15693_MAX_COMMAND_LENGTH]; + + // Build a suitable response to the reader INVENTORY command + BuildInventoryResponse(uid); // Listen to reader - answerLen1 = GetIso15693AnswerFromSniff(buf, 100, &samples, &elapsed) ; + while (!BUTTON_PRESS()) { + uint32_t eof_time = 0, start_time = 0; + int cmd_len = GetIso15693CommandFromReader(cmd, sizeof(cmd), &eof_time); - if (answerLen1 >=1) // we should do a better check than this - { - // Build a suitable reponse to the reader INVENTORY cocmmand - // not so obsvious, but in the call to BuildInventoryResponse, the command is copied to the global ToSend buffer used below. - - BuildInventoryResponse(uid); - - TransmitTo15693Reader(ToSend,ToSendMax); + if ((cmd_len >= 5) && (cmd[0] & ISO15693_REQ_INVENTORY) && (cmd[1] == ISO15693_INVENTORY)) { // TODO: check more flags + bool slow = !(cmd[0] & ISO15693_REQ_DATARATE_HIGH); + start_time = eof_time + DELAY_ISO15693_VCD_TO_VICC - DELAY_ARM_TO_READER; + TransmitTo15693Reader(ToSend, ToSendMax, start_time, slow); + } + + Dbprintf("%d bytes read from reader:", cmd_len); + Dbhexdump(cmd_len, cmd, false); } - Dbprintf("%d octets read from reader command: %x %x %x %x %x %x %x %x %x", answerLen1, - buf[0], buf[1], buf[2], buf[3], - buf[4], buf[5], buf[6], buf[7], buf[8]); - - Dbprintf("Simulationg uid: %x %x %x %x %x %x %x %x", - uid[0], uid[1], uid[2], uid[3], - uid[4], uid[5], uid[6], uid[7]); - LEDsoff(); } // Since there is no standardized way of reading the AFI out of a tag, we will brute force it // (some manufactures offer a way to read the AFI, though) -void BruteforceIso15693Afi(uint32_t speed) -{ +void BruteforceIso15693Afi(uint32_t speed) +{ LEDsoff(); LED_A_ON(); - + uint8_t data[20]; uint8_t *recv=data; int datalen=0, recvlen=0; - + Iso15693InitReader(); - + // first without AFI - // Tags should respond wihtout AFI and with AFI=0 even when AFI is active - - data[0]=ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | - ISO15_REQ_INVENTORY | ISO15_REQINV_SLOT1; - data[1]=ISO15_CMD_INVENTORY; - data[2]=0; // mask length - datalen=AddCrc(data,3); - recvlen=SendDataTag(data, datalen, false, speed, &recv); + // Tags should respond without AFI and with AFI=0 even when AFI is active + + data[0] = ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_INVENTORY | ISO15693_REQINV_SLOT1; + data[1] = ISO15693_INVENTORY; + data[2] = 0; // mask length + datalen = AddCrc(data,3); + recvlen = SendDataTag(data, datalen, false, speed, &recv); WDT_HIT(); if (recvlen>=12) { Dbprintf("NoAFI UID=%s",sprintUID(NULL,&recv[2])); } - + // now with AFI - - data[0]=ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | - ISO15_REQ_INVENTORY | ISO15_REQINV_AFI | ISO15_REQINV_SLOT1; - data[1]=ISO15_CMD_INVENTORY; - data[2]=0; // AFI - data[3]=0; // mask length - + + data[0] = ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_INVENTORY | ISO15693_REQINV_AFI | ISO15693_REQINV_SLOT1; + data[1] = ISO15693_INVENTORY; + data[2] = 0; // AFI + data[3] = 0; // mask length + for (int i=0;i<256;i++) { data[2]=i & 0xFF; datalen=AddCrc(data,4); recvlen=SendDataTag(data, datalen, false, speed, &recv); WDT_HIT(); if (recvlen>=12) { - Dbprintf("AFI=%i UID=%s",i,sprintUID(NULL,&recv[2])); + Dbprintf("AFI=%i UID=%s", i, sprintUID(NULL,&recv[2])); } - } + } Dbprintf("AFI Bruteforcing done."); - + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); } @@ -1202,31 +1455,31 @@ void DirectTag15693Command(uint32_t datalen, uint32_t speed, uint32_t recv, uint int recvlen=0; uint8_t *recvbuf = BigBuf_get_addr(); - + LED_A_ON(); - + if (DEBUG) { Dbprintf("SEND"); - Dbhexdump(datalen,data,true); + Dbhexdump(datalen, data, false); } - + recvlen = SendDataTag(data, datalen, true, speed, (recv?&recvbuf:NULL)); - if (recv) { + if (recv) { cmd_send(CMD_ACK, recvlen>48?48:recvlen, 0, 0, recvbuf, 48); - + if (DEBUG) { Dbprintf("RECV"); - DbdecodeIso15693Answer(recvlen,recvbuf); - Dbhexdump(recvlen,recvbuf,true); + DbdecodeIso15693Answer(recvlen,recvbuf); + Dbhexdump(recvlen, recvbuf, false); } } - // for the time being, switch field off to protect rdv4.0 + // for the time being, switch field off to protect rdv4.0 // note: this prevents using hf 15 cmd with s option - which isn't implemented yet anyway FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); - + LED_A_OFF(); } diff --git a/armsrc/util.c b/armsrc/util.c index a1b0f151..fbb6d489 100644 --- a/armsrc/util.c +++ b/armsrc/util.c @@ -373,13 +373,13 @@ void StartCountSspClk() AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK // TC1 Clock = MCK(48MHz)/2 = 24MHz | AT91C_TC_CPCSTOP // Stop clock on RC compare | AT91C_TC_EEVTEDG_RISING // Trigger on rising edge of Event - | AT91C_TC_EEVT_TIOB // Event-Source: TIOB1 (= ssp_clk from FPGA = 13,56MHz/16) + | AT91C_TC_EEVT_TIOB // Event-Source: TIOB1 (= ssp_clk from FPGA = 13,56MHz/16 ... 13,56MHz/4) | AT91C_TC_ENETRG // Enable external trigger event | AT91C_TC_WAVESEL_UP // Upmode without automatic trigger on RC compare | AT91C_TC_WAVE // Waveform Mode | AT91C_TC_AEEVT_SET // Set TIOA1 on external event | AT91C_TC_ACPC_CLEAR; // Clear TIOA1 on RC Compare - AT91C_BASE_TC1->TC_RC = 0x04; // RC Compare value = 0x04 + AT91C_BASE_TC1->TC_RC = 0x02; // RC Compare value = 0x02 // use TC0 to count TIOA1 pulses AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; // disable TC0 @@ -402,7 +402,7 @@ void StartCountSspClk() AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN; // enable TC2 // - // synchronize the counter with the ssp_frame signal. Note: FPGA must be in any iso14443 mode, otherwise SSC_FRAME and SSC_CLK signals would not be present + // synchronize the counter with the ssp_frame signal. Note: FPGA must be in a FPGA mode with SSC transfer, otherwise SSC_FRAME and SSC_CLK signals would not be present // while(!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME)); // wait for ssp_frame to go high (start of frame) while(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME); // wait for ssp_frame to be low @@ -430,15 +430,15 @@ void ResetSspClk(void) { } -uint32_t RAMFUNC GetCountSspClk(){ - uint32_t tmp_count; - tmp_count = (AT91C_BASE_TC2->TC_CV << 16) | AT91C_BASE_TC0->TC_CV; - if ((tmp_count & 0x0000ffff) == 0) { //small chance that we may have missed an increment in TC2 - return (AT91C_BASE_TC2->TC_CV << 16); - } - else { - return tmp_count; - } +uint32_t GetCountSspClk(){ + uint32_t hi, lo; + + do { + hi = AT91C_BASE_TC2->TC_CV; + lo = AT91C_BASE_TC0->TC_CV; + } while(hi != AT91C_BASE_TC2->TC_CV); + + return (hi << 16) | lo; } diff --git a/client/cmdhf15.c b/client/cmdhf15.c index c116b001..e5f4af31 100644 --- a/client/cmdhf15.c +++ b/client/cmdhf15.c @@ -35,6 +35,7 @@ #include "util.h" #include "cmdparser.h" #include "iso15693tools.h" +#include "protocols.h" #include "cmdmain.h" #define FrameSOF Iso15693FrameSOF @@ -212,18 +213,17 @@ int getUID(uint8_t *buf) for (int retry=0;retry<3; retry++) { // don't give up the at the first try - req[0]= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | - ISO15_REQ_INVENTORY | ISO15_REQINV_SLOT1; - req[1]=ISO15_CMD_INVENTORY; - req[2]=0; // mask length - reqlen=AddCrc(req,3); - c.arg[0]=reqlen; + req[0] = ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_INVENTORY | ISO15693_REQINV_SLOT1; + req[1] = ISO15693_INVENTORY; + req[2] = 0; // mask length + reqlen = AddCrc(req,3); + c.arg[0] = reqlen; SendCommand(&c); if (WaitForResponseTimeout(CMD_ACK,&resp,1000)) { recv = resp.d.asBytes; - if (resp.arg[0]>=12 && ISO15_CRC_CHECK==Crc(recv,12)) { + if (resp.arg[0]>=12 && ISO15693_CRC_CHECK==Crc(recv,12)) { memcpy(buf,&recv[2],8); return 1; } @@ -424,6 +424,7 @@ int CmdHF15Sim(const char *Cmd) PrintAndLog("Starting simulating UID %02X %02X %02X %02X %02X %02X %02X %02X", uid[0],uid[1],uid[2],uid[3],uid[4], uid[5], uid[6], uid[7]); + PrintAndLog("Press the button to stop simulation"); UsbCommand c = {CMD_SIMTAG_ISO_15693, {0, 0, 0}}; memcpy(c.d.asBytes,uid,8); @@ -462,20 +463,19 @@ int CmdHF15DumpMem(const char*Cmd) { for (int retry=0; retry<5; retry++) { - req[0]= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | - ISO15_REQ_NONINVENTORY | ISO15_REQ_ADDRESS; - req[1]=ISO15_CMD_READ; + req[0]= ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_ADDRESS; + req[1] = ISO15693_READBLOCK; memcpy(&req[2],uid,8); - req[10]=blocknum; - reqlen=AddCrc(req,11); - c.arg[0]=reqlen; + req[10] = blocknum; + reqlen = AddCrc(req,11); + c.arg[0] = reqlen; SendCommand(&c); if (WaitForResponseTimeout(CMD_ACK,&resp,1000)) { recv = resp.d.asBytes; - if (ISO15_CRC_CHECK==Crc(recv,resp.arg[0])) { - if (!(recv[0] & ISO15_RES_ERROR)) { + if (ISO15693_CRC_CHECK==Crc(recv,resp.arg[0])) { + if (!(recv[0] & ISO15693_RES_ERROR)) { retry=0; *output=0; // reset outputstring sprintf(output, "Block %02x ",blocknum); @@ -499,7 +499,7 @@ int CmdHF15DumpMem(const char*Cmd) { // TODO: need fix // if (resp.arg[0]<3) // PrintAndLog("Lost Connection"); -// else if (ISO15_CRC_CHECK!=Crc(resp.d.asBytes,resp.arg[0])) +// else if (ISO15693_CRC_CHECK!=Crc(resp.d.asBytes,resp.arg[0])) // PrintAndLog("CRC Failed"); // else // PrintAndLog("Tag returned Error %i: %s",recv[1],TagErrorStr(recv[1])); @@ -547,12 +547,11 @@ int CmdHF15CmdInquiry(const char *Cmd) uint8_t *req=c.d.asBytes; int reqlen=0; - req[0]= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | - ISO15_REQ_INVENTORY | ISO15_REQINV_SLOT1; - req[1]=ISO15_CMD_INVENTORY; - req[2]=0; // mask length + req[0] = ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_INVENTORY | ISO15693_REQINV_SLOT1; + req[1] = ISO15693_INVENTORY; + req[2] = 0; // mask length reqlen=AddCrc(req,3); - c.arg[0]=reqlen; + c.arg[0] = reqlen; SendCommand(&c); @@ -706,7 +705,7 @@ int prepareHF15Cmd(char **cmd, UsbCommand *c, uint8_t iso15cmd[], int iso15cmdle while (**cmd==' ' || **cmd=='\t') (*cmd)++; if (strstr(*cmd,"-o")==*cmd) { - req[reqlen]=ISO15_REQ_OPTION; + req[reqlen]=ISO15693_REQ_OPTION; (*cmd)+=2; } @@ -721,36 +720,32 @@ int prepareHF15Cmd(char **cmd, UsbCommand *c, uint8_t iso15cmd[], int iso15cmdle case 's': case 'S': // you must have selected the tag earlier - req[reqlen++]|= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | - ISO15_REQ_NONINVENTORY | ISO15_REQ_SELECT; - memcpy(&req[reqlen],&iso15cmd[0],iso15cmdlen); - reqlen+=iso15cmdlen; + req[reqlen++] |= ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_SELECT; + memcpy(&req[reqlen],&iso15cmd[0],iso15cmdlen); + reqlen += iso15cmdlen; break; case 'u': case 'U': // unaddressed mode may not be supported by all vendors - req[reqlen++]|= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | - ISO15_REQ_NONINVENTORY; - memcpy(&req[reqlen],&iso15cmd[0],iso15cmdlen); - reqlen+=iso15cmdlen; + req[reqlen++] |= ISO15693_REQ_DATARATE_HIGH; + memcpy(&req[reqlen],&iso15cmd[0],iso15cmdlen); + reqlen += iso15cmdlen; break; case '*': // we scan for the UID ourself - req[reqlen++]|= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | - ISO15_REQ_NONINVENTORY | ISO15_REQ_ADDRESS; - memcpy(&req[reqlen],&iso15cmd[0],iso15cmdlen); + req[reqlen++] |= ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_ADDRESS; + memcpy(&req[reqlen],&iso15cmd[0],iso15cmdlen); reqlen+=iso15cmdlen; - if (!getUID(uid)) { - PrintAndLog("No Tag found"); - return 0; - } - memcpy(req+reqlen,uid,8); - PrintAndLog("Detected UID %s",sprintUID(NULL,uid)); - reqlen+=8; + if (!getUID(uid)) { + PrintAndLog("No Tag found"); + return 0; + } + memcpy(req+reqlen,uid,8); + PrintAndLog("Detected UID %s",sprintUID(NULL,uid)); + reqlen+=8; break; default: - req[reqlen++]|= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | - ISO15_REQ_NONINVENTORY | ISO15_REQ_ADDRESS; + req[reqlen++] |= ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_ADDRESS; memcpy(&req[reqlen],&iso15cmd[0],iso15cmdlen); reqlen+=iso15cmdlen; @@ -809,7 +804,7 @@ int CmdHF15CmdSysinfo(const char *Cmd) { return 0; } - prepareHF15Cmd(&cmd, &c,(uint8_t[]){ISO15_CMD_SYSINFO},1); + prepareHF15Cmd(&cmd, &c,(uint8_t[]){ISO15693_GET_SYSTEM_INFO},1); reqlen=c.arg[0]; reqlen=AddCrc(req,reqlen); @@ -819,8 +814,8 @@ int CmdHF15CmdSysinfo(const char *Cmd) { if (WaitForResponseTimeout(CMD_ACK,&resp,1000) && resp.arg[0]>2) { recv = resp.d.asBytes; - if (ISO15_CRC_CHECK==Crc(recv,resp.arg[0])) { - if (!(recv[0] & ISO15_RES_ERROR)) { + if (ISO15693_CRC_CHECK==Crc(recv,resp.arg[0])) { + if (!(recv[0] & ISO15693_RES_ERROR)) { *output=0; // reset outputstring for ( i=1; i2) { recv = resp.d.asBytes; - if (ISO15_CRC_CHECK==Crc(recv,resp.arg[0])) { - if (!(recv[0] & ISO15_RES_ERROR)) { + if (ISO15693_CRC_CHECK==Crc(recv,resp.arg[0])) { + if (!(recv[0] & ISO15693_RES_ERROR)) { *output=0; // reset outputstring for ( int i=1; i2) { recv = resp.d.asBytes; - if (ISO15_CRC_CHECK==Crc(recv,resp.arg[0])) { - if (!(recv[0] & ISO15_RES_ERROR)) { + if (ISO15693_CRC_CHECK==Crc(recv,resp.arg[0])) { + if (!(recv[0] & ISO15693_RES_ERROR)) { *output=0; // reset outputstring //sprintf(output, "Block %2i ",blocknum); for ( int i=1; i page num ; *cmd2 -> data @@ -1086,8 +1081,8 @@ int CmdHF15CmdWrite(const char *Cmd) { if (WaitForResponseTimeout(CMD_ACK,&resp,2000) && resp.arg[0]>2) { recv = resp.d.asBytes; - if (ISO15_CRC_CHECK==Crc(recv,resp.arg[0])) { - if (!(recv[0] & ISO15_RES_ERROR)) { + if (ISO15693_CRC_CHECK==Crc(recv,resp.arg[0])) { + if (!(recv[0] & ISO15693_RES_ERROR)) { PrintAndLog("OK"); } else { PrintAndLog("Tag returned Error %i: %s",recv[1],TagErrorStr(recv[1])); diff --git a/common/iso15693tools.h b/common/iso15693tools.h index ec63728f..96095fba 100644 --- a/common/iso15693tools.h +++ b/common/iso15693tools.h @@ -5,67 +5,9 @@ #define ISO15693_H__ // ISO15693 CRC -#define ISO15_CRC_PRESET (uint16_t)0xFFFF -#define ISO15_CRC_POLY (uint16_t)0x8408 -#define ISO15_CRC_CHECK ((uint16_t)(~0xF0B8 & 0xFFFF)) // use this for checking of a correct crc - -// REQUEST FLAGS - -#define ISO15_REQ_SUBCARRIER_SINGLE 0x00 // Tag should respond using one subcarrier (ASK) -#define ISO15_REQ_SUBCARRIER_TWO 0x01 // Tag should respond using two subcarriers (FSK) -#define ISO15_REQ_DATARATE_LOW 0x00 // Tag should respond using low data rate -#define ISO15_REQ_DATARATE_HIGH 0x02 // Tag should respond using high data rate -#define ISO15_REQ_NONINVENTORY 0x00 -#define ISO15_REQ_INVENTORY 0x04 // This is an inventory request - see inventory flags -#define ISO15_REQ_PROTOCOL_NONEXT 0x00 -#define ISO15_REQ_PROTOCOL_EXT 0x08 // RFU - -// REQUEST FLAGS when INVENTORY is not set - -#define ISO15_REQ_SELECT 0x10 // only selected cards response -#define ISO15_REQ_ADDRESS 0x20 // this req contains an address -#define ISO15_REQ_OPTION 0x40 // Command specific option selector - -//REQUEST FLAGS when INVENTORY is set - -#define ISO15_REQINV_AFI 0x10 // AFI Field is present -#define ISO15_REQINV_SLOT1 0x20 // 1 Slot -#define ISO15_REQINV_SLOT16 0x00 // 16 Slots -#define ISO15_REQINV_OPTION 0x40 // Command specific option selector - -//RESPONSE FLAGS -#define ISO15_RES_ERROR 0x01 -#define ISO15_RES_EXT 0x08 // Protocol Extention - -// RESPONSE ERROR CODES -#define ISO15_NOERROR 0x00 -#define ISO15_ERROR_CMD_NOT_SUP 0x01 // Command not supported -#define ISO15_ERROR_CMD_NOT_REC 0x02 // Command not recognized (eg. parameter error) -#define ISO15_ERROR_CMD_OPTION 0x03 // Command option not supported -#define ISO15_ERROR_GENERIC 0x0F // No additional Info about this error -#define ISO15_ERROR_BLOCK_UNAVAILABLE 0x10 -#define ISO15_ERROR_BLOCK_LOCKED_ALREADY 0x11 // cannot lock again -#define ISO15_ERROR_BLOCK_LOCKED 0x12 // cannot be changed -#define ISO15_ERROR_BLOCK_WRITE 0x13 // Writing was unsuccessful -#define ISO15_ERROR_BLOCL_WRITELOCK 0x14 // Locking was unsuccessful - -// COMMAND CODES -#define ISO15_CMD_INVENTORY 0x01 -#define ISO15_CMD_STAYQUIET 0x02 -#define ISO15_CMD_READ 0x20 -#define ISO15_CMD_WRITE 0x21 -#define ISO15_CMD_LOCK 0x22 -#define ISO15_CMD_READMULTI 0x23 -#define ISO15_CMD_WRITEMULTI 0x24 -#define ISO15_CMD_SELECT 0x25 -#define ISO15_CMD_RESET 0x26 -#define ISO15_CMD_WRITEAFI 0x27 -#define ISO15_CMD_LOCKAFI 0x28 -#define ISO15_CMD_WRITEDSFID 0x29 -#define ISO15_CMD_LOCKDSFID 0x2A -#define ISO15_CMD_SYSINFO 0x2B -#define ISO15_CMD_SECSTATUS 0x2C - +#define ISO15693_CRC_PRESET (uint16_t)0xFFFF +#define ISO15693_CRC_POLY (uint16_t)0x8408 +#define ISO15693_CRC_CHECK ((uint16_t)(~0xF0B8 & 0xFFFF)) // use this for checking of a correct crc uint16_t Iso15693Crc(uint8_t *v, int n); int Iso15693AddCrc(uint8_t *req, int n); diff --git a/common/protocols.h b/common/protocols.h index 9ba69d5c..06a80de1 100644 --- a/common/protocols.h +++ b/common/protocols.h @@ -167,22 +167,50 @@ NXP/Philips CUSTOM COMMANDS #define ISO14443B_COMPLETION 0x0F #define ISO14443B_AUTHENTICATE 0x0A -//First byte is 26 -#define ISO15693_INVENTORY 0x01 -#define ISO15693_STAYQUIET 0x02 -//First byte is 02 -#define ISO15693_READBLOCK 0x20 -#define ISO15693_WRITEBLOCK 0x21 -#define ISO15693_LOCKBLOCK 0x22 -#define ISO15693_READ_MULTI_BLOCK 0x23 -#define ISO15693_SELECT 0x25 -#define ISO15693_RESET_TO_READY 0x26 -#define ISO15693_WRITE_AFI 0x27 -#define ISO15693_LOCK_AFI 0x28 -#define ISO15693_WRITE_DSFID 0x29 -#define ISO15693_LOCK_DSFID 0x2A -#define ISO15693_GET_SYSTEM_INFO 0x2B -#define ISO15693_READ_MULTI_SECSTATUS 0x2C +// ISO15693 COMMANDS +#define ISO15693_INVENTORY 0x01 +#define ISO15693_STAYQUIET 0x02 +#define ISO15693_READBLOCK 0x20 +#define ISO15693_WRITEBLOCK 0x21 +#define ISO15693_LOCKBLOCK 0x22 +#define ISO15693_READ_MULTI_BLOCK 0x23 +#define ISO15693_SELECT 0x25 +#define ISO15693_RESET_TO_READY 0x26 +#define ISO15693_WRITE_AFI 0x27 +#define ISO15693_LOCK_AFI 0x28 +#define ISO15693_WRITE_DSFID 0x29 +#define ISO15693_LOCK_DSFID 0x2A +#define ISO15693_GET_SYSTEM_INFO 0x2B +#define ISO15693_READ_MULTI_SECSTATUS 0x2C + +// ISO15693 REQUEST FLAGS +#define ISO15693_REQ_SUBCARRIER_TWO (1<<0) +#define ISO15693_REQ_DATARATE_HIGH (1<<1) +#define ISO15693_REQ_INVENTORY (1<<2) +#define ISO15693_REQ_PROTOCOL_EXT (1<<3) // RFU +#define ISO15693_REQ_OPTION (1<<6) // Command specific option selector +// when REQ_INVENTORY is not set +#define ISO15693_REQ_SELECT (1<<4) // only selected cards response +#define ISO15693_REQ_ADDRESS (1<<5) // this req contains an address +// when REQ_INVENTORY is set +#define ISO15693_REQINV_AFI (1<<4) // AFI Field is present +#define ISO15693_REQINV_SLOT1 (1<<5) // 1 Slot (16 slots if not set) + +// ISO15693 RESPONSE FLAGS +#define ISO15693_RES_ERROR (1<<0) +#define ISO15693_RES_EXT (1<<3) // Protocol Extention + +// ISO15693 RESPONSE ERROR CODES +#define ISO15693_NOERROR 0x00 +#define ISO15693_ERROR_CMD_NOT_SUP 0x01 // Command not supported +#define ISO15693_ERROR_CMD_NOT_REC 0x02 // Command not recognized (eg. parameter error) +#define ISO15693_ERROR_CMD_OPTION 0x03 // Command option not supported +#define ISO15693_ERROR_GENERIC 0x0F // No additional Info about this error +#define ISO15693_ERROR_BLOCK_UNAVAILABLE 0x10 +#define ISO15693_ERROR_BLOCK_LOCKED_ALREADY 0x11 // cannot lock again +#define ISO15693_ERROR_BLOCK_LOCKED 0x12 // cannot be changed +#define ISO15693_ERROR_BLOCK_WRITE 0x13 // Writing was unsuccessful +#define ISO15693_ERROR_BLOCL_WRITELOCK 0x14 // Locking was unsuccessful // Topaz command set: diff --git a/fpga/hi_simulate.v b/fpga/hi_simulate.v index 78650c4a..8d70bb1b 100644 --- a/fpga/hi_simulate.v +++ b/fpga/hi_simulate.v @@ -16,6 +16,13 @@ // Jonathan Westhues, October 2006 //----------------------------------------------------------------------------- +// possible mod_types: +`define NO_MODULATION 3'b000 +`define MODULATE_BPSK 3'b001 +`define MODULATE_212K 3'b010 +`define MODULATE_424K 3'b100 +`define MODULATE_424K_8BIT 3'b101 + module hi_simulate( pck0, ck_1356meg, ck_1356megb, pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4, @@ -35,10 +42,6 @@ module hi_simulate( output dbg; input [2:0] mod_type; -// Power amp goes between LOW and tri-state, so pwr_hi (and pwr_lo) can -// always be low. -assign pwr_hi = 1'b0; -assign pwr_lo = 1'b0; // The comparator with hysteresis on the output from the peak detector. reg after_hysteresis; @@ -52,8 +55,8 @@ end // Divide 13.56 MHz to produce various frequencies for SSP_CLK -// and modulation. 11 bits allow for factors of up to /128. -reg [10:0] ssp_clk_divider; +// and modulation. +reg [7:0] ssp_clk_divider; always @(posedge adc_clk) ssp_clk_divider <= (ssp_clk_divider + 1); @@ -62,10 +65,10 @@ reg ssp_clk; always @(negedge adc_clk) begin - if(mod_type == 3'b101) + if(mod_type == `MODULATE_424K_8BIT) // Get bit every at 53KHz (every 8th carrier bit of 424kHz) ssp_clk <= ssp_clk_divider[7]; - else if(mod_type == 3'b010) + else if(mod_type == `MODULATE_212K) // Get next bit at 212kHz ssp_clk <= ssp_clk_divider[5]; else @@ -89,7 +92,7 @@ always @(negedge ssp_clk) reg ssp_frame; always @(ssp_frame_divider_to_arm or ssp_frame_divider_from_arm or mod_type) - if(mod_type == 3'b000) // not modulating, so listening, to ARM + if(mod_type == `NO_MODULATION) // not modulating, so listening, to ARM ssp_frame = (ssp_frame_divider_to_arm == 3'b000); else ssp_frame = (ssp_frame_divider_from_arm == 3'b000); @@ -102,27 +105,29 @@ always @(posedge ssp_clk) // Modulating carrier frequency is fc/64 (212kHz) to fc/16 (848kHz). Reuse ssp_clk divider for that. reg modulating_carrier; always @(mod_type or ssp_clk or ssp_dout) - if(mod_type == 3'b000) + if (mod_type == `NO_MODULATION) modulating_carrier <= 1'b0; // no modulation - else if(mod_type == 3'b001) + else if (mod_type == `MODULATE_BPSK) modulating_carrier <= ssp_dout ^ ssp_clk_divider[3]; // XOR means BPSK - else if(mod_type == 3'b010) + else if (mod_type == `MODULATE_212K) modulating_carrier <= ssp_dout & ssp_clk_divider[5]; // switch 212kHz subcarrier on/off - else if(mod_type == 3'b100 || mod_type == 3'b101) + else if (mod_type == `MODULATE_424K || mod_type == `MODULATE_424K_8BIT) modulating_carrier <= ssp_dout & ssp_clk_divider[4]; // switch 424kHz modulation on/off else modulating_carrier <= 1'b0; // yet unused -// This one is all LF, so doesn't matter -assign pwr_oe2 = modulating_carrier; -// Toggle only one of these, since we are already producing much deeper +// Load modulation. Toggle only one of these, since we are already producing much deeper // modulation than a real tag would. -assign pwr_oe1 = modulating_carrier; -assign pwr_oe4 = modulating_carrier; +assign pwr_hi = 1'b0; // HF antenna connected to GND +assign pwr_oe3 = 1'b0; // 10k Load +assign pwr_oe1 = modulating_carrier; // 33 Ohms Load +assign pwr_oe4 = modulating_carrier; // 33 Ohms Load + +// This is all LF and doesn't matter +assign pwr_lo = 1'b0; +assign pwr_oe2 = 1'b0; -// This one is always on, so that we can watch the carrier. -assign pwr_oe3 = 1'b0; assign dbg = ssp_din; From 3685f89cbd6c524c91b41bd41bd3db038b3f1b43 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Tue, 23 Oct 2018 19:50:37 +0200 Subject: [PATCH 304/310] small fix to please @dmaij's compiler (issue #703) --- armsrc/iso15693.c | 1 + 1 file changed, 1 insertion(+) diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index 9479c3c0..50432392 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -837,6 +837,7 @@ static void DecodeReaderInit(uint8_t *data, uint16_t max_len, DecodeReader_t* De DecodeReader->state = STATE_READER_UNSYNCD; DecodeReader->byteCount = 0; DecodeReader->bitCount = 0; + DecodeReader->posCount = 0; DecodeReader->shiftReg = 0; } From ae3340a0fb04bf8f8b8c874e0859d7d0886e2db1 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko <807634+merlokk@users.noreply.github.com> Date: Wed, 24 Oct 2018 19:58:12 +0300 Subject: [PATCH 305/310] Mfp commands (#698) * add write perso from https://github.com/Proxmark/proxmark3/blob/master/client/scripts/mifarePlus.lua * commit perso from https://github.com/Proxmark/proxmark3/blob/master/client/scripts/mifarePlus.lua#L184 * added errors https://github.com/Proxmark/proxmark3/blob/master/client/scripts/mifarePlus.lua#L91 * fix bug in CLIParamHexToBuf * added init card command * auth4 refactoring * add changelog --- CHANGELOG.md | 1 + client/Makefile | 2 + client/cliparser/cliparser.c | 2 +- client/cliparser/cliparser.h | 1 + client/cmdhf14a.c | 10 +- client/cmdhfmf.c | 114 +------------ client/cmdhfmfp.c | 302 +++++++++++++++++++++++++++++++++++ client/mifare4.c | 118 ++++++++++++++ client/mifare4.h | 30 ++++ common/polarssl/libpcrypto.c | 44 +++++ common/polarssl/libpcrypto.h | 20 +++ 11 files changed, 529 insertions(+), 115 deletions(-) create mode 100644 client/mifare4.c create mode 100644 client/mifare4.h create mode 100644 common/polarssl/libpcrypto.c create mode 100644 common/polarssl/libpcrypto.h diff --git a/CHANGELOG.md b/CHANGELOG.md index fbc40cda..5f1847e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac ### Fixed ### Added +- `hf mfp` group of commands (Merlok) ## [v3.1.0][2018-10-10] diff --git a/client/Makefile b/client/Makefile index c0a319a4..72d5080d 100644 --- a/client/Makefile +++ b/client/Makefile @@ -110,6 +110,7 @@ CMDSRCS = $(SRC_SMARTCARD) \ polarssl/bignum.c\ polarssl/rsa.c\ polarssl/sha1.c\ + polarssl/libpcrypto.c\ cliparser/argtable3.c\ cliparser/cliparser.c\ mfkey.c\ @@ -120,6 +121,7 @@ CMDSRCS = $(SRC_SMARTCARD) \ loclass/fileutils.c\ whereami.c\ mifarehost.c\ + mifare4.c\ parity.c\ crc.c \ crc16.c \ diff --git a/client/cliparser/cliparser.c b/client/cliparser/cliparser.c index 931d68cd..95422039 100644 --- a/client/cliparser/cliparser.c +++ b/client/cliparser/cliparser.c @@ -156,7 +156,7 @@ int CLIParamHexToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int int ibuf = 0; uint8_t buf[256] = {0}; - int res = CLIParamStrToBuf(argstr, buf, maxdatalen, &ibuf); + int res = CLIParamStrToBuf(argstr, buf, maxdatalen * 2, &ibuf); // *2 because here HEX if (res || !ibuf) return res; diff --git a/client/cliparser/cliparser.h b/client/cliparser/cliparser.h index 2f5ac317..c4bc7068 100644 --- a/client/cliparser/cliparser.h +++ b/client/cliparser/cliparser.h @@ -17,6 +17,7 @@ #define arg_getsize(a) (sizeof(a) / sizeof(a[0])) #define arg_get_lit(n)(((struct arg_lit*)argtable[n])->count) +#define arg_get_int_count(n)(((struct arg_int*)argtable[n])->count) #define arg_get_int(n)(((struct arg_int*)argtable[n])->ival[0]) #define arg_get_str(n)((struct arg_str*)argtable[n]) #define arg_get_str_len(n)(strlen(((struct arg_str*)argtable[n])->sval[0])) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 31654188..326eaf50 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -750,6 +750,11 @@ int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leav recv = resp.d.asBytes; int iLen = resp.arg[0]; + if(!iLen) { + PrintAndLog("14aRAW ERROR: No card response."); + return 1; + } + *dataoutlen = iLen - 2; if (*dataoutlen < 0) *dataoutlen = 0; @@ -766,11 +771,6 @@ int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leav memcpy(dataout, &recv[2], *dataoutlen); - if(!iLen) { - PrintAndLog("14aRAW ERROR: No card response."); - return 1; - } - // CRC Check if (iLen == -1) { PrintAndLog("14aRAW ERROR: ISO 14443A CRC error."); diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index eb85b8c5..83180e7d 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -29,7 +29,7 @@ #include "hardnested/hardnested_bf_core.h" #include "cliparser/cliparser.h" #include "cmdhf14a.h" -#include +#include "mifare4.h" #define NESTED_SECTOR_RETRY 10 // how often we try mfested() until we give up @@ -2637,50 +2637,12 @@ int CmdDecryptTraceCmds(const char *Cmd){ return tryDecryptWord(param_get32ex(Cmd,0,0,16),param_get32ex(Cmd,1,0,16),param_get32ex(Cmd,2,0,16),data,len/2); } -int aes_encode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length){ - uint8_t iiv[16] = {0}; - if (iv) - memcpy(iiv, iv, 16); - - aes_context aes; - aes_init(&aes); - if (aes_setkey_enc(&aes, key, 128)) - return 1; - if (aes_crypt_cbc(&aes, AES_ENCRYPT, length, iiv, input, output)) - return 2; - aes_free(&aes); - - return 0; -} - -int aes_decode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length){ - uint8_t iiv[16] = {0}; - if (iv) - memcpy(iiv, iv, 16); - - aes_context aes; - aes_init(&aes); - if (aes_setkey_dec(&aes, key, 128)) - return 1; - if (aes_crypt_cbc(&aes, AES_DECRYPT, length, iiv, input, output)) - return 2; - aes_free(&aes); - - return 0; -} - int CmdHF14AMfAuth4(const char *cmd) { uint8_t keyn[20] = {0}; int keynlen = 0; uint8_t key[16] = {0}; int keylen = 0; - 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}; - - + CLIParserInit("hf mf auth4", "Executes AES authentication command in ISO14443-4", "Usage:\n\thf mf auth4 4000 000102030405060708090a0b0c0d0e0f -> executes authentication\n" @@ -2694,8 +2656,8 @@ int CmdHF14AMfAuth4(const char *cmd) { }; CLIExecWithReturn(cmd, argtable, true); - CLIGetStrWithReturn(1, keyn, &keynlen); - CLIGetStrWithReturn(2, key, &keylen); + CLIGetHexWithReturn(1, keyn, &keynlen); + CLIGetHexWithReturn(2, key, &keylen); CLIParserFree(); if (keynlen != 2) { @@ -2708,73 +2670,7 @@ int CmdHF14AMfAuth4(const char *cmd) { return 1; } - uint8_t cmd1[] = {0x70, keyn[1], keyn[0], 0x00}; - int res = ExchangeRAW14a(cmd1, sizeof(cmd1), true, true, data, sizeof(data), &datalen); - if (res) { - PrintAndLog("ERROR exchande raw error: %d", res); - DropField(); - return 2; - } - - PrintAndLog("phase2: %s", sprint_hex(cmd2, 33)); - - res = ExchangeRAW14a(cmd2, sizeof(cmd2), false, false, data, sizeof(data), &datalen); - if (res) { - PrintAndLog("ERROR exchande raw error: %d", res); - DropField(); - return 4; - } - - PrintAndLog(">> %s", sprint_hex(datain, datainlen)); + + int res = ExchangeRAW14a(datain, datainlen, activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen); + + if(VerboseMode) + PrintAndLog("<<< %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 CmdHFMFPInfo(const char *cmd) { if (cmd && strlen(cmd) > 0) @@ -103,10 +161,254 @@ int CmdHFMFPInfo(const char *cmd) { return 0; } +int CmdHFMFPWritePerso(const char *cmd) { + uint8_t keyNum[64] = {0}; + int keyNumLen = 0; + uint8_t key[64] = {0}; + int keyLen = 0; + + CLIParserInit("hf mfp wrp", + "Executes Write Perso command. Can be used in SL0 mode only.", + "Usage:\n\thf mfp wrp 4000 000102030405060708090a0b0c0d0e0f -> write key (00..0f) to key number 4000 \n" + "\thf mfp wrp 4000 -> write default key(0xff..0xff) to key number 4000"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("vV", "verbose", "show internal data."), + arg_str1(NULL, NULL, "", NULL), + arg_strx0(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool verbose = arg_get_lit(1); + CLIGetHexWithReturn(2, keyNum, &keyNumLen); + CLIGetHexWithReturn(3, key, &keyLen); + CLIParserFree(); + + SetVerboseMode(verbose); + + if (!keyLen) { + memmove(key, DefaultKey, 16); + keyLen = 16; + } + + if (keyNumLen != 2) { + PrintAndLog("Key number length must be 2 bytes instead of: %d", keyNumLen); + return 1; + } + if (keyLen != 16) { + PrintAndLog("Key length must be 16 bytes instead of: %d", keyLen); + return 1; + } + + uint8_t data[250] = {0}; + int datalen = 0; + + int res = MFPWritePerso(keyNum, key, true, false, data, sizeof(data), &datalen); + if (res) { + PrintAndLog("Exchange error: %d", res); + return res; + } + + if (datalen != 3) { + PrintAndLog("Command must return 3 bytes instead of: %d", datalen); + return 1; + } + + if (data[0] != 0x90) { + PrintAndLog("Command error: %02x %s", data[0], GetErrorDescription(data[0])); + return 1; + } + PrintAndLog("Write OK."); + + return 0; +} + +uint16_t CardAddresses[] = {0x9000, 0x9001, 0x9002, 0x9003, 0x9004, 0xA000, 0xA001, 0xA080, 0xA081, 0xC000, 0xC001}; + +int CmdHFMFPInitPerso(const char *cmd) { + int res; + uint8_t key[256] = {0}; + int keyLen = 0; + uint8_t keyNum[2] = {0}; + uint8_t data[250] = {0}; + int datalen = 0; + + CLIParserInit("hf mfp initp", + "Executes Write Perso command for all card's keys. Can be used in SL0 mode only.", + "Usage:\n\thf mfp initp 000102030405060708090a0b0c0d0e0f -> fill all the keys with key (00..0f)\n" + "\thf mfp initp -vv -> fill all the keys with default key(0xff..0xff) and show all the data exchange"); + + void* argtable[] = { + arg_param_begin, + arg_litn("vV", "verbose", 0, 2, "show internal data."), + arg_strx0(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool verbose = arg_get_lit(1); + bool verbose2 = arg_get_lit(1) > 1; + CLIGetHexWithReturn(2, key, &keyLen); + CLIParserFree(); + + if (keyLen && keyLen != 16) { + PrintAndLog("Key length must be 16 bytes instead of: %d", keyLen); + return 1; + } + + if (!keyLen) + memmove(key, DefaultKey, 16); + + SetVerboseMode(verbose2); + for (uint16_t sn = 0x4000; sn < 0x4050; sn++) { + keyNum[0] = sn >> 8; + keyNum[1] = sn & 0xff; + res = MFPWritePerso(keyNum, key, (sn == 0x4000), true, data, sizeof(data), &datalen); + if (!res && (datalen == 3) && data[0] == 0x09) { + PrintAndLog("2k card detected."); + break; + } + if (res || (datalen != 3) || data[0] != 0x90) { + PrintAndLog("Write error on address %04x", sn); + break; + } + } + + SetVerboseMode(verbose); + for (int i = 0; i < sizeof(CardAddresses) / 2; i++) { + keyNum[0] = CardAddresses[i] >> 8; + keyNum[1] = CardAddresses[i] & 0xff; + res = MFPWritePerso(keyNum, key, false, true, data, sizeof(data), &datalen); + if (!res && (datalen == 3) && data[0] == 0x09) { + PrintAndLog("Skipped[%04x]...", CardAddresses[i]); + } else { + if (res || (datalen != 3) || data[0] != 0x90) { + PrintAndLog("Write error on address %04x", CardAddresses[i]); + break; + } + } + } + + DropField(); + + if (res) + return res; + + PrintAndLog("Done."); + + return 0; +} + +int CmdHFMFPCommitPerso(const char *cmd) { + CLIParserInit("hf mfp commitp", + "Executes Commit Perso command. Can be used in SL0 mode only.", + "Usage:\n\thf mfp commitp -> \n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("vV", "verbose", "show internal data."), + arg_int0(NULL, NULL, "SL mode", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool verbose = arg_get_lit(1); + CLIParserFree(); + + SetVerboseMode(verbose); + + uint8_t data[250] = {0}; + int datalen = 0; + + int res = MFPCommitPerso(true, false, data, sizeof(data), &datalen); + if (res) { + PrintAndLog("Exchange error: %d", res); + return res; + } + + if (datalen != 3) { + PrintAndLog("Command must return 3 bytes instead of: %d", datalen); + return 1; + } + + if (data[0] != 0x90) { + PrintAndLog("Command error: %02x %s", data[0], GetErrorDescription(data[0])); + return 1; + } + PrintAndLog("Switch level OK."); + + return 0; +} + +int CmdHFMFPAuth(const char *cmd) { + uint8_t keyn[250] = {0}; + int keynlen = 0; + uint8_t key[250] = {0}; + int keylen = 0; + + CLIParserInit("hf mfp auth", + "Executes AES authentication command in ISO14443-4", + "Usage:\n\thf mfp auth 4000 000102030405060708090a0b0c0d0e0f -> executes authentication\n" + "\thf mfp auth 9003 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -v -> executes authentication and shows all the system data\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("vV", "verbose", "show internal data."), + arg_str1(NULL, NULL, "", NULL), + arg_str1(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool verbose = arg_get_lit(1); + CLIGetHexWithReturn(2, keyn, &keynlen); + CLIGetHexWithReturn(3, key, &keylen); + CLIParserFree(); + + if (keynlen != 2) { + PrintAndLog("ERROR: must be 2 bytes long instead of: %d", keynlen); + return 1; + } + + if (keylen != 16) { + PrintAndLog("ERROR: must be 16 bytes long instead of: %d", keylen); + return 1; + } + + return MifareAuth4(NULL, keyn, key, true, false, verbose); +} + +int CmdHFMFPRdbl(const char *cmd) { + //mf4Session session + //int res = MifareAuth4(&session, keyn, key, true, false, verbose); + //res = Read(); + + return 0; +} + +int CmdHFMFPRdsc(const char *cmd) { + + return 0; +} + +int CmdHFMFPWrbl(const char *cmd) { + + return 0; +} + static command_t CommandTable[] = { {"help", CmdHelp, 1, "This help"}, {"info", CmdHFMFPInfo, 0, "Info about Mifare Plus tag"}, + {"wrp", CmdHFMFPWritePerso, 0, "Write Perso command"}, + {"initp", CmdHFMFPInitPerso, 0, "Fills all the card's keys"}, + {"commitp", CmdHFMFPCommitPerso, 0, "Move card to SL1 or SL3 mode"}, + {"auth", CmdHFMFPAuth, 0, "Authentication in iso1443-4"}, +// {"rdbl", CmdHFMFPRdbl, 0, "Read blocks"}, +// {"rdsc", CmdHFMFPRdsc, 0, "Read sectors"}, +// {"wrbl", CmdHFMFPWrbl, 0, "Write blocks"}, {NULL, NULL, 0, NULL} }; diff --git a/client/mifare4.c b/client/mifare4.c new file mode 100644 index 00000000..3489c857 --- /dev/null +++ b/client/mifare4.c @@ -0,0 +1,118 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 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. +//----------------------------------------------------------------------------- +// iso14443-4 mifare commands +//----------------------------------------------------------------------------- + +#include "mifare4.h" +#include +#include +#include "cmdhf14a.h" +#include "util.h" +#include "ui.h" +#include "polarssl/libpcrypto.h" + +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}; + + if (session) + session->Authenticated = false; + + uint8_t cmd1[] = {0x70, keyn[1], keyn[0], 0x00}; + int res = ExchangeRAW14a(cmd1, sizeof(cmd1), activateField, true, data, sizeof(data), &datalen); + if (res) { + PrintAndLog("ERROR exchande raw error: %d", res); + DropField(); + return 2; + } + + if (verbose) + PrintAndLog("phase2: %s", sprint_hex(cmd2, 33)); + + res = ExchangeRAW14a(cmd2, sizeof(cmd2), false, false, data, sizeof(data), &datalen); + if (res) { + PrintAndLog("ERROR exchande raw error: %d", res); + DropField(); + return 4; + } + + if (verbose) + PrintAndLog("Authenticated = true; + session->KeyNum = keyn[1] + (keyn[0] << 8); + memmove(session->Rnd1, Rnd1, 16); + memmove(session->Rnd2, Rnd2, 16); + } + + PrintAndLog("Authentication OK"); + + return 0; +} + diff --git a/client/mifare4.h b/client/mifare4.h new file mode 100644 index 00000000..70711847 --- /dev/null +++ b/client/mifare4.h @@ -0,0 +1,30 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 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. +//----------------------------------------------------------------------------- +// iso14443-4 mifare commands +//----------------------------------------------------------------------------- + +#ifndef MIFARE4_H +#define MIFARE4_H + +#include +#include +#include + +typedef struct { + bool Authenticated; + uint16_t KeyNum; + uint8_t Rnd1[16]; + uint8_t Rnd2[16]; + +}mf4Session; + +extern int MifareAuth4(mf4Session *session, uint8_t *keyn, uint8_t *key, bool activateField, bool leaveSignalON, bool verbose); + + + +#endif // mifare4.h diff --git a/common/polarssl/libpcrypto.c b/common/polarssl/libpcrypto.c new file mode 100644 index 00000000..032c3a18 --- /dev/null +++ b/common/polarssl/libpcrypto.c @@ -0,0 +1,44 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 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. +//----------------------------------------------------------------------------- +// crypto commands +//----------------------------------------------------------------------------- + +#include "polarssl/libpcrypto.h" +#include + +int aes_encode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length){ + uint8_t iiv[16] = {0}; + if (iv) + memcpy(iiv, iv, 16); + + aes_context aes; + aes_init(&aes); + if (aes_setkey_enc(&aes, key, 128)) + return 1; + if (aes_crypt_cbc(&aes, AES_ENCRYPT, length, iiv, input, output)) + return 2; + aes_free(&aes); + + return 0; +} + +int aes_decode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length){ + uint8_t iiv[16] = {0}; + if (iv) + memcpy(iiv, iv, 16); + + aes_context aes; + aes_init(&aes); + if (aes_setkey_dec(&aes, key, 128)) + return 1; + if (aes_crypt_cbc(&aes, AES_DECRYPT, length, iiv, input, output)) + return 2; + aes_free(&aes); + + return 0; +} \ No newline at end of file diff --git a/common/polarssl/libpcrypto.h b/common/polarssl/libpcrypto.h new file mode 100644 index 00000000..84732cd3 --- /dev/null +++ b/common/polarssl/libpcrypto.h @@ -0,0 +1,20 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 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. +//----------------------------------------------------------------------------- +// crypto commands +//----------------------------------------------------------------------------- + +#ifndef LIBPCRYPTO_H +#define LIBPCRYPTO_H + +#include +#include + +extern int aes_encode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length); +extern int aes_decode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length); + +#endif /* libpcrypto.h */ From cdc9a7562d70ec1b4c58841acc64150774e377b6 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko <807634+merlokk@users.noreply.github.com> Date: Fri, 26 Oct 2018 15:43:06 +0300 Subject: [PATCH 306/310] inc timeouts (#705) --- CI/appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CI/appveyor.yml b/CI/appveyor.yml index 1c22c92e..a38ff647 100644 --- a/CI/appveyor.yml +++ b/CI/appveyor.yml @@ -257,14 +257,14 @@ test_script: [bool]$res=$false # Wait 120 sec timeout for Job - if(Wait-Job $Job -Timeout 120){ + if(Wait-Job $Job -Timeout 150){ $Results = $Job | Receive-Job if($Results -like "true"){ $res=$true } } else { Write-host "Test [$Name] timeout" -ForegroundColor Red - Add-AppveyorTest -Name "$Name" -Framework NUnit -Filename "$File" -Outcome Failed -Duration 40000 -ErrorMessage "timeout" + Add-AppveyorTest -Name "$Name" -Framework NUnit -Filename "$File" -Outcome Failed -Duration 60000 -ErrorMessage "timeout" } Remove-Job -Force $Job From c8a0f5503172f25620670a9ba992d8c923b5df95 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko <807634+merlokk@users.noreply.github.com> Date: Fri, 26 Oct 2018 20:18:53 +0300 Subject: [PATCH 307/310] Mfp read plain (#704) * added aes-cmac-128 * aes-cmac added to cryptosystem. not works( --- client/Makefile | 1 + client/cliparser/cliparser.h | 1 + client/cmdhfmfp.c | 335 +++++++++++++++++++++++++++++++++- client/emv/test/cryptotest.c | 6 +- client/mifare4.c | 50 ++++- client/mifare4.h | 7 + common/polarssl/aes_cmac128.c | 322 ++++++++++++++++++++++++++++++++ common/polarssl/aes_cmac128.h | 81 ++++++++ common/polarssl/libpcrypto.c | 41 ++++- common/polarssl/libpcrypto.h | 2 + 10 files changed, 836 insertions(+), 10 deletions(-) create mode 100644 common/polarssl/aes_cmac128.c create mode 100644 common/polarssl/aes_cmac128.h diff --git a/client/Makefile b/client/Makefile index 72d5080d..1b211d4f 100644 --- a/client/Makefile +++ b/client/Makefile @@ -107,6 +107,7 @@ CMDSRCS = $(SRC_SMARTCARD) \ crapto1/crypto1.c\ polarssl/des.c \ polarssl/aes.c\ + polarssl/aes_cmac128.c\ polarssl/bignum.c\ polarssl/rsa.c\ polarssl/sha1.c\ diff --git a/client/cliparser/cliparser.h b/client/cliparser/cliparser.h index c4bc7068..05910ea4 100644 --- a/client/cliparser/cliparser.h +++ b/client/cliparser/cliparser.h @@ -19,6 +19,7 @@ #define arg_get_lit(n)(((struct arg_lit*)argtable[n])->count) #define arg_get_int_count(n)(((struct arg_int*)argtable[n])->count) #define arg_get_int(n)(((struct arg_int*)argtable[n])->ival[0]) +#define arg_get_int_def(n,def)(arg_get_int_count(n)?(arg_get_int(n)):(def)) #define arg_get_str(n)((struct arg_str*)argtable[n]) #define arg_get_str_len(n)(strlen(((struct arg_str*)argtable[n])->sval[0])) diff --git a/client/cmdhfmfp.c b/client/cmdhfmfp.c index a4c53f56..c5fd8eed 100644 --- a/client/cmdhfmfp.c +++ b/client/cmdhfmfp.c @@ -35,6 +35,9 @@ typedef struct { static const PlusErrorsElm PlusErrors[] = { {0xFF, ""}, {0x00, "Unknown error"}, + {0x06, "Block use error"}, + {0x07, "Command use error"}, + {0x08, "Invalid write command"}, {0x09, "Invalid block number"}, {0x0b, "Command code error"}, {0x0c, "Length error"}, @@ -82,6 +85,37 @@ int MFPCommitPerso(bool activateField, bool leaveSignalON, uint8_t *dataout, int 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, rcmd, 4, &rcmd[4], VerboseMode); + + int res = intExchangeRAW14aPlus(rcmd, plain?4:sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen); + if(res) + return res; + + if(session && mac) + CalculateMAC(session, dataout, *dataoutlen, 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, rcmd, 19, &rcmd[19], VerboseMode); + + int res = intExchangeRAW14aPlus(rcmd, sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen); + if(res) + return res; + + if(session && mac) + CalculateMAC(session, dataout, *dataoutlen, mac, VerboseMode); + + return 0; +} + int CmdHFMFPInfo(const char *cmd) { if (cmd && strlen(cmd) > 0) @@ -349,7 +383,7 @@ int CmdHFMFPAuth(const char *cmd) { int keylen = 0; CLIParserInit("hf mfp auth", - "Executes AES authentication command in ISO14443-4", + "Executes AES authentication command for Mifare Plus card", "Usage:\n\thf mfp auth 4000 000102030405060708090a0b0c0d0e0f -> executes authentication\n" "\thf mfp auth 9003 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -v -> executes authentication and shows all the system data\n"); @@ -381,20 +415,307 @@ int CmdHFMFPAuth(const char *cmd) { } int CmdHFMFPRdbl(const char *cmd) { - //mf4Session session - //int res = MifareAuth4(&session, keyn, key, true, false, verbose); - //res = Read(); + uint8_t keyn[2] = {0}; + uint8_t key[250] = {0}; + int keylen = 0; + + CLIParserInit("hf mfp rdbl", + "Reads several blocks from Mifare Plus card in plain mode.", + "Usage:\n\thf mfp rdbl 0 000102030405060708090a0b0c0d0e0f -> executes authentication and read block 0 data\n" + "\thf mfp rdbl 1 -v -> executes authentication and shows sector 1 data with default key 0xFF..0xFF and some additional data\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("vV", "verbose", "show internal data."), + arg_int0("nN", "count", "blocks count (by default 1).", NULL), + arg_lit0("bB", "keyb", "use key B (by default keyA)."), + arg_lit0("pP", "plain", "plain communication between reader and card."), + arg_int1(NULL, NULL, "", NULL), + arg_str0(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, false); + + bool verbose = arg_get_lit(1); + int blocksCount = arg_get_int_def(2, 1); + bool keyB = arg_get_lit(3); + int plain = arg_get_lit(4) | true; + uint32_t blockn = arg_get_int(5); + CLIGetHexWithReturn(6, key, &keylen); + CLIParserFree(); + + SetVerboseMode(verbose); + + if (!keylen) { + memmove(key, DefaultKey, 16); + keylen = 16; + } + + if (blockn > 255) { + PrintAndLog("ERROR: must be in range [0..255] instead of: %d", blockn); + return 1; + } + + if (keylen != 16) { + PrintAndLog("ERROR: must be 16 bytes long instead of: %d", keylen); + return 1; + } + + // 3 blocks - wo iso14443-4 chaining + if (blocksCount > 3) { + PrintAndLog("ERROR: blocks count must be less than 3 instead of: %d", blocksCount); + return 1; + } + + uint8_t sectorNum = mfSectorNum(blockn & 0xff); + uint16_t uKeyNum = 0x4000 + sectorNum * 2 + (keyB ? 1 : 0); + keyn[0] = uKeyNum >> 8; + keyn[1] = uKeyNum & 0xff; + if (verbose) + PrintAndLog("--block:%d sector[%d]:%02x key:%04x", blockn, mfNumBlocksPerSector(sectorNum), sectorNum, uKeyNum); + + mf4Session session; + int res = MifareAuth4(&session, keyn, key, true, true, verbose); + if (res) { + PrintAndLog("Authentication error: %d", res); + return res; + } + + uint8_t data[250] = {0}; + int datalen = 0; + uint8_t mac[8] = {0}; + res = MFPReadBlock(&session, plain, blockn & 0xff, blocksCount, false, false, data, sizeof(data), &datalen, mac); + if (res) { + PrintAndLog("Read error: %d", res); + return res; + } + + if (datalen && data[0] != 0x90) { + PrintAndLog("Card read error: %02x %s", data[0], GetErrorDescription(data[0])); + return 6; + } + + if (datalen != 1 + blocksCount * 16 + 8 + 2) { + PrintAndLog("Error return length:%d", datalen); + return 5; + } + + int indx = blockn; + for(int i = 0; i < blocksCount; i++) { + PrintAndLog("data[%03d]: %s", indx, sprint_hex(&data[1 + i * 16], 16)); + indx++; + if (mfIsSectorTrailer(indx)){ + PrintAndLog("data[%03d]: ------------------- trailer -------------------", indx); + indx++; + } + } + + if (!memcmp(&data[blocksCount * 16 + 1], mac, 8)) { + PrintAndLog("WARNING: mac not equal..."); + PrintAndLog("MAC card: %s", sprint_hex(&data[blocksCount * 16 + 1], 8)); + PrintAndLog("MAC reader: %s", sprint_hex(mac, 8)); + } else { + if(verbose) + PrintAndLog("MAC: %s", sprint_hex(&data[blocksCount * 16 + 1], 8)); + } return 0; } int CmdHFMFPRdsc(const char *cmd) { + uint8_t keyn[2] = {0}; + uint8_t key[250] = {0}; + int keylen = 0; + + CLIParserInit("hf mfp rdsc", + "Reads one sector from Mifare Plus card in plain mode.", + "Usage:\n\thf mfp rdsc 0 000102030405060708090a0b0c0d0e0f -> executes authentication and read sector 0 data\n" + "\thf mfp rdsc 1 -v -> executes authentication and shows sector 1 data with default key 0xFF..0xFF and some additional data\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("vV", "verbose", "show internal data."), + arg_lit0("bB", "keyb", "use key B (by default keyA)."), + arg_lit0("pP", "plain", "plain communication between reader and card."), + arg_int1(NULL, NULL, "", NULL), + arg_str0(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, false); + + bool verbose = arg_get_lit(1); + bool keyB = arg_get_lit(2); + bool plain = arg_get_lit(3) | true; + uint32_t sectorNum = arg_get_int(4); + CLIGetHexWithReturn(5, key, &keylen); + CLIParserFree(); + + SetVerboseMode(verbose); + + if (!keylen) { + memmove(key, DefaultKey, 16); + keylen = 16; + } + + if (sectorNum > 39) { + PrintAndLog("ERROR: must be in range [0..39] instead of: %d", sectorNum); + return 1; + } + + if (keylen != 16) { + PrintAndLog("ERROR: must be 16 bytes long instead of: %d", keylen); + return 1; + } + + uint16_t uKeyNum = 0x4000 + sectorNum * 2 + (keyB ? 1 : 0); + keyn[0] = uKeyNum >> 8; + keyn[1] = uKeyNum & 0xff; + if (verbose) + PrintAndLog("--sector[%d]:%02x key:%04x", mfNumBlocksPerSector(sectorNum), sectorNum, uKeyNum); + + mf4Session session; + int res = MifareAuth4(&session, keyn, key, true, true, verbose); + if (res) { + PrintAndLog("Authentication error: %d", res); + return res; + } + + uint8_t data[250] = {0}; + int datalen = 0; + uint8_t mac[8] = {0}; + for(int n = mfFirstBlockOfSector(sectorNum); n < mfFirstBlockOfSector(sectorNum) + mfNumBlocksPerSector(sectorNum); n++) { + res = MFPReadBlock(&session, plain, n & 0xff, 1, false, true, data, sizeof(data), &datalen, mac); + if (res) { + PrintAndLog("Read error: %d", res); + DropField(); + return res; + } + + if (datalen && data[0] != 0x90) { + PrintAndLog("Card read error: %02x %s", data[0], GetErrorDescription(data[0])); + DropField(); + return 6; + } + if (datalen != 1 + 16 + 8 + 2) { + PrintAndLog("Error return length:%d", datalen); + DropField(); + return 5; + } + + PrintAndLog("data[%03d]: %s", n, sprint_hex(&data[1], 16)); + + if (!memcmp(&data[1 + 16], mac, 8)) { + PrintAndLog("WARNING: mac on block %d not equal...", n); + PrintAndLog("MAC card: %s", sprint_hex(&data[1 + 16], 8)); + PrintAndLog("MAC reader: %s", sprint_hex(mac, 8)); + } else { + if(verbose) + PrintAndLog("MAC: %s", sprint_hex(&data[1 + 16], 8)); + } + } + DropField(); return 0; } int CmdHFMFPWrbl(const char *cmd) { + uint8_t keyn[2] = {0}; + uint8_t key[250] = {0}; + int keylen = 0; + uint8_t datain[250] = {0}; + int datainlen = 0; + CLIParserInit("hf mfp wrbl", + "Writes one block to Mifare Plus card.", + "Usage:\n\thf mfp wrbl 1 ff0000000000000000000000000000ff 000102030405060708090a0b0c0d0e0f -> writes block 1 data\n" + "\thf mfp wrbl 2 ff0000000000000000000000000000ff -v -> writes block 2 data with default key 0xFF..0xFF and some additional data\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("vV", "verbose", "show internal data."), + arg_lit0("bB", "keyb", "use key B (by default keyA)."), + arg_int1(NULL, NULL, "", NULL), + arg_str1(NULL, NULL, "", NULL), + arg_str0(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, false); + + bool verbose = arg_get_lit(1); + bool keyB = arg_get_lit(2); + uint32_t blockNum = arg_get_int(3); + CLIGetHexWithReturn(4, datain, &datainlen); + CLIGetHexWithReturn(5, key, &keylen); + CLIParserFree(); + + SetVerboseMode(verbose); + + if (!keylen) { + memmove(key, DefaultKey, 16); + keylen = 16; + } + + if (blockNum > 39) { + PrintAndLog("ERROR: must be in range [0..255] instead of: %d", blockNum); + return 1; + } + + if (keylen != 16) { + PrintAndLog("ERROR: must be 16 bytes long instead of: %d", keylen); + return 1; + } + + if (datainlen != 16) { + PrintAndLog("ERROR: must be 16 bytes long instead of: %d", datainlen); + return 1; + } + + uint8_t sectorNum = mfSectorNum(blockNum & 0xff); + uint16_t uKeyNum = 0x4000 + sectorNum * 2 + (keyB ? 1 : 0); + keyn[0] = uKeyNum >> 8; + keyn[1] = uKeyNum & 0xff; + if (verbose) + PrintAndLog("--block:%d sector[%d]:%02x key:%04x", blockNum & 0xff, mfNumBlocksPerSector(sectorNum), sectorNum, uKeyNum); + + mf4Session session; + int res = MifareAuth4(&session, keyn, key, true, true, verbose); + if (res) { + PrintAndLog("Authentication error: %d", res); + return res; + } + + uint8_t data[250] = {0}; + int datalen = 0; + uint8_t mac[8] = {0}; + res = MFPWriteBlock(&session, blockNum & 0xff, datain, false, false, data, sizeof(data), &datalen, mac); + if (res) { + PrintAndLog("Write error: %d", res); + DropField(); + return res; + } + + if (datalen != 3 && (datalen != 3 + 8)) { + PrintAndLog("Error return length:%d", datalen); + DropField(); + return 5; + } + + if (datalen && data[0] != 0x90) { + PrintAndLog("Card write error: %02x %s", data[0], GetErrorDescription(data[0])); + DropField(); + return 6; + } + + if (!memcmp(&data[1], mac, 8)) { + PrintAndLog("WARNING: mac not equal..."); + PrintAndLog("MAC card: %s", sprint_hex(&data[1], 8)); + PrintAndLog("MAC reader: %s", sprint_hex(mac, 8)); + } else { + if(verbose) + PrintAndLog("MAC: %s", sprint_hex(&data[1], 8)); + } + + DropField(); return 0; } @@ -405,9 +726,9 @@ static command_t CommandTable[] = {"wrp", CmdHFMFPWritePerso, 0, "Write Perso command"}, {"initp", CmdHFMFPInitPerso, 0, "Fills all the card's keys"}, {"commitp", CmdHFMFPCommitPerso, 0, "Move card to SL1 or SL3 mode"}, - {"auth", CmdHFMFPAuth, 0, "Authentication in iso1443-4"}, -// {"rdbl", CmdHFMFPRdbl, 0, "Read blocks"}, -// {"rdsc", CmdHFMFPRdsc, 0, "Read sectors"}, + {"auth", CmdHFMFPAuth, 0, "Authentication"}, + {"rdbl", CmdHFMFPRdbl, 0, "Read blocks"}, + {"rdsc", CmdHFMFPRdsc, 0, "Read sectors"}, // {"wrbl", CmdHFMFPWrbl, 0, "Write blocks"}, {NULL, NULL, 0, NULL} }; diff --git a/client/emv/test/cryptotest.c b/client/emv/test/cryptotest.c index a6d06e68..b778d256 100644 --- a/client/emv/test/cryptotest.c +++ b/client/emv/test/cryptotest.c @@ -14,6 +14,7 @@ #include "bignum.h" #include "aes.h" +#include "aes_cmac128.h" #include "des.h" #include "rsa.h" #include "sha1.h" @@ -32,7 +33,10 @@ int ExecuteCryptoTests(bool verbose) { res = aes_self_test(verbose); if (res) TestFail = true; - + + res = aes_cmac_self_test(verbose); + if (res) TestFail = true; + res = des_self_test(verbose); if (res) TestFail = true; diff --git a/client/mifare4.c b/client/mifare4.c index 3489c857..e1021196 100644 --- a/client/mifare4.c +++ b/client/mifare4.c @@ -16,6 +16,18 @@ #include "ui.h" #include "polarssl/libpcrypto.h" +int CalculateMAC(mf4Session *session, uint8_t *data, int datalen, uint8_t *mac, bool verbose) { + if (!session || !session->Authenticated || !mac || !data || !datalen) + return 1; + + memset(mac, 0x00, 8); + + if (verbose) + PrintAndLog("MAC data[%d]: %s", datalen, sprint_hex(data, datalen)); + + return aes_cmac8(NULL, session->Key, data, mac, datalen); +} + int MifareAuth4(mf4Session *session, uint8_t *keyn, uint8_t *key, bool activateField, bool leaveSignalON, bool verbose) { uint8_t data[257] = {0}; int datalen = 0; @@ -71,7 +83,7 @@ int MifareAuth4(mf4Session *session, uint8_t *keyn, uint8_t *key, bool activateF 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(); @@ -109,6 +121,7 @@ int MifareAuth4(mf4Session *session, uint8_t *keyn, uint8_t *key, bool activateF session->KeyNum = keyn[1] + (keyn[0] << 8); memmove(session->Rnd1, Rnd1, 16); memmove(session->Rnd2, Rnd2, 16); + memmove(session->Key, key, 16); } PrintAndLog("Authentication OK"); @@ -116,3 +129,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; + +} diff --git a/client/mifare4.h b/client/mifare4.h index 70711847..5b789a70 100644 --- a/client/mifare4.h +++ b/client/mifare4.h @@ -17,14 +17,21 @@ typedef struct { bool Authenticated; + uint8_t Key[16]; uint16_t KeyNum; uint8_t Rnd1[16]; uint8_t Rnd2[16]; }mf4Session; +extern int CalculateMAC(mf4Session *session, 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 uint8_t mfNumBlocksPerSector(uint8_t sectorNo); +extern uint8_t mfFirstBlockOfSector(uint8_t sectorNo); +extern uint8_t mfSectorTrailer(uint8_t blockNo); +extern bool mfIsSectorTrailer(uint8_t blockNo); +extern uint8_t mfSectorNum(uint8_t blockNo); #endif // mifare4.h diff --git a/common/polarssl/aes_cmac128.c b/common/polarssl/aes_cmac128.c new file mode 100644 index 00000000..595f89b8 --- /dev/null +++ b/common/polarssl/aes_cmac128.c @@ -0,0 +1,322 @@ +/* + * AES-CMAC from NIST Special Publication 800-38B — Recommendation for block cipher modes of operation: The CMAC mode for authentication. + * + * Copyright (C) 2006-2014, Brainspark B.V. + * Copyright (C) 2014, Anargyros Plemenos + * Tests added Merkok, 2018 + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Reference : https://polarssl.org/discussions/generic/authentication-token + * NIST Special Publication 800-38B — Recommendation for block cipher modes of operation: The CMAC mode for authentication. + * Tests here: + * https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/AES_CMAC.pdf +*/ + +#include "polarssl/aes_cmac128.h" +#include + +#define MIN(a,b) ((a)<(b)?(a):(b)) +#define _MSB(x) (((x)[0] & 0x80)?1:0) + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl_config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_AES_C) +#include "aes.h" +#endif + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_printf printf +#endif + + +/** + * zero a structure + */ +#define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x)) + +/** + * zero a structure given a pointer to the structure + */ +#define ZERO_STRUCTP(x) do{ if((x) != NULL) memset((char *)(x), 0, sizeof(*(x)));} while(0) + + +/* For CMAC Calculation */ +static unsigned char const_Rb[16] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87 +}; +static unsigned char const_Zero[16] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static inline void aes_cmac_128_left_shift_1(const uint8_t in[16], uint8_t out[16]) +{ + uint8_t overflow = 0; + int8_t i; + + for (i = 15; i >= 0; i--) { + out[i] = in[i] << 1; + out[i] |= overflow; + overflow = _MSB(&in[i]); + } +} + +static inline void aes_cmac_128_xor(const uint8_t in1[16], const uint8_t in2[16], + uint8_t out[16]) +{ + uint8_t i; + + for (i = 0; i < 16; i++) { + out[i] = in1[i] ^ in2[i]; + } +} + +/* + * AES-CMAC-128 context setup + */ +void aes_cmac128_starts(aes_cmac128_context *ctx, const uint8_t K[16]) +{ + uint8_t L[16]; + + /* Zero struct of aes_context */ + ZERO_STRUCTP(ctx); + /* Initialize aes_context */ + aes_setkey_enc(&ctx->aes_key, K, 128); + + /* step 1 - generate subkeys k1 and k2 */ + aes_crypt_ecb(&ctx->aes_key, AES_ENCRYPT, const_Zero, L); + + if (_MSB(L) == 0) { + aes_cmac_128_left_shift_1(L, ctx->K1); + } else { + uint8_t tmp_block[16]; + + aes_cmac_128_left_shift_1(L, tmp_block); + aes_cmac_128_xor(tmp_block, const_Rb, ctx->K1); + ZERO_STRUCT(tmp_block); + } + + if (_MSB(ctx->K1) == 0) { + aes_cmac_128_left_shift_1(ctx->K1, ctx->K2); + } else { + uint8_t tmp_block[16]; + + aes_cmac_128_left_shift_1(ctx->K1, tmp_block); + aes_cmac_128_xor(tmp_block, const_Rb, ctx->K2); + ZERO_STRUCT(tmp_block); + } + + ZERO_STRUCT(L); +} + +/* + * AES-CMAC-128 process message + */ +void aes_cmac128_update(aes_cmac128_context *ctx, const uint8_t *_msg, size_t _msg_len) +{ + uint8_t tmp_block[16]; + uint8_t Y[16]; + const uint8_t *msg = _msg; + size_t msg_len = _msg_len; + + /* + * copy the remembered last block + */ + ZERO_STRUCT(tmp_block); + if (ctx->last_len) { + memcpy(tmp_block, ctx->last, ctx->last_len); + } + + /* + * check if we expand the block + */ + if (ctx->last_len < 16) { + size_t len = MIN(16 - ctx->last_len, msg_len); + + memcpy(&tmp_block[ctx->last_len], msg, len); + memcpy(ctx->last, tmp_block, 16); + msg += len; + msg_len -= len; + ctx->last_len += len; + } + + if (msg_len == 0) { + /* if it is still the last block, we are done */ + ZERO_STRUCT(tmp_block); + return; + } + + /* + * It is not the last block anymore + */ + ZERO_STRUCT(ctx->last); + ctx->last_len = 0; + + /* + * now checksum everything but the last block + */ + aes_cmac_128_xor(ctx->X, tmp_block, Y); + aes_crypt_ecb(&ctx->aes_key, AES_ENCRYPT, Y, ctx->X); + + while (msg_len > 16) { + memcpy(tmp_block, msg, 16); + msg += 16; + msg_len -= 16; + + aes_cmac_128_xor(ctx->X, tmp_block, Y); + aes_crypt_ecb(&ctx->aes_key, AES_ENCRYPT, Y, ctx->X); + } + + /* + * copy the last block, it will be processed in + * aes_cmac128_final(). + */ + memcpy(ctx->last, msg, msg_len); + ctx->last_len = msg_len; + + ZERO_STRUCT(tmp_block); + ZERO_STRUCT(Y); +} + +/* + * AES-CMAC-128 compute T + */ +void aes_cmac128_final(aes_cmac128_context *ctx, uint8_t T[16]) +{ + uint8_t tmp_block[16]; + uint8_t Y[16]; + + if (ctx->last_len < 16) { + ctx->last[ctx->last_len] = 0x80; + aes_cmac_128_xor(ctx->last, ctx->K2, tmp_block); + } else { + aes_cmac_128_xor(ctx->last, ctx->K1, tmp_block); + } + + aes_cmac_128_xor(tmp_block, ctx->X, Y); + aes_crypt_ecb(&ctx->aes_key, AES_ENCRYPT, Y, T); + + ZERO_STRUCT(tmp_block); + ZERO_STRUCT(Y); + ZERO_STRUCTP(ctx); +} + +/* + * Checkup routine + * + * https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values + * https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/AES_CMAC.pdf + */ +int aes_cmac_self_test( int verbose ) +{ + unsigned char key[16] = {0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C}; + unsigned char mac[16] = {0}; + aes_cmac128_context ctx; + int ret; + + // check Example1: + if( verbose != 0 ) + polarssl_printf( " AES-CMAC-128 zero length data: " ); + unsigned char ex1data[16] = {0}; + aes_cmac128_starts(&ctx, key); + aes_cmac128_update(&ctx, ex1data, 0); + aes_cmac128_final(&ctx, mac); + unsigned char ex1res[16] = {0xBB, 0x1D, 0x69, 0x29, 0xE9, 0x59, 0x37, 0x28, 0x7F, 0xA3, 0x7D, 0x12, 0x9B, 0x75, 0x67, 0x46}; + if(!memcmp(mac, ex1res, 16)) { + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } else { + polarssl_printf( "failed\n" ); + ret = 1; + goto exit; + } + + // check Example2: + if( verbose != 0 ) + polarssl_printf( " AES-CMAC-128 one block data : " ); + unsigned char ex2data[16] = {0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A}; + aes_cmac128_starts(&ctx, key); + aes_cmac128_update(&ctx, ex2data, sizeof(ex2data)); + aes_cmac128_final(&ctx, mac); + unsigned char ex2res[16] = {0x07, 0x0A, 0x16, 0xB4, 0x6B, 0x4D, 0x41, 0x44, 0xF7, 0x9B, 0xDD, 0x9D, 0xD0, 0x4A, 0x28, 0x7C}; + if(!memcmp(mac, ex2res, 16)) { + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } else { + polarssl_printf( "failed\n" ); + ret = 1; + goto exit; + } + + // check Example3: + if( verbose != 0 ) + polarssl_printf( " AES-CMAC-128 20 bytes of data: " ); + unsigned char ex3data[20] = {0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A, + 0xAE, 0x2D, 0x8A, 0x57}; + aes_cmac128_starts(&ctx, key); + aes_cmac128_update(&ctx, ex3data, sizeof(ex3data)); + aes_cmac128_final(&ctx, mac); + unsigned char ex3res[16] = {0x7D, 0x85, 0x44, 0x9E, 0xA6, 0xEA, 0x19, 0xC8, 0x23, 0xA7, 0xBF, 0x78, 0x83, 0x7D, 0xFA, 0xDE}; + if(!memcmp(mac, ex3res, 16)) { + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } else { + polarssl_printf( "failed\n" ); + ret = 1; + goto exit; + } + + // check Example4: + if( verbose != 0 ) + polarssl_printf( " AES-CMAC-128 4 blocks of data: " ); + unsigned char ex4data[64] = {0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A, + 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51, + 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF, + 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10}; + aes_cmac128_starts(&ctx, key); + aes_cmac128_update(&ctx, ex4data, sizeof(ex4data)); + aes_cmac128_final(&ctx, mac); + unsigned char ex4res[16] = {0x51, 0xF0, 0xBE, 0xBF, 0x7E, 0x3B, 0x9D, 0x92, 0xFC, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3C, 0xFE}; + if(!memcmp(mac, ex4res, 16)) { + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } else { + polarssl_printf( "failed\n" ); + ret = 1; + goto exit; + } + + if( verbose != 0 ) + polarssl_printf( "\n" ); + + ret = 0; + +exit: + return( ret ); +} + diff --git a/common/polarssl/aes_cmac128.h b/common/polarssl/aes_cmac128.h new file mode 100644 index 00000000..b792755f --- /dev/null +++ b/common/polarssl/aes_cmac128.h @@ -0,0 +1,81 @@ +/* + * AES-CMAC from NIST Special Publication 800-38B — Recommendation for block cipher modes of operation: The CMAC mode for authentication. + * + * Copyright (C) 2006-2014, Brainspark B.V. + * Copyright (C) 2014, Anargyros Plemenos + * Tests added Merkok, 2018 + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Reference : https://polarssl.org/discussions/generic/authentication-token + * NIST Special Publication 800-38B — Recommendation for block cipher modes of operation: The CMAC mode for authentication. + * Tests here: + * https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/AES_CMAC.pdf +*/ + +#include +#include +#include "aes.h" + +typedef struct aes_cmac_128_context { + aes_context aes_key; + + uint8_t K1[16]; + uint8_t K2[16]; + + uint8_t X[16]; + + uint8_t last[16]; + size_t last_len; +} +aes_cmac128_context; + +/* + * \brief AES-CMAC-128 context setup + * + * \param ctx context to be initialized + * \param key secret key for AES-128 + */ +void aes_cmac128_starts(aes_cmac128_context *ctx, const uint8_t K[16]); + +/* + * \brief AES-CMAC-128 process message + * + * \param ctx context to be initialized + * \param _msg the given message + * \param _msg_len the length of message + */ +void aes_cmac128_update(aes_cmac128_context *ctx, const uint8_t *_msg, size_t _msg_len); + +/* + * \brief AES-CMAC-128 compute T + * + * \param ctx context to be initialized + * \param T the generated MAC which is used to validate the message + */ +void aes_cmac128_final(aes_cmac128_context *ctx, uint8_t T[16]); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int aes_cmac_self_test( int verbose ); + diff --git a/common/polarssl/libpcrypto.c b/common/polarssl/libpcrypto.c index 032c3a18..9be9fd26 100644 --- a/common/polarssl/libpcrypto.c +++ b/common/polarssl/libpcrypto.c @@ -10,7 +10,9 @@ #include "polarssl/libpcrypto.h" #include +#include +// NIST Special Publication 800-38A — Recommendation for block cipher modes of operation: methods and techniques, 2001. int aes_encode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length){ uint8_t iiv[16] = {0}; if (iv) @@ -41,4 +43,41 @@ int aes_decode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int l aes_free(&aes); return 0; -} \ No newline at end of file +} + +// NIST Special Publication 800-38B — Recommendation for block cipher modes of operation: The CMAC mode for authentication. +// https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/AES_CMAC.pdf +int aes_cmac(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *mac, int length) { + memset(mac, 0x00, 16); + uint8_t iiv[16] = {0}; + if (iv) + memcpy(iiv, iv, 16); + + // padding: ISO/IEC 9797-1 Message Authentication Codes (MACs) - Part 1: Mechanisms using a block cipher + uint8_t data[2049] = {0}; // length + 16 + memcpy(data, input, length); + data[length] = 0x80; + int datalen = (length & 0xfffffff0) + 0x10; + + // NIST 800-38B + aes_cmac128_context ctx; + aes_cmac128_starts(&ctx, key); + aes_cmac128_update(&ctx, data, datalen); + aes_cmac128_final(&ctx, mac); + + return 0; +} + +int aes_cmac8(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *mac, int length) { + uint8_t cmac[16] = {0}; + memset(mac, 0x00, 8); + + int res = aes_cmac(iv, key, input, cmac, length); + if (res) + return res; + + for(int i = 0; i < 8; i++) + mac[i] = cmac[i * 2 + 1]; + + return 0; +} diff --git a/common/polarssl/libpcrypto.h b/common/polarssl/libpcrypto.h index 84732cd3..1b74c143 100644 --- a/common/polarssl/libpcrypto.h +++ b/common/polarssl/libpcrypto.h @@ -16,5 +16,7 @@ extern int aes_encode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length); extern int aes_decode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length); +extern int aes_cmac(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *mac, int length); +extern int aes_cmac8(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *mac, int length); #endif /* libpcrypto.h */ From 4b5d696c179edc687ba0b61c7d96ea0ef54cad44 Mon Sep 17 00:00:00 2001 From: drhatson <44032553+drhatson@users.noreply.github.com> Date: Wed, 31 Oct 2018 18:46:12 +0000 Subject: [PATCH 308/310] changes to mifare plus code (#706) --- CHANGELOG.md | 1 + client/cmdhfmfp.c | 60 ++++++++++------ client/mifare4.c | 133 ++++++++++++++++++++++++++++++----- client/mifare4.h | 22 ++++-- common/polarssl/libpcrypto.c | 11 +-- 5 files changed, 175 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f1847e1..7781935e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac ## [unreleased][unreleased] ### Changed +- Changed hf mfp security. Now it works in all the modes. (drHatson) ### Fixed diff --git a/client/cmdhfmfp.c b/client/cmdhfmfp.c index c5fd8eed..1ff24eb1 100644 --- a/client/cmdhfmfp.c +++ b/client/cmdhfmfp.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 @@ -34,13 +35,15 @@ typedef struct { static const PlusErrorsElm PlusErrors[] = { {0xFF, ""}, - {0x00, "Unknown error"}, - {0x06, "Block use error"}, - {0x07, "Command use error"}, - {0x08, "Invalid write command"}, - {0x09, "Invalid block number"}, - {0x0b, "Command code error"}, + {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); @@ -88,14 +91,17 @@ int MFPCommitPerso(bool activateField, bool leaveSignalON, uint8_t *dataout, int 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, rcmd, 4, &rcmd[4], VerboseMode); + 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 && mac) - CalculateMAC(session, dataout, *dataoutlen, mac, VerboseMode); + if (session) + session->R_Ctr++; + + if(session && mac && *dataoutlen > 11) + CalculateMAC(session, mtypReadResp, blockNum, blockCount, dataout, *dataoutlen - 8 - 2, mac, VerboseMode); return 0; } @@ -104,14 +110,17 @@ int MFPWriteBlock(mf4Session *session, uint8_t blockNum, uint8_t *data, bool act uint8_t rcmd[1 + 2 + 16 + 8] = {0xA3, blockNum, 0x00}; memmove(&rcmd[3], data, 16); if (session) - CalculateMAC(session, rcmd, 19, &rcmd[19], VerboseMode); + 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 && mac) - CalculateMAC(session, dataout, *dataoutlen, mac, VerboseMode); + if (session) + session->W_Ctr++; + + if(session && mac && *dataoutlen > 3) + CalculateMAC(session, mtypWriteResp, blockNum, 1, dataout, *dataoutlen, mac, VerboseMode); return 0; } @@ -420,7 +429,7 @@ int CmdHFMFPRdbl(const char *cmd) { int keylen = 0; CLIParserInit("hf mfp rdbl", - "Reads several blocks from Mifare Plus card in plain mode.", + "Reads several blocks from Mifare Plus card.", "Usage:\n\thf mfp rdbl 0 000102030405060708090a0b0c0d0e0f -> executes authentication and read block 0 data\n" "\thf mfp rdbl 1 -v -> executes authentication and shows sector 1 data with default key 0xFF..0xFF and some additional data\n"); @@ -429,7 +438,7 @@ int CmdHFMFPRdbl(const char *cmd) { arg_lit0("vV", "verbose", "show internal data."), arg_int0("nN", "count", "blocks count (by default 1).", NULL), arg_lit0("bB", "keyb", "use key B (by default keyA)."), - arg_lit0("pP", "plain", "plain communication between reader and card."), + arg_lit0("pP", "plain", "plain communication mode between reader and card."), arg_int1(NULL, NULL, "", NULL), arg_str0(NULL, NULL, "", NULL), arg_param_end @@ -439,7 +448,7 @@ int CmdHFMFPRdbl(const char *cmd) { bool verbose = arg_get_lit(1); int blocksCount = arg_get_int_def(2, 1); bool keyB = arg_get_lit(3); - int plain = arg_get_lit(4) | true; + int plain = arg_get_lit(4); uint32_t blockn = arg_get_int(5); CLIGetHexWithReturn(6, key, &keylen); CLIParserFree(); @@ -467,6 +476,10 @@ int CmdHFMFPRdbl(const char *cmd) { return 1; } + if (blocksCount > 1 && mfIsSectorTrailer(blockn)) { + PrintAndLog("WARNING: trailer!"); + } + uint8_t sectorNum = mfSectorNum(blockn & 0xff); uint16_t uKeyNum = 0x4000 + sectorNum * 2 + (keyB ? 1 : 0); keyn[0] = uKeyNum >> 8; @@ -504,13 +517,13 @@ int CmdHFMFPRdbl(const char *cmd) { for(int i = 0; i < blocksCount; i++) { PrintAndLog("data[%03d]: %s", indx, sprint_hex(&data[1 + i * 16], 16)); indx++; - if (mfIsSectorTrailer(indx)){ + if (mfIsSectorTrailer(indx) && i != blocksCount - 1){ PrintAndLog("data[%03d]: ------------------- trailer -------------------", indx); indx++; } } - if (!memcmp(&data[blocksCount * 16 + 1], mac, 8)) { + if (memcmp(&data[blocksCount * 16 + 1], mac, 8)) { PrintAndLog("WARNING: mac not equal..."); PrintAndLog("MAC card: %s", sprint_hex(&data[blocksCount * 16 + 1], 8)); PrintAndLog("MAC reader: %s", sprint_hex(mac, 8)); @@ -528,7 +541,7 @@ int CmdHFMFPRdsc(const char *cmd) { int keylen = 0; CLIParserInit("hf mfp rdsc", - "Reads one sector from Mifare Plus card in plain mode.", + "Reads one sector from Mifare Plus card.", "Usage:\n\thf mfp rdsc 0 000102030405060708090a0b0c0d0e0f -> executes authentication and read sector 0 data\n" "\thf mfp rdsc 1 -v -> executes authentication and shows sector 1 data with default key 0xFF..0xFF and some additional data\n"); @@ -536,7 +549,7 @@ int CmdHFMFPRdsc(const char *cmd) { arg_param_begin, arg_lit0("vV", "verbose", "show internal data."), arg_lit0("bB", "keyb", "use key B (by default keyA)."), - arg_lit0("pP", "plain", "plain communication between reader and card."), + arg_lit0("pP", "plain", "plain communication mode between reader and card."), arg_int1(NULL, NULL, "", NULL), arg_str0(NULL, NULL, "", NULL), arg_param_end @@ -545,7 +558,7 @@ int CmdHFMFPRdsc(const char *cmd) { bool verbose = arg_get_lit(1); bool keyB = arg_get_lit(2); - bool plain = arg_get_lit(3) | true; + bool plain = arg_get_lit(3); uint32_t sectorNum = arg_get_int(4); CLIGetHexWithReturn(5, key, &keylen); CLIParserFree(); @@ -604,7 +617,7 @@ int CmdHFMFPRdsc(const char *cmd) { PrintAndLog("data[%03d]: %s", n, sprint_hex(&data[1], 16)); - if (!memcmp(&data[1 + 16], mac, 8)) { + if (memcmp(&data[1 + 16], mac, 8)) { PrintAndLog("WARNING: mac on block %d not equal...", n); PrintAndLog("MAC card: %s", sprint_hex(&data[1 + 16], 8)); PrintAndLog("MAC reader: %s", sprint_hex(mac, 8)); @@ -706,7 +719,7 @@ int CmdHFMFPWrbl(const char *cmd) { return 6; } - if (!memcmp(&data[1], mac, 8)) { + if (memcmp(&data[1], mac, 8)) { PrintAndLog("WARNING: mac not equal..."); PrintAndLog("MAC card: %s", sprint_hex(&data[1], 8)); PrintAndLog("MAC reader: %s", sprint_hex(mac, 8)); @@ -716,6 +729,7 @@ int CmdHFMFPWrbl(const char *cmd) { } DropField(); + PrintAndLog("Write OK."); return 0; } @@ -729,7 +743,7 @@ static command_t CommandTable[] = {"auth", CmdHFMFPAuth, 0, "Authentication"}, {"rdbl", CmdHFMFPRdbl, 0, "Read blocks"}, {"rdsc", CmdHFMFPRdsc, 0, "Read sectors"}, -// {"wrbl", CmdHFMFPWrbl, 0, "Write blocks"}, + {"wrbl", CmdHFMFPWrbl, 0, "Write blocks"}, {NULL, NULL, 0, NULL} }; diff --git a/client/mifare4.c b/client/mifare4.c index e1021196..866e854f 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 @@ -16,24 +17,85 @@ #include "ui.h" #include "polarssl/libpcrypto.h" -int CalculateMAC(mf4Session *session, uint8_t *data, int datalen, uint8_t *mac, bool verbose) { - if (!session || !session->Authenticated || !mac || !data || !datalen) +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", datalen, sprint_hex(data, datalen)); + PrintAndLog("MAC data[%d]: %s", macdatalen, sprint_hex(macdata, macdatalen)); - return aes_cmac8(NULL, session->Key, data, mac, datalen); + 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; @@ -67,17 +129,17 @@ 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) @@ -97,19 +159,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(); @@ -118,10 +210,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"); diff --git a/client/mifare4.h b/client/mifare4.h index 5b789a70..2453797b 100644 --- a/client/mifare4.h +++ b/client/mifare4.h @@ -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 @@ -19,12 +20,25 @@ typedef struct { bool Authenticated; uint8_t Key[16]; uint16_t KeyNum; - uint8_t Rnd1[16]; - uint8_t Rnd2[16]; - + uint8_t RndA[16]; + uint8_t RndB[16]; + uint8_t TI[4]; + uint8_t PICCap2[6]; + uint8_t PCDCap2[6]; + uint8_t Kenc[16]; + uint8_t Kmac[16]; + uint16_t R_Ctr; + uint16_t W_Ctr; }mf4Session; -extern int CalculateMAC(mf4Session *session, uint8_t *data, int datalen, uint8_t *mac, bool verbose); +typedef enum { + mtypReadCmd, + mtypReadResp, + mtypWriteCmd, + mtypWriteResp, +} MACType_t; + +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 uint8_t mfNumBlocksPerSector(uint8_t sectorNo); diff --git a/common/polarssl/libpcrypto.c b/common/polarssl/libpcrypto.c index 9be9fd26..13e37f00 100644 --- a/common/polarssl/libpcrypto.c +++ b/common/polarssl/libpcrypto.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 @@ -45,7 +46,7 @@ int aes_decode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int l return 0; } -// NIST Special Publication 800-38B — Recommendation for block cipher modes of operation: The CMAC mode for authentication. +// NIST Special Publication 800-38B — Recommendation for block cipher modes of operation: The CMAC mode for authentication. // https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/AES_CMAC.pdf int aes_cmac(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *mac, int length) { memset(mac, 0x00, 16); @@ -53,16 +54,10 @@ int aes_cmac(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *mac, int length if (iv) memcpy(iiv, iv, 16); - // padding: ISO/IEC 9797-1 Message Authentication Codes (MACs) - Part 1: Mechanisms using a block cipher - uint8_t data[2049] = {0}; // length + 16 - memcpy(data, input, length); - data[length] = 0x80; - int datalen = (length & 0xfffffff0) + 0x10; - // NIST 800-38B aes_cmac128_context ctx; aes_cmac128_starts(&ctx, key); - aes_cmac128_update(&ctx, data, datalen); + aes_cmac128_update(&ctx, input, length); aes_cmac128_final(&ctx, mac); return 0; From 6eeb5f1c29d33848c6a71703d02c8465237c7e7e Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko <807634+merlokk@users.noreply.github.com> Date: Wed, 31 Oct 2018 20:47:37 +0200 Subject: [PATCH 309/310] fix warning under linux (#709) --- armsrc/iso15693.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index 50432392..04f43825 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -862,7 +862,7 @@ static int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint3 uint8_t dmaBuf[ISO15693_DMA_BUFFER_SIZE]; // the decoder data structure - DecodeReader_t DecodeReader; + DecodeReader_t DecodeReader = {0}; DecodeReaderInit(received, max_len, &DecodeReader); // wait for last transfer to complete From bdf96aae39d6df270de7f6f58dc034e35434d933 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Wed, 31 Oct 2018 22:00:36 +0100 Subject: [PATCH 310/310] fix hf 15 reader (merge error) --- armsrc/iso15693.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index 04f43825..da2aab69 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -175,6 +175,8 @@ static void CodeIso15693AsReader(uint8_t *cmd, int n) for(i = 0; i < 4; i++) { ToSendStuffBit(1); } + + ToSendMax++; } // encode data using "1 out of 256" scheme