Просмотр исходного кода

Picopass: Read Elite (#1888)

* working elite dict
* add csn to display

Co-authored-by: あく <alleteam@gmail.com>
Patrick Cunningham 3 лет назад
Родитель
Сommit
56f760aa07

+ 151 - 0
applications/plugins/picopass/helpers/iclass_elite_dict.c

@@ -0,0 +1,151 @@
+#include "iclass_elite_dict.h"
+
+#include <lib/toolbox/args.h>
+#include <lib/flipper_format/flipper_format.h>
+
+#define ICLASS_ELITE_DICT_FLIPPER_PATH EXT_PATH("picopass/assets/iclass_elite_dict.txt")
+#define ICLASS_ELITE_DICT_USER_PATH EXT_PATH("picopass/assets/iclass_elite_dict_user.txt")
+
+#define TAG "IclassEliteDict"
+
+#define ICLASS_ELITE_KEY_LINE_LEN (17)
+#define ICLASS_ELITE_KEY_LEN (8)
+
+struct IclassEliteDict {
+    Stream* stream;
+    uint32_t total_keys;
+};
+
+bool iclass_elite_dict_check_presence(IclassEliteDictType dict_type) {
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+
+    bool dict_present = false;
+    if(dict_type == IclassEliteDictTypeFlipper) {
+        dict_present = storage_common_stat(storage, ICLASS_ELITE_DICT_FLIPPER_PATH, NULL) ==
+                       FSE_OK;
+    } else if(dict_type == IclassEliteDictTypeUser) {
+        dict_present = storage_common_stat(storage, ICLASS_ELITE_DICT_USER_PATH, NULL) == FSE_OK;
+    }
+
+    furi_record_close(RECORD_STORAGE);
+
+    return dict_present;
+}
+
+IclassEliteDict* iclass_elite_dict_alloc(IclassEliteDictType dict_type) {
+    IclassEliteDict* dict = malloc(sizeof(IclassEliteDict));
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+    dict->stream = buffered_file_stream_alloc(storage);
+    furi_record_close(RECORD_STORAGE);
+    FuriString* next_line = furi_string_alloc();
+
+    bool dict_loaded = false;
+    do {
+        if(dict_type == IclassEliteDictTypeFlipper) {
+            if(!buffered_file_stream_open(
+                   dict->stream, ICLASS_ELITE_DICT_FLIPPER_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) {
+                buffered_file_stream_close(dict->stream);
+                break;
+            }
+        } else if(dict_type == IclassEliteDictTypeUser) {
+            if(!buffered_file_stream_open(
+                   dict->stream, ICLASS_ELITE_DICT_USER_PATH, FSAM_READ_WRITE, FSOM_OPEN_ALWAYS)) {
+                buffered_file_stream_close(dict->stream);
+                break;
+            }
+        }
+
+        // Read total amount of keys
+        while(true) {
+            if(!stream_read_line(dict->stream, next_line)) break;
+            if(furi_string_get_char(next_line, 0) == '#') continue;
+            if(furi_string_size(next_line) != ICLASS_ELITE_KEY_LINE_LEN) continue;
+            dict->total_keys++;
+        }
+        furi_string_reset(next_line);
+        stream_rewind(dict->stream);
+
+        dict_loaded = true;
+        FURI_LOG_I(TAG, "Loaded dictionary with %lu keys", dict->total_keys);
+    } while(false);
+
+    if(!dict_loaded) {
+        buffered_file_stream_close(dict->stream);
+        free(dict);
+        dict = NULL;
+    }
+
+    furi_string_free(next_line);
+
+    return dict;
+}
+
+void iclass_elite_dict_free(IclassEliteDict* dict) {
+    furi_assert(dict);
+    furi_assert(dict->stream);
+
+    buffered_file_stream_close(dict->stream);
+    stream_free(dict->stream);
+    free(dict);
+}
+
+uint32_t iclass_elite_dict_get_total_keys(IclassEliteDict* dict) {
+    furi_assert(dict);
+
+    return dict->total_keys;
+}
+
+bool iclass_elite_dict_get_next_key(IclassEliteDict* dict, uint8_t* key) {
+    furi_assert(dict);
+    furi_assert(dict->stream);
+
+    uint8_t key_byte_tmp = 0;
+    FuriString* next_line = furi_string_alloc();
+
+    bool key_read = false;
+    *key = 0ULL;
+    while(!key_read) {
+        if(!stream_read_line(dict->stream, next_line)) break;
+        if(furi_string_get_char(next_line, 0) == '#') continue;
+        if(furi_string_size(next_line) != ICLASS_ELITE_KEY_LINE_LEN) continue;
+        for(uint8_t i = 0; i < ICLASS_ELITE_KEY_LEN * 2; i += 2) {
+            args_char_to_hex(
+                furi_string_get_char(next_line, i),
+                furi_string_get_char(next_line, i + 1),
+                &key_byte_tmp);
+            key[i / 2] = key_byte_tmp;
+        }
+        key_read = true;
+    }
+
+    furi_string_free(next_line);
+    return key_read;
+}
+
+bool iclass_elite_dict_rewind(IclassEliteDict* dict) {
+    furi_assert(dict);
+    furi_assert(dict->stream);
+
+    return stream_rewind(dict->stream);
+}
+
+bool iclass_elite_dict_add_key(IclassEliteDict* dict, uint8_t* key) {
+    furi_assert(dict);
+    furi_assert(dict->stream);
+
+    FuriString* key_str = furi_string_alloc();
+    for(size_t i = 0; i < 6; i++) {
+        furi_string_cat_printf(key_str, "%02X", key[i]);
+    }
+    furi_string_cat_printf(key_str, "\n");
+
+    bool key_added = false;
+    do {
+        if(!stream_seek(dict->stream, 0, StreamOffsetFromEnd)) break;
+        if(!stream_insert_string(dict->stream, key_str)) break;
+        key_added = true;
+    } while(false);
+
+    furi_string_free(key_str);
+    return key_added;
+}

+ 28 - 0
applications/plugins/picopass/helpers/iclass_elite_dict.h

@@ -0,0 +1,28 @@
+#pragma once
+
+#include <stdbool.h>
+#include <storage/storage.h>
+#include <lib/flipper_format/flipper_format.h>
+#include <lib/toolbox/stream/file_stream.h>
+#include <lib/toolbox/stream/buffered_file_stream.h>
+
+typedef enum {
+    IclassEliteDictTypeUser,
+    IclassEliteDictTypeFlipper,
+} IclassEliteDictType;
+
+typedef struct IclassEliteDict IclassEliteDict;
+
+bool iclass_elite_dict_check_presence(IclassEliteDictType dict_type);
+
+IclassEliteDict* iclass_elite_dict_alloc(IclassEliteDictType dict_type);
+
+void iclass_elite_dict_free(IclassEliteDict* dict);
+
+uint32_t iclass_elite_dict_get_total_keys(IclassEliteDict* dict);
+
+bool iclass_elite_dict_get_next_key(IclassEliteDict* dict, uint8_t* key);
+
+bool iclass_elite_dict_rewind(IclassEliteDict* dict);
+
+bool iclass_elite_dict_add_key(IclassEliteDict* dict, uint8_t* key);

+ 1 - 1
applications/plugins/picopass/lib/loclass/optimized_elite.c

@@ -185,7 +185,7 @@ static void loclass_desencrypt_iclass(uint8_t* iclass_key, uint8_t* input, uint8
  * @param loclass_hash1 loclass_hash1
  * @param key_sel output key_sel=h[loclass_hash1[i]]
  */
-void hash2(uint8_t* key64, uint8_t* outp_keytable) {
+void loclass_hash2(uint8_t* key64, uint8_t* outp_keytable) {
     /**
      *Expected:
      * High Security Key Table

+ 2 - 0
applications/plugins/picopass/picopass_device.h

@@ -9,6 +9,7 @@
 #include "rfal_picopass.h"
 #include <optimized_ikeys.h>
 #include <optimized_cipher.h>
+#include "helpers/iclass_elite_dict.h"
 
 #define PICOPASS_DEV_NAME_MAX_LEN 22
 #define PICOPASS_READER_DATA_MAX_SIZE 64
@@ -49,6 +50,7 @@ typedef struct {
     bool se_enabled;
     bool sio;
     bool biometrics;
+    uint8_t key[8];
     uint8_t pin_length;
     PicopassEncryption encryption;
     uint8_t credential[8];

+ 87 - 16
applications/plugins/picopass/picopass_worker.c

@@ -1,5 +1,7 @@
 #include "picopass_worker_i.h"
 
+#include <flipper_format/flipper_format.h>
+
 #define TAG "PicopassWorker"
 
 const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78};
@@ -176,7 +178,7 @@ ReturnCode picopass_read_preauth(PicopassBlock* AA1) {
     return ERR_NONE;
 }
 
-ReturnCode picopass_read_card(PicopassBlock* AA1) {
+ReturnCode picopass_auth(PicopassBlock* AA1, PicopassPacs* pacs) {
     rfalPicoPassReadCheckRes rcRes;
     rfalPicoPassCheckRes chkRes;
 
@@ -197,11 +199,69 @@ ReturnCode picopass_read_card(PicopassBlock* AA1) {
     loclass_opt_doReaderMAC(ccnr, div_key, mac);
 
     err = rfalPicoPassPollerCheck(mac, &chkRes);
-    if(err != ERR_NONE) {
-        FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err);
-        return err;
+    if(err == ERR_NONE) {
+        return ERR_NONE;
+    }
+    FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err);
+
+    FURI_LOG_E(TAG, "Starting dictionary attack");
+
+    size_t index = 0;
+    uint8_t key[PICOPASS_BLOCK_LEN] = {0};
+
+    if(!iclass_elite_dict_check_presence(IclassEliteDictTypeFlipper)) {
+        FURI_LOG_E(TAG, "Dictionary not found");
+        return ERR_PARAM;
+    }
+
+    IclassEliteDict* dict = iclass_elite_dict_alloc(IclassEliteDictTypeFlipper);
+    if(!dict) {
+        FURI_LOG_E(TAG, "Dictionary not allocated");
+        return ERR_PARAM;
+    }
+
+    FURI_LOG_D(TAG, "Loaded %lu keys", iclass_elite_dict_get_total_keys(dict));
+    while(iclass_elite_dict_get_next_key(dict, key)) {
+        FURI_LOG_D(
+            TAG,
+            "Try to auth with key %d %02x%02x%02x%02x%02x%02x%02x%02x",
+            index++,
+            key[0],
+            key[1],
+            key[2],
+            key[3],
+            key[4],
+            key[5],
+            key[6],
+            key[7]);
+
+        err = rfalPicoPassPollerReadCheck(&rcRes);
+        if(err != ERR_NONE) {
+            FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err);
+            return err;
+        }
+        memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
+
+        loclass_iclass_calc_div_key(AA1[PICOPASS_CSN_BLOCK_INDEX].data, key, div_key, true);
+        loclass_opt_doReaderMAC(ccnr, div_key, mac);
+
+        err = rfalPicoPassPollerCheck(mac, &chkRes);
+        if(err == ERR_NONE) {
+            memcpy(pacs->key, key, PICOPASS_BLOCK_LEN);
+            break;
+        }
+    }
+
+    if(dict) {
+        iclass_elite_dict_free(dict);
     }
 
+    return err;
+}
+
+ReturnCode picopass_read_card(PicopassBlock* AA1) {
+    ReturnCode err;
+
     size_t app_limit = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] < PICOPASS_MAX_APP_LIMIT ?
                            AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] :
                            PICOPASS_MAX_APP_LIMIT;
@@ -352,28 +412,39 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) {
             pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0);
             if(pacs->se_enabled) {
                 FURI_LOG_D(TAG, "SE enabled");
+                nextState = PicopassWorkerEventFail;
             }
 
-            err = picopass_read_card(AA1);
-            if(err != ERR_NONE) {
-                FURI_LOG_E(TAG, "picopass_read_card error %d", err);
-                nextState = PicopassWorkerEventFail;
+            if(nextState == PicopassWorkerEventSuccess) {
+                err = picopass_auth(AA1, pacs);
+                if(err != ERR_NONE) {
+                    FURI_LOG_E(TAG, "picopass_try_auth error %d", err);
+                    nextState = PicopassWorkerEventFail;
+                }
             }
 
             if(nextState == PicopassWorkerEventSuccess) {
-                err = picopass_device_parse_credential(AA1, pacs);
+                err = picopass_read_card(AA1);
+                if(err != ERR_NONE) {
+                    FURI_LOG_E(TAG, "picopass_read_card error %d", err);
+                    nextState = PicopassWorkerEventFail;
+                }
             }
-            if(err != ERR_NONE) {
-                FURI_LOG_E(TAG, "picopass_device_parse_credential error %d", err);
-                nextState = PicopassWorkerEventFail;
+
+            if(nextState == PicopassWorkerEventSuccess) {
+                err = picopass_device_parse_credential(AA1, pacs);
+                if(err != ERR_NONE) {
+                    FURI_LOG_E(TAG, "picopass_device_parse_credential error %d", err);
+                    nextState = PicopassWorkerEventFail;
+                }
             }
 
             if(nextState == PicopassWorkerEventSuccess) {
                 err = picopass_device_parse_wiegand(pacs->credential, &pacs->record);
-            }
-            if(err != ERR_NONE) {
-                FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err);
-                nextState = PicopassWorkerEventFail;
+                if(err != ERR_NONE) {
+                    FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err);
+                    nextState = PicopassWorkerEventFail;
+                }
             }
 
             // Notify caller and exit

+ 1 - 0
applications/plugins/picopass/picopass_worker_i.h

@@ -18,6 +18,7 @@
 struct PicopassWorker {
     FuriThread* thread;
     Storage* storage;
+    Stream* dict_stream;
 
     PicopassDeviceData* dev_data;
     PicopassWorkerCallback callback;

+ 19 - 10
applications/plugins/picopass/scenes/picopass_scene_read_card_success.c

@@ -15,12 +15,10 @@ void picopass_scene_read_card_success_widget_callback(
 
 void picopass_scene_read_card_success_on_enter(void* context) {
     Picopass* picopass = context;
-    FuriString* credential_str;
-    FuriString* wiegand_str;
-    FuriString* sio_str;
-    credential_str = furi_string_alloc();
-    wiegand_str = furi_string_alloc();
-    sio_str = furi_string_alloc();
+    FuriString* csn_str = furi_string_alloc_set("CSN:");
+    FuriString* credential_str = furi_string_alloc();
+    FuriString* wiegand_str = furi_string_alloc();
+    FuriString* sio_str = furi_string_alloc();
 
     DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
 
@@ -28,10 +26,18 @@ void picopass_scene_read_card_success_on_enter(void* context) {
     notification_message(picopass->notifications, &sequence_success);
 
     // Setup view
+    PicopassBlock* AA1 = picopass->dev->dev_data.AA1;
     PicopassPacs* pacs = &picopass->dev->dev_data.pacs;
     Widget* widget = picopass->widget;
 
-    if(pacs->record.bitLength == 0) {
+    uint8_t csn[PICOPASS_BLOCK_LEN];
+    memcpy(csn, &AA1->data[PICOPASS_CSN_BLOCK_INDEX], PICOPASS_BLOCK_LEN);
+    for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
+        furi_string_cat_printf(csn_str, " %02X", csn[i]);
+    }
+
+    // Neither of these are valid.  Indicates the block was all 0x00 or all 0xff
+    if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) {
         furi_string_cat_printf(wiegand_str, "Read Failed");
 
         if(pacs->se_enabled) {
@@ -79,18 +85,21 @@ void picopass_scene_read_card_success_on_enter(void* context) {
     }
 
     widget_add_string_element(
-        widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(wiegand_str));
+        widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(csn_str));
+    widget_add_string_element(
+        widget, 64, 20, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(wiegand_str));
     widget_add_string_element(
         widget,
         64,
-        32,
+        36,
         AlignCenter,
         AlignCenter,
         FontSecondary,
         furi_string_get_cstr(credential_str));
     widget_add_string_element(
-        widget, 64, 42, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(sio_str));
+        widget, 64, 46, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(sio_str));
 
+    furi_string_free(csn_str);
     furi_string_free(credential_str);
     furi_string_free(wiegand_str);
     furi_string_free(sio_str);