diff --git a/firmware/crypto.c b/firmware/crypto.c index 2b092a93a7..40c219b4d8 100644 --- a/firmware/crypto.c +++ b/firmware/crypto.c @@ -251,3 +251,14 @@ int cryptoMessageDecrypt(curve_point *nonce, uint8_t *payload, pb_size_t payload *msg_len = o; return 0; } + +int cryptoMultisigPubkeyIndex(const MultisigRedeemScriptType *multisig, const uint8_t *pubkey, uint32_t pubkey_len) +{ + int i; + for (i = 0; i < multisig->pubkeys_count; i++) { + if (multisig->pubkeys[i].size == pubkey_len && memcmp(multisig->pubkeys[i].bytes, pubkey, pubkey_len) == 0) { + return i; + } + } + return -1; +} diff --git a/firmware/crypto.h b/firmware/crypto.h index 74bc348205..9435f0c2fd 100644 --- a/firmware/crypto.h +++ b/firmware/crypto.h @@ -25,6 +25,7 @@ #include #include #include +#include "types.pb.h" uint32_t ser_length(uint32_t len, uint8_t *out); @@ -38,4 +39,6 @@ int cryptoMessageEncrypt(curve_point *pubkey, const uint8_t *msg, pb_size_t msg_ int cryptoMessageDecrypt(curve_point *nonce, uint8_t *payload, pb_size_t payload_len, const uint8_t *hmac, pb_size_t hmac_len, const uint8_t *privkey, uint8_t *msg, pb_size_t *msg_len, bool *display_only, bool *signing, uint8_t *address_raw); +int cryptoMultisigPubkeyIndex(const MultisigRedeemScriptType *multisig, const uint8_t *pubkey, uint32_t pubkey_len); + #endif diff --git a/firmware/fsm.c b/firmware/fsm.c index f72d768178..59a9bdb4e7 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -43,6 +43,7 @@ #include "crypto.h" #include "base58.h" #include "bip39.h" +#include "ripemd160.h" // message methods @@ -487,7 +488,24 @@ void fsm_msgGetAddress(GetAddress *msg) fsm_deriveKey(node, msg->address_n, msg->address_n_count); - ecdsa_get_address(node->public_key, coin->address_type, resp->address); + if (msg->has_multisig) { + if (cryptoMultisigPubkeyIndex(&(msg->multisig), node->public_key, 33) < 0) { + fsm_sendFailure(FailureType_Failure_Other, "Pubkey not found in multisig script"); + layoutHome(); + return; + } + uint8_t buf[32]; + if (compile_script_multisig_hash(&(msg->multisig), buf) == 0) { + fsm_sendFailure(FailureType_Failure_Other, "Invalid multisig script"); + layoutHome(); + return; + } + ripemd160(buf, 32, buf + 1); + buf[0] = 0x05; // multisig cointype + base58_encode_check(buf, 21, resp->address); + } else { + ecdsa_get_address(node->public_key, coin->address_type, resp->address); + } if (msg->has_show_display && msg->show_display) { layoutAddress(resp->address); diff --git a/firmware/signing.c b/firmware/signing.c index c6b5643be1..6b0411468b 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -24,6 +24,7 @@ #include "transaction.h" #include "ecdsa.h" #include "protect.h" +#include "crypto.h" static uint32_t inputs_count; static uint32_t outputs_count; @@ -378,14 +379,8 @@ void signing_txack(TransactionType *tx) return; } // fill in the signature - int i, pubkey_idx = -1; - for (i = 0; i < input.multisig.pubkeys_count; i++) { - if (input.multisig.pubkeys[i].size == 33 && memcmp(input.multisig.pubkeys[i].bytes, pubkey, 33) == 0) { - pubkey_idx = i; - break; - } - } - if (pubkey_idx == -1) { + int pubkey_idx = cryptoMultisigPubkeyIndex(&(input.multisig), pubkey, 33); + if (pubkey_idx < 0) { fsm_sendFailure(FailureType_Failure_Other, "Pubkey not found in multisig script"); signing_abort(); return; diff --git a/firmware/transaction.c b/firmware/transaction.c index 958e740f1a..34c730e7dd 100644 --- a/firmware/transaction.c +++ b/firmware/transaction.c @@ -149,8 +149,8 @@ uint32_t compile_script_multisig(const MultisigRedeemScriptType *multisig, uint8 out[r] = 0xAE; r++; // OP_CHECKMULTISIG } else { r++; + uint8_t dummy[8]; for (i = 0; i < n; i++) { - uint8_t dummy[8]; r += op_push(multisig->pubkeys[i].size, dummy); r += multisig->pubkeys[i].size; } @@ -160,6 +160,36 @@ uint32_t compile_script_multisig(const MultisigRedeemScriptType *multisig, uint8 return r; } +uint32_t compile_script_multisig_hash(const MultisigRedeemScriptType *multisig, uint8_t *hash) +{ + if (!multisig->has_m) return 0; + uint32_t m = multisig->m; + uint32_t n = multisig->pubkeys_count; + if (m < 2 || m > 3) return 0; + if (n < 2 || n > 3) return 0; + + SHA256_CTX ctx; + sha256_Init(&ctx); + + uint8_t d, dummy[8]; + d = 0x50 + m; + sha256_Update(&ctx, &d, 1); + uint32_t i, r; + for (i = 0; i < n; i++) { + r = op_push(multisig->pubkeys[i].size, dummy); + sha256_Update(&ctx, dummy, r); + sha256_Update(&ctx, multisig->pubkeys[i].bytes, multisig->pubkeys[i].size); + } + d = 0x50 + n; + sha256_Update(&ctx, &d, 1); + d = 0xAE; + sha256_Update(&ctx, &d, 1); + + sha256_Final(hash, &ctx); + + return 1; +} + uint32_t serialize_script_sig(const uint8_t *signature, uint32_t signature_len, const uint8_t *pubkey, uint32_t pubkey_len, uint8_t *out) { uint32_t r = 0; diff --git a/firmware/transaction.h b/firmware/transaction.h index eca3c16fa9..7fb819aa3a 100644 --- a/firmware/transaction.h +++ b/firmware/transaction.h @@ -43,6 +43,7 @@ typedef struct { uint32_t compile_script_sig(uint8_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); uint32_t serialize_script_multisig(const MultisigRedeemScriptType *multisig, uint8_t *out); int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, TxOutputBinType *out, bool needs_confirm);