Files
app-ethereum/src_features/signMessage/cmd_signMessage.c
Alexandre Paillier 5dc1a0cfcd EIP-191 clear signing
2022-06-08 11:45:20 +02:00

217 lines
6.9 KiB
C

#include <stdbool.h>
#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, uint8_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
}
}
/**
* 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, uint8_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 {
snprintf(strings.tmp.tmp + value_strlen,
sizeof(strings.tmp.tmp) - value_strlen,
"%.*H",
length,
data);
}
}
}
void handleSignPersonalMessage(uint8_t p1,
uint8_t p2,
uint8_t *workBuffer,
uint16_t dataLength,
unsigned int *flags,
unsigned int *tx) {
// Point to this unused global variable for persistency
bool *is_ascii = (bool *) &strings.tmp.tmp2[0];
UNUSED(tx);
uint8_t hashMessage[INT256_LENGTH];
if (p1 == P1_FIRST) {
char tmp[11];
uint32_t index;
uint32_t base = 10;
uint8_t pos = 0;
uint32_t i;
if (dataLength < 1) {
PRINTF("Invalid data\n");
THROW(0x6a80);
}
if (appState != APP_STATE_IDLE) {
reset_app_context();
}
appState = APP_STATE_SIGNING_MESSAGE;
tmpCtx.messageSigningContext.pathLength = workBuffer[0];
if ((tmpCtx.messageSigningContext.pathLength < 0x01) ||
(tmpCtx.messageSigningContext.pathLength > MAX_BIP32_PATH)) {
PRINTF("Invalid path\n");
THROW(0x6a80);
}
workBuffer++;
dataLength--;
for (i = 0; i < tmpCtx.messageSigningContext.pathLength; i++) {
if (dataLength < sizeof(uint32_t)) {
PRINTF("Invalid data\n");
THROW(0x6a80);
}
tmpCtx.messageSigningContext.bip32Path[i] = U4BE(workBuffer, 0);
workBuffer += sizeof(uint32_t);
dataLength -= sizeof(uint32_t);
}
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);
for (index = 1; (((index * base) <= tmpCtx.messageSigningContext.remainingLength) &&
(((index * base) / base) == index));
index *= base)
;
for (; index; index /= base) {
tmp[pos++] = '0' + ((tmpCtx.messageSigningContext.remainingLength / index) % base);
}
tmp[pos] = '\0';
cx_hash((cx_hash_t *) &global_sha3, 0, (uint8_t *) tmp, pos, NULL, 0);
cx_sha256_init(&tmpContent.sha2);
*is_ascii = is_data_ascii(workBuffer, dataLength);
init_value_str(*is_ascii);
} 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_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);
}
}