mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-07-06 14:52:33 +00:00
feat(legacy): support external Ethereum definitions
This commit is contained in:
parent
c2c0900c5d
commit
0f07d74063
1
legacy/firmware/.changelog.d/15.added
Normal file
1
legacy/firmware/.changelog.d/15.added
Normal file
@ -0,0 +1 @@
|
|||||||
|
Signed Ethereum network and token definitions from host
|
5
legacy/firmware/.gitignore
vendored
5
legacy/firmware/.gitignore
vendored
@ -1,7 +1,8 @@
|
|||||||
coin_info.[ch]
|
coin_info.[ch]
|
||||||
nem_mosaics.[ch]
|
nem_mosaics.[ch]
|
||||||
ethereum_networks.h
|
ethereum_definitions_constants.h
|
||||||
ethereum_tokens.[ch]
|
ethereum_networks.c
|
||||||
|
ethereum_tokens.c
|
||||||
u2f_knownapps.h
|
u2f_knownapps.h
|
||||||
|
|
||||||
bl_data.h
|
bl_data.h
|
||||||
|
@ -61,6 +61,8 @@ OBJS += crypto.o
|
|||||||
ifneq ($(BITCOIN_ONLY),1)
|
ifneq ($(BITCOIN_ONLY),1)
|
||||||
OBJS += u2f.o
|
OBJS += u2f.o
|
||||||
OBJS += ethereum.o
|
OBJS += ethereum.o
|
||||||
|
OBJS += ethereum_definitions.o
|
||||||
|
OBJS += ethereum_networks.o
|
||||||
OBJS += ethereum_tokens.o
|
OBJS += ethereum_tokens.o
|
||||||
OBJS += nem2.o
|
OBJS += nem2.o
|
||||||
OBJS += nem_mosaics.o
|
OBJS += nem_mosaics.o
|
||||||
@ -140,6 +142,7 @@ OBJS += protob/messages-management.pb.o
|
|||||||
ifneq ($(BITCOIN_ONLY),1)
|
ifneq ($(BITCOIN_ONLY),1)
|
||||||
OBJS += ../vendor/trezor-crypto/cash_addr.o
|
OBJS += ../vendor/trezor-crypto/cash_addr.o
|
||||||
OBJS += protob/messages-ethereum.pb.o
|
OBJS += protob/messages-ethereum.pb.o
|
||||||
|
OBJS += protob/messages-ethereum-definitions.pb.o
|
||||||
OBJS += protob/messages-nem.pb.o
|
OBJS += protob/messages-nem.pb.o
|
||||||
OBJS += protob/messages-stellar.pb.o
|
OBJS += protob/messages-stellar.pb.o
|
||||||
endif
|
endif
|
||||||
|
@ -51,6 +51,7 @@ static uint32_t data_total, data_left;
|
|||||||
static EthereumTxRequest msg_tx_request;
|
static EthereumTxRequest msg_tx_request;
|
||||||
static CONFIDENTIAL uint8_t privkey[32];
|
static CONFIDENTIAL uint8_t privkey[32];
|
||||||
static uint64_t chain_id;
|
static uint64_t chain_id;
|
||||||
|
static const char *chain_suffix;
|
||||||
static bool eip1559;
|
static bool eip1559;
|
||||||
struct SHA3_CTX keccak_ctx = {0};
|
struct SHA3_CTX keccak_ctx = {0};
|
||||||
|
|
||||||
@ -64,6 +65,7 @@ struct signing_params {
|
|||||||
bool pubkeyhash_set;
|
bool pubkeyhash_set;
|
||||||
uint8_t pubkeyhash[20];
|
uint8_t pubkeyhash[20];
|
||||||
uint64_t chain_id;
|
uint64_t chain_id;
|
||||||
|
const char *chain_suffix;
|
||||||
|
|
||||||
uint32_t data_length;
|
uint32_t data_length;
|
||||||
uint32_t data_initial_chunk_size;
|
uint32_t data_initial_chunk_size;
|
||||||
@ -72,7 +74,7 @@ struct signing_params {
|
|||||||
bool has_to;
|
bool has_to;
|
||||||
const char *to;
|
const char *to;
|
||||||
|
|
||||||
const TokenType *token;
|
const EthereumTokenInfo *token;
|
||||||
|
|
||||||
uint32_t value_size;
|
uint32_t value_size;
|
||||||
const uint8_t *value_bytes;
|
const uint8_t *value_bytes;
|
||||||
@ -313,30 +315,28 @@ static void send_signature(void) {
|
|||||||
* using standard ethereum units.
|
* using standard ethereum units.
|
||||||
* The buffer must be at least 25 bytes.
|
* The buffer must be at least 25 bytes.
|
||||||
*/
|
*/
|
||||||
static void ethereumFormatAmount(const bignum256 *amnt, const TokenType *token,
|
static void ethereumFormatAmount(const bignum256 *amnt,
|
||||||
char *buf, int buflen) {
|
const EthereumTokenInfo *token, char *buf,
|
||||||
|
int buflen) {
|
||||||
bignum256 bn1e9 = {0};
|
bignum256 bn1e9 = {0};
|
||||||
bn_read_uint32(1000000000, &bn1e9);
|
bn_read_uint32(1000000000, &bn1e9);
|
||||||
const char *suffix = NULL;
|
char suffix[50] = {' ', 0};
|
||||||
int decimals = 18;
|
int decimals = 18;
|
||||||
if (token == UnknownToken) {
|
if (token) {
|
||||||
strlcpy(buf, "Unknown token value", buflen);
|
strlcpy(suffix + 1, token->symbol, sizeof(suffix) - 1);
|
||||||
return;
|
|
||||||
} else if (token != NULL) {
|
|
||||||
suffix = token->ticker;
|
|
||||||
decimals = token->decimals;
|
decimals = token->decimals;
|
||||||
} else if (bn_is_less(amnt, &bn1e9)) {
|
} else if (bn_is_less(amnt, &bn1e9)) {
|
||||||
suffix = " Wei";
|
strlcpy(suffix + 1, "Wei", sizeof(suffix) - 1);
|
||||||
decimals = 0;
|
decimals = 0;
|
||||||
} else {
|
} 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);
|
bn_format(amnt, NULL, suffix, decimals, 0, false, ',', buf, buflen);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void layoutEthereumConfirmTx(const uint8_t *to, uint32_t to_len,
|
static void layoutEthereumConfirmTx(const uint8_t *to, uint32_t to_len,
|
||||||
const uint8_t *value, uint32_t value_len,
|
const uint8_t *value, uint32_t value_len,
|
||||||
const TokenType *token) {
|
const EthereumTokenInfo *token) {
|
||||||
bignum256 val = {0};
|
bignum256 val = {0};
|
||||||
uint8_t pad_val[32] = {0};
|
uint8_t pad_val[32] = {0};
|
||||||
memzero(pad_val, sizeof(pad_val));
|
memzero(pad_val, sizeof(pad_val));
|
||||||
@ -508,6 +508,7 @@ static bool ethereum_signing_init_common(struct signing_params *params) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
chain_id = params->chain_id;
|
chain_id = params->chain_id;
|
||||||
|
chain_suffix = params->chain_suffix;
|
||||||
|
|
||||||
if (params->data_length > 0) {
|
if (params->data_length > 0) {
|
||||||
if (params->data_initial_chunk_size == 0) {
|
if (params->data_initial_chunk_size == 0) {
|
||||||
@ -550,7 +551,8 @@ static bool ethereum_signing_init_common(struct signing_params *params) {
|
|||||||
return true;
|
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)) {
|
if (params->has_to && ethereum_parse(params->to, params->pubkeyhash)) {
|
||||||
params->pubkeyhash_set = true;
|
params->pubkeyhash_set = true;
|
||||||
} else {
|
} else {
|
||||||
@ -564,7 +566,7 @@ static void ethereum_signing_handle_erc20(struct signing_params *params) {
|
|||||||
memcmp(params->data_initial_chunk_bytes,
|
memcmp(params->data_initial_chunk_bytes,
|
||||||
"\xa9\x05\x9c\xbb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
"\xa9\x05\x9c\xbb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||||
16) == 0) {
|
16) == 0) {
|
||||||
params->token = tokenByChainAddress(chain_id, params->pubkeyhash);
|
params->token = token;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -594,10 +596,11 @@ static bool ethereum_signing_confirm_common(
|
|||||||
return true;
|
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 = {
|
struct signing_params params = {
|
||||||
.chain_id = msg->chain_id,
|
.chain_id = msg->chain_id,
|
||||||
|
.chain_suffix = defs->network->symbol,
|
||||||
.data_length = msg->data_length,
|
.data_length = msg->data_length,
|
||||||
.data_initial_chunk_size = msg->data_initial_chunk.size,
|
.data_initial_chunk_size = msg->data_initial_chunk.size,
|
||||||
.data_initial_chunk_bytes = msg->data_initial_chunk.bytes,
|
.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(¶ms);
|
ethereum_signing_handle_erc20(¶ms, defs->token);
|
||||||
|
|
||||||
if (!ethereum_signing_confirm_common(¶ms)) {
|
if (!ethereum_signing_confirm_common(¶ms)) {
|
||||||
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
|
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,
|
void ethereum_signing_init_eip1559(const EthereumSignTxEIP1559 *msg,
|
||||||
const HDNode *node) {
|
const HDNode *node,
|
||||||
|
const EthereumDefinitionsDecoded *defs) {
|
||||||
struct signing_params params = {
|
struct signing_params params = {
|
||||||
.chain_id = msg->chain_id,
|
.chain_id = msg->chain_id,
|
||||||
|
.chain_suffix = defs->network->symbol,
|
||||||
|
|
||||||
.data_length = msg->data_length,
|
.data_length = msg->data_length,
|
||||||
.data_initial_chunk_size = msg->data_initial_chunk.size,
|
.data_initial_chunk_size = msg->data_initial_chunk.size,
|
||||||
@ -729,7 +734,7 @@ void ethereum_signing_init_eip1559(const EthereumSignTxEIP1559 *msg,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ethereum_signing_handle_erc20(¶ms);
|
ethereum_signing_handle_erc20(¶ms, defs->token);
|
||||||
|
|
||||||
if (!ethereum_signing_confirm_common(¶ms)) {
|
if (!ethereum_signing_confirm_common(¶ms)) {
|
||||||
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
|
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
|
||||||
@ -1040,9 +1045,23 @@ bool ethereum_parse(const char *address, uint8_t pubkeyhash[20]) {
|
|||||||
return true;
|
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,
|
static bool ethereum_path_check_bip44(uint32_t address_n_count,
|
||||||
const uint32_t *address_n,
|
const uint32_t *address_n,
|
||||||
bool pubkey_export, uint64_t chain) {
|
bool pubkey_export,
|
||||||
|
const EthereumNetworkInfo *network) {
|
||||||
bool valid = (address_n_count >= 3);
|
bool valid = (address_n_count >= 3);
|
||||||
valid = valid && (address_n[0] == (PATH_HARDENED | 44));
|
valid = valid && (address_n[0] == (PATH_HARDENED | 44));
|
||||||
valid = valid && (address_n[1] & PATH_HARDENED);
|
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);
|
valid = valid && ((address_n[2] & PATH_UNHARDEN_MASK) <= PATH_MAX_ACCOUNT);
|
||||||
|
|
||||||
uint32_t path_slip44 = address_n[1] & PATH_UNHARDEN_MASK;
|
uint32_t path_slip44 = address_n[1] & PATH_UNHARDEN_MASK;
|
||||||
if (chain == CHAIN_ID_UNKNOWN) {
|
valid = valid && check_ethereum_slip44_unhardened(path_slip44, network);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pubkey_export) {
|
if (pubkey_export) {
|
||||||
// m/44'/coin_type'/account'/*
|
// 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,
|
static bool ethereum_path_check_casa45(uint32_t address_n_count,
|
||||||
const uint32_t *address_n,
|
const uint32_t *address_n,
|
||||||
uint64_t chain) {
|
const EthereumNetworkInfo *network) {
|
||||||
bool valid = (address_n_count == 5);
|
bool valid = (address_n_count == 5);
|
||||||
valid = valid && (address_n[0] == (PATH_HARDENED | 45));
|
valid = valid && (address_n[0] == (PATH_HARDENED | 45));
|
||||||
valid = valid && (address_n[1] < PATH_HARDENED);
|
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);
|
valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX);
|
||||||
|
|
||||||
uint32_t path_slip44 = address_n[1];
|
uint32_t path_slip44 = address_n[1];
|
||||||
if (chain == CHAIN_ID_UNKNOWN) {
|
valid = valid && check_ethereum_slip44_unhardened(path_slip44, network);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return valid;
|
return valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ethereum_path_check(uint32_t address_n_count, const uint32_t *address_n,
|
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) {
|
if (address_n_count == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (address_n[0] == (PATH_HARDENED | 44)) {
|
if (address_n[0] == (PATH_HARDENED | 44)) {
|
||||||
return ethereum_path_check_bip44(address_n_count, address_n, pubkey_export,
|
return ethereum_path_check_bip44(address_n_count, address_n, pubkey_export,
|
||||||
chain);
|
network);
|
||||||
}
|
}
|
||||||
if (address_n[0] == (PATH_HARDENED | 45)) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -23,13 +23,16 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "bip32.h"
|
#include "bip32.h"
|
||||||
|
#include "ethereum_definitions.h"
|
||||||
#include "messages-ethereum.pb.h"
|
#include "messages-ethereum.pb.h"
|
||||||
|
|
||||||
#define CHAIN_ID_UNKNOWN UINT64_MAX
|
#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,
|
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_abort(void);
|
||||||
void ethereum_signing_txack(const EthereumTxAck *msg);
|
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_parse(const char *address, uint8_t pubkeyhash[20]);
|
||||||
|
|
||||||
bool ethereum_path_check(uint32_t address_n_count, const uint32_t *address_n,
|
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
|
#endif
|
||||||
|
313
legacy/firmware/ethereum_definitions.c
Normal file
313
legacy/firmware/ethereum_definitions.c
Normal file
@ -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;
|
||||||
|
}
|
37
legacy/firmware/ethereum_definitions.h
Normal file
37
legacy/firmware/ethereum_definitions.h
Normal file
@ -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
|
26
legacy/firmware/ethereum_definitions_constants.h.mako
Normal file
26
legacy/firmware/ethereum_definitions_constants.h.mako
Normal file
@ -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
|
48
legacy/firmware/ethereum_networks.c.mako
Normal file
48
legacy/firmware/ethereum_networks.c.mako
Normal file
@ -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;
|
||||||
|
}
|
34
legacy/firmware/ethereum_networks.h
Normal file
34
legacy/firmware/ethereum_networks.h
Normal file
@ -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
|
// DO NOT EDIT
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include "ethereum.h"
|
||||||
#include "ethereum_tokens.h"
|
#include "ethereum_tokens.h"
|
||||||
|
|
||||||
const TokenType tokens[TOKENS_COUNT] = {
|
<% erc20_list = list(supported_on("trezor1", erc20)) %>\
|
||||||
% for t in supported_on("trezor1", erc20):
|
#define TOKENS_COUNT ${len(erc20_list)}
|
||||||
{${"{:>2}".format(t.chain_id)}, ${c_str(t.address_bytes)}, " ${ascii(t.symbol)}", ${t.decimals}}, // ${t.chain} / ${t.name}
|
|
||||||
|
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
|
% 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 EthereumTokenInfo UNKNOWN_TOKEN = {
|
||||||
const TokenType *UnknownToken = &_UnknownToken;
|
.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;
|
if (!address) return 0;
|
||||||
for (int i = 0; i < TOKENS_COUNT; i++) {
|
for (int i = 0; i < TOKENS_COUNT; i++) {
|
||||||
if (chain_id == tokens[i].chain_id && memcmp(address, tokens[i].address, 20) == 0) {
|
if (chain_id == tokens[i].chain_id && memcmp(address, tokens[i].address.bytes, sizeof(tokens[i].address.bytes)) == 0) {
|
||||||
return &(tokens[i]);
|
return &(tokens[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return UnknownToken;
|
return &UNKNOWN_TOKEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_unknown_token(const EthereumTokenInfo *token) {
|
||||||
|
return token->chain_id == CHAIN_ID_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
32
legacy/firmware/ethereum_tokens.h
Normal file
32
legacy/firmware/ethereum_tokens.h
Normal file
@ -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
|
#if !BITCOIN_ONLY
|
||||||
#include "ethereum.h"
|
#include "ethereum.h"
|
||||||
|
#include "ethereum_definitions.h"
|
||||||
|
#include "ethereum_networks.h"
|
||||||
#include "nem.h"
|
#include "nem.h"
|
||||||
#include "nem2.h"
|
#include "nem2.h"
|
||||||
#include "stellar.h"
|
#include "stellar.h"
|
||||||
|
@ -19,9 +19,8 @@
|
|||||||
|
|
||||||
static bool fsm_ethereumCheckPath(uint32_t address_n_count,
|
static bool fsm_ethereumCheckPath(uint32_t address_n_count,
|
||||||
const uint32_t *address_n, bool pubkey_export,
|
const uint32_t *address_n, bool pubkey_export,
|
||||||
uint64_t chain_id) {
|
const EthereumNetworkInfo *network) {
|
||||||
if (ethereum_path_check(address_n_count, address_n, pubkey_export,
|
if (ethereum_path_check(address_n_count, address_n, pubkey_export, network)) {
|
||||||
chain_id)) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,6 +32,38 @@ static bool fsm_ethereumCheckPath(uint32_t address_n_count,
|
|||||||
return fsm_layoutPathWarning();
|
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) {
|
void fsm_msgEthereumGetPublicKey(const EthereumGetPublicKey *msg) {
|
||||||
RESP_INIT(EthereumPublicKey);
|
RESP_INIT(EthereumPublicKey);
|
||||||
|
|
||||||
@ -44,12 +75,6 @@ void fsm_msgEthereumGetPublicKey(const EthereumGetPublicKey *msg) {
|
|||||||
const CoinInfo *coin = fsm_getCoin(true, "Bitcoin");
|
const CoinInfo *coin = fsm_getCoin(true, "Bitcoin");
|
||||||
if (!coin) return;
|
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;
|
const char *curve = coin->curve_name;
|
||||||
uint32_t fingerprint;
|
uint32_t fingerprint;
|
||||||
HDNode *node = fsm_getDerivedNode(curve, msg->address_n, msg->address_n_count,
|
HDNode *node = fsm_getDerivedNode(curve, msg->address_n, msg->address_n_count,
|
||||||
@ -93,8 +118,12 @@ void fsm_msgEthereumSignTx(const EthereumSignTx *msg) {
|
|||||||
|
|
||||||
CHECK_PIN
|
CHECK_PIN
|
||||||
|
|
||||||
if (!fsm_ethereumCheckPath(msg->address_n_count, msg->address_n, false,
|
const EthereumDefinitionsDecoded *defs =
|
||||||
msg->chain_id)) {
|
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();
|
layoutHome();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -103,7 +132,7 @@ void fsm_msgEthereumSignTx(const EthereumSignTx *msg) {
|
|||||||
msg->address_n_count, NULL);
|
msg->address_n_count, NULL);
|
||||||
if (!node) return;
|
if (!node) return;
|
||||||
|
|
||||||
ethereum_signing_init(msg, node);
|
ethereum_signing_init(msg, node, defs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void fsm_msgEthereumSignTxEIP1559(const EthereumSignTxEIP1559 *msg) {
|
void fsm_msgEthereumSignTxEIP1559(const EthereumSignTxEIP1559 *msg) {
|
||||||
@ -111,8 +140,12 @@ void fsm_msgEthereumSignTxEIP1559(const EthereumSignTxEIP1559 *msg) {
|
|||||||
|
|
||||||
CHECK_PIN
|
CHECK_PIN
|
||||||
|
|
||||||
if (!fsm_ethereumCheckPath(msg->address_n_count, msg->address_n, false,
|
const EthereumDefinitionsDecoded *defs =
|
||||||
msg->chain_id)) {
|
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();
|
layoutHome();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -121,7 +154,7 @@ void fsm_msgEthereumSignTxEIP1559(const EthereumSignTxEIP1559 *msg) {
|
|||||||
msg->address_n_count, NULL);
|
msg->address_n_count, NULL);
|
||||||
if (!node) return;
|
if (!node) return;
|
||||||
|
|
||||||
ethereum_signing_init_eip1559(msg, node);
|
ethereum_signing_init_eip1559(msg, node, defs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void fsm_msgEthereumTxAck(const EthereumTxAck *msg) {
|
void fsm_msgEthereumTxAck(const EthereumTxAck *msg) {
|
||||||
@ -137,8 +170,16 @@ void fsm_msgEthereumGetAddress(const EthereumGetAddress *msg) {
|
|||||||
|
|
||||||
CHECK_PIN
|
CHECK_PIN
|
||||||
|
|
||||||
if (!fsm_ethereumCheckPath(msg->address_n_count, msg->address_n, false,
|
uint32_t slip44 = (msg->address_n_count > 1)
|
||||||
CHAIN_ID_UNKNOWN)) {
|
? (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();
|
layoutHome();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -153,9 +194,6 @@ void fsm_msgEthereumGetAddress(const EthereumGetAddress *msg) {
|
|||||||
layoutHome();
|
layoutHome();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t slip44 =
|
|
||||||
(msg->address_n_count > 1) ? (msg->address_n[1] & PATH_UNHARDEN_MASK) : 0;
|
|
||||||
bool rskip60 = false;
|
bool rskip60 = false;
|
||||||
uint64_t chain_id = 0;
|
uint64_t chain_id = 0;
|
||||||
// constants from trezor-common/defs/ethereum/networks.json
|
// constants from trezor-common/defs/ethereum/networks.json
|
||||||
@ -195,8 +233,16 @@ void fsm_msgEthereumSignMessage(const EthereumSignMessage *msg) {
|
|||||||
|
|
||||||
CHECK_PIN
|
CHECK_PIN
|
||||||
|
|
||||||
if (!fsm_ethereumCheckPath(msg->address_n_count, msg->address_n, false,
|
uint32_t slip44 = (msg->address_n_count > 1)
|
||||||
CHAIN_ID_UNKNOWN)) {
|
? (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();
|
layoutHome();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -282,8 +328,16 @@ void fsm_msgEthereumSignTypedHash(const EthereumSignTypedHash *msg) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fsm_ethereumCheckPath(msg->address_n_count, msg->address_n, false,
|
uint32_t slip44 = (msg->address_n_count > 1)
|
||||||
CHAIN_ID_UNKNOWN)) {
|
? (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();
|
layoutHome();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,8 @@ SKIPPED_MESSAGES += Ethereum NEM Stellar
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
PROTO_NAMES = messages messages-bitcoin messages-common messages-crypto messages-debug \
|
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_OPTIONS = $(PROTO_NAMES:=.options)
|
||||||
PROTO_COMPILED = $(PROTO_NAMES:=.pb)
|
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
|
1
legacy/firmware/protob/messages-ethereum-definitions.proto
Symbolic link
1
legacy/firmware/protob/messages-ethereum-definitions.proto
Symbolic link
@ -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.address_n max_count:8
|
||||||
EthereumSignMessage.message max_size:1024
|
EthereumSignMessage.message max_size:1024
|
||||||
|
EthereumSignMessage.encoded_network max_size:1024
|
||||||
|
|
||||||
EthereumVerifyMessage.address max_size:43
|
EthereumVerifyMessage.address max_size:43
|
||||||
EthereumVerifyMessage.signature max_size:65
|
EthereumVerifyMessage.signature max_size:65
|
||||||
@ -37,11 +38,14 @@ EthereumMessageSignature.signature max_size:65
|
|||||||
EthereumSignTypedHash.address_n max_count:8
|
EthereumSignTypedHash.address_n max_count:8
|
||||||
EthereumSignTypedHash.domain_separator_hash max_size:32
|
EthereumSignTypedHash.domain_separator_hash max_size:32
|
||||||
EthereumSignTypedHash.message_hash max_size:32
|
EthereumSignTypedHash.message_hash max_size:32
|
||||||
|
EthereumSignTypedHash.encoded_network max_size:1024
|
||||||
|
|
||||||
EthereumTypedDataSignature.address max_size:43
|
EthereumTypedDataSignature.address max_size:43
|
||||||
EthereumTypedDataSignature.signature max_size:65
|
EthereumTypedDataSignature.signature max_size:65
|
||||||
|
|
||||||
EthereumGetAddress.address_n max_count:8
|
EthereumGetAddress.address_n max_count:8
|
||||||
|
EthereumGetAddress.encoded_network max_size:1024
|
||||||
|
|
||||||
EthereumGetPublicKey.address_n max_count:8
|
EthereumGetPublicKey.address_n max_count:8
|
||||||
|
|
||||||
EthereumAddress._old_address max_size:20
|
EthereumAddress._old_address max_size:20
|
||||||
|
Loading…
Reference in New Issue
Block a user