From ac2668f8980d7ce640f22e9ea25d756c60720e6b Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Mon, 30 Dec 2019 14:55:52 +0100 Subject: [PATCH] speedup hf mf chk * revert queuing change * instead allow arbitrary number of keys in MifareChkKeys() * and move progress printing to MifareChkKeys() --- armsrc/mifarecmd.c | 27 ++--- armsrc/mifareutil.c | 2 - client/cmdhfmf.c | 34 +++--- client/mifare/mifarehost.c | 211 ++++++++++++++----------------------- client/mifare/mifarehost.h | 3 +- 5 files changed, 99 insertions(+), 178 deletions(-) diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 6fc51bfb..b9032c5f 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -1001,18 +1001,9 @@ void MifareChkKeys(uint16_t arg0, uint32_t arg1, uint8_t arg2, uint8_t *datain) bool multisectorCheck = arg1 & 0x02; bool init = arg1 & 0x04; bool drop_field = arg1 & 0x08; - static bool reject_next = false; uint32_t auth_timeout = arg1 >> 16; uint8_t keyCount = arg2; - if (reject_next) { - reject_next = false; - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LED_D_OFF(); - cmd_send(CMD_ACK, 0, -3, 0, NULL, 0); - return; - } - LED_A_ON(); if (init) { @@ -1033,24 +1024,18 @@ void MifareChkKeys(uint16_t arg0, uint32_t arg1, uint8_t arg2, uint8_t *datain) TKeyIndex keyIndex = {{0}}; uint8_t sectorCnt = blockNo; res = MifareMultisectorChk(datain, keyCount, sectorCnt, keyType, &auth_timeout, OLD_MF_DBGLEVEL, &keyIndex); - - if (res >= 0) + if (res >= 0) { cmd_send(CMD_ACK, 1, res, 0, keyIndex, 80); - else + } else { cmd_send(CMD_ACK, 0, res, 0, NULL, 0); - - if (res < 0 && usb_poll_validate_length()) // we want to exit but another set of keys has been queued! - reject_next = true; + } } else { res = MifareChkBlockKeys(datain, keyCount, blockNo, keyType, &auth_timeout, OLD_MF_DBGLEVEL); - - if (res != 0 && usb_poll_validate_length()) // we want to exit but another set of keys has been queued! - reject_next = true; - - if (res > 0) + if (res > 0) { cmd_send(CMD_ACK, 1, res, 0, datain + (res - 1) * 6, 6); - else + } else { cmd_send(CMD_ACK, 0, res, 0, NULL, 0); + } } if (drop_field || res != 0) { diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c index 657c63d4..71c65eec 100644 --- a/armsrc/mifareutil.c +++ b/armsrc/mifareutil.c @@ -867,8 +867,6 @@ int MifareChkBlockKeys(uint8_t *keys, uint8_t keyCount, uint8_t blockNo, uint8_t int retryCount = 0; for (uint8_t i = 0; i < keyCount; i++) { - // Allow button press / usb cmd to interrupt device - ui64Key = bytes_to_num(keys + i * 6, 6); int res = MifareChkBlockKey(uid, &cuid, &cascade_levels, ui64Key, blockNo, keyType, auth_timeout, debugLevel); diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 05a00844..74b68c09 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -675,7 +675,7 @@ int CmdHF14AMfNested(const char *Cmd) { } // check if we can authenticate to sector - res = mfCheckKeys(blockNo, keyType, timeout14a, true, true, true, false, 1, key, &key64); + res = mfCheckKeys(blockNo, keyType, timeout14a, true, 1, key, &key64); if (res) { PrintAndLog("Can't authenticate to block:%3d key type:%c key:%s", blockNo, keyType?'B':'A', sprint_hex(key, 6)); return 3; @@ -1270,29 +1270,23 @@ int CmdHF14AMfChk(const char *Cmd) { } else { int keyAB = keyType; do { - for (uint32_t c = 0; c < keycnt; c += max_keys) { + res = mfCheckKeys(blockNo, keyAB & 0x01, timeout14a, true, keycnt, keyBlock, &key64); + clearTraceLog = false; - uint32_t size = keycnt-c > max_keys ? max_keys : keycnt-c; - bool init = (c == 0); - bool drop_field = (c + size == keycnt); - res = mfCheckKeys(blockNo, keyAB & 0x01, timeout14a, true, init, drop_field, false, size, &keyBlock[6 * c], &key64); - clearTraceLog = false; + if (res != 1) { + if (!res) { + // Use the common format below + // PrintAndLog("Found valid key:[%d:%c]%012" PRIx64, blockNo, (keyAB & 0x01)?'B':'A', key64); + foundAKey = true; - if (res != 1) { - if (!res) { - // Use the common format below - // PrintAndLog("Found valid key:[%d:%c]%012" PRIx64, blockNo, (keyAB & 0x01)?'B':'A', key64); - foundAKey = true; + // Store the Single Key for display list + // For a single block check, SectorsCnt = Sector that contains the block + e_sector[SectorsCnt-1].foundKey[(keyAB & 0x01)] = true; // flag key found + e_sector[SectorsCnt-1].Key[(keyAB & 0x01)] = key64; // Save key data - // Store the Single Key for display list - // For a single block check, SectorsCnt = Sector that contains the block - e_sector[SectorsCnt-1].foundKey[(keyAB & 0x01)] = true; // flag key found - e_sector[SectorsCnt-1].Key[(keyAB & 0x01)] = key64; // Save key data - - } - } else { - PrintAndLog("Command execute timeout"); } + } else { + PrintAndLog("Command execute timeout"); } } while(--keyAB > 0); } diff --git a/client/mifare/mifarehost.c b/client/mifare/mifarehost.c index cf251c67..7b710710 100644 --- a/client/mifare/mifarehost.c +++ b/client/mifare/mifarehost.c @@ -196,24 +196,15 @@ int mfDarkside(uint64_t *key) { PrintAndLog("Found a possible key. Trying to authenticate...\n"); } - *key = -1; - uint8_t keyBlock[USB_CMD_DATA_SIZE]; - int max_keys = USB_CMD_DATA_SIZE/6; - for (int i = 0; i < keycount; i += max_keys) { - int size = keycount - i > max_keys ? max_keys : keycount - i; - for (int j = 0; j < size; j++) { - if (par_list == 0) { - num_to_bytes(last_keylist[i*max_keys + j], 6, keyBlock+(j*6)); - } else { - num_to_bytes(keylist[i*max_keys + j], 6, keyBlock+(j*6)); - } - } - bool init = (i == 0); - bool drop_field = (i + size == keycount); - if (!mfCheckKeys(0, 0, 0, false, init, drop_field, false, size, keyBlock, key)) { - break; - } + uint8_t *keys_to_chk = malloc(keycount * 6); + for (int i = 0; i < keycount; i++) { + num_to_bytes(keylist[i], 6, keys_to_chk+i); } + + *key = -1; + mfCheckKeys(0, 0, 0, false, keycount, keys_to_chk, key); + + free(keys_to_chk); if (*key != -1) { free(last_keylist); @@ -229,42 +220,64 @@ int mfDarkside(uint64_t *key) { return 0; } -int mfCheckKeys(uint8_t blockNo, uint8_t keyType, uint16_t timeout14a, bool clear_trace, bool init, bool drop_field, bool dont_wait, uint8_t keycnt, uint8_t * keyBlock, uint64_t * key) { - *key = -1; - bool multisectorCheck = false; - uint8_t flags = clear_trace | multisectorCheck << 1 | init << 2 | drop_field << 3; +int mfCheckKeys(uint8_t blockNo, uint8_t keyType, uint16_t timeout14a, bool clear_trace, uint32_t keycnt, uint8_t *keys, uint64_t *found_key) { - UsbCommand c = {CMD_MIFARE_CHKKEYS, {((blockNo & 0xff) | ((keyType & 0xff) << 8)), flags | timeout14a << 16, keycnt}}; - memcpy(c.d.asBytes, keyBlock, 6 * keycnt); - SendCommand(&c); + bool display_progress = false; + uint64_t start_time = msclock(); + uint64_t next_print_time = start_time + 5 * 1000; - if (dont_wait) - return 3; - - return mfCheckKeysGetResponse(key); -} - -int mfCheckKeysGetResponse(uint64_t *key) { - UsbCommand resp; - if (!WaitForResponseTimeout(CMD_ACK,&resp,3000)) - return 1; - - if ((resp.arg[0] & 0xff) != 0x01) { - if (((int)resp.arg[1]) < 0) - return (int)resp.arg[1]; - - return 2; + if (keycnt > 1000) { + PrintAndLog("We have %d keys to check. This will take some time!", keycnt); + PrintAndLog("Press button to abort."); + display_progress = true; } - if (key) - *key = bytes_to_num(resp.d.asBytes, 6); + uint32_t max_keys = (keycnt > (USB_CMD_DATA_SIZE / 6)) ? (USB_CMD_DATA_SIZE / 6) : keycnt; + *found_key = -1; + bool multisectorCheck = false; - return 0; + for (int i = 0, ii = 0; i < keycnt; i += max_keys) { + + if ((i + max_keys) >= keycnt) { + max_keys = keycnt - i; + } + + bool init = (i == 0); + bool drop_field = (max_keys == keycnt); + uint8_t flags = clear_trace | multisectorCheck << 1 | init << 2 | drop_field << 3; + + UsbCommand c = {CMD_MIFARE_CHKKEYS, {((blockNo & 0xff) | ((keyType & 0xff) << 8)), flags | timeout14a << 16, max_keys}}; + memcpy(c.d.asBytes, keys + i * 6, max_keys * 6); + SendCommand(&c); + + UsbCommand resp; + if (!WaitForResponseTimeout(CMD_ACK, &resp, 3000)) + return 1; + + if ((resp.arg[0] & 0xff) != 0x01) { + if (((int)resp.arg[1]) < 0) { // error + return (int)resp.arg[1]; + } else { // nothing found yet + if (display_progress && msclock() >= next_print_time) { + float brute_force_per_second = (float)(i - ii) / (float)(msclock() - start_time) * 1000.0; + ii = i; + start_time = msclock(); + next_print_time = start_time + 10 * 1000; + PrintAndLog(" %8d keys left | %5.1f keys/sec | worst case %6.1f seconds remaining", keycnt - i, brute_force_per_second, (keycnt-i)/brute_force_per_second); + } + } + } else { // success + *found_key = bytes_to_num(resp.d.asBytes, 6); + return 0; + } + } + + return 2; // nothing found } -int mfCheckKeysSec(uint8_t sectorCnt, uint8_t keyType, uint16_t timeout14a, bool clear_trace, bool init, bool drop_field, uint8_t keycnt, uint8_t * keyBlock, sector_t * e_sector){ +int mfCheckKeysSec(uint8_t sectorCnt, uint8_t keyType, uint16_t timeout14a, bool clear_trace, bool init, bool drop_field, uint8_t keycnt, uint8_t * keyBlock, sector_t * e_sector) { uint8_t keyPtr = 0; @@ -317,7 +330,7 @@ typedef uint32_t uid; uint32_t blockNo; uint32_t keyType; - uint32_t nt; + uint32_t nt_enc; uint32_t ks1; } StateList_t; @@ -329,12 +342,11 @@ void __attribute__((force_align_arg_pointer)) #endif #endif -*nested_worker_thread(void *arg) -{ +*nested_worker_thread(void *arg) { struct Crypto1State *p1; StateList_t *statelist = arg; - statelist->head.slhead = lfsr_recovery32(statelist->ks1, statelist->nt ^ statelist->uid); + statelist->head.slhead = lfsr_recovery32(statelist->ks1, statelist->nt_enc ^ statelist->uid); for (p1 = statelist->head.slhead; *(uint64_t *)p1 != 0; p1++); statelist->len = p1 - statelist->head.slhead; statelist->tail.sltail = --p1; @@ -345,7 +357,7 @@ __attribute__((force_align_arg_pointer)) int mfnested(uint8_t blockNo, uint8_t keyType, uint16_t timeout14a, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *resultKey, bool calibrate) { - uint32_t i, j, last_count; + uint32_t i; uint32_t uid; UsbCommand resp; @@ -359,11 +371,6 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint16_t timeout14a, uint8_t *key int isOK = 1; - uint64_t next_print_time = 0; - uint64_t start_time; - float brute_force_time; - float brute_force_per_second; - // flush queue (void)WaitForResponseTimeout(CMD_ACK,NULL,100); @@ -390,7 +397,7 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint16_t timeout14a, uint8_t *key statelists[i].blockNo = resp.arg[2] & 0xff; statelists[i].keyType = (resp.arg[2] >> 8) & 0xff; statelists[i].uid = uid; - memcpy(&statelists[i].nt, (void *)(resp.d.asBytes + 4 + i * 8 + 0), 4); + memcpy(&statelists[i].nt_enc, (void *)(resp.d.asBytes + 4 + i * 8 + 0), 4); memcpy(&statelists[i].ks1, (void *)(resp.d.asBytes + 4 + i * 8 + 4), 4); } @@ -398,7 +405,7 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint16_t timeout14a, uint8_t *key memcpy(&authentication_timeout, resp.d.asBytes + 20, 4); PrintAndLog("Setting authentication timeout to %" PRIu32 "us", authentication_timeout * 1000 / 106); - if (statelists[0].nt == statelists[1].nt && statelists[0].ks1 == statelists[1].ks1) + if (statelists[0].nt_enc == statelists[1].nt_enc && statelists[0].ks1 == statelists[1].ks1) num_unique_nonces = 1; else num_unique_nonces = 2; @@ -429,14 +436,14 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint16_t timeout14a, uint8_t *key savestate = *p1; while(Compare16Bits(p1, savep) == 0 && p1 <= statelists[0].tail.sltail) { *p3 = *p1; - lfsr_rollback_word(p3, statelists[0].nt ^ statelists[0].uid, 0); + lfsr_rollback_word(p3, statelists[0].nt_enc ^ statelists[0].uid, 0); p3++; p1++; } savestate = *p2; while(Compare16Bits(p2, savep) == 0 && p2 <= statelists[1].tail.sltail) { *p4 = *p2; - lfsr_rollback_word(p4, statelists[1].nt ^ statelists[1].uid, 0); + lfsr_rollback_word(p4, statelists[1].nt_enc ^ statelists[1].uid, 0); p4++; p2++; } @@ -454,7 +461,7 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint16_t timeout14a, uint8_t *key statelists[1].tail.sltail=--p4; for (i = 0; i < 2; i++) { - PrintAndLog("statelist %d: length:%d block:%02d keytype:%d nt:%08X ks1:%08X", i, statelists[i].len, statelists[i].blockNo, statelists[i].keyType, statelists[i].nt, statelists[i].ks1); + PrintAndLog("statelist %d: length:%d block:%02d keytype:%d nt_enc:%08X ks1:%08X", i, statelists[i].len, statelists[i].blockNo, statelists[i].keyType, statelists[i].nt_enc, statelists[i].ks1); } // the statelists now contain possible keys. The key we are searching for must be in the @@ -469,92 +476,30 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint16_t timeout14a, uint8_t *key PrintAndLog("Nonce 1 and 2 are the same!"); } - if (statelists[0].len > 100) { - PrintAndLog("We have %d keys to check. This will take a very long time!", statelists[0].len); - PrintAndLog("Press button to abort."); - } - else if (statelists[0].len < 1) { - PrintAndLog("No candidate keys to check!"); - } - else { - PrintAndLog("We have %d key(s) to check.", statelists[0].len); - } - - uint32_t max_keys = (statelists[0].len > (USB_CMD_DATA_SIZE / 6)) ? (USB_CMD_DATA_SIZE / 6) : statelists[0].len; - keyBlock = calloc(max_keys, 6); - + uint32_t num_keys = statelists[0].len; + keyBlock = calloc(num_keys, 6); if (keyBlock == NULL) { free(statelists[0].head.slhead); free(statelists[1].head.slhead); return -4; } - memset(resultKey, 0, 6); - start_time = msclock(); - next_print_time = start_time + 1 * 1000; - bool want_queue = (max_keys < statelists[0].len); - bool queued_next_set = false; + for (i = 0; i < num_keys; i++) { + crypto1_get_lfsr(statelists[0].head.slhead + i, &key64); + num_to_bytes(key64, 6, keyBlock + i*6); + } + // The list may still contain several key candidates. Test each of them with mfCheckKeys - for (i = 0; i < statelists[0].len; i += max_keys) { - if (next_print_time <= msclock()) { - brute_force_per_second = ((float)i) / (((float)(msclock() - start_time)) / 1000.0); - brute_force_time = ((float)(statelists[0].len - i)) / brute_force_per_second; - next_print_time = msclock() + 10 * 1000; - PrintAndLog(" %8d keys left | %5.1f keys/sec | worst case %6.1f seconds remaining", statelists[0].len - i, brute_force_per_second, brute_force_time); - } + isOK = mfCheckKeys(statelists[0].blockNo, statelists[0].keyType, authentication_timeout, true, num_keys, keyBlock, &key64); - if ((i+max_keys) >= statelists[0].len) - max_keys = statelists[0].len - i; - - for (j = 0; j < max_keys; j++) { - crypto1_get_lfsr(statelists[0].head.slhead + i + j, &key64); - num_to_bytes(key64, 6, keyBlock+(j*6)); - } - - key64 = 0; - - bool init = (i == 0); - bool drop_field = (i + max_keys == statelists[0].len); - bool dont_wait = want_queue && !queued_next_set; - isOK = mfCheckKeys(statelists[0].blockNo, statelists[0].keyType, authentication_timeout, true, init, drop_field, dont_wait, max_keys, keyBlock, &key64); - - if (dont_wait && isOK == 3) { - queued_next_set = true; - continue; - } - - if (isOK == 1) { // timeout - isOK = -1; - break; - } - else if (isOK < 0) { // -2 is button pressed - break; - } - else if (!isOK) { - num_to_bytes(key64, 6, resultKey); - break; - } + if (isOK == 0) { // success, key found + num_to_bytes(key64, 6, resultKey); } - if (queued_next_set) { - i -= max_keys; // fix the count from the last trip through the for() loop - - if (!isOK) { - mfCheckKeysGetResponse(NULL); // we already have what we want, just consume the queued response - } - else { - isOK = mfCheckKeysGetResponse(&key64); - - if (isOK == 1) // timeout - isOK = -1; - else if (!isOK) - num_to_bytes(key64, 6, resultKey); - } + if (isOK == 1) { // timeout + isOK = -1; } - if (isOK == 0 && statelists[0].len != 1) - PrintAndLog("Key found in %0.2f seconds after checking %d keys\n", ((float)(msclock() - start_time)) / 1000.0, i+max_keys); - free(statelists[0].head.slhead); free(statelists[1].head.slhead); free(keyBlock); diff --git a/client/mifare/mifarehost.h b/client/mifare/mifarehost.h index 8a5b238d..82fe7ad8 100644 --- a/client/mifare/mifarehost.h +++ b/client/mifare/mifarehost.h @@ -40,8 +40,7 @@ extern char logHexFileName[FILE_PATH_SIZE]; extern int mfDarkside(uint64_t *key); extern int mfnested(uint8_t blockNo, uint8_t keyType, uint16_t timeout14a, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *ResultKeys, bool calibrate); -extern int mfCheckKeys(uint8_t blockNo, uint8_t keyType, uint16_t timeout14a, bool clear_trace, bool init, bool drop_field, bool dont_wait, uint8_t keycnt, uint8_t *keyBlock, uint64_t *key); -extern int mfCheckKeysGetResponse(uint64_t *key); +extern int mfCheckKeys(uint8_t blockNo, uint8_t keyType, uint16_t timeout14a, bool clear_trace, uint32_t keycnt, uint8_t *keyBlock, uint64_t *key); extern int mfCheckKeysSec(uint8_t sectorCnt, uint8_t keyType, uint16_t timeout14a, bool clear_trace, bool init, bool drop_field, uint8_t keycnt, uint8_t * keyBlock, sector_t * e_sector); extern int mfReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *data);