From bf465357ee682ba307158e6d671f50948c47c4be Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 23 May 2016 21:27:18 +0100 Subject: [PATCH 01/28] Include placeholder handlers for the Ethereum protocol --- firmware/Makefile | 1 + firmware/fsm.c | 19 +++++++++++++++++++ firmware/fsm.h | 3 +++ firmware/messages.c | 5 +++++ 4 files changed, 28 insertions(+) diff --git a/firmware/Makefile b/firmware/Makefile index 36fc371..6d134cd 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -59,3 +59,4 @@ CFLAGS += -DDEBUG_LOG=0 CFLAGS += -DSCM_REVISION='"$(shell git rev-parse HEAD | sed 's:\(..\):\\x\1:g')"' CFLAGS += -DED25519_CUSTOMRANDOM=1 CFLAGS += -DED25519_CUSTOMHASH=1 +CFLAGS += -DUSE_ETHEREUM=1 diff --git a/firmware/fsm.c b/firmware/fsm.c index ea7b1c3..f5ee14a 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -419,6 +419,19 @@ void fsm_msgSignTx(SignTx *msg) signing_init(msg->inputs_count, msg->outputs_count, coin, node, msg->version, msg->lock_time); } + +void fsm_msgEthereumSignTx(EthereumSignTx *msg) +{ + (void)msg; + fsm_sendFailure(FailureType_Failure_Other, "Unsupported feature"); +} + +void fsm_msgEthereumTxAck(EthereumTxAck *msg) +{ + (void)msg; + fsm_sendFailure(FailureType_Failure_Other, "Unsupported feature"); +} + void fsm_msgCancel(Cancel *msg) { (void)msg; @@ -628,6 +641,12 @@ void fsm_msgGetAddress(GetAddress *msg) layoutHome(); } +void fsm_msgEthereumGetAddress(EthereumGetAddress *msg) +{ + (void)msg; + fsm_sendFailure(FailureType_Failure_Other, "Unsupported feature"); +} + void fsm_msgEntropyAck(EntropyAck *msg) { if (msg->has_entropy) { diff --git a/firmware/fsm.h b/firmware/fsm.h index b361ad7..085c236 100644 --- a/firmware/fsm.h +++ b/firmware/fsm.h @@ -59,6 +59,9 @@ void fsm_msgEstimateTxSize(EstimateTxSize *msg); void fsm_msgRecoveryDevice(RecoveryDevice *msg); void fsm_msgWordAck(WordAck *msg); void fsm_msgSetU2FCounter(SetU2FCounter *msg); +void fsm_msgEthereumGetAddress(EthereumGetAddress *msg); +void fsm_msgEthereumSignTx(EthereumSignTx *msg); +void fsm_msgEthereumTxAck(EthereumTxAck *msg); // debug message functions #if DEBUG_LINK diff --git a/firmware/messages.c b/firmware/messages.c index ee323b8..9981bc2 100644 --- a/firmware/messages.c +++ b/firmware/messages.c @@ -73,6 +73,9 @@ static const struct MessagesMap_t MessagesMap[] = { {'n', 'i', MessageType_MessageType_RecoveryDevice, RecoveryDevice_fields, (void (*)(void *))fsm_msgRecoveryDevice}, {'n', 'i', MessageType_MessageType_WordAck, WordAck_fields, (void (*)(void *))fsm_msgWordAck}, {'n', 'i', MessageType_MessageType_SetU2FCounter, SetU2FCounter_fields, (void (*)(void *))fsm_msgSetU2FCounter}, + {'n', 'i', MessageType_MessageType_EthereumGetAddress, EthereumGetAddress_fields, (void (*)(void *))fsm_msgEthereumGetAddress}, + {'n', 'i', MessageType_MessageType_EthereumSignTx, EthereumSignTx_fields, (void (*)(void *))fsm_msgEthereumSignTx}, + {'n', 'i', MessageType_MessageType_EthereumTxAck, EthereumTxAck_fields, (void (*)(void *))fsm_msgEthereumTxAck}, // out messages {'n', 'o', MessageType_MessageType_Success, Success_fields, 0}, {'n', 'o', MessageType_MessageType_Failure, Failure_fields, 0}, @@ -95,6 +98,8 @@ static const struct MessagesMap_t MessagesMap[] = { {'n', 'o', MessageType_MessageType_PassphraseRequest, PassphraseRequest_fields, 0}, {'n', 'o', MessageType_MessageType_TxSize, TxSize_fields, 0}, {'n', 'o', MessageType_MessageType_WordRequest, WordRequest_fields, 0}, + {'n', 'o', MessageType_MessageType_EthereumAddress, EthereumAddress_fields, 0}, + {'n', 'o', MessageType_MessageType_EthereumTxRequest, EthereumTxRequest_fields, 0}, #if DEBUG_LINK // debug in messages // {'d', 'i', MessageType_MessageType_DebugLinkDecision, DebugLinkDecision_fields, (void (*)(void *))fsm_msgDebugLinkDecision}, From 3c2d9111e21d55d8d5d73fe9b0ac3da47e4bb773 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 23 May 2016 21:28:33 +0100 Subject: [PATCH 02/28] Implement EthereumGetAddress --- firmware/fsm.c | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/firmware/fsm.c b/firmware/fsm.c index f5ee14a..9bbfa0c 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -643,8 +643,44 @@ void fsm_msgGetAddress(GetAddress *msg) void fsm_msgEthereumGetAddress(EthereumGetAddress *msg) { - (void)msg; - fsm_sendFailure(FailureType_Failure_Other, "Unsupported feature"); + RESP_INIT(EthereumAddress); + + if (!storage_isInitialized()) { + fsm_sendFailure(FailureType_Failure_NotInitialized, "Device not initialized"); + return; + } + + if (!protectPin(true)) { + layoutHome(); + return; + } + + const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count); + if (!node) return; + + resp->address.size = 20; + + if (!ecdsa_get_ethereum_pubkeyhash(node->public_key, resp->address.bytes)) return; + + if (msg->has_show_display && msg->show_display) { + char desc[16]; + strlcpy(desc, "Address:", sizeof(desc)); + + char address[43]; + address[0] = '0'; + address[1] = 'x'; + data2hex(resp->address.bytes, 20, address + 2); + + layoutAddress(address, desc); + if (!protectButton(ButtonRequestType_ButtonRequest_Address, true)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, "Show address cancelled"); + layoutHome(); + return; + } + } + + msg_write(MessageType_MessageType_Address, resp); + layoutHome(); } void fsm_msgEntropyAck(EntropyAck *msg) From 352d296f773db6b58bd186d1c082f61788240868 Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Tue, 24 May 2016 20:31:25 +0100 Subject: [PATCH 03/28] Return the correct MessageType from EthereumGetAddress --- firmware/fsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/fsm.c b/firmware/fsm.c index 9bbfa0c..3640fbf 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -679,7 +679,7 @@ void fsm_msgEthereumGetAddress(EthereumGetAddress *msg) } } - msg_write(MessageType_MessageType_Address, resp); + msg_write(MessageType_MessageType_EthereumAddress, resp); layoutHome(); } From a031b79e2485460bbff4b5714407abd2743ecd3f Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Tue, 24 May 2016 20:31:34 +0100 Subject: [PATCH 04/28] Add sha3.o to OBJS --- firmware/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/firmware/Makefile b/firmware/Makefile index 6d134cd..dbb7a46 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -34,6 +34,7 @@ OBJS += ../vendor/trezor-crypto/base58.o OBJS += ../vendor/trezor-crypto/ripemd160.o OBJS += ../vendor/trezor-crypto/sha2.o +OBJS += ../vendor/trezor-crypto/sha3.o OBJS += ../vendor/trezor-crypto/aescrypt.o OBJS += ../vendor/trezor-crypto/aeskey.o From 9c7e41f15bbce502d2596b351c66d9b8fae2d9e5 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 24 May 2016 22:14:10 +0100 Subject: [PATCH 05/28] Reorder fsm.c for logical grouping of signing methods --- firmware/fsm.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/firmware/fsm.c b/firmware/fsm.c index 3640fbf..3ab2304 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -419,6 +419,22 @@ void fsm_msgSignTx(SignTx *msg) signing_init(msg->inputs_count, msg->outputs_count, coin, node, msg->version, msg->lock_time); } +void fsm_msgTxAck(TxAck *msg) +{ + if (msg->has_tx) { + signing_txack(&(msg->tx)); + } else { + fsm_sendFailure(FailureType_Failure_SyntaxError, "No transaction provided"); + } +} + +void fsm_msgCancel(Cancel *msg) +{ + (void)msg; + recovery_abort(); + signing_abort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, "Aborted"); +} void fsm_msgEthereumSignTx(EthereumSignTx *msg) { @@ -432,23 +448,6 @@ void fsm_msgEthereumTxAck(EthereumTxAck *msg) fsm_sendFailure(FailureType_Failure_Other, "Unsupported feature"); } -void fsm_msgCancel(Cancel *msg) -{ - (void)msg; - recovery_abort(); - signing_abort(); - fsm_sendFailure(FailureType_Failure_ActionCancelled, "Aborted"); -} - -void fsm_msgTxAck(TxAck *msg) -{ - if (msg->has_tx) { - signing_txack(&(msg->tx)); - } else { - fsm_sendFailure(FailureType_Failure_SyntaxError, "No transaction provided"); - } -} - void fsm_msgCipherKeyValue(CipherKeyValue *msg) { if (!storage_isInitialized()) { From a9449520b8f6c0dc17157f71580f92f8b9f8a5ab Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 24 May 2016 22:22:30 +0100 Subject: [PATCH 06/28] Ethereum signing skeleton --- firmware/Makefile | 1 + firmware/ethereum.c | 61 +++++++++++++++++++++++++++++++++++++++++++++ firmware/ethereum.h | 32 ++++++++++++++++++++++++ firmware/fsm.c | 21 +++++++++++++--- 4 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 firmware/ethereum.c create mode 100644 firmware/ethereum.h diff --git a/firmware/Makefile b/firmware/Makefile index dbb7a46..845d3c0 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -17,6 +17,7 @@ OBJS += recovery.o OBJS += reset.o OBJS += signing.o OBJS += crypto.o +OBJS += ethereum.o OBJS += debug.o diff --git a/firmware/ethereum.c b/firmware/ethereum.c new file mode 100644 index 0000000..f9653bb --- /dev/null +++ b/firmware/ethereum.c @@ -0,0 +1,61 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2016 Alex Beregszaszi + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#include "ethereum.h" +#include "fsm.h" +#include "layout2.h" +#include "messages.h" +#include "transaction.h" +#include "ecdsa.h" +#include "protect.h" +#include "crypto.h" +#include "secp256k1.h" +#include "sha3.h" + +static bool signing = false; + +void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) +{ + (void)node; + + signing = true; + + fsm_sendFailure(FailureType_Failure_Other, "Unsupported feature"); +} + +void ethereum_signing_txack(EthereumTxAck *tx) +{ + (void)tx; + + if (!signing) { + fsm_sendFailure(FailureType_Failure_UnexpectedMessage, "Not in Signing mode"); + layoutHome(); + return; + } + + fsm_sendFailure(FailureType_Failure_Other, "Unsupported feature"); +} + +void ethereum_signing_abort(void) +{ + if (signing) { + layoutHome(); + signing = false; + } +} diff --git a/firmware/ethereum.h b/firmware/ethereum.h new file mode 100644 index 0000000..8c55d2d --- /dev/null +++ b/firmware/ethereum.h @@ -0,0 +1,32 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2016 Alex Beregszaszi + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#ifndef __ETHEREUM_H__ +#define __ETHEREUM_H__ + +#include +#include +#include "bip32.h" +#include "messages.pb.h" + +void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node); +void ethereum_signing_abort(void); +void ethereum_signing_txack(EthereumTxAck *msg); + +#endif diff --git a/firmware/fsm.c b/firmware/fsm.c index 3ab2304..b2e3ef7 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -47,6 +47,7 @@ #include "curves.h" #include "secp256k1.h" #include +#include "ethereum.h" // message methods @@ -433,19 +434,31 @@ void fsm_msgCancel(Cancel *msg) (void)msg; recovery_abort(); signing_abort(); + ethereum_signing_abort(); fsm_sendFailure(FailureType_Failure_ActionCancelled, "Aborted"); } void fsm_msgEthereumSignTx(EthereumSignTx *msg) { - (void)msg; - fsm_sendFailure(FailureType_Failure_Other, "Unsupported feature"); + if (!storage_isInitialized()) { + fsm_sendFailure(FailureType_Failure_NotInitialized, "Device not initialized"); + return; + } + + if (!protectPin(true)) { + layoutHome(); + return; + } + + const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count); + if (!node) return; + + ethereum_signing_init(msg, node); } void fsm_msgEthereumTxAck(EthereumTxAck *msg) { - (void)msg; - fsm_sendFailure(FailureType_Failure_Other, "Unsupported feature"); + ethereum_signing_txack(msg); } void fsm_msgCipherKeyValue(CipherKeyValue *msg) From 1d2f9b6ecd9c9782a5fcdab87d21bb24f9f5a402 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 25 May 2016 00:38:32 +0100 Subject: [PATCH 07/28] Initial signing implementation for Ethereum --- firmware/ethereum.c | 217 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 211 insertions(+), 6 deletions(-) diff --git a/firmware/ethereum.c b/firmware/ethereum.c index f9653bb..24c42a7 100644 --- a/firmware/ethereum.c +++ b/firmware/ethereum.c @@ -29,32 +29,237 @@ #include "sha3.h" static bool signing = false; +static size_t data_left; +static EthereumTxRequest resp; +static uint8_t hash[32], sig[64], privkey[32]; +// FIXME: this is currently 400 bytes. Could be probably improved. +struct SHA3_CTX keccak_ctx; + +/* + * Encode length according to RLP. + * FIXME: improve + */ +static int rlp_encode_length(uint8_t *buf, int length, uint8_t firstbyte, bool list) +{ + if (!list && (length == 1 && firstbyte <= 0x7f)) { + buf[0] = firstbyte; + return 1; + } else if (length <= 55) { + buf[0] = (list ? 0xc0 : 0x80) + length; + return 1; + } else if (length <= 0xff) { + buf[0] = (list ? 0xf7 : 0xb7) + 1; + buf[1] = length; + return 2; + } else if (length <= 0xffff) { + buf[0] = (list ? 0xf7 : 0xb7) + 2; + buf[1] = length >> 8; + buf[2] = length & 0xff; + return 3; + } else { + buf[0] = (list ? 0xf7 : 0xb7) + 3; + buf[1] = length >> 16; + buf[2] = length >> 8; + buf[3] = length & 0xff; + return 4; + } +} + +/* + * Calculate the number of bytes needed for an RLP length header. + * NOTE: supports up to 16MB of data (how unlikely...) + * FIXME: improve + */ +static int rlp_calculate_length(int length, uint8_t firstbyte) { + if (length == 1 && firstbyte <= 0x7f) { + return 1; + } else if (length <= 55) { + return 2; + } else if (length <= 0xff) { + return 2; + } else if (length <= 0xffff) { + return 3; + } else + return 4; +} + +static inline void hash_data(const uint8_t *buf, size_t size) +{ + sha3_Update(&keccak_ctx, buf, size); +} + +/* + * Push an RLP encoded length to the hash buffer. + */ +static void hash_rlp_length(int length, uint8_t firstbyte) +{ + uint8_t buf[4]; + size_t size = rlp_encode_length(buf, length, firstbyte, false); + hash_data(buf, size); +} + +/* + * Push an RLP encoded list length to the hash buffer. + */ +static void hash_rlp_list_length(int length) +{ + uint8_t buf[4]; + size_t size = rlp_encode_length(buf, length, 0, true); + hash_data(buf, size); +} + +/* + * Push an RLP encoded length field and data to the hash buffer. + */ +static void hash_rlp_field(const uint8_t *buf, size_t size) +{ + hash_rlp_length(size, buf[0]); + /* FIXME: this special case should be handled more nicely */ + if (!(size == 1 && buf[0] <= 0x7f)) + hash_data(buf, size); +} + +static void send_request_chunk(size_t length) +{ + resp.data_length = length <= 1024 ? length : 1024; + msg_write(MessageType_MessageType_EthereumTxRequest, &resp); +} + +/* + * RLP fields: + * - nonce (0 .. 32) + * - gas_price (0 .. 32) + * - gas_limit (0 .. 32) + * - to (0, 20) + * - value (0 .. 32) + * - data (0 ..) + */ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) { - (void)node; - signing = true; + sha3_256_Init(&keccak_ctx); - fsm_sendFailure(FailureType_Failure_Other, "Unsupported feature"); + memset(&resp, 0, sizeof(EthereumTxRequest)); + /* NOTE: in the first stage we'll always request more data */ + resp.has_data_length = true; + + /* Stage 1: Calculate total RLP length */ + int total_rlp_length = 0; + + if (msg->has_nonce) + total_rlp_length += rlp_calculate_length(msg->nonce.size, msg->nonce.bytes[0]); + else + total_rlp_length++; + + if (msg->has_gas_price) + total_rlp_length += rlp_calculate_length(msg->gas_price.size, msg->gas_price.bytes[0]); + else + total_rlp_length++; + + if (msg->has_gas_limit) + total_rlp_length += rlp_calculate_length(msg->gas_limit.size, msg->gas_limit.bytes[0]); + else + total_rlp_length++; + + if (msg->has_to) + total_rlp_length += rlp_calculate_length(msg->to.size, msg->to.bytes[0]); + else + total_rlp_length++; + + if (msg->has_value) + total_rlp_length += rlp_calculate_length(msg->value.size, msg->value.bytes[0]); + else + total_rlp_length++; + + if (msg->has_data_initial_chunk) { + if (msg->has_data_length) + total_rlp_length += rlp_calculate_length(msg->data_initial_chunk.size + msg->data_length, msg->data_initial_chunk.bytes[0]); + else + total_rlp_length += rlp_calculate_length(msg->data_initial_chunk.size, msg->data_initial_chunk.bytes[0]); + } else + total_rlp_length++; + + /* Stage 2: Store header fields */ + hash_rlp_list_length(total_rlp_length); + + if (msg->has_nonce) + hash_rlp_field(msg->nonce.bytes, msg->nonce.size); + if (msg->has_gas_price) + hash_rlp_field(msg->gas_price.bytes, msg->gas_price.size); + if (msg->has_gas_limit) + hash_rlp_field(msg->gas_limit.bytes, msg->gas_limit.size); + if (msg->has_to) + hash_rlp_field(msg->to.bytes, msg->to.size); + if (msg->has_value) + hash_rlp_field(msg->value.bytes, msg->value.size); + if (msg->has_data_initial_chunk) + hash_rlp_field(msg->data_initial_chunk.bytes, msg->data_initial_chunk.size); + + /* FIXME: probably this shouldn't be done here, but at a later stage */ + memcpy(privkey, node->private_key, 32); + + if (msg->has_data_length && msg->data_length > 0) { + data_left = msg->data_length; + send_request_chunk(msg->data_length); + } } void ethereum_signing_txack(EthereumTxAck *tx) { - (void)tx; - if (!signing) { fsm_sendFailure(FailureType_Failure_UnexpectedMessage, "Not in Signing mode"); layoutHome(); return; } - fsm_sendFailure(FailureType_Failure_Other, "Unsupported feature"); + if (data_left > 0 && (!tx->has_data_chunk || tx->data_chunk.size == 0)) { + fsm_sendFailure(FailureType_Failure_Other, "Empty data chunk received"); + ethereum_signing_abort(); + return; + } + + hash_data(tx->data_chunk.bytes, tx->data_chunk.size); + + data_left -= tx->data_chunk.size; + + if (data_left > 0) { + /* Request more data */ + send_request_chunk(data_left); + } else { + /* Create signature */ + keccak_Final(&keccak_ctx, hash); + uint8_t v; + if (ecdsa_sign_digest(&secp256k1, privkey, hash, sig, &v) != 0) { + fsm_sendFailure(FailureType_Failure_Other, "Signing failed"); + ethereum_signing_abort(); + return; + } + + memset(privkey, 0, sizeof(privkey)); + + /* Send back the result */ + resp.has_data_length = false; + + resp.has_signature_v = true; + resp.signature_v = v + 27; + + resp.has_signature_r = true; + resp.signature_r.size = 32; + memcpy(resp.signature_r.bytes, sig, 32); + + resp.has_signature_s = true; + resp.signature_s.size = 32; + memcpy(resp.signature_s.bytes, sig + 32, 32); + + msg_write(MessageType_MessageType_EthereumTxRequest, &resp); + } } void ethereum_signing_abort(void) { if (signing) { + memset(privkey, 0, sizeof(privkey)); layoutHome(); signing = false; } From 2b6c99117999ace8742716b1c4459a0547d97701 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 25 May 2016 02:41:10 +0100 Subject: [PATCH 08/28] Split out send_signature and support short requests --- firmware/ethereum.c | 98 ++++++++++++++++++++++++++++++++------------- 1 file changed, 71 insertions(+), 27 deletions(-) diff --git a/firmware/ethereum.c b/firmware/ethereum.c index 24c42a7..ab3416f 100644 --- a/firmware/ethereum.c +++ b/firmware/ethereum.c @@ -125,6 +125,37 @@ static void send_request_chunk(size_t length) msg_write(MessageType_MessageType_EthereumTxRequest, &resp); } +static void send_signature(void) +{ + keccak_Final(&keccak_ctx, hash); + uint8_t v; + if (ecdsa_sign_digest(&secp256k1, privkey, hash, sig, &v) != 0) { + fsm_sendFailure(FailureType_Failure_Other, "Signing failed"); + ethereum_signing_abort(); + return; + } + + memset(privkey, 0, sizeof(privkey)); + + /* Send back the result */ + resp.has_data_length = false; + + resp.has_signature_v = true; + resp.signature_v = v + 27; + + resp.has_signature_r = true; + resp.signature_r.size = 32; + memcpy(resp.signature_r.bytes, sig, 32); + + resp.has_signature_s = true; + resp.signature_s.size = 32; + memcpy(resp.signature_s.bytes, sig + 32, 32); + + msg_write(MessageType_MessageType_EthereumTxRequest, &resp); + + ethereum_signing_abort(); +} + /* * RLP fields: * - nonce (0 .. 32) @@ -147,31 +178,43 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) /* Stage 1: Calculate total RLP length */ int total_rlp_length = 0; + layoutProgress("Signing Eth", 1); + if (msg->has_nonce) total_rlp_length += rlp_calculate_length(msg->nonce.size, msg->nonce.bytes[0]); else total_rlp_length++; + layoutProgress("Signing Eth", 2); + if (msg->has_gas_price) total_rlp_length += rlp_calculate_length(msg->gas_price.size, msg->gas_price.bytes[0]); else total_rlp_length++; + layoutProgress("Signing Eth", 3); + if (msg->has_gas_limit) total_rlp_length += rlp_calculate_length(msg->gas_limit.size, msg->gas_limit.bytes[0]); else total_rlp_length++; + layoutProgress("Signing Eth", 4); + if (msg->has_to) total_rlp_length += rlp_calculate_length(msg->to.size, msg->to.bytes[0]); else total_rlp_length++; + layoutProgress("Signing Eth", 5); + if (msg->has_value) total_rlp_length += rlp_calculate_length(msg->value.size, msg->value.bytes[0]); else total_rlp_length++; + layoutProgress("Signing Eth", 6); + if (msg->has_data_initial_chunk) { if (msg->has_data_length) total_rlp_length += rlp_calculate_length(msg->data_initial_chunk.size + msg->data_length, msg->data_initial_chunk.bytes[0]); @@ -180,28 +223,55 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) } else total_rlp_length++; + layoutProgress("Signing Eth", 7); + /* Stage 2: Store header fields */ hash_rlp_list_length(total_rlp_length); + layoutProgress("Signing Eth", 8); + if (msg->has_nonce) hash_rlp_field(msg->nonce.bytes, msg->nonce.size); + else + hash_rlp_length(1, 0); + if (msg->has_gas_price) hash_rlp_field(msg->gas_price.bytes, msg->gas_price.size); + else + hash_rlp_length(1, 0); + if (msg->has_gas_limit) hash_rlp_field(msg->gas_limit.bytes, msg->gas_limit.size); + else + hash_rlp_length(1, 0); + if (msg->has_to) hash_rlp_field(msg->to.bytes, msg->to.size); + else + hash_rlp_length(1, 0); + if (msg->has_value) hash_rlp_field(msg->value.bytes, msg->value.size); + else + hash_rlp_length(1, 0); + if (msg->has_data_initial_chunk) hash_rlp_field(msg->data_initial_chunk.bytes, msg->data_initial_chunk.size); + else + hash_rlp_length(1, 0); + + layoutProgress("Signing Eth", 9); /* FIXME: probably this shouldn't be done here, but at a later stage */ memcpy(privkey, node->private_key, 32); if (msg->has_data_length && msg->data_length > 0) { + layoutProgress("Signing Eth", 20); data_left = msg->data_length; send_request_chunk(msg->data_length); + } else { + layoutProgress("Signing Eth", 50); + send_signature(); } } @@ -224,35 +294,9 @@ void ethereum_signing_txack(EthereumTxAck *tx) data_left -= tx->data_chunk.size; if (data_left > 0) { - /* Request more data */ send_request_chunk(data_left); } else { - /* Create signature */ - keccak_Final(&keccak_ctx, hash); - uint8_t v; - if (ecdsa_sign_digest(&secp256k1, privkey, hash, sig, &v) != 0) { - fsm_sendFailure(FailureType_Failure_Other, "Signing failed"); - ethereum_signing_abort(); - return; - } - - memset(privkey, 0, sizeof(privkey)); - - /* Send back the result */ - resp.has_data_length = false; - - resp.has_signature_v = true; - resp.signature_v = v + 27; - - resp.has_signature_r = true; - resp.signature_r.size = 32; - memcpy(resp.signature_r.bytes, sig, 32); - - resp.has_signature_s = true; - resp.signature_s.size = 32; - memcpy(resp.signature_s.bytes, sig + 32, 32); - - msg_write(MessageType_MessageType_EthereumTxRequest, &resp); + send_signature(); } } From e0395b13eb922614d253423d838e7097c1b4c9bc Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 25 May 2016 09:28:53 +0100 Subject: [PATCH 09/28] Fix RLP length calculation --- firmware/ethereum.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/firmware/ethereum.c b/firmware/ethereum.c index ab3416f..32d5672 100644 --- a/firmware/ethereum.c +++ b/firmware/ethereum.c @@ -74,13 +74,13 @@ static int rlp_calculate_length(int length, uint8_t firstbyte) { if (length == 1 && firstbyte <= 0x7f) { return 1; } else if (length <= 55) { - return 2; + return 1 + length; } else if (length <= 0xff) { - return 2; + return 2 + length; } else if (length <= 0xffff) { - return 3; + return 3 + length; } else - return 4; + return 4 + length; } static inline void hash_data(const uint8_t *buf, size_t size) From ab49a7cb45bcaabf836c683063c17ecf831866a0 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 25 May 2016 15:32:52 +0100 Subject: [PATCH 10/28] Calculate data length based on the initial chunk and the supplied length --- firmware/ethereum.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/firmware/ethereum.c b/firmware/ethereum.c index 32d5672..337138a 100644 --- a/firmware/ethereum.c +++ b/firmware/ethereum.c @@ -177,6 +177,12 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) /* Stage 1: Calculate total RLP length */ int total_rlp_length = 0; + int total_data_length = 0; + + if (msg->has_data_initial_chunk) + total_data_length += msg->data_initial_chunk.size; + if (msg->has_data_length) + total_data_length += msg->data_length; layoutProgress("Signing Eth", 1); @@ -215,12 +221,9 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) layoutProgress("Signing Eth", 6); - if (msg->has_data_initial_chunk) { - if (msg->has_data_length) - total_rlp_length += rlp_calculate_length(msg->data_initial_chunk.size + msg->data_length, msg->data_initial_chunk.bytes[0]); - else - total_rlp_length += rlp_calculate_length(msg->data_initial_chunk.size, msg->data_initial_chunk.bytes[0]); - } else + if (msg->has_data_initial_chunk) + total_rlp_length += rlp_calculate_length(total_data_length, msg->data_initial_chunk.bytes[0]); + else total_rlp_length++; layoutProgress("Signing Eth", 7); @@ -255,9 +258,10 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) else hash_rlp_length(1, 0); - if (msg->has_data_initial_chunk) - hash_rlp_field(msg->data_initial_chunk.bytes, msg->data_initial_chunk.size); - else + if (msg->has_data_initial_chunk) { + hash_rlp_length(total_data_length, msg->data_initial_chunk.bytes[0]); + hash_data(msg->data_initial_chunk.bytes, msg->data_initial_chunk.size); + } else hash_rlp_length(1, 0); layoutProgress("Signing Eth", 9); From e0a174300378cfd6e85aca3ed6a324189eaa9e34 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 25 May 2016 15:33:12 +0100 Subject: [PATCH 11/28] Add sanity checks for data fields in EthereumSignTx --- firmware/ethereum.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/firmware/ethereum.c b/firmware/ethereum.c index 337138a..840a5e0 100644 --- a/firmware/ethereum.c +++ b/firmware/ethereum.c @@ -175,6 +175,19 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) /* NOTE: in the first stage we'll always request more data */ resp.has_data_length = true; + /* FIXME: simplify this check */ + if (msg->has_data_initial_chunk) { + if (msg->has_data_length && msg->data_initial_chunk.size != 1024) { + fsm_sendFailure(FailureType_Failure_Other, "Data length provided, but initial chunk too small"); + ethereum_signing_abort(); + return; + } + } else if (msg->has_data_length) { + fsm_sendFailure(FailureType_Failure_Other, "Data length provided, but no initial chunk"); + ethereum_signing_abort(); + return; + } + /* Stage 1: Calculate total RLP length */ int total_rlp_length = 0; int total_data_length = 0; From a617200c9c45a1726348b08e765201de4e20ae74 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 25 May 2016 15:33:23 +0100 Subject: [PATCH 12/28] Add confirmation dialog to EthereumSignTx --- firmware/ethereum.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/firmware/ethereum.c b/firmware/ethereum.c index 840a5e0..4a25b38 100644 --- a/firmware/ethereum.c +++ b/firmware/ethereum.c @@ -27,6 +27,7 @@ #include "crypto.h" #include "secp256k1.h" #include "sha3.h" +#include "util.h" static bool signing = false; static size_t data_left; @@ -156,6 +157,30 @@ static void send_signature(void) ethereum_signing_abort(); } +/* FIXME */ +static void layoutEthereumConfirmTx(const uint8_t *to, const uint8_t *value) +{ + static char _value[21] = {0}; + static char _to1[21] = {0}; + static char _to2[21] = {0}; + + data2hex(value, 10, _value); + data2hex(to, 10, _to1); + data2hex(to + 10, 10, _to2); + + layoutDialogSwipe(DIALOG_ICON_QUESTION, + "Cancel", + "Confirm", + NULL, + "Really send", + _value, + "from your wallet?", + "To:", + _to1, + _to2 + ); +} + /* * RLP fields: * - nonce (0 .. 32) @@ -188,6 +213,14 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) return; } + /* FIXME: include default values (0 / 0) */ + layoutEthereumConfirmTx(msg->has_to ? msg->to.bytes : NULL, msg->has_value ? msg->value.bytes : NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, "Signing cancelled by user"); + ethereum_signing_abort(); + return; + } + /* Stage 1: Calculate total RLP length */ int total_rlp_length = 0; int total_data_length = 0; From 78b1370de99c11bf378b9fb74ce54906b8d0e55a Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 25 May 2016 15:39:11 +0100 Subject: [PATCH 13/28] More input sanity checks in EthereumSignTx --- firmware/ethereum.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/firmware/ethereum.c b/firmware/ethereum.c index 4a25b38..5dd0354 100644 --- a/firmware/ethereum.c +++ b/firmware/ethereum.c @@ -202,10 +202,17 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) /* FIXME: simplify this check */ if (msg->has_data_initial_chunk) { - if (msg->has_data_length && msg->data_initial_chunk.size != 1024) { - fsm_sendFailure(FailureType_Failure_Other, "Data length provided, but initial chunk too small"); - ethereum_signing_abort(); - return; + if (msg->has_data_length) { + if (msg->data_initial_chunk.size != 1024) { + fsm_sendFailure(FailureType_Failure_Other, "Data length provided, but initial chunk too small"); + ethereum_signing_abort(); + return; + } + if (msg->data_length == 0) { + fsm_sendFailure(FailureType_Failure_Other, "Invalid data length provided"); + ethereum_signing_abort(); + return; + } } } else if (msg->has_data_length) { fsm_sendFailure(FailureType_Failure_Other, "Data length provided, but no initial chunk"); From 079d282541e97c6c28ceb776d59e69fe362859f8 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 25 May 2016 15:40:23 +0100 Subject: [PATCH 14/28] Simplify send_request_chunk() --- firmware/ethereum.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/firmware/ethereum.c b/firmware/ethereum.c index 5dd0354..5e554c6 100644 --- a/firmware/ethereum.c +++ b/firmware/ethereum.c @@ -120,9 +120,9 @@ static void hash_rlp_field(const uint8_t *buf, size_t size) hash_data(buf, size); } -static void send_request_chunk(size_t length) +static void send_request_chunk(void) { - resp.data_length = length <= 1024 ? length : 1024; + resp.data_length = data_left <= 1024 ? data_left : 1024; msg_write(MessageType_MessageType_EthereumTxRequest, &resp); } @@ -325,7 +325,7 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) if (msg->has_data_length && msg->data_length > 0) { layoutProgress("Signing Eth", 20); data_left = msg->data_length; - send_request_chunk(msg->data_length); + send_request_chunk(); } else { layoutProgress("Signing Eth", 50); send_signature(); @@ -351,7 +351,7 @@ void ethereum_signing_txack(EthereumTxAck *tx) data_left -= tx->data_chunk.size; if (data_left > 0) { - send_request_chunk(data_left); + send_request_chunk(); } else { send_signature(); } From 7432805b6aae638bf4378e10ee2fb9511ca41800 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 25 May 2016 20:03:13 +0100 Subject: [PATCH 15/28] Fix special RLP case for length=1 firstbyte=0 --- firmware/ethereum.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/firmware/ethereum.c b/firmware/ethereum.c index 5e554c6..6baf2cb 100644 --- a/firmware/ethereum.c +++ b/firmware/ethereum.c @@ -43,7 +43,8 @@ struct SHA3_CTX keccak_ctx; static int rlp_encode_length(uint8_t *buf, int length, uint8_t firstbyte, bool list) { if (!list && (length == 1 && firstbyte <= 0x7f)) { - buf[0] = firstbyte; + /* Extra-special case: null is encoded differently */ + buf[0] = !firstbyte ? 0x80 : firstbyte; return 1; } else if (length <= 55) { buf[0] = (list ? 0xc0 : 0x80) + length; From 4a195ebd86874803a04c31724f4fc037c032d81b Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Thu, 26 May 2016 21:56:31 +0100 Subject: [PATCH 16/28] Don't include 0x in address display, so everything fits --- firmware/fsm.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/firmware/fsm.c b/firmware/fsm.c index b2e3ef7..e72e38a 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -678,10 +678,8 @@ void fsm_msgEthereumGetAddress(EthereumGetAddress *msg) char desc[16]; strlcpy(desc, "Address:", sizeof(desc)); - char address[43]; - address[0] = '0'; - address[1] = 'x'; - data2hex(resp->address.bytes, 20, address + 2); + char address[41]; + data2hex(resp->address.bytes, 20, address); layoutAddress(address, desc); if (!protectButton(ButtonRequestType_ButtonRequest_Address, true)) { From 7d9a56e678cfcb035a994a498ffe31002355a94d Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Tue, 28 Jun 2016 20:44:54 +0200 Subject: [PATCH 17/28] fix curly braces in if statements --- firmware/ethereum.c | 70 ++++++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 27 deletions(-) diff --git a/firmware/ethereum.c b/firmware/ethereum.c index 6baf2cb..b76a2b1 100644 --- a/firmware/ethereum.c +++ b/firmware/ethereum.c @@ -81,8 +81,9 @@ static int rlp_calculate_length(int length, uint8_t firstbyte) { return 2 + length; } else if (length <= 0xffff) { return 3 + length; - } else + } else { return 4 + length; + } } static inline void hash_data(const uint8_t *buf, size_t size) @@ -117,8 +118,9 @@ static void hash_rlp_field(const uint8_t *buf, size_t size) { hash_rlp_length(size, buf[0]); /* FIXME: this special case should be handled more nicely */ - if (!(size == 1 && buf[0] <= 0x7f)) + if (!(size == 1 && buf[0] <= 0x7f)) { hash_data(buf, size); + } } static void send_request_chunk(void) @@ -233,52 +235,60 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) int total_rlp_length = 0; int total_data_length = 0; - if (msg->has_data_initial_chunk) + if (msg->has_data_initial_chunk) { total_data_length += msg->data_initial_chunk.size; - if (msg->has_data_length) + } + if (msg->has_data_length) { total_data_length += msg->data_length; + } layoutProgress("Signing Eth", 1); - if (msg->has_nonce) + if (msg->has_nonce) { total_rlp_length += rlp_calculate_length(msg->nonce.size, msg->nonce.bytes[0]); - else + } else { total_rlp_length++; + } layoutProgress("Signing Eth", 2); - if (msg->has_gas_price) + if (msg->has_gas_price) { total_rlp_length += rlp_calculate_length(msg->gas_price.size, msg->gas_price.bytes[0]); - else + } else { total_rlp_length++; + } layoutProgress("Signing Eth", 3); - if (msg->has_gas_limit) + if (msg->has_gas_limit) { total_rlp_length += rlp_calculate_length(msg->gas_limit.size, msg->gas_limit.bytes[0]); - else + } else { total_rlp_length++; + } layoutProgress("Signing Eth", 4); - if (msg->has_to) + if (msg->has_to) { total_rlp_length += rlp_calculate_length(msg->to.size, msg->to.bytes[0]); - else + } else { total_rlp_length++; + } layoutProgress("Signing Eth", 5); - if (msg->has_value) + if (msg->has_value) { total_rlp_length += rlp_calculate_length(msg->value.size, msg->value.bytes[0]); - else + } else { total_rlp_length++; + } layoutProgress("Signing Eth", 6); - if (msg->has_data_initial_chunk) + if (msg->has_data_initial_chunk) { total_rlp_length += rlp_calculate_length(total_data_length, msg->data_initial_chunk.bytes[0]); - else + } else { total_rlp_length++; + } layoutProgress("Signing Eth", 7); @@ -287,36 +297,42 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) layoutProgress("Signing Eth", 8); - if (msg->has_nonce) + if (msg->has_nonce) { hash_rlp_field(msg->nonce.bytes, msg->nonce.size); - else + } else { hash_rlp_length(1, 0); + } - if (msg->has_gas_price) + if (msg->has_gas_price) { hash_rlp_field(msg->gas_price.bytes, msg->gas_price.size); - else + } else { hash_rlp_length(1, 0); + } - if (msg->has_gas_limit) + if (msg->has_gas_limit) { hash_rlp_field(msg->gas_limit.bytes, msg->gas_limit.size); - else + } else { hash_rlp_length(1, 0); + } - if (msg->has_to) + if (msg->has_to) { hash_rlp_field(msg->to.bytes, msg->to.size); - else + } else { hash_rlp_length(1, 0); + } - if (msg->has_value) + if (msg->has_value) { hash_rlp_field(msg->value.bytes, msg->value.size); - else + } else { hash_rlp_length(1, 0); + } if (msg->has_data_initial_chunk) { hash_rlp_length(total_data_length, msg->data_initial_chunk.bytes[0]); hash_data(msg->data_initial_chunk.bytes, msg->data_initial_chunk.size); - } else + } else { hash_rlp_length(1, 0); + } layoutProgress("Signing Eth", 9); From 48008ddd8eeae19005011dc01a3d9bc24c1be244 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Thu, 30 Jun 2016 14:03:12 +0200 Subject: [PATCH 18/28] implement layoutEthereumConfirmTx --- firmware/ethereum.c | 77 ++++++++++++++++++++++++++++++++++++++++----- firmware/layout2.c | 7 ++++- 2 files changed, 76 insertions(+), 8 deletions(-) diff --git a/firmware/ethereum.c b/firmware/ethereum.c index b76a2b1..98b1747 100644 --- a/firmware/ethereum.c +++ b/firmware/ethereum.c @@ -160,23 +160,87 @@ static void send_signature(void) ethereum_signing_abort(); } -/* FIXME */ static void layoutEthereumConfirmTx(const uint8_t *to, const uint8_t *value) { - static char _value[21] = {0}; + static bignum256 val; + static uint16_t num[26]; + static uint8_t last_used = 0; + if (value) { + bn_read_be(value, &val); + } else { + bn_zero(&val); + } + for (int i = 0; i < 26; i++) { + bn_divmod1000(&val, (uint32_t *)&(num[i])); + if (num[i] > 0) { + last_used = i; + } + } + + static char _value[25] = {0}; + const char *value_ptr = _value; + + if (last_used < 3) { + // value is smaller than 1e9 wei => show value in wei + _value[0] = '0' + (num[2] / 100) % 10; + _value[1] = '0' + (num[2] / 10) % 10; + _value[2] = '0' + (num[2]) % 10; + _value[3] = '0' + (num[1] / 100) % 10; + _value[4] = '0' + (num[1] / 10) % 10; + _value[5] = '0' + (num[1]) % 10; + _value[6] = '0' + (num[0] / 100) % 10; + _value[7] = '0' + (num[0] / 10) % 10; + _value[8] = '0' + (num[0]) % 10; + strlcpy(_value + 9, " wei", sizeof(_value) - 9); + } else if (last_used < 9) { + // value is bigger than 1e9 wei and smaller than 1e9 ETH => show value in ETH + _value[0] = '0' + (num[8] / 100) % 10; + _value[1] = '0' + (num[8] / 10) % 10; + _value[2] = '0' + (num[8]) % 10; + _value[3] = '0' + (num[7] / 100) % 10; + _value[4] = '0' + (num[7] / 10) % 10; + _value[5] = '0' + (num[7]) % 10; + _value[6] = '0' + (num[6] / 100) % 10; + _value[7] = '0' + (num[6] / 10) % 10; + _value[8] = '0' + (num[6]) % 10; + _value[9] = '.'; + _value[10] = '0' + (num[5] / 100) % 10; + _value[11] = '0' + (num[5] / 10) % 10; + _value[12] = '0' + (num[5]) % 10; + _value[13] = '0' + (num[4] / 100) % 10; + _value[14] = '0' + (num[4] / 10) % 10; + _value[15] = '0' + (num[4]) % 10; + _value[16] = '0' + (num[3] / 100) % 10; + _value[17] = '0' + (num[3] / 10) % 10; + _value[18] = '0' + (num[3]) % 10; + strlcpy(_value + 19, " ETH", sizeof(_value) - 19); + } else { + // value is bigger than 1e9 ETH => won't fit on display (probably won't happen unless you are Vitalik) + strlcpy(_value, "more than a billion ETH", sizeof(_value)); + } + + value_ptr = _value; + while (*value_ptr == '0') { // skip leading zeroes + value_ptr++; + } + static char _to1[21] = {0}; static char _to2[21] = {0}; - data2hex(value, 10, _value); - data2hex(to, 10, _to1); - data2hex(to + 10, 10, _to2); + if (to) { + data2hex(to, 10, _to1); + data2hex(to + 10, 10, _to2); + } else { + strlcpy(_to1, "no recipient", sizeof(_to1)); + strlcpy(_to2, "", sizeof(_to2)); + } layoutDialogSwipe(DIALOG_ICON_QUESTION, "Cancel", "Confirm", NULL, "Really send", - _value, + value_ptr, "from your wallet?", "To:", _to1, @@ -223,7 +287,6 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) return; } - /* FIXME: include default values (0 / 0) */ layoutEthereumConfirmTx(msg->has_to ? msg->to.bytes : NULL, msg->has_value ? msg->value.bytes : NULL); if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { fsm_sendFailure(FailureType_Failure_ActionCancelled, "Signing cancelled by user"); diff --git a/firmware/layout2.c b/firmware/layout2.c index fdb3626..c26d0fc 100644 --- a/firmware/layout2.c +++ b/firmware/layout2.c @@ -261,7 +261,12 @@ void layoutAddress(const char *address, const char *desc) } } - const char **str = split_message((const uint8_t *)address, strlen(address), 9); + uint32_t addrlen = strlen(address); + uint32_t rowlen = addrlen / 4; + if (addrlen % 4) { + rowlen++; + } + const char **str = split_message((const uint8_t *)address, addrlen, rowlen); if (desc) { oledDrawString(68, 0 * 9, desc); From 3d1ab24d92cca4094b63b8956bdecf61623486bb Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Wed, 10 Aug 2016 18:37:30 +0200 Subject: [PATCH 19/28] simplify ethereum code, EthereumSignTx.data_length is the total length now --- firmware/ethereum.c | 64 ++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 35 deletions(-) diff --git a/firmware/ethereum.c b/firmware/ethereum.c index 98b1747..f89a6f9 100644 --- a/firmware/ethereum.c +++ b/firmware/ethereum.c @@ -125,6 +125,7 @@ static void hash_rlp_field(const uint8_t *buf, size_t size) static void send_request_chunk(void) { + resp.has_data_length = true; resp.data_length = data_left <= 1024 ? data_left : 1024; msg_write(MessageType_MessageType_EthereumTxRequest, &resp); } @@ -264,27 +265,23 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) sha3_256_Init(&keccak_ctx); memset(&resp, 0, sizeof(EthereumTxRequest)); - /* NOTE: in the first stage we'll always request more data */ - resp.has_data_length = true; - /* FIXME: simplify this check */ - if (msg->has_data_initial_chunk) { - if (msg->has_data_length) { - if (msg->data_initial_chunk.size != 1024) { - fsm_sendFailure(FailureType_Failure_Other, "Data length provided, but initial chunk too small"); - ethereum_signing_abort(); - return; - } - if (msg->data_length == 0) { - fsm_sendFailure(FailureType_Failure_Other, "Invalid data length provided"); - ethereum_signing_abort(); - return; - } + if (msg->has_data_length) { + if (msg->data_length == 0) { + fsm_sendFailure(FailureType_Failure_Other, "Invalid data length provided"); + ethereum_signing_abort(); + return; + } + if (!msg->has_data_initial_chunk || msg->data_initial_chunk.size == 0) { + fsm_sendFailure(FailureType_Failure_Other, "Data length provided, but no initial chunk"); + ethereum_signing_abort(); + return; + } + if (msg->data_initial_chunk.size > msg->data_length) { + fsm_sendFailure(FailureType_Failure_Other, "Invalid size of initial chunk"); + ethereum_signing_abort(); + return; } - } else if (msg->has_data_length) { - fsm_sendFailure(FailureType_Failure_Other, "Data length provided, but no initial chunk"); - ethereum_signing_abort(); - return; } layoutEthereumConfirmTx(msg->has_to ? msg->to.bytes : NULL, msg->has_value ? msg->value.bytes : NULL); @@ -298,14 +295,11 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) int total_rlp_length = 0; int total_data_length = 0; - if (msg->has_data_initial_chunk) { - total_data_length += msg->data_initial_chunk.size; - } if (msg->has_data_length) { - total_data_length += msg->data_length; + total_data_length = msg->data_length; } - layoutProgress("Signing Eth", 1); + layoutProgress("Signing", 1); if (msg->has_nonce) { total_rlp_length += rlp_calculate_length(msg->nonce.size, msg->nonce.bytes[0]); @@ -313,7 +307,7 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) total_rlp_length++; } - layoutProgress("Signing Eth", 2); + layoutProgress("Signing", 2); if (msg->has_gas_price) { total_rlp_length += rlp_calculate_length(msg->gas_price.size, msg->gas_price.bytes[0]); @@ -321,7 +315,7 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) total_rlp_length++; } - layoutProgress("Signing Eth", 3); + layoutProgress("Signing", 3); if (msg->has_gas_limit) { total_rlp_length += rlp_calculate_length(msg->gas_limit.size, msg->gas_limit.bytes[0]); @@ -329,7 +323,7 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) total_rlp_length++; } - layoutProgress("Signing Eth", 4); + layoutProgress("Signing", 4); if (msg->has_to) { total_rlp_length += rlp_calculate_length(msg->to.size, msg->to.bytes[0]); @@ -337,7 +331,7 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) total_rlp_length++; } - layoutProgress("Signing Eth", 5); + layoutProgress("Signing", 5); if (msg->has_value) { total_rlp_length += rlp_calculate_length(msg->value.size, msg->value.bytes[0]); @@ -345,7 +339,7 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) total_rlp_length++; } - layoutProgress("Signing Eth", 6); + layoutProgress("Signing", 6); if (msg->has_data_initial_chunk) { total_rlp_length += rlp_calculate_length(total_data_length, msg->data_initial_chunk.bytes[0]); @@ -353,12 +347,12 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) total_rlp_length++; } - layoutProgress("Signing Eth", 7); + layoutProgress("Signing", 7); /* Stage 2: Store header fields */ hash_rlp_list_length(total_rlp_length); - layoutProgress("Signing Eth", 8); + layoutProgress("Signing", 8); if (msg->has_nonce) { hash_rlp_field(msg->nonce.bytes, msg->nonce.size); @@ -397,17 +391,17 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) hash_rlp_length(1, 0); } - layoutProgress("Signing Eth", 9); + layoutProgress("Signing", 9); /* FIXME: probably this shouldn't be done here, but at a later stage */ memcpy(privkey, node->private_key, 32); if (msg->has_data_length && msg->data_length > 0) { - layoutProgress("Signing Eth", 20); - data_left = msg->data_length; + layoutProgress("Signing", 20); + data_left = msg->data_length - msg->data_initial_chunk.size; send_request_chunk(); } else { - layoutProgress("Signing Eth", 50); + layoutProgress("Signing", 50); send_signature(); } } From 3db323c599caca8d284a434b521764429bff118d Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Wed, 10 Aug 2016 19:15:29 +0200 Subject: [PATCH 20/28] fix printing of ethereum value and address --- firmware/ethereum.c | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/firmware/ethereum.c b/firmware/ethereum.c index f89a6f9..9bf34cc 100644 --- a/firmware/ethereum.c +++ b/firmware/ethereum.c @@ -161,16 +161,19 @@ static void send_signature(void) ethereum_signing_abort(); } -static void layoutEthereumConfirmTx(const uint8_t *to, const uint8_t *value) +static void layoutEthereumConfirmTx(const uint8_t *to, const uint8_t *value, uint32_t value_len) { - static bignum256 val; - static uint16_t num[26]; - static uint8_t last_used = 0; - if (value) { - bn_read_be(value, &val); + bignum256 val; + if (value && value_len <= 32) { + uint8_t pad_val[32]; + memset(pad_val, 0, sizeof(pad_val)); + memcpy(pad_val + (32 - value_len), value, value_len); + bn_read_be(pad_val, &val); } else { bn_zero(&val); } + uint16_t num[26]; + uint8_t last_used = 0; for (int i = 0; i < 26; i++) { bn_divmod1000(&val, (uint32_t *)&(num[i])); if (num[i] > 0) { @@ -221,19 +224,24 @@ static void layoutEthereumConfirmTx(const uint8_t *to, const uint8_t *value) } value_ptr = _value; - while (*value_ptr == '0') { // skip leading zeroes + while (*value_ptr == '0' && *(value_ptr + 1) >= '0' && *(value_ptr + 1) <= '9') { // skip leading zeroes value_ptr++; } - static char _to1[21] = {0}; - static char _to2[21] = {0}; + static char _to1[17] = {0}; + static char _to2[17] = {0}; + static char _to3[17] = {0}; if (to) { - data2hex(to, 10, _to1); - data2hex(to + 10, 10, _to2); + strcpy(_to1, "to "); + data2hex(to, 6, _to1 + 3); + data2hex(to + 6, 7, _to2); + data2hex(to + 13, 7, _to3); + _to3[14] = '?'; _to3[15] = 0; } else { - strlcpy(_to1, "no recipient", sizeof(_to1)); + strlcpy(_to1, "to no recipient?", sizeof(_to1)); strlcpy(_to2, "", sizeof(_to2)); + strlcpy(_to3, "", sizeof(_to3)); } layoutDialogSwipe(DIALOG_ICON_QUESTION, @@ -242,10 +250,10 @@ static void layoutEthereumConfirmTx(const uint8_t *to, const uint8_t *value) NULL, "Really send", value_ptr, - "from your wallet?", - "To:", _to1, - _to2 + _to2, + _to3, + NULL ); } @@ -284,7 +292,7 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) } } - layoutEthereumConfirmTx(msg->has_to ? msg->to.bytes : NULL, msg->has_value ? msg->value.bytes : NULL); + layoutEthereumConfirmTx(msg->has_to ? msg->to.bytes : NULL, msg->has_value ? msg->value.bytes : NULL, msg->has_value ? msg->value.size : 0); if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { fsm_sendFailure(FailureType_Failure_ActionCancelled, "Signing cancelled by user"); ethereum_signing_abort(); From 1558d77ea020fb2b231f547823ff24ab3f057d97 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Tue, 16 Aug 2016 14:06:05 +0200 Subject: [PATCH 21/28] split rlp_encode_length into rlp_encode_length and rlp_encode_list_length --- firmware/ethereum.c | 50 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/firmware/ethereum.c b/firmware/ethereum.c index 9bf34cc..5b8df6c 100644 --- a/firmware/ethereum.c +++ b/firmware/ethereum.c @@ -38,28 +38,56 @@ struct SHA3_CTX keccak_ctx; /* * Encode length according to RLP. - * FIXME: improve */ -static int rlp_encode_length(uint8_t *buf, int length, uint8_t firstbyte, bool list) +static int rlp_encode_length(uint8_t *buf, int length, uint8_t firstbyte) { - if (!list && (length == 1 && firstbyte <= 0x7f)) { - /* Extra-special case: null is encoded differently */ - buf[0] = !firstbyte ? 0x80 : firstbyte; + if (length == 1 && firstbyte == 0x00) { + // extra-special case: null is encoded differently + buf[0] = 0x80; + return 1; + } else if (length == 1 && firstbyte <= 0x7f) { + buf[0] = firstbyte; return 1; } else if (length <= 55) { - buf[0] = (list ? 0xc0 : 0x80) + length; + buf[0] = 0x80 + length; return 1; } else if (length <= 0xff) { - buf[0] = (list ? 0xf7 : 0xb7) + 1; + buf[0] = 0xb7 + 1; buf[1] = length; return 2; } else if (length <= 0xffff) { - buf[0] = (list ? 0xf7 : 0xb7) + 2; + buf[0] = 0xb7 + 2; buf[1] = length >> 8; buf[2] = length & 0xff; return 3; } else { - buf[0] = (list ? 0xf7 : 0xb7) + 3; + buf[0] = 0xb7 + 3; + buf[1] = length >> 16; + buf[2] = length >> 8; + buf[3] = length & 0xff; + return 4; + } +} + +/* + * Encode list length according to RLP. + */ +static int rlp_encode_list_length(uint8_t *buf, int length) +{ + if (length <= 55) { + buf[0] = 0xc0 + length; + return 1; + } else if (length <= 0xff) { + buf[0] = 0xf7 + 1; + buf[1] = length; + return 2; + } else if (length <= 0xffff) { + buf[0] = 0xf7 + 2; + buf[1] = length >> 8; + buf[2] = length & 0xff; + return 3; + } else { + buf[0] = 0xf7 + 3; buf[1] = length >> 16; buf[2] = length >> 8; buf[3] = length & 0xff; @@ -97,7 +125,7 @@ static inline void hash_data(const uint8_t *buf, size_t size) static void hash_rlp_length(int length, uint8_t firstbyte) { uint8_t buf[4]; - size_t size = rlp_encode_length(buf, length, firstbyte, false); + size_t size = rlp_encode_length(buf, length, firstbyte); hash_data(buf, size); } @@ -107,7 +135,7 @@ static void hash_rlp_length(int length, uint8_t firstbyte) static void hash_rlp_list_length(int length) { uint8_t buf[4]; - size_t size = rlp_encode_length(buf, length, 0, true); + size_t size = rlp_encode_list_length(buf, length); hash_data(buf, size); } From 4e0a69b6ea4ab69e359561d78b13b8c1fa498ced Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Wed, 17 Aug 2016 13:26:54 +0200 Subject: [PATCH 22/28] refactor ethereum methods, show progress properly --- firmware/ethereum.c | 176 +++++++++++++++----------------------------- 1 file changed, 58 insertions(+), 118 deletions(-) diff --git a/firmware/ethereum.c b/firmware/ethereum.c index 5b8df6c..b52abc7 100644 --- a/firmware/ethereum.c +++ b/firmware/ethereum.c @@ -2,6 +2,7 @@ * This file is part of the TREZOR project. * * Copyright (C) 2016 Alex Beregszaszi + * Copyright (C) 2016 Pavol Rusnak * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -30,68 +31,85 @@ #include "util.h" static bool signing = false; -static size_t data_left; +static uint32_t data_total, data_left; static EthereumTxRequest resp; static uint8_t hash[32], sig[64], privkey[32]; -// FIXME: this is currently 400 bytes. Could be probably improved. struct SHA3_CTX keccak_ctx; -/* - * Encode length according to RLP. - */ -static int rlp_encode_length(uint8_t *buf, int length, uint8_t firstbyte) +static inline void hash_data(const uint8_t *buf, size_t size) { + sha3_Update(&keccak_ctx, buf, size); +} + +/* + * Push an RLP encoded length to the hash buffer. + */ +static void hash_rlp_length(uint32_t length, uint8_t firstbyte) +{ + uint8_t buf[4]; if (length == 1 && firstbyte == 0x00) { - // extra-special case: null is encoded differently + // special case: null is encoded differently buf[0] = 0x80; - return 1; + hash_data(buf, 1); } else if (length == 1 && firstbyte <= 0x7f) { buf[0] = firstbyte; - return 1; + hash_data(buf, 1); } else if (length <= 55) { buf[0] = 0x80 + length; - return 1; + hash_data(buf, 1); } else if (length <= 0xff) { buf[0] = 0xb7 + 1; buf[1] = length; - return 2; + hash_data(buf, 2); } else if (length <= 0xffff) { buf[0] = 0xb7 + 2; buf[1] = length >> 8; buf[2] = length & 0xff; - return 3; + hash_data(buf, 3); } else { buf[0] = 0xb7 + 3; buf[1] = length >> 16; buf[2] = length >> 8; buf[3] = length & 0xff; - return 4; + hash_data(buf, 4); } } /* - * Encode list length according to RLP. + * Push an RLP encoded list length to the hash buffer. */ -static int rlp_encode_list_length(uint8_t *buf, int length) +static void hash_rlp_list_length(uint32_t length) { + uint8_t buf[4]; if (length <= 55) { buf[0] = 0xc0 + length; - return 1; + hash_data(buf, 1); } else if (length <= 0xff) { buf[0] = 0xf7 + 1; buf[1] = length; - return 2; + hash_data(buf, 2); } else if (length <= 0xffff) { buf[0] = 0xf7 + 2; buf[1] = length >> 8; buf[2] = length & 0xff; - return 3; + hash_data(buf, 3); } else { buf[0] = 0xf7 + 3; buf[1] = length >> 16; buf[2] = length >> 8; buf[3] = length & 0xff; - return 4; + hash_data(buf, 4); + } +} + +/* + * Push an RLP encoded length field and data to the hash buffer. + */ +static void hash_rlp_field(const uint8_t *buf, size_t size) +{ + hash_rlp_length(size, buf[0]); + if (size > 1 || buf[0] >= 0x80) { + hash_data(buf, size); } } @@ -100,7 +118,8 @@ static int rlp_encode_list_length(uint8_t *buf, int length) * NOTE: supports up to 16MB of data (how unlikely...) * FIXME: improve */ -static int rlp_calculate_length(int length, uint8_t firstbyte) { +static int rlp_calculate_length(int length, uint8_t firstbyte) +{ if (length == 1 && firstbyte <= 0x7f) { return 1; } else if (length <= 55) { @@ -114,45 +133,10 @@ static int rlp_calculate_length(int length, uint8_t firstbyte) { } } -static inline void hash_data(const uint8_t *buf, size_t size) -{ - sha3_Update(&keccak_ctx, buf, size); -} - -/* - * Push an RLP encoded length to the hash buffer. - */ -static void hash_rlp_length(int length, uint8_t firstbyte) -{ - uint8_t buf[4]; - size_t size = rlp_encode_length(buf, length, firstbyte); - hash_data(buf, size); -} - -/* - * Push an RLP encoded list length to the hash buffer. - */ -static void hash_rlp_list_length(int length) -{ - uint8_t buf[4]; - size_t size = rlp_encode_list_length(buf, length); - hash_data(buf, size); -} - -/* - * Push an RLP encoded length field and data to the hash buffer. - */ -static void hash_rlp_field(const uint8_t *buf, size_t size) -{ - hash_rlp_length(size, buf[0]); - /* FIXME: this special case should be handled more nicely */ - if (!(size == 1 && buf[0] <= 0x7f)) { - hash_data(buf, size); - } -} static void send_request_chunk(void) { + layoutProgress("Signing", 1000 - 800 * data_left / data_total); resp.has_data_length = true; resp.data_length = data_left <= 1024 ? data_left : 1024; msg_write(MessageType_MessageType_EthereumTxRequest, &resp); @@ -160,6 +144,7 @@ static void send_request_chunk(void) static void send_signature(void) { + layoutProgress("Signing", 1000); keccak_Final(&keccak_ctx, hash); uint8_t v; if (ecdsa_sign_digest(&secp256k1, privkey, hash, sig, &v) != 0) { @@ -318,6 +303,9 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) ethereum_signing_abort(); return; } + data_total = msg->data_length; + } else { + data_total = 0; } layoutEthereumConfirmTx(msg->has_to ? msg->to.bytes : NULL, msg->has_value ? msg->value.bytes : NULL, msg->has_value ? msg->value.size : 0); @@ -328,67 +316,21 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) } /* Stage 1: Calculate total RLP length */ - int total_rlp_length = 0; - int total_data_length = 0; + uint32_t rlp_length = 0; - if (msg->has_data_length) { - total_data_length = msg->data_length; - } + layoutProgress("Signing", 0); - layoutProgress("Signing", 1); - - if (msg->has_nonce) { - total_rlp_length += rlp_calculate_length(msg->nonce.size, msg->nonce.bytes[0]); - } else { - total_rlp_length++; - } - - layoutProgress("Signing", 2); - - if (msg->has_gas_price) { - total_rlp_length += rlp_calculate_length(msg->gas_price.size, msg->gas_price.bytes[0]); - } else { - total_rlp_length++; - } - - layoutProgress("Signing", 3); - - if (msg->has_gas_limit) { - total_rlp_length += rlp_calculate_length(msg->gas_limit.size, msg->gas_limit.bytes[0]); - } else { - total_rlp_length++; - } - - layoutProgress("Signing", 4); - - if (msg->has_to) { - total_rlp_length += rlp_calculate_length(msg->to.size, msg->to.bytes[0]); - } else { - total_rlp_length++; - } - - layoutProgress("Signing", 5); - - if (msg->has_value) { - total_rlp_length += rlp_calculate_length(msg->value.size, msg->value.bytes[0]); - } else { - total_rlp_length++; - } - - layoutProgress("Signing", 6); - - if (msg->has_data_initial_chunk) { - total_rlp_length += rlp_calculate_length(total_data_length, msg->data_initial_chunk.bytes[0]); - } else { - total_rlp_length++; - } - - layoutProgress("Signing", 7); + rlp_length += msg->has_nonce ? rlp_calculate_length(msg->nonce.size, msg->nonce.bytes[0]) : 1; + rlp_length += msg->has_gas_price ? rlp_calculate_length(msg->gas_price.size, msg->gas_price.bytes[0]) : 1; + rlp_length += msg->has_gas_limit ? rlp_calculate_length(msg->gas_limit.size, msg->gas_limit.bytes[0]) : 1; + rlp_length += msg->has_to ? rlp_calculate_length(msg->to.size, msg->to.bytes[0]) : 1; + rlp_length += msg->has_value ? rlp_calculate_length(msg->value.size, msg->value.bytes[0]) : 1; + rlp_length += (msg->has_data_length && msg->has_data_initial_chunk) ? rlp_calculate_length(msg->data_length, msg->data_initial_chunk.bytes[0]) : 1; /* Stage 2: Store header fields */ - hash_rlp_list_length(total_rlp_length); + hash_rlp_list_length(rlp_length); - layoutProgress("Signing", 8); + layoutProgress("Signing", 100); if (msg->has_nonce) { hash_rlp_field(msg->nonce.bytes, msg->nonce.size); @@ -420,24 +362,22 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) hash_rlp_length(1, 0); } - if (msg->has_data_initial_chunk) { - hash_rlp_length(total_data_length, msg->data_initial_chunk.bytes[0]); + if (msg->has_data_length && msg->has_data_initial_chunk) { + hash_rlp_length(msg->data_length, msg->data_initial_chunk.bytes[0]); hash_data(msg->data_initial_chunk.bytes, msg->data_initial_chunk.size); } else { hash_rlp_length(1, 0); } - layoutProgress("Signing", 9); + layoutProgress("Signing", 200); /* FIXME: probably this shouldn't be done here, but at a later stage */ memcpy(privkey, node->private_key, 32); - if (msg->has_data_length && msg->data_length > 0) { - layoutProgress("Signing", 20); + if (msg->has_data_length && msg->data_length > msg->data_initial_chunk.size) { data_left = msg->data_length - msg->data_initial_chunk.size; send_request_chunk(); } else { - layoutProgress("Signing", 50); send_signature(); } } From efd443abe82f10f43930ce6a87e6908781b394ca Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Wed, 17 Aug 2016 17:18:13 +0200 Subject: [PATCH 23/28] implement ethereum signing check --- firmware/ethereum.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/firmware/ethereum.c b/firmware/ethereum.c index b52abc7..a023ab3 100644 --- a/firmware/ethereum.c +++ b/firmware/ethereum.c @@ -280,6 +280,31 @@ static void layoutEthereumConfirmTx(const uint8_t *to, const uint8_t *value, uin * - data (0 ..) */ +static bool ethereum_signing_check(EthereumSignTx *msg) +{ + // determine if address == 0 + bool address_zero = msg->has_to; + if (address_zero) { + for (size_t i = 0; i < msg->to.size; i++) { + if (msg->to.bytes[i] > 0) { + address_zero = false; + break; + } + } + } + + // sending value to address 0 + if (address_zero && msg->has_value && msg->value.size) { + return false; + } + // sending transaction to address 0 without a data field + if (address_zero && (!msg->has_data_length || msg->data_length == 0)) { + return false; + } + + return true; +} + void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) { signing = true; @@ -308,6 +333,13 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) data_total = 0; } + // safety checks + if (!ethereum_signing_check(msg)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, "Signing aborted (safety check failed)"); + ethereum_signing_abort(); + return; + } + layoutEthereumConfirmTx(msg->has_to ? msg->to.bytes : NULL, msg->has_value ? msg->value.bytes : NULL, msg->has_value ? msg->value.size : 0); if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { fsm_sendFailure(FailureType_Failure_ActionCancelled, "Signing cancelled by user"); From 22d0e7a05363de73508bb6425e6d623cc8e08dea Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Thu, 18 Aug 2016 16:32:51 +0200 Subject: [PATCH 24/28] Incorporated changes for updated master --- firmware/ethereum.c | 2 +- firmware/fsm.c | 3 ++- vendor/trezor-crypto | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/firmware/ethereum.c b/firmware/ethereum.c index a023ab3..857f2f9 100644 --- a/firmware/ethereum.c +++ b/firmware/ethereum.c @@ -257,7 +257,7 @@ static void layoutEthereumConfirmTx(const uint8_t *to, const uint8_t *value, uin strlcpy(_to3, "", sizeof(_to3)); } - layoutDialogSwipe(DIALOG_ICON_QUESTION, + layoutDialogSwipe(&bmp_icon_question, "Cancel", "Confirm", NULL, diff --git a/firmware/fsm.c b/firmware/fsm.c index e72e38a..c54439b 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -672,7 +672,8 @@ void fsm_msgEthereumGetAddress(EthereumGetAddress *msg) resp->address.size = 20; - if (!ecdsa_get_ethereum_pubkeyhash(node->public_key, resp->address.bytes)) return; + if (!hdnode_get_ethereum_pubkeyhash(node, resp->address.bytes)) + return; if (msg->has_show_display && msg->show_display) { char desc[16]; diff --git a/vendor/trezor-crypto b/vendor/trezor-crypto index 9a8df5a..f4ed553 160000 --- a/vendor/trezor-crypto +++ b/vendor/trezor-crypto @@ -1 +1 @@ -Subproject commit 9a8df5a4bb5de3b1ebaa28591c2ae0a5bb39348e +Subproject commit f4ed55377d67b48a94c219313faa3035186369cb From 05a73593f68e8424640b6c5fb9a4920d54cd0544 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Fri, 19 Aug 2016 22:25:01 +0200 Subject: [PATCH 25/28] No special case encoding for '\x0', cleanups The encoding for data '\x00' was tested here: http://testnet.etherscan.io/tx/0x05d6f97de3ecd33ad4059fa9bd342a10ef99d580a2d881b0c5a0c9e8c55ff975 --- firmware/ethereum.c | 59 +++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/firmware/ethereum.c b/firmware/ethereum.c index 857f2f9..358ccfc 100644 --- a/firmware/ethereum.c +++ b/firmware/ethereum.c @@ -3,6 +3,7 @@ * * Copyright (C) 2016 Alex Beregszaszi * Copyright (C) 2016 Pavol Rusnak + * Copyright (C) 2016 Jochen Hoenicke * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -30,7 +31,7 @@ #include "sha3.h" #include "util.h" -static bool signing = false; +static bool ethereum_signing = false; static uint32_t data_total, data_left; static EthereumTxRequest resp; static uint8_t hash[32], sig[64], privkey[32]; @@ -47,11 +48,7 @@ static inline void hash_data(const uint8_t *buf, size_t size) static void hash_rlp_length(uint32_t length, uint8_t firstbyte) { uint8_t buf[4]; - if (length == 1 && firstbyte == 0x00) { - // special case: null is encoded differently - buf[0] = 0x80; - hash_data(buf, 1); - } else if (length == 1 && firstbyte <= 0x7f) { + if (length == 1 && firstbyte <= 0x7f) { buf[0] = firstbyte; hash_data(buf, 1); } else if (length <= 55) { @@ -307,7 +304,7 @@ static bool ethereum_signing_check(EthereumSignTx *msg) void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) { - signing = true; + ethereum_signing = true; sha3_256_Init(&keccak_ctx); memset(&resp, 0, sizeof(EthereumTxRequest)); @@ -323,8 +320,11 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) ethereum_signing_abort(); return; } - if (msg->data_initial_chunk.size > msg->data_length) { - fsm_sendFailure(FailureType_Failure_Other, "Invalid size of initial chunk"); + /* Our encoding only supports transactions up to 2^24 bytes. To + * prevent exceeding the limit we use a stricter limit on data length. + */ + if (msg->data_length > 16000000) { + fsm_sendFailure(FailureType_Failure_Other, "Data length exceeds limit"); ethereum_signing_abort(); return; } @@ -332,6 +332,11 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) } else { data_total = 0; } + if (msg->data_initial_chunk.size > data_total) { + fsm_sendFailure(FailureType_Failure_Other, "Invalid size of initial chunk"); + ethereum_signing_abort(); + return; + } // safety checks if (!ethereum_signing_check(msg)) { @@ -357,7 +362,7 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) rlp_length += msg->has_gas_limit ? rlp_calculate_length(msg->gas_limit.size, msg->gas_limit.bytes[0]) : 1; rlp_length += msg->has_to ? rlp_calculate_length(msg->to.size, msg->to.bytes[0]) : 1; rlp_length += msg->has_value ? rlp_calculate_length(msg->value.size, msg->value.bytes[0]) : 1; - rlp_length += (msg->has_data_length && msg->has_data_initial_chunk) ? rlp_calculate_length(msg->data_length, msg->data_initial_chunk.bytes[0]) : 1; + rlp_length += rlp_calculate_length(data_total, msg->data_initial_chunk.bytes[0]); /* Stage 2: Store header fields */ hash_rlp_list_length(rlp_length); @@ -367,47 +372,43 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) if (msg->has_nonce) { hash_rlp_field(msg->nonce.bytes, msg->nonce.size); } else { - hash_rlp_length(1, 0); + hash_rlp_length(0, 0); } if (msg->has_gas_price) { hash_rlp_field(msg->gas_price.bytes, msg->gas_price.size); } else { - hash_rlp_length(1, 0); + hash_rlp_length(0, 0); } if (msg->has_gas_limit) { hash_rlp_field(msg->gas_limit.bytes, msg->gas_limit.size); } else { - hash_rlp_length(1, 0); + hash_rlp_length(0, 0); } if (msg->has_to) { hash_rlp_field(msg->to.bytes, msg->to.size); } else { - hash_rlp_length(1, 0); + hash_rlp_length(0, 0); } if (msg->has_value) { hash_rlp_field(msg->value.bytes, msg->value.size); } else { - hash_rlp_length(1, 0); + hash_rlp_length(0, 0); } - if (msg->has_data_length && msg->has_data_initial_chunk) { - hash_rlp_length(msg->data_length, msg->data_initial_chunk.bytes[0]); + hash_rlp_length(data_total, msg->data_initial_chunk.bytes[0]); + if (data_total > 1 || msg->data_initial_chunk.bytes[0] >= 0x80) { hash_data(msg->data_initial_chunk.bytes, msg->data_initial_chunk.size); - } else { - hash_rlp_length(1, 0); } - - layoutProgress("Signing", 200); + data_left = data_total - msg->data_initial_chunk.size; /* FIXME: probably this shouldn't be done here, but at a later stage */ memcpy(privkey, node->private_key, 32); - if (msg->has_data_length && msg->data_length > msg->data_initial_chunk.size) { - data_left = msg->data_length - msg->data_initial_chunk.size; + if (data_left > 0) { send_request_chunk(); } else { send_signature(); @@ -416,12 +417,18 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) void ethereum_signing_txack(EthereumTxAck *tx) { - if (!signing) { + if (!ethereum_signing) { fsm_sendFailure(FailureType_Failure_UnexpectedMessage, "Not in Signing mode"); layoutHome(); return; } + if (tx->data_chunk.size > data_left) { + fsm_sendFailure(FailureType_Failure_Other, "Too much data"); + ethereum_signing_abort(); + return; + } + if (data_left > 0 && (!tx->has_data_chunk || tx->data_chunk.size == 0)) { fsm_sendFailure(FailureType_Failure_Other, "Empty data chunk received"); ethereum_signing_abort(); @@ -441,9 +448,9 @@ void ethereum_signing_txack(EthereumTxAck *tx) void ethereum_signing_abort(void) { - if (signing) { + if (ethereum_signing) { memset(privkey, 0, sizeof(privkey)); layoutHome(); - signing = false; + ethereum_signing = false; } } From a37a2e36124e1ab428080bc974886da414922ddb Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Mon, 22 Aug 2016 21:58:57 +0200 Subject: [PATCH 26/28] Confirm data, streamlining code Set all size fields to 0, if fields was not given to avoid the conditions later. Display data and ask for confirmation. --- firmware/ethereum.c | 125 ++++++++++++++++++++++++++++++-------------- 1 file changed, 85 insertions(+), 40 deletions(-) diff --git a/firmware/ethereum.c b/firmware/ethereum.c index 358ccfc..67cf0e1 100644 --- a/firmware/ethereum.c +++ b/firmware/ethereum.c @@ -133,7 +133,10 @@ static int rlp_calculate_length(int length, uint8_t firstbyte) static void send_request_chunk(void) { - layoutProgress("Signing", 1000 - 800 * data_left / data_total); + int progress = 1000 - (data_total > 1000000 + ? data_left / (data_total/800) + : data_left * 800 / data_total); + layoutProgress("Signing", progress); resp.has_data_length = true; resp.data_length = data_left <= 1024 ? data_left : 1024; msg_write(MessageType_MessageType_EthereumTxRequest, &resp); @@ -171,7 +174,7 @@ static void send_signature(void) ethereum_signing_abort(); } -static void layoutEthereumConfirmTx(const uint8_t *to, const uint8_t *value, uint32_t value_len) +static void layoutEthereumConfirmTx(const uint8_t *to, uint32_t to_len, const uint8_t *value, uint32_t value_len) { bignum256 val; if (value && value_len <= 32) { @@ -242,14 +245,14 @@ static void layoutEthereumConfirmTx(const uint8_t *to, const uint8_t *value, uin static char _to2[17] = {0}; static char _to3[17] = {0}; - if (to) { + if (to_len) { strcpy(_to1, "to "); data2hex(to, 6, _to1 + 3); data2hex(to + 6, 7, _to2); data2hex(to + 13, 7, _to3); _to3[14] = '?'; _to3[15] = 0; } else { - strlcpy(_to1, "to no recipient?", sizeof(_to1)); + strlcpy(_to1, "to new contract?", sizeof(_to1)); strlcpy(_to2, "", sizeof(_to2)); strlcpy(_to3, "", sizeof(_to3)); } @@ -267,6 +270,45 @@ static void layoutEthereumConfirmTx(const uint8_t *to, const uint8_t *value, uin ); } +static void layoutEthereumData(const uint8_t *data, uint32_t len, uint32_t total_len) +{ + char hexdata[3][17]; + char summary[20]; + int i; + uint32_t printed = 0; + for (i = 0; i < 3; i++) { + uint32_t linelen = len - printed; + if (linelen > 8) + linelen = 8; + data2hex(data, linelen, hexdata[i]); + data += linelen; + printed += linelen; + } + + strcpy(summary, "... bytes"); + char *p = summary + 11; + uint32_t number = total_len; + while (number > 0) { + *p-- = '0' + number % 10; + number = number / 10; + } + char *summarystart = summary; + if (total_len == printed) + summarystart = summary + 4; + + layoutDialogSwipe(&bmp_icon_question, + "Cancel", + "Confirm", + NULL, + "Transaction data:", + hexdata[0], + hexdata[1], + hexdata[2], + summarystart, + NULL + ); +} + /* * RLP fields: * - nonce (0 .. 32) @@ -309,6 +351,25 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) memset(&resp, 0, sizeof(EthereumTxRequest)); + if (!msg->has_nonce || !msg->has_gas_price || !msg->has_gas_limit) { + fsm_sendFailure(FailureType_Failure_Other, "Required field missing"); + ethereum_signing_abort(); + return; + } + /* set fields to 0, to avoid conditions later */ + if (!msg->has_value) + msg->value.size = 0; + if (!msg->has_data_initial_chunk) + msg->data_initial_chunk.size = 0; + + if (!msg->has_to) { + msg->to.size = 0; + } else if (msg->to.size != 20) { + fsm_sendFailure(FailureType_Failure_Other, "Address has wrong length"); + ethereum_signing_abort(); + return; + } + if (msg->has_data_length) { if (msg->data_length == 0) { fsm_sendFailure(FailureType_Failure_Other, "Invalid data length provided"); @@ -345,23 +406,32 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) return; } - layoutEthereumConfirmTx(msg->has_to ? msg->to.bytes : NULL, msg->has_value ? msg->value.bytes : NULL, msg->has_value ? msg->value.size : 0); + layoutEthereumConfirmTx(msg->to.bytes, msg->to.size, msg->value.bytes, msg->value.size); if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { fsm_sendFailure(FailureType_Failure_ActionCancelled, "Signing cancelled by user"); ethereum_signing_abort(); return; } + if (data_total > 0) { + layoutEthereumData(msg->data_initial_chunk.bytes, msg->data_initial_chunk.size, data_total); + if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, "Signing cancelled by user"); + ethereum_signing_abort(); + return; + } + } + /* Stage 1: Calculate total RLP length */ uint32_t rlp_length = 0; layoutProgress("Signing", 0); - rlp_length += msg->has_nonce ? rlp_calculate_length(msg->nonce.size, msg->nonce.bytes[0]) : 1; - rlp_length += msg->has_gas_price ? rlp_calculate_length(msg->gas_price.size, msg->gas_price.bytes[0]) : 1; - rlp_length += msg->has_gas_limit ? rlp_calculate_length(msg->gas_limit.size, msg->gas_limit.bytes[0]) : 1; - rlp_length += msg->has_to ? rlp_calculate_length(msg->to.size, msg->to.bytes[0]) : 1; - rlp_length += msg->has_value ? rlp_calculate_length(msg->value.size, msg->value.bytes[0]) : 1; + rlp_length += rlp_calculate_length(msg->nonce.size, msg->nonce.bytes[0]); + rlp_length += rlp_calculate_length(msg->gas_price.size, msg->gas_price.bytes[0]); + rlp_length += rlp_calculate_length(msg->gas_limit.size, msg->gas_limit.bytes[0]); + rlp_length += rlp_calculate_length(msg->to.size, msg->to.bytes[0]); + rlp_length += rlp_calculate_length(msg->value.size, msg->value.bytes[0]); rlp_length += rlp_calculate_length(data_total, msg->data_initial_chunk.bytes[0]); /* Stage 2: Store header fields */ @@ -369,36 +439,11 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) layoutProgress("Signing", 100); - if (msg->has_nonce) { - hash_rlp_field(msg->nonce.bytes, msg->nonce.size); - } else { - hash_rlp_length(0, 0); - } - - if (msg->has_gas_price) { - hash_rlp_field(msg->gas_price.bytes, msg->gas_price.size); - } else { - hash_rlp_length(0, 0); - } - - if (msg->has_gas_limit) { - hash_rlp_field(msg->gas_limit.bytes, msg->gas_limit.size); - } else { - hash_rlp_length(0, 0); - } - - if (msg->has_to) { - hash_rlp_field(msg->to.bytes, msg->to.size); - } else { - hash_rlp_length(0, 0); - } - - if (msg->has_value) { - hash_rlp_field(msg->value.bytes, msg->value.size); - } else { - hash_rlp_length(0, 0); - } - + hash_rlp_field(msg->nonce.bytes, msg->nonce.size); + hash_rlp_field(msg->gas_price.bytes, msg->gas_price.size); + hash_rlp_field(msg->gas_limit.bytes, msg->gas_limit.size); + hash_rlp_field(msg->to.bytes, msg->to.size); + hash_rlp_field(msg->value.bytes, msg->value.size); hash_rlp_length(data_total, msg->data_initial_chunk.bytes[0]); if (data_total > 1 || msg->data_initial_chunk.bytes[0] >= 0x80) { hash_data(msg->data_initial_chunk.bytes, msg->data_initial_chunk.size); From 91dcead35eba26dbad74ccacbe3f99de904d0cc6 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Tue, 23 Aug 2016 18:44:46 +0200 Subject: [PATCH 27/28] Gas estimate screen, tweaked display of value --- firmware/ethereum.c | 210 ++++++++++++++++++++++++++----------------- vendor/trezor-crypto | 2 +- 2 files changed, 131 insertions(+), 81 deletions(-) diff --git a/firmware/ethereum.c b/firmware/ethereum.c index 67cf0e1..ca6b08b 100644 --- a/firmware/ethereum.c +++ b/firmware/ethereum.c @@ -174,73 +174,84 @@ static void send_signature(void) ethereum_signing_abort(); } -static void layoutEthereumConfirmTx(const uint8_t *to, uint32_t to_len, const uint8_t *value, uint32_t value_len) +/* Format a 256 bit number (amount in wei) into a human readable format + * using standard ethereum units. + * The buffer must be at least 25 bytes. + */ +static void ethereumFormatAmount(bignum256 *val, char buffer[25]) { - bignum256 val; - if (value && value_len <= 32) { - uint8_t pad_val[32]; - memset(pad_val, 0, sizeof(pad_val)); - memcpy(pad_val + (32 - value_len), value, value_len); - bn_read_be(pad_val, &val); - } else { - bn_zero(&val); - } + char value[25] = {0}; + char *value_ptr = value; + uint16_t num[26]; uint8_t last_used = 0; for (int i = 0; i < 26; i++) { - bn_divmod1000(&val, (uint32_t *)&(num[i])); + bn_divmod1000(val, (uint32_t *)&(num[i])); if (num[i] > 0) { last_used = i; } } - - static char _value[25] = {0}; - const char *value_ptr = _value; - + if (last_used < 3) { // value is smaller than 1e9 wei => show value in wei - _value[0] = '0' + (num[2] / 100) % 10; - _value[1] = '0' + (num[2] / 10) % 10; - _value[2] = '0' + (num[2]) % 10; - _value[3] = '0' + (num[1] / 100) % 10; - _value[4] = '0' + (num[1] / 10) % 10; - _value[5] = '0' + (num[1]) % 10; - _value[6] = '0' + (num[0] / 100) % 10; - _value[7] = '0' + (num[0] / 10) % 10; - _value[8] = '0' + (num[0]) % 10; - strlcpy(_value + 9, " wei", sizeof(_value) - 9); - } else if (last_used < 9) { - // value is bigger than 1e9 wei and smaller than 1e9 ETH => show value in ETH - _value[0] = '0' + (num[8] / 100) % 10; - _value[1] = '0' + (num[8] / 10) % 10; - _value[2] = '0' + (num[8]) % 10; - _value[3] = '0' + (num[7] / 100) % 10; - _value[4] = '0' + (num[7] / 10) % 10; - _value[5] = '0' + (num[7]) % 10; - _value[6] = '0' + (num[6] / 100) % 10; - _value[7] = '0' + (num[6] / 10) % 10; - _value[8] = '0' + (num[6]) % 10; - _value[9] = '.'; - _value[10] = '0' + (num[5] / 100) % 10; - _value[11] = '0' + (num[5] / 10) % 10; - _value[12] = '0' + (num[5]) % 10; - _value[13] = '0' + (num[4] / 100) % 10; - _value[14] = '0' + (num[4] / 10) % 10; - _value[15] = '0' + (num[4]) % 10; - _value[16] = '0' + (num[3] / 100) % 10; - _value[17] = '0' + (num[3] / 10) % 10; - _value[18] = '0' + (num[3]) % 10; - strlcpy(_value + 19, " ETH", sizeof(_value) - 19); + for (int i = last_used; i >= 0; i--) { + *value_ptr++ = '0' + (num[i] / 100) % 10; + *value_ptr++ = '0' + (num[i] / 10) % 10; + *value_ptr++ = '0' + (num[i]) % 10; + } + strcpy(value_ptr, " Wei"); + // value is at most 9 + 4 + 1 characters long + } else if (last_used < 10) { + // value is bigger than 1e9 wei and smaller than 1e12 ETH => show value in ETH + int i = last_used; + if (i < 6) + i = 6; + int end = i - 4; + while (i >= end) { + *value_ptr++ = '0' + (num[i] / 100) % 10; + *value_ptr++ = '0' + (num[i] / 10) % 10; + *value_ptr++ = '0' + (num[i]) % 10; + if (i == 6) + *value_ptr++ = '.'; + i--; + } + while (value_ptr[-1] == '0') // remove trailing zeros + value_ptr--; + // remove trailing dot. + if (value_ptr[-1] == '.') + value_ptr--; + strcpy(value_ptr, " ETH"); + // value is at most 16 + 4 + 1 characters long } else { // value is bigger than 1e9 ETH => won't fit on display (probably won't happen unless you are Vitalik) - strlcpy(_value, "more than a billion ETH", sizeof(_value)); + strlcpy(value, "trillions of ETH", sizeof(value)); } - value_ptr = _value; - while (*value_ptr == '0' && *(value_ptr + 1) >= '0' && *(value_ptr + 1) <= '9') { // skip leading zeroes + // skip leading zeroes + value_ptr = value; + while (*value_ptr == '0' && *(value_ptr + 1) >= '0' && *(value_ptr + 1) <= '9') { value_ptr++; } + // copy to destination buffer + strcpy(buffer, value_ptr); +} + +static void layoutEthereumConfirmTx(const uint8_t *to, uint32_t to_len, const uint8_t *value, uint32_t value_len) +{ + bignum256 val; + uint8_t pad_val[32]; + memset(pad_val, 0, sizeof(pad_val)); + memcpy(pad_val + (32 - value_len), value, value_len); + bn_read_be(pad_val, &val); + + char amount[25]; + if (bn_is_zero(&val)) { + strcpy(amount, "message"); + } else { + ethereumFormatAmount(&val, amount); + } + static char _to1[17] = {0}; static char _to2[17] = {0}; static char _to3[17] = {0}; @@ -261,8 +272,8 @@ static void layoutEthereumConfirmTx(const uint8_t *to, uint32_t to_len, const ui "Cancel", "Confirm", NULL, - "Really send", - value_ptr, + "Send", + amount, _to1, _to2, _to3, @@ -309,6 +320,49 @@ static void layoutEthereumData(const uint8_t *data, uint32_t len, uint32_t total ); } +static void layoutEthereumFee(const uint8_t *value, uint32_t value_len, + const uint8_t *gas_price, uint32_t gas_price_len, + const uint8_t *gas_limit, uint32_t gas_limit_len) +{ + bignum256 val, gas; + uint8_t pad_val[32]; + char tx_value[25]; + char gas_value[25]; + + memset(pad_val, 0, sizeof(pad_val)); + memcpy(pad_val + (32 - gas_price_len), gas_price, gas_price_len); + bn_read_be(pad_val, &val); + + memset(pad_val, 0, sizeof(pad_val)); + memcpy(pad_val + (32 - gas_limit_len), gas_limit, gas_limit_len); + bn_read_be(pad_val, &gas); + bn_multiply(&val, &gas, &secp256k1.prime); + + ethereumFormatAmount(&gas, gas_value); + + memset(pad_val, 0, sizeof(pad_val)); + memcpy(pad_val + (32 - value_len), value, value_len); + bn_read_be(pad_val, &val); + + if (bn_is_zero(&val)) { + strcpy(tx_value, "message"); + } else { + ethereumFormatAmount(&val, tx_value); + } + + layoutDialogSwipe(&bmp_icon_question, + "Cancel", + "Confirm", + NULL, + "Really send", + tx_value, + "paying up to", + gas_value, + "for gas?", + NULL + ); +} + /* * RLP fields: * - nonce (0 .. 32) @@ -321,23 +375,22 @@ static void layoutEthereumData(const uint8_t *data, uint32_t len, uint32_t total static bool ethereum_signing_check(EthereumSignTx *msg) { - // determine if address == 0 - bool address_zero = msg->has_to; - if (address_zero) { - for (size_t i = 0; i < msg->to.size; i++) { - if (msg->to.bytes[i] > 0) { - address_zero = false; - break; - } - } - } - - // sending value to address 0 - if (address_zero && msg->has_value && msg->value.size) { + if (!msg->has_nonce || !msg->has_gas_price || !msg->has_gas_limit) { return false; } - // sending transaction to address 0 without a data field - if (address_zero && (!msg->has_data_length || msg->data_length == 0)) { + + if (msg->to.size != 20 && msg->to.size != 0) { + /* Address has wrong length */ + return false; + } + + // sending transaction to address 0 (contract creation) without a data field + if (msg->to.size == 0 && (!msg->has_data_length || msg->data_length == 0)) { + return false; + } + + if (msg->gas_price.size + msg->gas_limit.size > 30) { + // sanity check that fee doesn't overflow return false; } @@ -350,25 +403,13 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) sha3_256_Init(&keccak_ctx); memset(&resp, 0, sizeof(EthereumTxRequest)); - - if (!msg->has_nonce || !msg->has_gas_price || !msg->has_gas_limit) { - fsm_sendFailure(FailureType_Failure_Other, "Required field missing"); - ethereum_signing_abort(); - return; - } /* set fields to 0, to avoid conditions later */ if (!msg->has_value) msg->value.size = 0; if (!msg->has_data_initial_chunk) msg->data_initial_chunk.size = 0; - - if (!msg->has_to) { + if (!msg->has_to) msg->to.size = 0; - } else if (msg->to.size != 20) { - fsm_sendFailure(FailureType_Failure_Other, "Address has wrong length"); - ethereum_signing_abort(); - return; - } if (msg->has_data_length) { if (msg->data_length == 0) { @@ -422,6 +463,15 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) } } + layoutEthereumFee(msg->value.bytes, msg->value.size, + msg->gas_price.bytes, msg->gas_price.size, + msg->gas_limit.bytes, msg->gas_limit.size); + if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, "Signing cancelled by user"); + ethereum_signing_abort(); + return; + } + /* Stage 1: Calculate total RLP length */ uint32_t rlp_length = 0; diff --git a/vendor/trezor-crypto b/vendor/trezor-crypto index f4ed553..7ce6b8b 160000 --- a/vendor/trezor-crypto +++ b/vendor/trezor-crypto @@ -1 +1 @@ -Subproject commit f4ed55377d67b48a94c219313faa3035186369cb +Subproject commit 7ce6b8b14761ab770ff896f7d9c7a76d434e3df2 From bc550139423385d73da2ee48e283f14eb9303884 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Thu, 25 Aug 2016 12:59:40 +0200 Subject: [PATCH 28/28] Simplified one-byte encodings --- firmware/ethereum.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/firmware/ethereum.c b/firmware/ethereum.c index ca6b08b..7073871 100644 --- a/firmware/ethereum.c +++ b/firmware/ethereum.c @@ -49,8 +49,7 @@ static void hash_rlp_length(uint32_t length, uint8_t firstbyte) { uint8_t buf[4]; if (length == 1 && firstbyte <= 0x7f) { - buf[0] = firstbyte; - hash_data(buf, 1); + /* empty length header */ } else if (length <= 55) { buf[0] = 0x80 + length; hash_data(buf, 1); @@ -105,9 +104,7 @@ static void hash_rlp_list_length(uint32_t length) static void hash_rlp_field(const uint8_t *buf, size_t size) { hash_rlp_length(size, buf[0]); - if (size > 1 || buf[0] >= 0x80) { - hash_data(buf, size); - } + hash_data(buf, size); } /* @@ -495,12 +492,9 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) hash_rlp_field(msg->to.bytes, msg->to.size); hash_rlp_field(msg->value.bytes, msg->value.size); hash_rlp_length(data_total, msg->data_initial_chunk.bytes[0]); - if (data_total > 1 || msg->data_initial_chunk.bytes[0] >= 0x80) { - hash_data(msg->data_initial_chunk.bytes, msg->data_initial_chunk.size); - } + hash_data(msg->data_initial_chunk.bytes, msg->data_initial_chunk.size); data_left = data_total - msg->data_initial_chunk.size; - /* FIXME: probably this shouldn't be done here, but at a later stage */ memcpy(privkey, node->private_key, 32); if (data_left > 0) {