mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-19 12:58:13 +00:00
commit
4c379e36ec
@ -17,6 +17,7 @@ OBJS += recovery.o
|
|||||||
OBJS += reset.o
|
OBJS += reset.o
|
||||||
OBJS += signing.o
|
OBJS += signing.o
|
||||||
OBJS += crypto.o
|
OBJS += crypto.o
|
||||||
|
OBJS += ethereum.o
|
||||||
|
|
||||||
OBJS += debug.o
|
OBJS += debug.o
|
||||||
|
|
||||||
@ -34,6 +35,7 @@ OBJS += ../vendor/trezor-crypto/base58.o
|
|||||||
|
|
||||||
OBJS += ../vendor/trezor-crypto/ripemd160.o
|
OBJS += ../vendor/trezor-crypto/ripemd160.o
|
||||||
OBJS += ../vendor/trezor-crypto/sha2.o
|
OBJS += ../vendor/trezor-crypto/sha2.o
|
||||||
|
OBJS += ../vendor/trezor-crypto/sha3.o
|
||||||
|
|
||||||
OBJS += ../vendor/trezor-crypto/aescrypt.o
|
OBJS += ../vendor/trezor-crypto/aescrypt.o
|
||||||
OBJS += ../vendor/trezor-crypto/aeskey.o
|
OBJS += ../vendor/trezor-crypto/aeskey.o
|
||||||
@ -59,3 +61,4 @@ CFLAGS += -DDEBUG_LOG=0
|
|||||||
CFLAGS += -DSCM_REVISION='"$(shell git rev-parse HEAD | sed 's:\(..\):\\x\1:g')"'
|
CFLAGS += -DSCM_REVISION='"$(shell git rev-parse HEAD | sed 's:\(..\):\\x\1:g')"'
|
||||||
CFLAGS += -DED25519_CUSTOMRANDOM=1
|
CFLAGS += -DED25519_CUSTOMRANDOM=1
|
||||||
CFLAGS += -DED25519_CUSTOMHASH=1
|
CFLAGS += -DED25519_CUSTOMHASH=1
|
||||||
|
CFLAGS += -DUSE_ETHEREUM=1
|
||||||
|
545
firmware/ethereum.c
Normal file
545
firmware/ethereum.c
Normal file
@ -0,0 +1,545 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 Alex Beregszaszi <alex@rtfs.hu>
|
||||||
|
* Copyright (C) 2016 Pavol Rusnak <stick@satoshilabs.com>
|
||||||
|
* Copyright (C) 2016 Jochen Hoenicke <hoenicke@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
static bool ethereum_signing = false;
|
||||||
|
static uint32_t data_total, data_left;
|
||||||
|
static EthereumTxRequest resp;
|
||||||
|
static uint8_t hash[32], sig[64], privkey[32];
|
||||||
|
struct SHA3_CTX keccak_ctx;
|
||||||
|
|
||||||
|
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 <= 0x7f) {
|
||||||
|
/* empty length header */
|
||||||
|
} else if (length <= 55) {
|
||||||
|
buf[0] = 0x80 + length;
|
||||||
|
hash_data(buf, 1);
|
||||||
|
} else if (length <= 0xff) {
|
||||||
|
buf[0] = 0xb7 + 1;
|
||||||
|
buf[1] = length;
|
||||||
|
hash_data(buf, 2);
|
||||||
|
} else if (length <= 0xffff) {
|
||||||
|
buf[0] = 0xb7 + 2;
|
||||||
|
buf[1] = length >> 8;
|
||||||
|
buf[2] = length & 0xff;
|
||||||
|
hash_data(buf, 3);
|
||||||
|
} else {
|
||||||
|
buf[0] = 0xb7 + 3;
|
||||||
|
buf[1] = length >> 16;
|
||||||
|
buf[2] = length >> 8;
|
||||||
|
buf[3] = length & 0xff;
|
||||||
|
hash_data(buf, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Push an RLP encoded list length to the hash buffer.
|
||||||
|
*/
|
||||||
|
static void hash_rlp_list_length(uint32_t length)
|
||||||
|
{
|
||||||
|
uint8_t buf[4];
|
||||||
|
if (length <= 55) {
|
||||||
|
buf[0] = 0xc0 + length;
|
||||||
|
hash_data(buf, 1);
|
||||||
|
} else if (length <= 0xff) {
|
||||||
|
buf[0] = 0xf7 + 1;
|
||||||
|
buf[1] = length;
|
||||||
|
hash_data(buf, 2);
|
||||||
|
} else if (length <= 0xffff) {
|
||||||
|
buf[0] = 0xf7 + 2;
|
||||||
|
buf[1] = length >> 8;
|
||||||
|
buf[2] = length & 0xff;
|
||||||
|
hash_data(buf, 3);
|
||||||
|
} else {
|
||||||
|
buf[0] = 0xf7 + 3;
|
||||||
|
buf[1] = length >> 16;
|
||||||
|
buf[2] = length >> 8;
|
||||||
|
buf[3] = length & 0xff;
|
||||||
|
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]);
|
||||||
|
hash_data(buf, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 1 + length;
|
||||||
|
} else if (length <= 0xff) {
|
||||||
|
return 2 + length;
|
||||||
|
} else if (length <= 0xffff) {
|
||||||
|
return 3 + length;
|
||||||
|
} else {
|
||||||
|
return 4 + length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void send_request_chunk(void)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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])
|
||||||
|
{
|
||||||
|
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]));
|
||||||
|
if (num[i] > 0) {
|
||||||
|
last_used = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_used < 3) {
|
||||||
|
// value is smaller than 1e9 wei => show value in wei
|
||||||
|
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, "trillions of ETH", sizeof(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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};
|
||||||
|
|
||||||
|
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 new contract?", sizeof(_to1));
|
||||||
|
strlcpy(_to2, "", sizeof(_to2));
|
||||||
|
strlcpy(_to3, "", sizeof(_to3));
|
||||||
|
}
|
||||||
|
|
||||||
|
layoutDialogSwipe(&bmp_icon_question,
|
||||||
|
"Cancel",
|
||||||
|
"Confirm",
|
||||||
|
NULL,
|
||||||
|
"Send",
|
||||||
|
amount,
|
||||||
|
_to1,
|
||||||
|
_to2,
|
||||||
|
_to3,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
* - gas_price (0 .. 32)
|
||||||
|
* - gas_limit (0 .. 32)
|
||||||
|
* - to (0, 20)
|
||||||
|
* - value (0 .. 32)
|
||||||
|
* - data (0 ..)
|
||||||
|
*/
|
||||||
|
|
||||||
|
static bool ethereum_signing_check(EthereumSignTx *msg)
|
||||||
|
{
|
||||||
|
if (!msg->has_nonce || !msg->has_gas_price || !msg->has_gas_limit) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node)
|
||||||
|
{
|
||||||
|
ethereum_signing = true;
|
||||||
|
sha3_256_Init(&keccak_ctx);
|
||||||
|
|
||||||
|
memset(&resp, 0, sizeof(EthereumTxRequest));
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
data_total = msg->data_length;
|
||||||
|
} 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)) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_ActionCancelled, "Signing aborted (safety check failed)");
|
||||||
|
ethereum_signing_abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
layoutProgress("Signing", 0);
|
||||||
|
|
||||||
|
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 */
|
||||||
|
hash_rlp_list_length(rlp_length);
|
||||||
|
|
||||||
|
layoutProgress("Signing", 100);
|
||||||
|
|
||||||
|
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]);
|
||||||
|
hash_data(msg->data_initial_chunk.bytes, msg->data_initial_chunk.size);
|
||||||
|
data_left = data_total - msg->data_initial_chunk.size;
|
||||||
|
|
||||||
|
memcpy(privkey, node->private_key, 32);
|
||||||
|
|
||||||
|
if (data_left > 0) {
|
||||||
|
send_request_chunk();
|
||||||
|
} else {
|
||||||
|
send_signature();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ethereum_signing_txack(EthereumTxAck *tx)
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_data(tx->data_chunk.bytes, tx->data_chunk.size);
|
||||||
|
|
||||||
|
data_left -= tx->data_chunk.size;
|
||||||
|
|
||||||
|
if (data_left > 0) {
|
||||||
|
send_request_chunk();
|
||||||
|
} else {
|
||||||
|
send_signature();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ethereum_signing_abort(void)
|
||||||
|
{
|
||||||
|
if (ethereum_signing) {
|
||||||
|
memset(privkey, 0, sizeof(privkey));
|
||||||
|
layoutHome();
|
||||||
|
ethereum_signing = false;
|
||||||
|
}
|
||||||
|
}
|
32
firmware/ethereum.h
Normal file
32
firmware/ethereum.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 Alex Beregszaszi <alex@rtfs.hu>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ETHEREUM_H__
|
||||||
|
#define __ETHEREUM_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#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
|
@ -47,6 +47,7 @@
|
|||||||
#include "curves.h"
|
#include "curves.h"
|
||||||
#include "secp256k1.h"
|
#include "secp256k1.h"
|
||||||
#include <libopencm3/stm32/flash.h>
|
#include <libopencm3/stm32/flash.h>
|
||||||
|
#include "ethereum.h"
|
||||||
|
|
||||||
// message methods
|
// message methods
|
||||||
|
|
||||||
@ -419,14 +420,6 @@ void fsm_msgSignTx(SignTx *msg)
|
|||||||
signing_init(msg->inputs_count, msg->outputs_count, coin, node, msg->version, msg->lock_time);
|
signing_init(msg->inputs_count, msg->outputs_count, coin, node, msg->version, msg->lock_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
void fsm_msgCancel(Cancel *msg)
|
|
||||||
{
|
|
||||||
(void)msg;
|
|
||||||
recovery_abort();
|
|
||||||
signing_abort();
|
|
||||||
fsm_sendFailure(FailureType_Failure_ActionCancelled, "Aborted");
|
|
||||||
}
|
|
||||||
|
|
||||||
void fsm_msgTxAck(TxAck *msg)
|
void fsm_msgTxAck(TxAck *msg)
|
||||||
{
|
{
|
||||||
if (msg->has_tx) {
|
if (msg->has_tx) {
|
||||||
@ -436,6 +429,38 @@ void fsm_msgTxAck(TxAck *msg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
ethereum_signing_txack(msg);
|
||||||
|
}
|
||||||
|
|
||||||
void fsm_msgCipherKeyValue(CipherKeyValue *msg)
|
void fsm_msgCipherKeyValue(CipherKeyValue *msg)
|
||||||
{
|
{
|
||||||
if (!storage_isInitialized()) {
|
if (!storage_isInitialized()) {
|
||||||
@ -628,6 +653,47 @@ void fsm_msgGetAddress(GetAddress *msg)
|
|||||||
layoutHome();
|
layoutHome();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void fsm_msgEthereumGetAddress(EthereumGetAddress *msg)
|
||||||
|
{
|
||||||
|
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 (!hdnode_get_ethereum_pubkeyhash(node, resp->address.bytes))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (msg->has_show_display && msg->show_display) {
|
||||||
|
char desc[16];
|
||||||
|
strlcpy(desc, "Address:", sizeof(desc));
|
||||||
|
|
||||||
|
char address[41];
|
||||||
|
data2hex(resp->address.bytes, 20, address);
|
||||||
|
|
||||||
|
layoutAddress(address, desc);
|
||||||
|
if (!protectButton(ButtonRequestType_ButtonRequest_Address, true)) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_ActionCancelled, "Show address cancelled");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
msg_write(MessageType_MessageType_EthereumAddress, resp);
|
||||||
|
layoutHome();
|
||||||
|
}
|
||||||
|
|
||||||
void fsm_msgEntropyAck(EntropyAck *msg)
|
void fsm_msgEntropyAck(EntropyAck *msg)
|
||||||
{
|
{
|
||||||
if (msg->has_entropy) {
|
if (msg->has_entropy) {
|
||||||
|
@ -59,6 +59,9 @@ void fsm_msgEstimateTxSize(EstimateTxSize *msg);
|
|||||||
void fsm_msgRecoveryDevice(RecoveryDevice *msg);
|
void fsm_msgRecoveryDevice(RecoveryDevice *msg);
|
||||||
void fsm_msgWordAck(WordAck *msg);
|
void fsm_msgWordAck(WordAck *msg);
|
||||||
void fsm_msgSetU2FCounter(SetU2FCounter *msg);
|
void fsm_msgSetU2FCounter(SetU2FCounter *msg);
|
||||||
|
void fsm_msgEthereumGetAddress(EthereumGetAddress *msg);
|
||||||
|
void fsm_msgEthereumSignTx(EthereumSignTx *msg);
|
||||||
|
void fsm_msgEthereumTxAck(EthereumTxAck *msg);
|
||||||
|
|
||||||
// debug message functions
|
// debug message functions
|
||||||
#if DEBUG_LINK
|
#if DEBUG_LINK
|
||||||
|
@ -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) {
|
if (desc) {
|
||||||
oledDrawString(68, 0 * 9, desc);
|
oledDrawString(68, 0 * 9, desc);
|
||||||
|
@ -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_RecoveryDevice, RecoveryDevice_fields, (void (*)(void *))fsm_msgRecoveryDevice},
|
||||||
{'n', 'i', MessageType_MessageType_WordAck, WordAck_fields, (void (*)(void *))fsm_msgWordAck},
|
{'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_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
|
// out messages
|
||||||
{'n', 'o', MessageType_MessageType_Success, Success_fields, 0},
|
{'n', 'o', MessageType_MessageType_Success, Success_fields, 0},
|
||||||
{'n', 'o', MessageType_MessageType_Failure, Failure_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_PassphraseRequest, PassphraseRequest_fields, 0},
|
||||||
{'n', 'o', MessageType_MessageType_TxSize, TxSize_fields, 0},
|
{'n', 'o', MessageType_MessageType_TxSize, TxSize_fields, 0},
|
||||||
{'n', 'o', MessageType_MessageType_WordRequest, WordRequest_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
|
#if DEBUG_LINK
|
||||||
// debug in messages
|
// debug in messages
|
||||||
// {'d', 'i', MessageType_MessageType_DebugLinkDecision, DebugLinkDecision_fields, (void (*)(void *))fsm_msgDebugLinkDecision},
|
// {'d', 'i', MessageType_MessageType_DebugLinkDecision, DebugLinkDecision_fields, (void (*)(void *))fsm_msgDebugLinkDecision},
|
||||||
|
2
vendor/trezor-crypto
vendored
2
vendor/trezor-crypto
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 9a8df5a4bb5de3b1ebaa28591c2ae0a5bb39348e
|
Subproject commit 7ce6b8b14761ab770ff896f7d9c7a76d434e3df2
|
Loading…
Reference in New Issue
Block a user