From b5f9a5738f558002c3c41d67ff8230483a98047d Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Mon, 24 Jul 2017 17:40:46 +0200 Subject: [PATCH] segwit support for SignMessage, VerifyMessage --- firmware/crypto.c | 65 +++++++++++++++++++++++++------- firmware/crypto.h | 2 +- firmware/fsm.c | 11 ++++-- firmware/protob/messages.options | 8 ++-- firmware/protob/messages.pb.h | 12 +++--- firmware/transaction.c | 11 +----- vendor/trezor-crypto | 2 +- 7 files changed, 73 insertions(+), 38 deletions(-) diff --git a/firmware/crypto.c b/firmware/crypto.c index b27a38946..590675c3e 100644 --- a/firmware/crypto.c +++ b/firmware/crypto.c @@ -20,6 +20,7 @@ #include #include "crypto.h" #include "sha2.h" +#include "ripemd160.h" #include "pbkdf2.h" #include "aes.h" #include "hmac.h" @@ -109,7 +110,7 @@ int gpgMessageSign(HDNode *node, const uint8_t *message, size_t message_len, uin } } -int cryptoMessageSign(const CoinType *coin, 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) { SHA256_CTX ctx; sha256_Init(&ctx); @@ -124,34 +125,48 @@ int cryptoMessageSign(const CoinType *coin, HDNode *node, const uint8_t *message uint8_t pby; int result = hdnode_sign_digest(node, hash, signature + 1, &pby, NULL); if (result == 0) { - signature[0] = 27 + pby + 4; + switch (script_type) { + case InputScriptType_SPENDP2SHWITNESS: + // segwit-in-p2sh + signature[0] = 35 + pby; + break; + case InputScriptType_SPENDWITNESS: + // segwit + signature[0] = 39 + pby; + break; + default: + // p2pkh + signature[0] = 31 + pby; + break; + } } 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) { - SHA256_CTX ctx; - uint8_t pubkey[65], addr_raw[MAX_ADDR_RAW_SIZE], hash[32]; + // check for invalid signature prefix + if (signature[0] < 27 || signature[0] > 43) { + return 1; + } // calculate hash + SHA256_CTX ctx; sha256_Init(&ctx); sha256_Update(&ctx, (const uint8_t *)coin->signed_message_header, strlen(coin->signed_message_header)); uint8_t varint[5]; uint32_t l = ser_length(message_len, varint); sha256_Update(&ctx, varint, l); sha256_Update(&ctx, message, message_len); + uint8_t hash[32]; sha256_Final(&ctx, hash); sha256_Raw(hash, 32, hash); - uint8_t recid = signature[0] - 27; - if (recid >= 8) { - return 1; - } - bool compressed = (recid >= 4); - recid &= 3; + uint8_t recid = (signature[0] - 27) % 4; + bool compressed = signature[0] >= 31; // check if signature verifies the digest and recover the public key + uint8_t pubkey[65]; if (ecdsa_verify_digest_recover(&secp256k1, pubkey, signature + 1, hash, recid) != 0) { return 3; } @@ -159,11 +174,35 @@ int cryptoMessageVerify(const CoinType *coin, const uint8_t *message, size_t mes if (compressed) { pubkey[0] = 0x02 | (pubkey[64] & 1); } + // check if the address is correct - ecdsa_get_address_raw(pubkey, address_type, addr_raw); - if (memcmp(addr_raw, address_raw, address_prefix_bytes_len(address_type) + 20) != 0) { - return 2; + uint8_t addr_raw[MAX_ADDR_RAW_SIZE]; + + // p2pkh + if (signature[0] >= 27 && signature[0] <= 34) { + if (address_type != coin->address_type) { + return 4; + } + ecdsa_get_address_raw(pubkey, address_type, addr_raw); + if (memcmp(addr_raw, address_raw, address_prefix_bytes_len(address_type) + 20) != 0) { + return 2; + } + } else + // segwit-in-p2sh + if (signature[0] >= 35 && signature[0] <= 38) { + if (address_type != coin->address_type_p2sh) { + return 4; + } + ecdsa_get_address_segwit_p2sh_raw(pubkey, address_type, addr_raw); + if (memcmp(addr_raw, address_raw, address_prefix_bytes_len(address_type) + 20) != 0) { + return 2; + } + } else + // segwit + if (signature[0] >= 39 && signature[0] <= 42) { + return 2; // not supported yet } + return 0; } diff --git a/firmware/crypto.h b/firmware/crypto.h index 1f211c37f..2c52cb330 100644 --- a/firmware/crypto.h +++ b/firmware/crypto.h @@ -37,7 +37,7 @@ 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, 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 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); diff --git a/firmware/fsm.c b/firmware/fsm.c index 47cf93291..44d7cc850 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -795,9 +795,14 @@ void fsm_msgSignMessage(SignMessage *msg) if (!node) return; layoutProgressSwipe(_("Signing"), 0); - if (cryptoMessageSign(coin, node, msg->message.bytes, msg->message.size, resp->signature.bytes) == 0) { + if (cryptoMessageSign(coin, node, msg->script_type, msg->message.bytes, msg->message.size, resp->signature.bytes) == 0) { resp->has_address = true; - hdnode_get_address(node, coin->address_type, resp->address, sizeof(resp->address)); + hdnode_fill_public_key(node); + if (!compute_address(coin, msg->script_type, node, false, NULL, resp->address)) { + fsm_sendFailure(FailureType_Failure_ProcessError, _("Error computing address")); + layoutHome(); + return; + } resp->has_signature = true; resp->signature.size = 65; msg_write(MessageType_MessageType_MessageSignature, resp); @@ -890,7 +895,7 @@ void fsm_msgSignIdentity(SignIdentity *msg) uint8_t digest[64]; sha256_Raw(msg->challenge_hidden.bytes, msg->challenge_hidden.size, digest); sha256_Raw((const uint8_t *)msg->challenge_visual, strlen(msg->challenge_visual), digest + 32); - result = cryptoMessageSign(&(coins[0]), node, digest, 64, resp->signature.bytes); + result = cryptoMessageSign(&(coins[0]), node, InputScriptType_SPENDADDRESS, digest, 64, resp->signature.bytes); } if (result == 0) { diff --git a/firmware/protob/messages.options b/firmware/protob/messages.options index d3ccac9ea..47d0d9b2c 100644 --- a/firmware/protob/messages.options +++ b/firmware/protob/messages.options @@ -57,12 +57,12 @@ SignMessage.address_n max_count:8 SignMessage.message max_size:1024 SignMessage.coin_name max_size:17 -VerifyMessage.address max_size:41 +VerifyMessage.address max_size:60 VerifyMessage.signature max_size:65 VerifyMessage.message max_size:1024 VerifyMessage.coin_name max_size:17 -MessageSignature.address max_size:41 +MessageSignature.address max_size:60 MessageSignature.signature max_size:65 EthereumSignMessage.address_n max_count:8 @@ -97,7 +97,7 @@ DecryptMessage skip_message:true # deprecated DecryptedMessage skip_message:true -# DecryptedMessage.address max_size:41 +# DecryptedMessage.address max_size:60 # DecryptedMessage.message max_size:1024 CipherKeyValue.address_n max_count:8 @@ -133,7 +133,7 @@ SignIdentity.challenge_hidden max_size:256 SignIdentity.challenge_visual max_size:256 SignIdentity.ecdsa_curve_name max_size:32 -SignedIdentity.address max_size:41 +SignedIdentity.address max_size:60 SignedIdentity.public_key max_size:33 SignedIdentity.signature max_size:65 diff --git a/firmware/protob/messages.pb.h b/firmware/protob/messages.pb.h index b6166a591..3ebb54e19 100644 --- a/firmware/protob/messages.pb.h +++ b/firmware/protob/messages.pb.h @@ -595,7 +595,7 @@ typedef struct { typedef struct _MessageSignature { bool has_address; - char address[41]; + char address[60]; bool has_signature; MessageSignature_signature_t signature; } MessageSignature; @@ -729,7 +729,7 @@ typedef struct { typedef struct _SignedIdentity { bool has_address; - char address[41]; + char address[60]; bool has_public_key; SignedIdentity_public_key_t public_key; bool has_signature; @@ -767,7 +767,7 @@ typedef struct { typedef struct _VerifyMessage { bool has_address; - char address[41]; + char address[60]; bool has_signature; VerifyMessage_signature_t signature; bool has_message; @@ -1192,8 +1192,8 @@ extern const pb_field_t DebugLinkFlashErase_fields[2]; #define WordRequest_size 6 #define WordAck_size 14 #define SignMessage_size 1100 -#define VerifyMessage_size 1156 -#define MessageSignature_size 110 +#define VerifyMessage_size 1175 +#define MessageSignature_size 129 #define CipherKeyValue_size 1358 #define CipheredKeyValue_size 1027 #define SignTx_size 43 @@ -1206,7 +1206,7 @@ extern const pb_field_t DebugLinkFlashErase_fields[2]; #define EthereumVerifyMessage_size 1116 #define EthereumMessageSignature_size 89 #define SignIdentity_size (558 + IdentityType_size) -#define SignedIdentity_size 145 +#define SignedIdentity_size 164 #define GetECDHSessionKey_size (107 + IdentityType_size) #define ECDHSessionKey_size 67 #define SetU2FCounter_size 6 diff --git a/firmware/transaction.c b/firmware/transaction.c index 15674d74b..7e0263279 100644 --- a/firmware/transaction.c +++ b/firmware/transaction.c @@ -124,16 +124,7 @@ bool compute_address(const CoinType *coin, if (!coin->has_address_type_p2sh) { return 0; } - prelen = address_prefix_bytes_len(coin->address_type_p2sh); - raw[0] = 0; // version byte - raw[1] = 20; // push 20 bytes - ecdsa_get_pubkeyhash(node->public_key, raw + 2); - sha256_Raw(raw, 22, digest); - address_write_prefix_bytes(coin->address_type_p2sh, raw); - ripemd160(digest, 32, raw + prelen); - if (!base58_encode_check(raw, prelen + 20, address, MAX_ADDR_SIZE)) { - return 0; - } + ecdsa_get_address_segwit_p2sh(node->public_key, coin->address_type_p2sh, address, MAX_ADDR_SIZE); } else { ecdsa_get_address(node->public_key, coin->address_type, address, MAX_ADDR_SIZE); } diff --git a/vendor/trezor-crypto b/vendor/trezor-crypto index cfe8f39cd..dfdb4d2d7 160000 --- a/vendor/trezor-crypto +++ b/vendor/trezor-crypto @@ -1 +1 @@ -Subproject commit cfe8f39cd436bf98e864775a9131e81e554e7bf1 +Subproject commit dfdb4d2d766506e9f54dd2c69c5e664fe4e39304