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

Add picopass protocol files and strip out unused

Eric Betts 2 лет назад
Родитель
Сommit
280b099c9f

+ 157 - 0
protocol/picopass_poller.c

@@ -0,0 +1,157 @@
+#include "picopass_poller_i.h"
+
+#include <furi/furi.h>
+
+#define TAG "Picopass"
+
+typedef NfcCommand (*PicopassPollerStateHandler)(PicopassPoller* instance);
+
+static void picopass_poller_reset(PicopassPoller* instance) {
+  UNUSED(instance);
+}
+
+NfcCommand picopass_poller_request_mode_handler(PicopassPoller* instance) {
+    NfcCommand command = NfcCommandContinue;
+
+    instance->event.type = PicopassPollerEventTypeRequestMode;
+    command = instance->callback(instance->event, instance->context);
+    instance->mode = instance->event_data.req_mode.mode;
+    instance->state = PicopassPollerStateDetect;
+
+    return command;
+}
+
+NfcCommand picopass_poller_detect_handler(PicopassPoller* instance) {
+    NfcCommand command = NfcCommandContinue;
+    PicopassError error = picopass_poller_actall(instance);
+
+    if(error == PicopassErrorNone) {
+        instance->state = PicopassPollerStateSelect;
+        instance->event.type = PicopassPollerEventTypeCardDetected;
+        command = instance->callback(instance->event, instance->context);
+    } else {
+        furi_delay_ms(100);
+    }
+
+    return command;
+}
+
+NfcCommand picopass_poller_select_handler(PicopassPoller* instance) {
+    NfcCommand command = NfcCommandContinue;
+
+    do {
+        PicopassError error = picopass_poller_identify(instance, &instance->col_res_serial_num);
+        if(error != PicopassErrorNone) {
+            instance->state = PicopassPollerStateFail;
+            break;
+        }
+
+        error =
+            picopass_poller_select(instance, &instance->col_res_serial_num, &instance->serial_num);
+        if(error != PicopassErrorNone) {
+            instance->state = PicopassPollerStateFail;
+            break;
+        }
+
+        instance->state = PicopassPollerStateSuccess;
+    } while(false);
+
+    return command;
+}
+
+
+NfcCommand picopass_poller_success_handler(PicopassPoller* instance) {
+    NfcCommand command = NfcCommandContinue;
+
+    instance->event.type = PicopassPollerEventTypeSuccess;
+    command = instance->callback(instance->event, instance->context);
+    furi_delay_ms(100);
+
+    return command;
+}
+
+NfcCommand picopass_poller_fail_handler(PicopassPoller* instance) {
+    NfcCommand command = NfcCommandReset;
+
+    instance->event.type = PicopassPollerEventTypeFail;
+    command = instance->callback(instance->event, instance->context);
+    picopass_poller_reset(instance);
+    instance->state = PicopassPollerStateDetect;
+
+    return command;
+}
+
+static const PicopassPollerStateHandler picopass_poller_state_handler[PicopassPollerStateNum] = {
+    [PicopassPollerStateRequestMode] = picopass_poller_request_mode_handler,
+    [PicopassPollerStateDetect] = picopass_poller_detect_handler,
+    [PicopassPollerStateSelect] = picopass_poller_select_handler,
+    [PicopassPollerStateSuccess] = picopass_poller_success_handler,
+    [PicopassPollerStateFail] = picopass_poller_fail_handler,
+};
+
+static NfcCommand picopass_poller_callback(NfcEvent event, void* context) {
+    furi_assert(context);
+
+    PicopassPoller* instance = context;
+    NfcCommand command = NfcCommandContinue;
+
+    if(event.type == NfcEventTypePollerReady) {
+        command = picopass_poller_state_handler[instance->state](instance);
+    }
+
+    if(instance->session_state == PicopassPollerSessionStateStopRequest) {
+        command = NfcCommandStop;
+    }
+
+    return command;
+}
+
+void picopass_poller_start(
+    PicopassPoller* instance,
+    PicopassPollerCallback callback,
+    void* context) {
+    furi_assert(instance);
+    furi_assert(instance->session_state == PicopassPollerSessionStateIdle);
+
+    instance->callback = callback;
+    instance->context = context;
+
+    instance->session_state = PicopassPollerSessionStateActive;
+    nfc_start(instance->nfc, picopass_poller_callback, instance);
+}
+
+void picopass_poller_stop(PicopassPoller* instance) {
+    furi_assert(instance);
+
+    instance->session_state = PicopassPollerSessionStateStopRequest;
+    nfc_stop(instance->nfc);
+    instance->session_state = PicopassPollerSessionStateIdle;
+}
+
+PicopassPoller* picopass_poller_alloc(Nfc* nfc) {
+    furi_assert(nfc);
+
+    PicopassPoller* instance = malloc(sizeof(PicopassPoller));
+    instance->nfc = nfc;
+    nfc_config(instance->nfc, NfcModePoller, NfcTechIso15693);
+    nfc_set_guard_time_us(instance->nfc, 10000);
+    nfc_set_fdt_poll_fc(instance->nfc, 5000);
+    nfc_set_fdt_poll_poll_us(instance->nfc, 1000);
+
+    instance->event.data = &instance->event_data;
+
+    instance->tx_buffer = bit_buffer_alloc(PICOPASS_POLLER_BUFFER_SIZE);
+    instance->rx_buffer = bit_buffer_alloc(PICOPASS_POLLER_BUFFER_SIZE);
+    instance->tmp_buffer = bit_buffer_alloc(PICOPASS_POLLER_BUFFER_SIZE);
+
+    return instance;
+}
+
+void picopass_poller_free(PicopassPoller* instance) {
+    furi_assert(instance);
+
+    bit_buffer_free(instance->tx_buffer);
+    bit_buffer_free(instance->rx_buffer);
+    bit_buffer_free(instance->tmp_buffer);
+    free(instance);
+}

+ 54 - 0
protocol/picopass_poller.h

@@ -0,0 +1,54 @@
+#pragma once
+
+#include <nfc/nfc.h>
+#include "picopass_protocol.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    PicopassPollerEventTypeRequestMode,
+    PicopassPollerEventTypeCardDetected,
+    PicopassPollerEventTypeCardLost,
+    PicopassPollerEventTypeSuccess,
+    PicopassPollerEventTypeFail,
+} PicopassPollerEventType;
+
+typedef enum {
+    PicopassPollerModeRead,
+    PicopassPollerModeWrite,
+    PicopassPollerModeWriteKey,
+} PicopassPollerMode;
+
+typedef struct {
+    PicopassPollerMode mode;
+} PicopassPollerEventDataRequestMode;
+
+typedef union {
+    PicopassPollerEventDataRequestMode req_mode;
+} PicopassPollerEventData;
+
+typedef struct {
+    PicopassPollerEventType type;
+    PicopassPollerEventData* data;
+} PicopassPollerEvent;
+
+typedef NfcCommand (*PicopassPollerCallback)(PicopassPollerEvent event, void* context);
+
+typedef struct PicopassPoller PicopassPoller;
+
+PicopassPoller* picopass_poller_alloc(Nfc* nfc);
+
+void picopass_poller_free(PicopassPoller* instance);
+
+void picopass_poller_start(
+    PicopassPoller* instance,
+    PicopassPollerCallback callback,
+    void* context);
+
+void picopass_poller_stop(PicopassPoller* instance);
+
+#ifdef __cplusplus
+}
+#endif

+ 107 - 0
protocol/picopass_poller_i.c

@@ -0,0 +1,107 @@
+#include "picopass_poller_i.h"
+
+#include <nfc/helpers/iso14443_crc.h>
+
+#define PICOPASS_POLLER_FWT_FC (100000)
+
+#define TAG "Picopass"
+
+static PicopassError picopass_poller_process_error(NfcError error) {
+    PicopassError ret = PicopassErrorNone;
+
+    switch(error) {
+    case NfcErrorNone:
+        ret = PicopassErrorNone;
+        break;
+
+    default:
+        ret = PicopassErrorTimeout;
+        break;
+    }
+
+    return ret;
+}
+
+static PicopassError picopass_poller_send_frame(
+    PicopassPoller* instance,
+    BitBuffer* tx_buffer,
+    BitBuffer* rx_buffer,
+    uint32_t fwt_fc) {
+    PicopassError ret = PicopassErrorNone;
+
+    do {
+        NfcError error = nfc_poller_trx(instance->nfc, tx_buffer, rx_buffer, fwt_fc);
+        if(error != NfcErrorNone) {
+            ret = picopass_poller_process_error(error);
+            break;
+        }
+        if(!iso13239_crc_check(Iso13239CrcTypePicopass, rx_buffer)) {
+            ret = PicopassErrorIncorrectCrc;
+            break;
+        }
+        iso13239_crc_trim(instance->rx_buffer);
+    } while(false);
+
+    return ret;
+}
+
+PicopassError picopass_poller_actall(PicopassPoller* instance) {
+    PicopassError ret = PicopassErrorNone;
+
+    bit_buffer_reset(instance->tx_buffer);
+    bit_buffer_append_byte(instance->tx_buffer, RFAL_PICOPASS_CMD_ACTALL);
+
+    NfcError error = nfc_poller_trx(
+        instance->nfc, instance->tx_buffer, instance->rx_buffer, PICOPASS_POLLER_FWT_FC);
+    if(error != NfcErrorIncompleteFrame) {
+        ret = picopass_poller_process_error(error);
+    }
+
+    return ret;
+}
+
+PicopassError picopass_poller_identify(
+    PicopassPoller* instance,
+    PicopassColResSerialNum* col_res_serial_num) {
+    PicopassError ret = PicopassErrorNone;
+
+    do {
+        bit_buffer_reset(instance->tx_buffer);
+        bit_buffer_append_byte(instance->tx_buffer, RFAL_PICOPASS_CMD_READ_OR_IDENTIFY);
+        ret = picopass_poller_send_frame(
+            instance, instance->tx_buffer, instance->rx_buffer, PICOPASS_POLLER_FWT_FC);
+        if(ret != PicopassErrorNone) break;
+        if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(PicopassColResSerialNum)) {
+            ret = PicopassErrorProtocol;
+            break;
+        }
+        bit_buffer_write_bytes(
+            instance->rx_buffer, col_res_serial_num->data, sizeof(PicopassColResSerialNum));
+    } while(false);
+
+    return ret;
+}
+
+PicopassError picopass_poller_select(
+    PicopassPoller* instance,
+    PicopassColResSerialNum* col_res_serial_num,
+    PicopassSerialNum* serial_num) {
+    PicopassError ret = PicopassErrorNone;
+
+    do {
+        bit_buffer_reset(instance->tx_buffer);
+        bit_buffer_append_byte(instance->tx_buffer, RFAL_PICOPASS_CMD_SELECT);
+        bit_buffer_append_bytes(
+            instance->tx_buffer, col_res_serial_num->data, sizeof(PicopassColResSerialNum));
+        ret = picopass_poller_send_frame(
+            instance, instance->tx_buffer, instance->rx_buffer, PICOPASS_POLLER_FWT_FC);
+        if(ret != PicopassErrorNone) break;
+        if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(PicopassSerialNum)) {
+            ret = PicopassErrorProtocol;
+            break;
+        }
+        bit_buffer_write_bytes(instance->rx_buffer, serial_num->data, sizeof(PicopassSerialNum));
+    } while(false);
+
+    return ret;
+}

+ 56 - 0
protocol/picopass_poller_i.h

@@ -0,0 +1,56 @@
+#pragma once
+
+#include "rfal_picopass.h"
+#include "picopass_poller.h"
+#include "picopass_protocol.h"
+
+#include <nfc/helpers/iso13239_crc.h>
+
+#define PICOPASS_POLLER_BUFFER_SIZE (255)
+#define PICOPASS_CRC_SIZE (2)
+
+typedef enum {
+    PicopassPollerSessionStateIdle,
+    PicopassPollerSessionStateActive,
+    PicopassPollerSessionStateStopRequest,
+} PicopassPollerSessionState;
+
+typedef enum {
+    PicopassPollerStateRequestMode,
+    PicopassPollerStateDetect,
+    PicopassPollerStateSelect,
+    PicopassPollerStateSuccess,
+    PicopassPollerStateFail,
+
+    PicopassPollerStateNum,
+} PicopassPollerState;
+
+struct PicopassPoller {
+    Nfc* nfc;
+    PicopassPollerSessionState session_state;
+    PicopassPollerState state;
+    PicopassPollerMode mode;
+
+    PicopassColResSerialNum col_res_serial_num;
+    PicopassSerialNum serial_num;
+    PicopassMac mac;
+
+    BitBuffer* tx_buffer;
+    BitBuffer* rx_buffer;
+    BitBuffer* tmp_buffer;
+
+    PicopassPollerEvent event;
+    PicopassPollerEventData event_data;
+    PicopassPollerCallback callback;
+    void* context;
+};
+
+PicopassError picopass_poller_actall(PicopassPoller* instance);
+
+PicopassError
+    picopass_poller_identify(PicopassPoller* instance, PicopassColResSerialNum* col_res_serial_num);
+
+PicopassError picopass_poller_select(
+    PicopassPoller* instance,
+    PicopassColResSerialNum* col_res_serial_num,
+    PicopassSerialNum* serial_num);

+ 46 - 0
protocol/picopass_protocol.h

@@ -0,0 +1,46 @@
+#pragma once
+
+#define PICOPASS_BLOCK_LEN 8
+#define PICOPASS_MAX_APP_LIMIT 32
+#define PICOPASS_UID_LEN 8
+#define PICOPASS_READ_CHECK_RESP_LEN 8
+#define PICOPASS_CHECK_RESP_LEN 4
+#define PICOPASS_MAC_LEN 4
+#define PICOPASS_KEY_LEN 8
+
+#define PICOPASS_FDT_LISTEN_FC (1000)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    PicopassErrorNone,
+    PicopassErrorTimeout,
+    PicopassErrorIncorrectCrc,
+    PicopassErrorProtocol,
+} PicopassError;
+
+typedef struct {
+    uint8_t data[PICOPASS_UID_LEN];
+} PicopassColResSerialNum;
+
+typedef struct {
+    uint8_t data[PICOPASS_UID_LEN];
+} PicopassSerialNum;
+
+typedef struct {
+    uint8_t data[PICOPASS_READ_CHECK_RESP_LEN];
+} PicopassReadCheckResp;
+
+typedef struct {
+    uint8_t data[PICOPASS_CHECK_RESP_LEN];
+} PicopassCheckResp;
+
+typedef struct {
+    uint8_t data[PICOPASS_MAC_LEN];
+} PicopassMac;
+
+#ifdef __cplusplus
+}
+#endif

+ 64 - 0
protocol/rfal_picopass.h

@@ -0,0 +1,64 @@
+#pragma once
+
+#include <furi_hal_nfc.h>
+
+#define RFAL_PICOPASS_UID_LEN 8
+#define PICOPASS_BLOCK_LEN 8
+
+enum {
+    // PicoPass command bytes:
+    // Low nibble used for command
+    // High nibble used for options and checksum (MSB)
+    // The only option we care about in 15693 mode is the key
+    // which is only used by READCHECK, so for simplicity we
+    // don't bother breaking down the command and flags into parts
+
+    // READ: ADDRESS(1) CRC16(2) -> DATA(8) CRC16(2)
+    // IDENTIFY: No args -> ASNB(8) CRC16(2)
+    RFAL_PICOPASS_CMD_READ_OR_IDENTIFY = 0x0C,
+    // ADDRESS(1) CRC16(2) -> DATA(32) CRC16(2)
+    RFAL_PICOPASS_CMD_READ4 = 0x06,
+    // ADDRESS(1) DATA(8) SIGN(4)|CRC16(2) -> DATA(8) CRC16(2)
+    RFAL_PICOPASS_CMD_UPDATE = 0x87,
+    // ADDRESS(1) -> DATA(8)
+    RFAL_PICOPASS_CMD_READCHECK_KD = 0x88,
+    // ADDRESS(1) -> DATA(8)
+    RFAL_PICOPASS_CMD_READCHECK_KC = 0x18,
+    // CHALLENGE(4) READERSIGNATURE(4) -> CHIPRESPONSE(4)
+    RFAL_PICOPASS_CMD_CHECK = 0x05,
+    // No args -> SOF
+    RFAL_PICOPASS_CMD_ACTALL = 0x0A,
+    // No args -> SOF
+    RFAL_PICOPASS_CMD_ACT = 0x8E,
+    // ASNB(8)|SERIALNB(8) -> SERIALNB(8) CRC16(2)
+    RFAL_PICOPASS_CMD_SELECT = 0x81,
+    // No args -> SERIALNB(8) CRC16(2)
+    RFAL_PICOPASS_CMD_DETECT = 0x0F,
+    // No args -> SOF
+    RFAL_PICOPASS_CMD_HALT = 0x00,
+    // PAGE(1) CRC16(2) -> BLOCK1(8) CRC16(2)
+    RFAL_PICOPASS_CMD_PAGESEL = 0x84,
+};
+
+typedef struct {
+    uint8_t CSN[RFAL_PICOPASS_UID_LEN]; // Anti-collision CSN
+    uint8_t crc[2];
+} rfalPicoPassIdentifyRes;
+
+typedef struct {
+    uint8_t CSN[RFAL_PICOPASS_UID_LEN]; // Real CSN
+    uint8_t crc[2];
+} rfalPicoPassSelectRes;
+
+typedef struct {
+    uint8_t CCNR[8];
+} rfalPicoPassReadCheckRes;
+
+typedef struct {
+    uint8_t mac[4];
+} rfalPicoPassCheckRes;
+
+typedef struct {
+    uint8_t data[PICOPASS_BLOCK_LEN];
+    uint8_t crc[2];
+} rfalPicoPassReadBlockRes;

+ 0 - 6
scenes/seader_scene_read_14a.c

@@ -1,12 +1,6 @@
 #include "../seader_i.h"
 #include <dolphin/dolphin.h>
 
-void seader_read_14a_worker_callback(SeaderWorkerEvent event, void* context) {
-    UNUSED(event);
-    Seader* seader = context;
-    view_dispatcher_send_custom_event(seader->view_dispatcher, SeaderCustomEventWorkerExit);
-}
-
 void seader_scene_read_14a_on_enter(void* context) {
     Seader* seader = context;
     dolphin_deed(DolphinDeedNfcRead);

+ 11 - 15
scenes/seader_scene_read_picopass.c

@@ -1,12 +1,6 @@
 #include "../seader_i.h"
 #include <dolphin/dolphin.h>
 
-void seader_read_picopass_worker_callback(SeaderWorkerEvent event, void* context) {
-    UNUSED(event);
-    Seader* seader = context;
-    view_dispatcher_send_custom_event(seader->view_dispatcher, SeaderCustomEventWorkerExit);
-}
-
 void seader_scene_read_picopass_on_enter(void* context) {
     Seader* seader = context;
     dolphin_deed(DolphinDeedNfcRead);
@@ -18,13 +12,9 @@ void seader_scene_read_picopass_on_enter(void* context) {
 
     // Start worker
     view_dispatcher_switch_to_view(seader->view_dispatcher, SeaderViewPopup);
-    seader_worker_start(
-        seader->worker,
-        SeaderWorkerStateReadPicopass,
-        seader->uart,
-        seader->credential,
-        seader_read_picopass_worker_callback,
-        seader);
+
+    seader->picopass_poller = picopass_poller_alloc(seader->nfc);
+    picopass_poller_start(seader->picopass_poller, seader_worker_poller_callback_picopass, seader);
 
     seader_blink_start(seader);
 }
@@ -39,6 +29,9 @@ bool seader_scene_read_picopass_on_event(void* context, SceneManagerEvent event)
             scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess);
             consumed = true;
         }
+    } else if(event.type == SceneManagerEventTypeBack) {
+        scene_manager_search_and_switch_to_previous_scene(seader->scene_manager, SeaderSceneStart);
+        consumed = true;
     }
     return consumed;
 }
@@ -46,8 +39,11 @@ bool seader_scene_read_picopass_on_event(void* context, SceneManagerEvent event)
 void seader_scene_read_picopass_on_exit(void* context) {
     Seader* seader = context;
 
-    // Stop worker
-    seader_worker_stop(seader->worker);
+    if(seader->picopass_poller) {
+      picopass_poller_stop(seader->picopass_poller);
+      picopass_poller_free(seader->picopass_poller);
+    }
+
     // Clear view
     popup_reset(seader->popup);
 

+ 0 - 2
scenes/seader_scene_sam_present.c

@@ -21,14 +21,12 @@ void seader_scene_sam_present_on_update(void* context) {
     Submenu* submenu = seader->submenu;
     submenu_reset(submenu);
 
-    /*
     submenu_add_item(
         submenu,
         "Read Picopass",
         SubmenuIndexReadPicopass,
         seader_scene_sam_present_submenu_callback,
         seader);
-        */
     submenu_add_item(
         submenu,
         "Read 14443A",

+ 2 - 0
seader_i.h

@@ -40,6 +40,7 @@
 #include <Payload.h>
 #include <FrameProtocol.h>
 
+#include "protocol/picopass_poller.h"
 #include "scenes/seader_scene.h"
 
 #include "seader_bridge.h"
@@ -105,6 +106,7 @@ struct Seader {
 
     Nfc* nfc;
     NfcPoller* poller;
+    PicopassPoller* picopass_poller;
 
     NfcDevice* nfc_device;
 };

+ 41 - 1
seader_worker.c

@@ -452,6 +452,30 @@ void seader_send_nfc_rx(SeaderUartBridge* seader_uart, uint8_t* buffer, size_t l
     ASN_STRUCT_FREE(asn_DEF_Response, response);
 }
 
+NfcCommand seader_iso15693_transmit(
+    Seader* seader,
+    uint8_t* buffer,
+    size_t len,
+    uint16_t timeout,
+    uint8_t format[3]) {
+    UNUSED(timeout);
+    UNUSED(format);
+    UNUSED(seader);
+    UNUSED(buffer);
+    UNUSED(len);
+
+    /*
+    BitBuffer* tx_buffer = bit_buffer_alloc(len);
+    BitBuffer* rx_buffer = bit_buffer_alloc(SEADER_POLLER_MAX_BUFFER_SIZE);
+    */
+    NfcCommand ret = NfcCommandContinue;
+
+
+
+    return ret;
+}
+
+
 /* Assumes this is called in the context of the NFC API callback */
 NfcCommand seader_iso14443a_transmit(
     Seader* seader,
@@ -514,7 +538,8 @@ NfcCommand seader_parse_nfc_command_transmit(Seader* seader, NFCSend_t* nfcSend)
 #endif
 
     if(frameProtocol == FrameProtocol_iclass) {
-        // TODO
+        return seader_iso15693_transmit(
+            seader, nfcSend->data.buf, nfcSend->data.size, (uint16_t)timeOut, nfcSend->format->buf);
     } else if(frameProtocol == FrameProtocol_nfc) {
         return seader_iso14443a_transmit(
             seader, nfcSend->data.buf, nfcSend->data.size, (uint16_t)timeOut, nfcSend->format->buf);
@@ -814,3 +839,18 @@ NfcCommand seader_worker_poller_callback_iso14443_4a(NfcGenericEvent event, void
 
     return NfcCommandContinue;
 }
+
+NfcCommand seader_worker_poller_callback_picopass(PicopassPollerEvent event, void* context) {
+    furi_assert(context);
+    NfcCommand command = NfcCommandContinue;
+
+    Seader* seader = context;
+    UNUSED(seader);
+
+    if(event.type == PicopassPollerEventTypeSuccess) {
+      FURI_LOG_D(TAG, "PicopassPollerEventTypeSuccess");
+    }
+
+    return command;
+}
+

+ 2 - 0
seader_worker.h

@@ -56,3 +56,5 @@ void seader_worker_process_sam_message(Seader* seader, CCID_Message* message);
 void seader_worker_send_version(SeaderWorker* seader_worker);
 
 NfcCommand seader_worker_poller_callback_iso14443_4a(NfcGenericEvent event, void* context);
+
+NfcCommand seader_worker_poller_callback_picopass(PicopassPollerEvent event, void* context);