Skip to content

Commit 8b65648

Browse files
committed
APDURunner
1 parent b5c45a9 commit 8b65648

10 files changed

+508
-0
lines changed

apdu_log.c

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
#include "apdu_log.h"
2+
3+
#include <storage/storage.h>
4+
#include <flipper_format/flipper_format.h>
5+
#include <toolbox/stream/file_stream.h>
6+
#include <toolbox/stream/buffered_file_stream.h>
7+
#include <toolbox/args.h>
8+
9+
#define TAG "APDULog"
10+
11+
struct APDULog {
12+
Stream* stream;
13+
size_t total_lines;
14+
};
15+
16+
static inline void apdu_log_add_ending_new_line(APDULog* instance) {
17+
if(stream_seek(instance->stream, -1, StreamOffsetFromEnd)) {
18+
uint8_t last_char = 0;
19+
20+
// Check if the last char is new line or add a new line
21+
if(stream_read(instance->stream, &last_char, 1) == 1 && last_char != '\n') {
22+
FURI_LOG_D(TAG, "Adding new line ending");
23+
stream_write_char(instance->stream, '\n');
24+
}
25+
26+
stream_rewind(instance->stream);
27+
}
28+
}
29+
30+
static bool apdu_log_read_log_line(APDULog* instance, FuriString* line, bool* is_endfile) {
31+
if(stream_read_line(instance->stream, line) == false) {
32+
*is_endfile = true;
33+
}
34+
35+
else {
36+
size_t newline_index = furi_string_search_char(line, '\n', 0);
37+
38+
if(newline_index != FURI_STRING_FAILURE) {
39+
furi_string_left(line, newline_index);
40+
}
41+
42+
FURI_LOG_T(
43+
TAG, "Read line: %s, len: %zu", furi_string_get_cstr(line), furi_string_size(line));
44+
45+
return true;
46+
}
47+
48+
return false;
49+
}
50+
51+
bool apdu_log_check_presence(const char* path) {
52+
furi_check(path);
53+
54+
Storage* storage = furi_record_open(RECORD_STORAGE);
55+
56+
bool log_present = storage_common_stat(storage, path, NULL) == FSE_OK;
57+
58+
furi_record_close(RECORD_STORAGE);
59+
60+
return log_present;
61+
}
62+
63+
APDULog* apdu_log_alloc(const char* path, APDULogMode mode) {
64+
furi_check(path);
65+
66+
APDULog* instance = malloc(sizeof(APDULog));
67+
68+
Storage* storage = furi_record_open(RECORD_STORAGE);
69+
instance->stream = buffered_file_stream_alloc(storage);
70+
71+
FS_OpenMode open_mode = (mode == APDULogModeOpenAlways) ? FSOM_OPEN_ALWAYS :
72+
FSOM_OPEN_EXISTING;
73+
74+
instance->total_lines = 0;
75+
76+
bool file_exists =
77+
buffered_file_stream_open(instance->stream, path, FSAM_READ_WRITE, open_mode);
78+
79+
if(!file_exists) {
80+
buffered_file_stream_close(instance->stream);
81+
} else {
82+
// Eventually add new line character in the last line to avoid skipping lines
83+
apdu_log_add_ending_new_line(instance);
84+
}
85+
86+
FuriString* line = furi_string_alloc();
87+
88+
bool is_endfile = false;
89+
90+
// In this loop we only count the entries in the file
91+
// We prefer not to load the whole file in memory for space reasons
92+
while(file_exists && !is_endfile) {
93+
bool read_log = apdu_log_read_log_line(instance, line, &is_endfile);
94+
if(read_log) {
95+
instance->total_lines++;
96+
}
97+
}
98+
stream_rewind(instance->stream);
99+
FURI_LOG_I(TAG, "Loaded log with %zu lines", instance->total_lines);
100+
101+
furi_string_free(line);
102+
103+
return instance;
104+
}
105+
106+
void apdu_log_free(APDULog* instance) {
107+
furi_check(instance);
108+
furi_check(instance->stream);
109+
110+
buffered_file_stream_close(instance->stream);
111+
stream_free(instance->stream);
112+
free(instance);
113+
114+
furi_record_close(RECORD_STORAGE);
115+
}
116+
117+
size_t apdu_log_get_total_lines(APDULog* instance) {
118+
furi_check(instance);
119+
120+
return instance->total_lines;
121+
}
122+
123+
bool apdu_log_rewind(APDULog* instance) {
124+
furi_check(instance);
125+
furi_check(instance->stream);
126+
127+
return stream_rewind(instance->stream);
128+
}
129+
130+
bool apdu_log_get_next_log_str(APDULog* instance, FuriString* log) {
131+
furi_assert(instance);
132+
furi_assert(instance->stream);
133+
furi_assert(log);
134+
135+
bool log_read = false;
136+
bool is_endfile = false;
137+
138+
furi_string_reset(log);
139+
140+
while(!log_read && !is_endfile)
141+
log_read = apdu_log_read_log_line(instance, log, &is_endfile);
142+
143+
return log_read;
144+
}

apdu_log.h

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#pragma once
2+
3+
#include <flipper_format/flipper_format.h>
4+
#include <stdbool.h>
5+
#include <stdint.h>
6+
#include <stddef.h>
7+
8+
#ifdef __cplusplus
9+
extern "C" {
10+
#endif
11+
12+
typedef enum {
13+
APDULogModeOpenExisting,
14+
APDULogModeOpenAlways,
15+
} APDULogMode;
16+
17+
typedef struct APDULog APDULog;
18+
19+
/** Check if the file list exists
20+
*
21+
* @param path - list path
22+
*
23+
* @return true if list exists, false otherwise
24+
*/
25+
bool apdu_log_check_presence(const char* path);
26+
27+
/** Open or create list
28+
* Depending on mode, list will be opened or created.
29+
*
30+
* @param path - Path of the file that contain the list
31+
* @param mode - ListKeysMode value
32+
*
33+
* @return Returns APDULog list instance
34+
*/
35+
APDULog* apdu_log_alloc(const char* path, APDULogMode mode);
36+
37+
/** Close list
38+
*
39+
* @param instance - APDULog list instance
40+
*/
41+
void apdu_log_free(APDULog* instance);
42+
43+
/** Get total number of lines in list
44+
*
45+
* @param instance - APDULog list instance
46+
*
47+
* @return Returns total number of lines in list
48+
*/
49+
size_t apdu_log_get_total_lines(APDULog* instance);
50+
51+
/** Rewind list
52+
*
53+
* @param instance - APDULog list instance
54+
*
55+
* @return Returns true if rewind was successful, false otherwise
56+
*/
57+
bool apdu_log_rewind(APDULog* instance);
58+
59+
bool apdu_log_get_next_log_str(APDULog* instance, FuriString* log);
60+
61+
#ifdef __cplusplus
62+
}
63+
#endif

apdu_runner.c

+178
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
#include "apdu_runner.h"
2+
3+
#define TAG "APDU_Runner"
4+
5+
// Max length of firmware upgrade: 731 bytes
6+
#define SEADER_APDU_MAX_LEN 732
7+
8+
void seader_apdu_runner_cleanup(Seader* seader, SeaderWorkerEvent event) {
9+
SeaderWorker* seader_worker = seader->worker;
10+
seader_worker_change_state(seader_worker, SeaderWorkerStateReady);
11+
apdu_log_free(seader->apdu_log);
12+
seader->apdu_log = NULL;
13+
if(seader_worker->callback) {
14+
seader_worker->callback(event, seader_worker->context);
15+
}
16+
}
17+
18+
bool seader_apdu_runner_send_next_line(Seader* seader) {
19+
SeaderWorker* seader_worker = seader->worker;
20+
SeaderUartBridge* seader_uart = seader_worker->uart;
21+
SeaderAPDURunnerContext* apdu_runner_ctx = &(seader->apdu_runner_ctx);
22+
23+
FuriString* line = furi_string_alloc();
24+
apdu_log_get_next_log_str(seader->apdu_log, line);
25+
26+
size_t len = furi_string_size(line) / 2; // String is in HEX, divide by 2 for bytes
27+
uint8_t* apdu = malloc(len);
28+
if(apdu == NULL) {
29+
FURI_LOG_E(TAG, "Failed to allocate memory for APDU");
30+
seader_apdu_runner_cleanup(seader, SeaderWorkerEventAPDURunnerError);
31+
return false;
32+
}
33+
34+
if(len > SEADER_UART_RX_BUF_SIZE) {
35+
FURI_LOG_E(TAG, "APDU length is too long");
36+
seader_apdu_runner_cleanup(seader, SeaderWorkerEventAPDURunnerError);
37+
free(apdu);
38+
return false;
39+
}
40+
41+
if(!hex_chars_to_uint8(furi_string_get_cstr(line), apdu)) {
42+
FURI_LOG_E(TAG, "Failed to convert line to number");
43+
seader_apdu_runner_cleanup(seader, SeaderWorkerEventAPDURunnerError);
44+
free(apdu);
45+
return false;
46+
}
47+
FURI_LOG_I(
48+
TAG,
49+
"APDU Runner => (%d/%d): %s",
50+
apdu_runner_ctx->current_line,
51+
apdu_runner_ctx->total_lines,
52+
furi_string_get_cstr(line));
53+
54+
if(seader_worker->callback) {
55+
seader_worker->callback(SeaderWorkerEventAPDURunnerUpdate, seader_worker->context);
56+
}
57+
58+
apdu_runner_ctx->current_line++;
59+
if(seader_uart->T == 1) {
60+
seader_send_t1(seader_uart, apdu, len);
61+
} else {
62+
seader_ccid_XfrBlock(seader_uart, apdu, len);
63+
}
64+
furi_string_free(line);
65+
free(apdu);
66+
67+
return true;
68+
}
69+
70+
void seader_apdu_runner_init(Seader* seader) {
71+
SeaderAPDURunnerContext* apdu_runner_ctx = &(seader->apdu_runner_ctx);
72+
73+
if(apdu_log_check_presence(SEADER_APDU_RUNNER_FILE_NAME)) {
74+
FURI_LOG_I(TAG, "APDU log file exists");
75+
} else {
76+
FURI_LOG_W(TAG, "APDU log file does not exist");
77+
return;
78+
}
79+
80+
seader->apdu_log = apdu_log_alloc(SEADER_APDU_RUNNER_FILE_NAME, APDULogModeOpenExisting);
81+
apdu_runner_ctx->current_line = 0;
82+
apdu_runner_ctx->total_lines = apdu_log_get_total_lines(seader->apdu_log);
83+
FURI_LOG_I(TAG, "APDU log lines: %d", apdu_runner_ctx->total_lines);
84+
85+
seader_apdu_runner_send_next_line(seader);
86+
}
87+
88+
bool seader_apdu_runner_response(Seader* seader, uint8_t* r_apdu, size_t r_len) {
89+
SeaderUartBridge* seader_uart = seader->worker->uart;
90+
SeaderAPDURunnerContext* apdu_runner_ctx = &(seader->apdu_runner_ctx);
91+
uint8_t GET_RESPONSE[] = {0x00, 0xc0, 0x00, 0x00, 0xff};
92+
93+
uint8_t SW1 = r_apdu[r_len - 2];
94+
uint8_t SW2 = r_apdu[r_len - 1];
95+
96+
switch(SW1) {
97+
case 0x61:
98+
//FURI_LOG_D(TAG, "Request %d bytes", SW2);
99+
GET_RESPONSE[4] = SW2;
100+
seader_ccid_XfrBlock(seader_uart, GET_RESPONSE, sizeof(GET_RESPONSE));
101+
return true;
102+
}
103+
104+
if(r_len < SEADER_UART_RX_BUF_SIZE) {
105+
char* display = malloc(r_len * 2 + 1);
106+
if(display == NULL) {
107+
FURI_LOG_E(TAG, "Failed to allocate memory for display");
108+
seader_apdu_runner_cleanup(seader, SeaderWorkerEventAPDURunnerError);
109+
return false;
110+
}
111+
memset(display, 0, r_len * 2 + 1);
112+
for(uint8_t i = 0; i < r_len; i++) {
113+
snprintf(display + (i * 2), sizeof(display), "%02x", r_apdu[i]);
114+
}
115+
FURI_LOG_I(
116+
TAG,
117+
"APDU Runner <=: (%d/%d): %s",
118+
apdu_runner_ctx->current_line,
119+
apdu_runner_ctx->total_lines,
120+
display);
121+
free(display);
122+
} else {
123+
FURI_LOG_I(TAG, "APDU Runner <=: Response too long to display");
124+
}
125+
126+
/** Compare last two bytes to expected line **/
127+
128+
FuriString* line = furi_string_alloc();
129+
apdu_log_get_next_log_str(seader->apdu_log, line);
130+
if(furi_string_size(line) % 2 == 1) {
131+
FURI_LOG_E(TAG, "APDU log file has odd number of characters");
132+
seader_apdu_runner_cleanup(seader, SeaderWorkerEventAPDURunnerError);
133+
return false;
134+
}
135+
136+
size_t len = furi_string_size(line) / 2; // String is in HEX, divide by 2 for bytes
137+
uint8_t* apdu = malloc(len);
138+
if(apdu == NULL) {
139+
FURI_LOG_E(TAG, "Failed to allocate memory for APDU");
140+
seader_apdu_runner_cleanup(seader, SeaderWorkerEventAPDURunnerError);
141+
return false;
142+
}
143+
144+
if(!hex_chars_to_uint8(furi_string_get_cstr(line), apdu)) {
145+
FURI_LOG_E(TAG, "Failed to convert line to byte array");
146+
seader_apdu_runner_cleanup(seader, SeaderWorkerEventAPDURunnerError);
147+
free(apdu);
148+
// TODO: Send failed event
149+
return false;
150+
}
151+
152+
apdu_runner_ctx->current_line++;
153+
furi_string_free(line);
154+
155+
if(memcmp(r_apdu + r_len - 2, apdu + len - 2, 2) != 0) {
156+
FURI_LOG_W(
157+
TAG,
158+
"APDU runner response does not match. Response %02x%02x != expected %02x%02x",
159+
r_apdu[r_len - 2],
160+
r_apdu[r_len - 1],
161+
apdu[len - 2],
162+
apdu[len - 1]);
163+
seader_apdu_runner_cleanup(seader, SeaderWorkerEventAPDURunnerError);
164+
free(apdu);
165+
return false;
166+
}
167+
free(apdu);
168+
169+
// Check if we are at the end of the log
170+
if(apdu_runner_ctx->current_line >= apdu_runner_ctx->total_lines) {
171+
FURI_LOG_I(TAG, "APDU runner finished");
172+
seader_apdu_runner_cleanup(seader, SeaderWorkerEventAPDURunnerSuccess);
173+
return false;
174+
}
175+
176+
// Send next line
177+
return seader_apdu_runner_send_next_line(seader);
178+
}

apdu_runner.h

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
#pragma once
3+
4+
#include <lib/toolbox/hex.h>
5+
#include "seader_i.h"
6+
7+
#define SEADER_APDU_RUNNER_FILE_NAME APP_DATA_PATH("script.apdu")
8+
9+
void seader_apdu_runner_init(Seader* seader);
10+
bool seader_apdu_runner_response(Seader* seader, uint8_t* r_apdu, size_t r_len);

0 commit comments

Comments
 (0)