From 55f8beef9f842d1b8a633562330490fe6c55a79c Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Thu, 13 Oct 2022 19:23:29 +0300 Subject: [PATCH] [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 --- lib/nfc/nfc_worker.c | 80 +++++++++++++++++++----------- lib/nfc/protocols/mifare_classic.c | 39 ++++++++++++++- lib/nfc/protocols/mifare_classic.h | 4 ++ 3 files changed, 93 insertions(+), 30 deletions(-) diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 55d67c65..ebe20390 100644 --- a/lib/nfc/nfc_worker.c +++ b/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; + } + 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_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); 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); diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index 78320245..e879ff4e 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/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, §ors_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); + } } } } diff --git a/lib/nfc/protocols/mifare_classic.h b/lib/nfc/protocols/mifare_classic.h index b9921fb1..ead846e4 100644 --- a/lib/nfc/protocols/mifare_classic.h +++ b/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,