MX 1 tahun lalu
induk
melakukan
e0d2ee8564

+ 144 - 0
apdu_log.c

@@ -0,0 +1,144 @@
+#include "apdu_log.h"
+
+#include <storage/storage.h>
+#include <flipper_format/flipper_format.h>
+#include <toolbox/stream/file_stream.h>
+#include <toolbox/stream/buffered_file_stream.h>
+#include <toolbox/args.h>
+
+#define TAG "APDULog"
+
+struct APDULog {
+    Stream* stream;
+    size_t total_lines;
+};
+
+static inline void apdu_log_add_ending_new_line(APDULog* instance) {
+    if(stream_seek(instance->stream, -1, StreamOffsetFromEnd)) {
+        uint8_t last_char = 0;
+
+        // Check if the last char is new line or add a new line
+        if(stream_read(instance->stream, &last_char, 1) == 1 && last_char != '\n') {
+            FURI_LOG_D(TAG, "Adding new line ending");
+            stream_write_char(instance->stream, '\n');
+        }
+
+        stream_rewind(instance->stream);
+    }
+}
+
+static bool apdu_log_read_log_line(APDULog* instance, FuriString* line, bool* is_endfile) {
+    if(stream_read_line(instance->stream, line) == false) {
+        *is_endfile = true;
+    }
+
+    else {
+        size_t newline_index = furi_string_search_char(line, '\n', 0);
+
+        if(newline_index != FURI_STRING_FAILURE) {
+            furi_string_left(line, newline_index);
+        }
+
+        FURI_LOG_T(
+            TAG, "Read line: %s, len: %zu", furi_string_get_cstr(line), furi_string_size(line));
+
+        return true;
+    }
+
+    return false;
+}
+
+bool apdu_log_check_presence(const char* path) {
+    furi_check(path);
+
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+
+    bool log_present = storage_common_stat(storage, path, NULL) == FSE_OK;
+
+    furi_record_close(RECORD_STORAGE);
+
+    return log_present;
+}
+
+APDULog* apdu_log_alloc(const char* path, APDULogMode mode) {
+    furi_check(path);
+
+    APDULog* instance = malloc(sizeof(APDULog));
+
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+    instance->stream = buffered_file_stream_alloc(storage);
+
+    FS_OpenMode open_mode = (mode == APDULogModeOpenAlways) ? FSOM_OPEN_ALWAYS :
+                                                              FSOM_OPEN_EXISTING;
+
+    instance->total_lines = 0;
+
+    bool file_exists =
+        buffered_file_stream_open(instance->stream, path, FSAM_READ_WRITE, open_mode);
+
+    if(!file_exists) {
+        buffered_file_stream_close(instance->stream);
+    } else {
+        // Eventually add new line character in the last line to avoid skipping lines
+        apdu_log_add_ending_new_line(instance);
+    }
+
+    FuriString* line = furi_string_alloc();
+
+    bool is_endfile = false;
+
+    // In this loop we only count the entries in the file
+    // We prefer not to load the whole file in memory for space reasons
+    while(file_exists && !is_endfile) {
+        bool read_log = apdu_log_read_log_line(instance, line, &is_endfile);
+        if(read_log) {
+            instance->total_lines++;
+        }
+    }
+    stream_rewind(instance->stream);
+    FURI_LOG_I(TAG, "Loaded log with %zu lines", instance->total_lines);
+
+    furi_string_free(line);
+
+    return instance;
+}
+
+void apdu_log_free(APDULog* instance) {
+    furi_check(instance);
+    furi_check(instance->stream);
+
+    buffered_file_stream_close(instance->stream);
+    stream_free(instance->stream);
+    free(instance);
+
+    furi_record_close(RECORD_STORAGE);
+}
+
+size_t apdu_log_get_total_lines(APDULog* instance) {
+    furi_check(instance);
+
+    return instance->total_lines;
+}
+
+bool apdu_log_rewind(APDULog* instance) {
+    furi_check(instance);
+    furi_check(instance->stream);
+
+    return stream_rewind(instance->stream);
+}
+
+bool apdu_log_get_next_log_str(APDULog* instance, FuriString* log) {
+    furi_assert(instance);
+    furi_assert(instance->stream);
+    furi_assert(log);
+
+    bool log_read = false;
+    bool is_endfile = false;
+
+    furi_string_reset(log);
+
+    while(!log_read && !is_endfile)
+        log_read = apdu_log_read_log_line(instance, log, &is_endfile);
+
+    return log_read;
+}

+ 63 - 0
apdu_log.h

@@ -0,0 +1,63 @@
+#pragma once
+
+#include <flipper_format/flipper_format.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    APDULogModeOpenExisting,
+    APDULogModeOpenAlways,
+} APDULogMode;
+
+typedef struct APDULog APDULog;
+
+/** Check if the file list exists
+ *
+ * @param path      - list path
+ *
+ * @return true if list exists, false otherwise
+*/
+bool apdu_log_check_presence(const char* path);
+
+/** Open or create list
+ * Depending on mode, list will be opened or created.
+ *
+ * @param path      - Path of the file that contain the list
+ * @param mode      - ListKeysMode value
+ *
+ * @return Returns APDULog list instance
+*/
+APDULog* apdu_log_alloc(const char* path, APDULogMode mode);
+
+/** Close list
+ *
+ * @param instance  - APDULog list instance
+*/
+void apdu_log_free(APDULog* instance);
+
+/** Get total number of lines in list
+ *
+ * @param instance  - APDULog list instance
+ *
+ * @return Returns total number of lines in list
+*/
+size_t apdu_log_get_total_lines(APDULog* instance);
+
+/** Rewind list
+ *
+ * @param instance  - APDULog list instance
+ *
+ * @return Returns true if rewind was successful, false otherwise
+*/
+bool apdu_log_rewind(APDULog* instance);
+
+bool apdu_log_get_next_log_str(APDULog* instance, FuriString* log);
+
+#ifdef __cplusplus
+}
+#endif

+ 178 - 0
apdu_runner.c

@@ -0,0 +1,178 @@
+#include "apdu_runner.h"
+
+#define TAG "APDU_Runner"
+
+// Max length of firmware upgrade: 731 bytes
+#define SEADER_APDU_MAX_LEN 732
+
+void seader_apdu_runner_cleanup(Seader* seader, SeaderWorkerEvent event) {
+    SeaderWorker* seader_worker = seader->worker;
+    seader_worker_change_state(seader_worker, SeaderWorkerStateReady);
+    apdu_log_free(seader->apdu_log);
+    seader->apdu_log = NULL;
+    if(seader_worker->callback) {
+        seader_worker->callback(event, seader_worker->context);
+    }
+}
+
+bool seader_apdu_runner_send_next_line(Seader* seader) {
+    SeaderWorker* seader_worker = seader->worker;
+    SeaderUartBridge* seader_uart = seader_worker->uart;
+    SeaderAPDURunnerContext* apdu_runner_ctx = &(seader->apdu_runner_ctx);
+
+    FuriString* line = furi_string_alloc();
+    apdu_log_get_next_log_str(seader->apdu_log, line);
+
+    size_t len = furi_string_size(line) / 2; // String is in HEX, divide by 2 for bytes
+    uint8_t* apdu = malloc(len);
+    if(apdu == NULL) {
+        FURI_LOG_E(TAG, "Failed to allocate memory for APDU");
+        seader_apdu_runner_cleanup(seader, SeaderWorkerEventAPDURunnerError);
+        return false;
+    }
+
+    if(len > SEADER_UART_RX_BUF_SIZE) {
+        FURI_LOG_E(TAG, "APDU length is too long");
+        seader_apdu_runner_cleanup(seader, SeaderWorkerEventAPDURunnerError);
+        free(apdu);
+        return false;
+    }
+
+    if(!hex_chars_to_uint8(furi_string_get_cstr(line), apdu)) {
+        FURI_LOG_E(TAG, "Failed to convert line to number");
+        seader_apdu_runner_cleanup(seader, SeaderWorkerEventAPDURunnerError);
+        free(apdu);
+        return false;
+    }
+    FURI_LOG_I(
+        TAG,
+        "APDU Runner => (%d/%d): %s",
+        apdu_runner_ctx->current_line,
+        apdu_runner_ctx->total_lines,
+        furi_string_get_cstr(line));
+
+    if(seader_worker->callback) {
+        seader_worker->callback(SeaderWorkerEventAPDURunnerUpdate, seader_worker->context);
+    }
+
+    apdu_runner_ctx->current_line++;
+    if(seader_uart->T == 1) {
+        seader_send_t1(seader_uart, apdu, len);
+    } else {
+        seader_ccid_XfrBlock(seader_uart, apdu, len);
+    }
+    furi_string_free(line);
+    free(apdu);
+
+    return true;
+}
+
+void seader_apdu_runner_init(Seader* seader) {
+    SeaderAPDURunnerContext* apdu_runner_ctx = &(seader->apdu_runner_ctx);
+
+    if(apdu_log_check_presence(SEADER_APDU_RUNNER_FILE_NAME)) {
+        FURI_LOG_I(TAG, "APDU log file exists");
+    } else {
+        FURI_LOG_W(TAG, "APDU log file does not exist");
+        return;
+    }
+
+    seader->apdu_log = apdu_log_alloc(SEADER_APDU_RUNNER_FILE_NAME, APDULogModeOpenExisting);
+    apdu_runner_ctx->current_line = 0;
+    apdu_runner_ctx->total_lines = apdu_log_get_total_lines(seader->apdu_log);
+    FURI_LOG_I(TAG, "APDU log lines: %d", apdu_runner_ctx->total_lines);
+
+    seader_apdu_runner_send_next_line(seader);
+}
+
+bool seader_apdu_runner_response(Seader* seader, uint8_t* r_apdu, size_t r_len) {
+    SeaderUartBridge* seader_uart = seader->worker->uart;
+    SeaderAPDURunnerContext* apdu_runner_ctx = &(seader->apdu_runner_ctx);
+    uint8_t GET_RESPONSE[] = {0x00, 0xc0, 0x00, 0x00, 0xff};
+
+    uint8_t SW1 = r_apdu[r_len - 2];
+    uint8_t SW2 = r_apdu[r_len - 1];
+
+    switch(SW1) {
+    case 0x61:
+        //FURI_LOG_D(TAG, "Request %d bytes", SW2);
+        GET_RESPONSE[4] = SW2;
+        seader_ccid_XfrBlock(seader_uart, GET_RESPONSE, sizeof(GET_RESPONSE));
+        return true;
+    }
+
+    if(r_len < SEADER_UART_RX_BUF_SIZE) {
+        char* display = malloc(r_len * 2 + 1);
+        if(display == NULL) {
+            FURI_LOG_E(TAG, "Failed to allocate memory for display");
+            seader_apdu_runner_cleanup(seader, SeaderWorkerEventAPDURunnerError);
+            return false;
+        }
+        memset(display, 0, r_len * 2 + 1);
+        for(uint8_t i = 0; i < r_len; i++) {
+            snprintf(display + (i * 2), sizeof(display), "%02x", r_apdu[i]);
+        }
+        FURI_LOG_I(
+            TAG,
+            "APDU Runner <=: (%d/%d): %s",
+            apdu_runner_ctx->current_line,
+            apdu_runner_ctx->total_lines,
+            display);
+        free(display);
+    } else {
+        FURI_LOG_I(TAG, "APDU Runner <=: Response too long to display");
+    }
+
+    /** Compare last two bytes to expected line **/
+
+    FuriString* line = furi_string_alloc();
+    apdu_log_get_next_log_str(seader->apdu_log, line);
+    if(furi_string_size(line) % 2 == 1) {
+        FURI_LOG_E(TAG, "APDU log file has odd number of characters");
+        seader_apdu_runner_cleanup(seader, SeaderWorkerEventAPDURunnerError);
+        return false;
+    }
+
+    size_t len = furi_string_size(line) / 2; // String is in HEX, divide by 2 for bytes
+    uint8_t* apdu = malloc(len);
+    if(apdu == NULL) {
+        FURI_LOG_E(TAG, "Failed to allocate memory for APDU");
+        seader_apdu_runner_cleanup(seader, SeaderWorkerEventAPDURunnerError);
+        return false;
+    }
+
+    if(!hex_chars_to_uint8(furi_string_get_cstr(line), apdu)) {
+        FURI_LOG_E(TAG, "Failed to convert line to byte array");
+        seader_apdu_runner_cleanup(seader, SeaderWorkerEventAPDURunnerError);
+        free(apdu);
+        // TODO: Send failed event
+        return false;
+    }
+
+    apdu_runner_ctx->current_line++;
+    furi_string_free(line);
+
+    if(memcmp(r_apdu + r_len - 2, apdu + len - 2, 2) != 0) {
+        FURI_LOG_W(
+            TAG,
+            "APDU runner response does not match.  Response %02x%02x != expected %02x%02x",
+            r_apdu[r_len - 2],
+            r_apdu[r_len - 1],
+            apdu[len - 2],
+            apdu[len - 1]);
+        seader_apdu_runner_cleanup(seader, SeaderWorkerEventAPDURunnerError);
+        free(apdu);
+        return false;
+    }
+    free(apdu);
+
+    // Check if we are at the end of the log
+    if(apdu_runner_ctx->current_line >= apdu_runner_ctx->total_lines) {
+        FURI_LOG_I(TAG, "APDU runner finished");
+        seader_apdu_runner_cleanup(seader, SeaderWorkerEventAPDURunnerSuccess);
+        return false;
+    }
+
+    // Send next line
+    return seader_apdu_runner_send_next_line(seader);
+}

+ 10 - 0
apdu_runner.h

@@ -0,0 +1,10 @@
+
+#pragma once
+
+#include <lib/toolbox/hex.h>
+#include "seader_i.h"
+
+#define SEADER_APDU_RUNNER_FILE_NAME APP_DATA_PATH("script.apdu")
+
+void seader_apdu_runner_init(Seader* seader);
+bool seader_apdu_runner_response(Seader* seader, uint8_t* r_apdu, size_t r_len);

+ 4 - 28
ccid.c

@@ -147,11 +147,12 @@ void seader_ccid_XfrBlockToSlot(
     seader_uart->tx_len = header_len + len;
     seader_uart->tx_len = seader_add_lrc(seader_uart->tx_buf, seader_uart->tx_len);
 
-    char display[SEADER_UART_RX_BUF_SIZE * 2 + 1] = {0};
+    char* display = malloc(seader_uart->tx_len * 2 + 1);
     for(uint8_t i = 0; i < seader_uart->tx_len; i++) {
         snprintf(display + (i * 2), sizeof(display), "%02x", seader_uart->tx_buf[i]);
     }
     FURI_LOG_D(TAG, "seader_ccid_XfrBlockToSlot(%d) %d: %s", slot, seader_uart->tx_len, display);
+    free(display);
 
     furi_thread_flags_set(furi_thread_get_id(seader_uart->tx_thread), WorkerEvtSamRx);
 }
@@ -162,11 +163,12 @@ size_t seader_ccid_process(Seader* seader, uint8_t* cmd, size_t cmd_len) {
     CCID_Message message;
     message.consumed = 0;
 
-    char display[SEADER_UART_RX_BUF_SIZE * 2 + 1] = {0};
+    char* display = malloc(cmd_len * 2 + 1);
     for(uint8_t i = 0; i < cmd_len; i++) {
         snprintf(display + (i * 2), sizeof(display), "%02x", cmd[i]);
     }
     FURI_LOG_D(TAG, "seader_ccid_process %d: %s", cmd_len, display);
+    free(display);
 
     if(cmd_len == 2) {
         if(cmd[0] == CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange) {
@@ -253,11 +255,6 @@ size_t seader_ccid_process(Seader* seader, uint8_t* cmd, size_t cmd_len) {
         message.bError = ccid[8];
         message.payload = ccid + 10;
 
-        memset(display, 0, sizeof(display));
-        for(uint8_t i = 0; i < message.dwLength; i++) {
-            snprintf(display + (i * 2), sizeof(display), "%02x", message.payload[i]);
-        }
-
         if(cmd_len < 2 + 10 + message.dwLength + 1) {
             // Incomplete
             return message.consumed;
@@ -274,27 +271,6 @@ size_t seader_ccid_process(Seader* seader, uint8_t* cmd, size_t cmd_len) {
             return message.consumed;
         }
 
-        /*
-        if(message.dwLength == 0) {
-            FURI_LOG_D(
-                TAG,
-                "CCID [%d|%d] type: %02x, status: %02x, error: %02x",
-                message.bSlot,
-                message.bSeq,
-                message.bMessageType,
-                message.bStatus,
-                message.bError);
-        } else {
-            FURI_LOG_D(
-                TAG,
-                "CCID [%d|%d] %ld: %s",
-                message.bSlot,
-                message.bSeq,
-                message.dwLength,
-                display);
-        }
-        */
-
         //0306 81 00000000 0000 0200 01 87
         //0306 81 00000000 0000 0100 01 84
         if(message.bMessageType == CCID_MESSAGE_TYPE_RDR_to_PC_SlotStatus) {

+ 0 - 2
lib/loclass/optimized_cipher.c

@@ -144,7 +144,6 @@ static inline void loclass_opt_suc(
     bool add32Zeroes) {
     for(int i = 0; i < length; i++) {
         uint8_t head = in[i];
-#pragma GCC unroll 8
         for(int j = 0; j < 8; j++) {
             loclass_opt_successor(k, s, head);
             head >>= 1;
@@ -159,7 +158,6 @@ static inline void loclass_opt_suc(
 }
 
 static inline void loclass_opt_output(const uint8_t* k, LoclassState_t* s, uint8_t* buffer) {
-#pragma GCC unroll 4
     for(uint8_t times = 0; times < 4; times++) {
         uint8_t bout = 0;
         bout |= (s->r & 0x4) >> 2;

+ 1 - 1
readme.md

@@ -43,7 +43,7 @@ Optionally 3d print a [case designed by sean](https://www.printables.com/model/5
 ### To Build ASN1 (if you change seader.asn1)
 
  * Install git version of [asnc1](https://github.com/vlm/asn1c) (`brew install asn1c --head` on macos)
- * Run `asn1c -D ./lib/asn1 -no-gen-example -pdu=all seader.asn` in in root to generate asn1c files
+ * Run `asn1c -D ./lib/asn1 -no-gen-example -pdu=all seader.asn1` in in root to generate asn1c files
 
 ## References
 

+ 16 - 4
sam_api.c

@@ -157,7 +157,13 @@ bool seader_send_apdu(
         return false;
     }
 
-    uint8_t apdu[SEADER_UART_RX_BUF_SIZE];
+    uint8_t length = APDU_HEADER_LEN + payloadLen;
+    uint8_t* apdu = malloc(length);
+    if(!apdu) {
+        FURI_LOG_E(TAG, "Failed to allocate memory for apdu in seader_send_apdu");
+        return false;
+    }
+
     apdu[0] = CLA;
     apdu[1] = INS;
     apdu[2] = P1;
@@ -172,7 +178,6 @@ bool seader_send_apdu(
     }
 
     memcpy(apdu + APDU_HEADER_LEN, payload, payloadLen);
-    uint8_t length = APDU_HEADER_LEN + payloadLen;
 
     memset(display, 0, sizeof(display));
     for(uint8_t i = 0; i < length; i++) {
@@ -185,6 +190,7 @@ bool seader_send_apdu(
     } else {
         seader_ccid_XfrBlock(seader_uart, apdu, length);
     }
+    free(apdu);
 
     return true;
 }
@@ -981,17 +987,19 @@ bool seader_worker_state_machine(
 
     switch(payload->present) {
     case Payload_PR_response:
+        FURI_LOG_D(TAG, "Payload_PR_response");
         seader_parse_response(seader, &payload->choice.response);
         processed = true;
         break;
     case Payload_PR_nfcCommand:
+        FURI_LOG_D(TAG, "Payload_PR_nfcCommand");
         if(online) {
             seader_parse_nfc_command(seader, &payload->choice.nfcCommand, spc);
             processed = true;
         }
         break;
     case Payload_PR_errorResponse:
-        FURI_LOG_W(TAG, "Error Response");
+        FURI_LOG_W(TAG, "Payload_PR_errorResponse");
         processed = true;
         view_dispatcher_send_custom_event(seader->view_dispatcher, SeaderCustomEventWorkerExit);
         break;
@@ -1031,8 +1039,12 @@ bool seader_process_success_response_i(
                 ->op->print_struct(
                     &asn_DEF_Payload, payload, 1, seader_print_struct_callback, payloadDebug);
             if(strlen(payloadDebug) > 0) {
-                FURI_LOG_D(TAG, "Payload: %s", payloadDebug);
+                FURI_LOG_D(TAG, "Received Payload: %s", payloadDebug);
+            } else {
+                FURI_LOG_D(TAG, "Received empty Payload");
             }
+        } else {
+            FURI_LOG_D(TAG, "Online mode");
         }
 #endif
 

+ 78 - 0
scenes/seader_scene_apdu_runner.c

@@ -0,0 +1,78 @@
+#include "../seader_i.h"
+#include <dolphin/dolphin.h>
+
+#define TAG "Seader:Scene:APDURunner"
+
+char seader_scene_apdu_runner_update_text[24];
+
+void seader_apdu_runner_worker_callback(SeaderWorkerEvent event, void* context) {
+    Seader* seader = context;
+    view_dispatcher_send_custom_event(seader->view_dispatcher, event);
+}
+
+void seader_scene_apdu_runner_on_enter(void* context) {
+    Seader* seader = context;
+    // Setup view
+    Popup* popup = seader->popup;
+    popup_set_header(popup, "APDU Runner", 68, 30, AlignLeft, AlignTop);
+
+    // TODO: Make icon for interaction with SAM
+    popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
+
+    // Start worker
+    seader_worker_start(
+        seader->worker,
+        SeaderWorkerStateAPDURunner,
+        seader->uart,
+        seader_apdu_runner_worker_callback,
+        seader);
+
+    view_dispatcher_switch_to_view(seader->view_dispatcher, SeaderViewPopup);
+}
+
+bool seader_scene_apdu_runner_on_event(void* context, SceneManagerEvent event) {
+    Seader* seader = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == GuiButtonTypeLeft) {
+            scene_manager_search_and_switch_to_previous_scene(
+                seader->scene_manager, SeaderSceneSamPresent);
+            consumed = true;
+        } else if(event.event == SeaderWorkerEventAPDURunnerUpdate) {
+            SeaderAPDURunnerContext apdu_runner_ctx = seader->apdu_runner_ctx;
+            Popup* popup = seader->popup;
+            snprintf(
+                seader_scene_apdu_runner_update_text,
+                sizeof(seader_scene_apdu_runner_update_text),
+                "APDU Runner\n%d/%d",
+                apdu_runner_ctx.current_line,
+                apdu_runner_ctx.total_lines);
+            popup_set_header(
+                popup, seader_scene_apdu_runner_update_text, 68, 30, AlignLeft, AlignTop);
+            consumed = true;
+        } else if(event.event == SeaderWorkerEventAPDURunnerSuccess) {
+            notification_message(seader->notifications, &sequence_success);
+            Popup* popup = seader->popup;
+            popup_set_header(popup, "APDU Runner\nSuccess", 68, 30, AlignLeft, AlignTop);
+            consumed = true;
+        } else if(event.event == SeaderWorkerEventAPDURunnerError) {
+            notification_message(seader->notifications, &sequence_error);
+            Popup* popup = seader->popup;
+            popup_set_header(popup, "APDU Runner\nError", 68, 30, AlignLeft, AlignTop);
+            consumed = true;
+        }
+    } else if(event.type == SceneManagerEventTypeBack) {
+        scene_manager_search_and_switch_to_previous_scene(
+            seader->scene_manager, SeaderSceneSamPresent);
+        consumed = true;
+    }
+    return consumed;
+}
+
+void seader_scene_apdu_runner_on_exit(void* context) {
+    Seader* seader = context;
+
+    // Clear view
+    popup_reset(seader->popup);
+}

+ 1 - 0
scenes/seader_scene_config.h

@@ -17,3 +17,4 @@ ADD_SCENE(seader, sam_info, SamInfo)
 ADD_SCENE(seader, virtual_credential, VirtualCredential)
 ADD_SCENE(seader, formats, Formats)
 ADD_SCENE(seader, read_mfc, ReadMfc)
+ADD_SCENE(seader, apdu_runner, APDURunner)

+ 0 - 2
scenes/seader_scene_read_14a.c

@@ -29,7 +29,6 @@ bool seader_scene_read_14a_on_event(void* context, SceneManagerEvent event) {
 
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == SeaderCustomEventWorkerExit) {
-            seader->credential->type = SeaderCredentialType14A;
             scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess);
             consumed = true;
         } else if(event.event == SeaderCustomEventPollerDetect) {
@@ -37,7 +36,6 @@ bool seader_scene_read_14a_on_event(void* context, SceneManagerEvent event) {
             popup_set_header(popup, "DON'T\nMOVE", 68, 30, AlignLeft, AlignTop);
             consumed = true;
         } else if(event.event == SeaderCustomEventPollerSuccess) {
-            seader->credential->type = SeaderCredentialType14A;
             scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess);
             consumed = true;
         }

+ 0 - 2
scenes/seader_scene_read_mfc.c

@@ -33,7 +33,6 @@ bool seader_scene_read_mfc_on_event(void* context, SceneManagerEvent event) {
 
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == SeaderCustomEventWorkerExit) {
-            seader->credential->type = SeaderCredentialTypeMifareClassic;
             scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess);
             consumed = true;
         } else if(event.event == SeaderCustomEventPollerDetect) {
@@ -41,7 +40,6 @@ bool seader_scene_read_mfc_on_event(void* context, SceneManagerEvent event) {
             popup_set_header(popup, "DON'T\nMOVE", 68, 30, AlignLeft, AlignTop);
             consumed = true;
         } else if(event.event == SeaderCustomEventPollerSuccess) {
-            seader->credential->type = SeaderCredentialTypeMifareClassic;
             scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess);
             consumed = true;
         }

+ 0 - 2
scenes/seader_scene_read_picopass.c

@@ -28,7 +28,6 @@ bool seader_scene_read_picopass_on_event(void* context, SceneManagerEvent event)
 
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == SeaderCustomEventWorkerExit) {
-            seader->credential->type = SeaderCredentialTypePicopass;
             scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess);
             consumed = true;
         } else if(event.event == SeaderCustomEventPollerDetect) {
@@ -36,7 +35,6 @@ bool seader_scene_read_picopass_on_event(void* context, SceneManagerEvent event)
             popup_set_header(popup, "DON'T\nMOVE", 68, 30, AlignLeft, AlignTop);
             consumed = true;
         } else if(event.event == SeaderCustomEventPollerSuccess) {
-            seader->credential->type = SeaderCredentialTypePicopass;
             scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess);
             consumed = true;
         }

+ 14 - 8
scenes/seader_scene_sam_present.c

@@ -4,6 +4,7 @@ enum SubmenuIndex {
     SubmenuIndexRead14a,
     SubmenuIndexReadMfc,
     SubmenuIndexSaved,
+    SubmenuIndexAPDURunner,
     SubmenuIndexSamInfo,
     SubmenuIndexFwVersion,
 };
@@ -43,6 +44,14 @@ void seader_scene_sam_present_on_update(void* context) {
     submenu_add_item(
         submenu, "Saved", SubmenuIndexSaved, seader_scene_sam_present_submenu_callback, seader);
 
+    if(apdu_log_check_presence(SEADER_APDU_RUNNER_FILE_NAME)) {
+        submenu_add_item(
+            submenu,
+            "Run APDUs",
+            SubmenuIndexAPDURunner,
+            seader_scene_sam_present_submenu_callback,
+            seader);
+    }
     if(seader_worker->sam_version[0] != 0 && seader_worker->sam_version[1] != 0) {
         FuriString* fw_str = furi_string_alloc();
         furi_string_cat_printf(
@@ -72,24 +81,18 @@ bool seader_scene_sam_present_on_event(void* context, SceneManagerEvent event) {
     bool consumed = false;
 
     if(event.type == SceneManagerEventTypeCustom) {
+        scene_manager_set_scene_state(seader->scene_manager, SeaderSceneSamPresent, event.event);
+
         if(event.event == SubmenuIndexReadPicopass) {
-            scene_manager_set_scene_state(
-                seader->scene_manager, SeaderSceneSamPresent, SubmenuIndexReadPicopass);
             scene_manager_next_scene(seader->scene_manager, SeaderSceneReadPicopass);
             consumed = true;
         } else if(event.event == SubmenuIndexRead14a) {
-            scene_manager_set_scene_state(
-                seader->scene_manager, SeaderSceneSamPresent, SubmenuIndexRead14a);
             scene_manager_next_scene(seader->scene_manager, SeaderSceneRead14a);
             consumed = true;
         } else if(event.event == SubmenuIndexReadMfc) {
-            scene_manager_set_scene_state(
-                seader->scene_manager, SeaderSceneSamPresent, SubmenuIndexReadMfc);
             scene_manager_next_scene(seader->scene_manager, SeaderSceneReadMfc);
             consumed = true;
         } else if(event.event == SubmenuIndexSamInfo) {
-            scene_manager_set_scene_state(
-                seader->scene_manager, SeaderSceneSamPresent, SubmenuIndexSamInfo);
             scene_manager_next_scene(seader->scene_manager, SeaderSceneSamInfo);
             consumed = true;
         } else if(event.event == SubmenuIndexSaved) {
@@ -100,6 +103,9 @@ bool seader_scene_sam_present_on_event(void* context, SceneManagerEvent event) {
         } else if(event.event == SeaderWorkerEventSamMissing) {
             scene_manager_next_scene(seader->scene_manager, SeaderSceneSamMissing);
             consumed = true;
+        } else if(event.event == SubmenuIndexAPDURunner) {
+            scene_manager_next_scene(seader->scene_manager, SeaderSceneAPDURunner);
+            consumed = true;
         }
     } else if(event.type == SceneManagerEventTypeBack) {
         scene_manager_stop(seader->scene_manager);

+ 9 - 0
seader_i.h

@@ -54,6 +54,7 @@
 #include "t_1.h"
 #include "seader_worker.h"
 #include "seader_credential.h"
+#include "apdu_log.h"
 
 #define WORKER_ALL_RX_EVENTS                                                      \
     (WorkerEvtStop | WorkerEvtRxDone | WorkerEvtCfgChange | WorkerEvtLineCfgSet | \
@@ -89,6 +90,11 @@ typedef enum {
     WorkerEvtCtrlLineSet = (1 << 7),
 } WorkerEvtFlags;
 
+typedef struct {
+    uint16_t total_lines;
+    uint16_t current_line;
+} SeaderAPDURunnerContext;
+
 struct Seader {
     bool revert_power;
     bool is_debug_enabled;
@@ -120,6 +126,9 @@ struct Seader {
 
     PluginManager* plugin_manager;
     PluginWiegand* plugin_wiegand;
+
+    APDULog* apdu_log;
+    SeaderAPDURunnerContext apdu_runner_ctx;
 };
 
 struct SeaderPollerContainer {

+ 17 - 3
seader_worker.c

@@ -13,8 +13,6 @@
     (FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_TX_MANUAL | FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | \
      FURI_HAL_NFC_LL_TXRX_FLAGS_PAR_RX_REMV | FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP)
 
-char display[SEADER_UART_RX_BUF_SIZE * 2 + 1] = {0};
-
 // Forward declaration
 void seader_send_card_detected(SeaderUartBridge* seader_uart, CardDetails_t* cardDetails);
 
@@ -117,11 +115,18 @@ bool seader_worker_process_sam_message(Seader* seader, uint8_t* apdu, uint32_t l
     if(len < 2) {
         return false;
     }
-    memset(display, 0, sizeof(display));
+
+    if(seader_worker->state == SeaderWorkerStateAPDURunner) {
+        return seader_apdu_runner_response(seader, apdu, len);
+    }
+
+    char* display = malloc(len * 2 + 1);
+    memset(display, 0, len * 2 + 1);
     for(uint8_t i = 0; i < len; i++) {
         snprintf(display + (i * 2), sizeof(display), "%02x", apdu[i]);
     }
     FURI_LOG_I(TAG, "APDU: %s", display);
+    free(display);
 
     uint8_t SW1 = apdu[len - 2];
     uint8_t SW2 = apdu[len - 1];
@@ -210,6 +215,10 @@ int32_t seader_worker_task(void* context) {
     } else if(seader_worker->state == SeaderWorkerStateVirtualCredential) {
         FURI_LOG_D(TAG, "Virtual Credential");
         seader_worker_virtual_credential(seader);
+    } else if(seader_worker->state == SeaderWorkerStateAPDURunner) {
+        FURI_LOG_D(TAG, "APDU Runner");
+        seader_apdu_runner_init(seader);
+        return 0;
     }
     seader_worker_change_state(seader_worker, SeaderWorkerStateReady);
 
@@ -286,6 +295,11 @@ NfcCommand seader_worker_poller_callback_iso14443_4a(NfcGenericEvent event, void
             seader_worker_poller_conversation(seader, &spc);
         } else if(seader_worker->stage == SeaderPollerEventTypeComplete) {
             ret = NfcCommandStop;
+        } else if(seader_worker->stage == SeaderPollerEventTypeFail) {
+            ret = NfcCommandStop;
+            view_dispatcher_send_custom_event(
+                seader->view_dispatcher, SeaderCustomEventWorkerExit);
+            FURI_LOG_W(TAG, "SeaderPollerEventTypeFail");
         }
     } else if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeError) {
         Iso14443_4aPollerEventData* data = iso14443_4a_event->data;

+ 5 - 0
seader_worker.h

@@ -6,6 +6,7 @@
 #include "sam_api.h"
 #include "seader_credential.h"
 #include "seader_bridge.h"
+#include "apdu_runner.h"
 
 typedef struct SeaderWorker SeaderWorker;
 typedef struct CCID_Message CCID_Message;
@@ -19,6 +20,7 @@ typedef enum {
     // Main worker states
     SeaderWorkerStateCheckSam,
     SeaderWorkerStateVirtualCredential,
+    SeaderWorkerStateAPDURunner,
     // Transition
     SeaderWorkerStateStop,
 } SeaderWorkerState;
@@ -35,6 +37,9 @@ typedef enum {
     SeaderWorkerEventSamMissing,
     SeaderWorkerEventNoCardDetected,
     SeaderWorkerEventStartReading,
+    SeaderWorkerEventAPDURunnerUpdate,
+    SeaderWorkerEventAPDURunnerSuccess,
+    SeaderWorkerEventAPDURunnerError,
 } SeaderWorkerEvent;
 
 typedef enum {

+ 27 - 39
t_1.c

@@ -69,10 +69,27 @@ void seader_t_1_send_ack(Seader* seader) {
 BitBuffer* seader_t_1_tx_buffer;
 size_t seader_t_1_tx_buffer_offset = 0;
 
-void seader_send_t1(SeaderUartBridge* seader_uart, uint8_t* apdu, size_t len) {
-    uint8_t frame[SEADER_UART_RX_BUF_SIZE];
+void seader_send_t1_chunk(SeaderUartBridge* seader_uart, uint8_t PCB, uint8_t* chunk, size_t len) {
+    uint8_t* frame = malloc(3 + len + 1);
     uint8_t frame_len = 0;
 
+    frame[0] = NAD;
+    frame[1] = PCB;
+    frame[2] = len;
+    frame_len = 3;
+
+    if(len > 0) {
+        memcpy(frame + frame_len, chunk, len);
+        frame_len += len;
+    }
+
+    frame_len = seader_add_lrc(frame, frame_len);
+
+    seader_ccid_XfrBlock(seader_uart, frame, frame_len);
+    free(frame);
+}
+
+void seader_send_t1(SeaderUartBridge* seader_uart, uint8_t* apdu, size_t len) {
     if(len > IFSC_VALUE) {
         if(seader_t_1_tx_buffer == NULL) {
             seader_t_1_tx_buffer = bit_buffer_alloc(768);
@@ -82,33 +99,16 @@ void seader_send_t1(SeaderUartBridge* seader_uart, uint8_t* apdu, size_t len) {
             (bit_buffer_get_size_bytes(seader_t_1_tx_buffer) - seader_t_1_tx_buffer_offset);
         size_t copy_length = remaining > IFSC_VALUE ? IFSC_VALUE : remaining;
 
-        frame[0] = NAD;
+        uint8_t* chunk =
+            (uint8_t*)bit_buffer_get_data(seader_t_1_tx_buffer) + seader_t_1_tx_buffer_offset;
+
         if(remaining > IFSC_VALUE) {
-            frame[1] = seader_next_dpcb() | MORE_BIT;
+            uint8_t PCB = seader_next_dpcb() | MORE_BIT;
+            seader_send_t1_chunk(seader_uart, PCB, chunk, copy_length);
         } else {
-            frame[1] = seader_next_dpcb();
+            uint8_t PCB = seader_next_dpcb();
+            seader_send_t1_chunk(seader_uart, PCB, chunk, copy_length);
         }
-        frame[2] = copy_length;
-        frame_len = 3;
-
-        memcpy(
-            frame + frame_len,
-            bit_buffer_get_data(seader_t_1_tx_buffer) + seader_t_1_tx_buffer_offset,
-            copy_length);
-        frame_len += copy_length;
-
-        frame_len = seader_add_lrc(frame, frame_len);
-
-        /*
-        FURI_LOG_D(
-            TAG,
-            "Sending T=1 frame %s more bit set.  Remaining: %d, Copy Length: %d",
-            (remaining > IFSC_VALUE) ? "with" : "without",
-            remaining,
-            copy_length);
-            */
-
-        seader_ccid_XfrBlock(seader_uart, frame, frame_len);
 
         seader_t_1_tx_buffer_offset += copy_length;
         if(seader_t_1_tx_buffer_offset >= bit_buffer_get_size_bytes(seader_t_1_tx_buffer)) {
@@ -119,19 +119,7 @@ void seader_send_t1(SeaderUartBridge* seader_uart, uint8_t* apdu, size_t len) {
         return;
     }
 
-    frame[0] = NAD;
-    frame[1] = seader_next_dpcb();
-    frame[2] = len;
-    frame_len = 3;
-
-    if(len > 0) {
-        memcpy(frame + frame_len, apdu, len);
-        frame_len += len;
-    }
-
-    frame_len = seader_add_lrc(frame, frame_len);
-
-    seader_ccid_XfrBlock(seader_uart, frame, frame_len);
+    seader_send_t1_chunk(seader_uart, seader_next_dpcb(), apdu, len);
 }
 
 BitBuffer* seader_t_1_rx_buffer;

+ 1 - 1
uart.c

@@ -98,7 +98,7 @@ int32_t seader_uart_worker(void* context) {
     seader_uart->tx_sem = furi_semaphore_alloc(1, 1);
 
     seader_uart->tx_thread =
-        furi_thread_alloc_ex("SeaderUartTxWorker", 3 * 1024, seader_uart_tx_thread, seader);
+        furi_thread_alloc_ex("SeaderUartTxWorker", 1.5 * 1024, seader_uart_tx_thread, seader);
 
     seader_uart_serial_init(seader_uart, seader_uart->cfg.uart_ch);
     seader_uart_set_baudrate(seader_uart, seader_uart->cfg.baudrate);