Merge remote-tracking branch 'origin/release/2020-09'

pull/1246/head
Tomas Susanka 4 years ago
commit b99b8b3df3

@ -18,7 +18,7 @@
"certificates": [],
"withdrawals": [],
"metadata": "",
"input_flow": [["SWIPE", "YES"], ["SWIPE", "YES"]],
"input_flow": [["YES"], ["YES"], ["SWIPE", "YES"], ["SWIPE", "YES"]],
"inputs": [
{
"path": "m/44'/1815'/0'/0/1",
@ -48,7 +48,7 @@
"certificates": [],
"withdrawals": [],
"metadata": "",
"input_flow": [["SWIPE", "YES"], ["YES"], ["SWIPE", "YES"]],
"input_flow": [["YES"], ["YES"], ["SWIPE", "YES"], ["YES"], ["SWIPE", "YES"]],
"inputs": [
{
"path": "m/44'/1815'/0'/0/1",
@ -83,7 +83,7 @@
"certificates": [],
"withdrawals": [],
"metadata": "",
"input_flow": [["SWIPE", "YES"], ["YES"], ["SWIPE", "YES"]],
"input_flow": [["YES"], ["YES"], ["SWIPE", "YES"], ["YES"], ["SWIPE", "YES"]],
"inputs": [
{
"path": "m/44'/1815'/0'/0/1",

@ -35,13 +35,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Deprecated
### Removed
- ETP, GIN, PTC, ZEL support
- Remove ETP, GIN, PTC, ZEL support.
- Drop support for signing Zcash v3 transactions. [#982]
### Fixed
- CRW addresses are properly generated. [#1139]
- Fix boot loop after uploading invalid homescreen. [#1118]
- Allow 49/x not 49/x' for Casa. [#1190]
- Make sure Homescreen is properly initialized. [#1095]
### Security
- Show non-empty passphrase on device when it was entered on host.
- Show warning if nLockTime is set but ineffective due to all nSequence values being 0xffffffff.
## 2.3.2 [5th August 2020]
@ -268,6 +273,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
[#948]: https://github.com/trezor/trezor-firmware/issues/948
[#958]: https://github.com/trezor/trezor-firmware/issues/958
[#982]: https://github.com/trezor/trezor-firmware/issues/982
[#1027]: https://github.com/trezor/trezor-firmware/issues/1027
[#1030]: https://github.com/trezor/trezor-firmware/issues/1030
[#1042]: https://github.com/trezor/trezor-firmware/issues/1042
@ -278,6 +284,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
[#1074]: https://github.com/trezor/trezor-firmware/issues/1074
[#1087]: https://github.com/trezor/trezor-firmware/issues/1087
[#1089]: https://github.com/trezor/trezor-firmware/issues/1089
[#1095]: https://github.com/trezor/trezor-firmware/issues/1095
[#1098]: https://github.com/trezor/trezor-firmware/issues/1098
[#1115]: https://github.com/trezor/trezor-firmware/issues/1115
[#1118]: https://github.com/trezor/trezor-firmware/issues/1118
@ -287,3 +294,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
[#1165]: https://github.com/trezor/trezor-firmware/pull/1165
[#1173]: https://github.com/trezor/trezor-firmware/pull/1173
[#1188]: https://github.com/trezor/trezor-firmware/issues/1188
[#1190]: https://github.com/trezor/trezor-firmware/issues/1190

@ -3,11 +3,8 @@ from trezor.messages import MessageType
def boot() -> None:
wire.add(MessageType.AuthorizeCoinJoin, __name__, "authorize_coinjoin")
wire.add(MessageType.GetPublicKey, __name__, "get_public_key")
wire.add(MessageType.GetAddress, __name__, "get_address")
wire.add(MessageType.GetOwnershipId, __name__, "get_ownership_id")
wire.add(MessageType.GetOwnershipProof, __name__, "get_ownership_proof")
wire.add(MessageType.SignTx, __name__, "sign_tx")
wire.add(MessageType.SignMessage, __name__, "sign_message")
wire.add(MessageType.VerifyMessage, __name__, "verify_message")

@ -4,11 +4,11 @@ from trezor.messages.SignTx import SignTx
from trezor.messages.TxAck import TxAck
from trezor.messages.TxRequest import TxRequest
from apps.common import coininfo, paths
from apps.common import coininfo
from ..common import BITCOIN_NAMES
from ..keychain import with_keychain
from . import approvers, bitcoin, helpers, layout, progress
from . import approvers, bitcoin, helpers, progress
if not utils.BITCOIN_ONLY:
from . import bitcoinlike, decred, zcash
@ -52,30 +52,8 @@ async def sign_tx(
if req.request_type == TXFINISHED:
break
res = await ctx.call(req, TxAck, field_cache)
elif isinstance(req, helpers.UiConfirmOutput):
res = await layout.confirm_output(ctx, req.output, req.coin)
progress.report_init()
elif isinstance(req, helpers.UiConfirmTotal):
res = await layout.confirm_total(ctx, req.spending, req.fee, req.coin)
progress.report_init()
elif isinstance(req, helpers.UiConfirmJointTotal):
res = await layout.confirm_joint_total(
ctx, req.spending, req.total, req.coin
)
progress.report_init()
elif isinstance(req, helpers.UiConfirmFeeOverThreshold):
res = await layout.confirm_feeoverthreshold(ctx, req.fee, req.coin)
progress.report_init()
elif isinstance(req, helpers.UiConfirmChangeCountOverThreshold):
res = await layout.confirm_change_count_over_threshold(
ctx, req.change_count
)
progress.report_init()
elif isinstance(req, helpers.UiConfirmNonDefaultLocktime):
res = await layout.confirm_nondefault_locktime(ctx, req.lock_time)
progress.report_init()
elif isinstance(req, helpers.UiConfirmForeignAddress):
res = await paths.show_path_warning(ctx, req.address_n)
elif isinstance(req, helpers.UiConfirm):
res = await req.confirm_dialog(ctx)
progress.report_init()
else:
raise TypeError("Invalid signing instruction")

@ -15,6 +15,9 @@ from . import helpers, tx_weight
if False:
from ..authorization import CoinJoinAuthorization
# Setting nSequence to this value for every input in a transaction disables nLockTime.
_SEQUENCE_FINAL = const(0xFFFFFFFF)
# An Approver object computes the transaction totals and either prompts the user
# to confirm transaction parameters (output addresses, amounts and fees) or uses
@ -25,6 +28,7 @@ class Approver:
self.tx = tx
self.coin = coin
self.weight = tx_weight.TxWeightCalculator(tx.inputs_count, tx.outputs_count)
self.min_sequence = _SEQUENCE_FINAL # the minimum nSequence of all inputs
# amounts
self.total_in = 0 # sum of input amounts
@ -35,11 +39,13 @@ class Approver:
async def add_internal_input(self, txi: TxInputType, amount: int) -> None:
self.weight.add_input(txi)
self.total_in += amount
self.min_sequence = min(self.min_sequence, txi.sequence)
def add_external_input(self, txi: TxInputType) -> None:
self.weight.add_input(txi)
self.total_in += txi.amount
self.external_in += txi.amount
self.min_sequence = min(self.min_sequence, txi.sequence)
def add_change_output(self, txo: TxOutputType, script_pubkey: bytes) -> None:
self.weight.add_output(script_pubkey)
@ -100,7 +106,10 @@ class BasicApprover(Approver):
if self.change_count > self.MAX_SILENT_CHANGE_COUNT:
await helpers.confirm_change_count_over_threshold(self.change_count)
if self.tx.lock_time > 0:
await helpers.confirm_nondefault_locktime(self.tx.lock_time)
lock_time_disabled = self.min_sequence == _SEQUENCE_FINAL
await helpers.confirm_nondefault_locktime(
self.tx.lock_time, lock_time_disabled
)
if not self.external_in:
await helpers.confirm_total(total, fee, self.coin)
else:

@ -14,10 +14,12 @@ from trezor.messages.TxOutputBinType import TxOutputBinType
from trezor.messages.TxOutputType import TxOutputType
from trezor.messages.TxRequest import TxRequest
from apps.common import paths
from apps.common.coininfo import CoinInfo
from .. import common
from ..writers import TX_HASH_SIZE
from . import layout
if False:
from typing import Any, Awaitable
@ -27,57 +29,86 @@ if False:
# ===
class UiConfirmOutput:
class UiConfirm:
def confirm_dialog(self, ctx: wire.Context) -> Awaitable[Any]:
raise NotImplementedError
class UiConfirmOutput(UiConfirm):
def __init__(self, output: TxOutputType, coin: CoinInfo):
self.output = output
self.coin = coin
def confirm_dialog(self, ctx: wire.Context) -> Awaitable[Any]:
return layout.confirm_output(ctx, self.output, self.coin)
__eq__ = utils.obj_eq
class UiConfirmTotal:
class UiConfirmTotal(UiConfirm):
def __init__(self, spending: int, fee: int, coin: CoinInfo):
self.spending = spending
self.fee = fee
self.coin = coin
def confirm_dialog(self, ctx: wire.Context) -> Awaitable[Any]:
return layout.confirm_total(ctx, self.spending, self.fee, self.coin)
__eq__ = utils.obj_eq
class UiConfirmJointTotal:
class UiConfirmJointTotal(UiConfirm):
def __init__(self, spending: int, total: int, coin: CoinInfo):
self.spending = spending
self.total = total
self.coin = coin
def confirm_dialog(self, ctx: wire.Context) -> Awaitable[Any]:
return layout.confirm_joint_total(ctx, self.spending, self.total, self.coin)
__eq__ = utils.obj_eq
class UiConfirmFeeOverThreshold:
class UiConfirmFeeOverThreshold(UiConfirm):
def __init__(self, fee: int, coin: CoinInfo):
self.fee = fee
self.coin = coin
def confirm_dialog(self, ctx: wire.Context) -> Awaitable[Any]:
return layout.confirm_feeoverthreshold(ctx, self.fee, self.coin)
__eq__ = utils.obj_eq
class UiConfirmChangeCountOverThreshold:
class UiConfirmChangeCountOverThreshold(UiConfirm):
def __init__(self, change_count: int):
self.change_count = change_count
def confirm_dialog(self, ctx: wire.Context) -> Awaitable[Any]:
return layout.confirm_change_count_over_threshold(ctx, self.change_count)
__eq__ = utils.obj_eq
class UiConfirmForeignAddress:
class UiConfirmForeignAddress(UiConfirm):
def __init__(self, address_n: list):
self.address_n = address_n
def confirm_dialog(self, ctx: wire.Context) -> Awaitable[Any]:
return paths.show_path_warning(ctx, self.address_n)
__eq__ = utils.obj_eq
class UiConfirmNonDefaultLocktime:
def __init__(self, lock_time: int):
class UiConfirmNonDefaultLocktime(UiConfirm):
def __init__(self, lock_time: int, lock_time_disabled: bool):
self.lock_time = lock_time
self.lock_time_disabled = lock_time_disabled
def confirm_dialog(self, ctx: wire.Context) -> Awaitable[Any]:
return layout.confirm_nondefault_locktime(
ctx, self.lock_time, self.lock_time_disabled
)
__eq__ = utils.obj_eq
@ -106,8 +137,8 @@ def confirm_foreign_address(address_n: list) -> Awaitable[Any]: # type: ignore
return (yield UiConfirmForeignAddress(address_n))
def confirm_nondefault_locktime(lock_time: int) -> Awaitable[Any]: # type: ignore
return (yield UiConfirmNonDefaultLocktime(lock_time))
def confirm_nondefault_locktime(lock_time: int, lock_time_disabled: bool) -> Awaitable[Any]: # type: ignore
return (yield UiConfirmNonDefaultLocktime(lock_time, lock_time_disabled))
def request_tx_meta(tx_req: TxRequest, coin: CoinInfo, tx_hash: bytes = None) -> Awaitable[Any]: # type: ignore

@ -101,13 +101,21 @@ async def confirm_change_count_over_threshold(
await require_confirm(ctx, text, ButtonRequestType.SignTx)
async def confirm_nondefault_locktime(ctx: wire.Context, lock_time: int) -> None:
text = Text("Confirm locktime", ui.ICON_SEND, ui.GREEN)
text.normal("Locktime for this transaction is set to")
if lock_time < _LOCKTIME_TIMESTAMP_MIN_VALUE:
text.normal("blockheight:")
async def confirm_nondefault_locktime(
ctx: wire.Context, lock_time: int, lock_time_disabled: bool
) -> None:
if lock_time_disabled:
text = Text("Warning", ui.ICON_SEND, ui.GREEN)
text.normal("Locktime is set but will", "have no effect.")
text.br_half()
else:
text.normal("timestamp:")
text.bold(str(lock_time))
text = Text("Confirm locktime", ui.ICON_SEND, ui.GREEN)
text.normal("Locktime for this", "transaction is set to")
if lock_time < _LOCKTIME_TIMESTAMP_MIN_VALUE:
text.normal("blockheight:")
else:
text.normal("timestamp:")
text.bold(str(lock_time))
text.normal("Continue?")
await require_confirm(ctx, text, ButtonRequestType.SignTx)

@ -10,6 +10,7 @@ from trezor.ui.passphrase import CANCELLED, PassphraseKeyboard
from trezor.ui.text import Text
from . import button_request
from .confirm import require_confirm
_MAX_PASSPHRASE_LEN = const(50)
@ -53,6 +54,22 @@ async def _request_on_host(ctx: wire.Context) -> str:
raise wire.DataError(
"Passphrase not provided and on_device is False. Use empty string to set an empty passphrase."
)
# non-empty passphrase
if ack.passphrase:
text = Text("Hidden wallet", ICON_CONFIG)
text.normal("Access hidden wallet?")
text.br()
text.normal("Next screen will show")
text.normal("the passphrase!")
await require_confirm(ctx, text, ButtonRequestType.Other)
text = Text("Hidden wallet", ICON_CONFIG)
text.normal("Use this passphrase?")
text.br()
text.mono(ack.passphrase)
await require_confirm(ctx, text, ButtonRequestType.Other)
return ack.passphrase

@ -35,6 +35,7 @@ class TestApprover(unittest.TestCase):
TxInputType(
amount=denomination + 1000000 * (i + 1),
script_type=InputScriptType.EXTERNAL,
sequence=0xffffffff,
) for i in range(99)
]
@ -45,6 +46,7 @@ class TestApprover(unittest.TestCase):
address_n=[H_(84), H_(0), H_(0), 0, 1],
amount=denomination + 1000000,
script_type=InputScriptType.SPENDWITNESS,
sequence=0xffffffff,
)
)

@ -103,7 +103,7 @@ class TestSignSegwitTxNativeP2WPKH_GRS(unittest.TestCase):
helpers.UiConfirmOutput(out2, coin),
True,
helpers.UiConfirmNonDefaultLocktime(tx.lock_time),
helpers.UiConfirmNonDefaultLocktime(tx.lock_time, lock_time_disabled=False),
True,
helpers.UiConfirmTotal(12300000, 11000, coin),
@ -225,7 +225,7 @@ class TestSignSegwitTxNativeP2WPKH_GRS(unittest.TestCase):
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=1, tx_hash=None), serialized=EMPTY_SERIALIZED),
TxAck(tx=TransactionType(outputs=[out2])),
helpers.UiConfirmNonDefaultLocktime(tx.lock_time),
helpers.UiConfirmNonDefaultLocktime(tx.lock_time, lock_time_disabled=False),
True,
helpers.UiConfirmTotal(5000000 + 11000, 11000, coin),

@ -103,7 +103,7 @@ class TestSignSegwitTxP2WPKHInP2SH_GRS(unittest.TestCase):
helpers.UiConfirmOutput(out2, coin),
True,
helpers.UiConfirmNonDefaultLocktime(tx.lock_time),
helpers.UiConfirmNonDefaultLocktime(tx.lock_time, lock_time_disabled=False),
True,
helpers.UiConfirmTotal(123445789 + 11000, 11000, coin),
@ -225,7 +225,7 @@ class TestSignSegwitTxP2WPKHInP2SH_GRS(unittest.TestCase):
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=1, tx_hash=None), serialized=EMPTY_SERIALIZED),
TxAck(tx=TransactionType(outputs=[out2])),
helpers.UiConfirmNonDefaultLocktime(tx.lock_time),
helpers.UiConfirmNonDefaultLocktime(tx.lock_time, lock_time_disabled=False),
True,
helpers.UiConfirmTotal(12300000 + 11000, 11000, coin),

@ -22,6 +22,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Added
- XVG support. [#1165]
- Ask user to confirm custom nLockTime.
### Changed
- Print inverted question mark for non-printable characters.
@ -35,6 +36,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Fixed
### Security
- Show non-empty passphrase on device when it was entered on host.
## 1.9.2 [5th August 2020]

@ -601,6 +601,30 @@ const uint8_t *config_getSeed(void) {
memzero(passphrase, sizeof(passphrase));
return NULL;
}
// passphrase is used - confirm on the display
if (passphrase[0] != 0) {
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Access hidden wallet?"), NULL,
_("Next screen will show"), _("the passphrase!"), NULL,
NULL);
if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) {
memzero(mnemonic, sizeof(mnemonic));
memzero(passphrase, sizeof(passphrase));
fsm_sendFailure(FailureType_Failure_ActionCancelled,
_("Passphrase dismissed"));
layoutHome();
return NULL;
}
layoutShowPassphrase(passphrase);
if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) {
memzero(mnemonic, sizeof(mnemonic));
memzero(passphrase, sizeof(passphrase));
fsm_sendFailure(FailureType_Failure_ActionCancelled,
_("Passphrase dismissed"));
layoutHome();
return NULL;
}
}
// if storage was not imported (i.e. it was properly generated or recovered)
bool imported = false;
config_get_bool(KEY_IMPORTED, &imported);

@ -37,6 +37,8 @@
#include "timer.h"
#include "util.h"
#define LOCKTIME_TIMESTAMP_MIN_VALUE 500000000
#if !BITCOIN_ONLY
static const char *slip44_extras(uint32_t coin_type) {
@ -450,6 +452,25 @@ void layoutChangeCountOverThreshold(uint32_t change_count) {
_("Continue?"), NULL);
}
void layoutConfirmNondefaultLockTime(uint32_t lock_time,
bool lock_time_disabled) {
if (lock_time_disabled) {
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Warning!"), _("Locktime is set but"),
_("will have no effect."), NULL, _("Continue?"), NULL);
} else {
char str_locktime[11] = {0};
snprintf(str_locktime, sizeof(str_locktime), "%" PRIu32, lock_time);
char *str_type = (lock_time < LOCKTIME_TIMESTAMP_MIN_VALUE) ? "blockheight:"
: "timestamp:";
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Locktime for this"), _("transaction is set to"),
str_type, str_locktime, _("Continue?"), NULL);
}
}
void layoutSignMessage(const uint8_t *msg, uint32_t len) {
const char **str = NULL;
if (!is_valid_ascii(msg, len)) {
@ -823,6 +844,25 @@ void layoutU2FDialog(const char *verb, const char *appname) {
#endif
void layoutShowPassphrase(const char *passphrase) {
if (layoutLast != layoutShowPassphrase) {
layoutSwipe();
} else {
oledClear();
}
const char **str =
split_message((const uint8_t *)passphrase, strlen(passphrase), 21);
for (int i = 0; i < 3; i++) {
oledDrawString(0, i * 9 + 4, str[i], FONT_FIXED);
}
oledDrawStringCenter(OLED_WIDTH / 2, OLED_HEIGHT - 2 * 9 - 1,
_("Use this passphrase?"), FONT_STANDARD);
oledHLine(OLED_HEIGHT - 21);
layoutButtonNo(_("Cancel"), &bmp_btn_cancel);
layoutButtonYes(_("Confirm"), &bmp_btn_confirm);
oledRefresh();
}
#if !BITCOIN_ONLY
void layoutNEMDialog(const BITMAP *icon, const char *btnNo, const char *btnYes,

@ -53,6 +53,8 @@ void layoutConfirmTx(const CoinInfo *coin, uint64_t amount_out,
uint64_t amount_fee);
void layoutFeeOverThreshold(const CoinInfo *coin, uint64_t fee);
void layoutChangeCountOverThreshold(uint32_t change_count);
void layoutConfirmNondefaultLockTime(uint32_t lock_time,
bool lock_time_disabled);
void layoutSignMessage(const uint8_t *msg, uint32_t len);
void layoutVerifyAddress(const CoinInfo *coin, const char *address);
void layoutVerifyMessage(const uint8_t *msg, uint32_t len);
@ -69,6 +71,7 @@ void layoutXPUB(const char *xpub, int index, int page, bool ours);
void layoutSignIdentity(const IdentityType *identity, const char *challenge);
void layoutDecryptIdentity(const IdentityType *identity);
void layoutU2FDialog(const char *verb, const char *appname);
void layoutShowPassphrase(const char *passphrase);
void layoutNEMDialog(const BITMAP *icon, const char *btnNo, const char *btnYes,
const char *desc, const char *line1, const char *address);

@ -75,6 +75,7 @@ static uint32_t lock_time = 0;
static uint32_t expiry = 0;
static uint32_t version_group_id = 0;
static uint32_t timestamp = 0;
static uint32_t min_sequence = 0;
#if !BITCOIN_ONLY
static uint32_t branch_id = 0;
#endif
@ -107,6 +108,10 @@ static uint32_t tx_weight;
/* The maximum number of change-outputs allowed without user confirmation. */
#define MAX_SILENT_CHANGE_COUNT 2
/* Setting nSequence to this value for every input in a transaction disables
nLockTime. */
#define SEQUENCE_FINAL 0xffffffff
enum {
SIGHASH_ALL = 1,
SIGHASH_FORKID = 0x40,
@ -497,6 +502,7 @@ void signing_init(const SignTx *msg, const CoinInfo *_coin,
memcpy(&root, _root, sizeof(HDNode));
version = msg->version;
lock_time = msg->lock_time;
min_sequence = SEQUENCE_FINAL;
if (!coin->overwintered) {
if (msg->has_version_group_id) {
@ -782,12 +788,18 @@ static bool signing_check_input(const TxInputType *txinput) {
} else { // single signature
multisig_fp_mismatch = true;
}
// remember the input bip32 path
// change addresses must use the same bip32 path as all inputs
extract_input_bip32_path(txinput);
// remember the minimum nSequence value
if (txinput->sequence < min_sequence) min_sequence = txinput->sequence;
// compute segwit hashPrevouts & hashSequence
tx_prevout_hash(&hasher_prevouts, txinput);
tx_sequence_hash(&hasher_sequence, txinput);
#if !BITCOIN_ONLY
if (coin->decred) {
// serialize Decred prefix in Phase 1
@ -800,6 +812,7 @@ static bool signing_check_input(const TxInputType *txinput) {
tx_serialize_input_hash(&ti, txinput);
}
#endif
// hash prevout and script type to check it later (relevant for fee
// computation)
tx_prevout_hash(&hasher_check, txinput);
@ -914,6 +927,7 @@ static bool signing_confirm_tx(void) {
return false;
}
}
uint64_t fee = 0;
if (spending <= to_spend) {
fee = to_spend - spending;
@ -939,6 +953,16 @@ static bool signing_confirm_tx(void) {
}
}
if (lock_time != 0) {
bool lock_time_disabled = (min_sequence == SEQUENCE_FINAL);
layoutConfirmNondefaultLockTime(lock_time, lock_time_disabled);
if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
signing_abort();
return false;
}
}
// last confirmation
layoutConfirmTx(coin, to_spend - change_spend, fee);
if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) {

@ -1,533 +0,0 @@
# This file is part of the Trezor project.
#
# Copyright (C) 2020 SatoshiLabs and contributors
#
# This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License version 3
# as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the License along with this library.
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
import pytest
from trezorlib import btc, device, messages
from trezorlib.exceptions import TrezorFailure
from trezorlib.tools import parse_path
from ..tx_cache import TxCache
from .signtx import request_finished, request_input, request_meta, request_output
B = messages.ButtonRequestType
TX_CACHE_TESTNET = TxCache("Testnet")
TX_CACHE_MAINNET = TxCache("Bitcoin")
TXHASH_e5b7e2 = bytes.fromhex(
"e5b7e21b5ba720e81efd6bfa9f854ababdcddc75a43bfa60bf0fe069cfd1bb8a"
)
TXHASH_65b811 = bytes.fromhex(
"65b811d3eca0fe6915d9f2d77c86c5a7f19bf66b1b1253c2c51cb4ae5f0c017b"
)
PIN = "1234"
ROUND_ID_LEN = 32
pytestmark = pytest.mark.skip_t1
@pytest.mark.setup_client(pin=PIN)
def test_sign_tx(client):
with client:
client.use_pin_sequence([PIN])
btc.authorize_coinjoin(
client,
coordinator="www.example.com",
max_total_fee=10010,
fee_per_anonymity=5000000, # 0.005 %
n=parse_path("m/84'/1'/0'"),
coin_name="Testnet",
script_type=messages.InputScriptType.SPENDWITNESS,
)
client.call(messages.LockDevice())
with client:
client.set_expected_responses(
[messages.PreauthorizedRequest(), messages.OwnershipProof()]
)
btc.get_ownership_proof(
client,
"Testnet",
parse_path("84'/1'/0'/1/0"),
script_type=messages.InputScriptType.SPENDWITNESS,
user_confirmation=True,
commitment_data=b"www.example.com" + (1).to_bytes(ROUND_ID_LEN, "big"),
preauthorized=True,
)
with client:
client.set_expected_responses(
[messages.PreauthorizedRequest(), messages.OwnershipProof()]
)
btc.get_ownership_proof(
client,
"Testnet",
parse_path("84'/1'/0'/1/5"),
script_type=messages.InputScriptType.SPENDWITNESS,
user_confirmation=True,
commitment_data=b"www.example.com" + (1).to_bytes(ROUND_ID_LEN, "big"),
preauthorized=True,
)
inp1 = messages.TxInputType(
# seed "alcohol woman abuse must during monitor noble actual mixed trade anger aisle"
# 84'/1'/0'/0/0
# tb1qnspxpr2xj9s2jt6qlhuvdnxw6q55jvygcf89r2
amount=100000,
prev_hash=TXHASH_e5b7e2,
prev_index=0,
script_type=messages.InputScriptType.EXTERNAL,
ownership_proof=bytearray.fromhex(
"534c001900016b2055d8190244b2ed2d46513c40658a574d3bc2deb6969c0535bb818b44d2c40002483045022100d4ad0374c922848c71d913fba59c81b9075e0d33e884d953f0c4b4806b8ffd0c022024740e6717a2b6a5aa03148c3a28b02c713b4e30fc8aeae67fa69eb20e8ddcd9012103505f0d82bbdd251511591b34f36ad5eea37d3220c2b81a1189084431ddb3aa3d"
),
)
inp2 = messages.TxInputType(
address_n=parse_path("84'/1'/0'/1/0"),
amount=7289000,
prev_hash=TXHASH_65b811,
prev_index=1,
script_type=messages.InputScriptType.SPENDWITNESS,
)
# Other's coinjoined output.
out1 = messages.TxOutputType(
address="tb1qk7j3ahs2v6hrv4v282cf0tvxh0vqq7rpt3zcml",
amount=50000,
script_type=messages.OutputScriptType.PAYTOWITNESS,
)
# Our coinjoined output.
out2 = messages.TxOutputType(
address_n=parse_path("84'/1'/0'/1/1"),
amount=50000,
script_type=messages.OutputScriptType.PAYTOWITNESS,
)
# Our change output.
out3 = messages.TxOutputType(
address_n=parse_path("84'/1'/0'/1/2"),
amount=7289000 - 50000 - 5 - 5000,
script_type=messages.OutputScriptType.PAYTOWITNESS,
)
# Other's change output.
out4 = messages.TxOutputType(
address="tb1q9cqhdr9ydetjzrct6tyeuccws9505hl96azwxk",
amount=100000 - 50000 - 5 - 5000,
script_type=messages.OutputScriptType.PAYTOWITNESS,
)
# Coordinator's output.
out5 = messages.TxOutputType(
address="mvbu1Gdy8SUjTenqerxUaZyYjmveZvt33q",
amount=10,
script_type=messages.OutputScriptType.PAYTOWITNESS,
)
with client:
client.set_expected_responses(
[
messages.PreauthorizedRequest(),
request_input(0),
request_input(1),
request_meta(TXHASH_65b811),
request_input(0, TXHASH_65b811),
request_output(0, TXHASH_65b811),
request_output(1, TXHASH_65b811),
request_output(0),
request_output(1),
request_output(2),
request_output(3),
request_output(4),
request_input(0),
request_meta(TXHASH_e5b7e2),
request_input(0, TXHASH_e5b7e2),
request_output(0, TXHASH_e5b7e2),
request_output(1, TXHASH_e5b7e2),
request_input(0),
request_input(1),
request_output(0),
request_output(1),
request_output(2),
request_output(3),
request_output(4),
request_input(1),
request_finished(),
]
)
_, serialized_tx = btc.sign_tx(
client,
"Testnet",
[inp1, inp2],
[out1, out2, out3, out4, out5],
prev_txes=TX_CACHE_TESTNET,
preauthorized=True,
)
assert (
serialized_tx.hex()
== "010000000001028abbd1cf69e00fbf60fa3ba475dccdbdba4a859ffa6bfd1ee820a75b1be2b7e50000000000ffffffff7b010c5faeb41cc5c253121b6bf69bf1a7c5867cd7f2d91569fea0ecd311b8650100000000ffffffff0550c3000000000000160014b7a51ede0a66ae36558a3ab097ad86bbd800786150c3000000000000160014167dae080bca35c9ea49c0c8335dcc4b252a1d70cb616e00000000001600141d03a4d2167961b853d6cadfeab08e4937c5dfe8c3af0000000000001600142e01768ca46e57210f0bd2c99e630e8168fa5fe50a000000000000001976a914a579388225827d9f2fe9014add644487808c695d88ac00024730440220694105071db8c6c8ba3d385d01694b6f7c17546327ab26d4c53a6503fee301e202202dd310c23a195a6cebc904b91ebd15d782e6dacd08670a72ade2795e7d3ff4ec012103505647c017ff2156eb6da20fae72173d3b681a1d0a629f95f49e884db300689f00000000"
)
# Test for a second time.
btc.sign_tx(
client,
"Testnet",
[inp1, inp2],
[out1, out2, out3, out4, out5],
prev_txes=TX_CACHE_TESTNET,
preauthorized=True,
)
# Test for a third time, fees should exceed max_total_fee.
with pytest.raises(TrezorFailure, match="Fees exceed authorized limit"):
btc.sign_tx(
client,
"Testnet",
[inp1, inp2],
[out1, out2, out3, out4, out5],
prev_txes=TX_CACHE_TESTNET,
preauthorized=True,
)
def test_unfair_fee(client):
# Test unfair mining fee distribution amongst participants.
with client:
btc.authorize_coinjoin(
client,
coordinator="www.example.com",
max_total_fee=10000,
fee_per_anonymity=5000000, # 0.005 %
n=parse_path("m/84'/1'/0'"),
coin_name="Testnet",
script_type=messages.InputScriptType.SPENDWITNESS,
)
inp1 = messages.TxInputType(
# seed "alcohol woman abuse must during monitor noble actual mixed trade anger aisle"
# 84'/1'/0'/0/0
# tb1qnspxpr2xj9s2jt6qlhuvdnxw6q55jvygcf89r2
amount=100000,
prev_hash=TXHASH_e5b7e2,
prev_index=0,
script_type=messages.InputScriptType.EXTERNAL,
ownership_proof=bytearray.fromhex(
"534c001900016b2055d8190244b2ed2d46513c40658a574d3bc2deb6969c0535bb818b44d2c40002483045022100d4ad0374c922848c71d913fba59c81b9075e0d33e884d953f0c4b4806b8ffd0c022024740e6717a2b6a5aa03148c3a28b02c713b4e30fc8aeae67fa69eb20e8ddcd9012103505f0d82bbdd251511591b34f36ad5eea37d3220c2b81a1189084431ddb3aa3d"
),
)
inp2 = messages.TxInputType(
address_n=parse_path("84'/1'/0'/1/0"),
amount=7289000,
prev_hash=TXHASH_65b811,
prev_index=1,
script_type=messages.InputScriptType.SPENDWITNESS,
)
# Other's coinjoined output.
out1 = messages.TxOutputType(
address="tb1qk7j3ahs2v6hrv4v282cf0tvxh0vqq7rpt3zcml",
amount=50000,
script_type=messages.OutputScriptType.PAYTOWITNESS,
)
# Our coinjoined output.
out2 = messages.TxOutputType(
address_n=parse_path("84'/1'/0'/1/1"),
amount=50000,
script_type=messages.OutputScriptType.PAYTOWITNESS,
)
# Our change output.
out3 = messages.TxOutputType(
address_n=parse_path("84'/1'/0'/1/2"),
amount=7289000 - 50000 - 5 - 6000, # unfair mining fee
script_type=messages.OutputScriptType.PAYTOWITNESS,
)
# Other's change output.
out4 = messages.TxOutputType(
address="tb1q9cqhdr9ydetjzrct6tyeuccws9505hl96azwxk",
amount=100000 - 50000 - 5 - 4000,
script_type=messages.OutputScriptType.PAYTOWITNESS,
)
# Coordinator's output.
out5 = messages.TxOutputType(
address="mvbu1Gdy8SUjTenqerxUaZyYjmveZvt33q",
amount=10,
script_type=messages.OutputScriptType.PAYTOWITNESS,
)
with pytest.raises(TrezorFailure, match="fee over threshold"):
btc.sign_tx(
client,
"Testnet",
[inp1, inp2],
[out1, out2, out3, out4, out5],
prev_txes=TX_CACHE_TESTNET,
preauthorized=True,
)
def test_no_anonymity(client):
# Test CoinJoin transaction giving the user's outputs no gain in anonymity.
with client:
btc.authorize_coinjoin(
client,
coordinator="www.example.com",
max_total_fee=5005,
fee_per_anonymity=5000000, # 0.005 %
n=parse_path("m/84'/1'/0'"),
coin_name="Testnet",
script_type=messages.InputScriptType.SPENDWITNESS,
)
inp1 = messages.TxInputType(
# seed "alcohol woman abuse must during monitor noble actual mixed trade anger aisle"
# 84'/1'/0'/0/0
# tb1qnspxpr2xj9s2jt6qlhuvdnxw6q55jvygcf89r2
amount=100000,
prev_hash=TXHASH_e5b7e2,
prev_index=0,
script_type=messages.InputScriptType.EXTERNAL,
ownership_proof=bytearray.fromhex(
"534c001900016b2055d8190244b2ed2d46513c40658a574d3bc2deb6969c0535bb818b44d2c40002483045022100d4ad0374c922848c71d913fba59c81b9075e0d33e884d953f0c4b4806b8ffd0c022024740e6717a2b6a5aa03148c3a28b02c713b4e30fc8aeae67fa69eb20e8ddcd9012103505f0d82bbdd251511591b34f36ad5eea37d3220c2b81a1189084431ddb3aa3d"
),
)
inp2 = messages.TxInputType(
address_n=parse_path("84'/1'/0'/1/0"),
amount=7289000,
prev_hash=TXHASH_65b811,
prev_index=1,
script_type=messages.InputScriptType.SPENDWITNESS,
)
# Other's coinjoined output.
out1 = messages.TxOutputType(
address="tb1qk7j3ahs2v6hrv4v282cf0tvxh0vqq7rpt3zcml",
amount=30000,
script_type=messages.OutputScriptType.PAYTOWITNESS,
)
# Other's coinjoined output.
out2 = messages.TxOutputType(
address="tb1q9cqhdr9ydetjzrct6tyeuccws9505hl96azwxk",
amount=30000,
script_type=messages.OutputScriptType.PAYTOWITNESS,
)
# Our coinjoined output.
out3 = messages.TxOutputType(
address_n=parse_path("84'/1'/0'/1/1"),
amount=50000,
script_type=messages.OutputScriptType.PAYTOWITNESS,
)
# Our coinjoined output.
out4 = messages.TxOutputType(
address_n=parse_path("84'/1'/0'/1/2"),
amount=50000,
script_type=messages.OutputScriptType.PAYTOWITNESS,
)
# Our change output.
out5 = messages.TxOutputType(
address_n=parse_path("84'/1'/0'/1/2"),
amount=7289000 - 50000 - 50000 - 10 - 5000,
script_type=messages.OutputScriptType.PAYTOWITNESS,
)
# Other's change output.
out6 = messages.TxOutputType(
address="tb1q9cqhdr9ydetjzrct6tyeuccws9505hl96azwxk",
amount=100000 - 30000 - 30000 - 6 - 5000,
script_type=messages.OutputScriptType.PAYTOWITNESS,
)
# Coordinator's output.
out7 = messages.TxOutputType(
address="mvbu1Gdy8SUjTenqerxUaZyYjmveZvt33q",
amount=16,
script_type=messages.OutputScriptType.PAYTOWITNESS,
)
with pytest.raises(TrezorFailure, match="No anonymity gain"):
btc.sign_tx(
client,
"Testnet",
[inp1, inp2],
[out1, out2, out3, out4, out5, out6, out7],
prev_txes=TX_CACHE_TESTNET,
preauthorized=True,
)
def test_wrong_coordinator(client):
# Ensure that a preauthorized GetOwnershipProof fails if the commitment_data doesn't match the coordinator.
btc.authorize_coinjoin(
client,
max_total_fee=50000,
coordinator="www.example.com",
n=parse_path("m/84'/1'/0'"),
coin_name="Testnet",
script_type=messages.InputScriptType.SPENDWITNESS,
)
with pytest.raises(TrezorFailure, match="Unauthorized operation"):
ownership_proof, _ = btc.get_ownership_proof(
client,
"Testnet",
parse_path("84'/1'/0'/1/0"),
script_type=messages.InputScriptType.SPENDWITNESS,
user_confirmation=True,
commitment_data=b"www.example.org" + (1).to_bytes(ROUND_ID_LEN, "big"),
preauthorized=True,
)
def test_cancel_authorization(client):
# Ensure that a preauthorized GetOwnershipProof fails if the commitment_data doesn't match the coordinator.
btc.authorize_coinjoin(
client,
max_total_fee=50000,
coordinator="www.example.com",
n=parse_path("m/84'/1'/0'"),
coin_name="Testnet",
script_type=messages.InputScriptType.SPENDWITNESS,
)
device.cancel_authorization(client)
with pytest.raises(TrezorFailure, match="No preauthorized operation"):
ownership_proof, _ = btc.get_ownership_proof(
client,
"Testnet",
parse_path("84'/1'/0'/1/0"),
script_type=messages.InputScriptType.SPENDWITNESS,
user_confirmation=True,
commitment_data=b"www.example.com" + (1).to_bytes(ROUND_ID_LEN, "big"),
preauthorized=True,
)
@pytest.mark.skip_ui
def test_multisession_authorization(client):
# Authorize CoinJoin with www.example1.com in session 1.
btc.authorize_coinjoin(
client,
max_total_fee=50000,
coordinator="www.example1.com",
n=parse_path("m/84'/1'/0'"),
coin_name="Testnet",
script_type=messages.InputScriptType.SPENDWITNESS,
)
# Open a second session.
session_id1 = client.session_id
client.init_device(new_session=True)
# Authorize CoinJoin with www.example2.com in session 2.
btc.authorize_coinjoin(
client,
max_total_fee=50000,
coordinator="www.example2.com",
n=parse_path("m/84'/1'/0'"),
coin_name="Testnet",
script_type=messages.InputScriptType.SPENDWITNESS,
)
# Requesting a preauthorized ownership proof for www.example1.com should fail in session 2.
with pytest.raises(TrezorFailure, match="Unauthorized operation"):
ownership_proof, _ = btc.get_ownership_proof(
client,
"Testnet",
parse_path("84'/1'/0'/1/0"),
script_type=messages.InputScriptType.SPENDWITNESS,
user_confirmation=True,
commitment_data=b"www.example1.com" + (1).to_bytes(ROUND_ID_LEN, "big"),
preauthorized=True,
)
# Requesting a preauthorized ownership proof for www.example2.com should succeed in session 2.
ownership_proof, _ = btc.get_ownership_proof(
client,
"Testnet",
parse_path("84'/1'/0'/1/0"),
script_type=messages.InputScriptType.SPENDWITNESS,
user_confirmation=True,
commitment_data=b"www.example2.com" + (1).to_bytes(ROUND_ID_LEN, "big"),
preauthorized=True,
)
assert (
ownership_proof.hex()
== "534c00190101f3ce2cb33599634353452b60b38e311282b6fca743eb6147d3d492066c8963de0002483045022100ff4df2485a3206642ce7053902da16f26f0084faa2eb6288a1c27e389f057f4f02202268e0f4e253bd1387230b1ff3de315794e0b426f9cc9624e9c34fa73451164c012103505647c017ff2156eb6da20fae72173d3b681a1d0a629f95f49e884db300689f"
)
# Switch back to the first session.
session_id2 = client.session_id
client.init_device(session_id=session_id1)
# Requesting a preauthorized ownership proof for www.example1.com should succeed in session 1.
ownership_proof, _ = btc.get_ownership_proof(
client,
"Testnet",
parse_path("84'/1'/0'/1/0"),
script_type=messages.InputScriptType.SPENDWITNESS,
user_confirmation=True,
commitment_data=b"www.example1.com" + (1).to_bytes(ROUND_ID_LEN, "big"),
preauthorized=True,
)
assert (
ownership_proof.hex()
== "534c00190101f3ce2cb33599634353452b60b38e311282b6fca743eb6147d3d492066c8963de000247304402203b098674577c55c8d9151335c9e73ed74649fa01c461bd8390717bfca48167af02205ac35def1b0d7019fc492acb9bbd9914cf55e08e4f1a7e6d4f6f65cbc88b0bd2012103505647c017ff2156eb6da20fae72173d3b681a1d0a629f95f49e884db300689f"
)
# Requesting a preauthorized ownership proof for www.example2.com should fail in session 1.
with pytest.raises(TrezorFailure, match="Unauthorized operation"):
ownership_proof, _ = btc.get_ownership_proof(
client,
"Testnet",
parse_path("84'/1'/0'/1/0"),
script_type=messages.InputScriptType.SPENDWITNESS,
user_confirmation=True,
commitment_data=b"www.example2.com" + (1).to_bytes(ROUND_ID_LEN, "big"),
preauthorized=True,
)
# Cancel the authorization in session 1.
device.cancel_authorization(client)
# Requesting a preauthorized ownership proof should fail now.
with pytest.raises(TrezorFailure, match="No preauthorized operation"):
ownership_proof, _ = btc.get_ownership_proof(
client,
"Testnet",
parse_path("84'/1'/0'/1/0"),
script_type=messages.InputScriptType.SPENDWITNESS,
user_confirmation=True,
commitment_data=b"www.example1.com" + (1).to_bytes(ROUND_ID_LEN, "big"),
preauthorized=True,
)
# Switch to the second session.
client.init_device(session_id=session_id2)
# Requesting a preauthorized ownership proof for www.example2.com should still succeed in session 2.
ownership_proof, _ = btc.get_ownership_proof(
client,
"Testnet",
parse_path("84'/1'/0'/1/0"),
script_type=messages.InputScriptType.SPENDWITNESS,
user_confirmation=True,
commitment_data=b"www.example2.com" + (1).to_bytes(ROUND_ID_LEN, "big"),
preauthorized=True,
)

@ -1,96 +0,0 @@
# This file is part of the Trezor project.
#
# Copyright (C) 2012-2019 SatoshiLabs and contributors
#
# This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License version 3
# as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the License along with this library.
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
import pytest
from trezorlib import btc, messages
from trezorlib.exceptions import TrezorFailure
from trezorlib.tools import parse_path
pytestmark = pytest.mark.skip_t1
@pytest.mark.skip_ui
def test_ownership_id(client):
ownership_id = btc.get_ownership_id(
client,
"Bitcoin",
parse_path("m/84'/0'/0'/1/0"),
script_type=messages.InputScriptType.SPENDWITNESS,
)
assert (
ownership_id.hex()
== "a122407efc198211c81af4450f40b235d54775efd934d16b9e31c6ce9bad5707"
)
@pytest.mark.skip_ui
def test_p2wpkh_ownership_proof(client):
ownership_proof, _ = btc.get_ownership_proof(
client,
"Bitcoin",
parse_path("m/84'/0'/0'/1/0"),
script_type=messages.InputScriptType.SPENDWITNESS,
)
assert (
ownership_proof.hex()
== "534c00190001a122407efc198211c81af4450f40b235d54775efd934d16b9e31c6ce9bad57070002483045022100e5eaf2cb0a473b4545115c7b85323809e75cb106175ace38129fd62323d73df30220363dbc7acb7afcda022b1f8d97acb8f47c42043cfe0595583aa26e30bc8b3bb50121032ef68318c8f6aaa0adec0199c69901f0db7d3485eb38d9ad235221dc3d61154b"
)
@pytest.mark.skip_ui
def test_fake_ownership_id(client):
with pytest.raises(TrezorFailure, match="Invalid ownership identifier"):
btc.get_ownership_proof(
client,
"Bitcoin",
parse_path("m/84'/0'/0'/1/0"),
script_type=messages.InputScriptType.SPENDWITNESS,
ownership_ids=[
b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
],
)
def test_confirm_ownership_proof(client):
ownership_proof, _ = btc.get_ownership_proof(
client,
"Bitcoin",
parse_path("m/84'/0'/0'/1/0"),
script_type=messages.InputScriptType.SPENDWITNESS,
user_confirmation=True,
)
assert (
ownership_proof.hex()
== "534c00190101a122407efc198211c81af4450f40b235d54775efd934d16b9e31c6ce9bad57070002483045022100fa4561d261464360f18af9668c19e1b07d58f76814623aa8e5c9b2d85bc211d002207f364096183f461be75f763d2f970df6ac9b30a5a39556a07cab9c6f4d7883a80121032ef68318c8f6aaa0adec0199c69901f0db7d3485eb38d9ad235221dc3d61154b"
)
def test_confirm_ownership_proof_with_data(client):
ownership_proof, _ = btc.get_ownership_proof(
client,
"Bitcoin",
parse_path("m/84'/0'/0'/1/0"),
script_type=messages.InputScriptType.SPENDWITNESS,
user_confirmation=True,
commitment_data=b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f",
)
assert (
ownership_proof.hex()
== "534c00190101a122407efc198211c81af4450f40b235d54775efd934d16b9e31c6ce9bad57070002483045022100ac67fde3f0c8443c2708b3e405b17c4c8a51e510132b4f35aa6d6782713a53280220616192365f6202ee3f050d4e0e13c38198156024ca978fbd2b8c89c8823bb3dd0121032ef68318c8f6aaa0adec0199c69901f0db7d3485eb38d9ad235221dc3d61154b"
)

@ -1310,3 +1310,54 @@ class TestMsgSigntx:
btc.sign_tx(
client, "Testnet", [inp1], [out1, out2], prev_txes=TX_CACHE_TESTNET
)
@pytest.mark.parametrize(
"lock_time, sequence",
((499999999, 0xFFFFFFFE), (500000000, 0xFFFFFFFE), (1, 0xFFFFFFFF)),
)
def test_lock_time(self, client, lock_time, sequence):
# tx: d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882
# input 0: 0.0039 BTC
inp1 = messages.TxInputType(
address_n=parse_path("44h/0h/0h/0/0"),
# amount=390000,
prev_hash=TXHASH_d5f65e,
prev_index=0,
sequence=sequence,
)
out1 = messages.TxOutputType(
address="1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1",
amount=390000 - 10000,
script_type=messages.OutputScriptType.PAYTOADDRESS,
)
with client:
client.set_expected_responses(
[
request_input(0),
request_meta(TXHASH_d5f65e),
request_input(0, TXHASH_d5f65e),
request_input(1, TXHASH_d5f65e),
request_output(0, TXHASH_d5f65e),
request_output(0),
messages.ButtonRequest(code=B.ConfirmOutput),
messages.ButtonRequest(code=B.SignTx),
messages.ButtonRequest(code=B.SignTx),
request_input(0),
request_output(0),
request_output(0),
request_finished(),
]
)
details = messages.SignTx(lock_time=lock_time)
btc.sign_tx(
client,
"Bitcoin",
[inp1],
[out1],
details=details,
prev_txes=TX_CACHE_MAINNET,
)

@ -60,7 +60,6 @@ class TestMsgSigntxKomodo:
script_type=proto.OutputScriptType.PAYTOADDRESS,
)
trezor_core = client.features.model != "1"
with client:
client.set_expected_responses(
[
@ -71,7 +70,7 @@ class TestMsgSigntxKomodo:
request_extra_data(0, 11, TXHASH_2807c),
request_output(0),
proto.ButtonRequest(code=B.ConfirmOutput),
(trezor_core, proto.ButtonRequest(code=B.SignTx)),
proto.ButtonRequest(code=B.SignTx),
proto.ButtonRequest(code=B.SignTx),
request_input(0),
request_output(0),
@ -120,7 +119,6 @@ class TestMsgSigntxKomodo:
script_type=proto.OutputScriptType.PAYTOADDRESS,
)
trezor_core = client.features.model != "1"
with client:
client.set_expected_responses(
[
@ -133,7 +131,7 @@ class TestMsgSigntxKomodo:
proto.ButtonRequest(code=B.ConfirmOutput),
request_output(1),
proto.ButtonRequest(code=B.ConfirmOutput),
(trezor_core, proto.ButtonRequest(code=B.SignTx)),
proto.ButtonRequest(code=B.SignTx),
proto.ButtonRequest(code=B.SignTx),
request_input(0),
request_output(0),

@ -137,7 +137,12 @@ def test_session_recycling(client):
session_id_orig = client.session_id
with client:
client.set_expected_responses(
[messages.PassphraseRequest(), messages.Address()]
[
messages.PassphraseRequest(),
messages.ButtonRequest(),
messages.ButtonRequest(),
messages.Address(),
]
)
client.use_passphrase("TREZOR")
address = get_test_address(client)

@ -18,7 +18,7 @@ import random
import pytest
from trezorlib import messages
from trezorlib import exceptions, messages
from trezorlib.messages import FailureType
from trezorlib.tools import parse_path
@ -53,12 +53,21 @@ def _init_session(client, session_id=None):
def _get_xpub(client, passphrase=None):
"""Get XPUB and check that the appropriate passphrase flow has happened."""
response = client.call_raw(XPUB_REQUEST)
if passphrase is not None:
assert isinstance(response, messages.PassphraseRequest)
response = client.call_raw(messages.PassphraseAck(passphrase=passphrase))
assert isinstance(response, messages.PublicKey)
return response.xpub
expected_responses = [
messages.PassphraseRequest(),
messages.ButtonRequest(),
messages.ButtonRequest(),
messages.PublicKey(),
]
else:
expected_responses = [messages.PublicKey()]
with client:
client.use_passphrase(passphrase or "")
client.set_expected_responses(expected_responses)
result = client.call(XPUB_REQUEST)
return result.xpub
@pytest.mark.skip_ui
@ -239,7 +248,9 @@ def test_passphrase_on_device(client):
# try to get xpub with passphrase on host:
response = client.call_raw(XPUB_REQUEST)
assert isinstance(response, messages.PassphraseRequest)
response = client.call_raw(messages.PassphraseAck(passphrase="A", on_device=False))
# using `client.call` to auto-skip subsequent ButtonRequests for "show passphrase"
response = client.call(messages.PassphraseAck(passphrase="A", on_device=False))
assert isinstance(response, messages.PublicKey)
assert response.xpub == XPUB_PASSPHRASES["A"]
@ -255,6 +266,7 @@ def test_passphrase_on_device(client):
response = client.call_raw(XPUB_REQUEST)
assert isinstance(response, messages.PassphraseRequest)
response = client.call_raw(messages.PassphraseAck(on_device=True))
# no "show passphrase" here
assert isinstance(response, messages.ButtonRequest)
client.debug.input("A")
response = client.call_raw(messages.ButtonAck())
@ -352,12 +364,13 @@ def test_passphrase_length(client):
_init_session(client)
response = client.call_raw(XPUB_REQUEST)
assert isinstance(response, messages.PassphraseRequest)
response = client.call_raw(messages.PassphraseAck(passphrase))
if expected_result:
try:
response = client.call(messages.PassphraseAck(passphrase))
assert expected_result is True, "Call should have failed"
assert isinstance(response, messages.PublicKey)
else:
assert isinstance(response, messages.Failure)
assert response.code == FailureType.DataError
except exceptions.TrezorFailure as e:
assert expected_result is False, "Call should have succeeded"
assert e.code == FailureType.DataError
# 50 is ok
call(passphrase="A" * 50, expected_result=True)
@ -374,7 +387,7 @@ def _get_xpub_cardano(client, passphrase):
response = client.call_raw(msg)
if passphrase is not None:
assert isinstance(response, messages.PassphraseRequest)
response = client.call_raw(messages.PassphraseAck(passphrase=passphrase))
response = client.call(messages.PassphraseAck(passphrase=passphrase))
assert isinstance(response, messages.CardanoPublicKey)
return response.xpub

@ -5,9 +5,9 @@
"cardano-test_sign_tx.py::test_cardano_sign_tx[parameters11-result11]": "c32706d1092edf9ac2504c88eddfe3e55b8a5b6c0e2d6fcd7fbf84232aabfcb8",
"cardano-test_sign_tx.py::test_cardano_sign_tx[parameters12-result12]": "39c495f6c8d1a046044b8d49569f51f615b163abeae98c0be8313761de828862",
"cardano-test_sign_tx.py::test_cardano_sign_tx[parameters13-result13]": "f03f064e8829a27a49296c28755493983d86a235ddeac1c926c7195dd254940f",
"cardano-test_sign_tx.py::test_cardano_sign_tx[parameters14-result14]": "6aa71de5007b0faf1eea4b1cfda1da6a739f852c0d875a1e59d83c03178c2f98",
"cardano-test_sign_tx.py::test_cardano_sign_tx[parameters15-result15]": "7abf2e87a9b1e50afdf3502ba9480b07a59d59ccccf24915b46fb81285ae3fa8",
"cardano-test_sign_tx.py::test_cardano_sign_tx[parameters16-result16]": "d488a0f2c127a675a1c2e2d410b6c4f402cdb610e19886a8998aa5ad786a779e",
"cardano-test_sign_tx.py::test_cardano_sign_tx[parameters14-result14]": "623341dfed3aaca40284ec5b444fc768edc5af9c706d8c4e4f7a5e1e90343652",
"cardano-test_sign_tx.py::test_cardano_sign_tx[parameters15-result15]": "0f79d964628581aae91593f7a1e7bf9b4748b900d7973e1b48a78382cc8f6c4e",
"cardano-test_sign_tx.py::test_cardano_sign_tx[parameters16-result16]": "4597efa8c2d34df7ab70c626a244d14b783fa7be1f88f213c2f9a39d726976e2",
"cardano-test_sign_tx.py::test_cardano_sign_tx[parameters2-result2]": "539936eee440830f64536228980a78b098a8e421e8b6c819fe49bc8efcac9b18",
"cardano-test_sign_tx.py::test_cardano_sign_tx[parameters3-result3]": "6aa71de5007b0faf1eea4b1cfda1da6a739f852c0d875a1e59d83c03178c2f98",
"cardano-test_sign_tx.py::test_cardano_sign_tx[parameters4-result4]": "68505427a1021dd3ef7dea03956d1ea3167c8fa3016e90328151618c45d7695e",
@ -55,11 +55,6 @@
"test_msg_applysettings.py-test_apply_settings_passphrase": "5c1ed9a0be3d14475102d447da0b5d51bbb6dfaaeceff5ea9179064609db7870",
"test_msg_applysettings.py-test_apply_settings_passphrase_on_device": "3e6527e227bdde54f51bc9c417b176d0d87fdb6c40c4761368f50eb201b4beed",
"test_msg_applysettings.py-test_safety_checks": "19bd500c3b791d51bbd1140085f306a838194593697529263f362acb0b1ab445",
"test_msg_authorize_coinjoin.py::test_cancel_authorization": "d8a608beb6165f5667cc44dcff6bdc17ebb4638ddd3bd09e7f0e1e75d1e21135",
"test_msg_authorize_coinjoin.py::test_no_anonymity": "fd09da284b650e893990b95047b63a35b6b695fc5301d595f17a6d2cf9d90bcb",
"test_msg_authorize_coinjoin.py::test_sign_tx": "2838d4062333c241b6bbef7e680ec8a5764fe7bcaa41419e4141e146d3586a5d",
"test_msg_authorize_coinjoin.py::test_unfair_fee": "62314e936de46a6caaf02c8eb20f6f471be6e79ca0c5450cad6f67f9cb823f2b",
"test_msg_authorize_coinjoin.py::test_wrong_coordinator": "d8a608beb6165f5667cc44dcff6bdc17ebb4638ddd3bd09e7f0e1e75d1e21135",
"test_msg_backup_device.py::test_backup_bip39": "42325cccfc0bd54db180a01a076437ec981022338307339bb5e0f6463d842e23",
"test_msg_backup_device.py::test_backup_slip39_advanced": "9a01aa5ecdafa52571ed2149575f86ffea6c2984a2541ea8e5f9a7c37cf4c9fe",
"test_msg_backup_device.py::test_backup_slip39_basic": "57d841257b10c4f67cf76487c8f0bc95947a93a8e0b8c03d7a11894da3c233da",
@ -157,8 +152,6 @@
"test_msg_getentropy.py::test_entropy[65]": "a722fa2048fa3102889ec05558d25f837a364ef2a118e85975683e10a56f1356",
"test_msg_getentropy.py::test_entropy[8]": "a722fa2048fa3102889ec05558d25f837a364ef2a118e85975683e10a56f1356",
"test_msg_getentropy.py::test_entropy[9]": "a722fa2048fa3102889ec05558d25f837a364ef2a118e85975683e10a56f1356",
"test_msg_getownershipproof.py::test_confirm_ownership_proof": "7724ef59fee121da564b935b5880479c9518d97dc286e2949f13a8a8bdf6fa4a",
"test_msg_getownershipproof.py::test_confirm_ownership_proof_with_data": "1043f54be594bd3180c59c8474ef7ef46be35f99ad5f6890949c38680c097e52",
"test_msg_lisk_getaddress.py-test_lisk_getaddress": "0063ceb48d21aecd1ddabdb083c8afd2042cdf577e4751fa3f57b2b80f619084",
"test_msg_lisk_getpublickey.py-test_lisk_get_public_key": "e4cb8c7430c240e27a2211391ab5eba848be4f50136cf9f693142c2677a939d7",
"test_msg_lisk_signmessage.py-test_sign": "c47a6ec147137c75903cff19da6607eaef5a1fc03ace1f840d2952744342b568",
@ -173,10 +166,10 @@
"test_msg_lisk_verifymessage.py-test_verify": "45df85077b20182803b5c4363386c555845e070f3a8a019add99e34dad510a07",
"test_msg_lisk_verifymessage.py-test_verify_long": "d7d0ae3402b9ca6c7b0e61164fa483c4ba9549d306780c98ae15edd2dde51285",
"test_msg_loaddevice.py-test_load_device_1": "1c6db0d592b1d22b3c9fce3ddab8a9fd138f11d83e5d4e64431a02bf4ffed605",
"test_msg_loaddevice.py-test_load_device_2": "06426c64d246068fe80fea79b0afd06d3eac4545fbd711279b816c0fdbf1b794",
"test_msg_loaddevice.py-test_load_device_2": "dc13c8486d8a59c5062e19139d8b3cea4ece1a3bc93592be7dc226f83ba54477",
"test_msg_loaddevice.py-test_load_device_slip39_advanced": "1c6db0d592b1d22b3c9fce3ddab8a9fd138f11d83e5d4e64431a02bf4ffed605",
"test_msg_loaddevice.py-test_load_device_slip39_basic": "1c6db0d592b1d22b3c9fce3ddab8a9fd138f11d83e5d4e64431a02bf4ffed605",
"test_msg_loaddevice.py-test_load_device_utf": "dfac758aefbebd2594455986f86bb2c3b91c2428e15de6ed00f82145c0b54089",
"test_msg_loaddevice.py-test_load_device_utf": "ad7c162c76a13a161166aba78c461ad5525a9a5da846e8d99854248d521e6979",
"test_msg_monero_getaddress.py-test_monero_getaddress": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586",
"test_msg_monero_getwatchkey.py-test_monero_getwatchkey": "d77fa4d4322e145c41f1ce07526ff59f8b58d8854aeffaa5266e14cd572350e7",
"test_msg_nem_getaddress.py-test_nem_getaddress": "e726f99401a20eb74c33d755cecea2a3f69b7ae5b541302677ee05f80f5aef19",
@ -268,6 +261,9 @@
"test_msg_signtx.py-test_change_on_main_chain_allowed": "cfd5c83510c044c456622298138e222aee135a6df607bb6e5603228535f0762f",
"test_msg_signtx.py-test_fee_high_hardfail": "b450a59808fb20cbd01d34e8d24bf1a5814e9b2a10109710240c617b68e247b6",
"test_msg_signtx.py-test_fee_high_warning": "8cb3b31dce25fa36cd5c8322c71611dc7bc9d2290579ffd88dd67d21058bde04",
"test_msg_signtx.py-test_lock_time[1-4294967295]": "d805244ea557c3695101a6f79f13045f22bc16d5608744e0321eab7f3a98d8b0",
"test_msg_signtx.py-test_lock_time[499999999-4294967294]": "23a154e7b40680161bb099cfc6702d75909c222056867515647123573eef1716",
"test_msg_signtx.py-test_lock_time[500000000-4294967294]": "2ac61446c20785e45223a20ae90660905fedf20d2a383a65fcbc1edd5fe87ad1",
"test_msg_signtx.py-test_lots_of_change": "9e143458b399d187b6a3060fc95b998822f5a7ed67d6915610fd02c0ccab791e",
"test_msg_signtx.py-test_not_enough_funds": "dbaa027aa1f4b08b138a5965245593dab2a662b0f4d88dd28b82a64f88f5d7fe",
"test_msg_signtx.py-test_one_one_fee": "f6b6662fa1384f20640522a169575f8ca26185fca8ca3bc2a3a5ccd1fa9d2f68",
@ -311,14 +307,14 @@
"test_msg_signtx_external.py::test_p2wsh_external_presigned": "8374d50b803db0160de39ce7e5a4170112f4c99d703490920a2de735bd261bda",
"test_msg_signtx_grs.py-test_legacy": "3a80ea724a93ed225d64f8def739d63b11f8c096455f971feabec8be6f7597fb",
"test_msg_signtx_grs.py-test_legacy_change": "8dfc140534bdaa08f6916831dc0d510f57b07617f30df748e4e0456d4dd93ece",
"test_msg_signtx_grs.py-test_send_segwit_native": "a1098f71dd6c197cb8a4a45806808fbacc130044585cb003e61110d8bda80114",
"test_msg_signtx_grs.py-test_send_segwit_native_change": "2b9119aea79b50a26bd86cc49fd5421aedee42245b83919bf142d2ca2e50dd16",
"test_msg_signtx_grs.py-test_send_segwit_p2sh": "e596ff7f2e10633d8de6bd5b98928020dfacf578ce71d6d6e0ca27011ed8bd53",
"test_msg_signtx_grs.py-test_send_segwit_p2sh_change": "65a33cc9ecf52bdab93d1066bfaff7f30c9908fc5ada8a672c39df57eb6129c2",
"test_msg_signtx_grs.py-test_send_segwit_native": "82dfa15178d33e757da58943aff36dcc0eebb984e34832b71f6ca09b2a525cbc",
"test_msg_signtx_grs.py-test_send_segwit_native_change": "d8ae74de3aada1d136c4119f2306a63bd109901ce15d00ae916ba5b4457e798e",
"test_msg_signtx_grs.py-test_send_segwit_p2sh": "9ab885dd3b390813f8a47e1d1587abe07ab713e9f8696dc667b3a2925f23c103",
"test_msg_signtx_grs.py-test_send_segwit_p2sh_change": "6c352ab975a75a150f7c3415a967fb8635395ff8db0de89ecb9c2011cb519509",
"test_msg_signtx_invalid_path.py-test_invalid_path_fail": "b0f22cba2dbab2cd21c15c002b66ed89b6c728b10daa8d0c0e78abd4164a3912",
"test_msg_signtx_invalid_path.py-test_invalid_path_pass_forkid": "667dcb09b569e5b4e091e6b1ac7e8e057c0c730c931b22f8c0ee64050f3f467b",
"test_msg_signtx_komodo.py-test_one_one_fee_sapling": "aa32424c04f65c0e2de57f6eb26830eb037c7b4daaf300ae61d30968affd9193",
"test_msg_signtx_komodo.py-test_one_one_rewards_claim": "e53f221fda81469027e39e21877a81a8fafbffbece0a45aeda12aae8873b0464",
"test_msg_signtx_komodo.py-test_one_one_fee_sapling": "14bad8852ee51f6fec12677cced9ffafa0cbae91b4ba94e988a800544072ed21",
"test_msg_signtx_komodo.py-test_one_one_rewards_claim": "751e83d63bf01c6c57047b5e004629d613df75342371cd43a7b4b80a07f4b88d",
"test_msg_signtx_peercoin.py::test_timestamp_included": "825b9bdf5238c5c6415a254a6bae4b2bd9df8fc5cb31f66f0c20145cb4e60bbb",
"test_msg_signtx_segwit.py-test_attack_change_input_address": "5ae71202c062ef7942626a80a4ceeb8d8c4ea5065a97f0de6a97505e9cb82c2c",
"test_msg_signtx_segwit.py-test_attack_mixed_inputs": "ed4cf8ff26ca1abb0adf20e1020dad7861b5e63ead2a1a9c0c5e9080d37f6f1c",
@ -396,10 +392,10 @@
"test_multisig_change.py-test_multisig_mismatch_change": "7cb243b20be31a587dced4aaaf782a2d8487595369dde66aacb1b9a76e89c4fe",
"test_multisig_change.py-test_multisig_mismatch_inputs": "64741bd84c5394e719125c1fbe8c34ef866ac63ca24ee1299e4268c59a199466",
"test_op_return.py-test_opreturn": "87907ef9c2f4ce30ac95ad7d0cb3eac66762756e4ace52147bc589d64277f3b1",
"test_passphrase_slip39_advanced.py::test_128bit_passphrase": "3a92115b6bfb2d53f2445a67c9c5df6b6b5ff97769de98e3fac9e1bf424c5669",
"test_passphrase_slip39_advanced.py::test_256bit_passphrase": "3a92115b6bfb2d53f2445a67c9c5df6b6b5ff97769de98e3fac9e1bf424c5669",
"test_passphrase_slip39_basic.py::test_2of5_passphrase": "612dad8ab8762162a186ec9279d7de0bdfc589c52b4e4f4eba0545a00f21c3f0",
"test_passphrase_slip39_basic.py::test_3of6_passphrase": "612dad8ab8762162a186ec9279d7de0bdfc589c52b4e4f4eba0545a00f21c3f0",
"test_passphrase_slip39_advanced.py::test_128bit_passphrase": "4d8c7eea0bd6786a070880d94fe288294e570b8d357dd9bc01865a8acc39d143",
"test_passphrase_slip39_advanced.py::test_256bit_passphrase": "4d8c7eea0bd6786a070880d94fe288294e570b8d357dd9bc01865a8acc39d143",
"test_passphrase_slip39_basic.py::test_2of5_passphrase": "54091a6faba4ecc4e40db591bb8861d00464de34b38a31925202f5bc44e4c41c",
"test_passphrase_slip39_basic.py::test_3of6_passphrase": "54091a6faba4ecc4e40db591bb8861d00464de34b38a31925202f5bc44e4c41c",
"test_reset_backup.py::test_skip_backup_manual[0-backup_flow_bip39]": "9c5cded50e6ebe51dc6ecdaa6b793da9ec5527df582acbdc189494b809ee9f47",
"test_reset_backup.py::test_skip_backup_manual[1-backup_flow_slip39_basic]": "bf9ea5281234d622b39f388715dad86e95f0a0e1efbd3d4d6b60478a79edcc23",
"test_reset_backup.py::test_skip_backup_manual[2-backup_flow_slip39_advanced]": "e3fb56f53d04edde94aa11e5eac1e2dc732bddcd39def3981e03cffcbef1a96c",

@ -68,9 +68,20 @@ def test_passphrase_works(emulator):
messages.Deprecated_PassphraseStateRequest(),
messages.Address(),
]
elif (
emulator.client.features.model == "T" and emulator.client.version < (2, 3, 3)
) or (
emulator.client.features.model == "1" and emulator.client.version < (1, 9, 3)
):
expected_responses = [
messages.PassphraseRequest(),
messages.Address(),
]
else:
expected_responses = [
messages.PassphraseRequest(),
messages.ButtonRequest(),
messages.ButtonRequest(),
messages.Address(),
]
@ -96,9 +107,22 @@ def test_init_device(emulator):
messages.Features(),
messages.Address(),
]
elif (
emulator.client.features.model == "T" and emulator.client.version < (2, 3, 3)
) or (
emulator.client.features.model == "1" and emulator.client.version < (1, 9, 3)
):
expected_responses = [
messages.PassphraseRequest(),
messages.Address(),
messages.Features(),
messages.Address(),
]
else:
expected_responses = [
messages.PassphraseRequest(),
messages.ButtonRequest(),
messages.ButtonRequest(),
messages.Address(),
messages.Features(),
messages.Address(),

Loading…
Cancel
Save