1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-05-20 15:58:46 +00:00

feat(legacy): Support serialize option in SignTx.

This commit is contained in:
Andrew Kozlik 2022-12-13 09:41:30 +01:00 committed by matejcik
parent acec852aa4
commit 11109b4e46
2 changed files with 162 additions and 111 deletions

View File

@ -0,0 +1 @@
Implement `serialize` option in SignTx.

View File

@ -38,6 +38,7 @@
static uint32_t change_count; static uint32_t change_count;
static const CoinInfo *coin; static const CoinInfo *coin;
static AmountUnit amount_unit; static AmountUnit amount_unit;
static bool serialize;
static CONFIDENTIAL HDNode root; static CONFIDENTIAL HDNode root;
static CONFIDENTIAL HDNode node; static CONFIDENTIAL HDNode node;
static bool signing = false; static bool signing = false;
@ -705,12 +706,60 @@ void phase1_request_orig_input(void) {
} }
} }
void phase2_request_next_input(void) { void phase2_request_next_output(bool first) {
if (idx1 == info.next_legacy_input) { if (first) {
idx2 = 0; if (serialize) {
send_req_4_input(); idx1 = 0;
send_req_5_output();
return;
}
} else if (idx1 < info.outputs_count - 1) {
idx1++;
send_req_5_output();
return;
}
// Output serialization is finished. Generate witnesses.
if (to.is_segwit) {
idx1 = 0;
send_req_segwit_witness();
} else { } else {
send_req_nonlegacy_input(); send_req_finished();
signing_abort();
}
}
void phase2_request_next_input(bool first) {
if (first) {
idx1 = 0;
} else if (idx1 < info.inputs_count - 1) {
idx1++;
} else {
// Input processing is finished. Serialize outputs.
phase2_request_next_output(true);
return;
}
if (serialize || coin->force_bip143 || coin->overwintered) {
// We are processing all inputs.
if (idx1 == info.next_legacy_input) {
idx2 = 0;
send_req_4_input();
} else {
send_req_nonlegacy_input();
}
} else {
// We are only signing legacy inputs.
if (info.next_legacy_input == 0xffffffff || idx1 > info.next_legacy_input) {
// There are no legacy inputs or no more legacy inputs, so serialize
// outputs.
phase2_request_next_output(true);
} else {
// Sign next legacy input.
idx1 = info.next_legacy_input;
idx2 = 0;
send_req_4_input();
}
} }
} }
@ -739,8 +788,7 @@ void phase2_request_orig_input(void) {
return; return;
} }
idx1 = 0; phase2_request_next_input(true);
phase2_request_next_input();
} }
} }
@ -1035,6 +1083,7 @@ void signing_init(const SignTx *msg, const CoinInfo *_coin,
const HDNode *_root) { const HDNode *_root) {
coin = _coin; coin = _coin;
amount_unit = msg->has_amount_unit ? msg->amount_unit : AmountUnit_BITCOIN; amount_unit = msg->has_amount_unit ? msg->amount_unit : AmountUnit_BITCOIN;
serialize = msg->has_serialize ? msg->serialize : true;
memcpy(&root, _root, sizeof(HDNode)); memcpy(&root, _root, sizeof(HDNode));
if (msg->inputs_count > MAX_INPUTS_COUNT) { if (msg->inputs_count > MAX_INPUTS_COUNT) {
@ -1543,11 +1592,13 @@ static bool signing_add_input(TxInputType *txinput) {
#if !BITCOIN_ONLY #if !BITCOIN_ONLY
if (coin->decred) { if (coin->decred) {
// serialize Decred prefix in Phase 1 if (serialize) {
resp.has_serialized = true; // serialize Decred prefix in Phase 1
resp.serialized.has_serialized_tx = true; resp.has_serialized = true;
resp.serialized.serialized_tx.size = resp.serialized.has_serialized_tx = true;
tx_serialize_input(&to, txinput, resp.serialized.serialized_tx.bytes); resp.serialized.serialized_tx.size =
tx_serialize_input(&to, txinput, resp.serialized.serialized_tx.bytes);
}
// compute Decred hashPrefix // compute Decred hashPrefix
tx_serialize_input_hash(&ti, txinput); tx_serialize_input_hash(&ti, txinput);
@ -1592,7 +1643,7 @@ static bool signing_check_prevtx_hash(void) {
phase2_request_orig_input(); phase2_request_orig_input();
} else { } else {
// Proceed to transaction signing. // Proceed to transaction signing.
phase2_request_next_input(); phase2_request_next_input(true);
} }
} }
} }
@ -1678,11 +1729,13 @@ static bool signing_add_output(TxOutputType *txoutput) {
} }
#if !BITCOIN_ONLY #if !BITCOIN_ONLY
if (coin->decred) { if (coin->decred) {
// serialize Decred prefix in Phase 1 if (serialize) {
resp.has_serialized = true; // serialize Decred prefix in Phase 1
resp.serialized.has_serialized_tx = true; resp.has_serialized = true;
resp.serialized.serialized_tx.size = tx_serialize_output( resp.serialized.has_serialized_tx = true;
&to, &bin_output, resp.serialized.serialized_tx.bytes); resp.serialized.serialized_tx.size = tx_serialize_output(
&to, &bin_output, resp.serialized.serialized_tx.bytes);
}
// compute Decred hashPrefix // compute Decred hashPrefix
tx_serialize_output_hash(&ti, &bin_output); tx_serialize_output_hash(&ti, &bin_output);
@ -2458,12 +2511,12 @@ static void phase1_finish(void) {
phase2_request_orig_input(); phase2_request_orig_input();
} else { } else {
// Proceed directly to transaction signing. // Proceed directly to transaction signing.
phase2_request_next_input(); phase2_request_next_input(true);
} }
#if !BITCOIN_ONLY #if !BITCOIN_ONLY
} else if (coin->overwintered && info.version == 5) { } else if (coin->overwintered && info.version == 5) {
// ZIP-244 transactions are treated same as Taproot. // ZIP-244 transactions are treated same as Taproot.
phase2_request_next_input(); phase2_request_next_input(true);
#endif #endif
} else { } else {
// There are internal non-Taproot inputs. We need to verify all inputs, // There are internal non-Taproot inputs. We need to verify all inputs,
@ -2542,10 +2595,10 @@ static void signing_hash_decred(const TxInputType *txinput,
static bool signing_sign_ecdsa(TxInputType *txinput, const uint8_t *private_key, static bool signing_sign_ecdsa(TxInputType *txinput, const uint8_t *private_key,
const uint8_t *public_key, const uint8_t *hash) { const uint8_t *public_key, const uint8_t *hash) {
resp.has_serialized = true;
resp.serialized.has_signature_index = true; resp.serialized.has_signature_index = true;
resp.serialized.signature_index = idx1; resp.serialized.signature_index = idx1;
resp.serialized.has_signature = true; resp.serialized.has_signature = true;
resp.serialized.has_serialized_tx = true;
int ret = 0; int ret = 0;
#ifdef USE_SECP256K1_ZKP_ECDSA #ifdef USE_SECP256K1_ZKP_ECDSA
@ -2604,7 +2657,6 @@ static bool signing_sign_bip340(const uint8_t *private_key,
resp.serialized.has_signature_index = true; resp.serialized.has_signature_index = true;
resp.serialized.signature_index = idx1; resp.serialized.signature_index = idx1;
resp.serialized.has_signature = true; resp.serialized.has_signature = true;
resp.serialized.has_serialized_tx = true;
resp.serialized.signature.size = 64; resp.serialized.signature.size = 64;
uint8_t output_private_key[32] = {0}; uint8_t output_private_key[32] = {0};
@ -2631,10 +2683,13 @@ static bool signing_sign_legacy_input(void) {
// Compute the digest and generate signature. // Compute the digest and generate signature.
uint8_t hash[32] = {0}; uint8_t hash[32] = {0};
tx_hash_final(&ti, hash, false); tx_hash_final(&ti, hash, false);
resp.has_serialized = true;
if (!signing_sign_ecdsa(&input, privkey, pubkey, hash)) return false; if (!signing_sign_ecdsa(&input, privkey, pubkey, hash)) return false;
resp.serialized.serialized_tx.size = if (serialize) {
tx_serialize_input(&to, &input, resp.serialized.serialized_tx.bytes); resp.has_serialized = true;
resp.serialized.has_serialized_tx = true;
resp.serialized.serialized_tx.size =
tx_serialize_input(&to, &input, resp.serialized.serialized_tx.bytes);
}
return true; return true;
} }
@ -2658,13 +2713,17 @@ static bool signing_sign_segwit_input(TxInputType *txinput) {
return false; return false;
} }
uint32_t r = 0; if (serialize) {
// write witness (number of stack items followed by signature) resp.has_serialized = true;
r += ser_length(1, resp.serialized.serialized_tx.bytes + r); resp.serialized.has_serialized_tx = true;
r += tx_serialize_script(resp.serialized.signature.size, uint32_t r = 0;
resp.serialized.signature.bytes, // write witness (number of stack items followed by signature)
resp.serialized.serialized_tx.bytes + r); r += ser_length(1, resp.serialized.serialized_tx.bytes + r);
resp.serialized.serialized_tx.size = 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 || } else if (txinput->script_type == InputScriptType_SPENDP2SHWITNESS ||
txinput->script_type == InputScriptType_SPENDWITNESS) { txinput->script_type == InputScriptType_SPENDWITNESS) {
if (!txinput->has_amount) { if (!txinput->has_amount) {
@ -2688,66 +2747,72 @@ static bool signing_sign_segwit_input(TxInputType *txinput) {
signing_hash_bip143(&info, txinput, hash); signing_hash_bip143(&info, txinput, hash);
resp.has_serialized = true;
if (!signing_sign_ecdsa(txinput, node.private_key, node.public_key, hash)) if (!signing_sign_ecdsa(txinput, node.private_key, node.public_key, hash))
return false; return false;
uint8_t sighash = signing_hash_type(txinput) & 0xff; if (serialize) {
if (txinput->has_multisig) { resp.has_serialized = true;
uint32_t r = 1; // skip number of items (filled in later) resp.serialized.has_serialized_tx = true;
resp.serialized.serialized_tx.bytes[r] = 0; uint8_t sighash = signing_hash_type(txinput) & 0xff;
r++; if (txinput->has_multisig) {
int nwitnesses = 2; uint32_t r = 1; // skip number of items (filled in later)
for (uint32_t i = 0; i < txinput->multisig.signatures_count; i++) { resp.serialized.serialized_tx.bytes[r] = 0;
if (txinput->multisig.signatures[i].size == 0) { r++;
continue; int nwitnesses = 2;
} for (uint32_t i = 0; i < txinput->multisig.signatures_count; i++) {
nwitnesses++; if (txinput->multisig.signatures[i].size == 0) {
txinput->multisig.signatures[i] continue;
.bytes[txinput->multisig.signatures[i].size] = sighash; }
r += tx_serialize_script(txinput->multisig.signatures[i].size + 1, nwitnesses++;
txinput->multisig.signatures[i].bytes, txinput->multisig.signatures[i]
resp.serialized.serialized_tx.bytes + r); .bytes[txinput->multisig.signatures[i].size] = sighash;
} r += tx_serialize_script(txinput->multisig.signatures[i].size + 1,
uint32_t script_len = txinput->multisig.signatures[i].bytes,
compile_script_multisig(coin, &txinput->multisig, 0);
r += ser_length(script_len, resp.serialized.serialized_tx.bytes + r);
r += compile_script_multisig(coin, &txinput->multisig,
resp.serialized.serialized_tx.bytes + r); resp.serialized.serialized_tx.bytes + r);
resp.serialized.serialized_tx.bytes[0] = nwitnesses; }
resp.serialized.serialized_tx.size = r; uint32_t script_len =
} else { // single signature compile_script_multisig(coin, &txinput->multisig, 0);
uint32_t r = 0; r += ser_length(script_len, resp.serialized.serialized_tx.bytes + r);
r += ser_length(2, resp.serialized.serialized_tx.bytes + r); r += compile_script_multisig(coin, &txinput->multisig,
resp.serialized.signature.bytes[resp.serialized.signature.size] = sighash; resp.serialized.serialized_tx.bytes + r);
r += tx_serialize_script(resp.serialized.signature.size + 1, resp.serialized.serialized_tx.bytes[0] = nwitnesses;
resp.serialized.signature.bytes, resp.serialized.serialized_tx.size = r;
resp.serialized.serialized_tx.bytes + r); } else { // single signature
r += tx_serialize_script(33, node.public_key, uint32_t r = 0;
resp.serialized.serialized_tx.bytes + r); r += ser_length(2, resp.serialized.serialized_tx.bytes + r);
resp.serialized.serialized_tx.size = r; resp.serialized.signature.bytes[resp.serialized.signature.size] =
sighash;
r += tx_serialize_script(resp.serialized.signature.size + 1,
resp.serialized.signature.bytes,
resp.serialized.serialized_tx.bytes + r);
r += tx_serialize_script(33, node.public_key,
resp.serialized.serialized_tx.bytes + r);
resp.serialized.serialized_tx.size = r;
}
} }
} else { } else {
// no signature to be generated if (serialize) {
resp.has_serialized = true; // no signature to be generated
resp.serialized.has_signature_index = false; resp.has_serialized = true;
resp.serialized.has_signature = false; resp.serialized.has_signature_index = false;
resp.serialized.has_serialized_tx = true; resp.serialized.has_signature = false;
if (txinput->script_type == InputScriptType_EXTERNAL && resp.serialized.has_serialized_tx = true;
txinput->has_witness) { if (txinput->script_type == InputScriptType_EXTERNAL &&
// fill in the provided witness txinput->has_witness) {
memcpy(resp.serialized.serialized_tx.bytes, txinput->witness.bytes, // fill in the provided witness
txinput->witness.size); memcpy(resp.serialized.serialized_tx.bytes, txinput->witness.bytes,
resp.serialized.serialized_tx.size = txinput->witness.size; txinput->witness.size);
} else { resp.serialized.serialized_tx.size = txinput->witness.size;
// empty witness } else {
resp.serialized.serialized_tx.bytes[0] = 0; // empty witness
resp.serialized.serialized_tx.size = 1; resp.serialized.serialized_tx.bytes[0] = 0;
resp.serialized.serialized_tx.size = 1;
}
} }
} }
// if last witness add tx footer // if last witness add tx footer
if (idx1 == info.inputs_count - 1) { if (serialize && idx1 == info.inputs_count - 1) {
uint32_t r = resp.serialized.serialized_tx.size; uint32_t r = resp.serialized.serialized_tx.size;
r += tx_serialize_footer(&to, resp.serialized.serialized_tx.bytes + r); r += tx_serialize_footer(&to, resp.serialized.serialized_tx.bytes + r);
resp.serialized.serialized_tx.size = r; resp.serialized.serialized_tx.size = r;
@ -2761,11 +2826,14 @@ static bool signing_sign_decred_input(TxInputType *txinput) {
uint8_t hash[32] = {}, hash_witness[32] = {}; uint8_t hash[32] = {}, hash_witness[32] = {};
tx_hash_final(&ti, hash_witness, false); tx_hash_final(&ti, hash_witness, false);
signing_hash_decred(txinput, hash_witness, hash); signing_hash_decred(txinput, hash_witness, hash);
resp.has_serialized = true;
if (!signing_sign_ecdsa(txinput, node.private_key, node.public_key, hash)) if (!signing_sign_ecdsa(txinput, node.private_key, node.public_key, hash))
return false; return false;
resp.serialized.serialized_tx.size = tx_serialize_decred_witness( if (serialize) {
&to, txinput, resp.serialized.serialized_tx.bytes); resp.has_serialized = true;
resp.serialized.has_serialized_tx = true;
resp.serialized.serialized_tx.size = tx_serialize_decred_witness(
&to, txinput, resp.serialized.serialized_tx.bytes);
}
return true; return true;
} }
@ -3361,13 +3429,7 @@ void signing_txack(TransactionType *tx) {
progress = 500 + ((signatures * progress_step) >> PROGRESS_PRECISION); progress = 500 + ((signatures * progress_step) >> PROGRESS_PRECISION);
layoutProgress(_("Signing transaction"), progress); layoutProgress(_("Signing transaction"), progress);
update_ctr = 0; update_ctr = 0;
if (idx1 < info.inputs_count - 1) { phase2_request_next_input(false);
idx1++;
phase2_request_next_input();
} else {
idx1 = 0;
send_req_5_output();
}
} }
return; return;
@ -3387,7 +3449,6 @@ void signing_txack(TransactionType *tx) {
resp.has_serialized = true; resp.has_serialized = true;
resp.serialized.has_signature_index = false; resp.serialized.has_signature_index = false;
resp.serialized.has_signature = false; resp.serialized.has_signature = false;
resp.serialized.has_serialized_tx = true;
if (tx->inputs[0].script_type == InputScriptType_SPENDMULTISIG || if (tx->inputs[0].script_type == InputScriptType_SPENDMULTISIG ||
tx->inputs[0].script_type == InputScriptType_SPENDADDRESS) { tx->inputs[0].script_type == InputScriptType_SPENDADDRESS) {
if (!(coin->force_bip143 || coin->overwintered) || taproot_only) { if (!(coin->force_bip143 || coin->overwintered) || taproot_only) {
@ -3481,15 +3542,13 @@ void signing_txack(TransactionType *tx) {
// direct witness scripts require zero scriptSig // direct witness scripts require zero scriptSig
tx->inputs[0].script_sig.size = 0; tx->inputs[0].script_sig.size = 0;
} }
resp.serialized.serialized_tx.size = tx_serialize_input( if (serialize) {
&to, &tx->inputs[0], resp.serialized.serialized_tx.bytes); resp.has_serialized = true;
if (idx1 < info.inputs_count - 1) { resp.serialized.has_serialized_tx = true;
idx1++; resp.serialized.serialized_tx.size = tx_serialize_input(
phase2_request_next_input(); &to, &tx->inputs[0], resp.serialized.serialized_tx.bytes);
} else {
idx1 = 0;
send_req_5_output();
} }
phase2_request_next_input(false);
return; return;
case STAGE_REQUEST_5_OUTPUT: case STAGE_REQUEST_5_OUTPUT:
@ -3507,16 +3566,7 @@ void signing_txack(TransactionType *tx) {
resp.serialized.has_serialized_tx = true; resp.serialized.has_serialized_tx = true;
resp.serialized.serialized_tx.size = tx_serialize_output( resp.serialized.serialized_tx.size = tx_serialize_output(
&to, &bin_output, resp.serialized.serialized_tx.bytes); &to, &bin_output, resp.serialized.serialized_tx.bytes);
if (idx1 < info.outputs_count - 1) { phase2_request_next_output(false);
idx1++;
send_req_5_output();
} else if (to.is_segwit) {
idx1 = 0;
send_req_segwit_witness();
} else {
send_req_finished();
signing_abort();
}
return; return;
case STAGE_REQUEST_SEGWIT_WITNESS: case STAGE_REQUEST_SEGWIT_WITNESS: