diff --git a/src/swap_error_code_helpers.c b/src/swap_error_code_helpers.c new file mode 100644 index 000000000..93dac7c74 --- /dev/null +++ b/src/swap_error_code_helpers.c @@ -0,0 +1,49 @@ +#include "swap_error_code_helpers.h" +#include "swap_utils.h" + +__attribute__((noreturn)) void send_swap_error(uint16_t status_word, + uint8_t common_error_code, + uint8_t application_specific_error_code) { + send_swap_error_with_buffers(status_word, common_error_code, application_specific_error_code, NULL, 0); +} + +__attribute__((noreturn)) void send_swap_error_with_buffer(uint16_t status_word, + uint8_t common_error_code, + uint8_t application_specific_error_code, + const buffer_t buffer_data) { + send_swap_error_with_buffers(status_word, common_error_code, application_specific_error_code, &buffer_data, 1); +} + +__attribute__((noreturn)) void send_swap_error_with_buffers(uint16_t status_word, + uint8_t common_error_code, + uint8_t application_specific_error_code, + const buffer_t *buffer_data, + size_t count) { + // Force G_swap_response_ready to true + G_swap_response_ready = true; + + // Simply prepend a constructed buffer with the error code to the buffer list and use standard + // io function to send + uint8_t swap_error_code[2]; + swap_error_code[0] = common_error_code; + swap_error_code[1] = application_specific_error_code; + buffer_t response[1 + SWAP_ERROR_HELPER_MAX_BUFFER_COUNT] = {0}; + response[0].ptr = (uint8_t*) &swap_error_code; + response[0].size = sizeof(swap_error_code); + response[0].offset = 0; + + // Not really an error, let's just truncate without raising + if (count > SWAP_ERROR_HELPER_MAX_BUFFER_COUNT) { + PRINTF("send_swap_error_with_buffers truncated from %d to %d\n", count, SWAP_ERROR_HELPER_MAX_BUFFER_COUNT); + count = SWAP_ERROR_HELPER_MAX_BUFFER_COUNT; + } + for (uint8_t i = 0; i < count; i++) { + // We are only copying the buffer_t structure, not the content + memcpy(&(response[i + 1]), buffer_data, sizeof(buffer_t)); + } + + io_send_response_buffers((buffer_t *) &response, count + 1, status_word); + + // unreachable + os_sched_exit(0); +} diff --git a/src/swap_error_code_helpers.h b/src/swap_error_code_helpers.h new file mode 100644 index 000000000..1cf9d75e3 --- /dev/null +++ b/src/swap_error_code_helpers.h @@ -0,0 +1,89 @@ +#pragma once + +#include "io.h" +#include "buffer.h" + +/* +# Error response for applications started by Exchange in SWAP context + +## RAPDU status word + +Each application must define a unique status word for every Exchange-related error. + +## RAPDU data + +The first 2 bytes of the RAPDU data represent the error code. Format is 16 bits integer in big endian. + +The upper byte is common between all applications. It must be one of the following value: + +| Name | Value | Description | +| ----------------------------- | ------ | ------------------------------------ | +| ERROR_INTERNAL | 0x00 | Internal application error, forward to the firmware team for analysis. | +| ERROR_WRONG_AMOUNT | 0x01 | The amount does not match the one validated in Exchange. | +| ERROR_WRONG_DESTINATION | 0x02 | The destination address does not match the one validated in Exchange. | +| ERROR_WRONG_FEES | 0x03 | The fees are different from what was validated in Exchange. | +| ERROR_WRONG_METHOD | 0x04 | The method used is invalid in Exchange context. | +| ERROR_CROSSCHAIN_WRONG_MODE | 0x05 | The mode used for the cross-chain hash validation is not supported. | +| ERROR_CROSSCHAIN_WRONG_METHOD | 0x06 | The method used is invalid in cross-chain Exchange context. | +| ERROR_CROSSCHAIN_WRONG_HASH | 0x07 | The hash for the cross-chain transaction does not match the validated value. | +| ERROR_GENERIC | 0xFF | A generic or unspecified error not covered by the specific error codes above. Refer to the remaining bytes for further details on the error. | + +The lower byte can be set by the application to refine the error code returned + +So the error code for ERROR_WRONG_METHOD would be 0x04XX with XX being application specific (can be 00 if there is nothing to refine) + +The remaining bytes of the data are application-specific and can include, but are not limited to: +- Debugging information (e.g., error logs or internal state). +- Field values (e.g., expected vs actual amounts, destination, fees). +- More specific error codes tailored to the application's context. + */ + +typedef enum swap_error_common_code_e { + ERROR_INTERNAL = 0x00, + ERROR_WRONG_AMOUNT = 0x01, + ERROR_WRONG_DESTINATION = 0x02, + ERROR_WRONG_FEES = 0x03, + ERROR_WRONG_METHOD = 0x04, + ERROR_CROSSCHAIN_WRONG_MODE = 0x05, + ERROR_CROSSCHAIN_WRONG_METHOD = 0x06, + ERROR_CROSSCHAIN_WRONG_HASH = 0x07, + ERROR_GENERIC = 0xFF, +} swap_error_common_code_t; + +__attribute__((noreturn)) void send_swap_error(uint16_t status_word, + uint8_t common_error_code, + uint8_t application_specific_error_code); + +__attribute__((noreturn)) void send_swap_error_with_buffer(uint16_t status_word, + uint8_t common_error_code, + uint8_t application_specific_error_code, + const buffer_t buffer_data); + +#define SWAP_ERROR_HELPER_MAX_BUFFER_COUNT 8 +__attribute__((noreturn)) void send_swap_error_with_buffers(uint16_t status_word, + uint8_t common_error_code, + uint8_t application_specific_error_code, + const buffer_t *buffer_data, + size_t count); + +// Immediately call snprintf here (no function wrapping it cleanly in a .c file). +// This is because we don't have a vsnprintf implementation which would be needed if +// we were to pass the va_args to an intermediate function. +// See https://stackoverflow.com/a/150578 +#define send_swap_error_with_string(status_word, \ + common_error_code, \ + application_specific_error_code, \ + format, ...) \ + do { \ + /* Up to a full data apdu minus the status word and the swap error code */ \ + char format_buffer[sizeof(G_io_apdu_buffer) - sizeof(status_word) - 2] = {0}; \ + /* snprintf always returns 0 on our platform, don't check the return value */ \ + /* See https://github.com/LedgerHQ/ledger-secure-sdk/issues/236 */ \ + snprintf(format_buffer, sizeof(format_buffer), format, ##__VA_ARGS__); \ + PRINTF("send_swap_error_with_string %s\n", format_buffer); \ + buffer_t string_buffer; \ + string_buffer.ptr = (uint8_t*) &format_buffer; \ + string_buffer.size = strnlen(format_buffer, sizeof(format_buffer)); \ + string_buffer.offset = 0; \ + send_swap_error_with_buffers(status_word, common_error_code, application_specific_error_code, &string_buffer, 1);\ + } while (0)