From 69d99d202d0d9360c6cdbef9e17f25cd81477ff1 Mon Sep 17 00:00:00 2001 From: Daira Hopwood Date: Sun, 9 Oct 2016 20:38:51 +0100 Subject: [PATCH] Add support for multi-byte address prefixes. --- firmware/coins.c | 54 +++++++++++++++++++++++++++++++++++++++++- firmware/coins.h | 6 ++++- firmware/crypto.c | 11 +++++---- firmware/crypto.h | 2 +- firmware/fsm.c | 20 +++++++--------- firmware/transaction.c | 28 ++++++++++------------ firmware/transaction.h | 2 +- vendor/trezor-crypto | 2 +- 8 files changed, 89 insertions(+), 36 deletions(-) diff --git a/firmware/coins.c b/firmware/coins.c index 8a50dbdc81..d027f56d3b 100644 --- a/firmware/coins.c +++ b/firmware/coins.c @@ -55,7 +55,7 @@ const CoinType *coinByName(const char *name) return 0; } -const CoinType *coinByAddressType(uint8_t address_type) +const CoinType *coinByAddressType(uint32_t address_type) { int i; for (i = 0; i < COINS_COUNT; i++) { @@ -65,3 +65,55 @@ const CoinType *coinByAddressType(uint8_t address_type) } return 0; } + +size_t prefixBytesByAddressType(uint32_t address_type) +{ + if (address_type <= 0xFF) return 1; + if (address_type <= 0xFFFF) return 2; + if (address_type <= 0xFFFFFF) return 3; + return 4; +} + +bool addressHasExpectedPrefix(const uint8_t *addr, uint32_t address_type) +{ + if (address_type <= 0xFF) { + return address_type == (uint32_t)(addr[0]); + } + if (address_type <= 0xFFFF) { + return address_type == ((uint32_t)(addr[0] << 8) | (uint32_t)(addr[1])); + } + if (address_type <= 0xFFFFFF) { + return address_type == ((uint32_t)(addr[0] << 16) | (uint32_t)(addr[1] << 8) | (uint32_t)(addr[2])); + } + return address_type == ((uint32_t)(addr[0] << 24) | (uint32_t)(addr[1] << 16) | (uint32_t)(addr[2] << 8) | (uint32_t)(addr[3])); +} + +void writeAddressPrefix(uint8_t *addr, uint32_t address_type) +{ + if (address_type > 0xFFFFFF) *(addr++) = address_type >> 24; + if (address_type > 0xFFFF) *(addr++) = (address_type >> 16) & 0xFF; + if (address_type > 0xFF) *(addr++) = (address_type >> 8) & 0xFF; + *(addr++) = address_type & 0xFF; +} + +bool getAddressType(const CoinType *coin, const uint8_t *addr, uint32_t *address_type) +{ + if (coin->has_address_type && addressHasExpectedPrefix(addr, coin->address_type)) { + *address_type = coin->address_type; + return true; + } + if (coin->has_address_type_p2sh && addressHasExpectedPrefix(addr, coin->address_type_p2sh)) { + *address_type = coin->address_type_p2sh; + return true; + } + if (coin->has_address_type_p2wpkh && addressHasExpectedPrefix(addr, coin->address_type_p2wpkh)) { + *address_type = coin->address_type_p2wpkh; + return true; + } + if (coin->has_address_type_p2wsh && addressHasExpectedPrefix(addr, coin->address_type_p2wsh)) { + *address_type = coin->address_type_p2wsh; + return true; + } + *address_type = 0; + return false; +} diff --git a/firmware/coins.h b/firmware/coins.h index 32a3ba8b3c..b09e241fdd 100644 --- a/firmware/coins.h +++ b/firmware/coins.h @@ -28,6 +28,10 @@ extern const CoinType coins[COINS_COUNT]; const CoinType *coinByShortcut(const char *shortcut); const CoinType *coinByName(const char *name); -const CoinType *coinByAddressType(uint8_t address_type); +const CoinType *coinByAddressType(uint32_t address_type); +size_t prefixBytesByAddressType(uint32_t address_type); +bool addressHasExpectedPrefix(const uint8_t *addr, uint32_t address_type); +void writeAddressPrefix(uint8_t *addr, uint32_t address_type); +bool getAddressType(const CoinType *coin, const uint8_t *addr, uint32_t *address_type); #endif diff --git a/firmware/crypto.c b/firmware/crypto.c index a1c8f4c91d..b3e3ce1526 100644 --- a/firmware/crypto.c +++ b/firmware/crypto.c @@ -28,6 +28,7 @@ #include "curves.h" #include "secp256k1.h" #include "macros.h" +#include "coins.h" uint32_t ser_length(uint32_t len, uint8_t *out) { @@ -140,10 +141,10 @@ int cryptoMessageSign(const CoinType *coin, HDNode *node, const uint8_t *message return result; } -int cryptoMessageVerify(const CoinType *coin, const uint8_t *message, size_t message_len, const uint8_t *address_raw, const 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) { SHA256_CTX ctx; - uint8_t pubkey[65], addr_raw[21], hash[32]; + uint8_t pubkey[65], addr_raw[MAX_ADDR_RAW_SIZE], hash[32]; // calculate hash sha256_Init(&ctx); @@ -171,8 +172,8 @@ int cryptoMessageVerify(const CoinType *coin, const uint8_t *message, size_t mes pubkey[0] = 0x02 | (pubkey[64] & 1); } // check if the address is correct - ecdsa_get_address_raw(pubkey, address_raw[0], addr_raw); - if (memcmp(addr_raw, address_raw, 21) != 0) { + ecdsa_get_address_raw(pubkey, address_type, addr_raw); + if (memcmp(addr_raw, address_raw, prefixBytesByAddressType(address_type) + 20) != 0) { return 2; } return 0; @@ -272,9 +273,11 @@ int cryptoMessageDecrypt(curve_point *nonce, uint8_t *payload, size_t payload_le uint32_t l, o; l = deser_length(payload + 1, &o); if (*signing) { + // FIXME: assumes a raw address is 21 bytes (also below). if (1 + l + o + 21 + 65 != payload_len) { return 4; } + // FIXME: cryptoMessageVerify changed to take the address_type as a parameter. if (cryptoMessageVerify(payload + 1 + l, o, payload + 1 + l + o, payload + 1 + l + o + 21) != 0) { return 5; } diff --git a/firmware/crypto.h b/firmware/crypto.h index e6d3c00faf..ff97f91f2f 100644 --- a/firmware/crypto.h +++ b/firmware/crypto.h @@ -42,7 +42,7 @@ int cryptoGetECDHSessionKey(const HDNode *node, const uint8_t *peer_public_key, int cryptoMessageSign(const CoinType *coin, HDNode *node, const uint8_t *message, size_t message_len, uint8_t *signature); -int cryptoMessageVerify(const CoinType *coin, const uint8_t *message, size_t message_len, const uint8_t *address_raw, const 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); /* 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 1ed2a95a5c..c2b5dd57a6 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -732,9 +732,7 @@ void fsm_msgSignMessage(SignMessage *msg) layoutProgressSwipe("Signing", 0); if (cryptoMessageSign(coin, node, msg->message.bytes, msg->message.size, resp->signature.bytes) == 0) { resp->has_address = true; - uint8_t addr_raw[21]; - hdnode_get_address_raw(node, coin->address_type, addr_raw); - base58_encode_check(addr_raw, 21, resp->address, sizeof(resp->address)); + hdnode_get_address(node, coin->address_type, resp->address, sizeof(resp->address)); resp->has_signature = true; resp->signature.size = 65; msg_write(MessageType_MessageType_MessageSignature, resp); @@ -757,11 +755,13 @@ void fsm_msgVerifyMessage(VerifyMessage *msg) const CoinType *coin = fsm_getCoin(msg->coin_name); if (!coin) return; layoutProgressSwipe("Verifying", 0); - uint8_t addr_raw[21]; - if (!ecdsa_address_decode(msg->address, addr_raw)) { + uint8_t addr_raw[MAX_ADDR_RAW_SIZE]; + uint32_t address_type; + if (!getAddressType(coin, (const uint8_t *) msg->address, &address_type) || !ecdsa_address_decode(msg->address, address_type, addr_raw)) { fsm_sendFailure(FailureType_Failure_InvalidSignature, "Invalid address"); + return; } - if (msg->signature.size == 65 && cryptoMessageVerify(coin, msg->message.bytes, msg->message.size, addr_raw, msg->signature.bytes) == 0) { + if (msg->signature.size == 65 && cryptoMessageVerify(coin, msg->message.bytes, msg->message.size, address_type, addr_raw, msg->signature.bytes) == 0) { layoutVerifyAddress(msg->address); if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { fsm_sendFailure(FailureType_Failure_ActionCancelled, "Message verification cancelled"); @@ -845,9 +845,7 @@ void fsm_msgSignIdentity(SignIdentity *msg) resp->has_address = false; } else { resp->has_address = true; - uint8_t addr_raw[21]; - hdnode_get_address_raw(node, 0x00, addr_raw); // hardcoded Bitcoin address type - base58_encode_check(addr_raw, 21, resp->address, sizeof(resp->address)); + hdnode_get_address(node, 0x00, resp->address, sizeof(resp->address)); // hardcoded Bitcoin address type } resp->has_public_key = true; resp->public_key.size = 33; @@ -943,7 +941,7 @@ void fsm_msgEncryptMessage(EncryptMessage *msg) RESP_INIT(EncryptedMessage); const CoinType *coin = 0; const HDNode *node = 0; - uint8_t address_raw[21]; + uint8_t address_raw[MAX_ADDR_RAW_SIZE]; if (signing) { coin = coinByName(msg->coin_name); if (!coin) { @@ -1011,7 +1009,7 @@ void fsm_msgDecryptMessage(DecryptMessage *msg) RESP_INIT(DecryptedMessage); bool display_only = false; bool signing = false; - uint8_t address_raw[21]; + uint8_t address_raw[MAX_ADDR_RAW_SIZE]; if (cryptoMessageDecrypt(&nonce_pubkey, msg->message.bytes, msg->message.size, msg->hmac.bytes, msg->hmac.size, node->private_key, resp->message.bytes, &(resp->message.size), &display_only, &signing, address_raw) != 0) { fsm_sendFailure(FailureType_Failure_ActionCancelled, "Error decrypting message"); layoutHome(); diff --git a/firmware/transaction.c b/firmware/transaction.c index 6b0ec88e6c..54801a3761 100644 --- a/firmware/transaction.c +++ b/firmware/transaction.c @@ -59,7 +59,7 @@ int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, T { memset(out, 0, sizeof(TxOutputBinType)); out->amount = in->amount; - uint8_t addr_raw[21]; + uint8_t addr_raw[MAX_ADDR_RAW_SIZE]; if (in->script_type == OutputScriptType_PAYTOADDRESS) { @@ -80,12 +80,10 @@ int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, T return -1; } } - if (!ecdsa_address_decode(in->address, addr_raw)) { - return 0; - } - if (addr_raw[0] != coin->address_type) { + if (!ecdsa_address_decode(in->address, coin->address_type, addr_raw)) { return 0; } + } else { // does not have address_n neither address -> error return 0; } @@ -93,7 +91,7 @@ int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, T out->script_pubkey.bytes[0] = 0x76; // OP_DUP out->script_pubkey.bytes[1] = 0xA9; // OP_HASH_160 out->script_pubkey.bytes[2] = 0x14; // pushing 20 bytes - memcpy(out->script_pubkey.bytes + 3, addr_raw + 1, 20); + memcpy(out->script_pubkey.bytes + 3, addr_raw + prefixBytesByAddressType(coin->address_type), 20); out->script_pubkey.bytes[23] = 0x88; // OP_EQUALVERIFY out->script_pubkey.bytes[24] = 0xAC; // OP_CHECKSIG out->script_pubkey.size = 25; @@ -101,10 +99,7 @@ int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, T } if (in->script_type == OutputScriptType_PAYTOSCRIPTHASH) { - if (!in->has_address || !ecdsa_address_decode(in->address, addr_raw)) { - return 0; - } - if (addr_raw[0] != coin->address_type_p2sh) { + if (!in->has_address || !ecdsa_address_decode(in->address, coin->address_type_p2sh, addr_raw)) { return 0; } if (needs_confirm) { @@ -115,7 +110,7 @@ int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, T } out->script_pubkey.bytes[0] = 0xA9; // OP_HASH_160 out->script_pubkey.bytes[1] = 0x14; // pushing 20 bytes - memcpy(out->script_pubkey.bytes + 2, addr_raw + 1, 20); + memcpy(out->script_pubkey.bytes + 2, addr_raw + prefixBytesByAddressType(coin->address_type_p2sh), 20); out->script_pubkey.bytes[22] = 0x87; // OP_EQUAL out->script_pubkey.size = 23; return 23; @@ -123,16 +118,17 @@ int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, T if (in->script_type == OutputScriptType_PAYTOMULTISIG) { uint8_t buf[32]; + size_t prefix_bytes = prefixBytesByAddressType(coin->address_type_p2sh); if (!in->has_multisig) { return 0; } if (compile_script_multisig_hash(&(in->multisig), buf) == 0) { return 0; } - addr_raw[0] = coin->address_type_p2sh; - ripemd160(buf, 32, addr_raw + 1); + writeAddressPrefix(addr_raw, coin->address_type_p2sh); + ripemd160(buf, 32, addr_raw + prefix_bytes); if (needs_confirm) { - base58_encode_check(addr_raw, 21, in->address, sizeof(in->address)); + base58_encode_check(addr_raw, prefix_bytes + 20, in->address, sizeof(in->address)); layoutConfirmOutput(coin, in); if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { return -1; @@ -140,7 +136,7 @@ int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, T } out->script_pubkey.bytes[0] = 0xA9; // OP_HASH_160 out->script_pubkey.bytes[1] = 0x14; // pushing 20 bytes - memcpy(out->script_pubkey.bytes + 2, addr_raw + 1, 20); + memcpy(out->script_pubkey.bytes + 2, addr_raw + prefix_bytes, 20); out->script_pubkey.bytes[22] = 0x87; // OP_EQUAL out->script_pubkey.size = 23; return 23; @@ -159,7 +155,7 @@ int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, T return 0; } -uint32_t compile_script_sig(uint8_t address_type, const uint8_t *pubkeyhash, uint8_t *out) +uint32_t compile_script_sig(uint32_t address_type, const uint8_t *pubkeyhash, uint8_t *out) { if (coinByAddressType(address_type)) { // valid coin type out[0] = 0x76; // OP_DUP diff --git a/firmware/transaction.h b/firmware/transaction.h index e6980a0416..bd64baa4f2 100644 --- a/firmware/transaction.h +++ b/firmware/transaction.h @@ -41,7 +41,7 @@ typedef struct { SHA256_CTX ctx; } TxStruct; -uint32_t compile_script_sig(uint8_t address_type, const uint8_t *pubkeyhash, uint8_t *out); +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 *out); diff --git a/vendor/trezor-crypto b/vendor/trezor-crypto index 157caf3763..ad73c0d4e7 160000 --- a/vendor/trezor-crypto +++ b/vendor/trezor-crypto @@ -1 +1 @@ -Subproject commit 157caf3763866c386f9bcc287db3a1a472b5d55e +Subproject commit ad73c0d4e73fe138ebbbc39d6a335167ba7c9923