Merge branch 'Q' of github.com:Coldcard/q1firmware into Q
This commit is contained in:
commit
d54f57457e
@ -1043,7 +1043,7 @@ async def export_xpub(label, _2, item):
|
||||
if '{acct}' in path:
|
||||
msg += "Press (1) to select account other than zero. "
|
||||
if glob.NFC:
|
||||
msg += "Press (" + ("nfc" if version.has_qwerty else "3") + ") to share via NFC. "
|
||||
msg += "Press %s to share via NFC. " % (KEY_NFC if version.has_qwerty else "(3)")
|
||||
|
||||
ch = await ux_show_story(msg, escape='13')
|
||||
if ch == 'x': return
|
||||
@ -1816,13 +1816,14 @@ or upload a transaction to be signed \
|
||||
from your desktop wallet software or command line tools.\n\n'''
|
||||
|
||||
if NFC:
|
||||
msg += 'Press (3) to send PSBT using NFC.\n\n'
|
||||
msg += 'Press %s to send PSBT using NFC.\n\n' % (KEY_NFC if version.has_qwerty else "(3)")
|
||||
|
||||
msg += "You will always be prompted to confirm the details before \
|
||||
any signature is performed."
|
||||
|
||||
ch = await ux_show_story(msg, title=title, escape='3')
|
||||
if ch == '3' and NFC:
|
||||
target_nfc = KEY_NFC if version.has_qwerty else "3"
|
||||
if ch == target_nfc and NFC:
|
||||
await NFC.start_psbt_rx()
|
||||
|
||||
return
|
||||
|
||||
@ -358,7 +358,7 @@ Press (3) if you really understand and accept these risks.
|
||||
if n > 1:
|
||||
await NFC.share_text('\n'.join(addrs))
|
||||
elif n == 1:
|
||||
await NFC.share_deposit_address(addrs[0])
|
||||
await NFC.share_text(addrs[0])
|
||||
continue
|
||||
|
||||
elif choice == '0' and allow_change:
|
||||
|
||||
@ -811,7 +811,7 @@ class ApproveTransaction(UserAuthorizedAction):
|
||||
tmsg = txid + '\n\nPress (1) for QR Code of TXID. '
|
||||
|
||||
if NFC:
|
||||
tmsg += 'Press (3) to share signed txn via NFC.'
|
||||
tmsg += 'Press %s to share signed txn via NFC.' % (KEY_NFC if version.has_qwerty else "(3)")
|
||||
|
||||
ch = await ux_show_story(tmsg, "Final TXID", escape='13')
|
||||
|
||||
@ -819,7 +819,8 @@ class ApproveTransaction(UserAuthorizedAction):
|
||||
await show_qr_code(txid, True)
|
||||
continue
|
||||
|
||||
if ch == '3' and NFC:
|
||||
target_nfc = KEY_NFC if version.has_qwerty else "3"
|
||||
if ch == target_nfc and NFC:
|
||||
await NFC.share_signed_txn(txid, TXN_OUTPUT_OFFSET,
|
||||
self.result[0], self.result[1])
|
||||
continue
|
||||
@ -971,7 +972,7 @@ def psbt_encoding_taster(taste, psbt_len):
|
||||
# look at first 10 bytes, and detect file encoding (binary, hex, base64)
|
||||
# - return len is upper bound on size because of unknown whitespace
|
||||
from utils import HexStreamer, Base64Streamer, HexWriter, Base64Writer
|
||||
|
||||
taste = bytes(taste)
|
||||
if taste[0:5] == b'psbt\xff':
|
||||
decoder = None
|
||||
output_encoder = lambda x: x
|
||||
@ -1291,7 +1292,7 @@ class ShowAddressBase(UserAuthorizedAction):
|
||||
|
||||
if not version.has_qwerty:
|
||||
if NFC:
|
||||
msg += ' Press (3) to share via NFC.'
|
||||
msg += ' Press %s to share via NFC.' % (KEY_NFC if version.has_qwerty else "(3)")
|
||||
msg += ' Press (4) to view QR Code.'
|
||||
|
||||
while 1:
|
||||
|
||||
@ -90,7 +90,7 @@ def probe_system():
|
||||
has_battery = False
|
||||
has_qwerty = False
|
||||
is_edge = False
|
||||
supports_hsm = False
|
||||
supports_hsm = True
|
||||
has_nfc = True
|
||||
|
||||
cpuid = ckcc.get_cpu_id()
|
||||
|
||||
@ -158,12 +158,12 @@ def need_keypress(dev, request):
|
||||
return doit
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def enter_number(need_keypress):
|
||||
def enter_number(need_keypress, is_q1):
|
||||
def doit(number):
|
||||
number = str(number) if not isinstance(number, str) else number
|
||||
for d in number:
|
||||
need_keypress(d)
|
||||
need_keypress('y')
|
||||
need_keypress(KEY_ENTER if is_q1 else 'y')
|
||||
|
||||
return doit
|
||||
|
||||
@ -1564,14 +1564,14 @@ def nfc_read(request, needs_nfc):
|
||||
return doit_usb
|
||||
|
||||
@pytest.fixture()
|
||||
def nfc_write(request, needs_nfc):
|
||||
def nfc_write(request, needs_nfc, is_q1):
|
||||
# WRITE data into NFC "chip"
|
||||
def doit_usb(ccfile):
|
||||
sim_exec = request.getfixturevalue('sim_exec')
|
||||
need_keypress = request.getfixturevalue('need_keypress')
|
||||
rv = sim_exec('list(glob.NFC.big_write(%r))' % ccfile)
|
||||
if 'Traceback' in rv: raise pytest.fail(rv)
|
||||
need_keypress('y') # to end the animation and have it check value immediately
|
||||
need_keypress(KEY_ENTER if is_q1 else 'y') # to end the animation and have it check value immediately
|
||||
|
||||
try:
|
||||
raise NotImplementedError
|
||||
@ -1753,14 +1753,14 @@ def load_export_and_verify_signature(microsd_path, virtdisk_path, verify_detache
|
||||
|
||||
@pytest.fixture
|
||||
def load_export(need_keypress, cap_story, microsd_path, virtdisk_path, nfc_read_text, nfc_read_json,
|
||||
load_export_and_verify_signature):
|
||||
load_export_and_verify_signature, is_q1):
|
||||
def doit(way, label, is_json, sig_check=True, addr_fmt=AF_CLASSIC, ret_sig_addr=False,
|
||||
tail_check=None, sd_key=None, vdisk_key=None, nfc_key=None, ret_fname=False,
|
||||
fpattern=None):
|
||||
key_map = {
|
||||
"sd": sd_key or "1",
|
||||
"vdisk": vdisk_key or "2",
|
||||
"nfc": nfc_key or "3",
|
||||
"nfc": nfc_key or (KEY_NFC if is_q1 else "(3)"),
|
||||
}
|
||||
time.sleep(0.2)
|
||||
title, story = cap_story()
|
||||
@ -1769,7 +1769,7 @@ def load_export(need_keypress, cap_story, microsd_path, virtdisk_path, nfc_read_
|
||||
need_keypress(key_map['sd'])
|
||||
|
||||
elif way == "nfc":
|
||||
if f"({key_map['nfc']}) to share via NFC" not in story:
|
||||
if f"{key_map['nfc']} to share via NFC" not in story:
|
||||
pytest.skip("NFC disabled")
|
||||
else:
|
||||
need_keypress(key_map['nfc'])
|
||||
@ -1779,7 +1779,7 @@ def load_export(need_keypress, cap_story, microsd_path, virtdisk_path, nfc_read_
|
||||
else:
|
||||
nfc_export = nfc_read_text()
|
||||
time.sleep(0.3)
|
||||
need_keypress("x") # exit NFC animation
|
||||
need_keypress(KEY_CANCEL if is_q1 else "x") # exit NFC animation
|
||||
return nfc_export
|
||||
else:
|
||||
# virtual disk
|
||||
@ -1815,7 +1815,7 @@ def load_export(need_keypress, cap_story, microsd_path, virtdisk_path, nfc_read_
|
||||
if is_json:
|
||||
export = json.loads(export)
|
||||
|
||||
need_keypress("y")
|
||||
need_keypress(KEY_ENTER if is_q1 else "y")
|
||||
|
||||
if ret_sig_addr and sig_addr:
|
||||
return export, sig_addr
|
||||
|
||||
@ -6,7 +6,7 @@ from pycoin.key.BIP32Node import BIP32Node
|
||||
from pycoin.contrib.segwit_addr import encode as sw_encode
|
||||
from pycoin.encoding import a2b_hashed_base58, hash160
|
||||
from helpers import detruncate_address
|
||||
from charcodes import KEY_QR, KEY_NFC, KEY_LEFT, KEY_RIGHT, KEY_ENTER
|
||||
from charcodes import KEY_QR, KEY_NFC, KEY_LEFT, KEY_RIGHT, KEY_ENTER, KEY_CANCEL
|
||||
|
||||
@pytest.fixture
|
||||
def mk_common_derivations():
|
||||
@ -108,7 +108,7 @@ def generate_addresses_file(goto_address_explorer, need_keypress, cap_story, mic
|
||||
time.sleep(0.3)
|
||||
addresses = nfc_read_text()
|
||||
time.sleep(0.3)
|
||||
need_keypress("y")
|
||||
need_keypress(KEY_ENTER if is_q1 else "y")
|
||||
# nfc just returns 10 addresses
|
||||
assert len(addresses.split("\n")) == 10
|
||||
raise pytest.xfail("PASSED - different export format for NFC")
|
||||
@ -135,7 +135,7 @@ def generate_addresses_file(goto_address_explorer, need_keypress, cap_story, mic
|
||||
|
||||
def test_stub_menu(sim_execfile, goto_address_explorer, need_keypress,
|
||||
cap_menu, mk_common_derivations, pick_menu_item,
|
||||
parse_display_screen, validate_address):
|
||||
parse_display_screen, validate_address, is_q1):
|
||||
# For a given wallet, ensure the explorer shows the correct stub addresses
|
||||
node_prv = BIP32Node.from_wallet_key(
|
||||
sim_execfile('devtest/dump_private.py').strip()
|
||||
@ -167,7 +167,7 @@ def test_stub_menu(sim_execfile, goto_address_explorer, need_keypress,
|
||||
start, end = detruncate_address(m[_id])
|
||||
assert expected_addr.startswith(start)
|
||||
assert expected_addr.endswith(end)
|
||||
need_keypress("x")
|
||||
need_keypress(KEY_CANCEL if is_q1 else "x")
|
||||
|
||||
@pytest.mark.parametrize("chain", ["BTC", "XRT", "XTN"])
|
||||
@pytest.mark.parametrize("change", [True, False])
|
||||
@ -248,7 +248,7 @@ def test_address_display(goto_address_explorer, parse_display_screen, mk_common_
|
||||
sk = node_prv.subkey_for_path(subpath[2:])
|
||||
validate_address(given_addr, sk)
|
||||
|
||||
need_keypress("x") # back
|
||||
need_keypress(KEY_CANCEL if is_q1 else "x") # back
|
||||
|
||||
@pytest.mark.parametrize('click_idx', ["Classic P2PKH", "P2SH-Segwit", "Segwit P2WPKH"])
|
||||
@pytest.mark.parametrize("change", [True, False])
|
||||
@ -334,8 +334,8 @@ def test_account_menu(way, account_num, sim_execfile, pick_menu_item,
|
||||
sk = node_prv.subkey_for_path(subpath[2:])
|
||||
validate_address(addr, sk)
|
||||
|
||||
need_keypress('x')
|
||||
need_keypress('x')
|
||||
need_keypress(KEY_CANCEL if is_q1 else 'x')
|
||||
need_keypress(KEY_CANCEL if is_q1 else 'x')
|
||||
|
||||
# NOTE: (2**31)-1 = 0x7fff_ffff = 2147483647
|
||||
|
||||
@ -348,7 +348,10 @@ def test_account_menu(way, account_num, sim_execfile, pick_menu_item,
|
||||
"m/1'/2'/3'/4'/5'",
|
||||
])
|
||||
@pytest.mark.parametrize('which_fmt', [ AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH ])
|
||||
def test_custom_path(path, which_fmt, addr_vs_path, pick_menu_item, goto_address_explorer, need_keypress, cap_menu, parse_display_screen, validate_address, cap_story, cap_screen_qr, qr_quality_check, is_mark4plus, nfc_read_text, get_setting, press_select):
|
||||
def test_custom_path(path, which_fmt, addr_vs_path, pick_menu_item, goto_address_explorer,
|
||||
need_keypress, cap_menu, parse_display_screen, validate_address, cap_story,
|
||||
cap_screen_qr, qr_quality_check, is_mark4plus, nfc_read_text, get_setting,
|
||||
press_select, is_q1):
|
||||
|
||||
is_single = '{idx}' not in path
|
||||
|
||||
@ -430,11 +433,14 @@ def test_custom_path(path, which_fmt, addr_vs_path, pick_menu_item, goto_address
|
||||
assert 'Showing single addr' in body
|
||||
assert path in body
|
||||
|
||||
addr = body.split()[-1]
|
||||
if is_q1:
|
||||
addr = body.split("\n")[3]
|
||||
else:
|
||||
addr = body.split()[-1]
|
||||
|
||||
addr_vs_path(addr, path, addr_fmt=which_fmt)
|
||||
|
||||
need_keypress('2')
|
||||
need_keypress(KEY_QR if is_q1 else '4')
|
||||
qr = cap_screen_qr().decode('ascii')
|
||||
if which_fmt == AF_P2WPKH:
|
||||
assert qr == addr.upper()
|
||||
@ -443,13 +449,13 @@ def test_custom_path(path, which_fmt, addr_vs_path, pick_menu_item, goto_address
|
||||
|
||||
if is_mark4plus and get_setting('nfc', 0):
|
||||
# this is actually testing NFC export in qr code menu
|
||||
need_keypress('3')
|
||||
need_keypress(KEY_NFC if is_q1 else '3')
|
||||
time.sleep(.1)
|
||||
assert nfc_read_text() == addr
|
||||
need_keypress("x") # leave NFC animation
|
||||
need_keypress("x") # leave QR code display
|
||||
need_keypress(KEY_CANCEL if is_q1 else "x") # leave NFC animation
|
||||
need_keypress(KEY_CANCEL if is_q1 else "x") # leave QR code display
|
||||
# test NFC export in address explorer
|
||||
need_keypress('3')
|
||||
need_keypress(KEY_NFC if is_q1 else '3')
|
||||
time.sleep(.1)
|
||||
assert nfc_read_text() == addr
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import pytest, time, json, os, shutil, re
|
||||
from collections import OrderedDict
|
||||
from constants import simulator_fixed_words, simulator_fixed_tprv
|
||||
from charcodes import KEY_QR
|
||||
from charcodes import KEY_QR, KEY_ENTER, KEY_CANCEL
|
||||
from pycoin.key.BIP32Node import BIP32Node
|
||||
from mnemonic import Mnemonic
|
||||
|
||||
@ -54,14 +54,14 @@ def backup_system(settings_set, settings_remove, goto_home, pick_menu_item,
|
||||
assert "A temporary seed is in effect" in body
|
||||
assert "so backup will be of that seed" in body
|
||||
|
||||
need_keypress("y")
|
||||
need_keypress(KEY_ENTER if is_q1 else "y")
|
||||
time.sleep(.1)
|
||||
title, body = cap_story()
|
||||
|
||||
if ct:
|
||||
# cleartext backup
|
||||
if ' 1: zoo' in body:
|
||||
need_keypress("x")
|
||||
need_keypress(KEY_CANCEL if is_q1 else "x")
|
||||
|
||||
need_keypress("6")
|
||||
time.sleep(.1)
|
||||
|
||||
@ -11,7 +11,7 @@ from txn import fake_txn
|
||||
from test_ux import word_menu_entry
|
||||
from pycoin.key.BIP32Node import BIP32Node
|
||||
from helpers import xfp2str, a2b_hex
|
||||
from charcodes import KEY_CLEAR
|
||||
from charcodes import KEY_CLEAR, KEY_NFC, KEY_ENTER, KEY_DOWN, KEY_DELETE, KEY_SHIFT, KEY_CANCEL
|
||||
|
||||
|
||||
WORDLISTS = {
|
||||
@ -34,8 +34,9 @@ SEEDVAULT_TEST_DATA = [
|
||||
|
||||
@pytest.fixture
|
||||
def seed_vault_enable(cap_story, pick_menu_item, need_keypress, goto_home,
|
||||
settings_set):
|
||||
settings_set, is_q1):
|
||||
def doit(enable=True):
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
goto_home()
|
||||
pick_menu_item("Advanced/Tools")
|
||||
pick_menu_item("Danger Zone")
|
||||
@ -43,7 +44,7 @@ def seed_vault_enable(cap_story, pick_menu_item, need_keypress, goto_home,
|
||||
time.sleep(.1)
|
||||
_, story = cap_story()
|
||||
if "Enable Seed Vault?" in story:
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
|
||||
if enable:
|
||||
pick_menu_item("Enable")
|
||||
@ -52,7 +53,7 @@ def seed_vault_enable(cap_story, pick_menu_item, need_keypress, goto_home,
|
||||
time.sleep(.2)
|
||||
_, story = cap_story()
|
||||
if "Please remove all seeds from the vault" in story:
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
settings_set("seeds", [])
|
||||
pick_menu_item("Seed Vault")
|
||||
time.sleep(.1)
|
||||
@ -88,8 +89,10 @@ def ephemeral_seed_disabled_ui(cap_menu):
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def get_seed_value_ux(goto_home, pick_menu_item, need_keypress, cap_story, nfc_read_text, seed_story_to_words):
|
||||
def get_seed_value_ux(goto_home, pick_menu_item, need_keypress, cap_story,
|
||||
nfc_read_text, seed_story_to_words, is_q1):
|
||||
def doit(nfc=False):
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
goto_home()
|
||||
pick_menu_item("Advanced/Tools")
|
||||
pick_menu_item("Danger Zone")
|
||||
@ -99,18 +102,18 @@ def get_seed_value_ux(goto_home, pick_menu_item, need_keypress, cap_story, nfc_r
|
||||
title, body = cap_story()
|
||||
assert ('Are you SURE' in body) or ('Are you SURE' in title)
|
||||
assert 'can control all funds' in body
|
||||
need_keypress('y') # skip warning
|
||||
need_keypress(confirm) # skip warning
|
||||
time.sleep(0.01)
|
||||
title, story = cap_story()
|
||||
|
||||
if nfc:
|
||||
need_keypress("1") # show QR code
|
||||
time.sleep(.2)
|
||||
need_keypress("3") # any QR can be exported via NFC
|
||||
need_keypress(KEY_NFC if is_q1 else "3") # any QR can be exported via NFC
|
||||
time.sleep(.2)
|
||||
str_words = nfc_read_text()
|
||||
time.sleep(.5)
|
||||
need_keypress("y") # exit NFC animation
|
||||
need_keypress(confirm) # exit NFC animation
|
||||
return str_words.split(" ") # always truncated
|
||||
|
||||
return seed_story_to_words(story)
|
||||
@ -157,7 +160,7 @@ def goto_eph_seed_menu(goto_home, pick_menu_item, cap_story, need_keypress):
|
||||
|
||||
@pytest.fixture
|
||||
def restore_main_seed(goto_home, pick_menu_item, cap_story, cap_menu,
|
||||
need_keypress, settings_slots):
|
||||
need_keypress, settings_slots, is_q1):
|
||||
|
||||
def doit(preserve_settings=False, seed_vault=False):
|
||||
if seed_vault:
|
||||
@ -171,7 +174,7 @@ def restore_main_seed(goto_home, pick_menu_item, cap_story, cap_menu,
|
||||
pick_menu_item("Restore Master")
|
||||
time.sleep(.1)
|
||||
title, story = cap_story()
|
||||
ch = "y"
|
||||
ch = KEY_ENTER if is_q1 else "y"
|
||||
|
||||
assert "Restore main wallet and its settings?" in story
|
||||
if seed_vault:
|
||||
@ -201,8 +204,9 @@ def restore_main_seed(goto_home, pick_menu_item, cap_story, cap_menu,
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def confirm_tmp_seed(need_keypress, cap_story):
|
||||
def confirm_tmp_seed(need_keypress, cap_story, is_q1):
|
||||
def doit(seedvault=False, expect_xfp=None):
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
time.sleep(0.3)
|
||||
title, story = cap_story()
|
||||
if "Press (1) to store temporary seed into Seed Vault" in story:
|
||||
@ -214,9 +218,9 @@ def confirm_tmp_seed(need_keypress, cap_story):
|
||||
if expect_xfp is not None:
|
||||
assert expect_xfp in story
|
||||
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
else:
|
||||
need_keypress("y") # do not store
|
||||
need_keypress(confirm) # do not store
|
||||
|
||||
time.sleep(.2)
|
||||
title, story = cap_story()
|
||||
@ -227,7 +231,7 @@ def confirm_tmp_seed(need_keypress, cap_story):
|
||||
expect_xfp = title[1:-1]
|
||||
|
||||
assert "New temporary master key is in effect now." in story
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
return expect_xfp
|
||||
|
||||
return doit
|
||||
@ -235,7 +239,7 @@ def confirm_tmp_seed(need_keypress, cap_story):
|
||||
|
||||
@pytest.fixture
|
||||
def seed_vault_delete(pick_menu_item, need_keypress, cap_menu, cap_story,
|
||||
goto_home):
|
||||
goto_home, is_q1):
|
||||
def doit(xfp, wipe=True):
|
||||
# delete it from records
|
||||
goto_home()
|
||||
@ -257,7 +261,7 @@ def seed_vault_delete(pick_menu_item, need_keypress, cap_menu, cap_story,
|
||||
assert xfp in title
|
||||
assert "press (1)" in story
|
||||
if wipe:
|
||||
need_keypress("y")
|
||||
need_keypress(KEY_ENTER if is_q1 else "y")
|
||||
else:
|
||||
# preserve settings - remove just from seed vaul
|
||||
need_keypress("1")
|
||||
@ -275,7 +279,7 @@ def seed_vault_delete(pick_menu_item, need_keypress, cap_menu, cap_story,
|
||||
@pytest.fixture
|
||||
def verify_ephemeral_secret_ui(cap_story, need_keypress, cap_menu, dev, fake_txn,
|
||||
get_identity_story, try_sign, get_seed_value_ux,
|
||||
pick_menu_item, goto_home):
|
||||
pick_menu_item, goto_home, is_q1):
|
||||
def doit(mnemonic=None, xpub=None, expected_xfp=None, seed_vault=False,
|
||||
testnet=True):
|
||||
|
||||
@ -327,14 +331,14 @@ def verify_ephemeral_secret_ui(cap_story, need_keypress, cap_menu, dev, fake_txn
|
||||
assert e_master_xpub == xpub
|
||||
psbt = fake_txn(2, 2, master_xpub=e_master_xpub, segwit_in=True)
|
||||
try_sign(psbt, accept=True, finalize=True) # MUST NOT raise
|
||||
need_keypress("y")
|
||||
need_keypress(KEY_ENTER if is_q1 else "y")
|
||||
return in_effect_xfp
|
||||
|
||||
return doit
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def generate_ephemeral_words(goto_eph_seed_menu, pick_menu_item,
|
||||
def generate_ephemeral_words(goto_eph_seed_menu, pick_menu_item, is_q1,
|
||||
need_keypress, cap_story, settings_set, seed_story_to_words,
|
||||
ephemeral_seed_disabled_ui, confirm_tmp_seed):
|
||||
def doit(num_words, dice=False, from_main=False, seed_vault=None, testnet=True):
|
||||
@ -368,7 +372,7 @@ def generate_ephemeral_words(goto_eph_seed_menu, pick_menu_item,
|
||||
assert len(e_seed_words) == num_words
|
||||
|
||||
need_keypress("6") # skip quiz
|
||||
need_keypress("y") # yes - I'm sure
|
||||
need_keypress(KEY_ENTER if is_q1 else "y") # yes - I'm sure
|
||||
confirm_tmp_seed(seedvault=seed_vault)
|
||||
|
||||
return e_seed_words
|
||||
@ -379,7 +383,8 @@ def generate_ephemeral_words(goto_eph_seed_menu, pick_menu_item,
|
||||
@pytest.fixture
|
||||
def import_ephemeral_xprv(microsd_path, virtdisk_path, goto_eph_seed_menu,
|
||||
pick_menu_item, need_keypress, cap_story, settings_set,
|
||||
nfc_write_text, ephemeral_seed_disabled_ui, confirm_tmp_seed):
|
||||
nfc_write_text, ephemeral_seed_disabled_ui, confirm_tmp_seed,
|
||||
is_q1):
|
||||
def doit(way, extended_key=None, testnet=True, seed_vault=False, from_main=False):
|
||||
from pycoin.key.BIP32Node import BIP32Node
|
||||
if testnet:
|
||||
@ -422,10 +427,10 @@ def import_ephemeral_xprv(microsd_path, virtdisk_path, goto_eph_seed_menu,
|
||||
if "Press (1) to import extended private key file from SD Card" in story:
|
||||
need_keypress("1")
|
||||
elif way == "nfc":
|
||||
if "press (3) to import via NFC" not in story:
|
||||
if f"press ({'NFC' if is_q1 else '3'}) to import via NFC" not in story:
|
||||
pytest.xfail("NFC disabled")
|
||||
else:
|
||||
need_keypress("3")
|
||||
need_keypress(KEY_NFC if is_q1 else "3")
|
||||
time.sleep(0.2)
|
||||
nfc_write_text(ek)
|
||||
time.sleep(0.3)
|
||||
@ -440,7 +445,7 @@ def import_ephemeral_xprv(microsd_path, virtdisk_path, goto_eph_seed_menu,
|
||||
time.sleep(0.1)
|
||||
_, story = cap_story()
|
||||
assert "Select file containing the extended private key" in story
|
||||
need_keypress("y")
|
||||
need_keypress(KEY_ENTER if is_q1 else "y")
|
||||
pick_menu_item(fname)
|
||||
|
||||
confirm_tmp_seed(expect_xfp=node.fingerprint().hex().upper(),
|
||||
@ -479,7 +484,7 @@ def test_ephemeral_seed_generate(num_words, generate_ephemeral_words, dice,
|
||||
@pytest.mark.parametrize("preserve_settings", [False, True])
|
||||
@pytest.mark.parametrize("seed_vault", [False, True])
|
||||
def test_ephemeral_seed_import_words(nfc, truncated, num_words, cap_menu, pick_menu_item,
|
||||
need_keypress, reset_seed_words, goto_eph_seed_menu,
|
||||
reset_seed_words, goto_eph_seed_menu,
|
||||
word_menu_entry, nfc_write_text, verify_ephemeral_secret_ui,
|
||||
ephemeral_seed_disabled, get_seed_value_ux, seed_vault,
|
||||
settings_set, cap_story, preserve_settings, seed_vault_enable,
|
||||
@ -537,7 +542,9 @@ def test_ephemeral_seed_import_tapsigner(way, testnet, pick_menu_item, cap_story
|
||||
verify_ephemeral_secret_ui, ephemeral_seed_disabled,
|
||||
nfc_write_text, tapsigner_encrypted_backup, seed_vault,
|
||||
preserve_settings, seed_vault_enable, settings_set,
|
||||
seed_vault_delete, restore_main_seed, confirm_tmp_seed):
|
||||
seed_vault_delete, restore_main_seed, confirm_tmp_seed,
|
||||
is_q1):
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
reset_seed_words()
|
||||
if testnet:
|
||||
netcode = "XTN"
|
||||
@ -560,10 +567,10 @@ def test_ephemeral_seed_import_tapsigner(way, testnet, pick_menu_item, cap_story
|
||||
if "Press (1) to import TAPSIGNER encrypted backup file from SD Card" in story:
|
||||
need_keypress("1")
|
||||
elif way == "nfc":
|
||||
if "press (3) to import via NFC" not in story:
|
||||
if f"press ({'NFC' if is_q1 else '3'}) to import via NFC" not in story:
|
||||
pytest.xfail("NFC disabled")
|
||||
else:
|
||||
need_keypress("3")
|
||||
need_keypress(KEY_NFC if is_q1 else "3")
|
||||
time.sleep(0.2)
|
||||
nfc_write_text(fname)
|
||||
time.sleep(0.3)
|
||||
@ -578,14 +585,14 @@ def test_ephemeral_seed_import_tapsigner(way, testnet, pick_menu_item, cap_story
|
||||
time.sleep(0.1)
|
||||
_, story = cap_story()
|
||||
assert "Pick TAPSIGNER encrypted backup file" in story
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
pick_menu_item(fname)
|
||||
|
||||
time.sleep(0.1)
|
||||
_, story = cap_story()
|
||||
assert "your TAPSIGNER" in story
|
||||
assert "back of the card" in story
|
||||
need_keypress("y") # yes I have backup key
|
||||
need_keypress(confirm) # yes I have backup key
|
||||
enter_hex(backup_key_hex)
|
||||
|
||||
confirm_tmp_seed(expect_xfp=node.fingerprint().hex().upper(),
|
||||
@ -604,6 +611,8 @@ def test_ephemeral_seed_import_tapsigner_fail(pick_menu_item, cap_story, fail, i
|
||||
need_keypress, reset_seed_words, enter_hex,
|
||||
tapsigner_encrypted_backup, goto_eph_seed_menu,
|
||||
microsd_path, ephemeral_seed_disabled, settings_set):
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
cancel = KEY_CANCEL if is_q1 else "x"
|
||||
reset_seed_words()
|
||||
settings_set("seedvault", None)
|
||||
fail_msg = "Decryption failed - wrong key?"
|
||||
@ -627,13 +636,13 @@ def test_ephemeral_seed_import_tapsigner_fail(pick_menu_item, cap_story, fail, i
|
||||
time.sleep(0.1)
|
||||
_, story = cap_story()
|
||||
assert "Pick TAPSIGNER encrypted backup file" in story
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
pick_menu_item(fname)
|
||||
|
||||
time.sleep(0.1)
|
||||
_, story = cap_story()
|
||||
assert "Press OK to continue X to cancel." in story
|
||||
need_keypress("y") # yes I have backup key
|
||||
need_keypress(confirm) # yes I have backup key
|
||||
if fail == "wrong_key":
|
||||
backup_key_hex = os.urandom(16).hex()
|
||||
if fail == "key_len":
|
||||
@ -645,14 +654,14 @@ def test_ephemeral_seed_import_tapsigner_fail(pick_menu_item, cap_story, fail, i
|
||||
|
||||
if fail == "key_len" and is_q1:
|
||||
assert "Need 32 char" in cap_screen()
|
||||
need_keypress("x")
|
||||
need_keypress(cancel)
|
||||
return
|
||||
|
||||
title, story = cap_story()
|
||||
assert title == "FAILURE"
|
||||
assert fail_msg in story
|
||||
need_keypress("x")
|
||||
need_keypress("x")
|
||||
need_keypress(cancel)
|
||||
need_keypress(cancel)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("data", [
|
||||
@ -671,7 +680,8 @@ def test_ephemeral_seed_import_tapsigner_real(data, pick_menu_item, cap_story, m
|
||||
need_keypress, reset_seed_words, enter_hex,
|
||||
goto_eph_seed_menu, verify_ephemeral_secret_ui,
|
||||
ephemeral_seed_disabled, settings_set,
|
||||
confirm_tmp_seed, restore_main_seed):
|
||||
confirm_tmp_seed, restore_main_seed, is_q1):
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
fname, backup_key_hex, pub = data
|
||||
fpath = microsd_path(fname)
|
||||
shutil.copy(f"data/{fname}", fpath)
|
||||
@ -689,13 +699,13 @@ def test_ephemeral_seed_import_tapsigner_real(data, pick_menu_item, cap_story, m
|
||||
time.sleep(0.1)
|
||||
_, story = cap_story()
|
||||
assert "Pick TAPSIGNER encrypted backup file" in story
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
pick_menu_item(fname)
|
||||
|
||||
time.sleep(0.1)
|
||||
_, story = cap_story()
|
||||
assert "Press OK to continue X to cancel." in story
|
||||
need_keypress("y") # yes I have backup key
|
||||
need_keypress(confirm) # yes I have backup key
|
||||
enter_hex(backup_key_hex)
|
||||
confirm_tmp_seed(seedvault=False)
|
||||
verify_ephemeral_secret_ui(xpub=pub)
|
||||
@ -732,7 +742,7 @@ def test_activate_current_tmp_secret(reset_seed_words, goto_eph_seed_menu,
|
||||
pick_menu_item, need_keypress,
|
||||
word_menu_entry, settings_set,
|
||||
seed_vault, seed_vault_enable,
|
||||
confirm_tmp_seed):
|
||||
confirm_tmp_seed, is_q1):
|
||||
reset_seed_words()
|
||||
seed_vault_enable(seed_vault)
|
||||
|
||||
@ -759,7 +769,7 @@ def test_activate_current_tmp_secret(reset_seed_words, goto_eph_seed_menu,
|
||||
assert "Temporary master key already in use" in story
|
||||
assert title == "FAILED"
|
||||
assert in_effect_xfp == expected_xfp
|
||||
need_keypress("y")
|
||||
need_keypress(KEY_ENTER if is_q1 else "y")
|
||||
|
||||
|
||||
@pytest.mark.parametrize('data', SEEDVAULT_TEST_DATA)
|
||||
@ -768,6 +778,9 @@ def test_seed_vault_menus(dev, data, settings_set, master_settings_get, pick_men
|
||||
get_identity_story, get_seed_value_ux, fake_txn, try_sign,
|
||||
sim_exec, goto_home, seed_vault_enable, is_q1, enter_text):
|
||||
# Verify "seed vault" feature works as intended
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
cancel = KEY_CANCEL if is_q1 else "x"
|
||||
|
||||
reset_seed_words()
|
||||
xfp, entropy, mnemonic = data
|
||||
|
||||
@ -805,13 +818,13 @@ def test_seed_vault_menus(dev, data, settings_set, master_settings_get, pick_men
|
||||
assert ('%d words' % (6 * (vlen // 8))) in story
|
||||
else:
|
||||
assert 'xprv' in story
|
||||
need_keypress("x")
|
||||
need_keypress(cancel)
|
||||
|
||||
# rename
|
||||
pick_menu_item("Rename")
|
||||
if not is_q1:
|
||||
for _ in range(len(xfp) + 1): # [xfp]
|
||||
need_keypress("x")
|
||||
need_keypress(KEY_DELETE if is_q1 else "x")
|
||||
|
||||
# below should yield AAAA
|
||||
need_keypress("1")
|
||||
@ -819,7 +832,7 @@ def test_seed_vault_menus(dev, data, settings_set, master_settings_get, pick_men
|
||||
need_keypress("9") # next char
|
||||
need_keypress("1") # letters
|
||||
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
else:
|
||||
need_keypress(KEY_CLEAR)
|
||||
enter_text('AAAA')
|
||||
@ -828,7 +841,7 @@ def test_seed_vault_menus(dev, data, settings_set, master_settings_get, pick_men
|
||||
assert m[0] == "AAAA"
|
||||
|
||||
# check parent menu - must be updated too
|
||||
need_keypress("x")
|
||||
need_keypress(cancel)
|
||||
m = cap_menu()
|
||||
for item in m:
|
||||
if "AAAA" in item:
|
||||
@ -837,13 +850,13 @@ def test_seed_vault_menus(dev, data, settings_set, master_settings_get, pick_men
|
||||
assert False
|
||||
|
||||
# go back
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
pick_menu_item("Use This Seed")
|
||||
time.sleep(.1)
|
||||
title, story = cap_story()
|
||||
assert xfp in title
|
||||
assert 'temporary master key is in effect now' in story
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
active = get_seed_value_ux()
|
||||
if mnemonic:
|
||||
assert active == mnemonic.split()
|
||||
@ -864,7 +877,7 @@ def test_seed_vault_menus(dev, data, settings_set, master_settings_get, pick_men
|
||||
assert e_master_xpub != simulator_fixed_tpub
|
||||
psbt = fake_txn(2, 2, master_xpub=e_master_xpub, segwit_in=True)
|
||||
try_sign(psbt, accept=True, finalize=True) # MUST NOT raise
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
|
||||
encoded = sim_exec('from pincodes import pa; RV.write(repr(pa.fetch()))')
|
||||
assert 'Error' not in encoded
|
||||
@ -891,7 +904,7 @@ def test_seed_vault_captures(request, dev, settings_set, settings_get, pick_menu
|
||||
generate_ephemeral_words, goto_home, get_secrets, master_settings_get,
|
||||
import_ephemeral_xprv, set_bip39_pw, restore_main_seed,
|
||||
restore_seed_xor, derive_bip85_secret, activate_bip85_ephemeral,
|
||||
seed_vault_enable):
|
||||
seed_vault_enable, is_q1):
|
||||
# Capture seeds by all the different paths and verify correct values are captured.
|
||||
# - BIP-85 -> 12, 24 words
|
||||
# - BIP-85 -> xprv (BIP-32)
|
||||
@ -900,6 +913,7 @@ def test_seed_vault_captures(request, dev, settings_set, settings_get, pick_menu
|
||||
# - Capture a BIP-39 passphrase into words
|
||||
# - Trick pin -> duress wallet * 4 options
|
||||
# Then, verify those can all co-exist and be recalled correctly.
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
|
||||
reset_seed_words()
|
||||
seed_vault_enable(True)
|
||||
@ -962,8 +976,8 @@ def test_seed_vault_captures(request, dev, settings_set, settings_get, pick_menu
|
||||
xfp, encoded_sec, name, meta = obj
|
||||
pick_menu_item("Seed Vault")
|
||||
for _ in range(i):
|
||||
need_keypress("8") # go down
|
||||
need_keypress("y")
|
||||
need_keypress(KEY_DOWN if is_q1 else "8") # go down
|
||||
need_keypress(confirm)
|
||||
pick_menu_item('Use This Seed')
|
||||
time.sleep(0.1)
|
||||
|
||||
@ -971,7 +985,7 @@ def test_seed_vault_captures(request, dev, settings_set, settings_get, pick_menu
|
||||
assert 'New temporary master key' in story
|
||||
assert 'power down' not in story
|
||||
assert xfp in title
|
||||
need_keypress("y") # confirm activation of ephemeral secret
|
||||
need_keypress(confirm) # confirm activation of ephemeral secret
|
||||
|
||||
assert xfp2str(settings_get('xfp')) == xfp
|
||||
|
||||
@ -991,7 +1005,11 @@ def test_seed_vault_captures(request, dev, settings_set, settings_get, pick_menu
|
||||
def test_seed_vault_modifications(settings_set, reset_seed_words, pick_menu_item,
|
||||
generate_ephemeral_words, import_ephemeral_xprv,
|
||||
goto_home, cap_story, cap_menu, restore_main_seed,
|
||||
need_keypress, seed_vault_enable):
|
||||
need_keypress, seed_vault_enable, is_q1, do_keypresses):
|
||||
cancel = KEY_CANCEL if is_q1 else "x"
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
delete = KEY_DELETE if is_q1 else "x"
|
||||
k_down = KEY_DOWN if is_q1 else "8"
|
||||
reset_seed_words()
|
||||
seed_vault_enable(True)
|
||||
settings_set("seeds", [])
|
||||
@ -1018,7 +1036,7 @@ def test_seed_vault_modifications(settings_set, reset_seed_words, pick_menu_item
|
||||
# we are no longer in ephemral
|
||||
assert "Restore Master" not in m
|
||||
# first entry in menu
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
m = cap_menu()
|
||||
assert "Rename" in m
|
||||
assert "Use This Seed" in m # we are in master - so this must be there
|
||||
@ -1026,21 +1044,25 @@ def test_seed_vault_modifications(settings_set, reset_seed_words, pick_menu_item
|
||||
|
||||
# delete entry 0
|
||||
pick_menu_item("Delete")
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
time.sleep(.1)
|
||||
m = cap_menu()
|
||||
assert len(m) == 3
|
||||
|
||||
# first entry again
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
pick_menu_item("Rename")
|
||||
for _ in range(9):
|
||||
need_keypress("x")
|
||||
need_keypress("1") # big letters
|
||||
need_keypress("9")
|
||||
need_keypress("1")
|
||||
# name changed to AA
|
||||
need_keypress("y")
|
||||
for _ in range(11):
|
||||
need_keypress(delete)
|
||||
if is_q1:
|
||||
do_keypresses("AA")
|
||||
else:
|
||||
need_keypress("1") # big letters
|
||||
need_keypress("9")
|
||||
need_keypress("1")
|
||||
# name changed to AA
|
||||
|
||||
need_keypress(confirm)
|
||||
|
||||
m = cap_menu()
|
||||
assert m[0] == "AA"
|
||||
@ -1049,20 +1071,20 @@ def test_seed_vault_modifications(settings_set, reset_seed_words, pick_menu_item
|
||||
assert "Delete" in m
|
||||
|
||||
# go back
|
||||
need_keypress("x")
|
||||
need_keypress(KEY_CANCEL if is_q1 else "x")
|
||||
# second item
|
||||
need_keypress("8")
|
||||
need_keypress("y")
|
||||
need_keypress(k_down)
|
||||
need_keypress(confirm)
|
||||
time.sleep(.1)
|
||||
pick_menu_item("Use This Seed")
|
||||
title, _ = cap_story()
|
||||
need_keypress("y") # confirm new eph
|
||||
need_keypress(confirm) # confirm new eph
|
||||
time.sleep(.1)
|
||||
m = cap_menu()
|
||||
assert m[0] == title
|
||||
pick_menu_item("Seed Vault")
|
||||
need_keypress("8")
|
||||
need_keypress("y")
|
||||
need_keypress(k_down)
|
||||
need_keypress(confirm)
|
||||
time.sleep(.1)
|
||||
m = cap_menu()
|
||||
assert "Rename" in m
|
||||
@ -1070,21 +1092,25 @@ def test_seed_vault_modifications(settings_set, reset_seed_words, pick_menu_item
|
||||
assert "Delete" in m
|
||||
|
||||
pick_menu_item("Rename")
|
||||
for _ in range(9):
|
||||
need_keypress("x")
|
||||
need_keypress("1") # big letters
|
||||
need_keypress("9")
|
||||
need_keypress("1")
|
||||
need_keypress("9")
|
||||
need_keypress("1")
|
||||
for _ in range(11):
|
||||
need_keypress(delete)
|
||||
|
||||
if is_q1:
|
||||
do_keypresses("AAA")
|
||||
else:
|
||||
need_keypress("1") # big letters
|
||||
need_keypress("9")
|
||||
need_keypress("1")
|
||||
need_keypress("9")
|
||||
need_keypress("1")
|
||||
# name changed to AAA
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
|
||||
time.sleep(.1)
|
||||
m = cap_menu()
|
||||
assert m[0] == "AAA"
|
||||
pick_menu_item("Delete")
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
time.sleep(.1)
|
||||
m = cap_menu()
|
||||
# after we delete from seed vault together with its settings
|
||||
@ -1095,17 +1121,17 @@ def test_seed_vault_modifications(settings_set, reset_seed_words, pick_menu_item
|
||||
m = cap_menu()
|
||||
assert len(m) == 2
|
||||
|
||||
need_keypress("8")
|
||||
need_keypress("y")
|
||||
need_keypress(k_down)
|
||||
need_keypress(confirm)
|
||||
pick_menu_item("Use This Seed")
|
||||
title, _ = cap_story()
|
||||
need_keypress("y") # confirm new eph
|
||||
need_keypress(confirm) # confirm new eph
|
||||
time.sleep(.1)
|
||||
m = cap_menu()
|
||||
assert m[0] == title
|
||||
pick_menu_item("Seed Vault")
|
||||
need_keypress("8")
|
||||
need_keypress("y")
|
||||
need_keypress(k_down)
|
||||
need_keypress(confirm)
|
||||
time.sleep(.1)
|
||||
m = cap_menu()
|
||||
assert "Rename" in m
|
||||
@ -1118,7 +1144,7 @@ def test_seed_vault_modifications(settings_set, reset_seed_words, pick_menu_item
|
||||
m = cap_menu()
|
||||
assert len(m) == 3
|
||||
assert "Add current tmp" in m
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
# this is now different eph - modification not allowed
|
||||
time.sleep(.1)
|
||||
m = cap_menu()
|
||||
@ -1134,7 +1160,7 @@ def test_seed_vault_modifications(settings_set, reset_seed_words, pick_menu_item
|
||||
|
||||
def test_xfp_collision(reset_seed_words, settings_set, import_ephemeral_xprv,
|
||||
cap_story, need_keypress, pick_menu_item, cap_menu,
|
||||
seed_vault_enable):
|
||||
seed_vault_enable, is_q1):
|
||||
|
||||
node = BIP32Node.from_master_secret(os.urandom(32), netcode="XTN")
|
||||
xfp = node.fingerprint().hex().upper()
|
||||
@ -1165,7 +1191,7 @@ def test_xfp_collision(reset_seed_words, settings_set, import_ephemeral_xprv,
|
||||
sm = cap_menu()
|
||||
assert "Seed In Use" in sm
|
||||
assert "Use This Seed" not in sm
|
||||
need_keypress("x") # go back
|
||||
need_keypress(KEY_CANCEL if is_q1 else "x") # go back
|
||||
pick_menu_item(m[0])
|
||||
time.sleep(.1)
|
||||
sm = cap_menu()
|
||||
@ -1177,7 +1203,7 @@ def test_xfp_collision(reset_seed_words, settings_set, import_ephemeral_xprv,
|
||||
def test_add_current_active(reset_seed_words, settings_set, import_ephemeral_xprv,
|
||||
goto_home, pick_menu_item, cap_story, cap_menu,
|
||||
need_keypress, verify_ephemeral_secret_ui,
|
||||
seed_vault_enable, refuse):
|
||||
seed_vault_enable, refuse, is_q1):
|
||||
ADD_MI = "Add current tmp"
|
||||
|
||||
reset_seed_words()
|
||||
@ -1207,14 +1233,14 @@ def test_add_current_active(reset_seed_words, settings_set, import_ephemeral_xpr
|
||||
assert xfp in title
|
||||
assert "Add to Seed Vault?" in story
|
||||
if refuse:
|
||||
need_keypress("x")
|
||||
need_keypress(KEY_CANCEL if is_q1 else "x")
|
||||
time.sleep(.1)
|
||||
m = cap_menu()
|
||||
assert ADD_MI in m
|
||||
for mi in m:
|
||||
assert xfp not in mi
|
||||
else:
|
||||
need_keypress("y")
|
||||
need_keypress(KEY_ENTER if is_q1 else "y")
|
||||
verify_ephemeral_secret_ui(xpub=node.hwif(), seed_vault=True)
|
||||
|
||||
|
||||
@ -1223,11 +1249,12 @@ def test_add_current_active(reset_seed_words, settings_set, import_ephemeral_xpr
|
||||
@pytest.mark.parametrize('data', SEEDVAULT_TEST_DATA)
|
||||
def test_temporary_from_backup(multisig, backup_system, import_ms_wallet, get_setting,
|
||||
data, need_keypress, cap_story, set_encoded_secret,
|
||||
reset_seed_words, check_and_decrypt_backup,
|
||||
reset_seed_words, check_and_decrypt_backup, is_q1,
|
||||
goto_eph_seed_menu, pick_menu_item, word_menu_entry,
|
||||
verify_ephemeral_secret_ui, seedvault, settings_set,
|
||||
seed_vault_enable, confirm_tmp_seed, settings_path,
|
||||
seed_vault_delete, restore_main_seed, set_seed_words):
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
xfp_str, encoded_str, mnemonic = data
|
||||
if mnemonic:
|
||||
set_seed_words(mnemonic)
|
||||
@ -1239,7 +1266,7 @@ def test_temporary_from_backup(multisig, backup_system, import_ms_wallet, get_se
|
||||
|
||||
if multisig:
|
||||
import_ms_wallet(15, 15, dev_key=True)
|
||||
need_keypress('y')
|
||||
need_keypress(confirm)
|
||||
time.sleep(.1)
|
||||
assert len(get_setting('multisig')) == 1
|
||||
|
||||
@ -1261,11 +1288,11 @@ def test_temporary_from_backup(multisig, backup_system, import_ms_wallet, get_se
|
||||
time.sleep(.1)
|
||||
_, story = cap_story()
|
||||
if "Select file containing the backup" in story:
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
time.sleep(.1)
|
||||
pick_menu_item(fname)
|
||||
|
||||
word_menu_entry(bk_pw)
|
||||
word_menu_entry(bk_pw, has_checksum=False)
|
||||
|
||||
confirm_tmp_seed(seedvault)
|
||||
|
||||
@ -1282,8 +1309,8 @@ def test_temporary_from_backup(multisig, backup_system, import_ms_wallet, get_se
|
||||
restore_main_seed(False)
|
||||
|
||||
|
||||
def test_tmp_upgrade_disabled(reset_seed_words, need_keypress, pick_menu_item,
|
||||
cap_story, cap_menu, goto_home, unit_test,
|
||||
def test_tmp_upgrade_disabled(reset_seed_words, pick_menu_item, cap_story,
|
||||
cap_menu, goto_home, unit_test,
|
||||
import_ephemeral_xprv):
|
||||
reset_seed_words()
|
||||
goto_home()
|
||||
@ -1322,7 +1349,9 @@ def test_import_master_as_tmp(reset_seed_words, goto_eph_seed_menu, cap_story,
|
||||
ephemeral_seed_disabled, pick_menu_item, goto_home,
|
||||
need_keypress, word_menu_entry, settings_set,
|
||||
confirm_tmp_seed, cap_menu, microsd_path,
|
||||
restore_main_seed, get_identity_story):
|
||||
restore_main_seed, get_identity_story, is_q1):
|
||||
cancel = KEY_CANCEL if is_q1 else "x"
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
reset_seed_words()
|
||||
|
||||
goto_eph_seed_menu()
|
||||
@ -1340,7 +1369,7 @@ def test_import_master_as_tmp(reset_seed_words, goto_eph_seed_menu, cap_story,
|
||||
title, story = cap_story()
|
||||
assert "FAILED" == title
|
||||
assert 'Cannot use master seed as temporary.' in story
|
||||
need_keypress("x")
|
||||
need_keypress(cancel)
|
||||
|
||||
# go to ephemeral seed and then try to create new ephemeral seed from master
|
||||
# when in different temporary seed whatsoever
|
||||
@ -1350,7 +1379,7 @@ def test_import_master_as_tmp(reset_seed_words, goto_eph_seed_menu, cap_story,
|
||||
pick_menu_item("Generate Words")
|
||||
pick_menu_item(f"12 Words")
|
||||
need_keypress("6") # skip quiz
|
||||
need_keypress("y") # yes - I'm sure
|
||||
need_keypress(confirm) # yes - I'm sure
|
||||
confirm_tmp_seed(seedvault=False)
|
||||
|
||||
goto_home()
|
||||
@ -1368,7 +1397,7 @@ def test_import_master_as_tmp(reset_seed_words, goto_eph_seed_menu, cap_story,
|
||||
title, story = cap_story()
|
||||
assert "FAILED" == title
|
||||
assert 'Cannot use master seed as temporary.' in story
|
||||
need_keypress("x")
|
||||
need_keypress(cancel)
|
||||
|
||||
# now import same seed but represented as master extended key
|
||||
# this works and does not delete master settings as encoded
|
||||
@ -1383,7 +1412,7 @@ def test_import_master_as_tmp(reset_seed_words, goto_eph_seed_menu, cap_story,
|
||||
if "Press (1)" in story:
|
||||
need_keypress("1")
|
||||
|
||||
need_keypress("y") # Select file containing...
|
||||
need_keypress(confirm) # Select file containing...
|
||||
pick_menu_item(fname)
|
||||
confirm_tmp_seed(seedvault=False) # allowed
|
||||
|
||||
|
||||
@ -16,13 +16,15 @@ from helpers import xfp2str, slip132undo
|
||||
from conftest import simulator_fixed_xfp, simulator_fixed_tprv, simulator_fixed_words
|
||||
from ckcc_protocol.constants import AF_CLASSIC, AF_P2WPKH
|
||||
from pprint import pprint
|
||||
from charcodes import KEY_ENTER, KEY_CANCEL, KEY_NFC
|
||||
|
||||
|
||||
@pytest.mark.bitcoind
|
||||
@pytest.mark.parametrize('acct_num', [None, '0', '99', '123'])
|
||||
@pytest.mark.parametrize('way', ["sd", "vdisk", "nfc"])
|
||||
def test_export_core(way, dev, use_regtest, acct_num, pick_menu_item, goto_home, cap_story,
|
||||
need_keypress, microsd_path, virtdisk_path, bitcoind_wallet, bitcoind_d_wallet,
|
||||
enter_number, nfc_read_text, load_export, bitcoind):
|
||||
enter_number, nfc_read_text, load_export, bitcoind, is_q1):
|
||||
# test UX and operation of the 'bitcoin core' wallet export
|
||||
from pycoin.contrib.segwit_addr import encode as sw_encode
|
||||
use_regtest()
|
||||
@ -45,7 +47,7 @@ def test_export_core(way, dev, use_regtest, acct_num, pick_menu_item, goto_home,
|
||||
enter_number(acct_num)
|
||||
else:
|
||||
acct_num = '0'
|
||||
need_keypress('y')
|
||||
need_keypress(KEY_ENTER if is_q1 else 'y')
|
||||
|
||||
export = load_export(way, label="Bitcoin Core", is_json=False, addr_fmt=AF_P2WPKH)
|
||||
fp = io.StringIO(export).readlines()
|
||||
@ -161,7 +163,7 @@ def test_export_core(way, dev, use_regtest, acct_num, pick_menu_item, goto_home,
|
||||
@pytest.mark.parametrize('way', ["sd", "vdisk", "nfc"])
|
||||
@pytest.mark.parametrize('testnet', [True, False])
|
||||
def test_export_wasabi(way, dev, pick_menu_item, goto_home, cap_story, need_keypress, microsd_path,
|
||||
nfc_read_json, virtdisk_path, testnet, use_mainnet, load_export):
|
||||
nfc_read_json, virtdisk_path, testnet, use_mainnet, load_export, is_q1):
|
||||
# test UX and operation of the 'wasabi wallet export'
|
||||
if not testnet:
|
||||
use_mainnet()
|
||||
@ -176,7 +178,7 @@ def test_export_wasabi(way, dev, pick_menu_item, goto_home, cap_story, need_keyp
|
||||
title, story = cap_story()
|
||||
|
||||
assert 'This saves a skeleton Wasabi' in story
|
||||
need_keypress('y')
|
||||
need_keypress(KEY_ENTER if is_q1 else 'y')
|
||||
|
||||
obj = load_export(way, label="Wasabi wallet", is_json=True, addr_fmt=AF_P2WPKH)
|
||||
|
||||
@ -200,7 +202,8 @@ def test_export_wasabi(way, dev, pick_menu_item, goto_home, cap_story, need_keyp
|
||||
@pytest.mark.parametrize('way', ["sd", "vdisk", "nfc"])
|
||||
@pytest.mark.parametrize('testnet', [True, False])
|
||||
def test_export_electrum(way, dev, mode, acct_num, pick_menu_item, goto_home, cap_story, need_keypress,
|
||||
microsd_path, nfc_read_json, virtdisk_path, use_mainnet, testnet, load_export):
|
||||
microsd_path, nfc_read_json, virtdisk_path, use_mainnet, testnet, load_export,
|
||||
is_q1):
|
||||
# lightly test electrum wallet export
|
||||
if not testnet:
|
||||
use_mainnet()
|
||||
@ -228,7 +231,7 @@ def test_export_electrum(way, dev, mode, acct_num, pick_menu_item, goto_home, ca
|
||||
for n in acct_num:
|
||||
need_keypress(n)
|
||||
|
||||
need_keypress('y')
|
||||
need_keypress(KEY_ENTER if is_q1 else 'y')
|
||||
|
||||
time.sleep(0.1)
|
||||
pick_menu_item(mode)
|
||||
@ -272,7 +275,7 @@ def test_export_electrum(way, dev, mode, acct_num, pick_menu_item, goto_home, ca
|
||||
])
|
||||
def test_export_coldcard(way, dev, acct_num, app, pick_menu_item, goto_home, cap_story, need_keypress,
|
||||
microsd_path, nfc_read_json, virtdisk_path, addr_vs_path, enter_number,
|
||||
load_export, testnet, use_mainnet):
|
||||
load_export, testnet, use_mainnet, is_q1):
|
||||
if not testnet:
|
||||
use_mainnet()
|
||||
|
||||
@ -295,7 +298,7 @@ def test_export_coldcard(way, dev, acct_num, app, pick_menu_item, goto_home, cap
|
||||
enter_number(acct_num)
|
||||
else:
|
||||
acct_num = '0'
|
||||
need_keypress('y')
|
||||
need_keypress(KEY_ENTER if is_q1 else 'y')
|
||||
|
||||
obj = load_export(way, label=app_f_name, is_json=True, addr_fmt=AF_CLASSIC)
|
||||
|
||||
@ -353,7 +356,7 @@ def test_export_coldcard(way, dev, acct_num, app, pick_menu_item, goto_home, cap
|
||||
@pytest.mark.parametrize('acct_num', [None, '0', '99', '123'])
|
||||
def test_export_unchained(way, dev, pick_menu_item, goto_home, cap_story, need_keypress, acct_num,
|
||||
microsd_path, nfc_read_json, virtdisk_path, testnet, enter_number,
|
||||
load_export, settings_set, use_mainnet):
|
||||
load_export, settings_set, use_mainnet, is_q1):
|
||||
# test UX and operation of the 'unchained export'
|
||||
if not testnet:
|
||||
use_mainnet()
|
||||
@ -375,7 +378,7 @@ def test_export_unchained(way, dev, pick_menu_item, goto_home, cap_story, need_k
|
||||
enter_number(acct_num)
|
||||
else:
|
||||
acct_num = '0'
|
||||
need_keypress('y')
|
||||
need_keypress(KEY_ENTER if is_q1 else 'y')
|
||||
|
||||
obj = load_export(way, label="Unchained", is_json=True, sig_check=False)
|
||||
|
||||
@ -404,7 +407,7 @@ def test_export_unchained(way, dev, pick_menu_item, goto_home, cap_story, need_k
|
||||
@pytest.mark.parametrize('testnet', [True, False])
|
||||
def test_export_public_txt(way, dev, pick_menu_item, goto_home, need_keypress, microsd_path,
|
||||
addr_vs_path, virtdisk_path, nfc_read_text, cap_story, use_mainnet,
|
||||
load_export, testnet):
|
||||
load_export, testnet, is_q1):
|
||||
# test UX and values produced.
|
||||
if not testnet:
|
||||
use_mainnet()
|
||||
@ -418,7 +421,7 @@ def test_export_public_txt(way, dev, pick_menu_item, goto_home, need_keypress, m
|
||||
title, story = cap_story()
|
||||
|
||||
assert 'Saves a text file' in story
|
||||
need_keypress('y')
|
||||
need_keypress(KEY_ENTER if is_q1 else 'y')
|
||||
|
||||
contents = load_export(way, label="Summary", is_json=False, addr_fmt=AF_CLASSIC)
|
||||
fp = io.StringIO(contents).readlines()
|
||||
@ -465,9 +468,13 @@ def test_export_public_txt(way, dev, pick_menu_item, goto_home, need_keypress, m
|
||||
@pytest.mark.qrcode
|
||||
@pytest.mark.parametrize('acct_num', [ None, 0, 99, 8989])
|
||||
@pytest.mark.parametrize('use_nfc', [False, True])
|
||||
def test_export_xpub(use_nfc, acct_num, dev, cap_menu, pick_menu_item, goto_home, cap_story, need_keypress, enter_number,
|
||||
cap_screen_qr, use_mainnet, nfc_read_text):
|
||||
def test_export_xpub(use_nfc, acct_num, dev, cap_menu, pick_menu_item, goto_home,
|
||||
cap_story, need_keypress, enter_number, cap_screen_qr,
|
||||
use_mainnet, nfc_read_text, is_q1):
|
||||
# XPUB's via QR
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
cancel = KEY_CANCEL if is_q1 else "x"
|
||||
k_nfc = KEY_NFC if is_q1 else "3"
|
||||
|
||||
use_mainnet()
|
||||
|
||||
@ -495,9 +502,9 @@ def test_export_xpub(use_nfc, acct_num, dev, cap_menu, pick_menu_item, goto_home
|
||||
if is_xfp:
|
||||
got = cap_screen_qr().decode('ascii')
|
||||
if use_nfc:
|
||||
need_keypress('3')
|
||||
need_keypress(k_nfc)
|
||||
assert got == xfp2str(simulator_fixed_xfp).upper()
|
||||
need_keypress('x')
|
||||
need_keypress(cancel)
|
||||
continue
|
||||
|
||||
time.sleep(0.3)
|
||||
@ -518,16 +525,16 @@ def test_export_xpub(use_nfc, acct_num, dev, cap_menu, pick_menu_item, goto_home
|
||||
|
||||
expect = expect.format(acct=0)
|
||||
if not use_nfc:
|
||||
need_keypress('y')
|
||||
need_keypress(confirm)
|
||||
got_pub = cap_screen_qr().decode('ascii')
|
||||
else:
|
||||
assert 'Press (3)' in story
|
||||
assert f'Press {KEY_NFC if is_q1 else "(3)"}' in story
|
||||
assert 'NFC' in story
|
||||
need_keypress('3')
|
||||
need_keypress(k_nfc)
|
||||
time.sleep(0.2)
|
||||
got_pub = nfc_read_text()
|
||||
time.sleep(0.1)
|
||||
#need_keypress('y')
|
||||
#need_keypress(confirm)
|
||||
|
||||
if got_pub[0] not in 'xt':
|
||||
got_pub,*_ = slip132undo(got_pub)
|
||||
@ -539,7 +546,7 @@ def test_export_xpub(use_nfc, acct_num, dev, cap_menu, pick_menu_item, goto_home
|
||||
wallet = wallet.subkey_for_path(expect[2:])
|
||||
assert got.sec() == wallet.sec()
|
||||
|
||||
need_keypress('x')
|
||||
need_keypress(cancel)
|
||||
|
||||
@pytest.mark.parametrize("chain", ["BTC", "XTN", "XRT"])
|
||||
@pytest.mark.parametrize("way", ["sd", "vdisk", "nfc"])
|
||||
@ -548,7 +555,9 @@ def test_export_xpub(use_nfc, acct_num, dev, cap_menu, pick_menu_item, goto_home
|
||||
@pytest.mark.parametrize("int_ext", [True, False])
|
||||
def test_generic_descriptor_export(chain, addr_fmt, acct_num, goto_home, settings_set, need_keypress,
|
||||
pick_menu_item, way, cap_story, cap_menu, nfc_read_text, int_ext,
|
||||
microsd_path, settings_get, virtdisk_path, load_export):
|
||||
microsd_path, settings_get, virtdisk_path, load_export, is_q1):
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
|
||||
settings_set('chain', chain)
|
||||
chain_num = 1 if chain in ["XTN", "XRT"] else 0
|
||||
goto_home()
|
||||
@ -567,16 +576,16 @@ def test_generic_descriptor_export(chain, addr_fmt, acct_num, goto_home, setting
|
||||
need_keypress("1") # chosse account number
|
||||
for ch in str(acct_num):
|
||||
need_keypress(ch) # input num
|
||||
need_keypress("y") # confirm selection
|
||||
need_keypress(confirm) # confirm selection
|
||||
else:
|
||||
need_keypress("y") # confirm story
|
||||
need_keypress(confirm) # confirm story
|
||||
|
||||
time.sleep(.1)
|
||||
_, story = cap_story()
|
||||
assert "To export receiving and change descriptors in one descriptor (<0;1> notation) press OK" in story
|
||||
assert "press (1) to export receiving and change descriptors separately" in story
|
||||
if int_ext:
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
else:
|
||||
need_keypress("1")
|
||||
|
||||
@ -621,8 +630,12 @@ def test_generic_descriptor_export(chain, addr_fmt, acct_num, goto_home, setting
|
||||
|
||||
@pytest.mark.parametrize("chain", ["BTC", "XTN", "XRT"])
|
||||
@pytest.mark.parametrize("account", ["Postmix", "Premix"])
|
||||
def test_samourai_vs_generic(chain, account, settings_set, pick_menu_item, goto_home, need_keypress,
|
||||
cap_story, microsd_path, nfc_read_text, load_export):
|
||||
def test_samourai_vs_generic(chain, account, settings_set, pick_menu_item, goto_home,
|
||||
need_keypress, cap_story, microsd_path, nfc_read_text,
|
||||
load_export, is_q1):
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
cancel = KEY_CANCEL if is_q1 else "x"
|
||||
|
||||
if account == "Postmix":
|
||||
acct_num = 2147483646
|
||||
in_story = "Samourai POST-MIX"
|
||||
@ -639,12 +652,12 @@ def test_samourai_vs_generic(chain, account, settings_set, pick_menu_item, goto_
|
||||
need_keypress("1")
|
||||
for ch in str(acct_num):
|
||||
need_keypress(ch)
|
||||
need_keypress("y")
|
||||
need_keypress("y") # int_ext <0;1>
|
||||
need_keypress(confirm)
|
||||
need_keypress(confirm) # int_ext <0;1>
|
||||
pick_menu_item("Segwit P2WPKH") # both postmix and premix are p2wpkh only
|
||||
file_desc_generic = load_export("sd", label="Descriptor", is_json=False, addr_fmt=AF_P2WPKH)
|
||||
need_keypress("y") # written
|
||||
need_keypress("x") # go back to advanced
|
||||
need_keypress(confirm) # written
|
||||
need_keypress(cancel) # go back to advanced
|
||||
pick_menu_item("Export Wallet")
|
||||
pick_menu_item(f"Samourai {account}")
|
||||
time.sleep(.1)
|
||||
@ -655,7 +668,7 @@ def test_samourai_vs_generic(chain, account, settings_set, pick_menu_item, goto_
|
||||
assert "Press 1 to enter a non-zero account number" not in story # NOT
|
||||
assert "sensitive--in terms of privacy" in story
|
||||
assert "not compromise your funds directly" in story
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
file_desc = load_export("sd", label="Descriptor", is_json=False, addr_fmt=AF_P2WPKH)
|
||||
assert file_desc.strip() == file_desc_generic.strip()
|
||||
|
||||
|
||||
@ -114,7 +114,7 @@ def compute_policy_hash(policy):
|
||||
return b2a_hex(sha256(json_.encode()).digest()).decode()
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def enable_hsm_commands(dev, sim_exec):
|
||||
def enable_hsm_commands(dev, sim_exec, only_mk4):
|
||||
cmd = 'from glob import settings; settings.set("hsmcmd", 1)'
|
||||
sim_exec(cmd)
|
||||
yield
|
||||
|
||||
@ -10,17 +10,18 @@ from base64 import b64encode, b64decode
|
||||
from ckcc_protocol.protocol import CCProtocolPacker, CCProtoError, CCUserRefused
|
||||
from ckcc_protocol.constants import *
|
||||
from constants import addr_fmt_names, msg_sign_unmap_addr_fmt
|
||||
from charcodes import KEY_ENTER, KEY_CANCEL, KEY_NFC
|
||||
|
||||
|
||||
@pytest.mark.parametrize('msg', [ 'aZ', 'hello', 'abc def eght', "x"*140, 'a'*240])
|
||||
@pytest.mark.parametrize('path', [ 'm', "m/1/2", "m/1'/100'", 'm/23H/22p'])
|
||||
@pytest.mark.parametrize('addr_fmt', [ AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH ])
|
||||
def test_sign_msg_good(dev, need_keypress, msg, path, addr_fmt, addr_vs_path):
|
||||
def test_sign_msg_good(dev, need_keypress, msg, path, addr_fmt, addr_vs_path, is_q1):
|
||||
|
||||
msg = msg.encode('ascii')
|
||||
dev.send_recv(CCProtocolPacker.sign_message(msg, path, addr_fmt=addr_fmt), timeout=None)
|
||||
|
||||
need_keypress('y')
|
||||
need_keypress(KEY_ENTER if is_q1 else 'y')
|
||||
|
||||
done = None
|
||||
while done == None:
|
||||
@ -39,12 +40,14 @@ def test_sign_msg_good(dev, need_keypress, msg, path, addr_fmt, addr_vs_path):
|
||||
assert verify_message(addr, sig, msg.decode("ascii")) is True
|
||||
|
||||
|
||||
def test_sign_msg_refused(dev, need_keypress, msg=b'testing 123', path='m'):
|
||||
def test_sign_msg_refused(dev, need_keypress, is_q1):
|
||||
# user can refuse to sign (cancel)
|
||||
|
||||
msg = b'testing 123'
|
||||
path = 'm'
|
||||
dev.send_recv(CCProtocolPacker.sign_message(msg, path), timeout=None)
|
||||
|
||||
need_keypress('x')
|
||||
need_keypress(KEY_CANCEL if is_q1 else 'x')
|
||||
|
||||
with pytest.raises(CCUserRefused):
|
||||
done = None
|
||||
@ -76,11 +79,13 @@ def test_bad_paths(dev, path, expect):
|
||||
assert expect in str(ee)
|
||||
|
||||
@pytest.fixture
|
||||
def sign_on_microsd(open_microsd, cap_story, pick_menu_item, goto_home, need_keypress, microsd_path):
|
||||
def sign_on_microsd(open_microsd, cap_story, pick_menu_item, goto_home,
|
||||
need_keypress, microsd_path, is_q1):
|
||||
|
||||
# sign a file on the microSD card
|
||||
|
||||
def doit(msg, subpath=None, addr_fmt=None, expect_fail=False):
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
fname = 't-msgsign.txt'
|
||||
result_fname = 't-msgsign-signed.txt'
|
||||
|
||||
@ -103,7 +108,7 @@ def sign_on_microsd(open_microsd, cap_story, pick_menu_item, goto_home, need_key
|
||||
time.sleep(.1)
|
||||
_, story = cap_story()
|
||||
assert 'Choose text file to be signed' in story
|
||||
need_keypress('y')
|
||||
need_keypress(confirm)
|
||||
time.sleep(.1)
|
||||
|
||||
try:
|
||||
@ -128,7 +133,7 @@ def sign_on_microsd(open_microsd, cap_story, pick_menu_item, goto_home, need_key
|
||||
x_subpath = subpath.lower().replace('p', "'").replace('h', "'")
|
||||
assert ('%s =>' % x_subpath) in story
|
||||
|
||||
need_keypress('y')
|
||||
need_keypress(confirm)
|
||||
|
||||
# wait for it to finish
|
||||
for r in range(10):
|
||||
@ -239,7 +244,8 @@ def test_sign_msg_fails(dev, sign_on_microsd, msg, concern, no_file, transport,
|
||||
('Test', 2, 'IDgMx1ljPhLHlKUOwnO/jBIgK+K8n8mvDUDROzTgU8gOaPDMs+eYXJpNXXINUx5WpeV605p5uO6B3TzBVcvs478='),
|
||||
('Test1', 3, 'IEt/v9K95YVFuRtRtWaabPVwWOFv1FSA/e874I8ABgYMbRyVvHhSwLFz0RZuO87ukxDd4TOsRdofQwMEA90LCgI='),
|
||||
])
|
||||
def test_low_R_cases(msg, num_iter, expect, dev, set_seed_words, use_mainnet, need_keypress):
|
||||
def test_low_R_cases(msg, num_iter, expect, dev, set_seed_words, use_mainnet,
|
||||
need_keypress, is_q1):
|
||||
# Thanks to @craigraw of Sparrow for this test case, copied from:
|
||||
# <https://github.com/sparrowwallet/drongo/blob/master/src/test/java/com/sparrowwallet/drongo/crypto/ECKeyTest.java>
|
||||
|
||||
@ -254,7 +260,7 @@ def test_low_R_cases(msg, num_iter, expect, dev, set_seed_words, use_mainnet, ne
|
||||
msg = msg.encode('ascii')
|
||||
dev.send_recv(CCProtocolPacker.sign_message(msg, path, addr_fmt=addr_fmt), timeout=None)
|
||||
|
||||
need_keypress('y')
|
||||
need_keypress(KEY_ENTER if is_q1 else 'y')
|
||||
|
||||
done = None
|
||||
while done == None:
|
||||
@ -302,7 +308,9 @@ def test_nfc_msg_signing_invalid(body, goto_home, pick_menu_item, nfc_write_text
|
||||
@pytest.mark.parametrize("path", ["", "m/84'/0'/0'/300/0", "m/800'", "m/0/0/0/0/1/1/1"])
|
||||
@pytest.mark.parametrize("str_addr_fmt", ["p2pkh", "", "p2wpkh", "p2wpkh-p2sh", "p2sh-p2wpkh"])
|
||||
def test_nfc_msg_signing(msg, path, str_addr_fmt, nfc_write_text, nfc_read_text, pick_menu_item,
|
||||
goto_home, cap_story, need_keypress, addr_vs_path):
|
||||
goto_home, cap_story, need_keypress, addr_vs_path, is_q1):
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
cancel = KEY_CANCEL if is_q1 else "x"
|
||||
# import pdb;pdb.set_trace()
|
||||
for _ in range(5):
|
||||
# need to wait for ApproveMessageSign to be popped from ux stack
|
||||
@ -328,12 +336,12 @@ def test_nfc_msg_signing(msg, path, str_addr_fmt, nfc_write_text, nfc_read_text,
|
||||
assert "Ok to sign this?" in story
|
||||
assert msg in story
|
||||
assert path in story
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
signed_msg = nfc_read_text()
|
||||
if "BITCOIN SIGNED MESSAGE" not in signed_msg:
|
||||
# missed it? again
|
||||
signed_msg = nfc_read_text()
|
||||
need_keypress("y") # exit NFC animation
|
||||
need_keypress(confirm) # exit NFC animation
|
||||
pmsg, addr, sig = parse_signed_message(signed_msg)
|
||||
assert pmsg == msg
|
||||
addr_vs_path(addr, path, addr_fmt)
|
||||
@ -341,14 +349,15 @@ def test_nfc_msg_signing(msg, path, str_addr_fmt, nfc_write_text, nfc_read_text,
|
||||
time.sleep(0.5)
|
||||
_, story = cap_story()
|
||||
assert "Press OK to share again" in story
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
signed_msg_again = nfc_read_text()
|
||||
assert signed_msg == signed_msg_again
|
||||
need_keypress("x") # exit NFC animation
|
||||
need_keypress("x") # do not want to share again
|
||||
need_keypress(cancel) # exit NFC animation
|
||||
need_keypress(cancel) # do not want to share again
|
||||
|
||||
@pytest.fixture
|
||||
def verify_armored_signature(pick_menu_item, nfc_write_text, need_keypress, cap_story, goto_home):
|
||||
def verify_armored_signature(pick_menu_item, nfc_write_text, need_keypress,
|
||||
cap_story, goto_home, is_q1):
|
||||
def doit(way, fname=None, signed_msg=None):
|
||||
goto_home()
|
||||
pick_menu_item('Advanced/Tools')
|
||||
@ -363,7 +372,7 @@ def verify_armored_signature(pick_menu_item, nfc_write_text, need_keypress, cap_
|
||||
else:
|
||||
_, story = cap_story()
|
||||
assert 'Choose signature file.' in story
|
||||
need_keypress('y')
|
||||
need_keypress(KEY_ENTER if is_q1 else 'y')
|
||||
time.sleep(.1)
|
||||
pick_menu_item(fname)
|
||||
|
||||
@ -380,7 +389,7 @@ def verify_armored_signature(pick_menu_item, nfc_write_text, need_keypress, cap_
|
||||
"coldcard", "blablablablablablablabla", "morecornfor us", 240 * "a",
|
||||
))
|
||||
def test_verify_signature_file(way, addr_fmt, path, msg, sign_on_microsd, goto_home, pick_menu_item,
|
||||
need_keypress, cap_story, bitcoind, microsd_path, nfc_write_text,
|
||||
cap_story, bitcoind, microsd_path, nfc_write_text,
|
||||
verify_armored_signature):
|
||||
sig, addr = sign_on_microsd(msg, path, msg_sign_unmap_addr_fmt[addr_fmt])
|
||||
fname = 't-msgsign-signed.txt'
|
||||
@ -437,7 +446,7 @@ def test_verify_signature_file_header_warning(way, addr_sig, microsd_path, verif
|
||||
("bc1q2c8pym4m755pq4n4shu2wgzr7s58pygz8x6pg0mj0l6netq8am8qw69kss", "KPxCN2edt9w5ukd0feOlFS6PJjsKwm6ii/erZErKDIApIxjHqxBzoDvVqcTX0mtecNTGCkJPhxjRKCjNtdnTAp0=", 1),
|
||||
])
|
||||
def test_verify_signature_file_fail(way, addr_sig, microsd_path, cap_story, goto_home, nfc_write_text,
|
||||
pick_menu_item, need_keypress, verify_armored_signature):
|
||||
pick_menu_item, verify_armored_signature):
|
||||
fname = "fail-signed.txt"
|
||||
addr, sig, err_no = addr_sig
|
||||
|
||||
@ -464,7 +473,10 @@ def test_verify_signature_file_fail(way, addr_sig, microsd_path, cap_story, goto
|
||||
|
||||
@pytest.mark.parametrize("binary", [True, False])
|
||||
def test_verify_signature_file_digest_prob(binary, microsd_path, cap_story, pick_menu_item,
|
||||
need_keypress, goto_home):
|
||||
need_keypress, goto_home, is_q1):
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
cancel = KEY_CANCEL if is_q1 else "x"
|
||||
|
||||
fpattern = "to_sign"
|
||||
if binary:
|
||||
suffix = ".pdf"
|
||||
@ -487,19 +499,19 @@ def test_verify_signature_file_digest_prob(binary, microsd_path, cap_story, pick
|
||||
pick_menu_item("Advanced/Tools")
|
||||
pick_menu_item("File Management")
|
||||
pick_menu_item("List Files")
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
pick_menu_item(fname)
|
||||
need_keypress("4") # create detached sig
|
||||
need_keypress("y")
|
||||
need_keypress("x")
|
||||
need_keypress(confirm)
|
||||
need_keypress(cancel)
|
||||
pick_menu_item("Verify Sig File")
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
pick_menu_item(sig_name)
|
||||
time.sleep(0.1)
|
||||
title, story = cap_story()
|
||||
assert title == "CORRECT"
|
||||
assert "Good signature" in story
|
||||
need_keypress("y") # back in File Management
|
||||
need_keypress(confirm) # back in File Management
|
||||
|
||||
# modify contents of the file
|
||||
with open(fpath, mode) as f:
|
||||
@ -508,7 +520,7 @@ def test_verify_signature_file_digest_prob(binary, microsd_path, cap_story, pick
|
||||
|
||||
mod_digest = hashlib.sha256(mod_contents if binary else mod_contents.encode()).digest().hex()
|
||||
pick_menu_item("Verify Sig File")
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
pick_menu_item(sig_name)
|
||||
time.sleep(0.1)
|
||||
title, story = cap_story()
|
||||
@ -517,12 +529,12 @@ def test_verify_signature_file_digest_prob(binary, microsd_path, cap_story, pick
|
||||
assert ("'%s' has wrong contents" % fname) in story
|
||||
assert ("Got:\n%s" % orig_digest) in story
|
||||
assert ("Expected:\n%s" % mod_digest) in story
|
||||
need_keypress("y") # back in File Management
|
||||
need_keypress(confirm) # back in File Management
|
||||
|
||||
# remove file
|
||||
os.remove(fpath)
|
||||
pick_menu_item("Verify Sig File")
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
pick_menu_item(sig_name)
|
||||
time.sleep(0.1)
|
||||
title, story = cap_story()
|
||||
@ -530,12 +542,13 @@ def test_verify_signature_file_digest_prob(binary, microsd_path, cap_story, pick
|
||||
assert "Good signature" in story # sig is still correct
|
||||
assert ("'%s' is not present" % fname) in story
|
||||
assert 'Contents verification not possible' in story
|
||||
need_keypress("y") # back in File Management
|
||||
need_keypress(confirm) # back in File Management
|
||||
|
||||
|
||||
@pytest.mark.parametrize("f_num", [2, 10, 20])
|
||||
def test_verify_signature_file_digest_prob_multi(f_num, microsd_path, cap_story, pick_menu_item,
|
||||
need_keypress, goto_home):
|
||||
need_keypress, goto_home, is_q1):
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
files = []
|
||||
msg = ""
|
||||
for i in range(f_num):
|
||||
@ -567,13 +580,13 @@ def test_verify_signature_file_digest_prob_multi(f_num, microsd_path, cap_story,
|
||||
pick_menu_item("Advanced/Tools")
|
||||
pick_menu_item("File Management")
|
||||
pick_menu_item("Verify Sig File")
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
pick_menu_item(sig_name)
|
||||
time.sleep(0.1)
|
||||
title, story = cap_story()
|
||||
assert title == "CORRECT"
|
||||
assert "Good signature" in story
|
||||
need_keypress("y") # back in File Management
|
||||
need_keypress(confirm) # back in File Management
|
||||
|
||||
# change contents of 0th file
|
||||
fname, orig_digest, fpath, _, _ = files[0]
|
||||
@ -583,7 +596,7 @@ def test_verify_signature_file_digest_prob_multi(f_num, microsd_path, cap_story,
|
||||
f.write(new_contetns)
|
||||
|
||||
pick_menu_item("Verify Sig File")
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
pick_menu_item(sig_name)
|
||||
time.sleep(0.1)
|
||||
title, story = cap_story()
|
||||
@ -592,7 +605,7 @@ def test_verify_signature_file_digest_prob_multi(f_num, microsd_path, cap_story,
|
||||
assert ("'%s' has wrong contents" % fname) in story
|
||||
assert ("Got:\n%s" % orig_digest) in story
|
||||
assert ("Expected:\n%s" % mod_digest) in story
|
||||
need_keypress("y") # back in File Management
|
||||
need_keypress(confirm) # back in File Management
|
||||
|
||||
# change contents of 1st file remove 0th file
|
||||
# both warnings must be visible
|
||||
@ -605,7 +618,7 @@ def test_verify_signature_file_digest_prob_multi(f_num, microsd_path, cap_story,
|
||||
f.write(new_contetns)
|
||||
|
||||
pick_menu_item("Verify Sig File")
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
pick_menu_item(sig_name)
|
||||
time.sleep(0.1)
|
||||
title, story = cap_story()
|
||||
@ -616,12 +629,12 @@ def test_verify_signature_file_digest_prob_multi(f_num, microsd_path, cap_story,
|
||||
assert ("Expected:\n%s" % mod_digest) in story
|
||||
assert ("'%s' is not present" % fname0) in story
|
||||
assert 'Contents verification not possible' in story
|
||||
need_keypress("y") # back in File Management
|
||||
need_keypress(confirm) # back in File Management
|
||||
|
||||
# remove 1st file too
|
||||
os.remove(fpath)
|
||||
pick_menu_item("Verify Sig File")
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
pick_menu_item(sig_name)
|
||||
time.sleep(0.1)
|
||||
title, story = cap_story()
|
||||
@ -630,7 +643,7 @@ def test_verify_signature_file_digest_prob_multi(f_num, microsd_path, cap_story,
|
||||
warn_msg = "Files:\n" + "\n".join("> %s" % fname for fname in (fname0, fname1))
|
||||
assert warn_msg in story
|
||||
assert 'Contents verification not possible' in story
|
||||
need_keypress("y") # back in File Management
|
||||
need_keypress(confirm) # back in File Management
|
||||
|
||||
# reboult valid signed files
|
||||
for tup in files:
|
||||
@ -639,13 +652,13 @@ def test_verify_signature_file_digest_prob_multi(f_num, microsd_path, cap_story,
|
||||
f.write(conts)
|
||||
|
||||
pick_menu_item("Verify Sig File")
|
||||
need_keypress("y")
|
||||
need_keypress(confirm)
|
||||
pick_menu_item(sig_name)
|
||||
time.sleep(0.1)
|
||||
title, story = cap_story()
|
||||
assert title == "CORRECT"
|
||||
assert "Good signature" in story
|
||||
need_keypress("y") # back in File Management
|
||||
need_keypress(confirm) # back in File Management
|
||||
|
||||
@pytest.mark.parametrize("way", ("sd", "nfc"))
|
||||
@pytest.mark.parametrize("truncation_len", (0, 1))
|
||||
|
||||
@ -11,6 +11,7 @@ from struct import pack, unpack
|
||||
import ndef
|
||||
from hashlib import sha256
|
||||
from txn import *
|
||||
from charcodes import KEY_NFC, KEY_ENTER, KEY_CANCEL
|
||||
|
||||
@pytest.mark.parametrize('case', range(6))
|
||||
def test_ndef(case, load_shared_mod):
|
||||
@ -145,9 +146,13 @@ def test_ndef_ccfile(ccfile, load_shared_mod):
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def try_sign_nfc(cap_story, pick_menu_item, goto_home, need_keypress, sim_exec, nfc_read, nfc_write, nfc_block4rf):
|
||||
def try_sign_nfc(cap_story, pick_menu_item, goto_home, need_keypress,
|
||||
sim_exec, nfc_read, nfc_write, nfc_block4rf, is_q1):
|
||||
|
||||
# like "try_sign" but use NFC to send/receive PSBT/results
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
cancel = KEY_CANCEL if is_q1 else "x"
|
||||
k_nfc = KEY_NFC if is_q1 else "3"
|
||||
|
||||
sim_exec('from pyb import SDCard; SDCard.ejected = True; import nfc; nfc.NFCHandler.startup()')
|
||||
|
||||
@ -198,7 +203,7 @@ def try_sign_nfc(cap_story, pick_menu_item, goto_home, need_keypress, sim_exec,
|
||||
_, story = cap_story()
|
||||
assert 'NFC' in story
|
||||
|
||||
need_keypress('3')
|
||||
need_keypress(k_nfc)
|
||||
time.sleep(.1)
|
||||
nfc_write(ccfile)
|
||||
|
||||
@ -206,14 +211,14 @@ def try_sign_nfc(cap_story, pick_menu_item, goto_home, need_keypress, sim_exec,
|
||||
|
||||
if accept_ms_import:
|
||||
# would be better to do cap_story here
|
||||
need_keypress('y')
|
||||
need_keypress(confirm)
|
||||
time.sleep(0.050)
|
||||
|
||||
title, story = cap_story()
|
||||
assert title == 'OK TO SEND?'
|
||||
|
||||
if accept != None:
|
||||
need_keypress('y' if accept else 'x')
|
||||
need_keypress(confirm if accept else cancel)
|
||||
|
||||
if accept == False:
|
||||
time.sleep(0.050)
|
||||
@ -235,14 +240,14 @@ def try_sign_nfc(cap_story, pick_menu_item, goto_home, need_keypress, sim_exec,
|
||||
if 'Final TXID:' in lines:
|
||||
txid = lines[-1]
|
||||
|
||||
need_keypress('3')
|
||||
need_keypress(k_nfc)
|
||||
time.sleep(.1)
|
||||
contents = nfc_read()
|
||||
need_keypress('y')
|
||||
need_keypress(confirm)
|
||||
else:
|
||||
nfc_block4rf()
|
||||
contents = nfc_read()
|
||||
need_keypress('y')
|
||||
need_keypress(confirm)
|
||||
txid = None
|
||||
|
||||
got_txid = None
|
||||
@ -320,7 +325,11 @@ def try_sign_nfc(cap_story, pick_menu_item, goto_home, need_keypress, sim_exec,
|
||||
sim_exec('from pyb import SDCard; SDCard.ejected = False')
|
||||
|
||||
@pytest.mark.parametrize('num_outs', [ 1, 20, 250])
|
||||
def test_nfc_after(num_outs, fake_txn, try_sign, nfc_read, need_keypress, cap_story, only_mk4):
|
||||
def test_nfc_after(num_outs, fake_txn, try_sign, nfc_read, need_keypress,
|
||||
cap_story, is_q1):
|
||||
k_nfc = KEY_NFC if is_q1 else "3"
|
||||
# import pdb;pdb.set_trace()
|
||||
|
||||
# Read signing result (transaction) over NFC, decode it.
|
||||
psbt = fake_txn(1, num_outs)
|
||||
orig, result = try_sign(psbt, accept=True, finalize=True)
|
||||
@ -334,8 +343,8 @@ def test_nfc_after(num_outs, fake_txn, try_sign, nfc_read, need_keypress, cap_st
|
||||
title, story = cap_story()
|
||||
assert 'TXID' in title, story
|
||||
txid = a2b_hex(story.split()[0])
|
||||
assert 'Press (3)' in story
|
||||
need_keypress('3')
|
||||
assert f'Press {KEY_NFC if is_q1 else "(3)"}' in story
|
||||
need_keypress(k_nfc)
|
||||
|
||||
if too_big:
|
||||
title, story = cap_story()
|
||||
@ -343,7 +352,7 @@ def test_nfc_after(num_outs, fake_txn, try_sign, nfc_read, need_keypress, cap_st
|
||||
return
|
||||
|
||||
contents = nfc_read()
|
||||
#need_keypress('x')
|
||||
need_keypress(KEY_CANCEL if is_q1 else 'x')
|
||||
|
||||
#print("contents = " + B2A(contents))
|
||||
for got in ndef.message_decoder(contents):
|
||||
|
||||
Loading…
Reference in New Issue
Block a user