#include #include "shared_context.h" #include "apdu_constants.h" #include "utils.h" #include "ui_flow.h" 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')); } /** * 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))) { return false; } } 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 } } /** * @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); } /** * 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]); } } } } void handleSignPersonalMessage(uint8_t p1, uint8_t p2, const uint8_t *workBuffer, uint8_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(); } 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); } 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(NULL); #else // NO_CONSENT ux_flow_init(0, ux_sign_flow, NULL); #endif // NO_CONSENT *flags |= IO_ASYNCH_REPLY; } else { THROW(0x9000); } }