diff --git a/firmware/protob/types.pb.c b/firmware/protob/types.pb.c index 66c417590..5e6e1298a 100644 --- a/firmware/protob/types.pb.c +++ b/firmware/protob/types.pb.c @@ -29,9 +29,10 @@ const pb_field_t CoinType_fields[5] = { PB_LAST_FIELD }; -const pb_field_t MultisigRedeemScriptType_fields[3] = { +const pb_field_t MultisigRedeemScriptType_fields[4] = { PB_FIELD( 1, BYTES , REPEATED, STATIC , FIRST, MultisigRedeemScriptType, pubkeys, pubkeys, 0), PB_FIELD( 2, BYTES , REPEATED, STATIC , OTHER, MultisigRedeemScriptType, signatures, pubkeys, 0), + PB_FIELD( 3, UINT32 , OPTIONAL, STATIC , OTHER, MultisigRedeemScriptType, m, signatures, 0), PB_LAST_FIELD }; diff --git a/firmware/protob/types.pb.h b/firmware/protob/types.pb.h index 3b91af929..11df556d6 100644 --- a/firmware/protob/types.pb.h +++ b/firmware/protob/types.pb.h @@ -102,6 +102,8 @@ typedef struct _MultisigRedeemScriptType { MultisigRedeemScriptType_pubkeys_t pubkeys[5]; pb_size_t signatures_count; MultisigRedeemScriptType_signatures_t signatures[4]; + bool has_m; + uint32_t m; } MultisigRedeemScriptType; typedef PB_BYTES_ARRAY_T(520) TxOutputBinType_script_pubkey_t; @@ -191,7 +193,7 @@ extern const InputScriptType TxInputType_script_type_default; /* Initializer values for message structs */ #define HDNodeType_init_default {0, 0, 0, {0, {0}}, false, {0, {0}}, false, {0, {0}}} #define CoinType_init_default {false, "", false, "", false, 0, false, 0} -#define MultisigRedeemScriptType_init_default {0, {{0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}}, 0, {{0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}}} +#define MultisigRedeemScriptType_init_default {0, {{0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}}, 0, {{0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}}, false, 0} #define TxInputType_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}, {0, {0}}, 0, false, {0, {0}}, false, 4294967295u, false, InputScriptType_SPENDADDRESS, false, MultisigRedeemScriptType_init_default} #define TxOutputType_init_default {false, "", 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, (OutputScriptType)0} #define TxOutputBinType_init_default {0, {0, {0}}} @@ -200,7 +202,7 @@ extern const InputScriptType TxInputType_script_type_default; #define TxRequestSerializedType_init_default {false, 0, false, {0, {0}}, false, {0, {0}}} #define HDNodeType_init_zero {0, 0, 0, {0, {0}}, false, {0, {0}}, false, {0, {0}}} #define CoinType_init_zero {false, "", false, "", false, 0, false, 0} -#define MultisigRedeemScriptType_init_zero {0, {{0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}}, 0, {{0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}}} +#define MultisigRedeemScriptType_init_zero {0, {{0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}}, 0, {{0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}}, false, 0} #define TxInputType_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}, {0, {0}}, 0, false, {0, {0}}, false, 0, false, (InputScriptType)0, false, MultisigRedeemScriptType_init_zero} #define TxOutputType_init_zero {false, "", 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, (OutputScriptType)0} #define TxOutputBinType_init_zero {0, {0, {0}}} @@ -221,6 +223,7 @@ extern const InputScriptType TxInputType_script_type_default; #define HDNodeType_public_key_tag 6 #define MultisigRedeemScriptType_pubkeys_tag 1 #define MultisigRedeemScriptType_signatures_tag 2 +#define MultisigRedeemScriptType_m_tag 3 #define TxOutputBinType_amount_tag 1 #define TxOutputBinType_script_pubkey_tag 2 #define TxOutputType_address_tag 1 @@ -254,7 +257,7 @@ extern const InputScriptType TxInputType_script_type_default; /* Struct field encoding specification for nanopb */ extern const pb_field_t HDNodeType_fields[7]; extern const pb_field_t CoinType_fields[5]; -extern const pb_field_t MultisigRedeemScriptType_fields[3]; +extern const pb_field_t MultisigRedeemScriptType_fields[4]; extern const pb_field_t TxInputType_fields[8]; extern const pb_field_t TxOutputType_fields[5]; extern const pb_field_t TxOutputBinType_fields[3]; @@ -265,11 +268,11 @@ extern const pb_field_t TxRequestSerializedType_fields[4]; /* Maximum encoded size of messages (where known) */ #define HDNodeType_size 121 #define CoinType_size 47 -#define MultisigRedeemScriptType_size 663 -#define TxInputType_size 1289 +#define MultisigRedeemScriptType_size 669 +#define TxInputType_size 1295 #define TxOutputType_size 102 #define TxOutputBinType_size 534 -#define TransactionType_size 1957 +#define TransactionType_size 1963 #define TxRequestDetailsType_size 40 #define TxRequestSerializedType_size 1115 diff --git a/firmware/signing.c b/firmware/signing.c index 4c21cc4b3..4193e96b9 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -282,8 +282,12 @@ void signing_txack(TransactionType *tx) for (k = 0; k < tx->inputs[0].address_n_count; k++) { hdnode_private_ckd(&node, tx->inputs[0].address_n[k]); } - ecdsa_get_pubkeyhash(node.public_key, hash); - tx->inputs[0].script_sig.size = compile_script_sig(coin->address_type, hash, tx->inputs[0].script_sig.bytes); + if (tx->inputs[0].has_multisig) { + tx->inputs[0].script_sig.size = compile_script_multisig(&(tx->inputs[0].multisig), tx->inputs[0].script_sig.bytes); + } else { + ecdsa_get_pubkeyhash(node.public_key, hash); + tx->inputs[0].script_sig.size = compile_script_sig(coin->address_type, hash, tx->inputs[0].script_sig.bytes); + } if (tx->inputs[0].script_sig.size == 0) { fsm_sendFailure(FailureType_Failure_Other, "Failed to compile input"); signing_abort(); @@ -362,7 +366,26 @@ void signing_txack(TransactionType *tx) resp.serialized.has_serialized_tx = true; ecdsa_sign_digest(privkey, hash, sig); resp.serialized.signature.size = ecdsa_sig_to_der(sig, resp.serialized.signature.bytes); - input.script_sig.size = serialize_script_sig(resp.serialized.signature.bytes, resp.serialized.signature.size, pubkey, 33, input.script_sig.bytes); + if (input.has_multisig) { + // 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) { + fsm_sendFailure(FailureType_Failure_Other, "Pubkey not found in multisig script"); + signing_abort(); + return; + } + memcpy(input.multisig.signatures[pubkey_idx].bytes, resp.serialized.signature.bytes, resp.serialized.signature.size); + input.multisig.signatures[pubkey_idx].size = resp.serialized.signature.size; + input.script_sig.size = serialize_script_multisig(&(input.multisig), input.script_sig.bytes); + } else { + input.script_sig.size = serialize_script_sig(resp.serialized.signature.bytes, resp.serialized.signature.size, pubkey, 33, input.script_sig.bytes); + } resp.serialized.serialized_tx.size = tx_serialize_input(&to, input.prev_hash.bytes, input.prev_index, input.script_sig.bytes, input.script_sig.size, input.sequence, resp.serialized.serialized_tx.bytes); if (idx1i < inputs_count - 1) { idx1i++; diff --git a/firmware/transaction.c b/firmware/transaction.c index fc3d6e144..958e740f1 100644 --- a/firmware/transaction.c +++ b/firmware/transaction.c @@ -130,7 +130,37 @@ uint32_t compile_script_sig(uint8_t address_type, const uint8_t *pubkeyhash, uin } } -int serialize_script_sig(uint8_t *signature, uint32_t signature_len, uint8_t *pubkey, uint32_t pubkey_len, uint8_t *out) +// if out == NULL just compute the length +uint32_t compile_script_multisig(const MultisigRedeemScriptType *multisig, uint8_t *out) +{ + 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; + uint32_t i, r = 0; + if (out) { + out[r] = 0x50 + m; r++; + for (i = 0; i < n; i++) { + r += op_push(multisig->pubkeys[i].size, out + r); + memcpy(out + r, multisig->pubkeys[i].bytes, multisig->pubkeys[i].size); r += multisig->pubkeys[i].size; + } + out[r] = 0x50 + n; r++; + out[r] = 0xAE; r++; // OP_CHECKMULTISIG + } else { + r++; + for (i = 0; i < n; i++) { + uint8_t dummy[8]; + r += op_push(multisig->pubkeys[i].size, dummy); + r += multisig->pubkeys[i].size; + } + r++; + r++; + } + return r; +} + +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; r += op_push(signature_len + 1, out + r); @@ -141,6 +171,24 @@ int serialize_script_sig(uint8_t *signature, uint32_t signature_len, uint8_t *pu return r; } +uint32_t serialize_script_multisig(const MultisigRedeemScriptType *multisig, uint8_t *out) +{ + uint32_t i, r = 0; + out[r] = 0x00; r++; + for (i = 0; i < multisig->signatures_count; i++) { + if (multisig->signatures[i].size == 0) { + continue; + } + r += op_push(multisig->signatures[i].size + 1, out + r); + memcpy(out + r, multisig->signatures[i].bytes, multisig->signatures[i].size); r += multisig->signatures[i].size; + out[r] = 0x01; r++; + } + uint32_t script_len = compile_script_multisig(multisig, 0); + r += op_push(script_len, out + r); + r += compile_script_multisig(multisig, out + r); + return r; +} + // tx methods uint32_t tx_serialize_header(TxStruct *tx, uint8_t *out) diff --git a/firmware/transaction.h b/firmware/transaction.h index 2a2e09849..eca3c16fa 100644 --- a/firmware/transaction.h +++ b/firmware/transaction.h @@ -42,7 +42,9 @@ typedef struct { } TxStruct; uint32_t compile_script_sig(uint8_t address_type, const uint8_t *pubkeyhash, uint8_t *out); -int serialize_script_sig(uint8_t *signature, uint32_t signature_len, uint8_t *pubkey, uint32_t pubkey_len, uint8_t *out); +uint32_t compile_script_multisig(const MultisigRedeemScriptType *multisig, uint8_t *out); +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); uint32_t tx_serialize_input(TxStruct *tx, uint8_t *prev_hash, uint32_t prev_index, uint8_t *script_sig, uint32_t script_sig_len, uint32_t sequence, uint8_t *out); uint32_t tx_serialize_output(TxStruct *tx, uint64_t amount, uint8_t *script_pubkey, uint32_t script_pubkey_len, uint8_t *out); diff --git a/trezor-common b/trezor-common index e675d5fd7..3670728fd 160000 --- a/trezor-common +++ b/trezor-common @@ -1 +1 @@ -Subproject commit e675d5fd7684e437bb35fb24330234d43ee2e4db +Subproject commit 3670728fd205ccfc75571ec3a49bfc7d07d11b17