diff --git a/src/apdu_constants.h b/src/apdu_constants.h index 1937075..ce1c96a 100644 --- a/src/apdu_constants.h +++ b/src/apdu_constants.h @@ -88,12 +88,10 @@ void handleGetAppConfiguration(uint8_t p1, uint16_t dataLength, unsigned int *flags, unsigned int *tx); -void handleSignPersonalMessage(uint8_t p1, +bool handleSignPersonalMessage(uint8_t p1, uint8_t p2, - const uint8_t *dataBuffer, - uint16_t dataLength, - unsigned int *flags, - unsigned int *tx); + const uint8_t *const payload, + uint8_t length); void handleSignEIP712Message(uint8_t p1, uint8_t p2, const uint8_t *dataBuffer, diff --git a/src/main.c b/src/main.c index cf99d6f..fa3db8e 100644 --- a/src/main.c +++ b/src/main.c @@ -664,12 +664,11 @@ void handleApdu(unsigned int *flags, unsigned int *tx) { case INS_SIGN_PERSONAL_MESSAGE: memset(tmpCtx.transactionContext.tokenSet, 0, MAX_ITEMS); + *flags |= IO_ASYNCH_REPLY; handleSignPersonalMessage(G_io_apdu_buffer[OFFSET_P1], G_io_apdu_buffer[OFFSET_P2], G_io_apdu_buffer + OFFSET_CDATA, - G_io_apdu_buffer[OFFSET_LC], - flags, - tx); + G_io_apdu_buffer[OFFSET_LC]); break; case INS_SIGN_EIP_712_MESSAGE: diff --git a/src_bagl/ui_flow_signMessage.c b/src_bagl/ui_flow_signMessage.c index 5aa9500..29b3934 100644 --- a/src_bagl/ui_flow_signMessage.c +++ b/src_bagl/ui_flow_signMessage.c @@ -1,9 +1,11 @@ #include "shared_context.h" #include "ui_callbacks.h" +#include "sign_message.h" + // clang-format off UX_STEP_NOCB( - ux_sign_flow_1_step, + ux_191_step_review, pnn, { &C_icon_certificate, @@ -11,14 +13,36 @@ UX_STEP_NOCB( "message", }); UX_STEP_NOCB( - ux_sign_flow_2_step, + ux_191_step_message, bnnn_paging, { .title = "Message", .text = strings.tmp.tmp, }); +UX_STEP_INIT( + ux_191_step_dummy_pre, + NULL, + NULL, + { + dummy_pre_cb(); + }); UX_STEP_CB( - ux_sign_flow_3_step, + ux_191_step_theres_more, + bn, + theres_more_click_cb(), + { + "There's more!", + "Click to skip" + }); +UX_STEP_INIT( + ux_191_step_dummy_post, + NULL, + NULL, + { + dummy_post_cb(); + }); +UX_STEP_CB( + ux_191_step_sign, pbb, io_seproxyhal_touch_signMessage_ok(), { @@ -27,7 +51,7 @@ UX_STEP_CB( "message", }); UX_STEP_CB( - ux_sign_flow_4_step, + ux_191_step_cancel, pbb, io_seproxyhal_touch_signMessage_cancel(), { @@ -38,7 +62,32 @@ UX_STEP_CB( // clang-format on UX_FLOW(ux_sign_flow, - &ux_sign_flow_1_step, - &ux_sign_flow_2_step, - &ux_sign_flow_3_step, - &ux_sign_flow_4_step); + &ux_191_step_review, + &ux_191_step_message, + &ux_191_step_dummy_pre, + &ux_191_step_theres_more, + &ux_191_step_dummy_post, + &ux_191_step_sign, + &ux_191_step_cancel); + + +void ui_191_switch_to_message(void) +{ + ux_flow_init(0, ux_sign_flow, &ux_191_step_message); +} + +void ui_191_switch_to_message_end(void) +{ + ux_flow_init(0, ux_sign_flow, &ux_191_step_dummy_pre); + // with pos != REVIEW, automatically goes back to previous step +} + +void ui_191_switch_to_sign(void) +{ + ux_flow_init(0, ux_sign_flow, &ux_191_step_sign); +} + +void ui_191_switch_to_question(void) +{ + ux_flow_init(0, ux_sign_flow, &ux_191_step_theres_more); +} diff --git a/src_bagl/ui_flow_signMessage.h b/src_bagl/ui_flow_signMessage.h new file mode 100644 index 0000000..dd5e342 --- /dev/null +++ b/src_bagl/ui_flow_signMessage.h @@ -0,0 +1,9 @@ +#ifndef UI_FLOW_SIGNMESSAGE_H_ +#define UI_FLOW_SIGNMESSAGE_H_ + +void ui_191_switch_to_message(void); +void ui_191_switch_to_message_end(void); +void ui_191_switch_to_sign(void); +void ui_191_switch_to_question(void); + +#endif // UI_FLOW_SIGNMESSAGE_H_ diff --git a/src_features/signMessage/cmd_signMessage.c b/src_features/signMessage/cmd_signMessage.c index e9d47e0..d3d9786 100644 --- a/src_features/signMessage/cmd_signMessage.c +++ b/src_features/signMessage/cmd_signMessage.c @@ -3,197 +3,251 @@ #include "apdu_constants.h" #include "utils.h" #include "common_ui.h" +#include "sign_message.h" +#include "ui_flow_signMessage.h" + +static uint8_t state; +static bool ui_started; +static uint8_t processed_size; +static uint8_t ui_position; static const char SIGN_MAGIC[] = "\x19" "Ethereum Signed Message:\n"; -/** - * Check if a given character is a "special" displayable ASCII character - * - * @param[in] c character we're checking - * @return wether the character is special or not - */ -static inline bool is_char_special(char c) { - return ((c >= '\b') && (c <= '\r')); + +const uint8_t *unprocessed_data(void) +{ + return &G_io_apdu_buffer[OFFSET_CDATA] + processed_size; } -/** - * Check if a given data is made of ASCII characters - * - * @param[in] data the input data - * @param[in] the length of the input data - * @return wether the data is fully ASCII or not - */ -static bool is_data_ascii(const uint8_t *const data, size_t length) { - for (uint8_t idx = 0; idx < length; ++idx) { - if (!is_char_special(data[idx]) && ((data[idx] < 0x20) || (data[idx] > 0x7e))) { +uint8_t unprocessed_length(void) +{ + return G_io_apdu_buffer[OFFSET_LC] - processed_size; +} + +uint8_t remaining_ui_length(void) +{ + // -1 for the ending NULL byte + return (sizeof(strings.tmp.tmp) - 1) - strlen(strings.tmp.tmp); +} + +static void switch_to_message(void) +{ + ui_191_switch_to_message(); + ui_position = UI_191_REVIEW; +} + +static void switch_to_message_end(void) +{ + ui_191_switch_to_message_end(); + ui_position = UI_191_REVIEW; +} + +static void switch_to_sign(void) +{ + ui_191_switch_to_sign(); + ui_position = UI_191_END; +} + +static void switch_to_question(void) +{ + ui_191_switch_to_question(); + ui_position = UI_191_QUESTION; +} + +const uint8_t *first_apdu_data(const uint8_t *data, uint16_t *length) +{ + if (appState != APP_STATE_IDLE) { + reset_app_context(); + } + appState = APP_STATE_SIGNING_MESSAGE; + data = parseBip32(data, length, &tmpCtx.messageSigningContext.bip32); + if (data == NULL) { + return NULL; + } + + if (*length < sizeof(uint32_t)) { + PRINTF("Invalid data\n"); + return NULL; + } + + tmpCtx.messageSigningContext.remainingLength = U4BE(data, 0); + data += sizeof(uint32_t); + *length -= sizeof(uint32_t); + + // Initialize message header + length + cx_keccak_init(&global_sha3, 256); + cx_hash((cx_hash_t *) &global_sha3, + 0, + (uint8_t *) SIGN_MAGIC, + sizeof(SIGN_MAGIC) - 1, + NULL, + 0); + snprintf(strings.tmp.tmp2, + sizeof(strings.tmp.tmp2), + "%u", + tmpCtx.messageSigningContext.remainingLength); + cx_hash((cx_hash_t *) &global_sha3, + 0, + (uint8_t *) strings.tmp.tmp2, + strlen(strings.tmp.tmp2), + NULL, + 0); + strings.tmp.tmp[0] = '\0'; + state = STATE_191_HASH_DISPLAY; + ui_started = false; + ui_position = UI_191_REVIEW; + return data; +} + +bool feed_hash(const uint8_t *const data, uint8_t length) +{ + if (length > tmpCtx.messageSigningContext.remainingLength) + { + PRINTF("Error: Length mismatch ! (%u > %u)!\n", + length, + tmpCtx.messageSigningContext.remainingLength); + return false; + } + cx_hash((cx_hash_t *) &global_sha3, 0, data, length, NULL, 0); + if ((tmpCtx.messageSigningContext.remainingLength -= length) == 0) + { + // Finalize hash + cx_hash((cx_hash_t *) &global_sha3, + CX_LAST, + NULL, + 0, + tmpCtx.messageSigningContext.hash, + 32); + } + return true; +} + +bool feed_display(void) +{ + uint8_t ui_length; + + while ((unprocessed_length() > 0) && ((ui_length = remaining_ui_length()) > 0)) + { + sprintf(&strings.tmp.tmp[sizeof(strings.tmp.tmp) - 1 - ui_length], "%c", *(char*)unprocessed_data()); + processed_size += 1; + } + + if ((remaining_ui_length() == 0) || (tmpCtx.messageSigningContext.remainingLength == 0)) + { + if (!ui_started) + { + ui_display_sign(); + ui_started = true; + } + else + { + switch_to_message(); + } + } + + if ((unprocessed_length() == 0) && (tmpCtx.messageSigningContext.remainingLength > 0)) + { + *(uint16_t *) G_io_apdu_buffer = __builtin_bswap16(0x9000); + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); + } + + return true; +} + +bool handleSignPersonalMessage(uint8_t p1, + uint8_t p2, + const uint8_t *const payload, + uint8_t length) +{ + const uint8_t *data = payload; + + (void)p2; + processed_size = 0; + if (p1 == P1_FIRST) + { + if ((data = first_apdu_data(data, (uint16_t*)&length)) == NULL) + { return false; } + processed_size = data - payload; + } + else if (p1 != P1_MORE) + { + PRINTF("Error: Unexpected P1 (%u)!\n", p1); + } + + if (!feed_hash(data, length)) + { + return false; + } + + if (state == STATE_191_HASH_DISPLAY) + { + feed_display(); + } + else // hash only + { + if (tmpCtx.messageSigningContext.remainingLength == 0) + { + switch_to_sign(); + } + else + { + *(uint16_t *) G_io_apdu_buffer = __builtin_bswap16(0x9000); + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); + } } return true; } -/** - * Initialize value string that will be displayed in the UX STEP - * - * @param[in] if the value is ASCII - */ -static void init_value_str(bool is_ascii) { - if (is_ascii) { - strings.tmp.tmp[0] = '\0'; // init string as empty - } else { - strcpy(strings.tmp.tmp, "0x"); // will display the hex bytes instead +void dummy_pre_cb(void) +{ + if (ui_position == UI_191_REVIEW) + { + if ((state == STATE_191_HASH_DISPLAY) && ((tmpCtx.messageSigningContext.remainingLength > 0) || (unprocessed_length() > 0))) + { + switch_to_question(); + } + else + { + // Go to Sign / Cancel + switch_to_sign(); + } + } + else + { + ux_flow_prev(); + ui_position = UI_191_REVIEW; } } -/** - * @return Whether the currently stored data is initialized as ASCII or not - */ -static bool is_value_str_ascii() { - return (memcmp(strings.tmp.tmp, "0x", 2) != 0); -} +void theres_more_click_cb(void) +{ + state = STATE_191_HASH_ONLY; -/** - * Update the global UI string variable by formatting & appending the new data to it - * - * @param[in] data the input data - * @param[in] length the data length - * @param[in] is_ascii wether the data is ASCII or not - */ -static void feed_value_str(const uint8_t *const data, size_t length, bool is_ascii) { - uint16_t value_strlen = strlen(strings.tmp.tmp); - - if ((value_strlen + 1) < sizeof(strings.tmp.tmp)) { - if (is_ascii) { - uint8_t src_idx = 0; - uint16_t dst_idx = value_strlen; - bool prev_is_special = false; - - while ((src_idx < length) && (dst_idx < sizeof(strings.tmp.tmp))) { - if (prev_is_special) { - if (!is_char_special(data[src_idx])) { - prev_is_special = false; - } - } else { - if (is_char_special(data[src_idx])) { - prev_is_special = true; - strings.tmp.tmp[dst_idx] = ' '; - dst_idx += 1; - } - } - if (!is_char_special(data[src_idx])) { - strings.tmp.tmp[dst_idx] = data[src_idx]; - dst_idx += 1; - } - src_idx += 1; - } - - if (dst_idx < sizeof(strings.tmp.tmp)) { - strings.tmp.tmp[dst_idx] = '\0'; - } else { - const char marker[] = "..."; - - memcpy(strings.tmp.tmp + sizeof(strings.tmp.tmp) - sizeof(marker), - marker, - sizeof(marker)); - } - } else { - // truncate to strings.tmp.tmp 's size - length = MIN(length, (sizeof(strings.tmp.tmp) - value_strlen) / 2); - for (size_t i = 0; i < length; i++) { - snprintf(strings.tmp.tmp + value_strlen + 2 * i, - sizeof(strings.tmp.tmp) - value_strlen - 2 * i, - "%02X", - data[i]); - } - } + if (tmpCtx.messageSigningContext.remainingLength > 0) + { + *(uint16_t *) G_io_apdu_buffer = __builtin_bswap16(0x9000); + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); } } -void handleSignPersonalMessage(uint8_t p1, - uint8_t p2, - const uint8_t *workBuffer, - uint16_t dataLength, - unsigned int *flags, - unsigned int *tx) { - UNUSED(tx); - uint8_t hashMessage[INT256_LENGTH]; - - if (p1 == P1_FIRST) { - char tmp[11] = {0}; - - if (appState != APP_STATE_IDLE) { - reset_app_context(); +void dummy_post_cb(void) +{ + if (ui_position == UI_191_QUESTION) + { + strings.tmp.tmp[0] = '\0'; // empty display string + processed_size = 0; + if (unprocessed_length() > 0) + { + feed_display(); } - appState = APP_STATE_SIGNING_MESSAGE; - - workBuffer = parseBip32(workBuffer, &dataLength, &tmpCtx.messageSigningContext.bip32); - - if (workBuffer == NULL) { - THROW(0x6a80); - } - - if (dataLength < sizeof(uint32_t)) { - PRINTF("Invalid data\n"); - THROW(0x6a80); - } - - tmpCtx.messageSigningContext.remainingLength = U4BE(workBuffer, 0); - workBuffer += sizeof(uint32_t); - dataLength -= sizeof(uint32_t); - // Initialize message header + length - cx_keccak_init(&global_sha3, 256); - cx_hash((cx_hash_t *) &global_sha3, - 0, - (uint8_t *) SIGN_MAGIC, - sizeof(SIGN_MAGIC) - 1, - NULL, - 0); - snprintf(tmp, sizeof(tmp), "%u", tmpCtx.messageSigningContext.remainingLength); - cx_hash((cx_hash_t *) &global_sha3, 0, (uint8_t *) tmp, strlen(tmp), NULL, 0); - cx_sha256_init(&tmpContent.sha2); - - init_value_str(is_data_ascii(workBuffer, dataLength)); - - } else if (p1 != P1_MORE) { - THROW(0x6B00); + // TODO: respond to apdu ? } - if (p2 != 0) { - THROW(0x6B00); - } - if ((p1 == P1_MORE) && (appState != APP_STATE_SIGNING_MESSAGE)) { - PRINTF("Signature not initialized\n"); - THROW(0x6985); - } - if (dataLength > tmpCtx.messageSigningContext.remainingLength) { - THROW(0x6A80); - } - - cx_hash((cx_hash_t *) &global_sha3, 0, workBuffer, dataLength, NULL, 0); - cx_hash((cx_hash_t *) &tmpContent.sha2, 0, workBuffer, dataLength, NULL, 0); - tmpCtx.messageSigningContext.remainingLength -= dataLength; - - feed_value_str(workBuffer, dataLength, is_value_str_ascii()); - - if (tmpCtx.messageSigningContext.remainingLength == 0) { - cx_hash((cx_hash_t *) &global_sha3, - CX_LAST, - workBuffer, - 0, - tmpCtx.messageSigningContext.hash, - 32); - cx_hash((cx_hash_t *) &tmpContent.sha2, CX_LAST, workBuffer, 0, hashMessage, 32); - -#ifdef NO_CONSENT - io_seproxyhal_touch_signMessage_ok(); -#else // NO_CONSENT - ui_display_sign(); -#endif // NO_CONSENT - - *flags |= IO_ASYNCH_REPLY; - - } else { - THROW(0x9000); + else // UI_191_END + { + switch_to_message_end(); } } diff --git a/src_features/signMessage/sign_message.h b/src_features/signMessage/sign_message.h new file mode 100644 index 0000000..28348cd --- /dev/null +++ b/src_features/signMessage/sign_message.h @@ -0,0 +1,17 @@ +#ifndef SIGN_MESSAGE_H_ +#define SIGN_MESSAGE_H_ + +typedef enum { STATE_191_HASH_DISPLAY = 0, STATE_191_HASH_ONLY } sign_message_state; +typedef enum { UI_191_REVIEW = 0, UI_191_QUESTION, UI_191_END } ui_191_position; + + +#define UI_191_BUFFER strings.tmp.tmp + +void question_switcher(void); +void skip_rest_of_message(void); +void continue_displaying_message(void); +void dummy_pre_cb(void); +void theres_more_click_cb(void); +void dummy_post_cb(void); + +#endif // SIGN_MESSAGE_H_