X-Git-Url: https://git.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/blobdiff_plain/fdd9395d1a0f331f9cc74d6cdd6dd71447524e6c..929b61c6701f8266c855a669d4c7827cbe0d8eb8:/client/mifare/mifarehost.c diff --git a/client/mifare/mifarehost.c b/client/mifare/mifarehost.c index 90c69878..bc53e1fe 100644 --- a/client/mifare/mifarehost.c +++ b/client/mifare/mifarehost.c @@ -10,6 +10,7 @@ #include "mifarehost.h" +#include #include #include #include @@ -23,6 +24,7 @@ #include "parity.h" #include "util.h" #include "iso14443crc.h" +#include "util_posix.h" #include "mifare.h" #include "mifare4.h" @@ -114,8 +116,7 @@ static uint32_t nonce2key(uint32_t uid, uint32_t nt, uint32_t nr, uint32_t ar, u } -int mfDarkside(uint64_t *key) -{ +int mfDarkside(uint64_t *key) { uint32_t uid = 0; uint32_t nt = 0, nr = 0, ar = 0; uint64_t par_list = 0, ks_list = 0; @@ -195,22 +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)); - } - } - if (!mfCheckKeys(0, 0, 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); @@ -227,29 +221,73 @@ int mfDarkside(uint64_t *key) } -int mfCheckKeys (uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keycnt, uint8_t * keyBlock, uint64_t * key){ +int mfCheckKeys(uint8_t blockNo, uint8_t keyType, uint16_t timeout14a, bool clear_trace, uint32_t keycnt, uint8_t *keys, uint64_t *found_key) { - *key = -1; + bool display_progress = false; + uint64_t start_time = msclock(); + uint64_t next_print_time = start_time + 5 * 1000; - UsbCommand c = {CMD_MIFARE_CHKKEYS, {((blockNo & 0xff) | ((keyType & 0xff) << 8)), clear_trace, keycnt}}; - memcpy(c.d.asBytes, keyBlock, 6 * keycnt); - SendCommand(&c); + if (keycnt > 1000) { + PrintAndLog("We have %d keys to check. This will take some time!", keycnt); + PrintAndLog("Press button to abort."); + display_progress = true; + } - UsbCommand resp; - if (!WaitForResponseTimeout(CMD_ACK,&resp,3000)) return 1; - if ((resp.arg[0] & 0xff) != 0x01) return 2; - *key = bytes_to_num(resp.d.asBytes, 6); - return 0; + uint32_t max_keys = (keycnt > (USB_CMD_DATA_SIZE / 6)) ? (USB_CMD_DATA_SIZE / 6) : keycnt; + *found_key = -1; + bool multisectorCheck = false; + + 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, uint8_t timeout14a, bool clear_trace, 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; if (e_sector == NULL) return -1; - UsbCommand c = {CMD_MIFARE_CHKKEYS, {((sectorCnt & 0xff) | ((keyType & 0xff) << 8)), (clear_trace | 0x02)|((timeout14a & 0xff) << 8), keycnt}}; + bool multisectorCheck = true; + uint8_t flags = clear_trace | multisectorCheck << 1 | init << 2 | drop_field << 3; + + UsbCommand c = {CMD_MIFARE_CHKKEYS, {((sectorCnt & 0xff) | ((keyType & 0xff) << 8)), flags | timeout14a << 16, keycnt}}; memcpy(c.d.asBytes, keyBlock, 6 * keycnt); SendCommand(&c); @@ -292,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; @@ -304,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; @@ -319,15 +356,21 @@ __attribute__((force_align_arg_pointer)) } -int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *resultKey, bool calibrate) -{ - uint16_t i; +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; uint32_t uid; UsbCommand resp; + int num_unique_nonces; + StateList_t statelists[2]; struct Crypto1State *p1, *p2, *p3, *p4; + uint8_t *keyBlock = NULL; + uint64_t key64; + + int isOK = 1; + // flush queue (void)WaitForResponseTimeout(CMD_ACK,NULL,100); @@ -335,7 +378,11 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, memcpy(c.d.asBytes, key, 6); SendCommand(&c); - if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { + // some cards can cause it to get stuck in a loop, so break out of it + UsbCommand c = {CMD_PING}; + SendCommand(&c); + (void)WaitForResponseTimeout(CMD_ACK,NULL,500); return -1; } @@ -350,10 +397,19 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, 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); } + uint32_t authentication_timeout; + memcpy(&authentication_timeout, resp.d.asBytes + 20, 4); + PrintAndLog("Setting authentication timeout to %" PRIu32 "us", authentication_timeout * 1000 / 106); + + if (statelists[0].nt_enc == statelists[1].nt_enc && statelists[0].ks1 == statelists[1].ks1) + num_unique_nonces = 1; + else + num_unique_nonces = 2; + // calc keys pthread_t thread_id[2]; @@ -380,14 +436,14 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, 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++; } @@ -404,30 +460,51 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, statelists[0].tail.sltail=--p3; statelists[1].tail.sltail=--p4; + for (i = 0; i < 2; i++) { + 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 // intersection of both lists. Create the intersection: qsort(statelists[0].head.keyhead, statelists[0].len, sizeof(uint64_t), compare_uint64); - qsort(statelists[1].head.keyhead, statelists[1].len, sizeof(uint64_t), compare_uint64); - statelists[0].len = intersection(statelists[0].head.keyhead, statelists[1].head.keyhead); - memset(resultKey, 0, 6); - // The list may still contain several key candidates. Test each of them with mfCheckKeys - for (i = 0; i < statelists[0].len; i++) { - uint8_t keyBlock[6]; - uint64_t key64; + if (num_unique_nonces > 1) { + qsort(statelists[1].head.keyhead, statelists[1].len, sizeof(uint64_t), compare_uint64); + statelists[0].len = intersection(statelists[0].head.keyhead, statelists[1].head.keyhead); + } + else { + PrintAndLog("Nonce 1 and 2 are the same!"); + } + + 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; + } + + for (i = 0; i < num_keys; i++) { crypto1_get_lfsr(statelists[0].head.slhead + i, &key64); - num_to_bytes(key64, 6, keyBlock); - key64 = 0; - if (!mfCheckKeys(statelists[0].blockNo, statelists[0].keyType, false, 1, keyBlock, &key64)) { - num_to_bytes(key64, 6, resultKey); - break; - } + num_to_bytes(key64, 6, keyBlock + i*6); + } + + // The list may still contain several key candidates. Test each of them with mfCheckKeys + isOK = mfCheckKeys(statelists[0].blockNo, statelists[0].keyType, authentication_timeout, true, num_keys, keyBlock, &key64); + + if (isOK == 0) { // success, key found + num_to_bytes(key64, 6, resultKey); + } + + if (isOK == 1) { // timeout + isOK = -1; } free(statelists[0].head.slhead); free(statelists[1].head.slhead); + free(keyBlock); - return 0; + return isOK; } // MIFARE @@ -1087,7 +1164,7 @@ int DetectClassicPrng(void){ clearCommandBuffer(); SendCommand(&c); - if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + if (!WaitForResponseTimeout(CMD_NACK, &resp, 2000)) { PrintAndLog("PRNG UID: Reply timeout."); return -1; }