mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-18 20:38:10 +00:00
refactor(legacy): Transaction checksum in Bitcoin signing.
This commit is contained in:
parent
d16b44bad6
commit
706b74aaeb
@ -94,16 +94,15 @@ static uint8_t sig[64]; // Used in Phase 1 to store signature of original tx
|
|||||||
#if !BITCOIN_ONLY
|
#if !BITCOIN_ONLY
|
||||||
static uint8_t decred_hash_prefix[32];
|
static uint8_t decred_hash_prefix[32];
|
||||||
#endif
|
#endif
|
||||||
static uint8_t hash_inputs_check[32];
|
|
||||||
static uint64_t total_in, total_out, change_out;
|
static uint64_t total_in, total_out, change_out;
|
||||||
static uint64_t orig_total_in, orig_total_out, orig_change_out;
|
static uint64_t orig_total_in, orig_total_out, orig_change_out;
|
||||||
static uint32_t next_legacy_input;
|
|
||||||
static uint32_t progress, progress_step, progress_meta_step;
|
static uint32_t progress, progress_step, progress_meta_step;
|
||||||
static uint32_t tx_weight;
|
static uint32_t tx_weight;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t inputs_count;
|
uint32_t inputs_count;
|
||||||
uint32_t outputs_count;
|
uint32_t outputs_count;
|
||||||
|
uint32_t next_legacy_input;
|
||||||
uint32_t min_sequence;
|
uint32_t min_sequence;
|
||||||
bool multisig_fp_set;
|
bool multisig_fp_set;
|
||||||
bool multisig_fp_mismatch;
|
bool multisig_fp_mismatch;
|
||||||
@ -119,11 +118,13 @@ typedef struct {
|
|||||||
uint32_t branch_id;
|
uint32_t branch_id;
|
||||||
uint8_t hash_header[32];
|
uint8_t hash_header[32];
|
||||||
#endif
|
#endif
|
||||||
|
Hasher hasher_check;
|
||||||
Hasher hasher_prevouts;
|
Hasher hasher_prevouts;
|
||||||
Hasher hasher_amounts;
|
Hasher hasher_amounts;
|
||||||
Hasher hasher_scriptpubkeys;
|
Hasher hasher_scriptpubkeys;
|
||||||
Hasher hasher_sequences;
|
Hasher hasher_sequences;
|
||||||
Hasher hasher_outputs;
|
Hasher hasher_outputs;
|
||||||
|
uint8_t hash_inputs_check[32];
|
||||||
uint8_t hash_prevouts[32];
|
uint8_t hash_prevouts[32];
|
||||||
uint8_t hash_amounts[32];
|
uint8_t hash_amounts[32];
|
||||||
uint8_t hash_scriptpubkeys[32];
|
uint8_t hash_scriptpubkeys[32];
|
||||||
@ -217,6 +218,7 @@ Phase1 - process inputs
|
|||||||
- check previous transactions
|
- check previous transactions
|
||||||
=========================================================
|
=========================================================
|
||||||
|
|
||||||
|
Stage 1: Get inputs and optionally get original inputs.
|
||||||
foreach I (idx1):
|
foreach I (idx1):
|
||||||
Request I STAGE_REQUEST_1_INPUT
|
Request I STAGE_REQUEST_1_INPUT
|
||||||
Add I to segwit sub-hashes
|
Add I to segwit sub-hashes
|
||||||
@ -226,9 +228,11 @@ foreach I (idx1):
|
|||||||
Request input I2 orig_hash, orig_index STAGE_REQUEST_1_ORIG_INPUT
|
Request input I2 orig_hash, orig_index STAGE_REQUEST_1_ORIG_INPUT
|
||||||
Check I matches I2
|
Check I matches I2
|
||||||
Add I2 to original segwit sub-hashes
|
Add I2 to original segwit sub-hashes
|
||||||
|
Add I2 to orig_hash_inputs_check
|
||||||
if (Decred)
|
if (Decred)
|
||||||
Return I
|
Return I
|
||||||
|
|
||||||
|
Stage 2: Get outputs and optionally get original outputs.
|
||||||
foreach O (idx1):
|
foreach O (idx1):
|
||||||
Request O STAGE_REQUEST_2_OUTPUT
|
Request O STAGE_REQUEST_2_OUTPUT
|
||||||
Add O to Decred decred_hash_prefix
|
Add O to Decred decred_hash_prefix
|
||||||
@ -246,8 +250,10 @@ foreach O (idx1):
|
|||||||
Check tx fee
|
Check tx fee
|
||||||
Ask for confirmation
|
Ask for confirmation
|
||||||
|
|
||||||
|
Stage 3: Check transaction.
|
||||||
|
|
||||||
if (taproot_only)
|
if (taproot_only)
|
||||||
Skip to Phase 2.
|
Skip checking of previous transactions.
|
||||||
|
|
||||||
foreach I (idx1):
|
foreach I (idx1):
|
||||||
Request I STAGE_REQUEST_3_INPUT
|
Request I STAGE_REQUEST_3_INPUT
|
||||||
@ -514,7 +520,7 @@ void send_req_4_output(void) {
|
|||||||
msg_write(MessageType_MessageType_TxRequest, &resp);
|
msg_write(MessageType_MessageType_TxRequest, &resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_req_segwit_input(void) {
|
void send_req_nonlegacy_input(void) {
|
||||||
signing_stage = STAGE_REQUEST_NONLEGACY_INPUT;
|
signing_stage = STAGE_REQUEST_NONLEGACY_INPUT;
|
||||||
resp.has_request_type = true;
|
resp.has_request_type = true;
|
||||||
resp.request_type = RequestType_TXINPUT;
|
resp.request_type = RequestType_TXINPUT;
|
||||||
@ -569,7 +575,6 @@ void phase1_request_next_input(void) {
|
|||||||
idx1++;
|
idx1++;
|
||||||
send_req_1_input();
|
send_req_1_input();
|
||||||
} else {
|
} else {
|
||||||
hasher_Final(&hasher_check, hash_inputs_check);
|
|
||||||
idx1 = 0;
|
idx1 = 0;
|
||||||
|
|
||||||
if (is_replacement) {
|
if (is_replacement) {
|
||||||
@ -637,11 +642,11 @@ void phase1_request_orig_input(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void phase2_request_next_input(void) {
|
void phase2_request_next_input(void) {
|
||||||
if (idx1 == next_legacy_input) {
|
if (idx1 == info.next_legacy_input) {
|
||||||
idx2 = 0;
|
idx2 = 0;
|
||||||
send_req_4_input();
|
send_req_4_input();
|
||||||
} else {
|
} else {
|
||||||
send_req_segwit_input();
|
send_req_nonlegacy_input();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -832,6 +837,7 @@ static bool tx_info_init(TxInfo *tx_info, uint32_t inputs_count,
|
|||||||
|
|
||||||
tx_info->inputs_count = inputs_count;
|
tx_info->inputs_count = inputs_count;
|
||||||
tx_info->outputs_count = outputs_count;
|
tx_info->outputs_count = outputs_count;
|
||||||
|
tx_info->next_legacy_input = 0xffffffff;
|
||||||
tx_info->min_sequence = SEQUENCE_FINAL;
|
tx_info->min_sequence = SEQUENCE_FINAL;
|
||||||
tx_info->multisig_fp_set = false;
|
tx_info->multisig_fp_set = false;
|
||||||
tx_info->multisig_fp_mismatch = false;
|
tx_info->multisig_fp_mismatch = false;
|
||||||
@ -892,6 +898,8 @@ static bool tx_info_init(TxInfo *tx_info, uint32_t inputs_count,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
hasher_Init(&tx_info->hasher_check, HASHER_SHA2);
|
||||||
|
|
||||||
#if !BITCOIN_ONLY
|
#if !BITCOIN_ONLY
|
||||||
if (coin->overwintered) {
|
if (coin->overwintered) {
|
||||||
if (tx_info->version == 5) {
|
if (tx_info->version == 5) {
|
||||||
@ -985,8 +993,6 @@ void signing_init(const SignTx *msg, const CoinInfo *_coin,
|
|||||||
// this means 50 % per phase.
|
// this means 50 % per phase.
|
||||||
progress_step = (500 << PROGRESS_PRECISION) / info.inputs_count;
|
progress_step = (500 << PROGRESS_PRECISION) / info.inputs_count;
|
||||||
|
|
||||||
next_legacy_input = 0xffffffff;
|
|
||||||
|
|
||||||
uint32_t branch_id = 0;
|
uint32_t branch_id = 0;
|
||||||
#if !BITCOIN_ONLY
|
#if !BITCOIN_ONLY
|
||||||
branch_id = info.branch_id;
|
branch_id = info.branch_id;
|
||||||
@ -1348,6 +1354,7 @@ static void txinfo_fill_zip244_header_hash(TxInfo *tx_info) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void tx_info_finish(TxInfo *tx_info) {
|
static void tx_info_finish(TxInfo *tx_info) {
|
||||||
|
hasher_Final(&tx_info->hasher_check, tx_info->hash_inputs_check);
|
||||||
hasher_Final(&tx_info->hasher_prevouts, tx_info->hash_prevouts);
|
hasher_Final(&tx_info->hasher_prevouts, tx_info->hash_prevouts);
|
||||||
hasher_Final(&tx_info->hasher_amounts, tx_info->hash_amounts);
|
hasher_Final(&tx_info->hasher_amounts, tx_info->hash_amounts);
|
||||||
hasher_Final(&tx_info->hasher_scriptpubkeys, tx_info->hash_scriptpubkeys);
|
hasher_Final(&tx_info->hasher_scriptpubkeys, tx_info->hash_scriptpubkeys);
|
||||||
@ -1377,9 +1384,33 @@ static void tx_info_finish(TxInfo *tx_info) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool tx_info_check_inputs_hash(TxInfo *tx_info) {
|
||||||
|
uint8_t hash[32];
|
||||||
|
hasher_Final(&tx_info->hasher_check, hash);
|
||||||
|
if (memcmp(hash, tx_info->hash_inputs_check, 32) != 0) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_DataError,
|
||||||
|
_("Transaction has changed during signing"));
|
||||||
|
signing_abort();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool tx_info_check_outputs_hash(TxInfo *tx_info) {
|
||||||
|
uint8_t hash[32] = {0};
|
||||||
|
hasher_Final(&tx_info->hasher_check, hash);
|
||||||
|
if (memcmp(hash, tx_info->hash_outputs, 32) != 0) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_ProcessError,
|
||||||
|
_("Transaction has changed during signing"));
|
||||||
|
signing_abort();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool signing_add_input(TxInputType *txinput) {
|
static bool signing_add_input(TxInputType *txinput) {
|
||||||
// hash all input data to check it later (relevant for fee computation)
|
// hash all input data to check it later (relevant for fee computation)
|
||||||
if (!tx_input_check_hash(&hasher_check, txinput)) {
|
if (!tx_input_check_hash(&info.hasher_check, txinput)) {
|
||||||
fsm_sendFailure(FailureType_Failure_ProcessError,
|
fsm_sendFailure(FailureType_Failure_ProcessError,
|
||||||
_("Failed to hash input"));
|
_("Failed to hash input"));
|
||||||
signing_abort();
|
signing_abort();
|
||||||
@ -1448,11 +1479,7 @@ static bool signing_check_prevtx_hash(void) {
|
|||||||
idx1++;
|
idx1++;
|
||||||
send_req_3_input();
|
send_req_3_input();
|
||||||
} else {
|
} else {
|
||||||
hasher_Final(&hasher_check, hash);
|
if (!tx_info_check_inputs_hash(&info)) {
|
||||||
if (memcmp(hash, hash_inputs_check, 32) != 0) {
|
|
||||||
fsm_sendFailure(FailureType_Failure_DataError,
|
|
||||||
_("Transaction has changed during signing"));
|
|
||||||
signing_abort();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2396,17 +2423,12 @@ static bool signing_sign_bip340(const uint8_t *private_key,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool signing_sign_legacy_input(void) {
|
static bool signing_sign_legacy_input(void) {
|
||||||
uint8_t hash[32] = {0};
|
// Finalize legacy digest computation.
|
||||||
hasher_Final(&hasher_check, hash);
|
|
||||||
if (memcmp(hash, info.hash_outputs, 32) != 0 || taproot_only) {
|
|
||||||
fsm_sendFailure(FailureType_Failure_ProcessError,
|
|
||||||
_("Transaction has changed during signing"));
|
|
||||||
signing_abort();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t hash_type = signing_hash_type(&input);
|
uint32_t hash_type = signing_hash_type(&input);
|
||||||
hasher_Update(&ti.hasher, (const uint8_t *)&hash_type, 4);
|
hasher_Update(&ti.hasher, (const uint8_t *)&hash_type, 4);
|
||||||
|
|
||||||
|
// Compute the digest and generate signature.
|
||||||
|
uint8_t hash[32] = {0};
|
||||||
tx_hash_final(&ti, hash, false);
|
tx_hash_final(&ti, hash, false);
|
||||||
resp.has_serialized = true;
|
resp.has_serialized = true;
|
||||||
if (!signing_sign_ecdsa(&input, privkey, pubkey, hash)) return false;
|
if (!signing_sign_ecdsa(&input, privkey, pubkey, hash)) return false;
|
||||||
@ -2612,7 +2634,9 @@ void signing_txack(TransactionType *tx) {
|
|||||||
if (!coin->force_bip143 && !coin->overwintered) {
|
if (!coin->force_bip143 && !coin->overwintered) {
|
||||||
// remember the first non-segwit input -- this is the first input
|
// remember the first non-segwit input -- this is the first input
|
||||||
// we need to sign during phase2
|
// we need to sign during phase2
|
||||||
if (next_legacy_input == 0xffffffff) next_legacy_input = idx1;
|
if (info.next_legacy_input == 0xffffffff) {
|
||||||
|
info.next_legacy_input = idx1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (is_segwit_input_script_type(&tx->inputs[0])) {
|
} else if (is_segwit_input_script_type(&tx->inputs[0])) {
|
||||||
if (!to.is_segwit) {
|
if (!to.is_segwit) {
|
||||||
@ -2764,7 +2788,18 @@ void signing_txack(TransactionType *tx) {
|
|||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
case STAGE_REQUEST_3_INPUT:
|
case STAGE_REQUEST_3_INPUT:
|
||||||
if (!signing_validate_input(&tx->inputs[0])) {
|
if (idx1 == 0) {
|
||||||
|
hasher_Reset(&info.hasher_check);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!signing_validate_input(tx->inputs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tx_input_check_hash(&info.hasher_check, tx->inputs)) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_ProcessError,
|
||||||
|
_("Failed to hash input"));
|
||||||
|
signing_abort();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2775,16 +2810,6 @@ void signing_txack(TransactionType *tx) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (idx1 == 0) {
|
|
||||||
hasher_Reset(&hasher_check);
|
|
||||||
}
|
|
||||||
if (!tx_input_check_hash(&hasher_check, tx->inputs)) {
|
|
||||||
fsm_sendFailure(FailureType_Failure_ProcessError,
|
|
||||||
_("Failed to hash input"));
|
|
||||||
signing_abort();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(&input, tx->inputs, sizeof(TxInputType));
|
memcpy(&input, tx->inputs, sizeof(TxInputType));
|
||||||
|
|
||||||
if (!fill_input_script_pubkey(coin, &root, &input)) {
|
if (!fill_input_script_pubkey(coin, &root, &input)) {
|
||||||
@ -2987,10 +3012,10 @@ void signing_txack(TransactionType *tx) {
|
|||||||
info.lock_time, info.expiry, tx->branch_id, 0,
|
info.lock_time, info.expiry, tx->branch_id, 0,
|
||||||
coin->curve->hasher_sign, coin->overwintered,
|
coin->curve->hasher_sign, coin->overwintered,
|
||||||
info.version_group_id, info.timestamp);
|
info.version_group_id, info.timestamp);
|
||||||
hasher_Reset(&hasher_check);
|
hasher_Reset(&info.hasher_check);
|
||||||
}
|
}
|
||||||
// check inputs are the same as those in phase 1
|
// check inputs are the same as those in phase 1
|
||||||
if (!tx_input_check_hash(&hasher_check, tx->inputs)) {
|
if (!tx_input_check_hash(&info.hasher_check, tx->inputs)) {
|
||||||
fsm_sendFailure(FailureType_Failure_ProcessError,
|
fsm_sendFailure(FailureType_Failure_ProcessError,
|
||||||
_("Failed to hash input"));
|
_("Failed to hash input"));
|
||||||
signing_abort();
|
signing_abort();
|
||||||
@ -3006,10 +3031,10 @@ void signing_txack(TransactionType *tx) {
|
|||||||
memcpy(privkey, node.private_key, 32);
|
memcpy(privkey, node.private_key, 32);
|
||||||
memcpy(pubkey, node.public_key, 33);
|
memcpy(pubkey, node.public_key, 33);
|
||||||
} else {
|
} else {
|
||||||
if (next_legacy_input == idx1 && idx2 > idx1 &&
|
if (info.next_legacy_input == idx1 && idx2 > idx1 &&
|
||||||
(tx->inputs[0].script_type == InputScriptType_SPENDADDRESS ||
|
(tx->inputs[0].script_type == InputScriptType_SPENDADDRESS ||
|
||||||
tx->inputs[0].script_type == InputScriptType_SPENDMULTISIG)) {
|
tx->inputs[0].script_type == InputScriptType_SPENDMULTISIG)) {
|
||||||
next_legacy_input = idx2;
|
info.next_legacy_input = idx2;
|
||||||
}
|
}
|
||||||
tx->inputs[0].script_sig.size = 0;
|
tx->inputs[0].script_sig.size = 0;
|
||||||
}
|
}
|
||||||
@ -3023,15 +3048,11 @@ void signing_txack(TransactionType *tx) {
|
|||||||
idx2++;
|
idx2++;
|
||||||
send_req_4_input();
|
send_req_4_input();
|
||||||
} else {
|
} else {
|
||||||
uint8_t hash[32] = {0};
|
if (!tx_info_check_inputs_hash(&info)) {
|
||||||
hasher_Final(&hasher_check, hash);
|
|
||||||
if (memcmp(hash, hash_inputs_check, 32) != 0) {
|
|
||||||
fsm_sendFailure(FailureType_Failure_DataError,
|
|
||||||
_("Transaction has changed during signing"));
|
|
||||||
signing_abort();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
hasher_Reset(&hasher_check);
|
|
||||||
|
hasher_Reset(&info.hasher_check);
|
||||||
idx2 = 0;
|
idx2 = 0;
|
||||||
send_req_4_output();
|
send_req_4_output();
|
||||||
}
|
}
|
||||||
@ -3051,7 +3072,7 @@ void signing_txack(TransactionType *tx) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// check hashOutputs
|
// check hashOutputs
|
||||||
tx_output_hash(&hasher_check, &bin_output, coin->decred);
|
tx_output_hash(&info.hasher_check, &bin_output, coin->decred);
|
||||||
if (!tx_serialize_output_hash(&ti, &bin_output)) {
|
if (!tx_serialize_output_hash(&ti, &bin_output)) {
|
||||||
fsm_sendFailure(FailureType_Failure_ProcessError,
|
fsm_sendFailure(FailureType_Failure_ProcessError,
|
||||||
_("Failed to serialize output"));
|
_("Failed to serialize output"));
|
||||||
@ -3062,7 +3083,8 @@ void signing_txack(TransactionType *tx) {
|
|||||||
idx2++;
|
idx2++;
|
||||||
send_req_4_output();
|
send_req_4_output();
|
||||||
} else {
|
} else {
|
||||||
if (!signing_sign_legacy_input()) {
|
if (!tx_info_check_outputs_hash(&info) ||
|
||||||
|
!signing_sign_legacy_input()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// since this took a longer time, update progress
|
// since this took a longer time, update progress
|
||||||
|
Loading…
Reference in New Issue
Block a user