legacy, core: add and unify validation checks

release/2020-04
Tomas Susanka 4 years ago
parent 71a39bc0d7
commit 64584e271c

@ -1,5 +1,6 @@
import gc
from trezor import utils
from trezor.messages import FailureType, InputScriptType, OutputScriptType
from trezor.messages.RequestType import (
TXEXTRADATA,
@ -14,12 +15,14 @@ from trezor.messages.TxInputType import TxInputType
from trezor.messages.TxOutputBinType import TxOutputBinType
from trezor.messages.TxOutputType import TxOutputType
from trezor.messages.TxRequest import TxRequest
from trezor.utils import obj_eq
from .signing import SigningError
from apps.common.coininfo import CoinInfo
if False:
from typing import Union
CHANGE_OUTPUT_TO_INPUT_SCRIPT_TYPES = {
OutputScriptType.PAYTOADDRESS: InputScriptType.SPENDADDRESS,
OutputScriptType.PAYTOMULTISIG: InputScriptType.SPENDMULTISIG,
@ -50,7 +53,7 @@ class UiConfirmOutput:
self.output = output
self.coin = coin
__eq__ = obj_eq
__eq__ = utils.obj_eq
class UiConfirmTotal:
@ -59,7 +62,7 @@ class UiConfirmTotal:
self.fee = fee
self.coin = coin
__eq__ = obj_eq
__eq__ = utils.obj_eq
class UiConfirmFeeOverThreshold:
@ -67,21 +70,21 @@ class UiConfirmFeeOverThreshold:
self.fee = fee
self.coin = coin
__eq__ = obj_eq
__eq__ = utils.obj_eq
class UiConfirmForeignAddress:
def __init__(self, address_n: list):
self.address_n = address_n
__eq__ = obj_eq
__eq__ = utils.obj_eq
class UiConfirmNonDefaultLocktime:
def __init__(self, lock_time: int):
self.lock_time = lock_time
__eq__ = obj_eq
__eq__ = utils.obj_eq
def confirm_output(output: TxOutputType, coin: CoinInfo):
@ -218,11 +221,25 @@ def sanitize_tx_input(tx: TransactionType, coin: CoinInfo) -> TxInputType:
raise SigningError(
FailureType.DataError, "Input's address_n provided but not expected.",
)
if (txi.decred_script_version or txi.decred_script_version) and not coin.decred:
if not coin.decred and txi.decred_tree is not None:
raise SigningError(
FailureType.DataError,
"Decred details provided but Decred coin not specified.",
)
if txi.script_type in (
InputScriptType.SPENDWITNESS,
InputScriptType.SPENDP2SHWITNESS,
):
if not coin.segwit:
raise SigningError(
FailureType.DataError, "Segwit not enabled on this coin",
)
if txi.amount is None:
raise SigningError(
FailureType.DataError, "Segwit input without amount",
)
_sanitize_decred(txi, coin)
return txi
@ -259,14 +276,22 @@ def sanitize_tx_output(tx: TransactionType, coin: CoinInfo) -> TxOutputType:
if not txo.address_n and not txo.address:
raise SigningError(FailureType.DataError, "Missing address")
_sanitize_decred(txo, coin)
return txo
def sanitize_tx_binoutput(tx: TransactionType, coin: CoinInfo) -> TxOutputBinType:
txo_bin = tx.bin_outputs[0]
if txo_bin.decred_script_version and not coin.decred:
_sanitize_decred(txo_bin, coin)
return txo_bin
def _sanitize_decred(
tx: Union[TxInputType, TxOutputType, TxOutputBinType], coin: CoinInfo
):
if not coin.decred and tx.decred_script_version is not None:
raise SigningError(
FailureType.DataError,
"Decred details provided but Decred coin not specified.",
)
return txo_bin

@ -587,7 +587,7 @@ void signing_init(const SignTx *msg, const CoinInfo *_coin,
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
static bool signing_check_input(const TxInputType *txinput) {
static bool signing_validate_input(const TxInputType *txinput) {
if (txinput->has_multisig &&
(txinput->script_type != InputScriptType_SPENDMULTISIG &&
txinput->script_type != InputScriptType_SPENDP2SHWITNESS &&
@ -607,7 +607,114 @@ static bool signing_check_input(const TxInputType *txinput) {
signing_abort();
return false;
}
if (!coin->decred && txinput->has_decred_script_version) {
fsm_sendFailure(
FailureType_Failure_DataError,
_("Decred details provided but Decred coin not specified."));
signing_abort();
return false;
}
#if !BITCOIN_ONLY
if (coin->force_bip143 || coin->overwintered) {
if (!txinput->has_amount) {
fsm_sendFailure(FailureType_Failure_DataError,
_("Expected input with amount"));
signing_abort();
return false;
}
}
#endif
if (txinput->script_type == InputScriptType_SPENDWITNESS ||
txinput->script_type == InputScriptType_SPENDP2SHWITNESS) {
if (!coin->has_segwit) {
fsm_sendFailure(FailureType_Failure_DataError,
_("Segwit not enabled on this coin"));
signing_abort();
return false;
}
if (!txinput->has_amount) {
fsm_sendFailure(FailureType_Failure_DataError,
_("Segwit input without amount"));
signing_abort();
return false;
}
}
return true;
}
static bool signing_validate_output(TxOutputType *txoutput) {
if (txoutput->has_multisig &&
(txoutput->script_type != OutputScriptType_PAYTOMULTISIG &&
txoutput->script_type != OutputScriptType_PAYTOP2SHWITNESS &&
txoutput->script_type != OutputScriptType_PAYTOWITNESS)) {
fsm_sendFailure(FailureType_Failure_DataError,
_("Multisig field provided but not expected."));
signing_abort();
return false;
}
if (txoutput->address_n_count > 0 &&
(txoutput->script_type != OutputScriptType_PAYTOADDRESS &&
txoutput->script_type != OutputScriptType_PAYTOMULTISIG &&
txoutput->script_type != OutputScriptType_PAYTOWITNESS &&
txoutput->script_type != OutputScriptType_PAYTOP2SHWITNESS)) {
fsm_sendFailure(FailureType_Failure_DataError,
_("Output's address_n provided but not expected."));
signing_abort();
return false;
}
if (txoutput->script_type == OutputScriptType_PAYTOOPRETURN) {
if (txoutput->has_address || (txoutput->address_n_count > 0) ||
txoutput->has_multisig) {
fsm_sendFailure(FailureType_Failure_DataError,
_("OP_RETURN output with address or multisig"));
signing_abort();
return false;
}
if (txoutput->amount != 0) {
fsm_sendFailure(FailureType_Failure_DataError,
_("OP_RETURN output with non-zero amount"));
signing_abort();
return false;
}
} else {
if (txoutput->has_op_return_data) {
fsm_sendFailure(
FailureType_Failure_DataError,
_("OP RETURN data provided but not OP RETURN script type."));
signing_abort();
return false;
}
if (txoutput->has_address && txoutput->address_n_count > 0) {
fsm_sendFailure(FailureType_Failure_DataError,
_("Both address and address_n provided."));
signing_abort();
return false;
} else if (!txoutput->has_address && txoutput->address_n_count == 0) {
fsm_sendFailure(FailureType_Failure_DataError, _("Missing address"));
signing_abort();
return false;
}
}
return true;
}
static bool signing_validate_bin_output(TxOutputBinType *tx_bin_output) {
if (!coin->decred && tx_bin_output->has_decred_script_version) {
fsm_sendFailure(
FailureType_Failure_DataError,
_("Decred details provided but Decred coin not specified."));
signing_abort();
return false;
}
return true;
}
static bool signing_check_input(const TxInputType *txinput) {
/* compute multisig fingerprint */
/* (if all input share the same fingerprint, outputs having the same
* fingerprint will be considered as change outputs) */
@ -682,63 +789,6 @@ static bool signing_check_output(TxOutputType *txoutput) {
// add it to hash_outputs
// ask user for permission
if (txoutput->has_multisig &&
(txoutput->script_type != OutputScriptType_PAYTOMULTISIG &&
txoutput->script_type != OutputScriptType_PAYTOP2SHWITNESS &&
txoutput->script_type != OutputScriptType_PAYTOWITNESS)) {
fsm_sendFailure(FailureType_Failure_DataError,
_("Multisig field provided but not expected."));
signing_abort();
return false;
}
if (txoutput->address_n_count > 0 &&
(txoutput->script_type != OutputScriptType_PAYTOADDRESS &&
txoutput->script_type != OutputScriptType_PAYTOMULTISIG &&
txoutput->script_type != OutputScriptType_PAYTOWITNESS &&
txoutput->script_type != OutputScriptType_PAYTOP2SHWITNESS)) {
fsm_sendFailure(FailureType_Failure_DataError,
_("Output's address_n provided but not expected."));
signing_abort();
return false;
}
if (txoutput->has_op_return_data &&
(txoutput->script_type != OutputScriptType_PAYTOOPRETURN)) {
fsm_sendFailure(
FailureType_Failure_DataError,
_("OP RETURN data provided but not OP RETURN script type."));
signing_abort();
return false;
}
if (txoutput->script_type == OutputScriptType_PAYTOOPRETURN) {
if (txoutput->has_address || (txoutput->address_n_count > 0) ||
txoutput->has_multisig) {
fsm_sendFailure(FailureType_Failure_DataError,
_("OP_RETURN output with address or multisig"));
signing_abort();
return false;
}
if (txoutput->amount != 0) {
fsm_sendFailure(FailureType_Failure_DataError,
_("OP_RETURN output with non-zero amount"));
signing_abort();
return false;
}
} else {
if (txoutput->has_address && txoutput->address_n_count > 0) {
fsm_sendFailure(FailureType_Failure_DataError,
_("Both address and address_n provided."));
signing_abort();
return false;
} else if (!txoutput->has_address && txoutput->address_n_count == 0) {
fsm_sendFailure(FailureType_Failure_DataError, _("Missing address"));
signing_abort();
return false;
}
}
// check for change address
bool is_change = false;
if (txoutput->address_n_count > 0) {
@ -1188,7 +1238,10 @@ void signing_txack(TransactionType *tx) {
switch (signing_stage) {
case STAGE_REQUEST_1_INPUT:
signing_check_input(&tx->inputs[0]);
if (!signing_validate_input(&tx->inputs[0]) ||
!signing_check_input(&tx->inputs[0])) {
return;
}
tx_weight += tx_input_weight(coin, &tx->inputs[0]);
#if !BITCOIN_ONLY
@ -1211,13 +1264,8 @@ void signing_txack(TransactionType *tx) {
}
#endif
#if !BITCOIN_ONLY
if (coin->force_bip143 || coin->overwintered) {
if (!tx->inputs[0].has_amount) {
fsm_sendFailure(FailureType_Failure_DataError,
_("Expected input with amount"));
signing_abort();
return;
}
if (to_spend + tx->inputs[0].amount < to_spend) {
fsm_sendFailure(FailureType_Failure_DataError, _("Value overflow"));
signing_abort();
@ -1226,7 +1274,9 @@ void signing_txack(TransactionType *tx) {
to_spend += tx->inputs[0].amount;
authorized_amount += tx->inputs[0].amount;
phase1_request_next_input();
} else {
} else
#endif
{
// remember the first non-segwit input -- this is the first input
// we need to sign during phase2
if (next_nonsegwit_input == 0xffffffff) next_nonsegwit_input = idx1;
@ -1235,26 +1285,6 @@ void signing_txack(TransactionType *tx) {
} else if (tx->inputs[0].script_type == InputScriptType_SPENDWITNESS ||
tx->inputs[0].script_type ==
InputScriptType_SPENDP2SHWITNESS) {
#if !BITCOIN_ONLY
if (coin->decred) {
fsm_sendFailure(FailureType_Failure_DataError,
_("Decred does not support Segwit"));
signing_abort();
return;
}
#endif
if (!coin->has_segwit) {
fsm_sendFailure(FailureType_Failure_DataError,
_("Segwit not enabled on this coin"));
signing_abort();
return;
}
if (!tx->inputs[0].has_amount) {
fsm_sendFailure(FailureType_Failure_DataError,
_("Segwit input without amount"));
signing_abort();
return;
}
if (to_spend + tx->inputs[0].amount < to_spend) {
fsm_sendFailure(FailureType_Failure_DataError, _("Value overflow"));
signing_abort();
@ -1342,6 +1372,9 @@ void signing_txack(TransactionType *tx) {
}
return;
case STAGE_REQUEST_2_PREV_INPUT:
if (!signing_validate_input(&tx->inputs[0])) {
return;
}
progress = (idx1 * progress_step + idx2 * progress_meta_step) >>
PROGRESS_PRECISION;
if (!tx_serialize_input_hash(&tp, tx->inputs)) {
@ -1359,6 +1392,9 @@ void signing_txack(TransactionType *tx) {
}
return;
case STAGE_REQUEST_2_PREV_OUTPUT:
if (!signing_validate_bin_output(&tx->bin_outputs[0])) {
return;
}
progress = (idx1 * progress_step +
(tp.inputs_len + idx2) * progress_meta_step) >>
PROGRESS_PRECISION;
@ -1376,9 +1412,9 @@ void signing_txack(TransactionType *tx) {
}
#if !BITCOIN_ONLY
if (coin->decred && tx->bin_outputs[0].decred_script_version > 0) {
fsm_sendFailure(
FailureType_Failure_DataError,
_("Decred script version does not match previous output"));
fsm_sendFailure(FailureType_Failure_DataError,
_("Decred script version does "
"not match previous output"));
signing_abort();
return;
}
@ -1396,7 +1432,9 @@ void signing_txack(TransactionType *tx) {
#endif
} else {
/* prevtx is done */
signing_check_prevtx_hash();
if (!signing_check_prevtx_hash()) {
return;
}
}
return;
#if !BITCOIN_ONLY
@ -1414,18 +1452,24 @@ void signing_txack(TransactionType *tx) {
tp.extra_data_received,
MIN(1024, tp.extra_data_len - tp.extra_data_received));
} else {
signing_check_prevtx_hash();
if (!signing_check_prevtx_hash()) {
return;
}
}
return;
#endif
case STAGE_REQUEST_3_OUTPUT:
if (!signing_check_output(&tx->outputs[0])) {
if (!signing_validate_output(&tx->outputs[0]) ||
!signing_check_output(&tx->outputs[0])) {
return;
}
tx_weight += tx_output_weight(coin, &tx->outputs[0]);
phase1_request_next_output();
return;
case STAGE_REQUEST_4_INPUT:
if (!signing_validate_input(&tx->inputs[0])) {
return;
}
progress =
500 + ((signatures * progress_step + idx2 * progress_meta_step) >>
PROGRESS_PRECISION);
@ -1481,6 +1525,9 @@ void signing_txack(TransactionType *tx) {
}
return;
case STAGE_REQUEST_4_OUTPUT:
if (!signing_validate_output(&tx->outputs[0])) {
return;
}
progress = 500 + ((signatures * progress_step +
(inputs_count + idx2) * progress_meta_step) >>
PROGRESS_PRECISION);
@ -1521,6 +1568,9 @@ void signing_txack(TransactionType *tx) {
return;
case STAGE_REQUEST_SEGWIT_INPUT:
if (!signing_validate_input(&tx->inputs[0])) {
return;
}
resp.has_serialized = true;
resp.serialized.has_signature_index = false;
resp.serialized.has_signature = false;
@ -1627,6 +1677,9 @@ void signing_txack(TransactionType *tx) {
return;
case STAGE_REQUEST_5_OUTPUT:
if (!signing_validate_output(&tx->outputs[0])) {
return;
}
if (compile_output(coin, &root, tx->outputs, &bin_output, false) <= 0) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to compile output"));
@ -1650,6 +1703,9 @@ void signing_txack(TransactionType *tx) {
return;
case STAGE_REQUEST_SEGWIT_WITNESS:
if (!signing_validate_input(&tx->inputs[0])) {
return;
}
if (!signing_sign_segwit_input(&tx->inputs[0])) {
return;
}
@ -1669,6 +1725,9 @@ void signing_txack(TransactionType *tx) {
#if !BITCOIN_ONLY
case STAGE_REQUEST_DECRED_WITNESS:
if (!signing_validate_input(&tx->inputs[0])) {
return;
}
progress =
500 + ((signatures * progress_step + idx2 * progress_meta_step) >>
PROGRESS_PRECISION);

Loading…
Cancel
Save