Kaynağa Gözat

Add new remote for mfkey

Willy-JL 1 yıl önce
ebeveyn
işleme
fe34b19786

+ 0 - 6
mfkey/.gitignore

@@ -1,6 +0,0 @@
-dist/*
-.vscode
-.clang-format
-.editorconfig
-.env
-.ufbt

+ 0 - 2
mfkey/.gitsubtree

@@ -1,2 +0,0 @@
-https://github.com/xMasterX/all-the-plugins dev base_pack/mfkey
-https://github.com/noproto/FlipperMfkey master fap_plugin

+ 0 - 28
mfkey/application.fam

@@ -1,28 +0,0 @@
-App(
-    appid="mfkey",
-    name="MFKey",
-    apptype=FlipperAppType.EXTERNAL,
-    targets=["f7"],
-    entry_point="mfkey_main",
-    requires=[
-        "gui",
-        "storage",
-    ],
-    stack_size=1 * 1024,
-    fap_icon="mfkey.png",
-    fap_category="NFC",
-    fap_author="@noproto",
-    fap_icon_assets="images",
-    fap_weburl="https://github.com/noproto/FlipperMfkey",
-    fap_description="MIFARE Classic key recovery tool",
-    fap_version="2.7",
-)
-
-App(
-    appid="mfkey_init_plugin",
-    apptype=FlipperAppType.PLUGIN,
-    entry_point="init_plugin_ep",
-    requires=["mfkey"],
-    sources=["init_plugin.c"],
-    fal_embedded=True,
-)

+ 0 - 22
mfkey/crypto1.c

@@ -1,22 +0,0 @@
-#pragma GCC optimize("O3")
-#pragma GCC optimize("-funroll-all-loops")
-
-#include <inttypes.h>
-#include "crypto1.h"
-#include "mfkey.h"
-
-#define BIT(x, n) ((x) >> (n) & 1)
-
-void crypto1_get_lfsr(struct Crypto1State* state, MfClassicKey* lfsr) {
-    int i;
-    uint64_t lfsr_value = 0;
-    for(i = 23; i >= 0; --i) {
-        lfsr_value = lfsr_value << 1 | BIT(state->odd, i ^ 3);
-        lfsr_value = lfsr_value << 1 | BIT(state->even, i ^ 3);
-    }
-
-    // Assign the key value to the MfClassicKey struct
-    for(i = 0; i < 6; ++i) {
-        lfsr->data[i] = (lfsr_value >> ((5 - i) * 8)) & 0xFF;
-    }
-}

+ 0 - 207
mfkey/crypto1.h

@@ -1,207 +0,0 @@
-#ifndef CRYPTO1_H
-#define CRYPTO1_H
-
-#include <inttypes.h>
-#include "mfkey.h"
-#include <nfc/protocols/mf_classic/mf_classic.h>
-
-#define LF_POLY_ODD  (0x29CE5C)
-#define LF_POLY_EVEN (0x870804)
-#define BIT(x, n)    ((x) >> (n) & 1)
-#define BEBIT(x, n)  BIT(x, (n) ^ 24)
-#define SWAPENDIAN(x) \
-    ((x) = ((x) >> 8 & 0xff00ff) | ((x) & 0xff00ff) << 8, (x) = (x) >> 16 | (x) << 16)
-
-static inline uint32_t prng_successor(uint32_t x, uint32_t n);
-static inline int filter(uint32_t const x);
-static inline uint8_t evenparity32(uint32_t x);
-static inline void update_contribution(unsigned int data[], int item, int mask1, int mask2);
-void crypto1_get_lfsr(struct Crypto1State* state, MfClassicKey* lfsr);
-static inline uint32_t crypt_word(struct Crypto1State* s);
-static inline void crypt_word_noret(struct Crypto1State* s, uint32_t in, int x);
-static inline uint32_t crypt_word_ret(struct Crypto1State* s, uint32_t in, int x);
-static inline void rollback_word_noret(struct Crypto1State* s, uint32_t in, int x);
-static inline uint8_t napi_lfsr_rollback_bit(struct Crypto1State* s, uint32_t in, int fb);
-static inline uint32_t napi_lfsr_rollback_word(struct Crypto1State* s, uint32_t in, int fb);
-
-static const uint8_t lookup1[256] = {
-    0, 0,  16, 16, 0,  16, 0,  0,  0, 16, 0,  0,  16, 16, 16, 16, 0, 0,  16, 16, 0,  16, 0,  0,
-    0, 16, 0,  0,  16, 16, 16, 16, 0, 0,  16, 16, 0,  16, 0,  0,  0, 16, 0,  0,  16, 16, 16, 16,
-    8, 8,  24, 24, 8,  24, 8,  8,  8, 24, 8,  8,  24, 24, 24, 24, 8, 8,  24, 24, 8,  24, 8,  8,
-    8, 24, 8,  8,  24, 24, 24, 24, 8, 8,  24, 24, 8,  24, 8,  8,  8, 24, 8,  8,  24, 24, 24, 24,
-    0, 0,  16, 16, 0,  16, 0,  0,  0, 16, 0,  0,  16, 16, 16, 16, 0, 0,  16, 16, 0,  16, 0,  0,
-    0, 16, 0,  0,  16, 16, 16, 16, 8, 8,  24, 24, 8,  24, 8,  8,  8, 24, 8,  8,  24, 24, 24, 24,
-    0, 0,  16, 16, 0,  16, 0,  0,  0, 16, 0,  0,  16, 16, 16, 16, 0, 0,  16, 16, 0,  16, 0,  0,
-    0, 16, 0,  0,  16, 16, 16, 16, 8, 8,  24, 24, 8,  24, 8,  8,  8, 24, 8,  8,  24, 24, 24, 24,
-    8, 8,  24, 24, 8,  24, 8,  8,  8, 24, 8,  8,  24, 24, 24, 24, 0, 0,  16, 16, 0,  16, 0,  0,
-    0, 16, 0,  0,  16, 16, 16, 16, 8, 8,  24, 24, 8,  24, 8,  8,  8, 24, 8,  8,  24, 24, 24, 24,
-    8, 8,  24, 24, 8,  24, 8,  8,  8, 24, 8,  8,  24, 24, 24, 24};
-static const uint8_t lookup2[256] = {
-    0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4,
-    4, 4, 4, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, 2, 2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6,
-    2, 2, 6, 6, 6, 6, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 2, 2, 6, 6, 2, 6, 2,
-    2, 2, 6, 2, 2, 6, 6, 6, 6, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 0, 0, 4, 4,
-    0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 2,
-    2, 6, 6, 2, 6, 2, 2, 2, 6, 2, 2, 6, 6, 6, 6, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4,
-    4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, 2,
-    2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, 2, 2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 6, 2, 2,
-    2, 6, 2, 2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, 2, 2, 6, 6, 6, 6};
-
-static inline int filter(uint32_t const x) {
-    uint32_t f;
-    f = lookup1[x & 0xff] | lookup2[(x >> 8) & 0xff];
-    f |= 0x0d938 >> (x >> 16 & 0xf) & 1;
-    return BIT(0xEC57E80A, f);
-}
-
-#ifndef __ARM_ARCH_7EM__
-static inline uint8_t evenparity32(uint32_t x) {
-    return __builtin_parity(x);
-}
-#endif
-
-#ifdef __ARM_ARCH_7EM__
-static inline uint8_t evenparity32(uint32_t x) {
-    uint32_t result;
-    __asm__ volatile("eor r1, %[x], %[x], lsr #16  \n\t" // r1 = x ^ (x >> 16)
-                     "eor r1, r1, r1, lsr #8       \n\t" // r1 = r1 ^ (r1 >> 8)
-                     "eor r1, r1, r1, lsr #4       \n\t" // r1 = r1 ^ (r1 >> 4)
-                     "eor r1, r1, r1, lsr #2       \n\t" // r1 = r1 ^ (r1 >> 2)
-                     "eor r1, r1, r1, lsr #1       \n\t" // r1 = r1 ^ (r1 >> 1)
-                     "and %[result], r1, #1        \n\t" // result = r1 & 1
-                     : [result] "=r"(result)
-                     : [x] "r"(x)
-                     : "r1");
-    return result;
-}
-#endif
-
-static inline void update_contribution(unsigned int data[], int item, int mask1, int mask2) {
-    int p = data[item] >> 25;
-    p = p << 1 | evenparity32(data[item] & mask1);
-    p = p << 1 | evenparity32(data[item] & mask2);
-    data[item] = p << 24 | (data[item] & 0xffffff);
-}
-
-static inline uint32_t crypt_word(struct Crypto1State* s) {
-    // "in" and "x" are always 0 (last iteration)
-    uint32_t res_ret = 0;
-    uint32_t feedin, t;
-    for(int i = 0; i <= 31; i++) {
-        res_ret |= (filter(s->odd) << (24 ^ i)); //-V629
-        feedin = LF_POLY_EVEN & s->even;
-        feedin ^= LF_POLY_ODD & s->odd;
-        s->even = s->even << 1 | (evenparity32(feedin));
-        t = s->odd, s->odd = s->even, s->even = t;
-    }
-    return res_ret;
-}
-
-static inline void crypt_word_noret(struct Crypto1State* s, uint32_t in, int x) {
-    uint8_t ret;
-    uint32_t feedin, t, next_in;
-    for(int i = 0; i <= 31; i++) {
-        next_in = BEBIT(in, i);
-        ret = filter(s->odd);
-        feedin = ret & (!!x);
-        feedin ^= LF_POLY_EVEN & s->even;
-        feedin ^= LF_POLY_ODD & s->odd;
-        feedin ^= !!next_in;
-        s->even = s->even << 1 | (evenparity32(feedin));
-        t = s->odd, s->odd = s->even, s->even = t;
-    }
-    return;
-}
-
-static inline uint32_t crypt_word_ret(struct Crypto1State* s, uint32_t in, int x) {
-    uint32_t ret = 0;
-    uint32_t feedin, t, next_in;
-    uint8_t next_ret;
-    for(int i = 0; i <= 31; i++) {
-        next_in = BEBIT(in, i);
-        next_ret = filter(s->odd);
-        feedin = next_ret & (!!x);
-        feedin ^= LF_POLY_EVEN & s->even;
-        feedin ^= LF_POLY_ODD & s->odd;
-        feedin ^= !!next_in;
-        s->even = s->even << 1 | (evenparity32(feedin));
-        t = s->odd, s->odd = s->even, s->even = t;
-        ret |= next_ret << (24 ^ i);
-    }
-    return ret;
-}
-
-static inline void rollback_word_noret(struct Crypto1State* s, uint32_t in, int x) {
-    uint8_t ret;
-    uint32_t feedin, t, next_in;
-    for(int i = 31; i >= 0; i--) {
-        next_in = BEBIT(in, i);
-        s->odd &= 0xffffff;
-        t = s->odd, s->odd = s->even, s->even = t;
-        ret = filter(s->odd);
-        feedin = ret & (!!x);
-        feedin ^= s->even & 1;
-        feedin ^= LF_POLY_EVEN & (s->even >>= 1);
-        feedin ^= LF_POLY_ODD & s->odd;
-        feedin ^= !!next_in;
-        s->even |= (evenparity32(feedin)) << 23;
-    }
-    return;
-}
-
-// TODO:
-/*
-uint32_t rollback_word(struct Crypto1State *s, uint32_t in, int x) {
-    uint32_t res_ret = 0;
-    uint8_t ret;
-    uint32_t feedin, t, next_in;
-    for (int i = 31; i >= 0; i--) {
-        next_in = BEBIT(in, i);
-        s->odd &= 0xffffff;
-        t = s->odd, s->odd = s->even, s->even = t;
-        ret = filter(s->odd);
-        feedin = ret & (!!x);
-        feedin ^= s->even & 1;
-        feedin ^= LF_POLY_EVEN & (s->even >>= 1);
-        feedin ^= LF_POLY_ODD & s->odd;
-        feedin ^= !!next_in;
-        s->even |= (evenparity32(feedin)) << 23;
-        res_ret |= (ret << (24 ^ i));
-    }
-    return res_ret;
-}
-*/
-
-uint8_t napi_lfsr_rollback_bit(struct Crypto1State* s, uint32_t in, int fb) {
-    int out;
-    uint8_t ret;
-    uint32_t t;
-    s->odd &= 0xffffff;
-    t = s->odd, s->odd = s->even, s->even = t;
-
-    out = s->even & 1;
-    out ^= LF_POLY_EVEN & (s->even >>= 1);
-    out ^= LF_POLY_ODD & s->odd;
-    out ^= !!in;
-    out ^= (ret = filter(s->odd)) & !!fb;
-
-    s->even |= evenparity32(out) << 23;
-    return ret;
-}
-
-uint32_t napi_lfsr_rollback_word(struct Crypto1State* s, uint32_t in, int fb) {
-    int i;
-    uint32_t ret = 0;
-    for(i = 31; i >= 0; --i)
-        ret |= napi_lfsr_rollback_bit(s, BEBIT(in, i), fb) << (i ^ 24);
-    return ret;
-}
-
-static inline uint32_t prng_successor(uint32_t x, uint32_t n) {
-    SWAPENDIAN(x);
-    while(n--)
-        x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31;
-    return SWAPENDIAN(x);
-}
-
-#endif // CRYPTO1_H

BIN
mfkey/images/mfkey.png


+ 0 - 413
mfkey/init_plugin.c

@@ -1,413 +0,0 @@
-#include <furi_hal.h>
-#include <inttypes.h>
-#include <toolbox/keys_dict.h>
-#include <bit_lib/bit_lib.h>
-#include <toolbox/stream/buffered_file_stream.h>
-#include <nfc/protocols/mf_classic/mf_classic.h>
-#include "mfkey.h"
-#include "crypto1.h"
-#include "plugin_interface.h"
-#include <flipper_application/flipper_application.h>
-
-#define TAG "MFKey"
-
-// TODO: Remove defines that are not needed
-#define KEYS_DICT_SYSTEM_PATH        EXT_PATH("nfc/assets/mf_classic_dict.nfc")
-#define KEYS_DICT_USER_PATH          EXT_PATH("nfc/assets/mf_classic_dict_user.nfc")
-#define MF_CLASSIC_NONCE_PATH        EXT_PATH("nfc/.mfkey32.log")
-#define MF_CLASSIC_NESTED_NONCE_PATH EXT_PATH("nfc/.nested")
-#define MAX_NAME_LEN                 32
-#define MAX_PATH_LEN                 64
-
-#define LF_POLY_ODD  (0x29CE5C)
-#define LF_POLY_EVEN (0x870804)
-#define CONST_M1_1   (LF_POLY_EVEN << 1 | 1)
-#define CONST_M2_1   (LF_POLY_ODD << 1)
-#define CONST_M1_2   (LF_POLY_ODD)
-#define CONST_M2_2   (LF_POLY_EVEN << 1 | 1)
-#define BIT(x, n)    ((x) >> (n) & 1)
-#define BEBIT(x, n)  BIT(x, (n) ^ 24)
-#define SWAPENDIAN(x) \
-    ((x) = ((x) >> 8 & 0xff00ff) | ((x) & 0xff00ff) << 8, (x) = (x) >> 16 | (x) << 16)
-
-bool key_already_found_for_nonce_in_dict(KeysDict* dict, MfClassicNonce* nonce) {
-    bool found = false;
-    uint8_t key_bytes[sizeof(MfClassicKey)];
-    keys_dict_rewind(dict);
-    while(keys_dict_get_next_key(dict, key_bytes, sizeof(MfClassicKey))) {
-        uint64_t k = bit_lib_bytes_to_num_be(key_bytes, sizeof(MfClassicKey));
-        struct Crypto1State temp = {0, 0};
-        for(int i = 0; i < 24; i++) {
-            (&temp)->odd |= (BIT(k, 2 * i + 1) << (i ^ 3));
-            (&temp)->even |= (BIT(k, 2 * i) << (i ^ 3));
-        }
-        if(nonce->attack == mfkey32) {
-            crypt_word_noret(&temp, nonce->uid_xor_nt1, 0);
-            crypt_word_noret(&temp, nonce->nr1_enc, 1);
-            if(nonce->ar1_enc == (crypt_word(&temp) ^ nonce->p64b)) {
-                found = true;
-                break;
-            }
-        } else if(nonce->attack == static_nested) {
-            uint32_t expected_ks1 = crypt_word_ret(&temp, nonce->uid_xor_nt0, 0);
-            if(nonce->ks1_1_enc == expected_ks1) {
-                found = true;
-                break;
-            }
-        }
-    }
-    return found;
-}
-
-bool napi_mf_classic_mfkey32_nonces_check_presence() {
-    Storage* storage = furi_record_open(RECORD_STORAGE);
-
-    bool nonces_present = storage_common_stat(storage, MF_CLASSIC_NONCE_PATH, NULL) == FSE_OK;
-
-    furi_record_close(RECORD_STORAGE);
-
-    return nonces_present;
-}
-
-bool distance_in_nonces_file(const char* file_path, const char* file_name) {
-    char full_path[MAX_PATH_LEN];
-    snprintf(full_path, sizeof(full_path), "%s/%s", file_path, file_name);
-    bool distance_present = false;
-    Storage* storage = furi_record_open(RECORD_STORAGE);
-    Stream* file_stream = buffered_file_stream_alloc(storage);
-    FuriString* line_str;
-    line_str = furi_string_alloc();
-
-    if(buffered_file_stream_open(file_stream, full_path, FSAM_READ, FSOM_OPEN_EXISTING)) {
-        while(true) {
-            if(!stream_read_line(file_stream, line_str)) break;
-            if(furi_string_search_str(line_str, "distance") != FURI_STRING_FAILURE) {
-                distance_present = true;
-                break;
-            }
-        }
-    }
-
-    buffered_file_stream_close(file_stream);
-    stream_free(file_stream);
-    furi_string_free(line_str);
-    furi_record_close(RECORD_STORAGE);
-
-    return distance_present;
-}
-
-bool napi_mf_classic_nested_nonces_check_presence() {
-    Storage* storage = furi_record_open(RECORD_STORAGE);
-
-    if(!(storage_dir_exists(storage, MF_CLASSIC_NESTED_NONCE_PATH))) {
-        furi_record_close(RECORD_STORAGE);
-        return false;
-    }
-
-    bool nonces_present = false;
-    File* dir = storage_file_alloc(storage);
-    char filename_buffer[MAX_NAME_LEN];
-    FileInfo file_info;
-
-    if(storage_dir_open(dir, MF_CLASSIC_NESTED_NONCE_PATH)) {
-        while(storage_dir_read(dir, &file_info, filename_buffer, MAX_NAME_LEN)) {
-            // We only care about Static Nested files
-            if(!(file_info.flags & FSF_DIRECTORY) && strstr(filename_buffer, ".nonces") &&
-               !(distance_in_nonces_file(MF_CLASSIC_NESTED_NONCE_PATH, filename_buffer))) {
-                nonces_present = true;
-                break;
-            }
-        }
-    }
-
-    storage_dir_close(dir);
-    storage_file_free(dir);
-    furi_record_close(RECORD_STORAGE);
-
-    return nonces_present;
-}
-
-int binaryStringToInt(const char* binStr) {
-    int result = 0;
-    while(*binStr) {
-        result <<= 1;
-        if(*binStr == '1') {
-            result |= 1;
-        }
-        binStr++;
-    }
-    return result;
-}
-
-bool load_mfkey32_nonces(
-    MfClassicNonceArray* nonce_array,
-    ProgramState* program_state,
-    KeysDict* system_dict,
-    bool system_dict_exists,
-    KeysDict* user_dict) {
-    bool array_loaded = false;
-
-    do {
-        // https://github.com/flipperdevices/flipperzero-firmware/blob/5134f44c09d39344a8747655c0d59864bb574b96/applications/services/storage/filesystem_api_defines.h#L8-L22
-        if(!buffered_file_stream_open(
-               nonce_array->stream, MF_CLASSIC_NONCE_PATH, FSAM_READ_WRITE, FSOM_OPEN_EXISTING)) {
-            buffered_file_stream_close(nonce_array->stream);
-            break;
-        }
-
-        // Check for newline ending
-        if(!stream_eof(nonce_array->stream)) {
-            if(!stream_seek(nonce_array->stream, -1, StreamOffsetFromEnd)) break;
-            uint8_t last_char = 0;
-            if(stream_read(nonce_array->stream, &last_char, 1) != 1) break;
-            if(last_char != '\n') {
-                //FURI_LOG_D(TAG, "Adding new line ending");
-                if(stream_write_char(nonce_array->stream, '\n') != 1) break;
-            }
-            if(!stream_rewind(nonce_array->stream)) break;
-        }
-
-        // Read total amount of nonces
-        FuriString* next_line;
-        next_line = furi_string_alloc();
-        while(!(program_state->close_thread_please)) {
-            if(!stream_read_line(nonce_array->stream, next_line)) {
-                //FURI_LOG_T(TAG, "No nonces left");
-                break;
-            }
-            /*
-            FURI_LOG_T(
-                TAG,
-                "Read line: %s, len: %zu",
-                furi_string_get_cstr(next_line),
-                furi_string_size(next_line));
-            */
-            if(!furi_string_start_with_str(next_line, "Sec")) continue;
-            const char* next_line_cstr = furi_string_get_cstr(next_line);
-            MfClassicNonce res = {0};
-            res.attack = mfkey32;
-            int i = 0;
-            char* endptr;
-            for(i = 0; i <= 17; i++) {
-                if(i != 0) {
-                    next_line_cstr = strchr(next_line_cstr, ' ');
-                    if(next_line_cstr) {
-                        next_line_cstr++;
-                    } else {
-                        break;
-                    }
-                }
-                unsigned long value = strtoul(next_line_cstr, &endptr, 16);
-                switch(i) {
-                case 5:
-                    res.uid = value;
-                    break;
-                case 7:
-                    res.nt0 = value;
-                    break;
-                case 9:
-                    res.nr0_enc = value;
-                    break;
-                case 11:
-                    res.ar0_enc = value;
-                    break;
-                case 13:
-                    res.nt1 = value;
-                    break;
-                case 15:
-                    res.nr1_enc = value;
-                    break;
-                case 17:
-                    res.ar1_enc = value;
-                    break;
-                default:
-                    break; // Do nothing
-                }
-                next_line_cstr = endptr;
-            }
-            res.p64 = prng_successor(res.nt0, 64);
-            res.p64b = prng_successor(res.nt1, 64);
-            res.uid_xor_nt0 = res.uid ^ res.nt0;
-            res.uid_xor_nt1 = res.uid ^ res.nt1;
-
-            (program_state->total)++;
-            if((system_dict_exists && key_already_found_for_nonce_in_dict(system_dict, &res)) ||
-               (key_already_found_for_nonce_in_dict(user_dict, &res))) {
-                (program_state->cracked)++;
-                (program_state->num_completed)++;
-                continue;
-            }
-            //FURI_LOG_I(TAG, "No key found for %8lx %8lx", res.uid, res.ar1_enc);
-            // TODO: Refactor
-            nonce_array->remaining_nonce_array = realloc( //-V701
-                nonce_array->remaining_nonce_array,
-                sizeof(MfClassicNonce) * ((nonce_array->remaining_nonces) + 1));
-            nonce_array->remaining_nonces++;
-            nonce_array->remaining_nonce_array[(nonce_array->remaining_nonces) - 1] = res;
-            nonce_array->total_nonces++;
-        }
-        furi_string_free(next_line);
-        buffered_file_stream_close(nonce_array->stream);
-        //stream_free(nonce_array->stream);
-
-        array_loaded = true;
-        //FURI_LOG_I(TAG, "Loaded %lu Mfkey32 nonces", nonce_array->total_nonces);
-    } while(false);
-
-    return array_loaded;
-}
-
-bool load_nested_nonces(
-    MfClassicNonceArray* nonce_array,
-    ProgramState* program_state,
-    KeysDict* system_dict,
-    bool system_dict_exists,
-    KeysDict* user_dict) {
-    Storage* storage = furi_record_open(RECORD_STORAGE);
-    File* dir = storage_file_alloc(storage);
-    char filename_buffer[MAX_NAME_LEN];
-    FileInfo file_info;
-    FuriString* next_line = furi_string_alloc();
-
-    if(!storage_dir_open(dir, MF_CLASSIC_NESTED_NONCE_PATH)) {
-        storage_dir_close(dir);
-        storage_file_free(dir);
-        furi_record_close(RECORD_STORAGE);
-        furi_string_free(next_line);
-        return false;
-    }
-
-    while(storage_dir_read(dir, &file_info, filename_buffer, MAX_NAME_LEN)) {
-        if(!(file_info.flags & FSF_DIRECTORY) && strstr(filename_buffer, ".nonces") &&
-           !(distance_in_nonces_file(MF_CLASSIC_NESTED_NONCE_PATH, filename_buffer))) {
-            char full_path[MAX_PATH_LEN];
-            snprintf(
-                full_path,
-                sizeof(full_path),
-                "%s/%s",
-                MF_CLASSIC_NESTED_NONCE_PATH,
-                filename_buffer);
-
-            // TODO: We should only need READ_WRITE here if we plan on adding a newline to the end of the file if has none
-            if(!buffered_file_stream_open(
-                   nonce_array->stream, full_path, FSAM_READ_WRITE, FSOM_OPEN_EXISTING)) {
-                buffered_file_stream_close(nonce_array->stream);
-                continue;
-            }
-
-            while(stream_read_line(nonce_array->stream, next_line)) {
-                if(furi_string_search_str(next_line, "Nested:") != FURI_STRING_FAILURE) {
-                    MfClassicNonce res = {0};
-                    res.attack = static_nested;
-                    int parsed = sscanf(
-                        furi_string_get_cstr(next_line),
-                        "Nested: %*s %*s cuid 0x%" PRIx32 " nt0 0x%" PRIx32 " ks0 0x%" PRIx32
-                        " par0 %4[01] nt1 0x%" PRIx32 " ks1 0x%" PRIx32 " par1 %4[01]",
-                        &res.uid,
-                        &res.nt0,
-                        &res.ks1_1_enc,
-                        res.par_1_str,
-                        &res.nt1,
-                        &res.ks1_2_enc,
-                        res.par_2_str);
-
-                    if(parsed != 7) continue;
-                    res.par_1 = binaryStringToInt(res.par_1_str);
-                    res.par_2 = binaryStringToInt(res.par_2_str);
-                    res.uid_xor_nt0 = res.uid ^ res.nt0;
-                    res.uid_xor_nt1 = res.uid ^ res.nt1;
-
-                    (program_state->total)++;
-                    if((system_dict_exists &&
-                        key_already_found_for_nonce_in_dict(system_dict, &res)) ||
-                       (key_already_found_for_nonce_in_dict(user_dict, &res))) {
-                        (program_state->cracked)++;
-                        (program_state->num_completed)++;
-                        continue;
-                    }
-
-                    nonce_array->remaining_nonce_array = realloc(
-                        nonce_array->remaining_nonce_array,
-                        sizeof(MfClassicNonce) * (nonce_array->remaining_nonces + 1));
-                    nonce_array->remaining_nonce_array[nonce_array->remaining_nonces] = res;
-                    nonce_array->remaining_nonces++;
-                    nonce_array->total_nonces++;
-                }
-            }
-
-            buffered_file_stream_close(nonce_array->stream);
-        }
-    }
-
-    storage_dir_close(dir);
-    storage_file_free(dir);
-    furi_record_close(RECORD_STORAGE);
-    furi_string_free(next_line);
-
-    //FURI_LOG_I(TAG, "Loaded %lu Static Nested nonces", nonce_array->total_nonces);
-    return true;
-}
-
-MfClassicNonceArray* napi_mf_classic_nonce_array_alloc(
-    KeysDict* system_dict,
-    bool system_dict_exists,
-    KeysDict* user_dict,
-    ProgramState* program_state) {
-    MfClassicNonceArray* nonce_array = malloc(sizeof(MfClassicNonceArray));
-    MfClassicNonce* remaining_nonce_array_init = malloc(sizeof(MfClassicNonce) * 1);
-    nonce_array->remaining_nonce_array = remaining_nonce_array_init;
-    Storage* storage = furi_record_open(RECORD_STORAGE);
-    nonce_array->stream = buffered_file_stream_alloc(storage);
-    furi_record_close(RECORD_STORAGE);
-
-    bool array_loaded = false;
-
-    if(program_state->mfkey32_present) {
-        array_loaded = load_mfkey32_nonces(
-            nonce_array, program_state, system_dict, system_dict_exists, user_dict);
-    }
-
-    if(program_state->nested_present) {
-        array_loaded |= load_nested_nonces(
-            nonce_array, program_state, system_dict, system_dict_exists, user_dict);
-    }
-
-    if(!array_loaded) {
-        free(nonce_array);
-        nonce_array = NULL;
-    }
-
-    return nonce_array;
-}
-
-void napi_mf_classic_nonce_array_free(MfClassicNonceArray* nonce_array) {
-    // TODO: Track free state at the time this is called to ensure double free does not happen
-    furi_assert(nonce_array);
-    furi_assert(nonce_array->stream);
-
-    buffered_file_stream_close(nonce_array->stream);
-    stream_free(nonce_array->stream);
-    free(nonce_array);
-}
-
-/* Actual implementation of app<>plugin interface */
-static const MfkeyPlugin init_plugin = {
-    .name = "Initialization Plugin",
-    .napi_mf_classic_mfkey32_nonces_check_presence =
-        &napi_mf_classic_mfkey32_nonces_check_presence,
-    .napi_mf_classic_nested_nonces_check_presence = &napi_mf_classic_nested_nonces_check_presence,
-    .napi_mf_classic_nonce_array_alloc = &napi_mf_classic_nonce_array_alloc,
-    .napi_mf_classic_nonce_array_free = &napi_mf_classic_nonce_array_free,
-};
-
-/* Plugin descriptor to comply with basic plugin specification */
-static const FlipperAppPluginDescriptor init_plugin_descriptor = {
-    .appid = PLUGIN_APP_ID,
-    .ep_api_version = PLUGIN_API_VERSION,
-    .entry_point = &init_plugin,
-};
-
-/* Plugin entry point - must return a pointer to const descriptor  */
-const FlipperAppPluginDescriptor* init_plugin_ep() {
-    return &init_plugin_descriptor;
-}

+ 0 - 882
mfkey/mfkey.c

@@ -1,882 +0,0 @@
-#pragma GCC optimize("O3")
-#pragma GCC optimize("-funroll-all-loops")
-
-// TODO: Add keys to top of the user dictionary, not the bottom
-// TODO: More efficient dictionary bruteforce by scanning through hardcoded very common keys and previously found dictionary keys first?
-//       (a cache for key_already_found_for_nonce_in_dict)
-// TODO: Selectively unroll loops to reduce binary size
-// TODO: Collect parity during Mfkey32 attacks to further optimize the attack
-// TODO: Why different sscanf between Mfkey32 and Nested?
-// TODO: "Read tag again with NFC app" message upon completion, "Complete. Keys added: <n>"
-// TODO: Separate Mfkey32 and Nested functions where possible to reduce branch statements
-// TODO: More accurate timing for Nested
-// TODO: Find ~1 KB memory leak
-
-#include <furi.h>
-#include <furi_hal.h>
-#include <gui/gui.h>
-#include <gui/elements.h>
-#include "mfkey_icons.h"
-#include <inttypes.h>
-#include <toolbox/keys_dict.h>
-#include <bit_lib/bit_lib.h>
-#include <toolbox/stream/buffered_file_stream.h>
-#include <dolphin/dolphin.h>
-#include <notification/notification_messages.h>
-#include <nfc/protocols/mf_classic/mf_classic.h>
-#include "mfkey.h"
-#include "crypto1.h"
-#include "plugin_interface.h"
-#include <flipper_application/flipper_application.h>
-#include <loader/firmware_api/firmware_api.h>
-#include <storage/storage.h>
-
-#define TAG "MFKey"
-
-// TODO: Remove defines that are not needed
-#define KEYS_DICT_SYSTEM_PATH        EXT_PATH("nfc/assets/mf_classic_dict.nfc")
-#define KEYS_DICT_USER_PATH          EXT_PATH("nfc/assets/mf_classic_dict_user.nfc")
-#define MF_CLASSIC_NONCE_PATH        EXT_PATH("nfc/.mfkey32.log")
-#define MF_CLASSIC_NESTED_NONCE_PATH EXT_PATH("nfc/.nested")
-#define MAX_NAME_LEN                 32
-#define MAX_PATH_LEN                 64
-
-#define LF_POLY_ODD  (0x29CE5C)
-#define LF_POLY_EVEN (0x870804)
-#define CONST_M1_1   (LF_POLY_EVEN << 1 | 1)
-#define CONST_M2_1   (LF_POLY_ODD << 1)
-#define CONST_M1_2   (LF_POLY_ODD)
-#define CONST_M2_2   (LF_POLY_EVEN << 1 | 1)
-#define BIT(x, n)    ((x) >> (n) & 1)
-#define BEBIT(x, n)  BIT(x, (n) ^ 24)
-#define SWAPENDIAN(x) \
-    ((x) = ((x) >> 8 & 0xff00ff) | ((x) & 0xff00ff) << 8, (x) = (x) >> 16 | (x) << 16)
-//#define SIZEOF(arr) sizeof(arr) / sizeof(*arr)
-
-static int eta_round_time = 44;
-static int eta_total_time = 705;
-// MSB_LIMIT: Chunk size (out of 256)
-static int MSB_LIMIT = 16;
-
-int check_state(struct Crypto1State* t, MfClassicNonce* n) {
-    if(!(t->odd | t->even)) return 0;
-    if(n->attack == mfkey32) {
-        uint32_t rb = (napi_lfsr_rollback_word(t, 0, 0) ^ n->p64);
-        if(rb != n->ar0_enc) {
-            return 0;
-        }
-        rollback_word_noret(t, n->nr0_enc, 1);
-        rollback_word_noret(t, n->uid_xor_nt0, 0);
-        struct Crypto1State temp = {t->odd, t->even};
-        crypt_word_noret(t, n->uid_xor_nt1, 0);
-        crypt_word_noret(t, n->nr1_enc, 1);
-        if(n->ar1_enc == (crypt_word(t) ^ n->p64b)) {
-            crypto1_get_lfsr(&temp, &(n->key));
-            return 1;
-        }
-        return 0;
-    } else if(n->attack == static_nested) {
-        struct Crypto1State temp = {t->odd, t->even};
-        rollback_word_noret(t, n->uid_xor_nt1, 0);
-        if(n->ks1_1_enc == crypt_word_ret(t, n->uid_xor_nt0, 0)) {
-            rollback_word_noret(&temp, n->uid_xor_nt1, 0);
-            crypto1_get_lfsr(&temp, &(n->key));
-            return 1;
-        }
-        return 0;
-    }
-    return 0;
-}
-
-static inline int state_loop(
-    unsigned int* states_buffer,
-    int xks,
-    int m1,
-    int m2,
-    unsigned int in,
-    uint8_t and_val) {
-    int states_tail = 0;
-    int round = 0, s = 0, xks_bit = 0, round_in = 0;
-
-    for(round = 1; round <= 12; round++) {
-        xks_bit = BIT(xks, round);
-        if(round > 4) {
-            round_in = ((in >> (2 * (round - 4))) & and_val) << 24;
-        }
-
-        for(s = 0; s <= states_tail; s++) {
-            states_buffer[s] <<= 1;
-
-            if((filter(states_buffer[s]) ^ filter(states_buffer[s] | 1)) != 0) {
-                states_buffer[s] |= filter(states_buffer[s]) ^ xks_bit;
-                if(round > 4) {
-                    update_contribution(states_buffer, s, m1, m2);
-                    states_buffer[s] ^= round_in;
-                }
-            } else if(filter(states_buffer[s]) == xks_bit) {
-                // TODO: Refactor
-                if(round > 4) {
-                    states_buffer[++states_tail] = states_buffer[s + 1];
-                    states_buffer[s + 1] = states_buffer[s] | 1;
-                    update_contribution(states_buffer, s, m1, m2);
-                    states_buffer[s++] ^= round_in;
-                    update_contribution(states_buffer, s, m1, m2);
-                    states_buffer[s] ^= round_in;
-                } else {
-                    states_buffer[++states_tail] = states_buffer[++s];
-                    states_buffer[s] = states_buffer[s - 1] | 1;
-                }
-            } else {
-                states_buffer[s--] = states_buffer[states_tail--];
-            }
-        }
-    }
-
-    return states_tail;
-}
-
-int binsearch(unsigned int data[], int start, int stop) {
-    int mid, val = data[stop] & 0xff000000;
-    while(start != stop) {
-        mid = (stop - start) >> 1;
-        if((data[start + mid] ^ 0x80000000) > (val ^ 0x80000000))
-            stop = start + mid;
-        else
-            start += mid + 1;
-    }
-    return start;
-}
-void quicksort(unsigned int array[], int low, int high) {
-    //if (SIZEOF(array) == 0)
-    //    return;
-    if(low >= high) return;
-    int middle = low + (high - low) / 2;
-    unsigned int pivot = array[middle];
-    int i = low, j = high;
-    while(i <= j) {
-        while(array[i] < pivot) {
-            i++;
-        }
-        while(array[j] > pivot) {
-            j--;
-        }
-        if(i <= j) { // swap
-            int temp = array[i];
-            array[i] = array[j];
-            array[j] = temp;
-            i++;
-            j--;
-        }
-    }
-    if(low < j) {
-        quicksort(array, low, j);
-    }
-    if(high > i) {
-        quicksort(array, i, high);
-    }
-}
-int extend_table(unsigned int data[], int tbl, int end, int bit, int m1, int m2, unsigned int in) {
-    in <<= 24;
-    for(data[tbl] <<= 1; tbl <= end; data[++tbl] <<= 1) {
-        if((filter(data[tbl]) ^ filter(data[tbl] | 1)) != 0) {
-            data[tbl] |= filter(data[tbl]) ^ bit;
-            update_contribution(data, tbl, m1, m2);
-            data[tbl] ^= in;
-        } else if(filter(data[tbl]) == bit) {
-            data[++end] = data[tbl + 1];
-            data[tbl + 1] = data[tbl] | 1;
-            update_contribution(data, tbl, m1, m2);
-            data[tbl++] ^= in;
-            update_contribution(data, tbl, m1, m2);
-            data[tbl] ^= in;
-        } else {
-            data[tbl--] = data[end--];
-        }
-    }
-    return end;
-}
-
-int old_recover(
-    unsigned int odd[],
-    int o_head,
-    int o_tail,
-    int oks,
-    unsigned int even[],
-    int e_head,
-    int e_tail,
-    int eks,
-    int rem,
-    int s,
-    MfClassicNonce* n,
-    unsigned int in,
-    int first_run) {
-    int o, e, i;
-    if(rem == -1) {
-        for(e = e_head; e <= e_tail; ++e) {
-            even[e] = (even[e] << 1) ^ evenparity32(even[e] & LF_POLY_EVEN) ^ (!!(in & 4));
-            for(o = o_head; o <= o_tail; ++o, ++s) {
-                struct Crypto1State temp = {0, 0};
-                temp.even = odd[o];
-                temp.odd = even[e] ^ evenparity32(odd[o] & LF_POLY_ODD);
-                if(check_state(&temp, n)) {
-                    return -1;
-                }
-            }
-        }
-        return s;
-    }
-    if(first_run == 0) {
-        for(i = 0; (i < 4) && (rem-- != 0); i++) {
-            oks >>= 1;
-            eks >>= 1;
-            in >>= 2;
-            o_tail = extend_table(
-                odd, o_head, o_tail, oks & 1, LF_POLY_EVEN << 1 | 1, LF_POLY_ODD << 1, 0);
-            if(o_head > o_tail) return s;
-            e_tail = extend_table(
-                even, e_head, e_tail, eks & 1, LF_POLY_ODD, LF_POLY_EVEN << 1 | 1, in & 3);
-            if(e_head > e_tail) return s;
-        }
-    }
-    first_run = 0;
-    quicksort(odd, o_head, o_tail);
-    quicksort(even, e_head, e_tail);
-    while(o_tail >= o_head && e_tail >= e_head) {
-        if(((odd[o_tail] ^ even[e_tail]) >> 24) == 0) {
-            o_tail = binsearch(odd, o_head, o = o_tail);
-            e_tail = binsearch(even, e_head, e = e_tail);
-            s = old_recover(
-                odd, o_tail--, o, oks, even, e_tail--, e, eks, rem, s, n, in, first_run);
-            if(s == -1) {
-                break;
-            }
-        } else if((odd[o_tail] ^ 0x80000000) > (even[e_tail] ^ 0x80000000)) {
-            o_tail = binsearch(odd, o_head, o_tail) - 1;
-        } else {
-            e_tail = binsearch(even, e_head, e_tail) - 1;
-        }
-    }
-    return s;
-}
-
-static inline int sync_state(ProgramState* program_state) {
-    int ts = furi_hal_rtc_get_timestamp();
-    int elapsed_time = ts - program_state->eta_timestamp;
-    if(elapsed_time < program_state->eta_round) {
-        program_state->eta_round -= elapsed_time;
-    } else {
-        program_state->eta_round = 0;
-    }
-    if(elapsed_time < program_state->eta_total) {
-        program_state->eta_total -= elapsed_time;
-    } else {
-        program_state->eta_total = 0;
-    }
-    program_state->eta_timestamp = ts;
-    if(program_state->close_thread_please) {
-        return 1;
-    }
-    return 0;
-}
-
-int calculate_msb_tables(
-    int oks,
-    int eks,
-    int msb_round,
-    MfClassicNonce* n,
-    unsigned int* states_buffer,
-    struct Msb* odd_msbs,
-    struct Msb* even_msbs,
-    unsigned int* temp_states_odd,
-    unsigned int* temp_states_even,
-    unsigned int in,
-    ProgramState* program_state) {
-    //FURI_LOG_I(TAG, "MSB GO %i", msb_iter); // DEBUG
-    unsigned int msb_head = (MSB_LIMIT * msb_round); // msb_iter ranges from 0 to (256/MSB_LIMIT)-1
-    unsigned int msb_tail = (MSB_LIMIT * (msb_round + 1));
-    int states_tail = 0, tail = 0;
-    int i = 0, j = 0, semi_state = 0, found = 0;
-    unsigned int msb = 0;
-    in = ((in >> 16 & 0xff) | (in << 16) | (in & 0xff00)) << 1;
-    // TODO: Why is this necessary?
-    memset(odd_msbs, 0, MSB_LIMIT * sizeof(struct Msb));
-    memset(even_msbs, 0, MSB_LIMIT * sizeof(struct Msb));
-
-    for(semi_state = 1 << 20; semi_state >= 0; semi_state--) {
-        if(semi_state % 32768 == 0) {
-            if(sync_state(program_state) == 1) {
-                return 0;
-            }
-        }
-
-        if(filter(semi_state) == (oks & 1)) { //-V547
-            states_buffer[0] = semi_state;
-            states_tail = state_loop(states_buffer, oks, CONST_M1_1, CONST_M2_1, 0, 0);
-
-            for(i = states_tail; i >= 0; i--) {
-                msb = states_buffer[i] >> 24;
-                if((msb >= msb_head) && (msb < msb_tail)) {
-                    found = 0;
-                    for(j = 0; j < odd_msbs[msb - msb_head].tail - 1; j++) {
-                        if(odd_msbs[msb - msb_head].states[j] == states_buffer[i]) {
-                            found = 1;
-                            break;
-                        }
-                    }
-
-                    if(!found) {
-                        tail = odd_msbs[msb - msb_head].tail++;
-                        odd_msbs[msb - msb_head].states[tail] = states_buffer[i];
-                    }
-                }
-            }
-        }
-
-        if(filter(semi_state) == (eks & 1)) { //-V547
-            states_buffer[0] = semi_state;
-            states_tail = state_loop(states_buffer, eks, CONST_M1_2, CONST_M2_2, in, 3);
-
-            for(i = 0; i <= states_tail; i++) {
-                msb = states_buffer[i] >> 24;
-                if((msb >= msb_head) && (msb < msb_tail)) {
-                    found = 0;
-
-                    for(j = 0; j < even_msbs[msb - msb_head].tail; j++) {
-                        if(even_msbs[msb - msb_head].states[j] == states_buffer[i]) {
-                            found = 1;
-                            break;
-                        }
-                    }
-
-                    if(!found) {
-                        tail = even_msbs[msb - msb_head].tail++;
-                        even_msbs[msb - msb_head].states[tail] = states_buffer[i];
-                    }
-                }
-            }
-        }
-    }
-
-    oks >>= 12;
-    eks >>= 12;
-
-    for(i = 0; i < MSB_LIMIT; i++) {
-        if(sync_state(program_state) == 1) {
-            return 0;
-        }
-        // TODO: Why is this necessary?
-        memset(temp_states_even, 0, sizeof(unsigned int) * (1280));
-        memset(temp_states_odd, 0, sizeof(unsigned int) * (1280));
-        memcpy(temp_states_odd, odd_msbs[i].states, odd_msbs[i].tail * sizeof(unsigned int));
-        memcpy(temp_states_even, even_msbs[i].states, even_msbs[i].tail * sizeof(unsigned int));
-        int res = old_recover(
-            temp_states_odd,
-            0,
-            odd_msbs[i].tail,
-            oks,
-            temp_states_even,
-            0,
-            even_msbs[i].tail,
-            eks,
-            3,
-            0,
-            n,
-            in >> 16,
-            1);
-        if(res == -1) {
-            return 1;
-        }
-        //odd_msbs[i].tail = 0;
-        //even_msbs[i].tail = 0;
-    }
-
-    return 0;
-}
-
-void** allocate_blocks(const size_t* block_sizes, int num_blocks) {
-    void** block_pointers = malloc(num_blocks * sizeof(void*));
-    if(block_pointers == NULL) {
-        return NULL;
-    }
-
-    for(int i = 0; i < num_blocks; i++) {
-        if(memmgr_heap_get_max_free_block() < block_sizes[i]) {
-            // Not enough memory, free previously allocated blocks
-            for(int j = 0; j < i; j++) {
-                free(block_pointers[j]);
-            }
-            free(block_pointers);
-            return NULL;
-        }
-
-        block_pointers[i] = malloc(block_sizes[i]);
-        if(block_pointers[i] == NULL) {
-            // Allocation failed, free previously allocated blocks
-            for(int j = 0; j < i; j++) {
-                free(block_pointers[j]);
-            }
-            free(block_pointers);
-            return NULL;
-        }
-    }
-
-    return block_pointers;
-}
-
-bool is_full_speed() {
-    return MSB_LIMIT == 16;
-}
-
-bool recover(MfClassicNonce* n, int ks2, unsigned int in, ProgramState* program_state) {
-    bool found = false;
-    const size_t block_sizes[] = {49216, 49216, 5120, 5120, 4096};
-    const size_t reduced_block_sizes[] = {24608, 24608, 5120, 5120, 4096};
-    const int num_blocks = sizeof(block_sizes) / sizeof(block_sizes[0]);
-    void** block_pointers = allocate_blocks(block_sizes, num_blocks);
-    if(block_pointers == NULL) {
-        // System has less than the guaranteed amount of RAM (140 KB) - adjust some parameters to run anyway at half speed
-        if(is_full_speed()) {
-            //eta_round_time *= 2;
-            eta_total_time *= 2;
-            MSB_LIMIT /= 2;
-        }
-        block_pointers = allocate_blocks(reduced_block_sizes, num_blocks);
-        if(block_pointers == NULL) {
-            // System has less than 70 KB of RAM - should never happen so we don't reduce speed further
-            program_state->err = InsufficientRAM;
-            program_state->mfkey_state = Error;
-            return false;
-        }
-    }
-    struct Msb* odd_msbs = block_pointers[0];
-    struct Msb* even_msbs = block_pointers[1];
-    unsigned int* temp_states_odd = block_pointers[2];
-    unsigned int* temp_states_even = block_pointers[3];
-    unsigned int* states_buffer = block_pointers[4];
-    int oks = 0, eks = 0;
-    int i = 0, msb = 0;
-    for(i = 31; i >= 0; i -= 2) {
-        oks = oks << 1 | BEBIT(ks2, i);
-    }
-    for(i = 30; i >= 0; i -= 2) {
-        eks = eks << 1 | BEBIT(ks2, i);
-    }
-    int bench_start = furi_hal_rtc_get_timestamp();
-    program_state->eta_total = eta_total_time;
-    program_state->eta_timestamp = bench_start;
-    for(msb = 0; msb <= ((256 / MSB_LIMIT) - 1); msb++) {
-        program_state->search = msb;
-        program_state->eta_round = eta_round_time;
-        program_state->eta_total = eta_total_time - (eta_round_time * msb);
-        if(calculate_msb_tables(
-               oks,
-               eks,
-               msb,
-               n,
-               states_buffer,
-               odd_msbs,
-               even_msbs,
-               temp_states_odd,
-               temp_states_even,
-               in,
-               program_state)) {
-            //int bench_stop = furi_hal_rtc_get_timestamp();
-            //FURI_LOG_I(TAG, "Cracked in %i seconds", bench_stop - bench_start);
-            found = true;
-            break;
-        }
-        if(program_state->close_thread_please) {
-            break;
-        }
-    }
-    // Free the allocated blocks
-    for(int i = 0; i < num_blocks; i++) {
-        free(block_pointers[i]);
-    }
-    free(block_pointers);
-    return found;
-}
-
-bool key_already_found_for_nonce_in_solved(
-    MfClassicKey* keyarray,
-    int keyarray_size,
-    MfClassicNonce* nonce) {
-    for(int k = 0; k < keyarray_size; k++) {
-        uint64_t key_as_int = bit_lib_bytes_to_num_be(keyarray[k].data, sizeof(MfClassicKey));
-        struct Crypto1State temp = {0, 0};
-        for(int i = 0; i < 24; i++) {
-            (&temp)->odd |= (BIT(key_as_int, 2 * i + 1) << (i ^ 3));
-            (&temp)->even |= (BIT(key_as_int, 2 * i) << (i ^ 3));
-        }
-        if(nonce->attack == mfkey32) {
-            crypt_word_noret(&temp, nonce->uid_xor_nt1, 0);
-            crypt_word_noret(&temp, nonce->nr1_enc, 1);
-            if(nonce->ar1_enc == (crypt_word(&temp) ^ nonce->p64b)) {
-                return true;
-            }
-        } else if(nonce->attack == static_nested) {
-            uint32_t expected_ks1 = crypt_word_ret(&temp, nonce->uid_xor_nt0, 0);
-            if(nonce->ks1_1_enc == expected_ks1) {
-                return true;
-            }
-        }
-    }
-    return false;
-}
-
-#pragma GCC push_options
-#pragma GCC optimize("Os")
-static void finished_beep() {
-    // Beep to indicate completion
-    NotificationApp* notification = furi_record_open("notification");
-    notification_message(notification, &sequence_audiovisual_alert);
-    notification_message(notification, &sequence_display_backlight_on);
-    furi_record_close("notification");
-}
-
-void mfkey(ProgramState* program_state) {
-    MfClassicKey found_key; // recovered key
-    size_t keyarray_size = 0;
-    MfClassicKey* keyarray = malloc(sizeof(MfClassicKey) * 1);
-    uint32_t i = 0, j = 0;
-    //FURI_LOG_I(TAG, "Free heap before alloc(): %zub", memmgr_get_free_heap());
-    Storage* storage = furi_record_open(RECORD_STORAGE);
-    FlipperApplication* app = flipper_application_alloc(storage, firmware_api_interface);
-    flipper_application_preload(app, APP_ASSETS_PATH("plugins/mfkey_init_plugin.fal"));
-    flipper_application_map_to_memory(app);
-    const FlipperAppPluginDescriptor* app_descriptor =
-        flipper_application_plugin_get_descriptor(app);
-    const MfkeyPlugin* init_plugin = app_descriptor->entry_point;
-    // Check for nonces
-    program_state->mfkey32_present = init_plugin->napi_mf_classic_mfkey32_nonces_check_presence();
-    program_state->nested_present = init_plugin->napi_mf_classic_nested_nonces_check_presence();
-    if(!(program_state->mfkey32_present) && !(program_state->nested_present)) {
-        program_state->err = MissingNonces;
-        program_state->mfkey_state = Error;
-        flipper_application_free(app);
-        furi_record_close(RECORD_STORAGE);
-        free(keyarray);
-        return;
-    }
-    // Read dictionaries (optional)
-    KeysDict* system_dict = {0};
-    bool system_dict_exists = keys_dict_check_presence(KEYS_DICT_SYSTEM_PATH);
-    KeysDict* user_dict = {0};
-    bool user_dict_exists = keys_dict_check_presence(KEYS_DICT_USER_PATH);
-    uint32_t total_dict_keys = 0;
-    if(system_dict_exists) {
-        system_dict =
-            keys_dict_alloc(KEYS_DICT_SYSTEM_PATH, KeysDictModeOpenExisting, sizeof(MfClassicKey));
-        total_dict_keys += keys_dict_get_total_keys(system_dict);
-    }
-    user_dict = keys_dict_alloc(KEYS_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));
-    if(user_dict_exists) {
-        total_dict_keys += keys_dict_get_total_keys(user_dict);
-    }
-    user_dict_exists = true;
-    program_state->dict_count = total_dict_keys;
-    program_state->mfkey_state = DictionaryAttack;
-    // Read nonces
-    MfClassicNonceArray* nonce_arr;
-    nonce_arr = init_plugin->napi_mf_classic_nonce_array_alloc(
-        system_dict, system_dict_exists, user_dict, program_state);
-    if(system_dict_exists) {
-        keys_dict_free(system_dict);
-    }
-    if(nonce_arr->total_nonces == 0) {
-        // Nothing to crack
-        program_state->err = ZeroNonces;
-        program_state->mfkey_state = Error;
-        init_plugin->napi_mf_classic_nonce_array_free(nonce_arr);
-        flipper_application_free(app);
-        furi_record_close(RECORD_STORAGE);
-        keys_dict_free(user_dict);
-        free(keyarray);
-        return;
-    }
-    flipper_application_free(app);
-    furi_record_close(RECORD_STORAGE);
-    // TODO: Track free state at the time this is called to ensure double free does not happen
-    furi_assert(nonce_arr);
-    furi_assert(nonce_arr->stream);
-    buffered_file_stream_close(nonce_arr->stream);
-    stream_free(nonce_arr->stream);
-    //FURI_LOG_I(TAG, "Free heap after free(): %zub", memmgr_get_free_heap());
-    program_state->mfkey_state = MFKeyAttack;
-    // TODO: Work backwards on this array and free memory
-    for(i = 0; i < nonce_arr->total_nonces; i++) {
-        MfClassicNonce next_nonce = nonce_arr->remaining_nonce_array[i];
-        if(key_already_found_for_nonce_in_solved(keyarray, keyarray_size, &next_nonce)) {
-            nonce_arr->remaining_nonces--;
-            (program_state->cracked)++;
-            (program_state->num_completed)++;
-            continue;
-        }
-        //FURI_LOG_I(TAG, "Beginning recovery for %8lx", next_nonce.uid);
-        if(next_nonce.attack == mfkey32) {
-            if(!recover(&next_nonce, next_nonce.ar0_enc ^ next_nonce.p64, 0, program_state)) {
-                if(program_state->close_thread_please) {
-                    break;
-                }
-                // No key found in recover()
-                (program_state->num_completed)++;
-                continue;
-            }
-        } else if(next_nonce.attack == static_nested) {
-            if(!recover(
-                   &next_nonce,
-                   next_nonce.ks1_2_enc,
-                   next_nonce.nt1 ^ next_nonce.uid,
-                   program_state)) {
-                if(program_state->close_thread_please) {
-                    break;
-                }
-                // No key found in recover()
-                (program_state->num_completed)++;
-                continue;
-            }
-        }
-        (program_state->cracked)++;
-        (program_state->num_completed)++;
-        found_key = next_nonce.key;
-        bool already_found = false;
-        for(j = 0; j < keyarray_size; j++) {
-            if(memcmp(keyarray[j].data, found_key.data, MF_CLASSIC_KEY_SIZE) == 0) {
-                already_found = true;
-                break;
-            }
-        }
-        if(already_found == false) {
-            // New key
-            keyarray = realloc(keyarray, sizeof(MfClassicKey) * (keyarray_size + 1)); //-V701
-            keyarray_size += 1;
-            keyarray[keyarray_size - 1] = found_key;
-            (program_state->unique_cracked)++;
-        }
-    }
-    // TODO: Update display to show all keys were found
-    // TODO: Prepend found key(s) to user dictionary file
-    //FURI_LOG_I(TAG, "Unique keys found:");
-    for(i = 0; i < keyarray_size; i++) {
-        //FURI_LOG_I(TAG, "%012" PRIx64, keyarray[i]);
-        keys_dict_add_key(user_dict, keyarray[i].data, sizeof(MfClassicKey));
-    }
-    if(keyarray_size > 0) {
-        dolphin_deed(DolphinDeedNfcMfcAdd);
-    }
-    free(nonce_arr);
-    keys_dict_free(user_dict);
-    free(keyarray);
-    if(program_state->mfkey_state == Error) {
-        return;
-    }
-    //FURI_LOG_I(TAG, "mfkey function completed normally"); // DEBUG
-    program_state->mfkey_state = Complete;
-    // No need to alert the user if they asked it to stop
-    if(!(program_state->close_thread_please)) {
-        finished_beep();
-    }
-    return;
-}
-
-// Screen is 128x64 px
-static void render_callback(Canvas* const canvas, void* ctx) {
-    furi_assert(ctx);
-    ProgramState* program_state = ctx;
-    furi_mutex_acquire(program_state->mutex, FuriWaitForever);
-    char draw_str[44] = {};
-    canvas_clear(canvas);
-    canvas_draw_frame(canvas, 0, 0, 128, 64);
-    canvas_draw_frame(canvas, 0, 15, 128, 64);
-    snprintf(draw_str, sizeof(draw_str), "RAM: %zub", memmgr_get_free_heap());
-    canvas_draw_str_aligned(canvas, 48, 5, AlignLeft, AlignTop, draw_str);
-    canvas_draw_icon(canvas, 114, 4, &I_mfkey);
-    if(program_state->mfkey_state == MFKeyAttack) {
-        float eta_round = (float)1 - ((float)program_state->eta_round / (float)eta_round_time);
-        float eta_total = (float)1 - ((float)program_state->eta_total / (float)eta_total_time);
-        float progress = (float)program_state->num_completed / (float)program_state->total;
-        if(eta_round < 0 || eta_round > 1) {
-            // Round ETA miscalculated
-            eta_round = 1;
-            program_state->eta_round = 0;
-        }
-        if(eta_total < 0 || eta_total > 1) {
-            // Total ETA miscalculated
-            eta_total = 1;
-            program_state->eta_total = 0;
-        }
-        snprintf(
-            draw_str,
-            sizeof(draw_str),
-            "Cracking: %d/%d - in prog.",
-            program_state->num_completed,
-            program_state->total);
-        elements_progress_bar_with_text(canvas, 5, 18, 118, progress, draw_str);
-        snprintf(
-            draw_str,
-            sizeof(draw_str),
-            "Round: %d/%d - ETA %02d Sec",
-            (program_state->search) + 1, // Zero indexed
-            256 / MSB_LIMIT,
-            program_state->eta_round);
-        elements_progress_bar_with_text(canvas, 5, 31, 118, eta_round, draw_str);
-        snprintf(draw_str, sizeof(draw_str), "Total ETA %03d Sec", program_state->eta_total);
-        elements_progress_bar_with_text(canvas, 5, 44, 118, eta_total, draw_str);
-    } else if(program_state->mfkey_state == DictionaryAttack) {
-        snprintf(
-            draw_str, sizeof(draw_str), "Dict solves: %d (in progress)", program_state->cracked);
-        canvas_draw_str_aligned(canvas, 10, 18, AlignLeft, AlignTop, draw_str);
-        snprintf(draw_str, sizeof(draw_str), "Keys in dict: %d", program_state->dict_count);
-        canvas_draw_str_aligned(canvas, 26, 28, AlignLeft, AlignTop, draw_str);
-    } else if(program_state->mfkey_state == Complete) {
-        // TODO: Scrollable list view to see cracked keys if user presses down
-        elements_progress_bar(canvas, 5, 18, 118, 1);
-        canvas_set_font(canvas, FontSecondary);
-        snprintf(draw_str, sizeof(draw_str), "Complete");
-        canvas_draw_str_aligned(canvas, 40, 31, AlignLeft, AlignTop, draw_str);
-        snprintf(
-            draw_str,
-            sizeof(draw_str),
-            "Keys added to user dict: %d",
-            program_state->unique_cracked);
-        canvas_draw_str_aligned(canvas, 10, 41, AlignLeft, AlignTop, draw_str);
-    } else if(program_state->mfkey_state == Ready) {
-        canvas_draw_str_aligned(canvas, 50, 30, AlignLeft, AlignTop, "Ready");
-        elements_button_center(canvas, "Start");
-        elements_button_right(canvas, "Help");
-    } else if(program_state->mfkey_state == Help) {
-        canvas_draw_str_aligned(canvas, 7, 20, AlignLeft, AlignTop, "Collect nonces using Detect");
-        canvas_draw_str_aligned(canvas, 7, 30, AlignLeft, AlignTop, "Reader or FlipperNested.");
-        canvas_draw_str_aligned(canvas, 7, 40, AlignLeft, AlignTop, "Devs: noproto, AG, ALiberty");
-        canvas_draw_str_aligned(canvas, 7, 50, AlignLeft, AlignTop, "Thanks: bettse, Foxushka");
-    } else if(program_state->mfkey_state == Error) {
-        canvas_draw_str_aligned(canvas, 50, 25, AlignLeft, AlignTop, "Error");
-        if(program_state->err == MissingNonces) {
-            canvas_draw_str_aligned(canvas, 25, 36, AlignLeft, AlignTop, "No nonces found");
-        } else if(program_state->err == ZeroNonces) {
-            canvas_draw_str_aligned(canvas, 15, 36, AlignLeft, AlignTop, "Nonces already cracked");
-        } else if(program_state->err == InsufficientRAM) {
-            canvas_draw_str_aligned(canvas, 35, 36, AlignLeft, AlignTop, "No free RAM");
-        } else {
-            // Unhandled error
-        }
-    } else {
-        // Unhandled program state
-    }
-    canvas_set_font(canvas, FontPrimary);
-    canvas_draw_str_aligned(canvas, 5, 4, AlignLeft, AlignTop, "MFKey");
-    furi_mutex_release(program_state->mutex);
-}
-
-static void input_callback(InputEvent* input_event, void* event_queue) {
-    furi_assert(event_queue);
-
-    PluginEvent event = {.type = EventTypeKey, .input = *input_event};
-    furi_message_queue_put((FuriMessageQueue*)event_queue, &event, FuriWaitForever);
-}
-
-static void mfkey_state_init(ProgramState* program_state) {
-    program_state->mfkey_state = Ready;
-    program_state->cracked = 0;
-    program_state->unique_cracked = 0;
-    program_state->num_completed = 0;
-    program_state->total = 0;
-    program_state->dict_count = 0;
-}
-
-// Entrypoint for worker thread
-static int32_t mfkey_worker_thread(void* ctx) {
-    ProgramState* program_state = ctx;
-    program_state->mfkey_state = Initializing;
-    //FURI_LOG_I(TAG, "Hello from the mfkey worker thread"); // DEBUG
-    mfkey(program_state);
-    return 0;
-}
-
-int32_t mfkey_main() {
-    FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
-
-    ProgramState* program_state = malloc(sizeof(ProgramState));
-
-    mfkey_state_init(program_state);
-
-    program_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
-    if(!program_state->mutex) {
-        //FURI_LOG_E(TAG, "cannot create mutex\r\n");
-        free(program_state);
-        return 255;
-    }
-
-    // Set system callbacks
-    ViewPort* view_port = view_port_alloc();
-    view_port_draw_callback_set(view_port, render_callback, program_state);
-    view_port_input_callback_set(view_port, input_callback, event_queue);
-
-    // Open GUI and register view_port
-    Gui* gui = furi_record_open(RECORD_GUI);
-    gui_add_view_port(gui, view_port, GuiLayerFullscreen);
-
-    program_state->mfkeythread =
-        furi_thread_alloc_ex("MFKey Worker", 2048, mfkey_worker_thread, program_state);
-
-    PluginEvent event;
-    for(bool main_loop = true; main_loop;) {
-        FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
-
-        furi_mutex_acquire(program_state->mutex, FuriWaitForever);
-
-        if(event_status == FuriStatusOk) {
-            // press events
-            if(event.type == EventTypeKey) {
-                if(event.input.type == InputTypePress) {
-                    switch(event.input.key) {
-                    case InputKeyUp:
-                        break;
-                    case InputKeyDown:
-                        break;
-                    case InputKeyRight:
-                        if(program_state->mfkey_state == Ready) {
-                            program_state->mfkey_state = Help;
-                        }
-                        break;
-                    case InputKeyLeft:
-                        break;
-                    case InputKeyOk:
-                        if(program_state->mfkey_state == Ready) {
-                            furi_thread_start(program_state->mfkeythread);
-                        }
-                        break;
-                    case InputKeyBack:
-                        if(program_state->mfkey_state == Help) {
-                            program_state->mfkey_state = Ready;
-                        } else {
-                            program_state->close_thread_please = true;
-                            // Wait until thread is finished
-                            furi_thread_join(program_state->mfkeythread);
-                            main_loop = false;
-                        }
-                        break;
-                    default:
-                        break;
-                    }
-                }
-            }
-        }
-
-        furi_mutex_release(program_state->mutex);
-        view_port_update(view_port);
-    }
-
-    // Thread joined in back event handler
-    furi_thread_free(program_state->mfkeythread);
-    view_port_enabled_set(view_port, false);
-    gui_remove_view_port(gui, view_port);
-    furi_record_close("gui");
-    view_port_free(view_port);
-    furi_message_queue_free(event_queue);
-    furi_mutex_free(program_state->mutex);
-    free(program_state);
-
-    return 0;
-}
-#pragma GCC pop_options

+ 0 - 116
mfkey/mfkey.h

@@ -1,116 +0,0 @@
-#ifndef MFKEY_H
-#define MFKEY_H
-
-#include <furi_hal.h>
-#include <gui/gui.h>
-#include <gui/elements.h>
-#include <inttypes.h>
-#include <toolbox/keys_dict.h>
-#include <toolbox/stream/buffered_file_stream.h>
-#include <nfc/protocols/mf_classic/mf_classic.h>
-
-struct Crypto1State {
-    uint32_t odd, even;
-};
-struct Msb {
-    int tail;
-    uint32_t states[768];
-};
-
-typedef enum {
-    EventTypeTick,
-    EventTypeKey,
-} EventType;
-
-typedef struct {
-    EventType type;
-    InputEvent input;
-} PluginEvent;
-
-typedef enum {
-    MissingNonces,
-    ZeroNonces,
-    InsufficientRAM,
-} MFKeyError;
-
-typedef enum {
-    Ready,
-    Initializing,
-    DictionaryAttack,
-    MFKeyAttack,
-    Complete,
-    Error,
-    Help,
-} MFKeyState;
-
-// TODO: Can we eliminate any of the members of this struct?
-typedef struct {
-    FuriMutex* mutex;
-    MFKeyError err;
-    MFKeyState mfkey_state;
-    int cracked;
-    int unique_cracked;
-    int num_completed;
-    int total;
-    int dict_count;
-    int search;
-    int eta_timestamp;
-    int eta_total;
-    int eta_round;
-    bool mfkey32_present;
-    bool nested_present;
-    bool is_thread_running;
-    bool close_thread_please;
-    FuriThread* mfkeythread;
-} ProgramState;
-
-typedef enum {
-    mfkey32,
-    static_nested
-} AttackType;
-
-typedef struct {
-    AttackType attack;
-    MfClassicKey key; // key
-    uint32_t uid; // serial number
-    uint32_t nt0; // tag challenge first
-    uint32_t nt1; // tag challenge second
-    uint32_t uid_xor_nt0; // uid ^ nt0
-    uint32_t uid_xor_nt1; // uid ^ nt1
-    union {
-        // Mfkey32
-        struct {
-            uint32_t p64; // 64th successor of nt0
-            uint32_t p64b; // 64th successor of nt1
-            uint32_t nr0_enc; // first encrypted reader challenge
-            uint32_t ar0_enc; // first encrypted reader response
-            uint32_t nr1_enc; // second encrypted reader challenge
-            uint32_t ar1_enc; // second encrypted reader response
-        };
-        // Nested
-        struct {
-            uint32_t ks1_1_enc; // first encrypted keystream
-            uint32_t ks1_2_enc; // second encrypted keystream
-            char par_1_str[5]; // first parity bits (string representation)
-            char par_2_str[5]; // second parity bits (string representation)
-            uint8_t par_1; // first parity bits
-            uint8_t par_2; // second parity bits
-        };
-    };
-} MfClassicNonce;
-
-typedef struct {
-    Stream* stream;
-    uint32_t total_nonces;
-    MfClassicNonce* remaining_nonce_array;
-    size_t remaining_nonces;
-} MfClassicNonceArray;
-
-struct KeysDict {
-    Stream* stream;
-    size_t key_size;
-    size_t key_size_symbols;
-    size_t total_keys;
-};
-
-#endif // MFKEY_H

BIN
mfkey/mfkey.png


+ 0 - 13
mfkey/plugin_interface.h

@@ -1,13 +0,0 @@
-#pragma once
-
-#define PLUGIN_APP_ID      "mfkey"
-#define PLUGIN_API_VERSION 1
-
-typedef struct {
-    const char* name;
-    bool (*napi_mf_classic_mfkey32_nonces_check_presence)();
-    bool (*napi_mf_classic_nested_nonces_check_presence)();
-    MfClassicNonceArray* (
-        *napi_mf_classic_nonce_array_alloc)(KeysDict*, bool, KeysDict*, ProgramState*);
-    void (*napi_mf_classic_nonce_array_free)(MfClassicNonceArray*);
-} MfkeyPlugin;