From b5fa8a266a26c03322a9c0ac05683e9dc05ab415 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Mon, 30 Oct 2017 22:13:09 +0100 Subject: [PATCH] New CoinInfo separated from protobuf structures Having CoinType using the protobuf structures has several disadvantages. - We always need to change trezor-common if we need a new field (like bech32 prefix) - Every time Trezor initializes it sends all this information out and nobody cares. - The protobuf structures add storage overhead due to their fixed size. I also removed most of the `has_` fields except for forkid: - `has_segwit` was merged with segwit - `has_coin_shortcut` can be replaced by test for NULL if necessary. The fields were reordered for better padding. --- firmware/coins-gen.py | 28 ++++++---------------------- firmware/coins.c | 13 ++++++------- firmware/coins.h | 30 +++++++++++++++++++++++------- firmware/crypto.c | 4 ++-- firmware/crypto.h | 5 +++-- firmware/fsm.c | 25 ++++++++++++++++--------- firmware/layout2.c | 14 +++++++------- firmware/layout2.h | 7 ++++--- firmware/signing.c | 6 +++--- firmware/signing.h | 3 ++- firmware/transaction.c | 12 ++++++------ firmware/transaction.h | 5 +++-- 12 files changed, 81 insertions(+), 71 deletions(-) diff --git a/firmware/coins-gen.py b/firmware/coins-gen.py index 9535466e52..0c226d9e8a 100755 --- a/firmware/coins-gen.py +++ b/firmware/coins-gen.py @@ -13,34 +13,18 @@ if len(sys.argv) != 2 or sys.argv[1] not in ("count", "array"): def get_fields(coin): return [ - 'true' if coin['coin_name'] is not None else 'false', '"%s"' % coin['coin_name'] if coin['coin_name'] is not None else 'NULL', - - 'true' if coin['coin_shortcut'] is not None else 'false', '" %s"' % coin['coin_shortcut'] if coin['coin_shortcut'] is not None else 'NULL', - - 'true' if coin['address_type'] is not None else 'false', - '%d' % coin['address_type'] if coin['address_type'] is not None else '0', - - 'true' if coin['maxfee_kb'] is not None else 'false', '%d' % coin['maxfee_kb'] if coin['maxfee_kb'] is not None else '0', - - 'true' if coin['address_type_p2sh'] is not None else 'false', - '%d' % coin['address_type_p2sh'] if coin['address_type_p2sh'] is not None else '0', - - 'true' if coin['signed_message_header'] is not None else 'false', '"\\x%02x" "%s"' % (len(coin['signed_message_header']), coin['signed_message_header'].replace('\n', '\\n')) if coin['signed_message_header'] is not None else 'NULL', - - 'true' if coin['xpub_magic'] is not None else 'false', - '0x%s' % coin['xpub_magic'] if coin['xpub_magic'] is not None else '00000000', - - 'true' if coin['xprv_magic'] is not None else 'false', - '0x%s' % coin['xprv_magic'] if coin['xprv_magic'] is not None else '00000000', - - 'true' if coin['segwit'] is not None else 'false', + 'true' if coin['address_type'] is not None else 'false', + 'true' if coin['address_type_p2sh'] is not None else 'false', 'true' if coin['segwit'] else 'false', - 'true' if coin['forkid'] is not None else 'false', + '%d' % coin['address_type'] if coin['address_type'] is not None else '0', + '%d' % coin['address_type_p2sh'] if coin['address_type_p2sh'] is not None else '0', + '0x%s' % coin['xpub_magic'] if coin['xpub_magic'] is not None else '00000000', + '0x%s' % coin['xprv_magic'] if coin['xprv_magic'] is not None else '00000000', '%d' % coin['forkid'] if coin['forkid'] else '0' ] diff --git a/firmware/coins.c b/firmware/coins.c index ae914f777b..3af074b535 100644 --- a/firmware/coins.c +++ b/firmware/coins.c @@ -23,13 +23,12 @@ #include "ecdsa.h" #include "base58.h" -// filled CoinType Protobuf structure defined in https://github.com/trezor/trezor-common/blob/master/protob/types.proto#L133 -// address types > 0xFF represent a two-byte prefix in big-endian order -const CoinType coins[COINS_COUNT] = { +// filled CoinInfo structure defined in coins.h +const CoinInfo coins[COINS_COUNT] = { #include "coins_array.h" }; -const CoinType *coinByName(const char *name) +const CoinInfo *coinByName(const char *name) { if (!name) return 0; for (int i = 0; i < COINS_COUNT; i++) { @@ -40,7 +39,7 @@ const CoinType *coinByName(const char *name) return 0; } -const CoinType *coinByAddressType(uint32_t address_type) +const CoinInfo *coinByAddressType(uint32_t address_type) { for (int i = 0; i < COINS_COUNT; i++) { if (address_type == coins[i].address_type) { @@ -50,7 +49,7 @@ const CoinType *coinByAddressType(uint32_t address_type) return 0; } -bool coinExtractAddressType(const CoinType *coin, const char *addr, uint32_t *address_type) +bool coinExtractAddressType(const CoinInfo *coin, const char *addr, uint32_t *address_type) { if (!addr) return false; uint8_t addr_raw[MAX_ADDR_RAW_SIZE]; @@ -61,7 +60,7 @@ bool coinExtractAddressType(const CoinType *coin, const char *addr, uint32_t *ad return false; } -bool coinExtractAddressTypeRaw(const CoinType *coin, const uint8_t *addr_raw, uint32_t *address_type) +bool coinExtractAddressTypeRaw(const CoinInfo *coin, const uint8_t *addr_raw, uint32_t *address_type) { if (coin->has_address_type && address_check_prefix(addr_raw, coin->address_type)) { *address_type = coin->address_type; diff --git a/firmware/coins.h b/firmware/coins.h index 0ffc55eb7a..549f455e92 100644 --- a/firmware/coins.h +++ b/firmware/coins.h @@ -20,17 +20,33 @@ #ifndef __COINS_H__ #define __COINS_H__ -#include "messages.pb.h" +#include +#include #include "coins_count.h" -_Static_assert(pb_arraysize(Features, coins) >= COINS_COUNT, "Features.coins max_count not large enough"); +typedef struct _CoinInfo { + const char *coin_name; + const char *coin_shortcut; + uint64_t maxfee_kb; + const char *signed_message_header; + bool has_address_type; + bool has_address_type_p2sh; + bool has_segwit; + bool has_forkid; + // address types > 0xFF represent a two-byte prefix in big-endian order + uint32_t address_type; + uint32_t address_type_p2sh; + uint32_t xpub_magic; + uint32_t xprv_magic; + uint32_t forkid; +} CoinInfo; -extern const CoinType coins[COINS_COUNT]; +extern const CoinInfo coins[COINS_COUNT]; -const CoinType *coinByName(const char *name); -const CoinType *coinByAddressType(uint32_t address_type); -bool coinExtractAddressType(const CoinType *coin, const char *addr, uint32_t *address_type); -bool coinExtractAddressTypeRaw(const CoinType *coin, const uint8_t *addr_raw, uint32_t *address_type); +const CoinInfo *coinByName(const char *name); +const CoinInfo *coinByAddressType(uint32_t address_type); +bool coinExtractAddressType(const CoinInfo *coin, const char *addr, uint32_t *address_type); +bool coinExtractAddressTypeRaw(const CoinInfo *coin, const uint8_t *addr_raw, uint32_t *address_type); #endif diff --git a/firmware/crypto.c b/firmware/crypto.c index 7679706ca1..3a8d6ec45a 100644 --- a/firmware/crypto.c +++ b/firmware/crypto.c @@ -110,7 +110,7 @@ int gpgMessageSign(HDNode *node, const uint8_t *message, size_t message_len, uin } } -int cryptoMessageSign(const CoinType *coin, HDNode *node, InputScriptType script_type, const uint8_t *message, size_t message_len, uint8_t *signature) +int cryptoMessageSign(const CoinInfo *coin, HDNode *node, InputScriptType script_type, const uint8_t *message, size_t message_len, uint8_t *signature) { SHA256_CTX ctx; sha256_Init(&ctx); @@ -143,7 +143,7 @@ int cryptoMessageSign(const CoinType *coin, HDNode *node, InputScriptType script return result; } -int cryptoMessageVerify(const CoinType *coin, const uint8_t *message, size_t message_len, uint32_t address_type, const uint8_t *address_raw, const uint8_t *signature) +int cryptoMessageVerify(const CoinInfo *coin, const uint8_t *message, size_t message_len, uint32_t address_type, const uint8_t *address_raw, const uint8_t *signature) { // check for invalid signature prefix if (signature[0] < 27 || signature[0] > 43) { diff --git a/firmware/crypto.h b/firmware/crypto.h index 7caf6ed133..dbf393db02 100644 --- a/firmware/crypto.h +++ b/firmware/crypto.h @@ -27,6 +27,7 @@ #include #include #include +#include "coins.h" #include "types.pb.h" uint32_t ser_length(uint32_t len, uint8_t *out); @@ -37,9 +38,9 @@ int sshMessageSign(HDNode *node, const uint8_t *message, size_t message_len, uin int gpgMessageSign(HDNode *node, const uint8_t *message, size_t message_len, uint8_t *signature); -int cryptoMessageSign(const CoinType *coin, HDNode *node, InputScriptType script_type, const uint8_t *message, size_t message_len, uint8_t *signature); +int cryptoMessageSign(const CoinInfo *coin, HDNode *node, InputScriptType script_type, const uint8_t *message, size_t message_len, uint8_t *signature); -int cryptoMessageVerify(const CoinType *coin, const uint8_t *message, size_t message_len, uint32_t address_type, const uint8_t *address_raw, const uint8_t *signature); +int cryptoMessageVerify(const CoinInfo *coin, const uint8_t *message, size_t message_len, uint32_t address_type, const uint8_t *address_raw, const uint8_t *signature); /* ECIES disabled int cryptoMessageEncrypt(curve_point *pubkey, const uint8_t *msg, size_t msg_size, bool display_only, uint8_t *nonce, size_t *nonce_len, uint8_t *payload, size_t *payload_len, uint8_t *hmac, size_t *hmac_len, const uint8_t *privkey, const uint8_t *address_raw); diff --git a/firmware/fsm.c b/firmware/fsm.c index 4ff14c90ec..08687ef0e6 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -163,9 +163,9 @@ void fsm_sendFailure(FailureType code, const char *text) msg_write(MessageType_MessageType_Failure, resp); } -const CoinType *fsm_getCoin(bool has_name, const char *name) +const CoinInfo *fsm_getCoin(bool has_name, const char *name) { - const CoinType *coin; + const CoinInfo *coin; if (has_name) { coin = coinByName(name); } else { @@ -208,6 +208,8 @@ void fsm_msgInitialize(Initialize *msg) fsm_msgGetFeatures(0); } +_Static_assert(pb_arraysize(Features, coins) >= COINS_COUNT, "Features.coins max_count not large enough"); + void fsm_msgGetFeatures(GetFeatures *msg) { (void)msg; @@ -233,7 +235,12 @@ void fsm_msgGetFeatures(GetFeatures *msg) strlcpy(resp->label, storage.label, sizeof(resp->label)); } resp->coins_count = COINS_COUNT; - memcpy(resp->coins, coins, COINS_COUNT * sizeof(CoinType)); + for (int i = 0; i < COINS_COUNT; i++) { + resp->coins[i].has_coin_name = true; + strlcpy(resp->coins[i].coin_name, coins[i].coin_name, sizeof(resp->coins[i].coin_name)); + resp->coins[i].has_maxfee_kb = true; + resp->coins[i].maxfee_kb = coins[i].maxfee_kb; + } resp->has_initialized = true; resp->initialized = storage_isInitialized(); resp->has_imported = true; resp->imported = storage.has_imported && storage.imported; resp->has_pin_cached = true; resp->pin_cached = session_isPinCached(); @@ -358,7 +365,7 @@ void fsm_msgGetPublicKey(GetPublicKey *msg) CHECK_PIN - const CoinType *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); + const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); if (!coin) return; const char *curve = SECP256K1_NAME; @@ -471,7 +478,7 @@ void fsm_msgSignTx(SignTx *msg) CHECK_PIN - const CoinType *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); + const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); if (!coin) return; const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, 0, 0); if (!node) return; @@ -640,7 +647,7 @@ void fsm_msgGetAddress(GetAddress *msg) CHECK_PIN - const CoinType *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); + const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); if (!coin) return; HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count); if (!node) return; @@ -794,7 +801,7 @@ void fsm_msgSignMessage(SignMessage *msg) CHECK_PIN - const CoinType *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); + const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); if (!coin) return; HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count); if (!node) return; @@ -822,7 +829,7 @@ void fsm_msgVerifyMessage(VerifyMessage *msg) CHECK_PARAM(msg->has_address, _("No address provided")); CHECK_PARAM(msg->has_message, _("No message provided")); - const CoinType *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); + const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); if (!coin) return; uint8_t addr_raw[MAX_ADDR_RAW_SIZE]; uint32_t address_type; @@ -992,7 +999,7 @@ void fsm_msgEncryptMessage(EncryptMessage *msg) const HDNode *node = 0; uint8_t address_raw[MAX_ADDR_RAW_SIZE]; if (signing) { - const CoinType *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); + const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); if (!coin) return; CHECK_PIN diff --git a/firmware/layout2.c b/firmware/layout2.c index e17f2566f3..998f705f33 100644 --- a/firmware/layout2.c +++ b/firmware/layout2.c @@ -97,10 +97,10 @@ void layoutHome(void) system_millis_lock_start = system_millis; } -void layoutConfirmOutput(const CoinType *coin, const TxOutputType *out) +void layoutConfirmOutput(const CoinInfo *coin, const TxOutputType *out) { char str_out[32]; - bn_format_uint64(out->amount, NULL, coin->has_coin_shortcut ? coin->coin_shortcut : NULL, BITCOIN_DIVISIBILITY, 0, false, str_out, sizeof(str_out)); + bn_format_uint64(out->amount, NULL, coin->coin_shortcut, BITCOIN_DIVISIBILITY, 0, false, str_out, sizeof(str_out)); static char first_half[17 + 1]; strlcpy(first_half, out->address, sizeof(first_half)); layoutDialogSwipe(&bmp_icon_question, @@ -116,11 +116,11 @@ void layoutConfirmOutput(const CoinType *coin, const TxOutputType *out) ); } -void layoutConfirmTx(const CoinType *coin, uint64_t amount_out, uint64_t amount_fee) +void layoutConfirmTx(const CoinInfo *coin, uint64_t amount_out, uint64_t amount_fee) { char str_out[32], str_fee[32]; - bn_format_uint64(amount_out, NULL, coin->has_coin_shortcut ? coin->coin_shortcut : NULL, BITCOIN_DIVISIBILITY, 0, false, str_out, sizeof(str_out)); - bn_format_uint64(amount_fee, NULL, coin->has_coin_shortcut ? coin->coin_shortcut : NULL, BITCOIN_DIVISIBILITY, 0, false, str_fee, sizeof(str_fee)); + bn_format_uint64(amount_out, NULL, coin->coin_shortcut, BITCOIN_DIVISIBILITY, 0, false, str_out, sizeof(str_out)); + bn_format_uint64(amount_fee, NULL, coin->coin_shortcut, BITCOIN_DIVISIBILITY, 0, false, str_fee, sizeof(str_fee)); layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), @@ -134,10 +134,10 @@ void layoutConfirmTx(const CoinType *coin, uint64_t amount_out, uint64_t amount_ ); } -void layoutFeeOverThreshold(const CoinType *coin, uint64_t fee) +void layoutFeeOverThreshold(const CoinInfo *coin, uint64_t fee) { char str_fee[32]; - bn_format_uint64(fee, NULL, coin->has_coin_shortcut ? coin->coin_shortcut : NULL, BITCOIN_DIVISIBILITY, 0, false, str_fee, sizeof(str_fee)); + bn_format_uint64(fee, NULL, coin->coin_shortcut, BITCOIN_DIVISIBILITY, 0, false, str_fee, sizeof(str_fee)); layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), diff --git a/firmware/layout2.h b/firmware/layout2.h index f64ee2a7c1..32685c8e30 100644 --- a/firmware/layout2.h +++ b/firmware/layout2.h @@ -22,6 +22,7 @@ #include "layout.h" #include "types.pb.h" +#include "coins.h" #include "bitmaps.h" #include "bignum.h" #include "trezor.h" @@ -39,9 +40,9 @@ void layoutProgressSwipe(const char *desc, int permil); void layoutScreensaver(void); void layoutHome(void); -void layoutConfirmOutput(const CoinType *coin, const TxOutputType *out); -void layoutConfirmTx(const CoinType *coin, uint64_t amount_out, uint64_t amount_fee); -void layoutFeeOverThreshold(const CoinType *coin, uint64_t fee); +void layoutConfirmOutput(const CoinInfo *coin, const TxOutputType *out); +void layoutConfirmTx(const CoinInfo *coin, uint64_t amount_out, uint64_t amount_fee); +void layoutFeeOverThreshold(const CoinInfo *coin, uint64_t fee); void layoutSignMessage(const uint8_t *msg, uint32_t len); void layoutVerifyAddress(const char *address); void layoutVerifyMessage(const uint8_t *msg, uint32_t len); diff --git a/firmware/signing.c b/firmware/signing.c index 1673fd9fed..cc2ba989e9 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -30,7 +30,7 @@ static uint32_t inputs_count; static uint32_t outputs_count; -static const CoinType *coin; +static const CoinInfo *coin; static const HDNode *root; static CONFIDENTIAL HDNode node; static bool signing = false; @@ -424,7 +424,7 @@ bool compile_input_script_sig(TxInputType *tinput) return tinput->script_sig.size > 0; } -void signing_init(uint32_t _inputs_count, uint32_t _outputs_count, const CoinType *_coin, const HDNode *_root, uint32_t _version, uint32_t _lock_time) +void signing_init(uint32_t _inputs_count, uint32_t _outputs_count, const CoinInfo *_coin, const HDNode *_root, uint32_t _version, uint32_t _lock_time) { inputs_count = _inputs_count; outputs_count = _outputs_count; @@ -827,7 +827,7 @@ void signing_txack(TransactionType *tx) } } else if (tx->inputs[0].script_type == InputScriptType_SPENDWITNESS || tx->inputs[0].script_type == InputScriptType_SPENDP2SHWITNESS) { - if (!coin->has_segwit || !coin->segwit) { + if (!coin->has_segwit) { fsm_sendFailure(FailureType_Failure_DataError, _("Segwit not enabled on this coin")); signing_abort(); return; diff --git a/firmware/signing.h b/firmware/signing.h index cec7933d72..012ac66e04 100644 --- a/firmware/signing.h +++ b/firmware/signing.h @@ -23,9 +23,10 @@ #include #include #include "bip32.h" +#include "coins.h" #include "types.pb.h" -void signing_init(uint32_t _inputs_count, uint32_t _outputs_count, const CoinType *_coin, const HDNode *_root, uint32_t _version, uint32_t _lock_time); +void signing_init(uint32_t _inputs_count, uint32_t _outputs_count, const CoinInfo *_coin, const HDNode *_root, uint32_t _version, uint32_t _lock_time); void signing_abort(void); void signing_txack(TransactionType *tx); diff --git a/firmware/transaction.c b/firmware/transaction.c index 70ab0f1b53..2dbaac397d 100644 --- a/firmware/transaction.c +++ b/firmware/transaction.c @@ -58,7 +58,7 @@ uint32_t op_push(uint32_t i, uint8_t *out) { return 5; } -bool compute_address(const CoinType *coin, +bool compute_address(const CoinInfo *coin, InputScriptType script_type, const HDNode *node, bool has_multisig, const MultisigRedeemScriptType *multisig, @@ -77,14 +77,14 @@ bool compute_address(const CoinType *coin, } if (script_type == InputScriptType_SPENDWITNESS) { // segwit p2wsh: script hash is single sha256 - if (!coin->has_segwit || !coin->segwit) { + if (!coin->has_segwit) { return 0; } // disable native segwit for now return 0; } else if (script_type == InputScriptType_SPENDP2SHWITNESS) { // segwit p2wsh encapsuled in p2sh address - if (!coin->has_segwit || !coin->segwit) { + if (!coin->has_segwit) { return 0; } if (!coin->has_address_type_p2sh) { @@ -111,14 +111,14 @@ bool compute_address(const CoinType *coin, } } else if (script_type == InputScriptType_SPENDWITNESS) { // segwit p2wpkh: pubkey hash is ripemd160 of sha256 - if (!coin->has_segwit || !coin->segwit) { + if (!coin->has_segwit) { return 0; } // disable native segwit for now return 0; } else if (script_type == InputScriptType_SPENDP2SHWITNESS) { // segwit p2wpkh embedded in p2sh - if (!coin->has_segwit || !coin->segwit) { + if (!coin->has_segwit) { return 0; } if (!coin->has_address_type_p2sh) { @@ -131,7 +131,7 @@ bool compute_address(const CoinType *coin, return 1; } -int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, TxOutputBinType *out, bool needs_confirm) +int compile_output(const CoinInfo *coin, const HDNode *root, TxOutputType *in, TxOutputBinType *out, bool needs_confirm) { memset(out, 0, sizeof(TxOutputBinType)); out->amount = in->amount; diff --git a/firmware/transaction.h b/firmware/transaction.h index d3ea1ea0fb..34654e1516 100644 --- a/firmware/transaction.h +++ b/firmware/transaction.h @@ -24,6 +24,7 @@ #include #include "sha2.h" #include "bip32.h" +#include "coins.h" #include "types.pb.h" typedef struct { @@ -45,13 +46,13 @@ typedef struct { SHA256_CTX ctx; } TxStruct; -bool compute_address(const CoinType *coin, InputScriptType script_type, const HDNode *node, bool has_multisig, const MultisigRedeemScriptType *multisig, char address[MAX_ADDR_SIZE]); +bool compute_address(const CoinInfo *coin, InputScriptType script_type, const HDNode *node, bool has_multisig, const MultisigRedeemScriptType *multisig, char address[MAX_ADDR_SIZE]); uint32_t compile_script_sig(uint32_t address_type, const uint8_t *pubkeyhash, uint8_t *out); uint32_t compile_script_multisig(const MultisigRedeemScriptType *multisig, uint8_t *out); uint32_t compile_script_multisig_hash(const MultisigRedeemScriptType *multisig, uint8_t *hash); uint32_t serialize_script_sig(const uint8_t *signature, uint32_t signature_len, const uint8_t *pubkey, uint32_t pubkey_len, uint8_t sighash, uint8_t *out); uint32_t serialize_script_multisig(const MultisigRedeemScriptType *multisig, uint8_t sighash, uint8_t *out); -int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, TxOutputBinType *out, bool needs_confirm); +int compile_output(const CoinInfo *coin, const HDNode *root, TxOutputType *in, TxOutputBinType *out, bool needs_confirm); uint32_t tx_prevout_hash(SHA256_CTX *ctx, const TxInputType *input); uint32_t tx_script_hash(SHA256_CTX *ctx, uint32_t size, const uint8_t *data);