Files
app-ethereum/src_features/signMessage/cmd_signMessage.c

291 lines
7.7 KiB
C
Raw Normal View History

2022-05-23 16:39:24 +02:00
#include <stdbool.h>
2022-09-08 10:20:44 +02:00
#include <ctype.h>
#include <string.h>
2020-06-27 13:24:04 +02:00
#include "apdu_constants.h"
2022-09-02 14:22:11 +02:00
#include "sign_message.h"
2022-08-24 09:25:01 +02:00
#include "common_ui.h"
2020-06-27 13:24:04 +02:00
2022-09-02 14:22:11 +02:00
static uint8_t processed_size;
static struct {
sign_message_state sign_state : 1;
bool ui_started : 1;
} states;
2020-06-27 13:24:04 +02:00
static const char SIGN_MAGIC[] =
2020-12-01 16:20:13 +01:00
"\x19"
"Ethereum Signed Message:\n";
2020-06-27 13:24:04 +02:00
2022-05-23 16:39:24 +02:00
/**
* Send a response APDU with the given Status Word
2022-05-23 16:39:24 +02:00
*
* @param[in] sw status word
2022-05-23 16:39:24 +02:00
*/
static void apdu_reply(uint16_t sw) {
if ((sw != APDU_RESPONSE_OK) && states.ui_started) {
ui_idle();
}
2022-09-16 14:43:51 +02:00
G_io_apdu_buffer[0] = (sw >> 8) & 0xff;
G_io_apdu_buffer[1] = sw & 0xff;
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2);
2022-05-23 16:39:24 +02:00
}
/**
2022-09-15 11:45:05 +02:00
* Get unprocessed data from last received APDU
2022-05-23 16:39:24 +02:00
*
2022-09-15 11:45:05 +02:00
* @return pointer to data in APDU buffer
2022-05-23 16:39:24 +02:00
*/
static const uint8_t *unprocessed_data(void) {
2022-09-02 14:22:11 +02:00
return &G_io_apdu_buffer[OFFSET_CDATA] + processed_size;
2022-05-23 16:39:24 +02:00
}
/**
2022-09-15 11:45:05 +02:00
* Get size of unprocessed data from last received APDU
2022-05-23 16:39:24 +02:00
*
2022-09-15 11:45:05 +02:00
* @return size of data in bytes
2022-05-23 16:39:24 +02:00
*/
static size_t unprocessed_length(void) {
2022-09-02 14:22:11 +02:00
return G_io_apdu_buffer[OFFSET_LC] - processed_size;
2022-05-23 16:39:24 +02:00
}
/**
2022-09-15 11:45:05 +02:00
* Get used space from UI buffer
*
* @return size in bytes
*/
static size_t ui_buffer_length(void) {
2022-09-08 10:20:44 +02:00
return strlen(UI_191_BUFFER);
}
2022-05-23 16:39:24 +02:00
/**
2022-09-15 11:45:05 +02:00
* Get remaining space from UI buffer
2022-05-23 16:39:24 +02:00
*
2022-09-15 11:45:05 +02:00
* @return size in bytes
2022-05-23 16:39:24 +02:00
*/
static size_t remaining_ui_buffer_length(void) {
2022-09-02 14:22:11 +02:00
// -1 for the ending NULL byte
2022-09-08 10:20:44 +02:00
return (sizeof(UI_191_BUFFER) - 1) - ui_buffer_length();
2022-05-23 16:39:24 +02:00
}
2022-09-15 11:45:05 +02:00
/**
* Get free space from UI buffer
*
* @return pointer to the free space
*/
static char *remaining_ui_buffer(void) {
2022-09-08 10:20:44 +02:00
return &UI_191_BUFFER[ui_buffer_length()];
}
2022-07-08 11:12:50 +02:00
2022-09-15 11:45:05 +02:00
/**
* Reset the UI buffer
*
* Simply sets its first byte to a NULL character
*/
static void reset_ui_buffer(void) {
2022-09-08 10:20:44 +02:00
UI_191_BUFFER[0] = '\0';
2022-05-23 16:39:24 +02:00
}
2022-07-08 11:12:50 +02:00
2022-09-15 11:45:05 +02:00
/**
* Handle the data specific to the first APDU of an EIP-191 signature
*
* @param[in] data the APDU payload
* @param[in] length the payload size
* @return pointer to the start of the start of the message; \ref NULL if it failed
*/
static const uint8_t *first_apdu_data(const uint8_t *data, uint8_t *length) {
2022-09-02 14:22:11 +02:00
if (appState != APP_STATE_IDLE) {
apdu_reply(APDU_RESPONSE_CONDITION_NOT_SATISFIED);
2022-09-02 14:22:11 +02:00
}
appState = APP_STATE_SIGNING_MESSAGE;
data = parseBip32(data, length, &tmpCtx.messageSigningContext.bip32);
if (data == NULL) {
apdu_reply(APDU_RESPONSE_INVALID_DATA);
2022-09-02 14:22:11 +02:00
return NULL;
}
2022-05-23 16:39:24 +02:00
2022-09-02 14:22:11 +02:00
if (*length < sizeof(uint32_t)) {
PRINTF("Invalid data\n");
apdu_reply(APDU_RESPONSE_INVALID_DATA);
2022-09-02 14:22:11 +02:00
return NULL;
}
2022-07-08 11:12:50 +02:00
2022-09-02 14:22:11 +02:00
tmpCtx.messageSigningContext.remainingLength = U4BE(data, 0);
data += sizeof(uint32_t);
*length -= sizeof(uint32_t);
2022-07-08 11:12:50 +02:00
2022-09-02 14:22:11 +02:00
// 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);
2022-09-02 14:22:11 +02:00
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);
2022-09-08 10:20:44 +02:00
reset_ui_buffer();
states.sign_state = STATE_191_HASH_DISPLAY;
states.ui_started = false;
2022-09-02 14:22:11 +02:00
return data;
}
2022-07-08 11:12:50 +02:00
2022-09-15 11:45:05 +02:00
/**
* Feed the progressive hash with new data
*
* @param[in] data the new data
* @param[in] length the data length
* @return whether it was successful or not
*/
2022-09-16 14:43:51 +02:00
static bool feed_hash(const uint8_t *const data, const uint8_t length) {
if (length > tmpCtx.messageSigningContext.remainingLength) {
2022-09-02 14:22:11 +02:00
PRINTF("Error: Length mismatch ! (%u > %u)!\n",
length,
tmpCtx.messageSigningContext.remainingLength);
apdu_reply(APDU_RESPONSE_INVALID_DATA);
2022-09-02 14:22:11 +02:00
return false;
}
cx_hash((cx_hash_t *) &global_sha3, 0, data, length, NULL, 0);
if ((tmpCtx.messageSigningContext.remainingLength -= length) == 0) {
2022-09-02 14:22:11 +02:00
// Finalize hash
2020-12-01 16:20:13 +01:00
cx_hash((cx_hash_t *) &global_sha3,
2022-09-02 14:22:11 +02:00
CX_LAST,
2020-12-01 16:20:13 +01:00
NULL,
2022-09-02 14:22:11 +02:00
0,
tmpCtx.messageSigningContext.hash,
32);
}
return true;
}
2022-05-23 16:39:24 +02:00
2022-09-15 11:45:05 +02:00
/**
* Feed the UI with new data
*/
static void feed_display(void) {
2022-09-08 10:20:44 +02:00
int c;
2022-05-23 16:39:24 +02:00
while ((unprocessed_length() > 0) && (remaining_ui_buffer_length() > 0)) {
c = *(char *) unprocessed_data();
if (isspace(c)) // to replace all white-space characters as spaces
2022-09-08 10:20:44 +02:00
{
c = ' ';
}
if (isprint(c)) {
sprintf(remaining_ui_buffer(), "%c", (char) c);
2022-09-08 10:20:44 +02:00
processed_size += 1;
} else {
if (remaining_ui_buffer_length() >= 4) // 4 being the fixed length of \x00
2022-09-08 10:20:44 +02:00
{
snprintf(remaining_ui_buffer(), remaining_ui_buffer_length(), "\\x%02x", c);
processed_size += 1;
} else {
2022-09-08 10:20:44 +02:00
// fill the rest of the UI buffer spaces, to consider the buffer full
2022-09-15 16:32:26 +02:00
memset(remaining_ui_buffer(), ' ', remaining_ui_buffer_length());
2022-09-08 10:20:44 +02:00
}
}
2020-10-07 16:56:40 +02:00
}
2022-05-23 16:39:24 +02:00
if ((remaining_ui_buffer_length() == 0) ||
(tmpCtx.messageSigningContext.remainingLength == 0)) {
if (!states.ui_started) {
ui_191_start();
states.ui_started = true;
} else {
ui_191_switch_to_message();
2022-09-02 14:22:11 +02:00
}
2020-06-27 13:24:04 +02:00
}
2022-09-02 14:22:11 +02:00
if ((unprocessed_length() == 0) && (tmpCtx.messageSigningContext.remainingLength > 0)) {
apdu_reply(APDU_RESPONSE_OK);
2020-12-01 16:20:13 +01:00
}
2022-09-02 14:22:11 +02:00
}
2022-05-23 16:39:24 +02:00
2022-09-15 11:45:05 +02:00
/**
* EIP-191 APDU handler
*
* @param[in] p1 instruction parameter 1
* @param[in] p2 instruction parameter 2
* @param[in] payload received data
* @param[in] length data length
* @return whether the handling of the APDU was successful or not
*/
2022-09-02 14:22:11 +02:00
bool handleSignPersonalMessage(uint8_t p1,
uint8_t p2,
const uint8_t *const payload,
uint8_t length) {
2022-09-02 14:22:11 +02:00
const uint8_t *data = payload;
2022-05-23 16:39:24 +02:00
(void) p2;
2022-09-02 14:22:11 +02:00
processed_size = 0;
if (p1 == P1_FIRST) {
if ((data = first_apdu_data(data, &length)) == NULL) {
2022-09-02 14:22:11 +02:00
return false;
}
processed_size = data - payload;
} else if (p1 != P1_MORE) {
2022-09-02 14:22:11 +02:00
PRINTF("Error: Unexpected P1 (%u)!\n", p1);
apdu_reply(APDU_RESPONSE_INVALID_P1_P2);
return false;
} else if (appState != APP_STATE_SIGNING_MESSAGE) {
PRINTF("Error: App not already in signing state!\n");
apdu_reply(APDU_RESPONSE_INVALID_DATA);
return false;
2020-12-01 16:20:13 +01:00
}
2022-05-23 16:39:24 +02:00
if (!feed_hash(data, length)) {
2022-09-02 14:22:11 +02:00
return false;
}
2020-06-27 13:24:04 +02:00
if (states.sign_state == STATE_191_HASH_DISPLAY) {
2022-09-02 14:22:11 +02:00
feed_display();
} else // hash only
2022-09-02 14:22:11 +02:00
{
if (tmpCtx.messageSigningContext.remainingLength == 0) {
2020-06-27 13:24:04 +02:00
#ifdef NO_CONSENT
io_seproxyhal_touch_signMessage_ok();
#else
ui_191_switch_to_sign();
#endif
} else {
apdu_reply(APDU_RESPONSE_OK);
2022-09-02 14:22:11 +02:00
}
}
return true;
}
2020-06-27 13:24:04 +02:00
/**
* Decide whether to show the question to show more of the message or not
*/
void question_switcher(void) {
if ((states.sign_state == STATE_191_HASH_DISPLAY) &&
((tmpCtx.messageSigningContext.remainingLength > 0) || (unprocessed_length() > 0))) {
ui_191_switch_to_question();
} else {
// Go to Sign / Cancel
ui_191_switch_to_sign();
2022-09-02 14:22:11 +02:00
}
}
2020-06-27 13:24:04 +02:00
/**
* The user has decided to skip the rest of the message
*/
void skip_rest_of_message(void) {
states.sign_state = STATE_191_HASH_ONLY;
if (tmpCtx.messageSigningContext.remainingLength > 0) {
apdu_reply(APDU_RESPONSE_OK);
2020-12-01 16:20:13 +01:00
} else {
ui_191_switch_to_sign();
2022-09-06 09:41:16 +02:00
}
2022-09-02 14:22:11 +02:00
}
2020-06-27 13:24:04 +02:00
/**
* The user has decided to see the next chunk of the message
*/
void continue_displaying_message(void) {
reset_ui_buffer();
if (unprocessed_length() > 0) {
feed_display();
2020-12-01 16:20:13 +01:00
}
2020-06-27 13:24:04 +02:00
}