ソースを参照

1.4.0 - Huge update
Changes:
- Changed scenes, now they are more informative and beautiful (closes #6)
- Check if sector is alive in Nested attacks (closes #5)
- Now tag PRNG detected at sector, where key is available (fix not working with dead 0 sector, closes #4)
- Detect hard PRNG from start, hardnested doesn't require calibration now
- Settings menu: ability to always run Hard Nested (regardless of PRNG)
- Minor code refactoring, a lot of bug fixes (memory leaks, stability improvements)
- Fallback to Hard Nested now after 25 failed tries (was 10)

Hope I didn't break everything, but who knows...

AloneLiberty 2 年 前
コミット
3650991342

+ 4 - 10
application.fam

@@ -13,19 +13,13 @@ App(
     fap_icon="assets/icon.png",
     fap_category="NFC",
     fap_private_libs=[
-        Lib(
-            name="nested",
-        ),
-        Lib(
-            name="parity",
-        ),
-        Lib(
-            name="crypto1",
-        )
+        Lib(name="nested"),
+        Lib(name="parity"),
+        Lib(name="crypto1")
     ],
     fap_icon_assets="assets",
     fap_author="AloneLiberty",
     fap_description="Recover Mifare Classic keys",
     fap_weburl="https://github.com/AloneLiberty/FlipperNested",
-    fap_version=(1,3)
+    fap_version=(1, 4)
 )

+ 76 - 18
lib/nested/nested.c

@@ -169,9 +169,42 @@ static int valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, const uint8_t*
                0;
 }
 
-MifareNestedNonceType nested_check_nonce_type(FuriHalNfcTxRxContext* tx_rx) {
+void nonce_distance_notable(uint32_t* msb, uint32_t* lsb) {
+    uint16_t x = 1, pos;
+    uint8_t calc_ok = 0;
+
+    for(uint16_t i = 1; i; ++i) {
+        pos = (x & 0xff) << 8 | x >> 8;
+
+        if((pos == *msb) & !(calc_ok >> 0 & 0x01)) {
+            *msb = i;
+            calc_ok |= 0x01;
+        }
+
+        if((pos == *lsb) & !(calc_ok >> 1 & 0x01)) {
+            *lsb = i;
+            calc_ok |= 0x02;
+        }
+
+        if(calc_ok == 0x03) {
+            return;
+        }
+
+        x = x >> 1 | (x ^ x >> 2 ^ x >> 3 ^ x >> 5) << 15;
+    }
+}
+
+bool validate_prng_nonce_notable(uint32_t nonce) {
+    uint32_t msb = nonce >> 16;
+    uint32_t lsb = nonce & 0xffff;
+    nonce_distance_notable(&msb, &lsb);
+    return ((65535 - msb + lsb) % 65535) == 16;
+}
+
+MifareNestedNonceType nested_check_nonce_type(FuriHalNfcTxRxContext* tx_rx, uint8_t blockNo) {
     uint32_t nonces[5] = {};
-    uint16_t sameNonces = 0;
+    uint8_t sameNonces = 0;
+    uint8_t hardNonces = 0;
     Crypto1 crypt;
     Crypto1* crypto = {&crypt};
 
@@ -181,15 +214,15 @@ MifareNestedNonceType nested_check_nonce_type(FuriHalNfcTxRxContext* tx_rx) {
         furi_hal_nfc_activate_nfca(100, NULL);
 
         // Start communication
-        bool success = mifare_sendcmd_short(crypto, tx_rx, false, 0x60, 0);
+        bool success = mifare_sendcmd_short(crypto, tx_rx, false, 0x60, blockNo);
         if(!success) {
             continue;
         };
 
-        uint32_t byte = (uint32_t)nfc_util_bytes2num(tx_rx->rx_data, 4);
-
-        if(byte == 0) continue;
-        nonces[i] = byte;
+        uint32_t nt = (uint32_t)nfc_util_bytes2num(tx_rx->rx_data, 4);
+        if(nt == 0) continue;
+        if(!validate_prng_nonce_notable(nt)) hardNonces++;
+        nonces[i] = nt;
 
         nfc_deactivate();
     }
@@ -208,9 +241,13 @@ MifareNestedNonceType nested_check_nonce_type(FuriHalNfcTxRxContext* tx_rx) {
 
     if(sameNonces > 3) {
         return MifareNestedNonceStatic;
-    } else {
-        return MifareNestedNonce;
     }
+
+    if(hardNonces > 3) {
+        return MifareNestedNonceHard;
+    }
+
+    return MifareNestedNonceWeak;
 }
 
 struct nonce_info_static nested_static_nonce_attack(
@@ -404,12 +441,10 @@ struct distance_info nested_calibrate_distance_info(
         if(i != 65565) {
             if(rtr != 0) {
                 davg += i;
-                if(i != 0) {
-                    if(dmin == 0) {
-                        dmin = i;
-                    } else {
-                        dmin = MIN(dmin, i);
-                    }
+                if(dmin == 0) {
+                    dmin = i;
+                } else {
+                    dmin = MIN(dmin, i);
                 }
                 dmax = MAX(dmax, i);
             }
@@ -469,7 +504,10 @@ struct nonce_info nested_attack(
 
         while(r.target_nt[i] == 0) { // continue until we have an unambiguous nonce
             nfc_activate();
-            if(!furi_hal_nfc_activate_nfca(200, &cuid)) return r;
+            if(!furi_hal_nfc_activate_nfca(200, &cuid)) {
+                free(crypto);
+                return r;
+            }
 
             r.cuid = cuid;
 
@@ -524,7 +562,7 @@ struct nonce_info nested_attack(
                         break;
                     }
 
-                    FURI_LOG_D(TAG, "Nonce#%lu: valid, ntdist=%li", i + 1, j);
+                    FURI_LOG_D(TAG, "Nonce#%lu: valid, ntdist=%lu", i + 1, j);
                 }
             }
 
@@ -635,7 +673,6 @@ NestedCheckKeyResult nested_check_key(
     uint64_t ui64Key) {
     uint32_t cuid = 0;
     uint32_t nt;
-    Crypto1* crypto = malloc(sizeof(Crypto1));
 
     nfc_activate();
     if(!furi_hal_nfc_activate_nfca(200, &cuid)) return NestedCheckKeyNoTag;
@@ -643,14 +680,35 @@ NestedCheckKeyResult nested_check_key(
     FURI_LOG_D(
         TAG, "Checking %c key %012llX for block %u", !keyType ? 'A' : 'B', ui64Key, blockNo);
 
+    Crypto1* crypto = malloc(sizeof(Crypto1));
+
     bool success =
         mifare_classic_authex(crypto, tx_rx, cuid, blockNo, keyType, ui64Key, false, &nt);
 
+    free(crypto);
+
     nfc_deactivate();
 
     return success ? NestedCheckKeyValid : NestedCheckKeyInvalid;
 }
 
+bool nested_check_block(FuriHalNfcTxRxContext* tx_rx, uint8_t blockNo, uint8_t keyType) {
+    uint32_t cuid = 0;
+
+    nfc_activate();
+    if(!furi_hal_nfc_activate_nfca(200, &cuid)) return false;
+
+    Crypto1* crypto = malloc(sizeof(Crypto1));
+
+    bool success = mifare_sendcmd_short(crypto, tx_rx, false, 0x60 + (keyType & 0x01), blockNo);
+
+    free(crypto);
+
+    nfc_deactivate();
+
+    return success;
+}
+
 void nested_get_data(FuriHalNfcDevData* dev_data) {
     nfc_activate();
     furi_hal_nfc_detect(dev_data, 400);

+ 6 - 4
lib/nested/nested.h

@@ -10,12 +10,12 @@
 
 typedef enum {
     MifareNestedNonceNoTag,
-
-    MifareNestedNonce,
-    MifareNestedNonceStatic
+    MifareNestedNonceWeak,
+    MifareNestedNonceStatic,
+    MifareNestedNonceHard,
 } MifareNestedNonceType;
 
-MifareNestedNonceType nested_check_nonce_type(FuriHalNfcTxRxContext* tx_rx);
+MifareNestedNonceType nested_check_nonce_type(FuriHalNfcTxRxContext* tx_rx, uint8_t blockNo);
 
 struct nonce_info_static {
     uint32_t cuid;
@@ -99,6 +99,8 @@ NestedCheckKeyResult nested_check_key(
     uint8_t keyType,
     uint64_t ui64Key);
 
+bool nested_check_block(FuriHalNfcTxRxContext* tx_rx, uint8_t blockNo, uint8_t keyType);
+
 void nested_get_data();
 
 bool mifare_classic_authex(

+ 22 - 0
mifare_nested.c

@@ -270,6 +270,13 @@ MifareNested* mifare_nested_alloc() {
         MifareNestedViewWidget,
         widget_get_view(mifare_nested->widget));
 
+    // Variable Item List
+    mifare_nested->variable_item_list = variable_item_list_alloc();
+    view_dispatcher_add_view(
+        mifare_nested->view_dispatcher,
+        MifareNestedViewVariableList,
+        variable_item_list_get_view(mifare_nested->variable_item_list));
+
     // Nested attack state
     NestedState* plugin_state = collection_alloc();
     view_set_context(plugin_state->view, mifare_nested);
@@ -287,6 +294,10 @@ MifareNested* mifare_nested_alloc() {
     KeyInfo_t* key_info = malloc(sizeof(KeyInfo_t));
     mifare_nested->keys = key_info;
 
+    MifareNestedSettings* settings = malloc(sizeof(MifareNestedSettings));
+    settings->only_hardnested = false;
+    mifare_nested->settings = settings;
+
     view_set_draw_callback(plugin_state->view, nested_draw_callback);
     view_set_input_callback(plugin_state->view, nested_input_callback);
 
@@ -325,15 +336,26 @@ void mifare_nested_free(MifareNested* mifare_nested) {
     view_dispatcher_remove_view(mifare_nested->view_dispatcher, MifareNestedViewWidget);
     widget_free(mifare_nested->widget);
 
+    // Variable Item List
+    view_dispatcher_remove_view(mifare_nested->view_dispatcher, MifareNestedViewVariableList);
+    variable_item_list_free(mifare_nested->variable_item_list);
+
     // Nested
     view_dispatcher_remove_view(mifare_nested->view_dispatcher, MifareNestedViewCollecting);
 
     // Check keys
     view_dispatcher_remove_view(mifare_nested->view_dispatcher, MifareNestedViewCheckKeys);
 
+    // Nonces states
     free(mifare_nested->nonces);
     free(mifare_nested->nested_state);
 
+    // Keys
+    free(mifare_nested->keys);
+
+    // Settings
+    free(mifare_nested->settings);
+
     // Worker
     mifare_nested_worker_stop(mifare_nested->worker);
     mifare_nested_worker_free(mifare_nested->worker);

+ 11 - 2
mifare_nested_i.h

@@ -17,9 +17,11 @@
 #include <storage/storage.h>
 #include <lib/toolbox/path.h>
 #include <lib/nfc/nfc_device.h>
+#include <lib/toolbox/value_index.h>
+#include <gui/modules/variable_item_list.h>
 #include "mifare_nested_icons.h"
 
-#define NESTED_VERSION_APP "1.3.0"
+#define NESTED_VERSION_APP "1.4.0"
 #define NESTED_GITHUB_LINK "https://github.com/AloneLiberty/FlipperNested"
 #define NESTED_RECOVER_KEYS_GITHUB_LINK "https://github.com/AloneLiberty/FlipperNestedRecovery"
 #define NESTED_NONCE_FORMAT_VERSION "3"
@@ -33,6 +35,7 @@ enum MifareNestedCustomEvent {
     MifareNestedCustomEventWorkerExit,
     MifareNestedCustomEventByteInputDone,
     MifareNestedCustomEventTextInputDone,
+    MifareNestedCustomEventSceneSettingLock
 };
 
 typedef void (*NestedCallback)(void* context);
@@ -67,6 +70,10 @@ typedef struct {
     InputEvent input;
 } PluginEvent;
 
+typedef struct {
+    bool only_hardnested;
+} MifareNestedSettings;
+
 typedef enum { NestedRunIdle, NestedRunCheckKeys, NestedRunAttack } NestedRunNext;
 
 struct MifareNested {
@@ -76,7 +83,8 @@ struct MifareNested {
     NotificationApp* notifications;
     SceneManager* scene_manager;
     NfcDevice* nfc_dev;
-
+    VariableItemList* variable_item_list;
+    MifareNestedSettings* settings;
     FuriString* text_box_store;
 
     // Common Views
@@ -103,6 +111,7 @@ typedef enum {
     MifareNestedViewLoading,
     MifareNestedViewTextInput,
     MifareNestedViewWidget,
+    MifareNestedViewVariableList,
     MifareNestedViewCollecting,
     MifareNestedViewCheckKeys,
 } MifareNestedView;

+ 208 - 111
mifare_nested_worker.c

@@ -79,6 +79,8 @@ int32_t mifare_nested_worker_task(void* context) {
         mifare_nested_worker_collect_nonces_static(mifare_nested_worker);
     } else if(mifare_nested_worker->state == MifareNestedWorkerStateCollecting) {
         mifare_nested_worker_collect_nonces(mifare_nested_worker);
+    } else if(mifare_nested_worker->state == MifareNestedWorkerStateCollectingHard) {
+        mifare_nested_worker_collect_nonces_hard(mifare_nested_worker);
     } else if(mifare_nested_worker->state == MifareNestedWorkerStateValidating) {
         mifare_nested_worker_check_keys(mifare_nested_worker);
     }
@@ -88,46 +90,6 @@ int32_t mifare_nested_worker_task(void* context) {
     return 0;
 }
 
-void mifare_nested_worker_check(MifareNestedWorker* mifare_nested_worker) {
-    bool tag_found_notified = false;
-
-    while(mifare_nested_worker->state == MifareNestedWorkerStateCheck) {
-        FuriHalNfcTxRxContext tx_rx = {};
-        MifareNestedNonceType type = nested_check_nonce_type(&tx_rx);
-
-        if(type == MifareNestedNonceStatic) {
-            mifare_nested_worker->context->collecting_type =
-                MifareNestedWorkerStateCollectingStatic;
-
-            if(!tag_found_notified) {
-                mifare_nested_worker->callback(
-                    MifareNestedWorkerEventCollecting, mifare_nested_worker->context);
-                tag_found_notified = true;
-            }
-
-            mifare_nested_worker->callback(
-                MifareNestedWorkerEventCollecting, mifare_nested_worker->context);
-            break;
-        } else if(type == MifareNestedNonce) {
-            mifare_nested_worker->context->collecting_type = MifareNestedWorkerStateCollecting;
-
-            if(!tag_found_notified) {
-                mifare_nested_worker->callback(
-                    MifareNestedWorkerEventCollecting, mifare_nested_worker->context);
-                tag_found_notified = true;
-            }
-
-            mifare_nested_worker->callback(
-                MifareNestedWorkerEventCollecting, mifare_nested_worker->context);
-            break;
-        }
-
-        furi_delay_ms(250);
-    }
-
-    nfc_deactivate();
-}
-
 void mifare_nested_worker_write_uid_string(FuriHalNfcDevData* data, FuriString* string) {
     uint8_t* uid = data->uid;
     uint8_t uid_len = data->uid_len;
@@ -228,7 +190,7 @@ bool mifare_nested_worker_read_key_cache(FuriHalNfcDevData* data, MfClassicData*
             sector_count = 5;
         } else {
             break;
-        };
+        }
 
         if(!flipper_format_read_hex_uint64(file, "Key A map", &mf_data->key_a_mask, 1)) break;
         if(!flipper_format_read_hex_uint64(file, "Key B map", &mf_data->key_b_mask, 1)) break;
@@ -265,9 +227,9 @@ bool mifare_nested_worker_read_key_cache(FuriHalNfcDevData* data, MfClassicData*
 
 bool hex_char_to_hex_nibble(char c, uint8_t* nibble) {
     if((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) {
-        if((c >= '0' && c <= '9')) {
+        if(c <= '9') {
             *nibble = c - '0';
-        } else if((c >= 'A' && c <= 'F')) {
+        } else if(c <= 'F') {
             *nibble = c - 'A' + 10;
         } else {
             *nibble = c - 'a' + 10;
@@ -290,6 +252,16 @@ bool hex_char_to_uint8(char hi, char low, uint8_t* value) {
     }
 }
 
+void free_nonces(NonceList_t* nonces, uint8_t sector_count, uint8_t tries_count) {
+    for(uint8_t sector = 0; sector < sector_count; sector++) {
+        for(uint8_t key_type = 0; key_type < 2; key_type++) {
+            for(uint8_t tries = 0; tries < tries_count; tries++) {
+                free(nonces->nonces[sector][key_type][tries]);
+            }
+        }
+    }
+}
+
 MfClassicType mifare_nested_worker_get_tag_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) {
     UNUSED(ATQA1);
     if((ATQA0 == 0x44 || ATQA0 == 0x04)) {
@@ -318,7 +290,7 @@ uint32_t mifare_nested_worker_predict_delay(
     Crypto1* crypto = malloc(sizeof(Crypto1));
     uint32_t nt1, nt2, i = 0, previous = 0, prng_delay = 0, zero_prng_value = 65565, repeat = 0;
 
-    if(tries > 10) {
+    if(tries > 25) {
         free(crypto);
         return 2; // Too many tries, fallback to hardnested
     }
@@ -555,7 +527,7 @@ void mifare_nested_worker_write_nonces(
         furi_string_free(str);
     }
 
-    free(nonces);
+    free_nonces(nonces, sector_count, tries_count);
     furi_string_free(path);
     file_stream_close(file_stream);
     free(file_stream);
@@ -633,11 +605,72 @@ bool mifare_nested_worker_check_initial_keys(
         }
     }
 
+    nonces->cuid = 0;
+    nonces->hardnested_states = 0;
+    nonces->sector_count = sector_count;
+    nonces->tries = tries_count;
+
     return *key_block;
 }
 
+void mifare_nested_worker_check(MifareNestedWorker* mifare_nested_worker) {
+    while(mifare_nested_worker->state == MifareNestedWorkerStateCheck) {
+        FuriHalNfcTxRxContext tx_rx = {};
+        NfcDevice* dev = mifare_nested_worker->context->nfc_dev;
+        MfClassicData* mf_data = &dev->dev_data.mf_classic_data;
+        FuriHalNfcDevData data = {};
+        MifareNestedNonceType type = MifareNestedNonceNoTag;
+        nested_get_data(&data);
+
+        if(mifare_nested_worker_read_key_cache(&data, mf_data)) {
+            for(uint8_t sector = 0; sector < 40; sector++) {
+                if(FURI_BIT(mf_data->key_a_mask, sector) ||
+                   FURI_BIT(mf_data->key_b_mask, sector)) {
+                    type = nested_check_nonce_type(
+                        &tx_rx, mifare_nested_worker_get_block_by_sector(sector));
+                    break;
+                }
+            }
+
+            if(type == MifareNestedNonceNoTag) {
+                type = nested_check_nonce_type(&tx_rx, 0);
+            }
+        } else {
+            type = nested_check_nonce_type(&tx_rx, 0);
+        }
+
+        if(type == MifareNestedNonceStatic) {
+            mifare_nested_worker->context->collecting_type =
+                MifareNestedWorkerStateCollectingStatic;
+
+            mifare_nested_worker->callback(
+                MifareNestedWorkerEventCollecting, mifare_nested_worker->context);
+
+            break;
+        } else if(type == MifareNestedNonceWeak) {
+            mifare_nested_worker->context->collecting_type = MifareNestedWorkerStateCollecting;
+
+            mifare_nested_worker->callback(
+                MifareNestedWorkerEventCollecting, mifare_nested_worker->context);
+
+            break;
+        } else if(type == MifareNestedNonceHard) {
+            mifare_nested_worker->context->collecting_type = MifareNestedWorkerStateCollectingHard;
+
+            mifare_nested_worker->callback(
+                MifareNestedWorkerEventCollecting, mifare_nested_worker->context);
+
+            break;
+        }
+
+        furi_delay_ms(250);
+    }
+
+    nfc_deactivate();
+}
+
 void mifare_nested_worker_collect_nonces_static(MifareNestedWorker* mifare_nested_worker) {
-    NonceList_t* nonces = malloc(sizeof(NonceList_t));
+    NonceList_t nonces;
     Storage* storage = furi_record_open(RECORD_STORAGE);
     NfcDevice* dev = mifare_nested_worker->context->nfc_dev;
     MfClassicData* mf_data = &dev->dev_data.mf_classic_data;
@@ -650,7 +683,7 @@ void mifare_nested_worker_collect_nonces_static(MifareNestedWorker* mifare_neste
     uint32_t key_block = 0;
     uint32_t sector_count = 0;
 
-    FURI_LOG_I(TAG, "Running static nested attack");
+    FURI_LOG_I(TAG, "Running Static Nested attack");
     FuriString* tag_info = furi_string_alloc_printf("Tag UID: ");
     mifare_nested_worker_write_uid_string(&data, tag_info);
     FURI_LOG_I(TAG, "%s", furi_string_get_cstr(tag_info));
@@ -662,7 +695,7 @@ void mifare_nested_worker_collect_nonces_static(MifareNestedWorker* mifare_neste
     } else if(type == MfClassicType1k) {
         sector_count = 16;
         FURI_LOG_I(TAG, "Found Mifare Classic 1K tag");
-    } else if(type == MfClassicTypeMini) {
+    } else { // if(type == MfClassicTypeMini)
         sector_count = 5;
         FURI_LOG_I(TAG, "Found Mifare Classic Mini tag");
     }
@@ -677,19 +710,19 @@ void mifare_nested_worker_collect_nonces_static(MifareNestedWorker* mifare_neste
         nfc_deactivate();
 
         free(mf_data);
-        free(nonces);
+        free_nonces(&nonces, sector_count, 1);
 
         return;
     }
 
     if(!mifare_nested_worker_check_initial_keys(
-           nonces, mf_data, 1, sector_count, &key, &key_block, &found_key_type)) {
+           &nonces, mf_data, 1, sector_count, &key, &key_block, &found_key_type)) {
         mifare_nested_worker->callback(
             MifareNestedWorkerEventNeedKey, mifare_nested_worker->context);
         nfc_deactivate();
 
         free(mf_data);
-        free(nonces);
+        free_nonces(&nonces, sector_count, 1);
 
         return;
     }
@@ -697,14 +730,12 @@ void mifare_nested_worker_collect_nonces_static(MifareNestedWorker* mifare_neste
     FURI_LOG_I(
         TAG, "Using %c key for block %lu: %012llX", !found_key_type ? 'A' : 'B', key_block, key);
 
-    nonces->tries = 1;
-
     while(mifare_nested_worker->state == MifareNestedWorkerStateCollectingStatic) {
         FuriHalNfcTxRxContext tx_rx = {};
 
         for(uint8_t sector = 0; sector < sector_count; sector++) {
             for(uint8_t key_type = 0; key_type < 2; key_type++) {
-                Nonces* info = nonces->nonces[sector][key_type][0];
+                Nonces* info = nonces.nonces[sector][key_type][0];
 
                 if(info->collected) {
                     FURI_LOG_I(
@@ -716,11 +747,34 @@ void mifare_nested_worker_collect_nonces_static(MifareNestedWorker* mifare_neste
 
                     info->skipped = true;
 
-                    nonces->nonces[sector][key_type][0] = info;
-                    mifare_nested_worker->context->nonces = nonces;
+                    nonces.nonces[sector][key_type][0] = info;
+
+                    mifare_nested_worker->context->nonces = &nonces;
 
                     mifare_nested_worker->callback(
                         MifareNestedWorkerEventNewNonce, mifare_nested_worker->context);
+                    continue;
+                }
+
+                if(!nested_check_block(
+                       &tx_rx, mifare_nested_worker_get_block_by_sector(sector), key_type)) {
+                    FURI_LOG_E(
+                        TAG,
+                        "Skipping sector %u, block %u, key_type: %u as we can't auth on it",
+                        sector,
+                        mifare_nested_worker_get_block_by_sector(sector),
+                        key_type);
+
+                    info->skipped = true;
+
+                    nonces.nonces[sector][key_type][0] = info;
+
+                    mifare_nested_worker->context->nonces = &nonces;
+
+                    mifare_nested_worker->callback(
+                        MifareNestedWorkerEventNewNonce, mifare_nested_worker->context);
+
+                    continue;
                 }
 
                 while(!info->collected) {
@@ -743,17 +797,17 @@ void mifare_nested_worker_collect_nonces_static(MifareNestedWorker* mifare_neste
                             mifare_nested_worker_get_block_by_sector(sector),
                             key_type);
 
-                        info = nonces->nonces[sector][key_type][0];
+                        info = nonces.nonces[sector][key_type][0];
                         info->collected = true;
 
                         memcpy(&info->target_nt, result.target_nt, sizeof(result.target_nt));
                         memcpy(&info->target_ks, result.target_ks, sizeof(result.target_ks));
 
-                        nonces->nonces[sector][key_type][0] = info;
-                        nonces->cuid = result.cuid;
-                        nonces->sector_count = sector_count;
+                        nonces.nonces[sector][key_type][0] = info;
+                        nonces.cuid = result.cuid;
+                        nonces.sector_count = sector_count;
 
-                        mifare_nested_worker->context->nonces = nonces;
+                        mifare_nested_worker->context->nonces = &nonces;
 
                         mifare_nested_worker->callback(
                             MifareNestedWorkerEventNewNonce, mifare_nested_worker->context);
@@ -769,7 +823,7 @@ void mifare_nested_worker_collect_nonces_static(MifareNestedWorker* mifare_neste
         break;
     }
 
-    mifare_nested_worker_write_nonces(&data, storage, nonces, 1, sector_count, 0, 0);
+    mifare_nested_worker_write_nonces(&data, storage, &nonces, 1, sector_count, 0, 0);
 
     free(mf_data);
 
@@ -780,7 +834,7 @@ void mifare_nested_worker_collect_nonces_static(MifareNestedWorker* mifare_neste
 }
 
 void mifare_nested_worker_collect_nonces_hard(MifareNestedWorker* mifare_nested_worker) {
-    NonceList_t* nonces = malloc(sizeof(NonceList_t));
+    NonceList_t nonces;
     Storage* storage = furi_record_open(RECORD_STORAGE);
     NfcDevice* dev = mifare_nested_worker->context->nfc_dev;
     MfClassicData* mf_data = &dev->dev_data.mf_classic_data;
@@ -795,7 +849,7 @@ void mifare_nested_worker_collect_nonces_hard(MifareNestedWorker* mifare_nested_
     uint32_t cuid = 0;
     furi_hal_nfc_activate_nfca(200, &cuid);
 
-    FURI_LOG_I(TAG, "Running hardnested attack");
+    FURI_LOG_I(TAG, "Running Hard Nested attack");
     FuriString* tag_info = furi_string_alloc_printf("Tag UID: ");
     mifare_nested_worker_write_uid_string(&data, tag_info);
     FURI_LOG_I(TAG, "%s", furi_string_get_cstr(tag_info));
@@ -807,7 +861,7 @@ void mifare_nested_worker_collect_nonces_hard(MifareNestedWorker* mifare_nested_
     } else if(type == MfClassicType1k) {
         sector_count = 16;
         FURI_LOG_I(TAG, "Found Mifare Classic 1K tag");
-    } else if(type == MfClassicTypeMini) {
+    } else { // if(type == MfClassicTypeMini)
         sector_count = 5;
         FURI_LOG_I(TAG, "Found Mifare Classic Mini tag");
     }
@@ -822,19 +876,19 @@ void mifare_nested_worker_collect_nonces_hard(MifareNestedWorker* mifare_nested_
         nfc_deactivate();
 
         free(mf_data);
-        free(nonces);
+        free_nonces(&nonces, sector_count, 1);
 
         return;
     }
 
     if(!mifare_nested_worker_check_initial_keys(
-           nonces, mf_data, 1, sector_count, &key, &key_block, &found_key_type)) {
+           &nonces, mf_data, 1, sector_count, &key, &key_block, &found_key_type)) {
         mifare_nested_worker->callback(
             MifareNestedWorkerEventNeedKey, mifare_nested_worker->context);
         nfc_deactivate();
 
         free(mf_data);
-        free(nonces);
+        free_nonces(&nonces, sector_count, 1);
 
         return;
     }
@@ -843,24 +897,24 @@ void mifare_nested_worker_collect_nonces_hard(MifareNestedWorker* mifare_nested_
         TAG, "Using %c key for block %lu: %012llX", !found_key_type ? 'A' : 'B', key_block, key);
 
     FuriHalNfcTxRxContext tx_rx = {};
-    nonces->tries = 1;
-    nonces->hardnested_states = 0;
-    nonces->sector_count = sector_count;
+    nonces.tries = 1;
+    nonces.hardnested_states = 0;
+    nonces.sector_count = sector_count;
 
-    mifare_nested_worker->context->nonces = nonces;
+    mifare_nested_worker->context->nonces = &nonces;
 
     mifare_nested_worker->callback(MifareNestedWorkerEventNewNonce, mifare_nested_worker->context);
 
     mifare_nested_worker->callback(
         MifareNestedWorkerEventHardnestedStatesFound, mifare_nested_worker->context);
 
-    for(uint8_t sector = 0;
-        sector < sector_count && mifare_nested_worker->state == MifareNestedWorkerStateCollecting;
+    for(uint8_t sector = 0; sector < sector_count &&
+                            mifare_nested_worker->state == MifareNestedWorkerStateCollectingHard;
         sector++) {
         for(uint8_t key_type = 0;
-            key_type < 2 && mifare_nested_worker->state == MifareNestedWorkerStateCollecting;
+            key_type < 2 && mifare_nested_worker->state == MifareNestedWorkerStateCollectingHard;
             key_type++) {
-            Nonces* info = nonces->nonces[sector][key_type][0];
+            Nonces* info = nonces.nonces[sector][key_type][0];
             if(info->collected) {
                 FURI_LOG_I(
                     TAG,
@@ -871,8 +925,28 @@ void mifare_nested_worker_collect_nonces_hard(MifareNestedWorker* mifare_nested_
 
                 info->skipped = true;
 
-                nonces->nonces[sector][key_type][0] = info;
-                mifare_nested_worker->context->nonces = nonces;
+                nonces.nonces[sector][key_type][0] = info;
+                mifare_nested_worker->context->nonces = &nonces;
+
+                mifare_nested_worker->callback(
+                    MifareNestedWorkerEventNewNonce, mifare_nested_worker->context);
+
+                continue;
+            }
+
+            if(!nested_check_block(
+                   &tx_rx, mifare_nested_worker_get_block_by_sector(sector), key_type)) {
+                FURI_LOG_E(
+                    TAG,
+                    "Skipping sector %u, block %u, key_type: %u as we can't auth on it",
+                    sector,
+                    mifare_nested_worker_get_block_by_sector(sector),
+                    key_type);
+
+                info->skipped = true;
+
+                nonces.nonces[sector][key_type][0] = info;
+                mifare_nested_worker->context->nonces = &nonces;
 
                 mifare_nested_worker->callback(
                     MifareNestedWorkerEventNewNonce, mifare_nested_worker->context);
@@ -881,7 +955,7 @@ void mifare_nested_worker_collect_nonces_hard(MifareNestedWorker* mifare_nested_
             }
 
             while(!info->collected &&
-                  mifare_nested_worker->state == MifareNestedWorkerStateCollecting) {
+                  mifare_nested_worker->state == MifareNestedWorkerStateCollectingHard) {
                 Stream* file_stream = file_stream_alloc(storage);
                 FuriString* hardnested_file = furi_string_alloc();
                 mifare_nested_worker_get_hardnested_file_path(
@@ -910,7 +984,7 @@ void mifare_nested_worker_collect_nonces_hard(MifareNestedWorker* mifare_nested_
                     found[i] = 0;
                 }
 
-                while(mifare_nested_worker->state == MifareNestedWorkerStateCollecting) {
+                while(mifare_nested_worker->state == MifareNestedWorkerStateCollectingHard) {
                     struct nonce_info_hard result = nested_hard_nonce_attack(
                         &tx_rx,
                         key_block,
@@ -945,7 +1019,7 @@ void mifare_nested_worker_collect_nonces_hard(MifareNestedWorker* mifare_nested_
                             states += found[i];
                         }
 
-                        mifare_nested_worker->context->nonces->hardnested_states = states;
+                        nonces.hardnested_states = states;
 
                         mifare_nested_worker->callback(
                             MifareNestedWorkerEventHardnestedStatesFound,
@@ -972,11 +1046,11 @@ void mifare_nested_worker_collect_nonces_hard(MifareNestedWorker* mifare_nested_
 
                             info->collected = true;
                             info->hardnested = true;
-                            nonces->cuid = result.cuid;
+                            nonces.cuid = result.cuid;
 
-                            nonces->nonces[sector][key_type][0] = info;
+                            nonces.nonces[sector][key_type][0] = info;
 
-                            mifare_nested_worker->context->nonces = nonces;
+                            mifare_nested_worker->context->nonces = &nonces;
 
                             mifare_nested_worker->callback(
                                 MifareNestedWorkerEventNewNonce, mifare_nested_worker->context);
@@ -996,9 +1070,9 @@ void mifare_nested_worker_collect_nonces_hard(MifareNestedWorker* mifare_nested_
         }
     }
 
-    free(mf_data);
+    mifare_nested_worker_write_nonces(&data, storage, &nonces, 1, sector_count, 0, 0);
 
-    mifare_nested_worker_write_nonces(&data, storage, nonces, 1, sector_count, 0, 0);
+    free(mf_data);
 
     mifare_nested_worker->callback(
         MifareNestedWorkerEventNoncesCollected, mifare_nested_worker->context);
@@ -1007,7 +1081,7 @@ void mifare_nested_worker_collect_nonces_hard(MifareNestedWorker* mifare_nested_
 }
 
 void mifare_nested_worker_collect_nonces(MifareNestedWorker* mifare_nested_worker) {
-    NonceList_t* nonces = malloc(sizeof(NonceList_t));
+    NonceList_t nonces;
     Storage* storage = furi_record_open(RECORD_STORAGE);
     NfcDevice* dev = mifare_nested_worker->context->nfc_dev;
     MfClassicData* mf_data = &dev->dev_data.mf_classic_data;
@@ -1023,7 +1097,7 @@ void mifare_nested_worker_collect_nonces(MifareNestedWorker* mifare_nested_worke
     uint32_t distance = 0;
     uint32_t tries_count = 1;
 
-    FURI_LOG_I(TAG, "Running nested attack");
+    FURI_LOG_I(TAG, "Running Nested attack");
     FuriString* tag_info = furi_string_alloc_printf("Tag UID: ");
     mifare_nested_worker_write_uid_string(&data, tag_info);
     FURI_LOG_I(TAG, "%s", furi_string_get_cstr(tag_info));
@@ -1035,7 +1109,7 @@ void mifare_nested_worker_collect_nonces(MifareNestedWorker* mifare_nested_worke
     } else if(type == MfClassicType1k) {
         sector_count = 16;
         FURI_LOG_I(TAG, "Found Mifare Classic 1K tag");
-    } else if(type == MfClassicTypeMini) {
+    } else { // if(type == MfClassicTypeMini)
         sector_count = 5;
         FURI_LOG_I(TAG, "Found Mifare Classic Mini tag");
     }
@@ -1050,19 +1124,19 @@ void mifare_nested_worker_collect_nonces(MifareNestedWorker* mifare_nested_worke
         nfc_deactivate();
 
         free(mf_data);
-        free(nonces);
+        free_nonces(&nonces, sector_count, tries_count);
 
         return;
     }
 
     if(!mifare_nested_worker_check_initial_keys(
-           nonces, mf_data, 3, sector_count, &key, &key_block, &found_key_type)) {
+           &nonces, mf_data, 3, sector_count, &key, &key_block, &found_key_type)) {
         mifare_nested_worker->callback(
             MifareNestedWorkerEventNeedKey, mifare_nested_worker->context);
         nfc_deactivate();
 
         free(mf_data);
-        free(nonces);
+        free_nonces(&nonces, sector_count, tries_count);
 
         return;
     }
@@ -1094,7 +1168,7 @@ void mifare_nested_worker_collect_nonces(MifareNestedWorker* mifare_nested_worke
             nfc_deactivate();
 
             free(mf_data);
-            free(nonces);
+            free_nonces(&nonces, sector_count, tries_count);
 
             mifare_nested_worker_collect_nonces_hard(mifare_nested_worker);
             return;
@@ -1170,14 +1244,15 @@ void mifare_nested_worker_collect_nonces(MifareNestedWorker* mifare_nested_worke
             }
 
             if(delay == 2) {
-                FURI_LOG_E(TAG, "Can't determine delay in 10 tries, fallback to hardnested");
+                FURI_LOG_E(TAG, "Can't determine delay in 25 tries, fallback to hardnested");
 
                 nfc_deactivate();
 
                 free(mf_data);
-                free(nonces);
+                free_nonces(&nonces, sector_count, tries_count);
 
                 mifare_nested_worker_collect_nonces_hard(mifare_nested_worker);
+                return;
             }
 
             if(mifare_nested_worker->state == MifareNestedWorkerStateCollecting && !failed) {
@@ -1188,17 +1263,17 @@ void mifare_nested_worker_collect_nonces(MifareNestedWorker* mifare_nested_worke
             if(distance == 0 && !failed) {
                 FURI_LOG_E(TAG, "Found delay, but can't find distance");
 
-                mifare_nested_worker->callback(
-                    MifareNestedWorkerEventAttackFailed, mifare_nested_worker->context);
-
                 failed = true;
             }
 
             if(failed) {
                 nfc_deactivate();
 
+                mifare_nested_worker->callback(
+                    MifareNestedWorkerEventAttackFailed, mifare_nested_worker->context);
+
                 free(mf_data);
-                free(nonces);
+                free_nonces(&nonces, sector_count, tries_count);
 
                 return;
             }
@@ -1206,7 +1281,7 @@ void mifare_nested_worker_collect_nonces(MifareNestedWorker* mifare_nested_worke
             tries_count = 3;
         }
 
-        mifare_nested_worker->context->nonces = nonces;
+        mifare_nested_worker->context->nonces = &nonces;
 
         mifare_nested_worker->callback(
             MifareNestedWorkerEventNewNonce, mifare_nested_worker->context);
@@ -1214,7 +1289,7 @@ void mifare_nested_worker_collect_nonces(MifareNestedWorker* mifare_nested_worke
         for(uint8_t tries = 0; tries < tries_count; tries++) {
             for(uint8_t sector = 0; sector < sector_count; sector++) {
                 for(uint8_t key_type = 0; key_type < 2; key_type++) {
-                    Nonces* info = nonces->nonces[sector][key_type][tries];
+                    Nonces* info = nonces.nonces[sector][key_type][tries];
                     if(info->collected) {
                         FURI_LOG_I(
                             TAG,
@@ -1225,11 +1300,33 @@ void mifare_nested_worker_collect_nonces(MifareNestedWorker* mifare_nested_worke
 
                         info->skipped = true;
 
-                        nonces->nonces[sector][key_type][tries] = info;
-                        mifare_nested_worker->context->nonces = nonces;
+                        nonces.nonces[sector][key_type][tries] = info;
+                        mifare_nested_worker->context->nonces = &nonces;
 
                         mifare_nested_worker->callback(
                             MifareNestedWorkerEventNewNonce, mifare_nested_worker->context);
+
+                        continue;
+                    }
+
+                    if(!nested_check_block(
+                           &tx_rx, mifare_nested_worker_get_block_by_sector(sector), key_type)) {
+                        FURI_LOG_E(
+                            TAG,
+                            "Skipping sector %u, block %u, key_type: %u as we can't auth on it",
+                            sector,
+                            mifare_nested_worker_get_block_by_sector(sector),
+                            key_type);
+
+                        info->skipped = true;
+
+                        nonces.nonces[sector][key_type][0] = info;
+                        mifare_nested_worker->context->nonces = &nonces;
+
+                        mifare_nested_worker->callback(
+                            MifareNestedWorkerEventNewNonce, mifare_nested_worker->context);
+
+                        continue;
                     }
 
                     while(!info->collected) {
@@ -1255,19 +1352,19 @@ void mifare_nested_worker_collect_nonces(MifareNestedWorker* mifare_nested_worke
                                 mifare_nested_worker_get_block_by_sector(sector),
                                 key_type);
 
-                            info = nonces->nonces[sector][key_type][tries];
+                            info = nonces.nonces[sector][key_type][tries];
                             info->collected = true;
 
                             memcpy(&info->target_nt, result.target_nt, sizeof(result.target_nt));
                             memcpy(&info->target_ks, result.target_ks, sizeof(result.target_ks));
                             memcpy(&info->parity, result.parity, sizeof(result.parity));
 
-                            nonces->nonces[sector][key_type][tries] = info;
-                            nonces->cuid = result.cuid;
-                            nonces->sector_count = sector_count;
-                            nonces->tries = tries_count;
+                            nonces.nonces[sector][key_type][tries] = info;
+                            nonces.cuid = result.cuid;
+                            nonces.sector_count = sector_count;
+                            nonces.tries = tries_count;
 
-                            mifare_nested_worker->context->nonces = nonces;
+                            mifare_nested_worker->context->nonces = &nonces;
 
                             mifare_nested_worker->callback(
                                 MifareNestedWorkerEventNewNonce, mifare_nested_worker->context);
@@ -1286,7 +1383,7 @@ void mifare_nested_worker_collect_nonces(MifareNestedWorker* mifare_nested_worke
     }
 
     mifare_nested_worker_write_nonces(
-        &data, storage, nonces, tries_count, sector_count, delay, distance);
+        &data, storage, &nonces, tries_count, sector_count, delay, distance);
 
     free(mf_data);
 
@@ -1377,7 +1474,7 @@ void mifare_nested_worker_check_keys(MifareNestedWorker* mifare_nested_worker) {
     } else if(type == MfClassicType1k) {
         sector_count = 16;
         FURI_LOG_I(TAG, "Found Mifare Classic 1K tag");
-    } else if(type == MfClassicTypeMini) {
+    } else { // if(type == MfClassicTypeMini)
         sector_count = 5;
         FURI_LOG_I(TAG, "Found Mifare Classic Mini tag");
     }

+ 1 - 0
mifare_nested_worker.h

@@ -12,6 +12,7 @@ typedef enum {
     MifareNestedWorkerStateCheck,
     MifareNestedWorkerStateCollecting,
     MifareNestedWorkerStateCollectingStatic,
+    MifareNestedWorkerStateCollectingHard,
     MifareNestedWorkerStateValidating,
 
     MifareNestedWorkerStateStop,

+ 2 - 0
mifare_nested_worker_i.h

@@ -23,4 +23,6 @@ void mifare_nested_worker_collect_nonces(MifareNestedWorker* mifare_nested_worke
 
 void mifare_nested_worker_collect_nonces_static(MifareNestedWorker* mifare_nested_worker);
 
+void mifare_nested_worker_collect_nonces_hard(MifareNestedWorker* mifare_nested_worker);
+
 void mifare_nested_worker_check_keys(MifareNestedWorker* mifare_nested_worker);

+ 7 - 1
scenes/mifare_nested_scene_about.c

@@ -24,8 +24,14 @@ void mifare_nested_scene_about_on_enter(void* context) {
         "Ported nested attacks\nfrom proxmark (Iceman fork)\nCurrently supported attacks:\n - nested attack\n - static nested attack\n - hard nested attack\n\n");
     furi_string_cat_printf(
         temp_str,
-        "You will need desktop app to recover keys from collected nonces: %s",
+        "You will need desktop app to recover keys from collected nonces: %s\n\n",
         NESTED_RECOVER_KEYS_GITHUB_LINK);
+    furi_string_cat_printf(temp_str, "\e#%s\n", "Quick guide");
+    furi_string_cat_printf(temp_str, "1. Install key recovery script on PC:\n");
+    furi_string_cat_printf(temp_str, "pip install FlipperNested\n");
+    furi_string_cat_printf(temp_str, "2. Connect Flipper Zero to PC\n");
+    furi_string_cat_printf(temp_str, "3. Run key recovery:\n");
+    furi_string_cat_printf(temp_str, "FlipperNested");
 
     widget_add_text_box_element(
         mifare_nested->widget,

+ 7 - 2
scenes/mifare_nested_scene_added_keys.c

@@ -20,17 +20,22 @@ void mifare_nested_scene_added_keys_on_enter(void* context) {
         append[3] = 's';
     }
 
-    widget_add_string_element(widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "Found keys:");
+    widget_add_string_element(
+        widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "Results of key recovery");
 
     if(key_info->added_keys != 0) {
         snprintf(draw_str, sizeof(draw_str), "Added: %lu %s", key_info->added_keys, append);
         notification_message(mifare_nested->notifications, &sequence_success);
+        widget_add_icon_element(widget, 52, 17, &I_DolphinSuccess);
     } else {
         snprintf(draw_str, sizeof(draw_str), "No new keys were added");
+        widget_add_string_element(
+            widget, 0, 22, AlignLeft, AlignTop, FontSecondary, "Try running \"Nested attack\"");
+        widget_add_string_element(widget, 0, 32, AlignLeft, AlignTop, FontSecondary, "again");
         notification_message(mifare_nested->notifications, &sequence_error);
     }
 
-    widget_add_string_element(widget, 3, 16, AlignLeft, AlignTop, FontPrimary, draw_str);
+    widget_add_string_element(widget, 0, 12, AlignLeft, AlignTop, FontSecondary, draw_str);
     widget_add_button_element(
         widget,
         GuiButtonTypeLeft,

+ 4 - 0
scenes/mifare_nested_scene_check.c

@@ -65,6 +65,10 @@ bool mifare_nested_scene_check_on_event(void* context, SceneManagerEvent event)
             consumed = true;
         } else if(event.event == MifareNestedWorkerEventCollecting) {
             if(mifare_nested->run == NestedRunAttack) {
+                if(mifare_nested->settings->only_hardnested) {
+                    FURI_LOG_I("MifareNested", "Using Hard Nested because user settings");
+                    mifare_nested->collecting_type = MifareNestedWorkerStateCollectingHard;
+                }
                 scene_manager_next_scene(
                     mifare_nested->scene_manager, MifareNestedSceneCollecting);
             } else {

+ 1 - 0
scenes/mifare_nested_scene_config.h

@@ -10,3 +10,4 @@ ADD_SCENE(mifare_nested, about, About)
 ADD_SCENE(mifare_nested, static_encrypted_nonce, StaticEncryptedNonce)
 ADD_SCENE(mifare_nested, need_key_recovery, NeedKeyRecovery)
 ADD_SCENE(mifare_nested, need_collection, NeedCollection)
+ADD_SCENE(mifare_nested, settings, Settings)

+ 6 - 1
scenes/mifare_nested_scene_failed.c

@@ -18,7 +18,12 @@ void mifare_nested_scene_failed_on_enter(void* context) {
 
     widget_add_icon_element(widget, 73, 13, &I_DolphinCry);
     widget_add_string_element(
-        widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "Failed to preform attack");
+        widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "Failed to preform attack");
+    widget_add_string_element(widget, 0, 12, AlignLeft, AlignTop, FontSecondary, "Try running");
+    widget_add_string_element(
+        widget, 0, 22, AlignLeft, AlignTop, FontSecondary, "\"Nested attack\"");
+    widget_add_string_element(widget, 0, 32, AlignLeft, AlignTop, FontSecondary, "again or check");
+    widget_add_string_element(widget, 0, 42, AlignLeft, AlignTop, FontSecondary, "logs");
     widget_add_button_element(
         widget,
         GuiButtonTypeLeft,

+ 3 - 4
scenes/mifare_nested_scene_need_collection.c

@@ -18,10 +18,9 @@ void mifare_nested_scene_need_collection_on_enter(void* context) {
 
     widget_add_icon_element(widget, 73, 13, &I_DolphinCry);
     widget_add_string_element(
-        widget, 0, 4, AlignLeft, AlignTop, FontPrimary, "You need to collect");
-    widget_add_string_element(widget, 0, 16, AlignLeft, AlignTop, FontPrimary, "nonces");
-    widget_add_string_element(widget, 1, 28, AlignLeft, AlignTop, FontPrimary, "Run \"Nested");
-    widget_add_string_element(widget, 1, 40, AlignLeft, AlignTop, FontPrimary, "attack\"");
+        widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "Missing collected nonces");
+    widget_add_string_element(
+        widget, 0, 12, AlignLeft, AlignTop, FontSecondary, "Run \"Nested attack\"");
     widget_add_button_element(
         widget,
         GuiButtonTypeLeft,

+ 6 - 4
scenes/mifare_nested_scene_need_key_recovery.c

@@ -18,10 +18,12 @@ void mifare_nested_scene_need_key_recovery_on_enter(void* context) {
 
     widget_add_icon_element(widget, 74, 13, &I_DolphinCry);
     widget_add_string_element(
-        widget, 1, 4, AlignLeft, AlignTop, FontPrimary, "You need to recover");
-    widget_add_string_element(widget, 1, 16, AlignLeft, AlignTop, FontPrimary, "keys in desktop");
-    widget_add_string_element(widget, 1, 28, AlignLeft, AlignTop, FontPrimary, "app");
-    widget_add_string_element(widget, 1, 40, AlignLeft, AlignTop, FontPrimary, "Check \"About\"");
+        widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "Missing found keys");
+    widget_add_string_element(
+        widget, 0, 12, AlignLeft, AlignTop, FontSecondary, "First you need to");
+    widget_add_string_element(widget, 0, 22, AlignLeft, AlignTop, FontSecondary, "recover keys");
+    widget_add_string_element(widget, 0, 32, AlignLeft, AlignTop, FontSecondary, "Read \"About\"");
+    widget_add_string_element(widget, 0, 42, AlignLeft, AlignTop, FontSecondary, "for more info");
     widget_add_button_element(
         widget,
         GuiButtonTypeLeft,

+ 9 - 3
scenes/mifare_nested_scene_no_keys.c

@@ -17,9 +17,15 @@ void mifare_nested_scene_no_keys_on_enter(void* context) {
     notification_message(mifare_nested->notifications, &sequence_success);
 
     widget_add_icon_element(widget, 73, 13, &I_DolphinCry);
-    widget_add_string_element(widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "Scan tag and find");
-    widget_add_string_element(widget, 3, 16, AlignLeft, AlignTop, FontPrimary, "at least one");
-    widget_add_string_element(widget, 3, 28, AlignLeft, AlignTop, FontPrimary, "key to start");
+    widget_add_string_element(widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "No keys found");
+    widget_add_string_element(
+        widget, 0, 12, AlignLeft, AlignTop, FontSecondary, "Scan tag and find at");
+    widget_add_string_element(
+        widget, 0, 22, AlignLeft, AlignTop, FontSecondary, "least one key to");
+    widget_add_string_element(
+        widget, 0, 32, AlignLeft, AlignTop, FontSecondary, "start (save dump");
+    widget_add_string_element(
+        widget, 0, 42, AlignLeft, AlignTop, FontSecondary, "after scanning!)");
     widget_add_button_element(
         widget,
         GuiButtonTypeLeft,

+ 7 - 2
scenes/mifare_nested_scene_nonces_collected.c

@@ -16,8 +16,13 @@ void mifare_nested_scene_nonces_collected_on_enter(void* context) {
 
     notification_message(mifare_nested->notifications, &sequence_success);
 
-    widget_add_icon_element(widget, 50, 17, &I_DolphinSuccess);
-    widget_add_string_element(widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "Nonces collected!");
+    widget_add_icon_element(widget, 52, 17, &I_DolphinSuccess);
+    widget_add_string_element(widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "Nonces collected");
+    widget_add_string_element(
+        widget, 0, 12, AlignLeft, AlignTop, FontSecondary, "Now you can run");
+    widget_add_string_element(widget, 0, 22, AlignLeft, AlignTop, FontSecondary, "script on your");
+    widget_add_string_element(widget, 0, 32, AlignLeft, AlignTop, FontSecondary, "PC to recover");
+    widget_add_string_element(widget, 0, 42, AlignLeft, AlignTop, FontSecondary, "keys");
     widget_add_button_element(
         widget,
         GuiButtonTypeLeft,

+ 0 - 55
scenes/mifare_nested_scene_run_app.c

@@ -1,55 +0,0 @@
-#include "../mifare_nested_i.h"
-
-void mifare_nested_scene_run_app_widget_callback(
-    GuiButtonType result,
-    InputType type,
-    void* context) {
-    MifareNested* mifare_nested = context;
-    if(type == InputTypeShort) {
-        view_dispatcher_send_custom_event(mifare_nested->view_dispatcher, result);
-    }
-}
-
-void mifare_nested_scene_run_app_on_enter(void* context) {
-    MifareNested* mifare_nested = context;
-    Widget* widget = mifare_nested->widget;
-
-    notification_message(mifare_nested->notifications, &sequence_success);
-
-    widget_add_icon_element(widget, 73, 13, &I_DolphinCry);
-    widget_add_string_element(widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "You need to");
-    widget_add_string_element(widget, 3, 16, AlignLeft, AlignTop, FontPrimary, "recover keys.");
-    widget_add_string_element(widget, 3, 28, AlignLeft, AlignTop, FontPrimary, "Read README");
-    widget_add_button_element(
-        widget,
-        GuiButtonTypeLeft,
-        "Back",
-        mifare_nested_scene_run_app_widget_callback,
-        mifare_nested);
-
-    // Setup and start worker
-    view_dispatcher_switch_to_view(mifare_nested->view_dispatcher, MifareNestedViewWidget);
-}
-
-bool mifare_nested_scene_run_app_on_event(void* context, SceneManagerEvent event) {
-    MifareNested* mifare_nested = context;
-    bool consumed = false;
-
-    if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == GuiButtonTypeCenter || event.event == GuiButtonTypeLeft) {
-            scene_manager_search_and_switch_to_previous_scene(mifare_nested->scene_manager, 0);
-            consumed = true;
-        }
-    } else if(event.type == SceneManagerEventTypeBack) {
-        scene_manager_search_and_switch_to_previous_scene(mifare_nested->scene_manager, 0);
-        consumed = true;
-    }
-
-    return consumed;
-}
-
-void mifare_nested_scene_run_app_on_exit(void* context) {
-    MifareNested* mifare_nested = context;
-
-    widget_reset(mifare_nested->widget);
-}

+ 65 - 0
scenes/mifare_nested_scene_settings.c

@@ -0,0 +1,65 @@
+#include "../mifare_nested_i.h"
+#include <lib/toolbox/value_index.h>
+
+enum MifareNestedSettingsIndex { MifareNestedIndexBlock, MifareNestedIndexHardNested };
+
+#define HARD_NESTED_COUNT 2
+const char* const hard_nested_text[HARD_NESTED_COUNT] = {
+    "No",
+    "Yes",
+};
+
+const bool hard_nested_value[HARD_NESTED_COUNT] = {
+    false,
+    true,
+};
+
+static void mifare_nested_scene_settings_set_hard_nested(VariableItem* item) {
+    MifareNested* mifare_nested = variable_item_get_context(item);
+    uint8_t index = variable_item_get_current_value_index(item);
+
+    variable_item_set_current_value_text(item, hard_nested_text[index]);
+    mifare_nested->settings->only_hardnested = hard_nested_value[index];
+}
+
+void mifare_nested_scene_settings_on_enter(void* context) {
+    MifareNested* mifare_nested = context;
+    VariableItem* item;
+    uint8_t value_index;
+
+    item = variable_item_list_add(
+        mifare_nested->variable_item_list,
+        "Hard Nested only:",
+        HARD_NESTED_COUNT,
+        mifare_nested_scene_settings_set_hard_nested,
+        mifare_nested);
+
+    value_index = value_index_bool(
+        mifare_nested->settings->only_hardnested, hard_nested_value, HARD_NESTED_COUNT);
+
+    variable_item_set_current_value_index(item, value_index);
+    variable_item_set_current_value_text(item, hard_nested_text[value_index]);
+
+    view_dispatcher_switch_to_view(mifare_nested->view_dispatcher, MifareNestedViewVariableList);
+}
+
+bool mifare_nested_scene_settings_on_event(void* context, SceneManagerEvent event) {
+    MifareNested* mifare_nested = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == MifareNestedCustomEventSceneSettingLock) {
+            scene_manager_previous_scene(mifare_nested->scene_manager);
+            consumed = true;
+        }
+    }
+
+    return consumed;
+}
+
+void mifare_nested_scene_settings_on_exit(void* context) {
+    MifareNested* mifare_nested = context;
+    variable_item_list_set_selected_item(mifare_nested->variable_item_list, 0);
+    variable_item_list_reset(mifare_nested->variable_item_list);
+    scene_manager_set_scene_state(mifare_nested->scene_manager, MifareNestedSceneStart, 0);
+}

+ 17 - 1
scenes/mifare_nested_scene_start.c

@@ -1,5 +1,10 @@
 #include "../mifare_nested_i.h"
-enum SubmenuIndex { SubmenuIndexCollect, SubmenuIndexCheck, SubmenuIndexAbout };
+enum SubmenuIndex {
+    SubmenuIndexCollect,
+    SubmenuIndexCheck,
+    SubmenuIndexSettings,
+    SubmenuIndexAbout
+};
 
 void mifare_nested_scene_start_submenu_callback(void* context, uint32_t index) {
     MifareNested* mifare_nested = context;
@@ -24,6 +29,13 @@ void mifare_nested_scene_start_on_enter(void* context) {
         mifare_nested_scene_start_submenu_callback,
         mifare_nested);
 
+    submenu_add_item(
+        submenu,
+        "Settings",
+        SubmenuIndexSettings,
+        mifare_nested_scene_start_submenu_callback,
+        mifare_nested);
+
     submenu_add_item(
         submenu,
         "About",
@@ -50,6 +62,10 @@ bool mifare_nested_scene_start_on_event(void* context, SceneManagerEvent event)
             mifare_nested->run = NestedRunCheckKeys;
             scene_manager_next_scene(mifare_nested->scene_manager, MifareNestedSceneCheck);
             consumed = true;
+        } else if(event.event == SubmenuIndexSettings) {
+            mifare_nested->keys->found_keys = 123;
+            scene_manager_next_scene(mifare_nested->scene_manager, MifareNestedSceneSettings);
+            consumed = true;
         } else if(event.event == SubmenuIndexAbout) {
             scene_manager_next_scene(mifare_nested->scene_manager, MifareNestedSceneAbout);
             consumed = true;

+ 4 - 6
scenes/mifare_nested_scene_static_encrypted_nonce.c

@@ -18,12 +18,10 @@ void mifare_nested_scene_static_encrypted_nonce_on_enter(void* context) {
 
     widget_add_icon_element(widget, 73, 12, &I_DolphinCry);
     widget_add_string_element(
-        widget, 1, 9, AlignLeft, AlignTop, FontPrimary, "Static encrypted nonce");
-    widget_add_string_element(
-        widget, 1, 19, AlignLeft, AlignTop, FontSecondary, "This tag can't be");
-    widget_add_string_element(
-        widget, 1, 29, AlignLeft, AlignTop, FontSecondary, "used with Nested");
-    widget_add_string_element(widget, 1, 39, AlignLeft, AlignTop, FontSecondary, "attack.");
+        widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "Static encrypted nonce");
+    widget_add_string_element(widget, 0, 12, AlignLeft, AlignTop, FontSecondary, "This tag isn't");
+    widget_add_string_element(widget, 0, 22, AlignLeft, AlignTop, FontSecondary, "vulnerable to");
+    widget_add_string_element(widget, 0, 32, AlignLeft, AlignTop, FontSecondary, "Nested attack");
 
     widget_add_button_element(
         widget,