]> git.zerfleddert.de Git - proxmark3-svn/blobdiff - client/mifare/mifarehost.c
speedup 'hf mf chk' (#901)
[proxmark3-svn] / client / mifare / mifarehost.c
index 90c698781468ecefdfa4c87f0d67c6cc91eb3de7..7b7107105aa92b9ff000b1221b6cabc4d578ba1a 100644 (file)
@@ -10,6 +10,7 @@
 
 #include "mifarehost.h"
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -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
Impressum, Datenschutz