Skip to content

Commit

Permalink
Merge pull request #21 from bettse/apdu_runner
Browse files Browse the repository at this point in the history
APDU Runner
  • Loading branch information
bettse authored Jul 27, 2024
2 parents b5c45a9 + 6002037 commit 493c703
Show file tree
Hide file tree
Showing 11 changed files with 511 additions and 9 deletions.
144 changes: 144 additions & 0 deletions apdu_log.c
Original file line number Diff line number Diff line change
@@ -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 changes: 63 additions & 0 deletions apdu_log.h
Original file line number Diff line number Diff line change
@@ -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 changes: 178 additions & 0 deletions apdu_runner.c
Original file line number Diff line number Diff line change
@@ -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 changes: 10 additions & 0 deletions apdu_runner.h
Original file line number Diff line number Diff line change
@@ -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);
Loading

0 comments on commit 493c703

Please sign in to comment.