diff --git a/CHANGELOG.md b/CHANGELOG.md index fddd51abe..1532b044b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,14 @@ 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 `hw setmux` - improve user feedback for special case (@iceman1001) + - Changed 'filename' - unified file name param across client (@iceman1001) + - Fix `lf em 4x05 brute/chk` - fixed input params crash (@iceman1001) + - Fix `lf hitag reader --23` - now accepts 6bytes key (@iceman1001) + - Fix coverity scans findigs (@merlokk, @iceman1001) + - Fix `hf iclass config` - now fallback to default config card configuration (@iceman1001) + - Changed `nfc parser` - now also identify xvcard types (@iceman1001) + - Added `hf mf gview` - view contents of a magic Gen3 GTU (@iceman1001) - Added Standalone mode for nexwatch ID credentials (@Guilhem7, @MaximeBosca) - Fix `lf em 4x50/4x70 *` reverted a missunderstanding in byte order macros (@iceman1001) - Added more keys (@equipter) diff --git a/README.md b/README.md index 4ef8f7de0..d5a7ea055 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,7 @@ The [public roadmap](https://github.com/RfidResearchGroup/proxmark3/wiki/Public- ## Supported operative systems This repo compiles nicely on - WSL1 on Windows 10 - - Proxspace v3.9 [release v3.9](https://github.com/Gator96100/ProxSpace/releases) + - Proxspace enviroment [release v3.9](https://github.com/Gator96100/ProxSpace/releases) - Windows/MinGW environment - Ubuntu, ParrotOS, Gentoo, Pentoo, Kali, NetHunter, Arch Linux, Fedora, Debian, Raspbian - Android / Termux @@ -155,7 +155,7 @@ We don't maintain any precompiled binaries in this repo. There is community effo ## Official channels Where do you find the community? - [RFID Hacking community discord server](https://discord.gg/QfPvGFRQxH) - - [Proxmark3 IRC channel](http://webchat.freenode.net/?channels=#proxmark3) + - [Proxmark3 IRC channel](https://web.libera.chat/?channels=#proxmark3) - [Proxmark3 sub reddit](https://www.reddit.com/r/proxmark3/) - [Proxmark3 forum](http://www.proxmark.org/forum/index.php) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index af5c01c72..b6781ac6d 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1568,6 +1568,14 @@ static void PacketReceived(PacketCommandNG *packet) { MifareGen3Freez(); break; } + case CMD_HF_MIFARE_G3_RDBL: { + struct p { + uint8_t blockno; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + MifareG3ReadBlk(payload->blockno); + break; + } case CMD_HF_MIFARE_PERSONALIZE_UID: { struct p { uint8_t keytype; diff --git a/armsrc/hitag2.c b/armsrc/hitag2.c index 063ed6c07..8aa7178cb 100644 --- a/armsrc/hitag2.c +++ b/armsrc/hitag2.c @@ -1148,6 +1148,9 @@ void SniffHitag2(void) { // Enable and reset counter AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + // Assert a sync signal. This sets all timers to 0 on next active clock edge + AT91C_BASE_TCB->TCB_BCR = 1; + int frame_count = 0, response = 0, overflow = 0, lastbit = 1, tag_sof = 4; bool rising_edge = false, reader_frame = false, bSkip = true; uint8_t rx[HITAG_FRAME_LEN]; @@ -1293,11 +1296,15 @@ void SniffHitag2(void) { // Reset the timer to restart while-loop that receives frames AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; + + // Assert a sync signal. This sets all timers to 0 on next active clock edge + AT91C_BASE_TCB->TCB_BCR = 1; } LEDsoff(); AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); set_tracing(false); diff --git a/armsrc/lfadc.c b/armsrc/lfadc.c index 6a351bfca..2bfe41529 100644 --- a/armsrc/lfadc.c +++ b/armsrc/lfadc.c @@ -246,6 +246,9 @@ void lf_init(bool reader, bool simulate) { AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + // Assert a sync signal. This sets all timers to 0 on next active clock edge + AT91C_BASE_TCB->TCB_BCR = 1; + // Prepare data trace uint32_t bufsize = 10000; diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 4330f2c4f..2a8642f82 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -2594,6 +2594,42 @@ OUT: BigBuf_free(); } +void MifareG3ReadBlk(uint8_t blockno) { + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + clear_trace(); + set_tracing(true); + + int retval = PM3_SUCCESS; + uint8_t *par = BigBuf_malloc(MAX_PARITY_SIZE); + uint8_t *buf = BigBuf_malloc(PM3_CMD_DATA_SIZE); + uint8_t *uid = BigBuf_malloc(10); + if (iso14443a_select_card(uid, NULL, NULL, true, 0, true) == false) { + retval = PM3_ESOFT; + goto OUT; + } + + LED_B_ON(); + uint32_t save_iso14a_timeout = iso14a_get_timeout(); + iso14a_set_timeout(13560000 / 1000 / (8 * 16) * 1000); // 2 seconds timeout + + uint8_t cmd[] = { 0xCF, 0x00, 0x00, 0x00, 0x00, 0xCE, blockno, 0x00, 0x00}; + AddCrc14A(cmd, sizeof(cmd) - 2); + + ReaderTransmit(cmd, sizeof(cmd), NULL); + int res = ReaderReceive(buf, par); + if (res != 18) { + retval = PM3_ESOFT; + } + iso14a_set_timeout(save_iso14a_timeout); + LED_B_OFF(); + +OUT: + reply_ng(CMD_HF_MIFARE_G3_RDBL, retval, buf, 18); + // turns off + OnSuccessMagic(); + BigBuf_free(); +} + void MifareSetMod(uint8_t *datain) { uint8_t mod = datain[0]; diff --git a/armsrc/mifarecmd.h b/armsrc/mifarecmd.h index baf3d9e31..bacb8405e 100644 --- a/armsrc/mifarecmd.h +++ b/armsrc/mifarecmd.h @@ -49,6 +49,9 @@ void MifareGen3UID(uint8_t uidlen, uint8_t *uid); // Gen 3 magic card set UID wi void MifareGen3Blk(uint8_t block_len, uint8_t *block); // Gen 3 magic card overwrite manufacturer block void MifareGen3Freez(void); // Gen 3 magic card lock further UID changes +// MFC GEN3 GTU +void MifareG3ReadBlk(uint8_t blockno); + void MifareSetMod(uint8_t *datain); void MifarePersonalizeUID(uint8_t keyType, uint8_t perso_option, uint64_t key); diff --git a/armsrc/pcf7931.c b/armsrc/pcf7931.c index 6ad44d7c4..bb96ea97e 100644 --- a/armsrc/pcf7931.c +++ b/armsrc/pcf7931.c @@ -14,13 +14,17 @@ #define ALLOC 16 size_t DemodPCF7931(uint8_t **outBlocks) { + + // 2021 iceman, memor uint8_t bits[256] = {0x00}; uint8_t blocks[8][16]; + uint8_t *dest = BigBuf_get_addr(); int GraphTraceLen = BigBuf_max_traceLen(); - if (GraphTraceLen > 18000) + if (GraphTraceLen > 18000) { GraphTraceLen = 18000; + } int i = 2, j, lastval, bitidx, half_switch; int clock = 64; @@ -38,15 +42,17 @@ size_t DemodPCF7931(uint8_t **outBlocks) { /* Find first local max/min */ if (dest[1] > dest[0]) { while (i < GraphTraceLen) { - if (!(dest[i] > dest[i - 1]) && dest[i] > lmax) + if (!(dest[i] > dest[i - 1]) && dest[i] > lmax) { break; + } i++; } dir = 0; } else { while (i < GraphTraceLen) { - if (!(dest[i] < dest[i - 1]) && dest[i] < lmin) + if (!(dest[i] < dest[i - 1]) && dest[i] < lmin) { break; + } i++; } dir = 1; @@ -58,6 +64,7 @@ size_t DemodPCF7931(uint8_t **outBlocks) { block_done = 0; for (bitidx = 0; i < GraphTraceLen; i++) { + if ((dest[i - 1] > dest[i] && dir == 1 && dest[i] > lmax) || (dest[i - 1] < dest[i] && dir == 0 && dest[i] < lmin)) { lc = i - lastval; lastval = i; @@ -66,8 +73,8 @@ size_t DemodPCF7931(uint8_t **outBlocks) { // Tolerance is 1/8 of clock rate (arbitrary) if (ABS(lc - clock / 4) < tolerance) { // 16T0 - if ((i - pmc) == lc) { /* 16T0 was previous one */ - /* It's a PMC ! */ + if ((i - pmc) == lc) { // 16T0 was previous one + // It's a PMC i += (128 + 127 + 16 + 32 + 33 + 16) - 1; lastval = i; pmc = 0; @@ -77,8 +84,8 @@ size_t DemodPCF7931(uint8_t **outBlocks) { } } else if (ABS(lc - clock / 2) < tolerance) { // 32TO - if ((i - pmc) == lc) { /* 16T0 was previous one */ - /* It's a PMC ! */ + if ((i - pmc) == lc) { // 16T0 was previous one + // It's a PMC ! i += (128 + 127 + 16 + 32 + 33) - 1; lastval = i; pmc = 0; @@ -95,8 +102,9 @@ size_t DemodPCF7931(uint8_t **outBlocks) { // Error if (++warnings > 10) { - if (DBGLEVEL >= DBG_EXTENDED) - Dbprintf("Error: too many detection errors, aborting."); + if (DBGLEVEL >= DBG_EXTENDED) { + Dbprintf("Error: too many detection errors, aborting"); + } return 0; } @@ -122,13 +130,19 @@ size_t DemodPCF7931(uint8_t **outBlocks) { block_done = 0; half_switch = 0; } - if (i < GraphTraceLen) + + if (i < GraphTraceLen) { dir = (dest[i - 1] > dest[i]) ? 0 : 1; + } } - if (bitidx == 255) + + if (bitidx == 255) { bitidx = 0; - warnings = 0; - if (num_blocks == 4) break; + } + + if (num_blocks == 4) { + break; + } } memcpy(outBlocks, blocks, 16 * num_blocks); return num_blocks; @@ -138,10 +152,11 @@ bool IsBlock0PCF7931(uint8_t *block) { // assuming all RFU bits are set to 0 // if PAC is enabled password is set to 0 if (block[7] == 0x01) { - if (!memcmp(block, "\x00\x00\x00\x00\x00\x00\x00", 7) - && !memcmp(block + 9, "\x00\x00\x00\x00\x00\x00\x00", 7)) { + if (!memcmp(block, "\x00\x00\x00\x00\x00\x00\x00", 7) && + !memcmp(block + 9, "\x00\x00\x00\x00\x00\x00\x00", 7)) { return true; } + } else if (block[7] == 0x00) { if (!memcmp(block + 9, "\x00\x00\x00\x00\x00\x00\x00", 7)) { return true; @@ -211,8 +226,9 @@ void ReadPCF7931(void) { // exit if too many errors during reading if (tries > 50 && (2 * errors > tries)) { - if (DBGLEVEL >= DBG_INFO) + if (DBGLEVEL >= DBG_INFO) { Dbprintf("[!!] Error reading the tag, only partial content"); + } goto end; } @@ -461,27 +477,32 @@ void SendCmdPCF7931(uint32_t *tab) { //initialization of the timer AT91C_BASE_PMC->PMC_PCER |= (0x1 << AT91C_ID_TC0); 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; // timer disable - AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; //clock at 48/32 MHz + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; // timer disable + AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; // clock at 48/32 MHz AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN; + + // Assert a sync signal. This sets all timers to 0 on next active clock edge AT91C_BASE_TCB->TCB_BCR = 1; tempo = AT91C_BASE_TC0->TC_CV; for (u = 0; tab[u] != 0; u += 3) { // modulate antenna HIGH(GPIO_SSC_DOUT); - while (tempo != tab[u]) + while (tempo != tab[u]) { tempo = AT91C_BASE_TC0->TC_CV; + } // stop modulating antenna LOW(GPIO_SSC_DOUT); - while (tempo != tab[u + 1]) + while (tempo != tab[u + 1]) { tempo = AT91C_BASE_TC0->TC_CV; + } // modulate antenna HIGH(GPIO_SSC_DOUT); - while (tempo != tab[u + 2]) + while (tempo != tab[u + 2]) { tempo = AT91C_BASE_TC0->TC_CV; + } } LED_A_OFF(); diff --git a/armsrc/ticks.c b/armsrc/ticks.c index 9b563cc54..8f15afa12 100644 --- a/armsrc/ticks.c +++ b/armsrc/ticks.c @@ -125,6 +125,8 @@ void StartCountUS(void) { AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + + // Assert a sync signal. This sets all timers to 0 on next active clock edge AT91C_BASE_TCB->TCB_BCR = 1; while (AT91C_BASE_TC1->TC_CV > 0); diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 5c826790d..a81682337 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -213,6 +213,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/iso7816/iso7816core.c ${PM3_ROOT}/client/src/cipurse/cipursecrypto.c ${PM3_ROOT}/client/src/cipurse/cipursecore.c + ${PM3_ROOT}/client/src/cipurse/cipursetest.c ${PM3_ROOT}/client/src/loclass/cipher.c ${PM3_ROOT}/client/src/loclass/cipherutils.c ${PM3_ROOT}/client/src/loclass/elite_crack.c diff --git a/client/Makefile b/client/Makefile index c0f87e0a7..9ed3feb86 100644 --- a/client/Makefile +++ b/client/Makefile @@ -559,6 +559,7 @@ SRCS = aiddesfire.c \ fido/fidocore.c \ cipurse/cipursecore.c \ cipurse/cipursecrypto.c \ + cipurse/cipursetest.c \ fileutils.c \ flash.c \ generator.c \ diff --git a/client/luascripts/hf_mfu_magicwrite.lua b/client/luascripts/hf_mfu_magicwrite.lua index 199aec7c0..803f3f224 100644 --- a/client/luascripts/hf_mfu_magicwrite.lua +++ b/client/luascripts/hf_mfu_magicwrite.lua @@ -16,25 +16,25 @@ version = 'v1.1.4' desc = 'This script enables easy programming of a MAGIC NTAG 21* card' example = [[ -- read magic tag configuration - ]]..ansicolors.yellow..[[script run hf_mfu_magicwrite -c ]]..ansicolors.reset..[[ + ]]..ansicolors.yellow..[[script run hf_mfu_magicwrite -c ]]..ansicolors.reset..[[ -- set uid - ]]..ansicolors.yellow..[[script run hf_mfu_magicwrite -u 04112233445566 ]]..ansicolors.reset..[[ + ]]..ansicolors.yellow..[[script run hf_mfu_magicwrite -u 04112233445566 ]]..ansicolors.reset..[[ -- set pwd / pack - ]]..ansicolors.yellow..[[script run hf_mfu_magicwrite -p 11223344 -a 8080 ]]..ansicolors.reset..[[ + ]]..ansicolors.yellow..[[script run hf_mfu_magicwrite -p 11223344 -a 8080 ]]..ansicolors.reset..[[ -- set version to NTAG213 - ]]..ansicolors.yellow..[[script run hf_mfu_magicwrite -v 0004040201000f03 ]]..ansicolors.reset..[[ + ]]..ansicolors.yellow..[[script run hf_mfu_magicwrite -v 0004040201000f03 ]]..ansicolors.reset..[[ -- set signature - ]]..ansicolors.yellow..[[script run hf_mfu_magicwrite -s 1122334455667788990011223344556677889900112233445566778899001122 ]]..ansicolors.reset..[[ + ]]..ansicolors.yellow..[[script run hf_mfu_magicwrite -s 1122334455667788990011223344556677889900112233445566778899001122 ]]..ansicolors.reset..[[ -- wipe tag - ]]..ansicolors.yellow..[[script run hf_mfu_magicwrite -w ]]..ansicolors.reset..[[ + ]]..ansicolors.yellow..[[script run hf_mfu_magicwrite -w ]]..ansicolors.reset..[[ -- wipe a locked down tag by giving the password - ]]..ansicolors.yellow..[[script run hf_mfu_magicwrite -k ffffffff -w ]]..ansicolors.reset..[[ + ]]..ansicolors.yellow..[[script run hf_mfu_magicwrite -k ffffffff -w ]]..ansicolors.reset..[[ ]] usage = [[ diff --git a/client/src/cipurse/cipursecrypto.c b/client/src/cipurse/cipursecrypto.c index e68adc14f..9761fb1e3 100644 --- a/client/src/cipurse/cipursecrypto.c +++ b/client/src/cipurse/cipursecrypto.c @@ -506,6 +506,11 @@ void CipurseCAPDURespDecode(CipurseContext *ctx, uint8_t *srcdata, size_t srcdat CipurseCChannelDecrypt(ctx, srcdata, srcdatalen, buf, &buflen); //PrintAndLogEx(INFO, "data plain[%d]: %s", buflen, sprint_hex(buf, buflen)); + if (buflen == 0) { + PrintAndLogEx(ERR, "APDU can't decode crypto stream"); + break; + } + micdatalen = buflen - 2 - CIPURSE_MIC_LENGTH; memcpy(micdata, buf, buflen); memcpy(&micdata[micdatalen], &buf[buflen - 2], 2); diff --git a/client/src/cipurse/cipursetest.c b/client/src/cipurse/cipursetest.c new file mode 100644 index 000000000..6ccd4f91d --- /dev/null +++ b/client/src/cipurse/cipursetest.c @@ -0,0 +1,375 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2021 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. +//----------------------------------------------------------------------------- +// tests for crypto +//----------------------------------------------------------------------------- + +#include "cipursetest.h" + +#include +#include // memcpy memset +#include "fileutils.h" + +#include "cipurse/cipursecrypto.h" +#include "cipurse/cipursecore.h" + +uint8_t Key[] = CIPURSE_DEFAULT_KEY; +uint8_t KeyKvv[CIPURSE_KVV_LENGTH] = {0x5f, 0xd6, 0x7b, 0xcb}; + +uint8_t TestRandom[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22}; + +uint8_t TestData[16] = {0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +uint8_t TestDataPadded[16] = {0x11, 0x22, 0x33, 0x44, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static bool TestKVV(void) { + uint8_t kvv[CIPURSE_KVV_LENGTH] = {0}; + CipurseCGetKVV(Key, kvv); + + bool res = memcmp(KeyKvv, kvv, CIPURSE_KVV_LENGTH) == 0; + + if (res) + PrintAndLogEx(INFO, "kvv.............. " _GREEN_("passed")); + else + PrintAndLogEx(ERR, "kvv.............. " _RED_("fail")); + + return res; +} + +static bool TestISO9797M2(void) { + uint8_t data[32] = {0}; + + size_t ddatalen = 0; + AddISO9797M2Padding(data, &ddatalen, TestData, 4, 16); + bool res = (ddatalen == 16); + res = res && (memcmp(data, TestDataPadded, ddatalen) == 0); + + res = res && (FindISO9797M2PaddingDataLen(data, ddatalen) == 4); + + if (res) + PrintAndLogEx(INFO, "ISO9797M2........ " _GREEN_("passed")); + else + PrintAndLogEx(ERR, "ISO9797M2........ " _RED_("fail")); + + return res; +} + +static bool TestSMI(void) { + CipurseContext ctx = {0}; + CipurseCClearContext(&ctx); + + bool res = (isCipurseCChannelSecuritySet(&ctx) == false); + + CipurseCChannelSetSecurityLevels(&ctx, CPSPlain, CPSPlain); + res = res && (CipurseCGetSMI(&ctx, false) == 0x00); + res = res && (CipurseCGetSMI(&ctx, true) == 0x01); + + CipurseCChannelSetSecurityLevels(&ctx, CPSPlain, CPSMACed); + res = res && (CipurseCGetSMI(&ctx, false) == 0x04); + res = res && (CipurseCGetSMI(&ctx, true) == 0x05); + + CipurseCChannelSetSecurityLevels(&ctx, CPSMACed, CPSMACed); + res = res && (CipurseCGetSMI(&ctx, false) == 0x44); + res = res && (CipurseCGetSMI(&ctx, true) == 0x45); + + CipurseCChannelSetSecurityLevels(&ctx, CPSMACed, CPSEncrypted); + res = res && (CipurseCGetSMI(&ctx, false) == 0x48); + res = res && (CipurseCGetSMI(&ctx, true) == 0x49); + + CipurseCChannelSetSecurityLevels(&ctx, CPSEncrypted, CPSEncrypted); + res = res && (CipurseCGetSMI(&ctx, false) == 0x88); + res = res && (CipurseCGetSMI(&ctx, true) == 0x89); + + if (res) + PrintAndLogEx(INFO, "SMI.............. " _GREEN_("passed")); + else + PrintAndLogEx(ERR, "SMI.............. " _RED_("fail")); + + return res; +} + +static bool TestMIC(void) { + uint8_t mic[4] = {0}; + + CipurseCGenerateMIC(TestData, 4, mic); + uint8_t valid_mic4[4] = {0xD4, 0x71, 0xA7, 0x73}; + bool res = (memcmp(mic, valid_mic4, 4) == 0); + + res = res && (CipurseCCheckMIC(TestData, 4, mic)); + + CipurseCGenerateMIC(TestData, 6, mic); + uint8_t valid_mic6[4] = {0xAA, 0x90, 0xFC, 0x5A}; + res = res && (memcmp(mic, valid_mic6, 4) == 0); + + res = res && (CipurseCCheckMIC(TestData, 6, mic)); + + if (res) + PrintAndLogEx(INFO, "MIC.............. " _GREEN_("passed")); + else + PrintAndLogEx(ERR, "MIC.............. " _RED_("fail")); + + return res; +} + + +static bool TestAuth(void) { + CipurseContext ctx = {0}; + CipurseCClearContext(&ctx); + + bool res = (isCipurseCChannelSecuritySet(&ctx) == false); + + CipurseCSetKey(&ctx, 1, Key); + res = res && (memcmp(ctx.key, Key, 16) == 0); + res = res && (ctx.keyId == 1); + + CipurseCSetRandomFromPICC(&ctx, TestRandom); + res = res && (memcmp(ctx.RP, TestRandom, 16) == 0); + res = res && (memcmp(ctx.rP, &TestRandom[16], 6) == 0); + + uint8_t hrandom[] = {0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}; + CipurseCSetRandomHost(&ctx); + res = res && (memcmp(ctx.RT, hrandom, 16) == 0); + res = res && (memcmp(ctx.rT, &hrandom[16], 6) == 0); + + uint8_t authparams[16 + 16 + 6] = {0}; + CipurseCAuthenticateHost(&ctx, authparams); + uint8_t aparamstest[] = {0x12, 0xAA, 0x79, 0xA9, 0x03, 0xC5, 0xB4, 0x6A, 0x27, 0x1B, 0x13, 0xAE, 0x02, 0x50, 0x1C, 0x99, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 + }; + res = res && (memcmp(authparams, aparamstest, sizeof(authparams)) == 0); + + uint8_t ct[] = {0xBE, 0x10, 0x6B, 0xB9, 0xAD, 0x84, 0xBC, 0xE1, 0x9F, 0xAE, 0x0C, 0x62, 0xCC, 0xC7, 0x0D, 0x41}; + res = res && CipurseCCheckCT(&ctx, ct); + + CipurseCChannelSetSecurityLevels(&ctx, CPSMACed, CPSMACed); + res = res && (isCipurseCChannelSecuritySet(&ctx) == true); + + uint8_t framekey[] = {0xCF, 0x6F, 0x3A, 0x47, 0xFC, 0xAC, 0x8D, 0x38, 0x25, 0x75, 0x8B, 0xFC, 0x8B, 0x61, 0x68, 0xF3}; + res = res && (memcmp(ctx.frameKey, framekey, sizeof(framekey)) == 0); + + if (res) + PrintAndLogEx(INFO, "Auth............. " _GREEN_("passed")); + else + PrintAndLogEx(ERR, "Auth............. " _RED_("fail")); + + return res; +} + +static bool TestMAC(void) { + CipurseContext ctx = {0}; + + // authentication + CipurseCClearContext(&ctx); + CipurseCSetKey(&ctx, 1, Key); + CipurseCSetRandomFromPICC(&ctx, TestRandom); + uint8_t authparams[16 + 16 + 6] = {0}; + CipurseCAuthenticateHost(&ctx, authparams); + uint8_t ct[] = {0xBE, 0x10, 0x6B, 0xB9, 0xAD, 0x84, 0xBC, 0xE1, 0x9F, 0xAE, 0x0C, 0x62, 0xCC, 0xC7, 0x0D, 0x41}; + bool res = CipurseCCheckCT(&ctx, ct); + CipurseCChannelSetSecurityLevels(&ctx, CPSMACed, CPSMACed); + res = res && (isCipurseCChannelSecuritySet(&ctx) == true); + + // check MAC + uint8_t mac[8] = {0}; + + CipurseCGenerateMAC(&ctx, TestData, 4, mac); + uint8_t testmac1[8] = {0xAB, 0x5C, 0x86, 0x18, 0x7F, 0x73, 0xEC, 0x4E}; + res = res && (memcmp(mac, testmac1, 8) == 0); + + uint8_t framekey1[] = {0x7D, 0x6F, 0x31, 0x40, 0xC8, 0x47, 0xED, 0x3F, 0x0A, 0x21, 0xE6, 0xFB, 0xC7, 0xDB, 0x27, 0xB0}; + res = res && (memcmp(ctx.frameKey, framekey1, sizeof(framekey1)) == 0); + + CipurseCCalcMACPadded(&ctx, TestData, 4, mac); + uint8_t testmac2[8] = {0x9F, 0xE9, 0x54, 0xBF, 0xFC, 0xA0, 0x7D, 0x75}; + res = res && (memcmp(mac, testmac2, 8) == 0); + + uint8_t framekey2[] = {0x1E, 0xD4, 0xB6, 0x87, 0x85, 0x93, 0x5B, 0xAF, 0xA9, 0xF2, 0xF0, 0x8F, 0xA9, 0xF0, 0xA5, 0xFB}; + res = res && (memcmp(ctx.frameKey, framekey2, sizeof(framekey2)) == 0); + + CipurseCCalcMACPadded(&ctx, TestData, 4, mac); + uint8_t testmac3[8] = {0x15, 0x6F, 0x08, 0x5C, 0x0F, 0x80, 0xE7, 0x07}; + res = res && (memcmp(mac, testmac3, 8) == 0); + + uint8_t framekey3[] = {0x0C, 0x42, 0x93, 0x73, 0x88, 0x8F, 0x63, 0xB3, 0x10, 0x8E, 0xDF, 0xDB, 0xC1, 0x20, 0x63, 0x4C}; + res = res && (memcmp(ctx.frameKey, framekey3, sizeof(framekey3)) == 0); + + uint8_t testmac4[8] = {0x0E, 0xF0, 0x70, 0xA6, 0xA1, 0x15, 0x9A, 0xB6}; + res = res && CipurseCCheckMACPadded(&ctx, TestData, 4, testmac4); + + uint8_t framekey4[] = {0xA0, 0x65, 0x1A, 0x62, 0x56, 0x5D, 0xD7, 0xC9, 0x32, 0xAE, 0x1D, 0xE0, 0xCF, 0x8D, 0xC1, 0xB9}; + res = res && (memcmp(ctx.frameKey, framekey4, sizeof(framekey4)) == 0); + + if (res) + PrintAndLogEx(INFO, "channel MAC...... " _GREEN_("passed")); + else + PrintAndLogEx(ERR, "channel MAC...... " _RED_("fail")); + + return res; +} + +static bool TestEncDec(void) { + CipurseContext ctx = {0}; + + // authentication + CipurseCClearContext(&ctx); + CipurseCSetKey(&ctx, 1, Key); + CipurseCSetRandomFromPICC(&ctx, TestRandom); + uint8_t authparams[16 + 16 + 6] = {0}; + CipurseCAuthenticateHost(&ctx, authparams); + uint8_t ct[] = {0xBE, 0x10, 0x6B, 0xB9, 0xAD, 0x84, 0xBC, 0xE1, 0x9F, 0xAE, 0x0C, 0x62, 0xCC, 0xC7, 0x0D, 0x41}; + bool res = CipurseCCheckCT(&ctx, ct); + CipurseCChannelSetSecurityLevels(&ctx, CPSMACed, CPSMACed); + res = res && (isCipurseCChannelSecuritySet(&ctx) == true); + + // check Encode-Decode + uint8_t dstdata[32] = {0}; + size_t dstdatalen = 0; + + CipurseCEncryptDecrypt(&ctx, TestData, 16, dstdata, true); + uint8_t tested1[16] = {0x5F, 0x01, 0x18, 0x79, 0xE0, 0x57, 0xA7, 0xE5, 0x34, 0x39, 0x6E, 0x32, 0x62, 0xF2, 0x71, 0x27}; + res = res && (memcmp(dstdata, tested1, 16) == 0); + + uint8_t tested2[16] = {0xA6, 0x22, 0xB5, 0xCF, 0xE8, 0x6E, 0x67, 0xF4, 0xAA, 0x88, 0xB1, 0x19, 0x87, 0xCF, 0xC9, 0xD2}; + CipurseCEncryptDecrypt(&ctx, tested2, 16, dstdata, false); + res = res && (memcmp(dstdata, TestData, 16) == 0); + + CipurseCChannelEncrypt(&ctx, TestData, 16, dstdata, &dstdatalen); + uint8_t tested3[32] = {0x1E, 0x0C, 0xD1, 0xF5, 0x8E, 0x0B, 0xAE, 0xF0, 0x06, 0xC6, 0xED, 0x73, 0x3F, 0x8A, 0x87, 0xCF, + 0x36, 0xCC, 0xF2, 0xF4, 0x7D, 0x33, 0x50, 0xF1, 0x8E, 0xFF, 0xD1, 0x7D, 0x42, 0x88, 0xD5, 0xEE + }; + res = res && (dstdatalen == 32); + res = res && (memcmp(dstdata, tested3, 32) == 0); + + uint8_t tested4[32] = {0xC0, 0x42, 0xDB, 0xD9, 0x53, 0xFF, 0x01, 0xE5, 0xCC, 0x49, 0x8C, 0x9C, 0xDA, 0x60, 0x73, 0xA7, + 0xE1, 0xEB, 0x14, 0x69, 0xF6, 0x39, 0xF3, 0xE1, 0x07, 0x03, 0x32, 0xF4, 0x27, 0xF9, 0x48, 0x3D + }; + CipurseCChannelDecrypt(&ctx, tested4, 32, dstdata, &dstdatalen); + res = res && (dstdatalen == 16); + res = res && (memcmp(dstdata, TestData, 16) == 0); + + if (res) + PrintAndLogEx(INFO, "channel EncDec... " _GREEN_("passed")); + else + PrintAndLogEx(ERR, "channel EncDec... " _RED_("fail")); + + return res; +} + +//void CipurseCAPDUReqEncode(CipurseContext *ctx, sAPDU *srcapdu, sAPDU *dstapdu, uint8_t *dstdatabuf, bool includeLe, uint8_t Le); +//void CipurseCAPDURespDecode(CipurseContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen, uint16_t *sw); +static bool TestAPDU(void) { + CipurseContext ctx = {0}; + + // authentication + CipurseCClearContext(&ctx); + CipurseCSetKey(&ctx, 1, Key); + CipurseCSetRandomFromPICC(&ctx, TestRandom); + uint8_t authparams[16 + 16 + 6] = {0}; + CipurseCAuthenticateHost(&ctx, authparams); + uint8_t ct[] = {0xBE, 0x10, 0x6B, 0xB9, 0xAD, 0x84, 0xBC, 0xE1, 0x9F, 0xAE, 0x0C, 0x62, 0xCC, 0xC7, 0x0D, 0x41}; + bool res = CipurseCCheckCT(&ctx, ct); + CipurseCChannelSetSecurityLevels(&ctx, CPSMACed, CPSMACed); + res = res && (isCipurseCChannelSecuritySet(&ctx) == true); + + // check APDU formatting + sAPDU srcAPDU = {0}; + sAPDU dstAPDU = {0}; + uint8_t dstdata[256] = {0}; + size_t dstdatalen = 0; + + // MACED APDU + srcAPDU.CLA = 0x00; + srcAPDU.INS = 0x55; + srcAPDU.P1 = 0x11; + srcAPDU.P2 = 0x22; + srcAPDU.data = TestData; + srcAPDU.Lc = 5; + + CipurseCAPDUReqEncode(&ctx, &srcAPDU, &dstAPDU, dstdata, true, 0x88); + uint8_t test1[] = {0x45, 0x11, 0x22, 0x33, 0x44, 0x00, 0x88, 0x79, 0x2B, 0xB7, 0xDD, 0xD1, 0x69, 0xA6, 0x66}; + res = res && ((srcAPDU.CLA | 0x04) == dstAPDU.CLA); + res = res && (srcAPDU.INS == dstAPDU.INS); + res = res && (srcAPDU.P1 == dstAPDU.P1); + res = res && (srcAPDU.P2 == dstAPDU.P2); + res = res && (dstAPDU.Lc == sizeof(test1)); + res = res && (memcmp(dstdata, test1, sizeof(test1)) == 0); + + uint16_t sw = 0; + uint8_t test2[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x9D, 0x80, 0xE7, 0xE3, 0x34, 0xE9, 0x97, 0x82, 0xdd, 0xee}; + CipurseCAPDURespDecode(&ctx, test2, sizeof(test2), dstdata, &dstdatalen, &sw); + res = res && (dstdatalen == 6); + res = res && (memcmp(test2, dstdata, dstdatalen) == 0); + res = res && (sw == 0xddee); + + // Plain APDU + CipurseCChannelSetSecurityLevels(&ctx, CPSPlain, CPSPlain); + CipurseCAPDUReqEncode(&ctx, &srcAPDU, &dstAPDU, dstdata, true, 0x55); + uint8_t test3[] = {0x01, 0x11, 0x22, 0x33, 0x44, 0x00, 0x55}; + res = res && ((srcAPDU.CLA | 0x04) == dstAPDU.CLA); + res = res && (srcAPDU.INS == dstAPDU.INS); + res = res && (srcAPDU.P1 == dstAPDU.P1); + res = res && (srcAPDU.P2 == dstAPDU.P2); + res = res && (dstAPDU.Lc == sizeof(test3)); + res = res && (memcmp(dstdata, test3, sizeof(test3)) == 0); + + uint8_t test4[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xcc, 0xdd}; + CipurseCAPDURespDecode(&ctx, test4, sizeof(test4), dstdata, &dstdatalen, &sw); + res = res && (dstdatalen == 6); + res = res && (memcmp(test4, dstdata, dstdatalen) == 0); + res = res && (sw == 0xccdd); + + // Encrypted APDU + CipurseCChannelSetSecurityLevels(&ctx, CPSEncrypted, CPSEncrypted); + CipurseCAPDUReqEncode(&ctx, &srcAPDU, &dstAPDU, dstdata, true, 0x55); + uint8_t test5[] = {0x89, 0x7D, 0xED, 0x0D, 0x04, 0x8E, 0xE1, 0x99, 0x08, 0x70, 0x56, 0x7C, 0xEE, 0x67, 0xB3, 0x33, 0x6F, 0x00}; + res = res && ((srcAPDU.CLA | 0x04) == dstAPDU.CLA); + res = res && (srcAPDU.INS == dstAPDU.INS); + res = res && (srcAPDU.P1 == dstAPDU.P1); + res = res && (srcAPDU.P2 == dstAPDU.P2); + res = res && (dstAPDU.Lc == sizeof(test5)); + res = res && (memcmp(dstdata, test5, sizeof(test5)) == 0); + + uint8_t test6[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x7E, 0x4B, 0xA0, 0xB7, 0xcc, 0xdd}; + //CipurseCChannelEncrypt(&ctx, test6, sizeof(test6), dstdata, &dstdatalen); + //PrintAndLogEx(INFO, "dstdata[%d]: %s", dstdatalen, sprint_hex(dstdata, dstdatalen)); + + uint8_t test7[] = {0x07, 0xEF, 0x16, 0x91, 0xE7, 0x0F, 0xB5, 0x10, 0x63, 0xCE, 0x66, 0xDB, 0x3B, 0xC6, 0xD4, 0xE0, 0x90, 0x00}; + CipurseCAPDURespDecode(&ctx, test7, sizeof(test7), dstdata, &dstdatalen, &sw); + res = res && (dstdatalen == 8); + res = res && (memcmp(test6, dstdata, dstdatalen) == 0); + res = res && (sw == 0xccdd); + + if (res) + PrintAndLogEx(INFO, "apdu............. " _GREEN_("passed")); + else + PrintAndLogEx(ERR, "apdu............. " _RED_("fail")); + + return res; +} + +bool CIPURSETest(bool verbose) { + bool res = true; + + PrintAndLogEx(INFO, "------ " _CYAN_("CIPURSE Tests") " ------"); + + res = res && TestKVV(); + res = res && TestISO9797M2(); + res = res && TestSMI(); + res = res && TestMIC(); + res = res && TestAuth(); + res = res && TestMAC(); + res = res && TestEncDec(); + res = res && TestAPDU(); + + PrintAndLogEx(INFO, "---------------------------"); + if (res) + PrintAndLogEx(SUCCESS, " Tests [ %s ]", _GREEN_("ok")); + else + PrintAndLogEx(FAILED, " Tests [ %s ]", _RED_("fail")); + + PrintAndLogEx(NORMAL, ""); + return res; +} diff --git a/client/src/cipurse/cipursetest.h b/client/src/cipurse/cipursetest.h new file mode 100644 index 000000000..1f69a3abc --- /dev/null +++ b/client/src/cipurse/cipursetest.h @@ -0,0 +1,20 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2021 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. +//----------------------------------------------------------------------------- +// tests for crypto +//----------------------------------------------------------------------------- + +#ifndef __CIPURSETEST_H__ +#define __CIPURSETEST_H__ + +#include +#include "common.h" +#include "cipurse/cipursecrypto.h" + +bool CIPURSETest(bool verbose); + +#endif /* __CIPURSETEST_H__ */ diff --git a/client/src/cmdhf.c b/client/src/cmdhf.c index 8a7afeb2a..83b551e33 100644 --- a/client/src/cmdhf.c +++ b/client/src/cmdhf.c @@ -91,11 +91,13 @@ int CmdHFSearch(const char *Cmd) { PROMPT_CLEARLINE; PrintAndLogEx(INPLACE, " Searching for ISO14443-A tag..."); if (IfPm3Iso14443a()) { - if (infoHF14A(false, false, false) > 0) { + int sel_state = infoHF14A(false, false, false); + if (sel_state > 0) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO 14443-A tag") " found\n"); res = PM3_SUCCESS; - infoHF14A4Applications(verbose); + if (sel_state == 1) + infoHF14A4Applications(verbose); } } diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index b221869d7..b471ed9fd 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -1027,7 +1027,7 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea // 3 byte here - 1b framing header, 2b crc16 if (APDUInFramingEnable && - ((g_frame_len && (datainlen > g_frame_len - 3)) || (datainlen > PM3_CMD_DATA_SIZE - 3))) { + ((g_frame_len && (datainlen > g_frame_len - 3)) || (datainlen > PM3_CMD_DATA_SIZE - 3))) { int clen = 0; @@ -1807,6 +1807,8 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { memcpy(card.ats, resp.data.asBytes, resp.oldarg[0]); card.ats_len = resp.oldarg[0]; // note: ats_len includes CRC Bytes + if (card.ats_len > 3) + select_status = 1; } if (card.ats_len >= 3) { // a valid ATS consists of at least the length byte (TL) and 2 CRC bytes @@ -2097,6 +2099,8 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { if ((card.sak & 0x20) == 0x20) { PrintAndLogEx(INFO, "--> SAK incorrectly claims that card supports RATS <--"); } + if (select_status == 1) + select_status = 2; } int isMagic = 0; @@ -2169,7 +2173,7 @@ int infoHF14A4Applications(bool verbose) { int res = Iso7816Select(CC_CONTACTLESS, ActivateField, true, (uint8_t *)hintAIDList[i].aid, hintAIDList[i].aid_length, result, sizeof(result), &resultlen, &sw); ActivateField = false; if (res) - continue; + break; if (sw == 0x9000 || sw == 0x6283 || sw == 0x6285) { if (!found) { diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c index 427340ca4..41dd9062d 100644 --- a/client/src/cmdhfcipurse.c +++ b/client/src/cmdhfcipurse.c @@ -25,6 +25,7 @@ #include "cmdhfcipurse.h" #include "cipurse/cipursecore.h" #include "cipurse/cipursecrypto.h" +#include "cipurse/cipursetest.h" #include "ui.h" #include "cmdhf14a.h" #include "cmdtrace.h" @@ -126,7 +127,7 @@ static int CmdHFCipurseAuth(const char *Cmd) { CLIParserFree(ctx); return PM3_EINVARG; } - + uint8_t key[] = {0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73}; if (hdatalen) memcpy(key, hdata, CIPURSE_AES_KEY_LENGTH); @@ -150,10 +151,10 @@ static int CmdHFCipurseAuth(const char *Cmd) { CipurseCGetKVV(key, kvv); if (verbose) { PrintAndLogEx(INFO, "Key id" _YELLOW_("%d") " key " _YELLOW_("%s") " KVV " _YELLOW_("%s") - , keyId - , sprint_hex(key, CIPURSE_AES_KEY_LENGTH) - , sprint_hex_inrow(kvv, CIPURSE_KVV_LENGTH) - ); + , keyId + , sprint_hex(key, CIPURSE_AES_KEY_LENGTH) + , sprint_hex_inrow(kvv, CIPURSE_KVV_LENGTH) + ); } bool bres = CIPURSEChannelAuthenticate(keyId, key, verbose); @@ -172,7 +173,9 @@ static int CmdHFCipurseAuth(const char *Cmd) { static int CLIParseKeyAndSecurityLevels(CLIParserContext *ctx, size_t keyid, size_t sreqid, size_t srespid, uint8_t *key, CipurseChannelSecurityLevel *sreq, CipurseChannelSecurityLevel *sresp) { uint8_t hdata[250] = {0}; int hdatalen = sizeof(hdata); - CLIGetHexWithReturn(ctx, keyid, hdata, &hdatalen); + if (CLIParamHexToBuf(arg_get_str(ctx, keyid), hdata, hdatalen, &hdatalen)) + return PM3_ESOFT; + if (hdatalen && hdatalen != 16) { PrintAndLogEx(ERR, _RED_("ERROR:") " key length for AES128 must be 16 bytes only"); return PM3_EINVARG; @@ -186,7 +189,9 @@ static int CLIParseKeyAndSecurityLevels(CLIParserContext *ctx, size_t keyid, siz char cdata[250] = {0}; int cdatalen = sizeof(cdata); cdatalen--; // for trailer 0x00 - CLIGetStrWithReturn(ctx, sreqid, (uint8_t *)cdata, &cdatalen); + if (CLIParamStrToBuf(arg_get_str(ctx, sreqid), (uint8_t *)cdata, cdatalen, &cdatalen)) + return PM3_ESOFT; + if (cdatalen) { str_lower(cdata); if (strcmp(cdata, "plain") == 0) @@ -204,7 +209,9 @@ static int CLIParseKeyAndSecurityLevels(CLIParserContext *ctx, size_t keyid, siz cdatalen = sizeof(cdata); memset(cdata, 0, cdatalen); cdatalen--; // for trailer 0x00 - CLIGetStrWithReturn(ctx, srespid, (uint8_t *)cdata, &cdatalen); + if (CLIParamStrToBuf(arg_get_str(ctx, srespid), (uint8_t *)cdata, cdatalen, &cdatalen)) + return PM3_ESOFT; + if (cdatalen) { str_lower(cdata); if (strcmp(cdata, "plain") == 0) @@ -226,7 +233,7 @@ static int CmdHFCipurseReadFile(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf cipurse read", "Read file by file ID with key ID and key. If no key is supplied, default key of 737373...7373 will be used", - "hf cipurse read -f 2ff7 -> Authenticate with keyID 1, read file with id 2ff7\n" + "hf cipurse read --fid 2ff7 -> Authenticate with keyID 1, read file with id 2ff7\n" "hf cipurse read -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> Authenticate keyID 2 and read file\n"); void *argtable[] = { @@ -235,7 +242,7 @@ static int CmdHFCipurseReadFile(const char *Cmd) { arg_lit0("v", "verbose", "show technical data"), arg_int0("n", NULL, "", "key ID"), arg_str0("k", "key", "", "Auth key"), - arg_str0(NULL, "fid", "", "file ID"), + arg_str0(NULL, "fid", "", "file ID"), arg_int0("o", "offset", "", "offset for reading data from file"), arg_lit0(NULL, "noauth", "read file without authentication"), arg_str0(NULL, "sreq", "", "communication reader-PICC security level"), @@ -338,7 +345,7 @@ static int CmdHFCipurseWriteFile(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf cipurse write", "Write file by file ID with key ID and key. If no key is supplied, default key of 737373...7373 will be used", - "hf cipurse write -f 2ff7 -> Authenticate with keyID 1, write file with id 2ff7\n" + "hf cipurse write --fid 2ff7 -> Authenticate with keyID 1, write file with id 2ff7\n" "hf cipurse write -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> Authenticate keyID 2 and write file\n"); void *argtable[] = { @@ -347,7 +354,7 @@ static int CmdHFCipurseWriteFile(const char *Cmd) { arg_lit0("v", "verbose", "show technical data"), arg_int0("n", NULL, "", "key ID"), arg_str0("k", "key", "", "Auth key"), - arg_str0(NULL, "fid", "", "file ID"), + arg_str0(NULL, "fid", "", "file ID"), arg_int0("o", "offset", "", "offset for reading data from file"), arg_lit0(NULL, "noauth", "read file without authentication"), arg_str0(NULL, "sreq", "", "communication reader-PICC security level"), @@ -413,11 +420,11 @@ static int CmdHFCipurseWriteFile(const char *Cmd) { if (verbose) { PrintAndLogEx(INFO, "File id " _YELLOW_("%x") " offset " _YELLOW_("%zu") " key id " _YELLOW_("%d") " key " _YELLOW_("%s") - , fileId - , offset - , keyId - , sprint_hex(key, CIPURSE_AES_KEY_LENGTH) - ); + , fileId + , offset + , keyId + , sprint_hex(key, CIPURSE_AES_KEY_LENGTH) + ); PrintAndLogEx(INFO, "data[%d]: %s", hdatalen, sprint_hex(hdata, hdatalen)); } @@ -463,7 +470,7 @@ static int CmdHFCipurseReadFileAttr(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf cipurse aread", "Read file attributes by file ID with key ID and key. If no key is supplied, default key of 737373...7373 will be used", - "hf cipurse aread -f 2ff7 -> Authenticate with keyID 1, read file attributes with id 2ff7\n" + "hf cipurse aread --fid 2ff7 -> Authenticate with keyID 1, read file attributes with id 2ff7\n" "hf cipurse aread -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> Authenticate keyID 2, read file attributes\n"); void *argtable[] = { @@ -529,10 +536,10 @@ static int CmdHFCipurseReadFileAttr(const char *Cmd) { if (verbose) { PrintAndLogEx(INFO, "File id " _YELLOW_("%x") " key id " _YELLOW_("%d") " key " _YELLOW_("%s") - , fileId - , keyId - , sprint_hex(key, CIPURSE_AES_KEY_LENGTH) - ); + , fileId + , keyId + , sprint_hex(key, CIPURSE_AES_KEY_LENGTH) + ); } if (noAuth == false) { @@ -592,7 +599,7 @@ static int CmdHFCipurseDeleteFile(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf cipurse delete", "Read file by file ID with key ID and key. If no key is supplied, default key of 737373...7373 will be used", - "hf cipurse delete -f 2ff7 -> Authenticate with keyID 1, delete file with id 2ff7\n" + "hf cipurse delete --fid 2ff7 -> Authenticate with keyID 1, delete file with id 2ff7\n" "hf cipurse delete -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> Authenticate keyID 2 and delete file\n"); void *argtable[] = { @@ -651,10 +658,10 @@ static int CmdHFCipurseDeleteFile(const char *Cmd) { if (verbose) { PrintAndLogEx(INFO, "File id " _YELLOW_("%x") " key id " _YELLOW_("%d") " key " _YELLOW_("%s") - , fileId - , keyId - , sprint_hex(key, CIPURSE_AES_KEY_LENGTH) - ); + , fileId + , keyId + , sprint_hex(key, CIPURSE_AES_KEY_LENGTH) + ); } bool bres = CIPURSEChannelAuthenticate(keyId, key, verbose); @@ -682,7 +689,6 @@ static int CmdHFCipurseDeleteFile(const char *Cmd) { return PM3_SUCCESS; } - bool CheckCardCipurse(void) { uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; @@ -692,6 +698,11 @@ bool CheckCardCipurse(void) { return (res == 0 && sw == 0x9000); } +static int CmdHFCipurseTest(const char *Cmd) { + CIPURSETest(true); + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help."}, {"info", CmdHFCipurseInfo, IfPm3Iso14443a, "Get info about CIPURSE tag"}, @@ -700,6 +711,7 @@ static command_t CommandTable[] = { {"write", CmdHFCipurseWriteFile, IfPm3Iso14443a, "Write binary file"}, {"aread", CmdHFCipurseReadFileAttr, IfPm3Iso14443a, "Read file attributes"}, {"delete", CmdHFCipurseDeleteFile, IfPm3Iso14443a, "Delete file"}, + {"test", CmdHFCipurseTest, AlwaysAvailable, "Tests"}, {NULL, NULL, 0, NULL} }; diff --git a/client/src/cmdhfcryptorf.c b/client/src/cmdhfcryptorf.c index bbfb00c0e..412fc4421 100644 --- a/client/src/cmdhfcryptorf.c +++ b/client/src/cmdhfcryptorf.c @@ -260,7 +260,7 @@ static int CmdHFCryptoRFDump(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "filename to save dump to"), + arg_str0("f", "file", "", "filename to save dump to"), arg_lit0(NULL, "64", "64byte / 512bit memory"), arg_lit0(NULL, "512", "512byte / 4096bit memory"), arg_param_end @@ -486,7 +486,7 @@ static int CmdHFCryptoRFESave(const char *Cmd) { ); void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "filename of dumpfile"), + arg_str0("f", "file", "", "filename of dumpfile"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 08c4e9360..9d95834ca 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -37,6 +37,9 @@ #define ICLASS_AUTH_RETRY 10 #define ICLASS_DECRYPTION_BIN "iclass_decryptionkey.bin" +static void print_picopass_info(const picopass_hdr_t *hdr); +void print_picopass_header(const picopass_hdr_t *hdr); + static picopass_hdr_t iclass_last_known_card; static void iclass_set_last_known_card(picopass_hdr_t *card) { memcpy(&iclass_last_known_card, card, sizeof(picopass_hdr_t)); @@ -129,7 +132,7 @@ static void iclass_upload_emul(uint8_t *d, uint16_t n, uint16_t *bytes_sent) { uint16_t bytes_remaining = n; while (bytes_remaining > 0) { - uint32_t bytes_in_packet = MIN(PM3_CMD_DATA_SIZE, bytes_remaining); + uint32_t bytes_in_packet = MIN(PM3_CMD_DATA_SIZE - 4, bytes_remaining); if (bytes_in_packet == bytes_remaining) { // Disable fast mode on last packet conn.block_after_ACK = false; @@ -244,8 +247,8 @@ static void print_config_cards(void) { static void print_config_card(const iclass_config_card_item_t *o) { if (check_config_card(o)) { - PrintAndLogEx(INFO, "description... %s", o->desc); - PrintAndLogEx(INFO, "data....... " _YELLOW_("%s"), sprint_hex_inrow(o->data, sizeof(o->data))); + PrintAndLogEx(INFO, "description... " _YELLOW_("%s"), o->desc); + PrintAndLogEx(INFO, "data.......... " _YELLOW_("%s"), sprint_hex_inrow(o->data, sizeof(o->data))); } } @@ -253,20 +256,37 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke if (check_config_card(o) == false) { return PM3_EINVARG; } + + // generated config card header + picopass_hdr_t configcard; + memset(&configcard, 0xFF, sizeof(picopass_hdr_t)); + memcpy(configcard.csn, "\x41\x87\x66\x00\xFB\xFF\x12\xE0", 8); + memcpy(&configcard.conf, "\xFF\xFF\xFF\xFF\xF9\xFF\xFF\xBC", 8); + memcpy(&configcard.epurse, "\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8); + // defaulting to known AA1 key + HFiClassCalcDivKey(configcard.csn, iClass_Key_Table[0], configcard.key_d, false); + + // reference + picopass_hdr_t *cc = &configcard; + // get header from card - //bool have = memcmp(iclass_last_known_card.csn, "\x00\x00\x00\x00\x00\x00\x00\x00", 8); PrintAndLogEx(INFO, "trying to read a card.."); int res = read_iclass_csn(false, false); - if (res != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "Put a card on antenna and try again..."); - return res; + if (res == PM3_SUCCESS) { + cc = &iclass_last_known_card; + // calc diversified key for selected card + HFiClassCalcDivKey(cc->csn, iClass_Key_Table[0], cc->key_d, false); + } else { + PrintAndLogEx(INFO, "failed to read a card, will use default config card data"); } // generate dump file - uint8_t app1_limit = iclass_last_known_card.conf.app_limit; + uint8_t app1_limit = cc->conf.app_limit; uint8_t old_limit = app1_limit; - uint8_t tot_bytes = (app1_limit + 1) * 8; + uint16_t tot_bytes = (app1_limit + 1) * 8; + PrintAndLogEx(INFO, " APP1 limit: %u", app1_limit); + PrintAndLogEx(INFO, "total bytes: %u", tot_bytes); // normal size uint8_t *data = calloc(1, tot_bytes); if (data == NULL) { @@ -274,12 +294,9 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke return PM3_EMALLOC; } + memcpy(data, cc, sizeof(picopass_hdr_t)); - // calc diversified key for selected card - HFiClassCalcDivKey(iclass_last_known_card.csn, iClass_Key_Table[0], iclass_last_known_card.key_d, false); - - memset(data, 0x00, tot_bytes); - memcpy(data, (uint8_t *)&iclass_last_known_card, sizeof(picopass_hdr_t)); + print_picopass_header(cc); // Keyrolling configuration cards are special. if (strstr(o->desc, "Keyroll") != NULL) { @@ -295,7 +312,7 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke PrintAndLogEx(WARNING, "Adapting applimit1 for KEY rolling.."); app1_limit = 0x16; - iclass_last_known_card.conf.app_limit = 0x16; + cc->conf.app_limit = 0x16; tot_bytes = (app1_limit + 1) * 8; uint8_t *p = realloc(data, tot_bytes); @@ -308,25 +325,22 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke memset(data, 0xFF, tot_bytes); } - // need to encrypt - PrintAndLogEx(INFO, "Detecting cardhelper..."); - if (IsCardHelperPresent(false) == false) { - PrintAndLogEx(FAILED, "failed to detect cardhelper"); - free(data); - return PM3_ENODATA; - } - + // KEYROLL need to encrypt uint8_t ffs[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; if (Encrypt(ffs, ffs) == false) { PrintAndLogEx(WARNING, "failed to encrypt FF"); } + // local key copy + uint8_t lkey[8]; + memcpy(lkey, key, sizeof(lkey)); + uint8_t enckey1[8]; - if (Encrypt(key, enckey1) == false) { + if (Encrypt(lkey, enckey1) == false) { PrintAndLogEx(WARNING, "failed to encrypt key1"); } - memcpy(data, &iclass_last_known_card, sizeof(picopass_hdr_t)); + memcpy(data, cc, sizeof(picopass_hdr_t)); memcpy(data + (6 * 8), o->data, sizeof(o->data)); // encrypted keyroll key 0D @@ -338,7 +352,7 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke // encrypted partial keyroll key 14 uint8_t foo[8] = {0x15}; - memcpy(foo + 1, key, 7); + memcpy(foo + 1, lkey, 7); uint8_t enckey2[8]; if (Encrypt(foo, enckey2) == false) { PrintAndLogEx(WARNING, "failed to encrypt partial 1"); @@ -347,7 +361,7 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke // encrypted partial keyroll key 15 memset(foo, 0xFF, sizeof(foo)); - foo[0] = key[7]; + foo[0] = lkey[7]; if (Encrypt(foo, enckey2) == false) { PrintAndLogEx(WARNING, "failed to encrypt partial 2"); } @@ -358,12 +372,11 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke memcpy(data + (i * 8), ffs, sizeof(ffs)); } - // revert potential modified app1_limit - iclass_last_known_card.conf.app_limit = old_limit; + cc->conf.app_limit = old_limit; } else { - memcpy(data, &iclass_last_known_card, sizeof(picopass_hdr_t)); + memcpy(data, cc, sizeof(picopass_hdr_t)); memcpy(data + (6 * 8), o->data, sizeof(o->data)); } @@ -532,13 +545,13 @@ static void mem_app_config(const picopass_hdr_t *hdr) { } } -static void print_picopass_info(const picopass_hdr_t *hdr) { +void print_picopass_info(const picopass_hdr_t *hdr) { PrintAndLogEx(INFO, "-------------------- " _CYAN_("card configuration") " --------------------"); fuse_config(hdr); mem_app_config(hdr); } -static void print_picopass_header(const picopass_hdr_t *hdr) { +void print_picopass_header(const picopass_hdr_t *hdr) { PrintAndLogEx(INFO, "--------------------------- " _CYAN_("card") " ---------------------------"); PrintAndLogEx(SUCCESS, " CSN: " _GREEN_("%s") " uid", sprint_hex(hdr->csn, sizeof(hdr->csn))); PrintAndLogEx(SUCCESS, " Config: %s Card configuration", sprint_hex((uint8_t *)&hdr->conf, sizeof(hdr->conf))); @@ -3859,6 +3872,8 @@ int CmdHFiClass(const char *Cmd) { // SR | 6,7,8,9, | AA1, Access control payload | 2 // | 10,11,12,13,14,15,16 | AA1, Secure identity object (SIO) | // SEOS | | | +// MFC SIO| | | +// DESFIRE| | | //} int info_iclass(void) { diff --git a/client/src/cmdhflegic.c b/client/src/cmdhflegic.c index c1ada36a3..3e079cbb4 100644 --- a/client/src/cmdhflegic.c +++ b/client/src/cmdhflegic.c @@ -762,7 +762,7 @@ static int CmdLegicDump(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "specify a filename for dump file"), + arg_str0("f", "file", "", "specify a filename for dump file"), arg_lit0(NULL, "de", "deobfuscate dump data (xor with MCC)"), arg_param_end }; @@ -1032,7 +1032,7 @@ static int CmdLegicESave(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "Specify a filename to save"), + arg_str0("f", "file", "", "Specify a filename to save"), arg_int0("t", "type", "", "Tag type"), arg_lit0(NULL, "deobfuscate", "De-obfuscate dump data (xor with MCC)"), arg_param_end diff --git a/client/src/cmdhflto.c b/client/src/cmdhflto.c index 2ea818b0b..727da2548 100644 --- a/client/src/cmdhflto.c +++ b/client/src/cmdhflto.c @@ -650,7 +650,7 @@ static int CmdHfLTODump(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "specify a filename for dumpfile"), + arg_str0("f", "file", "", "specify a filename for dumpfile"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index b08de964c..ace8d2d0b 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -2885,7 +2885,7 @@ static int CmdHF14AMfChk(const char *Cmd) { arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), arg_lit0(NULL, "emu", "Fill simulator keys from found keys"), arg_lit0(NULL, "dump", "Dump found keys to binary file"), - arg_str0("f", "file", "", "filename of dictionary"), + arg_str0("f", "file", "", "filename of dictionary"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -5031,7 +5031,7 @@ static int CmdHF14AMfice(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "filename of nonce dump"), + arg_str0("f", "file", "", "filename of nonce dump"), arg_u64_0(NULL, "limit", "", "nonces to be collected"), arg_param_end }; @@ -6007,6 +6007,110 @@ static int CmdHF14AMfView(const char *Cmd) { return PM3_SUCCESS; } +static int CmdHF14AGen3View(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf gview", + "View `magic gen3 gtu` card memory", + "hf mf gview\n" + "hf mf gview --4k" + ); + void *argtable[] = { + arg_param_begin, + arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), + arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"), + arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), + arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool m0 = arg_get_lit(ctx, 1); + bool m1 = arg_get_lit(ctx, 2); + bool m2 = arg_get_lit(ctx, 3); + bool m4 = arg_get_lit(ctx, 4); + CLIParserFree(ctx); + + // validations + if ((m0 + m1 + m2 + m4) > 1) { + PrintAndLogEx(WARNING, "Only specify one MIFARE Type"); + return PM3_EINVARG; + } else if ((m0 + m1 + m2 + m4) == 0) { + m1 = true; + } + + char s[6]; + memset(s, 0, sizeof(s)); + uint16_t block_cnt = MIFARE_1K_MAXBLOCK; + if (m0) { + block_cnt = MIFARE_MINI_MAXBLOCK; + strncpy(s, "Mini", 5); + } else if (m1) { + block_cnt = MIFARE_1K_MAXBLOCK; + strncpy(s, "1K", 3); + } else if (m2) { + block_cnt = MIFARE_2K_MAXBLOCK; + strncpy(s, "2K", 3); + } else if (m4) { + block_cnt = MIFARE_4K_MAXBLOCK; + strncpy(s, "4K", 3); + } else { + PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); + return PM3_EINVARG; + } + PrintAndLogEx(SUCCESS, "View magic gen3 GTU MIFARE Classic " _GREEN_("%s"), s); + PrintAndLogEx(INFO, "." NOLF); + + // Select card to get UID/UIDLEN information + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { + PrintAndLogEx(WARNING, "iso14443a card select timeout"); + return PM3_ETIMEOUT; + } + + /* + 0: couldn't read + 1: OK, with ATS + 2: OK, no ATS + 3: proprietary Anticollision + */ + uint64_t select_status = resp.oldarg[0]; + + if (select_status == 0) { + PrintAndLogEx(WARNING, "iso14443a card select failed"); + return select_status; + } + + iso14a_card_select_t card; + memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); + + // reserve memory + uint16_t bytes = block_cnt * MFBLOCK_SIZE; + uint8_t *dump = calloc(bytes, sizeof(uint8_t)); + if (dump == NULL) { + PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + return PM3_EMALLOC; + } + + for (uint16_t i = 0; i < block_cnt; i++) { + + if (mfG3GetBlock(i, dump + (i * MFBLOCK_SIZE)) != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Can't get magic card block: %u", i); + PrintAndLogEx(HINT, "Verify your card size, and try again or try another tag position"); + free(dump); + return PM3_ESOFT; + } + PrintAndLogEx(NORMAL, "." NOLF); + fflush(stdout); + } + + PrintAndLogEx(NORMAL, ""); + mf_print_blocks(block_cnt, dump); + free(dump); + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdHF14AMfList, AlwaysAvailable, "List MIFARE history"}, @@ -6059,7 +6163,8 @@ static command_t CommandTable[] = { {"gen3uid", CmdHf14AGen3UID, IfPm3Iso14443a, "Set UID without changing manufacturer block"}, {"gen3blk", CmdHf14AGen3Block, IfPm3Iso14443a, "Overwrite manufacturer block"}, {"gen3freeze", CmdHf14AGen3Freeze, IfPm3Iso14443a, "Perma lock UID changes. irreversible"}, - + {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("magic gen3 GTU") " -----------------------"}, + {"gview", CmdHF14AGen3View, IfPm3Iso14443a, "View card"}, // {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("i") " -----------------------"}, // {"ice", CmdHF14AMfice, IfPm3Iso14443a, "collect MIFARE Classic nonces to file"}, {NULL, NULL, NULL, NULL} diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index 36310e680..2608a29eb 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -984,7 +984,7 @@ static int handler_desfire_auth(mfdes_authinput_t *payload, mfdes_auth_res_t *rp // Part 4 // tag->session_key = &default_key; - struct desfire_key *p = realloc (tag->session_key,sizeof(struct desfire_key)); + struct desfire_key *p = realloc(tag->session_key, sizeof(struct desfire_key)); if (!p) { PrintAndLogEx(FAILED, "Cannot allocate memory for session keys"); free(tag->session_key); @@ -992,7 +992,7 @@ static int handler_desfire_auth(mfdes_authinput_t *payload, mfdes_auth_res_t *rp } tag->session_key = p; - memset (tag->session_key, 0x00, sizeof(struct desfire_key)); + memset(tag->session_key, 0x00, sizeof(struct desfire_key)); Desfire_session_key_new(RndA, RndB, key, tag->session_key); @@ -1030,7 +1030,7 @@ static int handler_desfire_auth(mfdes_authinput_t *payload, mfdes_auth_res_t *rp // If the 3Des key first 8 bytes = 2nd 8 Bytes then we are really using Singe Des // As such we need to set the session key such that the 2nd 8 bytes = 1st 8 Bytes if (payload->algo == MFDES_ALGO_3DES) { - if (memcmp(key->data,&key->data[8],8) == 0) + if (memcmp(key->data, &key->data[8], 8) == 0) memcpy(&tag->session_key->data[8], tag->session_key->data, 8); } @@ -1285,7 +1285,7 @@ static int mifare_desfire_change_key(uint8_t key_no, uint8_t *new_key, uint8_t n } else if (new_algo == MFDES_ALGO_3K3DES) { // 3K3Des checksum must cover : C4 csPkt[0] = MFDES_CHANGE_KEY; - memcpy (&csPkt[1], data, 25); + memcpy(&csPkt[1], data, 25); desfire_crc32(csPkt, 26, data + 1 + cmdcnt); } else { desfire_crc32_append(data + 1, cmdcnt); @@ -1309,10 +1309,10 @@ static int mifare_desfire_change_key(uint8_t key_no, uint8_t *new_key, uint8_t n csPkt[0] = MFDES_CHANGE_KEY; memcpy(&csPkt[1], data, 18); desfire_crc32(csPkt, 19, data + 1 + cmdcnt); - } else if (new_algo == MFDES_ALGO_3K3DES) { + } else if (new_algo == MFDES_ALGO_3K3DES) { // 3K3Des checksum must cover : C4 csPkt[0] = MFDES_CHANGE_KEY; - memcpy (&csPkt[1], data, 25); + memcpy(&csPkt[1], data, 25); desfire_crc32(csPkt, 26, data + 1 + cmdcnt); } else { desfire_crc32_append(data + 1, cmdcnt); @@ -1348,13 +1348,12 @@ static int mifare_desfire_change_key(uint8_t key_no, uint8_t *new_key, uint8_t n size_t sn = recv_len; - if ((new_algo == MFDES_ALGO_AES) || (new_algo == MFDES_ALGO_3K3DES)) - { + if ((new_algo == MFDES_ALGO_AES) || (new_algo == MFDES_ALGO_3K3DES)) { // AES expects us to Calculate CMAC for status byte : OK 0x00 (0x91 00) // As such if we get this far without an error, we should be good // Since we are dropping the field, we dont need to maintain the CMAC etc. // Setting sn = 1 will allow the post process to just exit (as status only) - + // Simular 3K3Des has some work to validate, but as long as the reply code was 00 // e.g. 02 fe ec 77 ca 13 e0 c2 06 [91 00 (OK)] 69 67 diff --git a/client/src/cmdhw.c b/client/src/cmdhw.c index eed42dff5..d475ba76e 100644 --- a/client/src/cmdhw.c +++ b/client/src/cmdhw.c @@ -539,7 +539,7 @@ static int CmdSetMux(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hw setmux", "Set the ADC mux to a specific value", - "hw setmux --hiraw -> set HIGH RAW" + "hw setmux --hipkd -> set HIGH PEAK\n" ); void *argtable[] = { @@ -562,6 +562,13 @@ static int CmdSetMux(const char *Cmd) { return PM3_EINVARG; } +#ifdef WITH_FPC_USART + if (loraw || hiraw) { + PrintAndLogEx(INFO, "this ADC mux option is unavailable on RDV4 compiled with FPC USART"); + return PM3_EINVARG; + } +#endif + uint8_t arg = 0; if (lopkd) arg = 0; diff --git a/client/src/cmdlfem4x05.c b/client/src/cmdlfem4x05.c index 1a2fc8255..c9a25ac2d 100644 --- a/client/src/cmdlfem4x05.c +++ b/client/src/cmdlfem4x05.c @@ -1319,13 +1319,13 @@ int CmdEM4x05Chk(const char *Cmd) { CLIParserInit(&ctx, "lf em 4x05 chk", "This command uses a dictionary attack against EM4205/4305/4469/4569", "lf em 4x05 chk\n" - "lf em 4x05 chk -e 000022B8 -> remember to use 0x for hex\n" + "lf em 4x05 chk -e 000022B8 -> check password 000022B8\n" "lf em 4x05 chk -f t55xx_default_pwds -> use T55xx default dictionary" ); void *argtable[] = { arg_param_begin, - arg_strx0("f", "file", "<*.dic>", "loads a default keys dictionary file <*.dic>"), + arg_str0("f", "file", "", "loads a default keys dictionary file <*.dic>"), arg_str0("e", "em", "", "try the calculated password from some cloners based on EM4100 ID"), arg_param_end }; @@ -1334,7 +1334,18 @@ int CmdEM4x05Chk(const char *Cmd) { char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); - uint64_t card_id = arg_get_u64_hexstr_def(ctx, 2, 0); + uint64_t card_id = 0; + int res = arg_get_u64_hexstr_def_nlen(ctx, 2, 0, &card_id, 5, true); + if (res == 2) { + CLIParserFree(ctx); + PrintAndLogEx(WARNING, "EM4100 ID must be 5 hex bytes"); + return PM3_EINVARG; + } + if (res == 0) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + CLIParserFree(ctx); if (strlen(filename) == 0) { @@ -1366,7 +1377,7 @@ int CmdEM4x05Chk(const char *Cmd) { uint32_t keycount = 0; - int res = loadFileDICTIONARY_safe(filename, (void **) &keyBlock, 4, &keycount); + res = loadFileDICTIONARY_safe(filename, (void **) &keyBlock, 4, &keycount); if (res != PM3_SUCCESS || keycount == 0 || keyBlock == NULL) { PrintAndLogEx(WARNING, "no keys found in file"); if (keyBlock != NULL) @@ -1418,22 +1429,30 @@ int CmdEM4x05Chk(const char *Cmd) { int CmdEM4x05Brute(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf em 4x05 brute", - "This command tries to bruteforce the password of a EM4205/4305/4469/4569\n", + "This command tries to bruteforce the password of a EM4205/4305/4469/4569\n" + "The loop is running on device side, press Proxmark3 button to abort\n", "Note: if you get many false positives, change position on the antenna" "lf em 4x05 brute\n" - "lf em 4x05 brute -n 1 -> stop after first candidate found\n" - "lf em 4x05 brute -s 000022B8 -> remember to use 0x for hex" + "lf em 4x05 brute -n 1 -> stop after first candidate found\n" + "lf em 4x05 brute -s 000022AA -> start at 000022AA" ); void *argtable[] = { arg_param_begin, - arg_u64_0("s", "start", "", "Start bruteforce enumeration from this password value"), - arg_int0("n", NULL, "", "Stop after having found n candidates. Default: 0 => infinite"), + arg_str0("s", "start", "", "Start bruteforce enumeration from this password value"), + arg_u64_0("n", NULL, "", "Stop after having found n candidates. Default: 0 (infinite)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - uint32_t start_pwd = arg_get_u64_hexstr_def(ctx, 1, 0); - uint32_t n = arg_get_int_def(ctx, 2, 0); + uint32_t start_pwd = 0; + int res = arg_get_u32_hexstr_def(ctx, 1, 0, &start_pwd); + if (res != 1) { + CLIParserFree(ctx); + PrintAndLogEx(WARNING, "check `start_pwd` parameter"); + return PM3_EINVARG; + } + + uint32_t n = arg_get_u32_def(ctx, 2, 0); CLIParserFree(ctx); PrintAndLogEx(NORMAL, ""); @@ -1449,7 +1468,7 @@ int CmdEM4x05Brute(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_LF_EM4X_BF, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_LF_EM4X_BF, &resp, 1000)) { + if (WaitForResponseTimeout(CMD_LF_EM4X_BF, &resp, 1000) == false) { PrintAndLogEx(WARNING, "(EM4x05 Bruteforce) timeout while waiting for reply."); return PM3_ETIMEOUT; } @@ -1857,7 +1876,7 @@ int CmdEM4x05Unlock(const char *Cmd) { // compute number of bits flipped PrintAndLogEx(INFO, "Bitflips: %2u events => %s", bitcount32(bitflips), bitstring); PrintAndLogEx(INFO, "New protection word => " _CYAN_("%08X") "\n", word14b); - PrintAndLogEx(INFO, "Try " _YELLOW_("`lf em 4x05_dump`")); + PrintAndLogEx(INFO, "Try " _YELLOW_("`lf em 4x05 dump`")); } if (verbose) { diff --git a/client/src/cmdlfem4x50.c b/client/src/cmdlfem4x50.c index 89ee9efb0..7ecf281bc 100644 --- a/client/src/cmdlfem4x50.c +++ b/client/src/cmdlfem4x50.c @@ -14,6 +14,7 @@ #include "cmdlfem4x50.h" #include #include "cmdparser.h" // command_t +#include "util_posix.h" // msclock #include "fileutils.h" #include "commonutil.h" #include "pmflash.h" @@ -100,8 +101,6 @@ static void print_info_result(uint8_t *data, bool verbose) { PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); // data section - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, _YELLOW_("EM4x50 data:")); if (verbose) { print_result(words, 0, EM4X50_NO_WORDS - 1); } else { @@ -112,16 +111,16 @@ static void print_info_result(uint8_t *data, bool verbose) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "---- " _CYAN_("Configuration") " ----"); - PrintAndLogEx(INFO, "first word read %3i", fwr); - PrintAndLogEx(INFO, "last word read %3i", lwr); - PrintAndLogEx(INFO, "password check %3s", (bpwc) ? _RED_("on") : _GREEN_("off")); - PrintAndLogEx(INFO, "read after write %3s", (braw) ? "on" : "off"); + PrintAndLogEx(INFO, "first word read.... " _YELLOW_("%i"), fwr); + PrintAndLogEx(INFO, "last word read..... " _YELLOW_("%i"), lwr); + PrintAndLogEx(INFO, "password check..... %s", (bpwc) ? _RED_("on") : _GREEN_("off")); + PrintAndLogEx(INFO, "read after write... %s", (braw) ? "on" : "off"); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--------- " _CYAN_("Protection") " ---------"); - PrintAndLogEx(INFO, "first word read protected %3i", fwrp); - PrintAndLogEx(INFO, "last word read protected %3i", lwrp); - PrintAndLogEx(INFO, "first word write inhibited %3i", fwwi); - PrintAndLogEx(INFO, "last word write inhibited %3i", lwwi); + PrintAndLogEx(INFO, "--------- " _CYAN_("Protection") " ------------"); + PrintAndLogEx(INFO, "first word read protected.... %i", fwrp); + PrintAndLogEx(INFO, "last word read protected..... %i", lwrp); + PrintAndLogEx(INFO, "first word write inhibited... %i", fwwi); + PrintAndLogEx(INFO, "last word write inhibited.... %i", lwwi); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "zero values may indicate read protection"); PrintAndLogEx(NORMAL, ""); @@ -238,7 +237,7 @@ int CmdEM4x50ESave(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "filename", "", "data filename"), + arg_str0("f", "file", "", "save filename"), arg_param_end }; @@ -364,9 +363,9 @@ int CmdEM4x50Login(const char *Cmd) { int CmdEM4x50Brute(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf em 4x50 brute", - "Tries to bruteforce the password of a EM4x50.\n" + "Tries to bruteforce the password of a EM4x50 card.\n" "Function can be stopped by pressing pm3 button.", - "lf em 4x50 brute --first 12330000 --last 12340000 -> tries pwds from 0x12330000 to 0x1234000000\n" + "lf em 4x50 brute --first 12330000 --last 12340000 -> tries pwds from 0x12330000 to 0x1234000000\n" ); void *argtable[] = { @@ -408,12 +407,12 @@ int CmdEM4x50Brute(const char *Cmd) { int dur_m = (dur_s - dur_h * 3600) / 60; dur_s -= dur_h * 3600 + dur_m * 60; - PrintAndLogEx(INFO, "Trying %i passwords in range [0x%08x, 0x%08x]" + PrintAndLogEx(INFO, "Trying " _YELLOW_("%i") " passwords in range [0x%08x, 0x%08x]" , no_iter , etd.password1 , etd.password2 ); - PrintAndLogEx(INFO, "Estimated duration: %ih%im%is", dur_h, dur_m, dur_s); + PrintAndLogEx(INFO, "Estimated duration: %ih %im %is", dur_h, dur_m, dur_s); // start clearCommandBuffer(); @@ -423,9 +422,9 @@ int CmdEM4x50Brute(const char *Cmd) { // print response if (resp.status == PM3_SUCCESS) - PrintAndLogEx(SUCCESS, "Password " _GREEN_("found") ": 0x%08x", resp.data.asDwords[0]); + PrintAndLogEx(SUCCESS, "found valid password [ " _GREEN_("%08"PRIX32) " ]", resp.data.asDwords[0]); else - PrintAndLogEx(FAILED, "Password: " _RED_("not found")); + PrintAndLogEx(WARNING, "brute pwd failed"); return PM3_SUCCESS; } @@ -435,14 +434,14 @@ int CmdEM4x50Brute(const char *Cmd) { int CmdEM4x50Chk(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf em 4x50 chk", - "Dictionary attack against EM4x50.", + "Run dictionary key recovery against EM4x50 card.", "lf em 4x50 chk -> uses T55xx default dictionary\n" "lf em 4x50 chk -f my.dic" ); void *argtable[] = { arg_param_begin, - arg_str0("f", "filename", "", "dictionary filename"), + arg_str0("f", "file", "", "dictionary filename"), arg_param_end }; @@ -463,6 +462,8 @@ int CmdEM4x50Chk(const char *Cmd) { PrintAndLogEx(INFO, "treating file as T55xx keys"); } + uint64_t t1 = msclock(); + size_t datalen = 0; // 2021 iceman: how many keys shall we reserv space for? The t55xx dictionary has 139 keys. @@ -510,17 +511,22 @@ int CmdEM4x50Chk(const char *Cmd) { // print response if (status == PM3_SUCCESS) { + /* PrintAndLogEx(SUCCESS, "Key " _GREEN_("found: %02x %02x %02x %02x"), resp.data.asBytes[3], resp.data.asBytes[2], resp.data.asBytes[1], resp.data.asBytes[0] ); + */ + uint32_t pwd = BYTES2UINT32(resp.data.asBytes); + PrintAndLogEx(SUCCESS, "found valid password [ " _GREEN_("%08"PRIX32) " ]", pwd); } else { PrintAndLogEx(FAILED, "No key found"); } - PrintAndLogEx(INFO, "Done"); + t1 = msclock() - t1; + PrintAndLogEx(SUCCESS, "\ntime in check pwd " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); return PM3_SUCCESS; } @@ -639,8 +645,8 @@ int CmdEM4x50Info(const char *Cmd) { CLIParserInit(&ctx, "lf em 4x50 info", "Tag information EM4x50.", "lf em 4x50 info\n" - "lf em 4x50 info -v -> show data section\n" - "lf em 4x50 info -p 12345678 -> uses pwd 0x12345678\n" + "lf em 4x50 info -v -> show data section\n" + "lf em 4x50 info -p 12345678 -> uses pwd 0x12345678\n" ); void *argtable[] = { @@ -752,7 +758,7 @@ int CmdEM4x50Dump(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "filename", "", "dump filename (bin/eml/json)"), + arg_str0("f", "file", "", "dump filename (bin/eml/json)"), arg_str0("p", "pwd", "", "password, 4 hex bytes, lsb"), arg_param_end }; @@ -908,9 +914,9 @@ int CmdEM4x50Write(const char *Cmd) { // envokes changing the password of EM4x50 tag int CmdEM4x50WritePwd(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "lf em 4x50 writepwd", + CLIParserInit(&ctx, "lf em 4x50 wrpwd", "Writes EM4x50 password.", - "lf em 4x50 writepwd -p 4f22e7ff -n 12345678" + "lf em 4x50 wrpwd -p 4f22e7ff -n 12345678" ); void *argtable[] = { @@ -1063,7 +1069,7 @@ int CmdEM4x50Restore(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str0("u", "uid", "", "uid, 4 hex bytes, msb"), - arg_str0("f", "filename", "", "dump filename (bin/eml/json)"), + arg_str0("f", "file", "", "dump filename (bin/eml/json)"), arg_str0("p", "pwd", "", "password, 4 hex bytes, lsb"), arg_param_end }; @@ -1210,21 +1216,23 @@ int CmdEM4x50Sim(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"brute", CmdEM4x50Brute, IfPm3EM4x50, "guess password of EM4x50"}, - {"chk", CmdEM4x50Chk, IfPm3EM4x50, "check passwords from dictionary"}, - {"dump", CmdEM4x50Dump, IfPm3EM4x50, "dump EM4x50 tag"}, - {"info", CmdEM4x50Info, IfPm3EM4x50, "tag information EM4x50"}, - {"login", CmdEM4x50Login, IfPm3EM4x50, "login into EM4x50"}, - {"rdbl", CmdEM4x50Read, IfPm3EM4x50, "read word data from EM4x50"}, - {"wrbl", CmdEM4x50Write, IfPm3EM4x50, "write word data to EM4x50"}, - {"writepwd", CmdEM4x50WritePwd, IfPm3EM4x50, "change password of EM4x50"}, - {"wipe", CmdEM4x50Wipe, IfPm3EM4x50, "wipe EM4x50 tag"}, - {"reader", CmdEM4x50Reader, IfPm3EM4x50, "show standard read mode data of EM4x50"}, - {"restore", CmdEM4x50Restore, IfPm3EM4x50, "restore EM4x50 dump to tag"}, - {"sim", CmdEM4x50Sim, IfPm3EM4x50, "simulate EM4x50 tag"}, - {"eload", CmdEM4x50ELoad, IfPm3EM4x50, "upload dump of EM4x50 to emulator memory"}, - {"esave", CmdEM4x50ESave, IfPm3EM4x50, "save emulator memory to file"}, - {"eview", CmdEM4x50EView, IfPm3EM4x50, "view EM4x50 content in emulator memory"}, + {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("operations") " ---------------------"}, + {"brute", CmdEM4x50Brute, IfPm3EM4x50, "Simple bruteforce attack to find password"}, + {"chk", CmdEM4x50Chk, IfPm3EM4x50, "Check passwords from dictionary"}, + {"dump", CmdEM4x50Dump, IfPm3EM4x50, "Dump EM4x50 tag"}, + {"info", CmdEM4x50Info, IfPm3EM4x50, "Tag information"}, + {"login", CmdEM4x50Login, IfPm3EM4x50, "Login into EM4x50 tag"}, + {"rdbl", CmdEM4x50Read, IfPm3EM4x50, "Read EM4x50 word data"}, + {"reader", CmdEM4x50Reader, IfPm3EM4x50, "Show standard read mode data"}, + {"restore", CmdEM4x50Restore, IfPm3EM4x50, "Restore EM4x50 dump to tag"}, + {"wrbl", CmdEM4x50Write, IfPm3EM4x50, "Write EM4x50 word data"}, + {"wrpwd", CmdEM4x50WritePwd, IfPm3EM4x50, "Change EM4x50 password"}, + {"wipe", CmdEM4x50Wipe, IfPm3EM4x50, "Wipe EM4x50 tag"}, + {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("simulation") " ---------------------"}, + {"eload", CmdEM4x50ELoad, IfPm3EM4x50, "Upload EM4x50 dump to emulator memory"}, + {"esave", CmdEM4x50ESave, IfPm3EM4x50, "Save emulator memory to file"}, + {"eview", CmdEM4x50EView, IfPm3EM4x50, "View EM4x50 content in emulator memory"}, + {"sim", CmdEM4x50Sim, IfPm3EM4x50, "Simulate EM4x50 tag"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdlfhitag.c b/client/src/cmdlfhitag.c index 287f39f06..c90637b6c 100644 --- a/client/src/cmdlfhitag.c +++ b/client/src/cmdlfhitag.c @@ -278,7 +278,7 @@ static int CmdLFHitagEload(const char *Cmd) { if (dumplen == 48 || dumplen == 4 * 64) { lf_hitag_t *payload = calloc(1, sizeof(lf_hitag_t) + dumplen); - + if (use_ht1) payload->type = 1; if (use_ht2) @@ -570,8 +570,8 @@ static int CmdLFHitagReader(const char *Cmd) { } // sanity checks - if (keylen != 0 && keylen != 4) { - PrintAndLogEx(WARNING, "Wrong KEY len expected 0 or 4, got %d", keylen); + if (keylen != 0 && keylen != 4 && keylen != 6) { + PrintAndLogEx(WARNING, "Wrong KEY len expected 0, 4 or 6, got %d", keylen); return PM3_EINVARG; } @@ -657,7 +657,7 @@ static int CmdLFHitagCheckChallenges(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "filename", "", "filename to load from"), + arg_str0("f", "file", "", "filename to load ( w/o ext )"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); diff --git a/client/src/cmdlft55xx.c b/client/src/cmdlft55xx.c index e7ae82ed3..254149756 100644 --- a/client/src/cmdlft55xx.c +++ b/client/src/cmdlft55xx.c @@ -2210,7 +2210,7 @@ static int CmdT55xxDump(const char *Cmd) { // 1 (help) + 3 (two user specified params) + (5 T55XX_DLMODE_SINGLE) void *argtable[4 + 5] = { arg_param_begin, - arg_str0("f", "filename", "", "filename (default is generated on blk 0)"), + arg_str0("f", "file", "", "filename (default is generated on blk 0)"), arg_lit0("o", "override", "override, force pwd read despite danger to card"), arg_str0("p", "pwd", "", "password (4 hex bytes)"), }; diff --git a/client/src/emv/emvcore.c b/client/src/emv/emvcore.c index 7d0ab8ce4..2522aa8b5 100644 --- a/client/src/emv/emvcore.c +++ b/client/src/emv/emvcore.c @@ -758,6 +758,7 @@ int trDDA(Iso7816CommandChannel channel, bool decodeTLV, struct tlvdb *tlv) { tlvdb_free(atc_db); return 9; } + tlvdb_free(atc_db); } else { struct tlvdb *dac_db = emv_pki_recover_dac(issuer_pk, tlv, sda_tlv); diff --git a/client/src/iso7816/iso7816core.c b/client/src/iso7816/iso7816core.c index 3907d7744..4168036a4 100644 --- a/client/src/iso7816/iso7816core.c +++ b/client/src/iso7816/iso7816core.c @@ -38,10 +38,10 @@ void SetISODEPState(isodep_state_t state) { isodep_state = state; if (APDULogging) { PrintAndLogEx(SUCCESS, "Setting ISODEP -> %s%s%s" - , isodep_state == ISODEP_INACTIVE ? "inactive" : "" - , isodep_state == ISODEP_NFCA ? _GREEN_("NFC-A") : "" - , isodep_state == ISODEP_NFCB ? _GREEN_("NFC-B") : "" - ); + , isodep_state == ISODEP_INACTIVE ? "inactive" : "" + , isodep_state == ISODEP_NFCA ? _GREEN_("NFC-A") : "" + , isodep_state == ISODEP_NFCB ? _GREEN_("NFC-B") : "" + ); } } @@ -79,7 +79,7 @@ int Iso7816ExchangeEx(Iso7816CommandChannel channel, bool activate_field, bool l *result_len = 0; if (sw) { - *sw = 0; + *sw = 0; } if (activate_field) { @@ -177,30 +177,30 @@ int Iso7816ExchangeEx(Iso7816CommandChannel channel, bool activate_field, bool l int Iso7816Exchange(Iso7816CommandChannel channel, bool leave_field_on, sAPDU apdu, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) { return Iso7816ExchangeEx(channel - , false - , leave_field_on - , apdu - , false - , 0 - , result - , max_result_len - , result_len - , sw - ); + , false + , leave_field_on + , apdu + , false + , 0 + , result + , max_result_len + , result_len + , sw + ); } int Iso7816Select(Iso7816CommandChannel channel, bool activate_field, bool leave_field_on, uint8_t *aid, size_t aid_len, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) { return Iso7816ExchangeEx(channel - , activate_field - , leave_field_on - , (sAPDU) {0x00, 0xa4, 0x04, 0x00, aid_len, aid} - , (channel == CC_CONTACTLESS) - , 0 - , result - , max_result_len - , result_len - , sw - ); + , activate_field + , leave_field_on + , (sAPDU) {0x00, 0xa4, 0x04, 0x00, aid_len, aid} + , (channel == CC_CONTACTLESS) + , 0 + , result + , max_result_len + , result_len + , sw + ); } diff --git a/client/src/iso7816/iso7816core.h b/client/src/iso7816/iso7816core.h index beb5e3e90..003db1256 100644 --- a/client/src/iso7816/iso7816core.h +++ b/client/src/iso7816/iso7816core.h @@ -40,13 +40,13 @@ int Iso7816Connect(Iso7816CommandChannel channel); // exchange int Iso7816Exchange(Iso7816CommandChannel channel, bool leave_field_on, sAPDU apdu, uint8_t *result, size_t max_result_len, - size_t *result_len, uint16_t *sw); + size_t *result_len, uint16_t *sw); int Iso7816ExchangeEx(Iso7816CommandChannel channel, bool activate_field, bool leave_field_on, sAPDU apdu, bool include_le, - uint16_t le, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); + uint16_t le, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); // search application int Iso7816Select(Iso7816CommandChannel channel, bool activate_field, bool leave_field_on, uint8_t *aid, size_t aid_len, - uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); + uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); #endif diff --git a/client/src/mifare/desfire_crypto.c b/client/src/mifare/desfire_crypto.c index 24a05825f..e3e785e6c 100644 --- a/client/src/mifare/desfire_crypto.c +++ b/client/src/mifare/desfire_crypto.c @@ -68,7 +68,7 @@ void des_decrypt(void *out, const void *in, const void *key) { void tdes_nxp_receive(const void *in, void *out, size_t length, const void *key, unsigned char iv[8], int keymode) { if (length % 8) return; - + mbedtls_des3_context ctx3; if (keymode == 2) mbedtls_des3_set2key_dec(&ctx3, key); @@ -98,7 +98,7 @@ void tdes_nxp_receive(const void *in, void *out, size_t length, const void *key, } void tdes_nxp_send(const void *in, void *out, size_t length, const void *key, unsigned char iv[8], int keymode) { - if (length % 8) + if (length % 8) return; mbedtls_des3_context ctx3; diff --git a/client/src/mifare/mifarehost.c b/client/src/mifare/mifarehost.c index 9b0c20d36..cba4d5ac7 100644 --- a/client/src/mifare/mifarehost.c +++ b/client/src/mifare/mifarehost.c @@ -56,6 +56,8 @@ int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key) { return PM3_EOPABORTED; } + PrintAndLogEx(INFO, "." NOLF); + // wait cycle while (true) { PrintAndLogEx(NORMAL, "." NOLF); @@ -546,7 +548,7 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, PrintAndLogEx(SUCCESS, "\ntarget block:%3u key type: %c -- found valid key [ " _GREEN_("%s") "]", package->block, package->keytype ? 'B' : 'A', - sprint_hex(resultKey, 6) + sprint_hex_inrow(resultKey, 6) ); return PM3_SUCCESS; } @@ -725,7 +727,7 @@ int mfStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBl PrintAndLogEx(SUCCESS, "target block:%3u key type: %c -- found valid key [ " _GREEN_("%s") "]", package->block, package->keytype ? 'B' : 'A', - sprint_hex(resultKey, 6) + sprint_hex_inrow(resultKey, 6) ); return PM3_SUCCESS; } else if (res == PM3_ETIMEOUT || res == PM3_EOPABORTED) { @@ -1050,6 +1052,27 @@ int mfGen3Freeze(void) { } } +int mfG3GetBlock(uint8_t blockno, uint8_t *data) { + struct p { + uint8_t blockno; + } PACKED payload; + payload.blockno = blockno; + + clearCommandBuffer(); + SendCommandNG(CMD_HF_MIFARE_G3_RDBL, (uint8_t *)&payload, sizeof(payload)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_MIFARE_G3_RDBL, &resp, 1500)) { + if (resp.status != PM3_SUCCESS) + return PM3_EUNDEF; + memcpy(data, resp.data.asBytes, 16); + } else { + PrintAndLogEx(WARNING, "command execute timeout"); + return PM3_ETIMEOUT; + } + return PM3_SUCCESS; +} + + // variables uint32_t cuid = 0; // uid part used for crypto1. diff --git a/client/src/mifare/mifarehost.h b/client/src/mifare/mifarehost.h index 49adc8cbe..8f5f215fd 100644 --- a/client/src/mifare/mifarehost.h +++ b/client/src/mifare/mifarehost.h @@ -85,6 +85,8 @@ int mfGen3UID(uint8_t *uid, uint8_t uidlen, uint8_t *oldUid); int mfGen3Block(uint8_t *block, int blockLen, uint8_t *newBlock); int mfGen3Freeze(void); +int mfG3GetBlock(uint8_t blockno, uint8_t *data); + int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len); int detect_classic_prng(void); diff --git a/client/src/nfc/ndef.c b/client/src/nfc/ndef.c index 2bc841a23..ac131b221 100644 --- a/client/src/nfc/ndef.c +++ b/client/src/nfc/ndef.c @@ -25,6 +25,7 @@ #define NDEF_WIFIAPPL "application/vnd.wfa" #define NDEF_BLUEAPPL "application/vnd.bluetooth" #define NDEF_VCARDTEXT "text/vcard" +#define NDEF_XVCARDTEXT "text/x-vcard" static const char *TypeNameFormat_s[] = { "Empty Record", @@ -436,7 +437,7 @@ static int ndefDecodePayloadDeviceInfo(uint8_t *payload, size_t len) { n = *(p++); //uuid string // record.uuid_string = '123e4567-e89b-12d3-a456-426655440000' - // 8-4-4-4-12 + // 8-4-4-4-12 char uuid[37] = {0}; sprintf(uuid, "%s-", sprint_hex_inrow(p, 4)); p += 4; @@ -450,7 +451,7 @@ static int ndefDecodePayloadDeviceInfo(uint8_t *payload, size_t len) { p += 6; PrintAndLogEx(INFO, "UUID.......... " _YELLOW_("%s"), uuid); p++; - n = *(p++); + n = *(p++); PrintAndLogEx(INFO, "Version....... " _YELLOW_("%.*s"), n, p); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; @@ -490,7 +491,7 @@ static int ndefDecodePayloadSmartPoster(uint8_t *ndef, size_t ndeflen, bool prin } // recursive if (NDEFHeader.MessageEnd == false) { - ndefDecodePayloadSmartPoster(ndef + NDEFHeader.RecLen, ndeflen - NDEFHeader.RecLen, false, false); + ndefDecodePayloadSmartPoster(ndef + NDEFHeader.RecLen, ndeflen - NDEFHeader.RecLen, false, false); } if (print) { @@ -510,48 +511,48 @@ static int ndefDecodeMime_wifi(NDEFHeader_t *ndef) { static int ndefDecodeMime_vcard(NDEFHeader_t *ndef) { PrintAndLogEx(INFO, _CYAN_("VCARD details")); if (ndef->PayloadLen > 1) { - PrintAndLogEx(INFO, "Data... " _YELLOW_("%.*s"), (int)ndef->PayloadLen, ndef->Payload); - PrintAndLogEx(INFO, ">>> decorder, to be implemented <<<"); + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, "%.*s", (int)ndef->PayloadLen, ndef->Payload); } return PM3_SUCCESS; } static int ndefDecodeMime_bt(NDEFHeader_t *ndef) { - PrintAndLogEx(INFO, "Type............ " _YELLOW_("%.*s"), (int)ndef->TypeLen, ndef->Type ); + PrintAndLogEx(INFO, "Type............ " _YELLOW_("%.*s"), (int)ndef->TypeLen, ndef->Type); if (ndef->PayloadLen > 1) { uint16_t ooblen = (ndef->Payload[1] << 8 | ndef->Payload[0]); PrintAndLogEx(INFO, "OOB data len.... %u", ooblen); PrintAndLogEx(INFO, "BT MAC.......... " _YELLOW_("%s"), sprint_hex(ndef->Payload + 2, 6)); - // Let's check payload[8]. Tells us a bit about the UUID's. If 0x07 then it tells us a service UUID is 128bit - switch (ndef->Payload[8]) { - case 0x02: + // Let's check payload[8]. Tells us a bit about the UUID's. If 0x07 then it tells us a service UUID is 128bit + switch (ndef->Payload[8]) { + case 0x02: PrintAndLogEx(INFO, "Optional Data... incomplete list 16-bit UUID's"); - break; - case 0x03: + break; + case 0x03: PrintAndLogEx(INFO, "Optional Data... complete list 16-bit UUID's"); - break; - case 0x04: + break; + case 0x04: PrintAndLogEx(INFO, "Optional Data... incomplete list 32-bit UUID's"); - break; - case 0x05: + break; + case 0x05: PrintAndLogEx(INFO, "Optional Data... complete list 32-bit UUID's"); - break; - case 0x06: + break; + case 0x06: PrintAndLogEx(INFO, "Optional Data... incomplete list 128-bit UUID's"); - break; - case 0x07: + break; + case 0x07: PrintAndLogEx(INFO, "Optional Data... complete list 128-bit UUID's"); - break; - default: + break; + default: PrintAndLogEx(INFO, "Optional Data... [ %02x ]", ndef->Payload[8]); break; - } - // Let's check payload[9]. If 0x08 then SHORT_NAME or if 0x09 then COMPLETE_NAME - if (ndef->Payload[9] == 0x08 ) { + } + // Let's check payload[9]. If 0x08 then SHORT_NAME or if 0x09 then COMPLETE_NAME + if (ndef->Payload[9] == 0x08) { PrintAndLogEx(INFO, "Short name...... " _YELLOW_("%.*s"), (int)(ndef->PayloadLen - 10), ndef->Payload + 10); - } else if (ndef->Payload[9] == 0x09 ) { + } else if (ndef->Payload[9] == 0x09) { PrintAndLogEx(INFO, "Complete name... " _YELLOW_("%.*s"), (int)(ndef->PayloadLen - 10), ndef->Payload + 10); - } else { + } else { PrintAndLogEx(INFO, "[ %02x ]", ndef->Payload[9]); } PrintAndLogEx(NORMAL, ""); @@ -629,20 +630,29 @@ static int ndefDecodePayload(NDEFHeader_t *ndef) { PrintAndLogEx(INFO, "- decoder to be impl -"); } break; - case tnfMIMEMediaRecord: + case tnfMIMEMediaRecord: { PrintAndLogEx(INFO, "MIME Media Record"); + if (ndef->TypeLen == 0) { + PrintAndLogEx(INFO, "type length is zero"); + break; + } - if (str_startswith((const char *)ndef->Type, NDEF_WIFIAPPL)) { + char begin[ndef->TypeLen]; + memcpy(begin, ndef->Type, ndef->TypeLen); + str_lower(begin); + + if (str_startswith(begin, NDEF_WIFIAPPL)) { ndefDecodeMime_wifi(ndef); } - if (str_startswith((const char *)ndef->Type, NDEF_VCARDTEXT)) { + if (str_startswith(begin, NDEF_VCARDTEXT) || str_startswith(begin, NDEF_XVCARDTEXT)) { ndefDecodeMime_vcard(ndef); } - if (str_startswith((const char *)ndef->Type, NDEF_BLUEAPPL)) { + if (str_startswith(begin, NDEF_BLUEAPPL)) { ndefDecodeMime_bt(ndef); } break; + } case tnfAbsoluteURIRecord: PrintAndLogEx(INFO, "Absolute URI Record"); PrintAndLogEx(INFO, " payload : %.*s", (int)ndef->PayloadLen, ndef->Payload); @@ -686,7 +696,7 @@ static int ndefRecordDecodeAndPrint(uint8_t *ndefRecord, size_t ndefRecordLen) { print_buffer(NDEFHeader.Payload, NDEFHeader.PayloadLen, 1); } if (NDEFHeader.TypeLen && NDEFHeader.PayloadLen) { - ndefDecodePayload(&NDEFHeader); + ndefDecodePayload(&NDEFHeader); } return PM3_SUCCESS; diff --git a/doc/commands.json b/doc/commands.json index 04f5535af..571c53d13 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -973,7 +973,7 @@ }, "help": { "command": "help", - "description": "help use ` help` for details of a command prefs { edit client/device preferences... } -------- ----------------------- technology ----------------------- analyse { analyse utils... } data { plot window / data buffer manipulation... } emv { emv iso-14443 / iso-7816... } hf { high frequency commands... } hw { hardware commands... } lf { low frequency commands... } nfc { nfc commands... } reveng { crc calculations from reveng software... } smart { smart card iso-7816 commands... } script { scripting commands... } trace { trace manipulation... } wiegand { wiegand format manipulation... } -------- ----------------------- general ----------------------- clear clear screen hints turn hints on / off msleep add a pause in milliseconds rem add a text line in log file quit exit exit program [=] session log e:\\proxspace\\pm3/.proxmark3/logs/log_20210618.txt --------------------------------------------------------------------------------------- auto available offline: no run lf search / hf search / data plot / data save", + "description": "help use ` help` for details of a command prefs { edit client/device preferences... } -------- ----------------------- technology ----------------------- analyse { analyse utils... } data { plot window / data buffer manipulation... } emv { emv iso-14443 / iso-7816... } hf { high frequency commands... } hw { hardware commands... } lf { low frequency commands... } nfc { nfc commands... } reveng { crc calculations from reveng software... } smart { smart card iso-7816 commands... } script { scripting commands... } trace { trace manipulation... } wiegand { wiegand format manipulation... } -------- ----------------------- general ----------------------- clear clear screen hints turn hints on / off msleep add a pause in milliseconds rem add a text line in log file quit exit exit program [=] session log /home/osboxes/.proxmark3/logs/log_20210624.txt --------------------------------------------------------------------------------------- auto available offline: no run lf search / hf search / data plot / data save", "notes": [ "auto" ], @@ -1684,67 +1684,67 @@ }, "hf cipurse aread": { "command": "hf cipurse aread", - "description": "read file attributes by file id with key id and key", + "description": "read file attributes by file id with key id and key. if no key is supplied, default key of 737373...7373 will be used", "notes": [ - "hf cipurse aread -f 2ff7 -> authenticate with keyid=1 and key = 7373...7373 and read file attributes with id 2ff7", - "hf cipurse aread -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> authenticate with specified key and read file attributes" + "hf cipurse aread --fid 2ff7 -> authenticate with keyid 1, read file attributes with id 2ff7", + "hf cipurse aread -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> authenticate keyid 2, read file attributes" ], "offline": false, "options": [ "-h, --help this help", "-a, --apdu show apdu requests and responses", "-v, --verbose show technical data", - "-n, --keyid key id", - "-k, --key key for authenticate", - "-f, --file file id", + "-n key id", + "-k, --key auth key", + "--fid file id", "--noauth read file attributes without authentication", "--sreq communication reader-picc security level", "--sresp communication picc-reader security level", "--sel-adf show info about adf itself", "--sel-mf show info about master file" ], - "usage": "hf cipurse aread [-hav] [-n ] [-k ] [-f ] [--noauth] [--sreq ] [--sresp ] [--sel-adf] [--sel-mf]" + "usage": "hf cipurse aread [-hav] [-n ] [-k ] [--fid ] [--noauth] [--sreq ] [--sresp ] [--sel-adf] [--sel-mf]" }, "hf cipurse auth": { "command": "hf cipurse auth", - "description": "authenticate with key id and key", + "description": "authenticate with key id and key. if no key is supplied, default key of 737373...7373 will be used", "notes": [ - "hf cipurse auth -> authenticate with keyid=1 and key = 7373...7373", - "hf cipurse auth -n 2 -k 65656565656565656565656565656565 -> authenticate with key" + "hf cipurse auth -> authenticate with keyid 1, default key", + "hf cipurse auth -n 2 -k 65656565656565656565656565656565 -> authenticate keyid 2 with key" ], "offline": false, "options": [ "-h, --help this help", "-a, --apdu show apdu requests and responses", "-v, --verbose show technical data", - "-n, --keyid key id", - "-k, --key key for authenticate" + "-n key id", + "-k, --key auth key" ], "usage": "hf cipurse auth [-hav] [-n ] [-k ]" }, "hf cipurse delete": { "command": "hf cipurse delete", - "description": "read file by file id with key id and key", + "description": "read file by file id with key id and key. if no key is supplied, default key of 737373...7373 will be used", "notes": [ - "hf cipurse delete -f 2ff7 -> authenticate with keyid=1 and key = 7373...7373 and delete file with id 2ff7", - "hf cipurse delete -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> authenticate with specified key and delete file" + "hf cipurse delete --fid 2ff7 -> authenticate with keyid 1, delete file with id 2ff7", + "hf cipurse delete -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> authenticate keyid 2 and delete file" ], "offline": false, "options": [ "-h, --help this help", "-a, --apdu show apdu requests and responses", "-v, --verbose show technical data", - "-n, --keyid key id", - "-k, --key key for authenticate", - "-f, --file file id", + "-n key id", + "-k, --key auth key", + "--fid file id", "--sreq communication reader-picc security level", "--sresp communication picc-reader security level" ], - "usage": "hf cipurse delete [-hav] [-n ] [-k ] [-f ] [--sreq ] [--sresp ]" + "usage": "hf cipurse delete [-hav] [-n ] [-k ] [--fid ] [--sreq ] [--sresp ]" }, "hf cipurse help": { "command": "hf cipurse help", - "description": "help this help. --------------------------------------------------------------------------------------- hf cipurse info available offline: no get info from cipurse tags", + "description": "help this help. test tests --------------------------------------------------------------------------------------- hf cipurse info available offline: no get info from cipurse tags", "notes": [ "hf cipurse info" ], @@ -1756,48 +1756,63 @@ }, "hf cipurse read": { "command": "hf cipurse read", - "description": "read file by file id with key id and key", + "description": "read file by file id with key id and key. if no key is supplied, default key of 737373...7373 will be used", "notes": [ - "hf cipurse read -f 2ff7 -> authenticate with keyid=1 and key = 7373...7373 and read file with id 2ff7", - "hf cipurse read -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> authenticate with specified key and read file" + "hf cipurse read --fid 2ff7 -> authenticate with keyid 1, read file with id 2ff7", + "hf cipurse read -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> authenticate keyid 2 and read file" ], "offline": false, "options": [ "-h, --help this help", "-a, --apdu show apdu requests and responses", "-v, --verbose show technical data", - "-n, --keyid key id", - "-k, --key key for authenticate", - "-f, --file file id", + "-n key id", + "-k, --key auth key", + "--fid file id", "-o, --offset offset for reading data from file", "--noauth read file without authentication", "--sreq communication reader-picc security level", "--sresp communication picc-reader security level" ], - "usage": "hf cipurse read [-hav] [-n ] [-k ] [-f ] [-o ] [--noauth] [--sreq ] [--sresp ]" + "usage": "hf cipurse read [-hav] [-n ] [-k ] [--fid ] [-o ] [--noauth] [--sreq ] [--sresp ]" + }, + "hf cipurse test": { + "command": "hf cipurse test", + "description": "[=] ------ cipurse tests ------ [=] kvv.............. passed [=] iso9797m2........ passed [=] smi.............. passed [=] mic.............. passed [=] auth............. passed [=] channel mac...... passed [=] channel encdec... passed [=] apdu............. passed [=] --------------------------- [+] tests [ ok ] ======================================================================================= hf epa { german identification card... } --------------------------------------------------------------------------------------- hf epa help available offline: yes help this help --------------------------------------------------------------------------------------- hf epa cnonces available offline: no tries to collect nonces when doing part of pace protocol.", + "notes": [ + "hf epa cnonces --size 4 --num 4 --delay 1" + ], + "offline": true, + "options": [ + "-h, --help this help", + "--size nonce size", + "--num number of nonces to collect", + "-d, --delay delay between attempts" + ], + "usage": "hf epa cnonces [-h] --size --num -d " }, "hf cipurse write": { "command": "hf cipurse write", - "description": "write file by file id with key id and key", + "description": "write file by file id with key id and key. if no key is supplied, default key of 737373...7373 will be used", "notes": [ - "hf cipurse write -f 2ff7 -> authenticate with keyid=1 and key = 7373...7373 and write file with id 2ff7", - "hf cipurse write -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> authenticate with specified key and write file" + "hf cipurse write --fid 2ff7 -> authenticate with keyid 1, write file with id 2ff7", + "hf cipurse write -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> authenticate keyid 2 and write file" ], "offline": false, "options": [ "-h, --help this help", "-a, --apdu show apdu requests and responses", "-v, --verbose show technical data", - "-n, --keyid key id", - "-k, --key key for authenticate", - "-f, --file file id", + "-n key id", + "-k, --key auth key", + "--fid file id", "-o, --offset offset for reading data from file", "--noauth read file without authentication", "--sreq communication reader-picc security level", "--sresp communication picc-reader security level", - "-c, --content new file content" + "-d, --data hex data to write to new file" ], - "usage": "hf cipurse write [-hav] [-n ] [-k ] [-f ] [-o ] [--noauth] [--sreq ] [--sresp ] [-c ]" + "usage": "hf cipurse write [-hav] [-n ] [-k ] [--fid ] [-o ] [--noauth] [--sreq ] [--sresp ] [-d ]" }, "hf emrtd help": { "command": "hf emrtd help", @@ -1854,21 +1869,6 @@ ], "usage": "hf emrtd list [-h1fcrux] [--dict ]..." }, - "hf epa help": { - "command": "hf epa help", - "description": "help this help --------------------------------------------------------------------------------------- hf epa cnonces available offline: no tries to collect nonces when doing part of pace protocol.", - "notes": [ - "hf epa cnonces --size 4 --num 4 --delay 1" - ], - "offline": true, - "options": [ - "-h, --help this help", - "--size nonce size", - "--num number of nonces to collect", - "-d, --delay delay between attempts" - ], - "usage": "hf epa cnonces [-h] --size --num -d " - }, "hf epa preplay": { "command": "hf epa preplay", "description": "perform pace protocol by replaying given apdus", @@ -3491,9 +3491,9 @@ "hf mf fchk --2k -k ffffffffffff -> key recovery against mifare 2k", "hf mf fchk --4k -k ffffffffffff -> key recovery against mifare 4k", "hf mf fchk --1k -f mfc_default_keys.dic -> target 1k using default dictionary file", - "hf mf fchk --1k --emu -> target 1k, write to emulator memory", - "hf mf fchk --1k --dump -> target 1k, write to file", - "hf mf fchk --1k --mem -> target 1k, use dictionary from flashmemory" + "hf mf fchk --1k --emu -> target 1k, write keys to emulator memory", + "hf mf fchk --1k --dump -> target 1k, write keys to file", + "hf mf fchk --1k --mem -> target 1k, use dictionary from flash memory" ], "offline": false, "options": [ @@ -3553,6 +3553,23 @@ ], "usage": "hf mf gen3uid [-h] [-u ]" }, + "hf mf gview": { + "command": "hf mf gview", + "description": "view `magic gen3 gtu` card memory", + "notes": [ + "hf mf gview", + "hf mf gview --4k" + ], + "offline": false, + "options": [ + "-h, --help this help", + "--mini mifare classic mini / s20", + "--1k mifare classic 1k / s50 (def)", + "--2k mifare classic/plus 2k", + "--4k mifare classic 4k / s70" + ], + "usage": "hf mf gview [-h] [--mini] [--1k] [--2k] [--4k]" + }, "hf mf hardnested": { "command": "hf mf hardnested", "description": "nested attack for hardened mifare classic cards. `--i` set type of simd instructions. without this flag programs autodetect it. or hf mf hardnested -r --tk [known target key] add the known target key to check if it is present in the remaining key space hf mf hardnested --blk 0 -a -k a0a1a2a3a4a5 --tblk 4 --ta --tk ffffffffffff", @@ -4219,12 +4236,12 @@ ], "usage": "hf mfdes list [-h1fcrux] [--dict ]..." }, - "hf mfdes readdata": { - "command": "hf mfdes readdata", + "hf mfdes read": { + "command": "hf mfdes read", "description": "read data from file make sure to select aid or authenticate aid before running this command.", "notes": [ - "hf mfdes readdata -n 01 -t 0 -o 000000 -l 000000 -a 123456", - "hf mfdes readdata -n 01 -t 0 -> read all data from standard file, fileno 01" + "hf mfdes read -n 1 -t 0 -o 000000 -l 000000 -a 123456", + "hf mfdes read -n 1 -t 0 -> read all data from standard file, fileno 1" ], "offline": false, "options": [ @@ -4235,7 +4252,7 @@ "-t, --type file type (0 = standard / backup, 1 = record)", "-a, --aid app id to select (3 hex bytes, big endian)" ], - "usage": "hf mfdes readdata [-h] [-n ] [-o ]... [-l ]... [-t ] [-a ]..." + "usage": "hf mfdes read [-h] [-n ] [-o ]... [-l ]... [-t ] [-a ]..." }, "hf mfdes selectaid": { "command": "hf mfdes selectaid", @@ -4250,11 +4267,11 @@ ], "usage": "hf mfdes selectaid [-h] [-a ]..." }, - "hf mfdes writedata": { - "command": "hf mfdes writedata", + "hf mfdes write": { + "command": "hf mfdes write", "description": "write data to file make sure to select aid or authenticate aid before running this command.", "notes": [ - "hf mfdes writedata -n 01 -t 0 -o 000000 -d 3132333435363738" + "hf mfdes write -n 01 -t 0 -o 000000 -d 3132333435363738" ], "offline": false, "options": [ @@ -4265,7 +4282,7 @@ "-t, --type file type (0 = standard / backup, 1 = record)", "-a, --aid app id to select as hex bytes (3 bytes, big endian)" ], - "usage": "hf mfdes writedata [-h] [-n ] [-o ]... [-d ]... [-t ] [-a ]..." + "usage": "hf mfdes write [-h] [-n ] [-o ]... [-d ]... [-t ] [-a ]..." }, "hf mfp auth": { "command": "hf mfp auth", @@ -5061,8 +5078,8 @@ "command": "hw connect", "description": "connects to a proxmark3 device via specified serial port. baudrate here is only for physical uart or uart-bt, not for usb-cdc or blue shark add-on", "notes": [ - "hw connect -p com3", - "hw connect -p com3 -b 115200" + "hw connect -p /dev/ttyacm0", + "hw connect -p /dev/ttyacm0 -b 115200" ], "offline": true, "options": [ @@ -5761,7 +5778,7 @@ }, "lf em 4x50 chk": { "command": "lf em 4x50 chk", - "description": "dictionary attack against em4x50.", + "description": "run dictionary key recovery against em4x50 card.", "notes": [ "lf em 4x50 chk -> uses t55xx default dictionary", "lf em 4x50 chk -f my.dic" @@ -5831,7 +5848,7 @@ }, "lf em 4x50 help": { "command": "lf em 4x50 help", - "description": "help this help --------------------------------------------------------------------------------------- lf em 4x50 brute available offline: no tries to bruteforce the password of a em4x50. function can be stopped by pressing pm3 button.", + "description": "help this help ----------- --------------------- operations --------------------- ----------- --------------------- simulation --------------------- --------------------------------------------------------------------------------------- lf em 4x50 brute available offline: no tries to bruteforce the password of a em4x50 card. function can be stopped by pressing pm3 button.", "notes": [ "lf em 4x50 brute --first 12330000 --last 12340000 -> tries pwds from 0x12330000 to 0x1234000000" ], @@ -5962,11 +5979,11 @@ ], "usage": "lf em 4x50 wrbl [-h] -b -d [-p ]" }, - "lf em 4x50 writepwd": { - "command": "lf em 4x50 writepwd", + "lf em 4x50 wrpwd": { + "command": "lf em 4x50 wrpwd", "description": "writes em4x50 password.", "notes": [ - "lf em 4x50 writepwd -p 4f22e7ff -n 12345678" + "lf em 4x50 wrpwd -p 4f22e7ff -n 12345678" ], "offline": false, "options": [ @@ -5974,7 +5991,7 @@ "-p, --pwd password, 4 hex bytes, lsb", "-n, --new new password, 4 hex bytes, lsb" ], - "usage": "lf em 4x50 writepwd [-h] -p -n " + "usage": "lf em 4x50 wrpwd [-h] -p -n " }, "lf em 4x70 auth": { "command": "lf em 4x70 auth", @@ -6441,17 +6458,18 @@ "command": "lf hitag help", "description": "help this help list list hitag trace history --------------------------------------------------------------------------------------- lf hitag eload available offline: no loads hitag tag dump into emulator memory on device", "notes": [ - "lf hitag eload -f lf-hitag-11223344-dump.bin" + "lf hitag eload -2 -f lf-hitag-11223344-dump.bin" ], "offline": true, "options": [ "-h, --help this help", "-f, --file filename of dump", - "-1 simulate hitag1", - "-2 simulate hitag2", - "-s simulate hitags" + "-1 card type hitag1", + "-2 card type hitag2", + "-s card type hitags", + "-m card type hitagm" ], - "usage": "lf hitag eload [-h12s] -f " + "usage": "lf hitag eload [-h12sm] -f " }, "lf hitag info": { "command": "lf hitag info", @@ -7586,7 +7604,7 @@ }, "lf sniff": { "command": "lf sniff", - "description": "sniff low frequency signal. - use `lf config` to set parameters. - use `data plot` to look at it", + "description": "sniff low frequency signal. you need to configure the lf part on the proxmark3 device manually. usually a trigger and skip samples is a good thing to set before doing a low frequency sniff. - use `lf config` to set parameters. - use `data plot` to look at sniff signal. - use `lf search -1` to see if signal can be automatic decoded", "notes": [ "lf sniff -v", "lf sniff -s 3000 -@ -> oscilloscope style" @@ -9179,8 +9197,8 @@ } }, "metadata": { - "commands_extracted": 570, + "commands_extracted": 571, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2021-06-18T09:04:45" + "extracted_on": "2021-06-24T17:28:12" } } \ No newline at end of file diff --git a/doc/commands.md b/doc/commands.md index 32f98bdef..433b44e26 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -241,12 +241,13 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`hf cipurse help `|Y |`This help.` -|`hf cipurse info `|N |`Info about Cipurse tag.` -|`hf cipurse auth `|N |`Authentication.` -|`hf cipurse read `|N |`Read binary file.` -|`hf cipurse write `|N |`Write binary file.` -|`hf cipurse aread `|N |`Read file attributes.` -|`hf cipurse delete `|N |`Delete file.` +|`hf cipurse info `|N |`Get info about CIPURSE tag` +|`hf cipurse auth `|N |`Authenticate CIPURSE tag` +|`hf cipurse read `|N |`Read binary file` +|`hf cipurse write `|N |`Write binary file` +|`hf cipurse aread `|N |`Read file attributes` +|`hf cipurse delete `|N |`Delete file` +|`hf cipurse test `|Y |`Tests` ### hf epa @@ -445,6 +446,7 @@ Check column "offline" for their availability. |`hf mf gen3uid `|N |`Set UID without changing manufacturer block` |`hf mf gen3blk `|N |`Overwrite manufacturer block` |`hf mf gen3freeze `|N |`Perma lock UID changes. irreversible` +|`hf mf gview `|N |`View card` ### hf mfp @@ -518,8 +520,8 @@ Check column "offline" for their availability. |`hf mfdes deletefile `|N |`Create Delete File` |`hf mfdes dump `|N |`Dump all files` |`hf mfdes getvalue `|N |`Get value of file` -|`hf mfdes readdata `|N |`Read data from standard/backup/record file` -|`hf mfdes writedata `|N |`Write data to standard/backup/record file` +|`hf mfdes read `|N |`Read data from standard/backup/record file` +|`hf mfdes write `|N |`Write data to standard/backup/record file` ### hf seos @@ -722,21 +724,21 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`lf em 4x50 help `|Y |`This help` -|`lf em 4x50 brute `|N |`guess password of EM4x50` -|`lf em 4x50 chk `|N |`check passwords from dictionary` -|`lf em 4x50 dump `|N |`dump EM4x50 tag` -|`lf em 4x50 info `|N |`tag information EM4x50` -|`lf em 4x50 login `|N |`login into EM4x50` -|`lf em 4x50 rdbl `|N |`read word data from EM4x50` -|`lf em 4x50 wrbl `|N |`write word data to EM4x50` -|`lf em 4x50 writepwd `|N |`change password of EM4x50` -|`lf em 4x50 wipe `|N |`wipe EM4x50 tag` -|`lf em 4x50 reader `|N |`show standard read mode data of EM4x50` -|`lf em 4x50 restore `|N |`restore EM4x50 dump to tag` -|`lf em 4x50 sim `|N |`simulate EM4x50 tag` -|`lf em 4x50 eload `|N |`upload dump of EM4x50 to emulator memory` -|`lf em 4x50 esave `|N |`save emulator memory to file` -|`lf em 4x50 eview `|N |`view EM4x50 content in emulator memory` +|`lf em 4x50 brute `|N |`Simple bruteforce attack to find password` +|`lf em 4x50 chk `|N |`Check passwords from dictionary` +|`lf em 4x50 dump `|N |`Dump EM4x50 tag` +|`lf em 4x50 info `|N |`Tag information` +|`lf em 4x50 login `|N |`Login into EM4x50 tag` +|`lf em 4x50 rdbl `|N |`Read EM4x50 word data` +|`lf em 4x50 reader `|N |`Show standard read mode data` +|`lf em 4x50 restore `|N |`Restore EM4x50 dump to tag` +|`lf em 4x50 wrbl `|N |`Write EM4x50 word data` +|`lf em 4x50 wrpwd `|N |`Change EM4x50 password` +|`lf em 4x50 wipe `|N |`Wipe EM4x50 tag` +|`lf em 4x50 eload `|N |`Upload EM4x50 dump to emulator memory` +|`lf em 4x50 esave `|N |`Save emulator memory to file` +|`lf em 4x50 eview `|N |`View EM4x50 content in emulator memory` +|`lf em 4x50 sim `|N |`Simulate EM4x50 tag` ### lf em 4x70 diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index 4e89e4f86..53343c5e1 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -740,6 +740,9 @@ typedef struct { #define CMD_HF_MIFARE_GEN3BLK 0x0851 #define CMD_HF_MIFARE_GEN3FREEZ 0x0852 +// Gen 3 GTU magic cards +#define CMD_HF_MIFARE_G3_RDBL 0x0860 + #define CMD_UNKNOWN 0xFFFF //Mifare simulation flags diff --git a/tools/mf_nonce_brute/mf_nonce_brute.c b/tools/mf_nonce_brute/mf_nonce_brute.c index b064f3683..973989eda 100644 --- a/tools/mf_nonce_brute/mf_nonce_brute.c +++ b/tools/mf_nonce_brute/mf_nonce_brute.c @@ -417,6 +417,7 @@ static void *brute_thread(void *arguments) { if (isOK == false) { printf(_RED_("<-- not a valid cmd\n")); pthread_mutex_unlock(&print_lock); + free(revstate); continue; } @@ -425,6 +426,7 @@ static void *brute_thread(void *arguments) { if (isOK == false) { printf(_RED_("<-- not a valid crc\n")); pthread_mutex_unlock(&print_lock); + free(revstate); continue; } else { printf("<-- valid cmd\n"); diff --git a/tools/pm3_tests.sh b/tools/pm3_tests.sh index 38d66d23f..41882f2e5 100755 --- a/tools/pm3_tests.sh +++ b/tools/pm3_tests.sh @@ -491,6 +491,7 @@ while true; do if ! $SLOWTESTS; then if ! CheckExecute "hf iclass loclass test" "$CLIENTBIN -c 'hf iclass loclass --test'" "key diversification (ok)"; then break; fi if ! CheckExecute "emv test" "$CLIENTBIN -c 'emv test'" "Test(s) \[ ok"; then break; fi + if ! CheckExecute "hf cipurse test" "$CLIENTBIN -c 'hf cipurse test'" "Tests \[ ok"; then break; fi fi fi echo -e "\n------------------------------------------------------------"