Преглед на файлове

[FL-2876] MFC Improvements Part 2/2 (#1868)

* Remove keys incorrectly added by the key cache
* Improve responsiveness while checking for re-used keys and fix skipping keys when card is removed
* Actually check if the card is completely read
* Discard incorrect keys on a lower level
* nfc: clean up

Co-authored-by: gornekich <n.gorbadey@gmail.com>
Astra преди 3 години
родител
ревизия
55f8beef9f
променени са 3 файла, в които са добавени 92 реда и са изтрити 29 реда
  1. 50 28
      lib/nfc/nfc_worker.c
  2. 38 1
      lib/nfc/protocols/mifare_classic.c
  3. 4 0
      lib/nfc/protocols/mifare_classic.h

+ 50 - 28
lib/nfc/nfc_worker.c

@@ -191,7 +191,7 @@ static bool nfc_worker_read_mf_classic(NfcWorker* nfc_worker, FuriHalNfcTxRxCont
             uint8_t sectors_total =
                 mf_classic_get_total_sectors_num(nfc_worker->dev_data->mf_classic_data.type);
             FURI_LOG_I(TAG, "Read %d sectors out of %d total", sectors_read, sectors_total);
-            read_success = (sectors_read == sectors_total);
+            read_success = mf_classic_is_card_read(&nfc_worker->dev_data->mf_classic_data);
         }
     } while(false);
 
@@ -480,6 +480,9 @@ static void nfc_worker_mf_classic_key_attack(
     uint16_t start_sector) {
     furi_assert(nfc_worker);
 
+    bool card_found_notified = true;
+    bool card_removed_notified = false;
+
     MfClassicData* data = &nfc_worker->dev_data->mf_classic_data;
     uint32_t total_sectors = mf_classic_get_total_sectors_num(data->type);
 
@@ -487,36 +490,52 @@ static void nfc_worker_mf_classic_key_attack(
 
     // Check every sector's A and B keys with the given key
     for(size_t i = start_sector; i < total_sectors; i++) {
-        uint8_t block_num = mf_classic_get_sector_trailer_block_num_by_sector(i);
-        if(mf_classic_is_sector_read(data, i)) continue;
-        if(!mf_classic_is_key_found(data, i, MfClassicKeyA)) {
-            FURI_LOG_D(
-                TAG,
-                "Trying A key for sector %d, key: %04lx%08lx",
-                i,
-                (uint32_t)(key >> 32),
-                (uint32_t)key);
-            if(mf_classic_authenticate(tx_rx, block_num, key, MfClassicKeyA)) {
-                mf_classic_set_key_found(data, i, MfClassicKeyA, key);
-                FURI_LOG_D(TAG, "Key found");
-                nfc_worker->callback(NfcWorkerEventFoundKeyA, nfc_worker->context);
+        furi_hal_nfc_sleep();
+        if(furi_hal_nfc_activate_nfca(200, NULL)) {
+            furi_hal_nfc_sleep();
+            if(!card_found_notified) {
+                nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context);
+                card_found_notified = true;
+                card_removed_notified = false;
             }
-        }
-        if(!mf_classic_is_key_found(data, i, MfClassicKeyB)) {
-            FURI_LOG_D(
-                TAG,
-                "Trying B key for sector %d, key: %04lx%08lx",
-                i,
-                (uint32_t)(key >> 32),
-                (uint32_t)key);
-            if(mf_classic_authenticate(tx_rx, block_num, key, MfClassicKeyB)) {
-                mf_classic_set_key_found(data, i, MfClassicKeyB, key);
-                FURI_LOG_D(TAG, "Key found");
-                nfc_worker->callback(NfcWorkerEventFoundKeyB, nfc_worker->context);
+            uint8_t block_num = mf_classic_get_sector_trailer_block_num_by_sector(i);
+            if(mf_classic_is_sector_read(data, i)) continue;
+            if(!mf_classic_is_key_found(data, i, MfClassicKeyA)) {
+                FURI_LOG_D(
+                    TAG,
+                    "Trying A key for sector %d, key: %04lx%08lx",
+                    i,
+                    (uint32_t)(key >> 32),
+                    (uint32_t)key);
+                if(mf_classic_authenticate(tx_rx, block_num, key, MfClassicKeyA)) {
+                    mf_classic_set_key_found(data, i, MfClassicKeyA, key);
+                    FURI_LOG_D(TAG, "Key found");
+                    nfc_worker->callback(NfcWorkerEventFoundKeyA, nfc_worker->context);
+                }
+            }
+            if(!mf_classic_is_key_found(data, i, MfClassicKeyB)) {
+                FURI_LOG_D(
+                    TAG,
+                    "Trying B key for sector %d, key: %04lx%08lx",
+                    i,
+                    (uint32_t)(key >> 32),
+                    (uint32_t)key);
+                if(mf_classic_authenticate(tx_rx, block_num, key, MfClassicKeyB)) {
+                    mf_classic_set_key_found(data, i, MfClassicKeyB, key);
+                    FURI_LOG_D(TAG, "Key found");
+                    nfc_worker->callback(NfcWorkerEventFoundKeyB, nfc_worker->context);
+                }
+            }
+
+            if(mf_classic_is_sector_read(data, i)) continue;
+            mf_classic_read_sector(tx_rx, data, i);
+        } else {
+            if(!card_removed_notified) {
+                nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context);
+                card_removed_notified = true;
+                card_found_notified = false;
             }
         }
-        if(mf_classic_is_sector_read(data, i)) continue;
-        mf_classic_read_sector(tx_rx, data, i);
         if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break;
     }
 }
@@ -530,6 +549,7 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) {
         &nfc_worker->dev_data->mf_classic_dict_attack_data;
     uint32_t total_sectors = mf_classic_get_total_sectors_num(data->type);
     uint64_t key = 0;
+    uint64_t prev_key = 0;
     FuriHalNfcTxRxContext tx_rx = {};
     bool card_found_notified = true;
     bool card_removed_notified = false;
@@ -564,6 +584,7 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) {
                     nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context);
                     card_found_notified = true;
                     card_removed_notified = false;
+                    nfc_worker_mf_classic_key_attack(nfc_worker, prev_key, &tx_rx, i);
                 }
                 FURI_LOG_D(
                     TAG,
@@ -600,6 +621,7 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) {
                 }
                 if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break;
             }
+            memcpy(&prev_key, &key, sizeof(key));
         }
         if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break;
         mf_classic_read_sector(&tx_rx, data, i);

+ 38 - 1
lib/nfc/protocols/mifare_classic.c

@@ -155,6 +155,16 @@ void mf_classic_set_key_found(
     }
 }
 
+void mf_classic_set_key_not_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type) {
+    furi_assert(data);
+
+    if(key_type == MfClassicKeyA) {
+        FURI_BIT_CLEAR(data->key_a_mask, sector_num);
+    } else if(key_type == MfClassicKeyB) {
+        FURI_BIT_CLEAR(data->key_b_mask, sector_num);
+    }
+}
+
 bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num) {
     furi_assert(data);
 
@@ -203,6 +213,18 @@ void mf_classic_get_read_sectors_and_keys(
     }
 }
 
+bool mf_classic_is_card_read(MfClassicData* data) {
+    furi_assert(data);
+
+    uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type);
+    uint8_t sectors_read = 0;
+    uint8_t keys_found = 0;
+    mf_classic_get_read_sectors_and_keys(data, &sectors_read, &keys_found);
+    bool card_read = (sectors_read == sectors_total) && (keys_found == sectors_total * 2);
+
+    return card_read;
+}
+
 static bool mf_classic_is_allowed_access_sector_trailer(
     MfClassicEmulator* emulator,
     uint8_t block_num,
@@ -612,7 +634,15 @@ static bool mf_classic_read_sector_with_reader(
         }
 
         // Auth to first block in sector
-        if(!mf_classic_auth(tx_rx, first_block, key, key_type, crypto)) break;
+        if(!mf_classic_auth(tx_rx, first_block, key, key_type, crypto)) {
+            // Set key to MF_CLASSIC_NO_KEY to prevent further attempts
+            if(key_type == MfClassicKeyA) {
+                sector_reader->key_a = MF_CLASSIC_NO_KEY;
+            } else {
+                sector_reader->key_b = MF_CLASSIC_NO_KEY;
+            }
+            break;
+        }
         sector->total_blocks = mf_classic_get_blocks_num_in_sector(sector_reader->sector_num);
 
         // Read blocks
@@ -711,6 +741,13 @@ uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data
                     mf_classic_set_block_read(data, first_block + j, &temp_sector.block[j]);
                 }
                 sectors_read++;
+            } else {
+                // Invalid key, set it to not found
+                if(key_a != MF_CLASSIC_NO_KEY) {
+                    mf_classic_set_key_not_found(data, i, MfClassicKeyA);
+                } else {
+                    mf_classic_set_key_not_found(data, i, MfClassicKeyB);
+                }
             }
         }
     }

+ 4 - 0
lib/nfc/protocols/mifare_classic.h

@@ -98,12 +98,16 @@ void mf_classic_set_key_found(
     MfClassicKey key_type,
     uint64_t key);
 
+void mf_classic_set_key_not_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type);
+
 bool mf_classic_is_block_read(MfClassicData* data, uint8_t block_num);
 
 void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data);
 
 bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num);
 
+bool mf_classic_is_card_read(MfClassicData* data);
+
 void mf_classic_get_read_sectors_and_keys(
     MfClassicData* data,
     uint8_t* sectors_read,