feat(legacy): support external Ethereum definitions

pull/2914/head
Martin Novák 1 year ago committed by matejcik
parent c2c0900c5d
commit 0f07d74063

@ -0,0 +1 @@
Signed Ethereum network and token definitions from host

@ -1,7 +1,8 @@
coin_info.[ch]
nem_mosaics.[ch]
ethereum_networks.h
ethereum_tokens.[ch]
ethereum_definitions_constants.h
ethereum_networks.c
ethereum_tokens.c
u2f_knownapps.h
bl_data.h

@ -61,6 +61,8 @@ OBJS += crypto.o
ifneq ($(BITCOIN_ONLY),1)
OBJS += u2f.o
OBJS += ethereum.o
OBJS += ethereum_definitions.o
OBJS += ethereum_networks.o
OBJS += ethereum_tokens.o
OBJS += nem2.o
OBJS += nem_mosaics.o
@ -140,6 +142,7 @@ OBJS += protob/messages-management.pb.o
ifneq ($(BITCOIN_ONLY),1)
OBJS += ../vendor/trezor-crypto/cash_addr.o
OBJS += protob/messages-ethereum.pb.o
OBJS += protob/messages-ethereum-definitions.pb.o
OBJS += protob/messages-nem.pb.o
OBJS += protob/messages-stellar.pb.o
endif

@ -51,6 +51,7 @@ static uint32_t data_total, data_left;
static EthereumTxRequest msg_tx_request;
static CONFIDENTIAL uint8_t privkey[32];
static uint64_t chain_id;
static const char *chain_suffix;
static bool eip1559;
struct SHA3_CTX keccak_ctx = {0};
@ -64,6 +65,7 @@ struct signing_params {
bool pubkeyhash_set;
uint8_t pubkeyhash[20];
uint64_t chain_id;
const char *chain_suffix;
uint32_t data_length;
uint32_t data_initial_chunk_size;
@ -72,7 +74,7 @@ struct signing_params {
bool has_to;
const char *to;
const TokenType *token;
const EthereumTokenInfo *token;
uint32_t value_size;
const uint8_t *value_bytes;
@ -313,30 +315,28 @@ static void send_signature(void) {
* using standard ethereum units.
* The buffer must be at least 25 bytes.
*/
static void ethereumFormatAmount(const bignum256 *amnt, const TokenType *token,
char *buf, int buflen) {
static void ethereumFormatAmount(const bignum256 *amnt,
const EthereumTokenInfo *token, char *buf,
int buflen) {
bignum256 bn1e9 = {0};
bn_read_uint32(1000000000, &bn1e9);
const char *suffix = NULL;
char suffix[50] = {' ', 0};
int decimals = 18;
if (token == UnknownToken) {
strlcpy(buf, "Unknown token value", buflen);
return;
} else if (token != NULL) {
suffix = token->ticker;
if (token) {
strlcpy(suffix + 1, token->symbol, sizeof(suffix) - 1);
decimals = token->decimals;
} else if (bn_is_less(amnt, &bn1e9)) {
suffix = " Wei";
strlcpy(suffix + 1, "Wei", sizeof(suffix) - 1);
decimals = 0;
} else {
ASSIGN_ETHEREUM_SUFFIX(suffix, chain_id);
strlcpy(suffix + 1, chain_suffix, sizeof(suffix) - 1);
}
bn_format(amnt, NULL, suffix, decimals, 0, false, ',', buf, buflen);
}
static void layoutEthereumConfirmTx(const uint8_t *to, uint32_t to_len,
const uint8_t *value, uint32_t value_len,
const TokenType *token) {
const EthereumTokenInfo *token) {
bignum256 val = {0};
uint8_t pad_val[32] = {0};
memzero(pad_val, sizeof(pad_val));
@ -508,6 +508,7 @@ static bool ethereum_signing_init_common(struct signing_params *params) {
return false;
}
chain_id = params->chain_id;
chain_suffix = params->chain_suffix;
if (params->data_length > 0) {
if (params->data_initial_chunk_size == 0) {
@ -550,7 +551,8 @@ static bool ethereum_signing_init_common(struct signing_params *params) {
return true;
}
static void ethereum_signing_handle_erc20(struct signing_params *params) {
static void ethereum_signing_handle_erc20(struct signing_params *params,
const EthereumTokenInfo *token) {
if (params->has_to && ethereum_parse(params->to, params->pubkeyhash)) {
params->pubkeyhash_set = true;
} else {
@ -564,7 +566,7 @@ static void ethereum_signing_handle_erc20(struct signing_params *params) {
memcmp(params->data_initial_chunk_bytes,
"\xa9\x05\x9c\xbb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
16) == 0) {
params->token = tokenByChainAddress(chain_id, params->pubkeyhash);
params->token = token;
}
}
@ -594,10 +596,11 @@ static bool ethereum_signing_confirm_common(
return true;
}
void ethereum_signing_init(const EthereumSignTx *msg, const HDNode *node) {
void ethereum_signing_init(const EthereumSignTx *msg, const HDNode *node,
const EthereumDefinitionsDecoded *defs) {
struct signing_params params = {
.chain_id = msg->chain_id,
.chain_suffix = defs->network->symbol,
.data_length = msg->data_length,
.data_initial_chunk_size = msg->data_initial_chunk.size,
.data_initial_chunk_bytes = msg->data_initial_chunk.bytes,
@ -634,7 +637,7 @@ void ethereum_signing_init(const EthereumSignTx *msg, const HDNode *node) {
}
}
ethereum_signing_handle_erc20(&params);
ethereum_signing_handle_erc20(&params, defs->token);
if (!ethereum_signing_confirm_common(&params)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
@ -700,9 +703,11 @@ void ethereum_signing_init(const EthereumSignTx *msg, const HDNode *node) {
}
void ethereum_signing_init_eip1559(const EthereumSignTxEIP1559 *msg,
const HDNode *node) {
const HDNode *node,
const EthereumDefinitionsDecoded *defs) {
struct signing_params params = {
.chain_id = msg->chain_id,
.chain_suffix = defs->network->symbol,
.data_length = msg->data_length,
.data_initial_chunk_size = msg->data_initial_chunk.size,
@ -729,7 +734,7 @@ void ethereum_signing_init_eip1559(const EthereumSignTxEIP1559 *msg,
return;
}
ethereum_signing_handle_erc20(&params);
ethereum_signing_handle_erc20(&params, defs->token);
if (!ethereum_signing_confirm_common(&params)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
@ -1040,9 +1045,23 @@ bool ethereum_parse(const char *address, uint8_t pubkeyhash[20]) {
return true;
}
static bool check_ethereum_slip44_unhardened(
uint32_t slip44, const EthereumNetworkInfo *network) {
if (is_unknown_network(network)) {
// Allow Ethereum or testnet paths for unknown networks.
return slip44 == 60 || slip44 == 1;
} else if (network->slip44 != 60 && network->slip44 != 1) {
// Allow cross-signing with Ethereum unless it's testnet.
return (slip44 == network->slip44 || slip44 == 60);
} else {
return (slip44 == network->slip44);
}
}
static bool ethereum_path_check_bip44(uint32_t address_n_count,
const uint32_t *address_n,
bool pubkey_export, uint64_t chain) {
bool pubkey_export,
const EthereumNetworkInfo *network) {
bool valid = (address_n_count >= 3);
valid = valid && (address_n[0] == (PATH_HARDENED | 44));
valid = valid && (address_n[1] & PATH_HARDENED);
@ -1050,20 +1069,7 @@ static bool ethereum_path_check_bip44(uint32_t address_n_count,
valid = valid && ((address_n[2] & PATH_UNHARDEN_MASK) <= PATH_MAX_ACCOUNT);
uint32_t path_slip44 = address_n[1] & PATH_UNHARDEN_MASK;
if (chain == CHAIN_ID_UNKNOWN) {
valid = valid && (is_ethereum_slip44(path_slip44));
} else {
uint32_t chain_slip44 = ethereum_slip44_by_chain_id(chain);
if (chain_slip44 == SLIP44_UNKNOWN) {
// Allow Ethereum or testnet paths for unknown networks.
valid = valid && (path_slip44 == 60 || path_slip44 == 1);
} else if (chain_slip44 != 60 && chain_slip44 != 1) {
// Allow cross-signing with Ethereum unless it's testnet.
valid = valid && (path_slip44 == chain_slip44 || path_slip44 == 60);
} else {
valid = valid && (path_slip44 == chain_slip44);
}
}
valid = valid && check_ethereum_slip44_unhardened(path_slip44, network);
if (pubkey_export) {
// m/44'/coin_type'/account'/*
@ -1101,7 +1107,7 @@ static bool ethereum_path_check_bip44(uint32_t address_n_count,
static bool ethereum_path_check_casa45(uint32_t address_n_count,
const uint32_t *address_n,
uint64_t chain) {
const EthereumNetworkInfo *network) {
bool valid = (address_n_count == 5);
valid = valid && (address_n[0] == (PATH_HARDENED | 45));
valid = valid && (address_n[1] < PATH_HARDENED);
@ -1110,35 +1116,23 @@ static bool ethereum_path_check_casa45(uint32_t address_n_count,
valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX);
uint32_t path_slip44 = address_n[1];
if (chain == CHAIN_ID_UNKNOWN) {
valid = valid && (is_ethereum_slip44(path_slip44));
} else {
uint32_t chain_slip44 = ethereum_slip44_by_chain_id(chain);
if (chain_slip44 == SLIP44_UNKNOWN) {
// Allow Ethereum or testnet paths for unknown networks.
valid = valid && (path_slip44 == 60 || path_slip44 == 1);
} else if (chain_slip44 != 60 && chain_slip44 != 1) {
// Allow cross-signing with Ethereum unless it's testnet.
valid = valid && (path_slip44 == chain_slip44 || path_slip44 == 60);
} else {
valid = valid && (path_slip44 == chain_slip44);
}
}
valid = valid && check_ethereum_slip44_unhardened(path_slip44, network);
return valid;
}
bool ethereum_path_check(uint32_t address_n_count, const uint32_t *address_n,
bool pubkey_export, uint64_t chain) {
bool pubkey_export,
const EthereumNetworkInfo *network) {
if (address_n_count == 0) {
return false;
}
if (address_n[0] == (PATH_HARDENED | 44)) {
return ethereum_path_check_bip44(address_n_count, address_n, pubkey_export,
chain);
network);
}
if (address_n[0] == (PATH_HARDENED | 45)) {
return ethereum_path_check_casa45(address_n_count, address_n, chain);
return ethereum_path_check_casa45(address_n_count, address_n, network);
}
return false;
}

@ -23,13 +23,16 @@
#include <stdbool.h>
#include <stdint.h>
#include "bip32.h"
#include "ethereum_definitions.h"
#include "messages-ethereum.pb.h"
#define CHAIN_ID_UNKNOWN UINT64_MAX
void ethereum_signing_init(const EthereumSignTx *msg, const HDNode *node);
void ethereum_signing_init(const EthereumSignTx *msg, const HDNode *node,
const EthereumDefinitionsDecoded *defs);
void ethereum_signing_init_eip1559(const EthereumSignTxEIP1559 *msg,
const HDNode *node);
const HDNode *node,
const EthereumDefinitionsDecoded *defs);
void ethereum_signing_abort(void);
void ethereum_signing_txack(const EthereumTxAck *msg);
@ -42,5 +45,6 @@ void ethereum_typed_hash_sign(const EthereumSignTypedHash *msg,
bool ethereum_parse(const char *address, uint8_t pubkeyhash[20]);
bool ethereum_path_check(uint32_t address_n_count, const uint32_t *address_n,
bool pubkey_export, uint64_t chain);
bool pubkey_export,
const EthereumNetworkInfo *network);
#endif

@ -0,0 +1,313 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (C) 2022 Martin Novak <martin.novak@satoshilabs.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 <stdbool.h>
#include <string.h>
#include "crypto.h"
#include "ethereum.h"
#include "ethereum_definitions.h"
#include "ethereum_definitions_constants.h"
#include "ethereum_networks.h"
#include "ethereum_tokens.h"
#include "fsm.h"
#include "gettext.h"
#include "memzero.h"
#include "messages.h"
#include "pb.h"
#include "pb_decode.h"
#include "trezor.h" // because of the "VERSTR" macro used in "fsm_sendFailureDebug" function
#include "util.h"
typedef pb_byte_t proof_entry[SHA256_DIGEST_LENGTH];
struct EncodedDefinition {
// prefix
pb_byte_t format_version[FORMAT_VERSION_LENGTH];
uint8_t definition_type;
uint32_t data_version;
uint16_t payload_length;
// payload
const pb_byte_t *payload;
// suffix
uint8_t proof_length;
const proof_entry *proof;
const ed25519_signature *signed_root_hash;
};
static bool parse_encoded_definition(struct EncodedDefinition *const result,
const pb_size_t size,
const pb_byte_t *bytes) {
// format version + definition type + data version + payload length + payload
// (at least 1B) + proof length + signed Merkle tree root hash
if (size < (FORMAT_VERSION_LENGTH + 1 + 4 + 2 + 1 + 1 +
MERKLE_TREE_SIGNED_ROOT_SIZE)) {
return false;
}
const pb_byte_t *cursor = bytes;
memcpy(result->format_version, cursor, FORMAT_VERSION_LENGTH);
cursor += FORMAT_VERSION_LENGTH;
result->definition_type = *cursor;
cursor += 1;
result->data_version = *(uint32_t *)cursor;
cursor += 4;
result->payload_length = *(uint16_t *)cursor;
cursor += 2;
result->payload = cursor;
cursor += result->payload_length;
if (size <= cursor - bytes) {
return false;
}
result->proof_length = *cursor;
cursor += 1;
// check the whole size of incoming bytes array
if (size != (cursor - bytes) + result->proof_length * sizeof(proof_entry) +
MERKLE_TREE_SIGNED_ROOT_SIZE) {
return false;
}
result->proof = (proof_entry *)cursor;
cursor += result->proof_length * sizeof(proof_entry);
result->signed_root_hash = (ed25519_signature *)cursor;
return true;
}
static bool decode_definition(const pb_size_t size, const pb_byte_t *bytes,
const EthereumDefinitionType expected_type,
void *definition) {
// parse received definition
static struct EncodedDefinition parsed_def;
const char *error_str = _("Invalid Ethereum definition");
memzero(&parsed_def, sizeof(parsed_def));
if (!parse_encoded_definition(&parsed_def, size, bytes)) {
goto err;
}
// check definition fields
if (memcmp(FORMAT_VERSION, parsed_def.format_version,
FORMAT_VERSION_LENGTH)) {
goto err;
}
if (expected_type != parsed_def.definition_type) {
error_str = _("Definition type mismatch");
goto err;
}
if (MIN_DATA_VERSION > parsed_def.data_version) {
error_str = _("Definition is outdated");
goto err;
}
// compute Merkle tree root hash from proof
uint8_t hash[SHA256_DIGEST_LENGTH] = {0};
SHA256_CTX context = {0};
sha256_Init(&context);
// leaf hash = sha256('\x00' + leaf data)
sha256_Update(&context, (uint8_t[]){0}, 1);
// signed data is everything from start of `bytes` to the end of `payload`
const pb_byte_t *payload_end = parsed_def.payload + parsed_def.payload_length;
size_t signed_data_size = payload_end - bytes;
sha256_Update(&context, bytes, signed_data_size);
sha256_Final(&context, hash);
const uint8_t *min, *max;
for (uint8_t i = 0; i < parsed_def.proof_length; i++) {
sha256_Init(&context);
// node hash = sha256('\x01' + min(hash, next_proof) + max(hash,
// next_proof))
sha256_Update(&context, (uint8_t[]){1}, 1);
if (memcmp(hash, parsed_def.proof[i], SHA256_DIGEST_LENGTH) <= 0) {
min = hash;
max = parsed_def.proof[i];
} else {
min = parsed_def.proof[i];
max = hash;
}
sha256_Update(&context, min, SHA256_DIGEST_LENGTH);
sha256_Update(&context, max, SHA256_DIGEST_LENGTH);
sha256_Final(&context, hash);
}
// and verify its signature
if (ed25519_sign_open(hash, SHA256_DIGEST_LENGTH, DEFINITIONS_PUBLIC_KEY,
*(parsed_def.signed_root_hash)) != 0
#if DEBUG_LINK
&&
ed25519_sign_open(hash, SHA256_DIGEST_LENGTH, DEFINITIONS_DEV_PUBLIC_KEY,
*(parsed_def.signed_root_hash)) != 0
#endif
) {
// invalid signature
error_str = _("Invalid definition signature");
goto err;
}
// decode message
const pb_msgdesc_t *fields = (expected_type == EthereumDefinitionType_NETWORK
? EthereumNetworkInfo_fields
: EthereumTokenInfo_fields);
pb_istream_t stream =
pb_istream_from_buffer(parsed_def.payload, parsed_def.payload_length);
bool status = pb_decode(&stream, fields, definition);
if (status) {
return true;
}
// fallthrough to error handling in case of decoding failure
err:
memzero(&parsed_def, sizeof(parsed_def));
fsm_sendFailure(FailureType_Failure_DataError, error_str);
return false;
}
static const EthereumNetworkInfo *get_network(
const EncodedNetwork *encoded_network, const uint64_t chain_id,
const uint32_t slip44) {
static EthereumNetworkInfo decoded_network;
const EthereumNetworkInfo *network = &UNKNOWN_NETWORK;
// try to get built-in definition
if (chain_id != CHAIN_ID_UNKNOWN) {
network = ethereum_get_network_by_chain_id(chain_id);
} else if (slip44 != SLIP44_UNKNOWN) {
network = ethereum_get_network_by_slip44(slip44);
} else {
// if both chain_id and slip44 is unspecified, we do not have anything to
// match to the encoded definition, so just short-circuit here
return &UNKNOWN_NETWORK;
}
// if we found built-in definition, or if there's no data to decode, we are
// done
if (!is_unknown_network(network) || encoded_network == NULL) {
return network;
}
// if we still do not have any network definition try to decode received data
memzero(&decoded_network, sizeof(decoded_network));
if (!decode_definition(encoded_network->size, encoded_network->bytes,
EthereumDefinitionType_NETWORK, &decoded_network)) {
// error already sent by decode_definition
return NULL;
}
if (chain_id != CHAIN_ID_UNKNOWN && decoded_network.chain_id != chain_id) {
fsm_sendFailure(FailureType_Failure_DataError,
_("Network definition mismatch"));
return NULL;
}
if (slip44 != SLIP44_UNKNOWN && decoded_network.slip44 != slip44) {
fsm_sendFailure(FailureType_Failure_DataError,
_("Network definition mismatch"));
return NULL;
}
return &decoded_network;
}
static const EthereumTokenInfo *get_token(const EncodedToken *encoded_token,
const uint64_t chain_id,
const char *address) {
static EthereumTokenInfo decoded_token;
// if we do not know the chain_id, we cannot get the token
if (chain_id == CHAIN_ID_UNKNOWN) {
return &UNKNOWN_TOKEN;
}
// convert address string to bytes
EthereumTokenInfo_address_t address_bytes;
bool address_parsed = address && ethereum_parse(address, address_bytes.bytes);
if (!address_parsed) {
// without a valid address, we cannot get the token
return &UNKNOWN_TOKEN;
}
// try to get built-in definition
const EthereumTokenInfo *token =
ethereum_token_by_address(chain_id, address_bytes.bytes);
if (!is_unknown_token(token) || encoded_token == NULL) {
// if we found one, or if there's no data to decode, we are done
return token;
}
// try to decode received definition
memzero(&decoded_token, sizeof(decoded_token));
if (!decode_definition(encoded_token->size, encoded_token->bytes,
EthereumDefinitionType_TOKEN, &decoded_token)) {
// error already sent by decode_definition
return NULL;
}
if (decoded_token.chain_id != chain_id ||
memcmp(decoded_token.address.bytes, address_bytes.bytes,
sizeof(decoded_token.address.bytes))) {
// receiving a mismatched token is not an error (we expect being able to get
// multiple token definitions in the future, for multiple networks)
// but we must not accept the mismatched definition
memzero(&decoded_token, sizeof(decoded_token));
return &UNKNOWN_TOKEN;
}
return &decoded_token;
}
const EthereumDefinitionsDecoded *ethereum_get_definitions(
const EncodedNetwork *encoded_network, const EncodedToken *encoded_token,
const uint64_t chain_id, const uint32_t slip44, const char *token_address) {
static EthereumDefinitionsDecoded defs;
memzero(&defs, sizeof(defs));
const EthereumNetworkInfo *network =
get_network(encoded_network, chain_id, slip44);
if (network == NULL) {
// error while decoding, failure was sent by get_network
return NULL;
}
defs.network = network;
if (!is_unknown_network(network) && token_address != NULL) {
const EthereumTokenInfo *token =
get_token(encoded_token, network->chain_id, token_address);
if (token == NULL) {
// error while decoding, failure was sent by get_token
return NULL;
}
defs.token = token;
} else {
defs.token = &UNKNOWN_TOKEN;
}
return &defs;
}

@ -0,0 +1,37 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (C) 2022 Martin Novak <martin.novak@satoshilabs.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/>.
*/
#ifndef __ETHEREUM_DEFINITIONS_H__
#define __ETHEREUM_DEFINITIONS_H__
#include "messages-ethereum-definitions.pb.h"
typedef EthereumDefinitions_encoded_network_t EncodedNetwork;
typedef EthereumDefinitions_encoded_token_t EncodedToken;
typedef struct {
const EthereumNetworkInfo *network;
const EthereumTokenInfo *token;
} EthereumDefinitionsDecoded;
const EthereumDefinitionsDecoded *ethereum_get_definitions(
const EncodedNetwork *encoded_network, const EncodedToken *encoded_token,
const uint64_t chain_id, const uint32_t slip44, const char *token_address);
#endif

@ -0,0 +1,26 @@
// This file is automatically generated from ethereum_definitions_constants.h.mako
// DO NOT EDIT
#ifndef __ETHEREUM_DEFINITIONS_CONSTANTS_H__
#define __ETHEREUM_DEFINITIONS_CONSTANTS_H__
#include <stdint.h>
#include "crypto.h"
#include "messages-ethereum-definitions.pb.h"
#include "pb.h"
static const uint8_t DEFINITIONS_PUBLIC_KEY[] =
" "; // TODO: update
#if DEBUG_LINK
static const uint8_t DEFINITIONS_DEV_PUBLIC_KEY[] =
"\xdb\x99\x5f\xe2\x51\x69\xd1\x41\xca\xb9\xbb\xba\x92\xba\xa0\x1f\x9f\x2e"
"\x1e\xce\x7d\xf4\xcb\x2a\xc0\x51\x90\xf3\x7f\xcc\x1f\x9d";
#endif
#define MIN_DATA_VERSION ${ethereum_defs_timestamp}
#define FORMAT_VERSION_LENGTH 5
#define FORMAT_VERSION (const pb_byte_t *)"trzd1"
#define MERKLE_TREE_SIGNED_ROOT_SIZE sizeof(ed25519_signature)
#endif

@ -0,0 +1,48 @@
<% networks = list(supported_on("trezor1", eth)) %>\
// This file is automatically generated from ethereum_networks.c.mako
// DO NOT EDIT
#include "ethereum.h"
#include "ethereum_networks.h"
#define NETWORKS_COUNT ${len(networks)}
static const EthereumNetworkInfo networks[NETWORKS_COUNT] = {
% for n in networks:
{
.chain_id = ${n.chain_id},
.slip44 = ${n.slip44},
.symbol = ${c_str(n.shortcut)}, /* ${n.name} */
.name = "",
},
% endfor
};
const EthereumNetworkInfo UNKNOWN_NETWORK = {
.chain_id = CHAIN_ID_UNKNOWN,
.slip44 = SLIP44_UNKNOWN,
.symbol = "UNKN",
.name = "",
};
const EthereumNetworkInfo *ethereum_get_network_by_chain_id(uint64_t chain_id) {
for (size_t i = 0; i < NETWORKS_COUNT; i++) {
if (networks[i].chain_id == chain_id) {
return &networks[i];
}
}
return &UNKNOWN_NETWORK;
}
const EthereumNetworkInfo *ethereum_get_network_by_slip44(uint32_t slip44) {
for (size_t i = 0; i < NETWORKS_COUNT; i++) {
if (networks[i].slip44 == slip44) {
return &networks[i];
}
}
return &UNKNOWN_NETWORK;
}
bool is_unknown_network(const EthereumNetworkInfo *network) {
return network->chain_id == CHAIN_ID_UNKNOWN;
}

@ -0,0 +1,34 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (C) 2018 Pavol Rusnak <stick@satoshilabs.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/>.
*/
#ifndef __ETHEREUM_NETWORKS_H__
#define __ETHEREUM_NETWORKS_H__
#include <stdbool.h>
#include <stdint.h>
#include "messages-ethereum-definitions.pb.h"
extern const EthereumNetworkInfo UNKNOWN_NETWORK;
#define SLIP44_UNKNOWN UINT32_MAX
const EthereumNetworkInfo *ethereum_get_network_by_chain_id(uint64_t chain_id);
const EthereumNetworkInfo *ethereum_get_network_by_slip44(uint32_t slip44);
bool is_unknown_network(const EthereumNetworkInfo *network);
#endif

@ -1,61 +0,0 @@
<%
BKSL = "\\"
networks = list(supported_on("trezor1", eth))
max_chain_id_length = 0
max_slip44_length = 0
max_suffix_length = 0
for n in networks:
max_chain_id_length = max(len(str(n.chain_id)), max_chain_id_length)
max_slip44_length = max(len(str(n.slip44)), max_slip44_length)
max_suffix_length = max(len(n.shortcut), max_suffix_length)
def align_chain_id(n):
return "{:>{w}}".format(n.chain_id, w=max_chain_id_length)
def align_slip44(n):
return "{:>{w}}".format(n.slip44, w=max_slip44_length)
def align_suffix(n):
cstr = c_str(" " + n.shortcut) + ";"
# we add two quotes, a space and a semicolon. hence +4 chars
return "{:<{w}}".format(cstr, w=max_suffix_length + 4)
%>\
// This file is automatically generated from ethereum_networks.h.mako
// DO NOT EDIT
#ifndef __ETHEREUM_NETWORKS_H__
#define __ETHEREUM_NETWORKS_H__
#define SLIP44_UNKNOWN UINT32_MAX
#define ASSIGN_ETHEREUM_SUFFIX(suffix, chain_id) ${BKSL}
switch (chain_id) { ${BKSL}
% for n in networks:
case ${align_chain_id(n)}: suffix = ${align_suffix(n)} break; /* ${n.name} */ ${BKSL}
% endfor
default: suffix = " UNKN"; break; /* unknown chain */ ${BKSL}
}
static bool is_ethereum_slip44(uint32_t slip44) {
switch (slip44) {
% for slip44 in sorted(set(n.slip44 for n in networks)):
case ${slip44}:
% endfor
return true;
default:
return false;
}
}
static int32_t ethereum_slip44_by_chain_id(uint64_t chain_id) {
switch (chain_id) {
% for n in networks:
case ${align_chain_id(n)}: return ${align_slip44(n)}; /* ${n.name} */
% endfor
default: return SLIP44_UNKNOWN; /* unknown chain */
}
}
#endif

@ -2,24 +2,50 @@
// DO NOT EDIT
#include <string.h>
#include "ethereum.h"
#include "ethereum_tokens.h"
const TokenType tokens[TOKENS_COUNT] = {
% for t in supported_on("trezor1", erc20):
{${"{:>2}".format(t.chain_id)}, ${c_str(t.address_bytes)}, " ${ascii(t.symbol)}", ${t.decimals}}, // ${t.chain} / ${t.name}
<% erc20_list = list(supported_on("trezor1", erc20)) %>\
#define TOKENS_COUNT ${len(erc20_list)}
static const EthereumTokenInfo tokens[TOKENS_COUNT] = {
% for t in sorted(erc20_list, key=lambda token: (token.chain_id, token.name)):
{
.symbol = "${ascii(t.symbol)}",
.decimals = ${t.decimals},
.address = {
.size = 20,
.bytes = ${c_str(t.address_bytes)}
},
.chain_id = ${t.chain_id},
## .name = "${t.name}"
.name = "",
},
% endfor
};
static const TokenType _UnknownToken = { 0, "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", " UNKN", 0 };
const TokenType *UnknownToken = &_UnknownToken;
const EthereumTokenInfo UNKNOWN_TOKEN = {
.symbol = "Wei UNKN",
.decimals = 0,
.address = {
.size = 20,
.bytes = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff",
},
.chain_id = CHAIN_ID_UNKNOWN,
.name = "",
};
const TokenType *tokenByChainAddress(uint64_t chain_id, const uint8_t *address)
const EthereumTokenInfo *ethereum_token_by_address(uint64_t chain_id, const uint8_t *address)
{
if (!address) return 0;
for (int i = 0; i < TOKENS_COUNT; i++) {
if (chain_id == tokens[i].chain_id && memcmp(address, tokens[i].address, 20) == 0) {
return &(tokens[i]);
}
}
return UnknownToken;
if (!address) return 0;
for (int i = 0; i < TOKENS_COUNT; i++) {
if (chain_id == tokens[i].chain_id && memcmp(address, tokens[i].address.bytes, sizeof(tokens[i].address.bytes)) == 0) {
return &(tokens[i]);
}
}
return &UNKNOWN_TOKEN;
}
bool is_unknown_token(const EthereumTokenInfo *token) {
return token->chain_id == CHAIN_ID_UNKNOWN;
}

@ -0,0 +1,32 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (C) 2023 Trezor Company s.r.o.
*
* 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_TOKENS_H__
#define __ETHEREUM_TOKENS_H__
#include <stdint.h>
#include "ethereum_definitions.h"
extern const EthereumTokenInfo UNKNOWN_TOKEN;
const EthereumTokenInfo *ethereum_token_by_address(uint64_t chain_id,
const uint8_t *address);
bool is_unknown_token(const EthereumTokenInfo *token);
#endif

@ -1,25 +0,0 @@
// This file is automatically generated from ethereum_tokens.h.mako
// DO NOT EDIT
#ifndef __ETHEREUM_TOKENS_H__
#define __ETHEREUM_TOKENS_H__
#include <stdint.h>
<% erc20_list = list(supported_on("trezor1", erc20)) %>\
#define TOKENS_COUNT ${len(erc20_list)}
typedef struct {
uint64_t chain_id;
const char * const address;
const char * const ticker;
int decimals;
} TokenType;
extern const TokenType tokens[TOKENS_COUNT];
extern const TokenType *UnknownToken;
const TokenType *tokenByChainAddress(uint64_t chain_id, const uint8_t *address);
#endif

@ -56,6 +56,8 @@
#if !BITCOIN_ONLY
#include "ethereum.h"
#include "ethereum_definitions.h"
#include "ethereum_networks.h"
#include "nem.h"
#include "nem2.h"
#include "stellar.h"

@ -19,9 +19,8 @@
static bool fsm_ethereumCheckPath(uint32_t address_n_count,
const uint32_t *address_n, bool pubkey_export,
uint64_t chain_id) {
if (ethereum_path_check(address_n_count, address_n, pubkey_export,
chain_id)) {
const EthereumNetworkInfo *network) {
if (ethereum_path_check(address_n_count, address_n, pubkey_export, network)) {
return true;
}
@ -33,6 +32,38 @@ static bool fsm_ethereumCheckPath(uint32_t address_n_count,
return fsm_layoutPathWarning();
}
static const EthereumDefinitionsDecoded *get_definitions(
bool has_definitions, const EthereumDefinitions *definitions,
uint64_t chain_id, const char *to) {
const EncodedNetwork *encoded_network = NULL;
const EncodedToken *encoded_token = NULL;
if (has_definitions && definitions) {
if (definitions->has_encoded_network) {
encoded_network = &definitions->encoded_network;
}
if (definitions->has_encoded_token) {
encoded_token = &definitions->encoded_token;
}
}
return ethereum_get_definitions(encoded_network, encoded_token, chain_id,
SLIP44_UNKNOWN, to);
}
static const EthereumNetworkInfo *get_network_definition_only(
bool has_encoded_network, const EncodedNetwork *encoded_network,
const uint32_t slip44) {
const EncodedNetwork *en = NULL;
if (has_encoded_network) {
en = encoded_network;
}
const EthereumDefinitionsDecoded *defs =
ethereum_get_definitions(en, NULL, CHAIN_ID_UNKNOWN, slip44, NULL);
return defs ? defs->network : NULL;
}
void fsm_msgEthereumGetPublicKey(const EthereumGetPublicKey *msg) {
RESP_INIT(EthereumPublicKey);
@ -44,12 +75,6 @@ void fsm_msgEthereumGetPublicKey(const EthereumGetPublicKey *msg) {
const CoinInfo *coin = fsm_getCoin(true, "Bitcoin");
if (!coin) return;
if (!fsm_ethereumCheckPath(msg->address_n_count, msg->address_n, true,
CHAIN_ID_UNKNOWN)) {
layoutHome();
return;
}
const char *curve = coin->curve_name;
uint32_t fingerprint;
HDNode *node = fsm_getDerivedNode(curve, msg->address_n, msg->address_n_count,
@ -93,8 +118,12 @@ void fsm_msgEthereumSignTx(const EthereumSignTx *msg) {
CHECK_PIN
if (!fsm_ethereumCheckPath(msg->address_n_count, msg->address_n, false,
msg->chain_id)) {
const EthereumDefinitionsDecoded *defs =
get_definitions(msg->has_definitions, &msg->definitions, msg->chain_id,
msg->has_to ? msg->to : NULL);
if (!defs || !fsm_ethereumCheckPath(msg->address_n_count, msg->address_n,
false, defs->network)) {
layoutHome();
return;
}
@ -103,7 +132,7 @@ void fsm_msgEthereumSignTx(const EthereumSignTx *msg) {
msg->address_n_count, NULL);
if (!node) return;
ethereum_signing_init(msg, node);
ethereum_signing_init(msg, node, defs);
}
void fsm_msgEthereumSignTxEIP1559(const EthereumSignTxEIP1559 *msg) {
@ -111,8 +140,12 @@ void fsm_msgEthereumSignTxEIP1559(const EthereumSignTxEIP1559 *msg) {
CHECK_PIN
if (!fsm_ethereumCheckPath(msg->address_n_count, msg->address_n, false,
msg->chain_id)) {
const EthereumDefinitionsDecoded *defs =
get_definitions(msg->has_definitions, &msg->definitions, msg->chain_id,
msg->has_to ? msg->to : NULL);
if (!defs || !fsm_ethereumCheckPath(msg->address_n_count, msg->address_n,
false, defs->network)) {
layoutHome();
return;
}
@ -121,7 +154,7 @@ void fsm_msgEthereumSignTxEIP1559(const EthereumSignTxEIP1559 *msg) {
msg->address_n_count, NULL);
if (!node) return;
ethereum_signing_init_eip1559(msg, node);
ethereum_signing_init_eip1559(msg, node, defs);
}
void fsm_msgEthereumTxAck(const EthereumTxAck *msg) {
@ -137,8 +170,16 @@ void fsm_msgEthereumGetAddress(const EthereumGetAddress *msg) {
CHECK_PIN
if (!fsm_ethereumCheckPath(msg->address_n_count, msg->address_n, false,
CHAIN_ID_UNKNOWN)) {
uint32_t slip44 = (msg->address_n_count > 1)
? (msg->address_n[1] & PATH_UNHARDEN_MASK)
: SLIP44_UNKNOWN;
const EthereumNetworkInfo *network = get_network_definition_only(
msg->has_encoded_network, (const EncodedNetwork *)&msg->encoded_network,
slip44);
if (!network || !fsm_ethereumCheckPath(msg->address_n_count, msg->address_n,
false, network)) {
layoutHome();
return;
}
@ -153,9 +194,6 @@ void fsm_msgEthereumGetAddress(const EthereumGetAddress *msg) {
layoutHome();
return;
}
uint32_t slip44 =
(msg->address_n_count > 1) ? (msg->address_n[1] & PATH_UNHARDEN_MASK) : 0;
bool rskip60 = false;
uint64_t chain_id = 0;
// constants from trezor-common/defs/ethereum/networks.json
@ -195,8 +233,16 @@ void fsm_msgEthereumSignMessage(const EthereumSignMessage *msg) {
CHECK_PIN
if (!fsm_ethereumCheckPath(msg->address_n_count, msg->address_n, false,
CHAIN_ID_UNKNOWN)) {
uint32_t slip44 = (msg->address_n_count > 1)
? (msg->address_n[1] & PATH_UNHARDEN_MASK)
: SLIP44_UNKNOWN;
const EthereumNetworkInfo *network = get_network_definition_only(
msg->has_encoded_network, (const EncodedNetwork *)&msg->encoded_network,
slip44);
if (!network || !fsm_ethereumCheckPath(msg->address_n_count, msg->address_n,
false, network)) {
layoutHome();
return;
}
@ -282,8 +328,16 @@ void fsm_msgEthereumSignTypedHash(const EthereumSignTypedHash *msg) {
return;
}
if (!fsm_ethereumCheckPath(msg->address_n_count, msg->address_n, false,
CHAIN_ID_UNKNOWN)) {
uint32_t slip44 = (msg->address_n_count > 1)
? (msg->address_n[1] & PATH_UNHARDEN_MASK)
: SLIP44_UNKNOWN;
const EthereumNetworkInfo *network = get_network_definition_only(
msg->has_encoded_network, (const EncodedNetwork *)&msg->encoded_network,
slip44);
if (!network || !fsm_ethereumCheckPath(msg->address_n_count, msg->address_n,
false, network)) {
layoutHome();
return;
}

@ -14,7 +14,8 @@ SKIPPED_MESSAGES += Ethereum NEM Stellar
endif
PROTO_NAMES = messages messages-bitcoin messages-common messages-crypto messages-debug \
messages-ethereum messages-management messages-nem messages-stellar
messages-ethereum messages-ethereum-definitions messages-management messages-nem \
messages-stellar
PROTO_OPTIONS = $(PROTO_NAMES:=.options)
PROTO_COMPILED = $(PROTO_NAMES:=.pb)

@ -0,0 +1,9 @@
EthereumNetworkInfo.name max_size:256
EthereumNetworkInfo.symbol max_size:256
EthereumTokenInfo.name max_size:256
EthereumTokenInfo.address max_size:20
EthereumTokenInfo.symbol max_size:256
EthereumDefinitions.encoded_network max_size:1024
EthereumDefinitions.encoded_token max_size:1024

@ -0,0 +1 @@
../../vendor/trezor-common/protob/messages-ethereum-definitions.proto

@ -26,6 +26,7 @@ EthereumTxAck.data_chunk max_size:1024
EthereumSignMessage.address_n max_count:8
EthereumSignMessage.message max_size:1024
EthereumSignMessage.encoded_network max_size:1024
EthereumVerifyMessage.address max_size:43
EthereumVerifyMessage.signature max_size:65
@ -37,11 +38,14 @@ EthereumMessageSignature.signature max_size:65
EthereumSignTypedHash.address_n max_count:8
EthereumSignTypedHash.domain_separator_hash max_size:32
EthereumSignTypedHash.message_hash max_size:32
EthereumSignTypedHash.encoded_network max_size:1024
EthereumTypedDataSignature.address max_size:43
EthereumTypedDataSignature.signature max_size:65
EthereumGetAddress.address_n max_count:8
EthereumGetAddress.encoded_network max_size:1024
EthereumGetPublicKey.address_n max_count:8
EthereumAddress._old_address max_size:20

Loading…
Cancel
Save