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

Picopass write (#1658)

* [picopass] Prevent false success with non-standard key
* UI for writing
* worker function for write
* Update write command value
* Show card read failure message

Co-authored-by: あく <alleteam@gmail.com>
Eric Betts 3 лет назад
Родитель
Сommit
8e9043003f

+ 111 - 3
applications/picopass/picopass_worker.c

@@ -190,12 +190,87 @@ ReturnCode picopass_read_card(PicopassBlock* AA1) {
     return ERR_NONE;
 }
 
+ReturnCode picopass_write_card(PicopassBlock* AA1) {
+    rfalPicoPassIdentifyRes idRes;
+    rfalPicoPassSelectRes selRes;
+    rfalPicoPassReadCheckRes rcRes;
+    rfalPicoPassCheckRes chkRes;
+
+    ReturnCode err;
+
+    uint8_t div_key[8] = {0};
+    uint8_t mac[4] = {0};
+    uint8_t ccnr[12] = {0};
+
+    err = rfalPicoPassPollerIdentify(&idRes);
+    if(err != ERR_NONE) {
+        FURI_LOG_E(TAG, "rfalPicoPassPollerIdentify error %d", err);
+        return err;
+    }
+
+    err = rfalPicoPassPollerSelect(idRes.CSN, &selRes);
+    if(err != ERR_NONE) {
+        FURI_LOG_E(TAG, "rfalPicoPassPollerSelect error %d", err);
+        return err;
+    }
+
+    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_diversifyKey(selRes.CSN, picopass_iclass_key, div_key);
+    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;
+    }
+
+    for(size_t i = 6; i < 10; i++) {
+        FURI_LOG_D(TAG, "rfalPicoPassPollerWriteBlock %d", i);
+        uint8_t data[9] = {0};
+        data[0] = i;
+        memcpy(data + 1, AA1[i].data, RFAL_PICOPASS_MAX_BLOCK_LEN);
+        loclass_doMAC_N(data, sizeof(data), div_key, mac);
+        FURI_LOG_D(
+            TAG,
+            "loclass_doMAC_N %d %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x",
+            i,
+            data[1],
+            data[2],
+            data[3],
+            data[4],
+            data[5],
+            data[6],
+            data[7],
+            data[8],
+            mac[0],
+            mac[1],
+            mac[2],
+            mac[3]);
+
+        err = rfalPicoPassPollerWriteBlock(i, AA1[i].data, mac);
+        if(err != ERR_NONE) {
+            FURI_LOG_E(TAG, "rfalPicoPassPollerWriteBlock error %d", err);
+            return err;
+        }
+    }
+
+    return ERR_NONE;
+}
+
 int32_t picopass_worker_task(void* context) {
     PicopassWorker* picopass_worker = context;
 
     picopass_worker_enable_field();
     if(picopass_worker->state == PicopassWorkerStateDetect) {
         picopass_worker_detect(picopass_worker);
+    } else if(picopass_worker->state == PicopassWorkerStateWrite) {
+        picopass_worker_write(picopass_worker);
     }
     picopass_worker_disable_field(ERR_NONE);
 
@@ -212,27 +287,60 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) {
     PicopassPacs* pacs = &dev_data->pacs;
     ReturnCode err;
 
+    PicopassWorkerEvent nextState = PicopassWorkerEventSuccess;
+
     while(picopass_worker->state == PicopassWorkerStateDetect) {
         if(picopass_detect_card(1000) == ERR_NONE) {
             // Process first found device
             err = picopass_read_card(AA1);
             if(err != ERR_NONE) {
                 FURI_LOG_E(TAG, "picopass_read_card error %d", err);
+                nextState = PicopassWorkerEventFail;
             }
 
-            err = picopass_device_parse_credential(AA1, pacs);
+            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;
             }
 
-            err = picopass_device_parse_wiegand(pacs->credential, &pacs->record);
+            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;
+            }
+
+            // Notify caller and exit
+            if(picopass_worker->callback) {
+                picopass_worker->callback(nextState, picopass_worker->context);
+            }
+            break;
+        }
+        furi_delay_ms(100);
+    }
+}
+
+void picopass_worker_write(PicopassWorker* picopass_worker) {
+    PicopassDeviceData* dev_data = picopass_worker->dev_data;
+    PicopassBlock* AA1 = dev_data->AA1;
+    ReturnCode err;
+    PicopassWorkerEvent nextState = PicopassWorkerEventSuccess;
+
+    while(picopass_worker->state == PicopassWorkerStateWrite) {
+        if(picopass_detect_card(1000) == ERR_NONE) {
+            err = picopass_write_card(AA1);
+            if(err != ERR_NONE) {
+                FURI_LOG_E(TAG, "picopass_write_card error %d", err);
+                nextState = PicopassWorkerEventFail;
             }
 
             // Notify caller and exit
             if(picopass_worker->callback) {
-                picopass_worker->callback(PicopassWorkerEventSuccess, picopass_worker->context);
+                picopass_worker->callback(nextState, picopass_worker->context);
             }
             break;
         }

+ 1 - 0
applications/picopass/picopass_worker.h

@@ -11,6 +11,7 @@ typedef enum {
     PicopassWorkerStateReady,
     // Main worker states
     PicopassWorkerStateDetect,
+    PicopassWorkerStateWrite,
     // Transition
     PicopassWorkerStateStop,
 } PicopassWorkerState;

+ 1 - 0
applications/picopass/picopass_worker_i.h

@@ -31,3 +31,4 @@ void picopass_worker_change_state(PicopassWorker* picopass_worker, PicopassWorke
 int32_t picopass_worker_task(void* context);
 
 void picopass_worker_detect(PicopassWorker* picopass_worker);
+void picopass_worker_write(PicopassWorker* picopass_worker);

+ 2 - 0
applications/picopass/scenes/picopass_scene_config.h

@@ -9,3 +9,5 @@ ADD_SCENE(picopass, file_select, FileSelect)
 ADD_SCENE(picopass, device_info, DeviceInfo)
 ADD_SCENE(picopass, delete, Delete)
 ADD_SCENE(picopass, delete_success, DeleteSuccess)
+ADD_SCENE(picopass, write_card, WriteCard)
+ADD_SCENE(picopass, write_card_success, WriteCardSuccess)

+ 0 - 2
applications/picopass/scenes/picopass_scene_read_card.c

@@ -37,8 +37,6 @@ bool picopass_scene_read_card_on_event(void* context, SceneManagerEvent event) {
             scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess);
             consumed = true;
         }
-    } else if(event.type == SceneManagerEventTypeTick) {
-        consumed = true;
     }
     return consumed;
 }

+ 48 - 29
applications/picopass/scenes/picopass_scene_read_card_success.c

@@ -29,38 +29,57 @@ void picopass_scene_read_card_success_on_enter(void* context) {
     PicopassPacs* pacs = &picopass->dev->dev_data.pacs;
     Widget* widget = picopass->widget;
 
-    size_t bytesLength = 1 + pacs->record.bitLength / 8;
-    string_set_str(credential_str, "");
-    for(uint8_t i = PICOPASS_BLOCK_LEN - bytesLength; i < PICOPASS_BLOCK_LEN; i++) {
-        string_cat_printf(credential_str, " %02X", pacs->credential[i]);
-    }
-
-    if(pacs->record.valid) {
-        string_cat_printf(
-            wiegand_str, "FC: %u CN: %u", pacs->record.FacilityCode, pacs->record.CardNumber);
+    if(pacs->record.bitLength == 0) {
+        string_cat_printf(wiegand_str, "Read Failed");
+
+        widget_add_button_element(
+            widget,
+            GuiButtonTypeLeft,
+            "Retry",
+            picopass_scene_read_card_success_widget_callback,
+            picopass);
+
+        widget_add_string_element(
+            widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, string_get_cstr(wiegand_str));
     } else {
-        string_cat_printf(wiegand_str, "%d bits", pacs->record.bitLength);
-    }
+        size_t bytesLength = 1 + pacs->record.bitLength / 8;
+        string_set_str(credential_str, "");
+        for(uint8_t i = PICOPASS_BLOCK_LEN - bytesLength; i < PICOPASS_BLOCK_LEN; i++) {
+            string_cat_printf(credential_str, " %02X", pacs->credential[i]);
+        }
 
-    widget_add_button_element(
-        widget,
-        GuiButtonTypeLeft,
-        "Retry",
-        picopass_scene_read_card_success_widget_callback,
-        picopass);
-
-    widget_add_button_element(
-        widget,
-        GuiButtonTypeRight,
-        "More",
-        picopass_scene_read_card_success_widget_callback,
-        picopass);
-
-    widget_add_string_element(
-        widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, string_get_cstr(wiegand_str));
-    widget_add_string_element(
-        widget, 64, 32, AlignCenter, AlignCenter, FontSecondary, string_get_cstr(credential_str));
+        if(pacs->record.valid) {
+            string_cat_printf(
+                wiegand_str, "FC: %u CN: %u", pacs->record.FacilityCode, pacs->record.CardNumber);
+        } else {
+            string_cat_printf(wiegand_str, "%d bits", pacs->record.bitLength);
+        }
 
+        widget_add_button_element(
+            widget,
+            GuiButtonTypeLeft,
+            "Retry",
+            picopass_scene_read_card_success_widget_callback,
+            picopass);
+
+        widget_add_button_element(
+            widget,
+            GuiButtonTypeRight,
+            "More",
+            picopass_scene_read_card_success_widget_callback,
+            picopass);
+
+        widget_add_string_element(
+            widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, string_get_cstr(wiegand_str));
+        widget_add_string_element(
+            widget,
+            64,
+            32,
+            AlignCenter,
+            AlignCenter,
+            FontSecondary,
+            string_get_cstr(credential_str));
+    }
     string_clear(credential_str);
     string_clear(wiegand_str);
 

+ 5 - 0
applications/picopass/scenes/picopass_scene_saved_menu.c

@@ -24,6 +24,8 @@ void picopass_scene_saved_menu_on_enter(void* context) {
         picopass);
     submenu_add_item(
         submenu, "Info", SubmenuIndexInfo, picopass_scene_saved_menu_submenu_callback, picopass);
+    submenu_add_item(
+        submenu, "Write", SubmenuIndexWrite, picopass_scene_saved_menu_submenu_callback, picopass);
 
     submenu_set_selected_item(
         picopass->submenu,
@@ -46,6 +48,9 @@ bool picopass_scene_saved_menu_on_event(void* context, SceneManagerEvent event)
         } else if(event.event == SubmenuIndexInfo) {
             scene_manager_next_scene(picopass->scene_manager, PicopassSceneDeviceInfo);
             consumed = true;
+        } else if(event.event == SubmenuIndexWrite) {
+            scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteCard);
+            consumed = true;
         }
     }
 

+ 53 - 0
applications/picopass/scenes/picopass_scene_write_card.c

@@ -0,0 +1,53 @@
+#include "../picopass_i.h"
+#include <dolphin/dolphin.h>
+
+void picopass_write_card_worker_callback(PicopassWorkerEvent event, void* context) {
+    UNUSED(event);
+    Picopass* picopass = context;
+    view_dispatcher_send_custom_event(picopass->view_dispatcher, PicopassCustomEventWorkerExit);
+}
+
+void picopass_scene_write_card_on_enter(void* context) {
+    Picopass* picopass = context;
+    DOLPHIN_DEED(DolphinDeedNfcSave);
+
+    // Setup view
+    Popup* popup = picopass->popup;
+    popup_set_header(popup, "Writing\npicopass\ncard", 68, 30, AlignLeft, AlignTop);
+    popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
+
+    // Start worker
+    view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewPopup);
+    picopass_worker_start(
+        picopass->worker,
+        PicopassWorkerStateWrite,
+        &picopass->dev->dev_data,
+        picopass_write_card_worker_callback,
+        picopass);
+
+    picopass_blink_start(picopass);
+}
+
+bool picopass_scene_write_card_on_event(void* context, SceneManagerEvent event) {
+    Picopass* picopass = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == PicopassCustomEventWorkerExit) {
+            scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteCardSuccess);
+            consumed = true;
+        }
+    }
+    return consumed;
+}
+
+void picopass_scene_write_card_on_exit(void* context) {
+    Picopass* picopass = context;
+
+    // Stop worker
+    picopass_worker_stop(picopass->worker);
+    // Clear view
+    popup_reset(picopass->popup);
+
+    picopass_blink_stop(picopass);
+}

+ 57 - 0
applications/picopass/scenes/picopass_scene_write_card_success.c

@@ -0,0 +1,57 @@
+#include "../picopass_i.h"
+#include <dolphin/dolphin.h>
+
+void picopass_scene_write_card_success_widget_callback(
+    GuiButtonType result,
+    InputType type,
+    void* context) {
+    furi_assert(context);
+    Picopass* picopass = context;
+
+    if(type == InputTypeShort) {
+        view_dispatcher_send_custom_event(picopass->view_dispatcher, result);
+    }
+}
+
+void picopass_scene_write_card_success_on_enter(void* context) {
+    Picopass* picopass = context;
+    Widget* widget = picopass->widget;
+
+    DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
+
+    // Send notification
+    notification_message(picopass->notifications, &sequence_success);
+
+    widget_add_button_element(
+        widget,
+        GuiButtonTypeLeft,
+        "Retry",
+        picopass_scene_write_card_success_widget_callback,
+        picopass);
+
+    view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget);
+}
+
+bool picopass_scene_write_card_success_on_event(void* context, SceneManagerEvent event) {
+    Picopass* picopass = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == GuiButtonTypeLeft) {
+            consumed = scene_manager_previous_scene(picopass->scene_manager);
+        } else if(event.event == GuiButtonTypeRight) {
+            // Clear device name
+            picopass_device_set_name(picopass->dev, "");
+            scene_manager_next_scene(picopass->scene_manager, PicopassSceneCardMenu);
+            consumed = true;
+        }
+    }
+    return consumed;
+}
+
+void picopass_scene_write_card_success_on_exit(void* context) {
+    Picopass* picopass = context;
+
+    // Clear view
+    widget_reset(picopass->widget);
+}

+ 1 - 1
lib/ST25RFAL002/include/rfal_picopass.h

@@ -26,7 +26,7 @@ enum {
     RFAL_PICOPASS_CMD_READCHECK = 0x88,
     RFAL_PICOPASS_CMD_CHECK = 0x05,
     RFAL_PICOPASS_CMD_READ = 0x0C,
-    RFAL_PICOPASS_CMD_WRITE = 0x0C,
+    RFAL_PICOPASS_CMD_WRITE = 0x87,
 };
 
 typedef struct {

+ 7 - 9
lib/ST25RFAL002/source/rfal_picopass.c

@@ -2,6 +2,8 @@
 #include "rfal_picopass.h"
 #include "utils.h"
 
+#define TAG "RFAL_PICOPASS"
+
 typedef struct {
     uint8_t CMD;
     uint8_t CSN[RFAL_PICOPASS_UID_LEN];
@@ -169,18 +171,14 @@ ReturnCode rfalPicoPassPollerWriteBlock(uint8_t blockNum, uint8_t data[8], uint8
     uint16_t recvLen = 0;
     uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS;
     uint32_t fwt = rfalConvMsTo1fc(20);
-    rfalPicoPassReadBlockRes readRes;
+    rfalPicoPassReadBlockRes block;
 
     ret = rfalTransceiveBlockingTxRx(
-        txBuf,
-        sizeof(txBuf),
-        (uint8_t*)&readRes,
-        sizeof(rfalPicoPassReadBlockRes),
-        &recvLen,
-        flags,
-        fwt);
+        txBuf, sizeof(txBuf), (uint8_t*)&block, sizeof(block), &recvLen, flags, fwt);
 
-    // TODO: compare response
+    if(ret == ERR_NONE) {
+        // TODO: compare response
+    }
 
     return ret;
 }