forked from flipperdevices/flipperzero-firmware
-
-
Notifications
You must be signed in to change notification settings - Fork 547
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
508 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); |
Oops, something went wrong.