From c9d1ff93f5a8fe73b5cb904a32348df63cfd24b8 Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Fri, 12 Nov 2021 11:17:06 +0100 Subject: [PATCH] feat(legacy): Implement Taproot signing. --- legacy/firmware/.changelog.d/1656.added.2 | 1 + legacy/firmware/signing.c | 58 ++++++++++++++++--- tests/device_tests/test_msg_signtx_taproot.py | 2 +- 3 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 legacy/firmware/.changelog.d/1656.added.2 diff --git a/legacy/firmware/.changelog.d/1656.added.2 b/legacy/firmware/.changelog.d/1656.added.2 new file mode 100644 index 000000000..aae521d9f --- /dev/null +++ b/legacy/firmware/.changelog.d/1656.added.2 @@ -0,0 +1 @@ +Support spending from Taproot UTXOs. diff --git a/legacy/firmware/signing.c b/legacy/firmware/signing.c index e8e2d8fa8..a81280588 100644 --- a/legacy/firmware/signing.c +++ b/legacy/firmware/signing.c @@ -30,6 +30,7 @@ #include "protect.h" #include "secp256k1.h" #include "transaction.h" +#include "zkp_bip340.h" #ifdef USE_SECP256K1_ZKP_ECDSA #include "zkp_ecdsa.h" #endif @@ -2038,8 +2039,8 @@ static void signing_hash_decred(const TxInputType *txinput, } #endif -static bool signing_sign_hash(TxInputType *txinput, const uint8_t *private_key, - const uint8_t *public_key, const uint8_t *hash) { +static bool signing_sign_ecdsa(TxInputType *txinput, const uint8_t *private_key, + const uint8_t *public_key, const uint8_t *hash) { resp.serialized.has_signature_index = true; resp.serialized.signature_index = idx1; resp.serialized.has_signature = true; @@ -2096,6 +2097,31 @@ static bool signing_sign_hash(TxInputType *txinput, const uint8_t *private_key, return true; } +static bool signing_sign_bip340(const uint8_t *private_key, + const uint8_t *hash) { + resp.has_serialized = true; + resp.serialized.has_signature_index = true; + resp.serialized.signature_index = idx1; + resp.serialized.has_signature = true; + resp.serialized.has_serialized_tx = true; + resp.serialized.signature.size = 64; + + uint8_t output_private_key[32] = {0}; + bool ret = (zkp_bip340_tweak_private_key(private_key, NULL, + output_private_key) == 0); + ret = ret && + (zkp_bip340_sign_digest(output_private_key, hash, + resp.serialized.signature.bytes, NULL) == 0); + memzero(output_private_key, sizeof(output_private_key)); + + if (!ret) { + fsm_sendFailure(FailureType_Failure_ProcessError, _("Signing failed")); + signing_abort(); + } + + return ret; +} + static bool signing_sign_input(void) { uint8_t hash[32] = {0}; hasher_Final(&hasher_check, hash); @@ -2110,7 +2136,7 @@ static bool signing_sign_input(void) { hasher_Update(&ti.hasher, (const uint8_t *)&hash_type, 4); tx_hash_final(&ti, hash, false); resp.has_serialized = true; - if (!signing_sign_hash(&input, privkey, pubkey, hash)) return false; + if (!signing_sign_ecdsa(&input, privkey, pubkey, hash)) return false; resp.serialized.serialized_tx.size = tx_serialize_input(&to, &input, resp.serialized.serialized_tx.bytes); return true; @@ -2120,7 +2146,23 @@ static bool signing_sign_segwit_input(TxInputType *txinput) { // idx1: index to sign uint8_t hash[32] = {0}; - if (is_segwit_input_script_type(txinput)) { + if (txinput->script_type == InputScriptType_SPENDTAPROOT) { + signing_hash_bip341(&info, idx1, signing_hash_type(txinput), hash); + + if (!tx_info_check_input(&info, txinput) || !derive_node(txinput) || + !signing_sign_bip340(node.private_key, hash)) { + return false; + } + + uint32_t r = 0; + // write witness (number of stack items followed by signature) + r += ser_length(1, resp.serialized.serialized_tx.bytes + r); + r += tx_serialize_script(resp.serialized.signature.size, + resp.serialized.signature.bytes, + resp.serialized.serialized_tx.bytes + r); + resp.serialized.serialized_tx.size = r; + } else if (txinput->script_type == InputScriptType_SPENDP2SHWITNESS || + txinput->script_type == InputScriptType_SPENDWITNESS) { if (!txinput->has_amount) { fsm_sendFailure(FailureType_Failure_DataError, _("Segwit input without amount")); @@ -2136,7 +2178,7 @@ static bool signing_sign_segwit_input(TxInputType *txinput) { signing_hash_bip143(&info, txinput, hash); resp.has_serialized = true; - if (!signing_sign_hash(txinput, node.private_key, node.public_key, hash)) + if (!signing_sign_ecdsa(txinput, node.private_key, node.public_key, hash)) return false; uint8_t sighash = signing_hash_type(txinput) & 0xff; @@ -2199,7 +2241,7 @@ static bool signing_sign_decred_input(TxInputType *txinput) { tx_hash_final(&ti, hash_witness, false); signing_hash_decred(txinput, hash_witness, hash); resp.has_serialized = true; - if (!signing_sign_hash(txinput, node.private_key, node.public_key, hash)) + if (!signing_sign_ecdsa(txinput, node.private_key, node.public_key, hash)) return false; resp.serialized.serialized_tx.size = tx_serialize_decred_witness( &to, txinput, resp.serialized.serialized_tx.bytes); @@ -2733,8 +2775,8 @@ void signing_txack(TransactionType *tx) { { signing_hash_bip143(&info, &tx->inputs[0], hash); } - if (!signing_sign_hash(&tx->inputs[0], node.private_key, - node.public_key, hash)) + if (!signing_sign_ecdsa(&tx->inputs[0], node.private_key, + node.public_key, hash)) return; // since this took a longer time, update progress signatures++; diff --git a/tests/device_tests/test_msg_signtx_taproot.py b/tests/device_tests/test_msg_signtx_taproot.py index d8800a439..79ac239e1 100644 --- a/tests/device_tests/test_msg_signtx_taproot.py +++ b/tests/device_tests/test_msg_signtx_taproot.py @@ -49,7 +49,6 @@ TXHASH_65b811 = bytes.fromhex( ) -@pytest.mark.skip_t1 class TestMsgSigntxTaproot: def test_send_p2tr(self, client): inp1 = messages.TxInputType( @@ -269,6 +268,7 @@ class TestMsgSigntxTaproot: request_input(0), request_input(1), request_input(2), + (client.features.model == "1", request_input(3)), request_finished(), ] )