|
| 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 | +} |
0 commit comments