add test for getting C key from Seed Vault
This commit is contained in:
parent
446bea9926
commit
e726637319
@ -1495,8 +1495,8 @@ async def sign_psbt_file(filename, force_vdisk=False, slot_b=None, abort=False):
|
||||
psbt_len,
|
||||
approved_cb=done_signing,
|
||||
cb_kws={"filename": filename,
|
||||
"force_vdisk": force_vdisk,
|
||||
"output_encoder": output_encoder}
|
||||
"force_vdisk": force_vdisk,
|
||||
"output_encoder": output_encoder}
|
||||
)
|
||||
if abort:
|
||||
# needed for auto vdisk mode
|
||||
|
||||
@ -311,11 +311,7 @@ class CCCConfigMenu(MenuSystem):
|
||||
# trying to exit from CCCConfigMenu
|
||||
from seed import in_seed_vault
|
||||
|
||||
try:
|
||||
enc = CCCFeature.get_encoded_secret()
|
||||
except:
|
||||
# some test cases?
|
||||
enc = None
|
||||
enc = CCCFeature.get_encoded_secret()
|
||||
|
||||
if in_seed_vault(enc):
|
||||
# remind them to clear the seed-vault copy of Key C because it defeats feature
|
||||
@ -706,7 +702,7 @@ async def gen_or_import():
|
||||
"12-words or (2) for 24-words import." % OK
|
||||
|
||||
if settings.master_get("seedvault", False):
|
||||
msg += ' (6) for import from Seed Vault'
|
||||
msg += ' Press (6) to import from Seed Vault.'
|
||||
|
||||
ch = await ux_show_story(msg, escape='126', title="CCC Key C")
|
||||
|
||||
|
||||
@ -997,7 +997,7 @@ def settings_get(sim_exec):
|
||||
def master_settings_get(sim_exec):
|
||||
|
||||
def doit(key):
|
||||
cmd = f"RV.write(repr(settings.master_get('{key}')))"
|
||||
cmd = f"RV.write(repr(settings.master_get('{key}', False)))"
|
||||
resp = sim_exec(cmd)
|
||||
assert 'Traceback' not in resp, resp
|
||||
return eval(resp)
|
||||
@ -2469,6 +2469,29 @@ def garbage_collector():
|
||||
except: pass
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def build_test_seed_vault():
|
||||
def doit():
|
||||
from test_ephemeral import SEEDVAULT_TEST_DATA
|
||||
sv = []
|
||||
for item in SEEDVAULT_TEST_DATA:
|
||||
xfp, entropy, mnemonic = item
|
||||
|
||||
# build stashed encoded secret
|
||||
entropy_bytes = bytes.fromhex(entropy)
|
||||
if mnemonic:
|
||||
vlen = len(entropy_bytes)
|
||||
assert vlen in [16, 24, 32]
|
||||
marker = 0x80 | ((vlen // 8) - 2)
|
||||
stored_secret = bytes([marker]) + entropy_bytes
|
||||
else:
|
||||
stored_secret = entropy_bytes
|
||||
|
||||
sv.append((xfp, stored_secret.hex(), f"[{xfp}]", "meta"))
|
||||
return sv
|
||||
return doit
|
||||
|
||||
|
||||
# useful fixtures
|
||||
from test_backup import backup_system
|
||||
from test_bbqr import readback_bbqr, render_bbqr, readback_bbqr_ll, try_sign_bbqr
|
||||
|
||||
@ -547,28 +547,10 @@ def test_seed_vault_backup(settings_set, reset_seed_words, generate_ephemeral_wo
|
||||
assert xfp_ui in sv_xfp_menu
|
||||
|
||||
|
||||
def test_seed_vault_backup_frozen(reset_seed_words, settings_set, repl):
|
||||
from test_ephemeral import SEEDVAULT_TEST_DATA
|
||||
|
||||
def test_seed_vault_backup_frozen(reset_seed_words, settings_set, repl, build_test_seed_vault):
|
||||
reset_seed_words()
|
||||
settings_set("seedvault", 1)
|
||||
|
||||
sv = []
|
||||
for item in SEEDVAULT_TEST_DATA:
|
||||
xfp, entropy, mnemonic = item
|
||||
|
||||
# build stashed encoded secret
|
||||
entropy_bytes = bytes.fromhex(entropy)
|
||||
if mnemonic:
|
||||
vlen = len(entropy_bytes)
|
||||
assert vlen in [16, 24, 32]
|
||||
marker = 0x80 | ((vlen // 8) - 2)
|
||||
stored_secret = bytes([marker]) + entropy_bytes
|
||||
else:
|
||||
stored_secret = entropy_bytes
|
||||
|
||||
sv.append((xfp, stored_secret.hex(), f"[{xfp}]", "meta"))
|
||||
|
||||
sv = build_test_seed_vault()
|
||||
settings_set("seeds", sv)
|
||||
bk = repl.exec('import backups; RV.write(backups.render_backup_contents())', raw=1)
|
||||
assert 'Coldcard backup file' in bk
|
||||
|
||||
@ -178,7 +178,8 @@ _skip_quiz = False
|
||||
@pytest.fixture
|
||||
def setup_ccc(goto_home, pick_menu_item, cap_story, press_select, pass_word_quiz, is_q1,
|
||||
seed_story_to_words, cap_menu, OK, word_menu_entry, press_cancel, press_delete,
|
||||
enter_number, scan_a_qr, cap_screen, settings_get, need_keypress, microsd_path):
|
||||
enter_number, scan_a_qr, cap_screen, settings_get, need_keypress, microsd_path,
|
||||
master_settings_get):
|
||||
|
||||
def doit(c_words=None, mag=None, vel=None, whitelist=None, w2fa=None, first_time=True):
|
||||
if first_time:
|
||||
@ -196,6 +197,8 @@ def setup_ccc(goto_home, pick_menu_item, cap_story, press_select, pass_word_quiz
|
||||
assert f"Press {OK} to generate new 12-word seed phrase"
|
||||
assert "(1)" in story
|
||||
assert "(2)" in story
|
||||
if master_settings_get("seedvault"):
|
||||
assert "(6) to import from Seed Vault" in story
|
||||
|
||||
if c_words is None:
|
||||
nwords = 12 # always 12 words if generated by us
|
||||
@ -357,25 +360,24 @@ def setup_ccc(goto_home, pick_menu_item, cap_story, press_select, pass_word_quiz
|
||||
@pytest.fixture
|
||||
def enter_enabled_ccc(goto_home, pick_menu_item, cap_story, press_select, is_q1,
|
||||
word_menu_entry, cap_menu):
|
||||
def doit(c_words, first_time=False, seed_vault=False):
|
||||
if not first_time:
|
||||
goto_home()
|
||||
pick_menu_item("Advanced/Tools")
|
||||
pick_menu_item("Coldcard Co-Signing")
|
||||
def doit(c_words, seed_vault=False):
|
||||
goto_home()
|
||||
pick_menu_item("Advanced/Tools")
|
||||
pick_menu_item("Coldcard Co-Signing")
|
||||
time.sleep(.1)
|
||||
title, story = cap_story()
|
||||
if seed_vault:
|
||||
assert "You have a copy of the CCC key C in the Seed Vault" in story
|
||||
assert "You must delete that key from the vault once setup and debug is finished" in story
|
||||
assert "or all benefit of this feature is lost!" in story
|
||||
press_select()
|
||||
else:
|
||||
assert title == "CCC Enabled"
|
||||
assert "policy cannot be viewed, changed" in story
|
||||
assert "unless you have the seed words for key C" in story
|
||||
press_select()
|
||||
time.sleep(.1)
|
||||
title, story = cap_story()
|
||||
if seed_vault:
|
||||
assert "You have a copy of the CCC key C in the Seed Vault" in story
|
||||
assert "You must delete that key from the vault once setup and debug is finished" in story
|
||||
assert "or all benefit of this feature is lost!" in story
|
||||
press_select()
|
||||
else:
|
||||
assert title == "CCC Enabled"
|
||||
assert "policy cannot be viewed, changed" in story
|
||||
assert "unless you have the seed words for key C" in story
|
||||
press_select()
|
||||
time.sleep(.1)
|
||||
word_menu_entry(c_words)
|
||||
word_menu_entry(c_words)
|
||||
|
||||
return doit
|
||||
|
||||
@ -565,7 +567,7 @@ def policy_sign(start_sign, end_sign, cap_story, get_last_violation):
|
||||
@pytest.mark.bitcoind
|
||||
@pytest.mark.parametrize("mag_ok", [True, False])
|
||||
@pytest.mark.parametrize("mag", [1000000, None, 2])
|
||||
def test_ccc_magnitude(mag_ok, mag, setup_ccc, enter_enabled_ccc, ccc_ms_setup,
|
||||
def test_ccc_magnitude(mag_ok, mag, setup_ccc, ccc_ms_setup,
|
||||
bitcoind, settings_set, policy_sign,
|
||||
bitcoind_create_watch_only_wallet):
|
||||
|
||||
@ -585,8 +587,7 @@ def test_ccc_magnitude(mag_ok, mag, setup_ccc, enter_enabled_ccc, ccc_ms_setup,
|
||||
else:
|
||||
to_send = ((mag / 100000000)+1) if mag > 1000 else (mag+0.001)
|
||||
|
||||
words = setup_ccc(mag=mag, vel="Unlimited")
|
||||
enter_enabled_ccc(words, first_time=True)
|
||||
setup_ccc(mag=mag, vel="Unlimited")
|
||||
_, target_mi = ccc_ms_setup()
|
||||
bitcoind_wo = bitcoind_create_watch_only_wallet(target_mi)
|
||||
|
||||
@ -604,7 +605,7 @@ def test_ccc_magnitude(mag_ok, mag, setup_ccc, enter_enabled_ccc, ccc_ms_setup,
|
||||
|
||||
@pytest.mark.bitcoind
|
||||
@pytest.mark.parametrize("whitelist_ok", [True, False])
|
||||
def test_ccc_whitelist(whitelist_ok, setup_ccc, enter_enabled_ccc, ccc_ms_setup,
|
||||
def test_ccc_whitelist(whitelist_ok, setup_ccc, ccc_ms_setup,
|
||||
bitcoind, settings_set, policy_sign,
|
||||
bitcoind_create_watch_only_wallet):
|
||||
|
||||
@ -624,8 +625,7 @@ def test_ccc_whitelist(whitelist_ok, setup_ccc, enter_enabled_ccc, ccc_ms_setup,
|
||||
else:
|
||||
send_to = bitcoind.supply_wallet.getnewaddress()
|
||||
|
||||
words = setup_ccc(whitelist=whitelist, vel="Unlimited")
|
||||
enter_enabled_ccc(words, first_time=True)
|
||||
setup_ccc(whitelist=whitelist, vel="Unlimited")
|
||||
_, target_mi = ccc_ms_setup()
|
||||
bitcoind_wo = bitcoind_create_watch_only_wallet(target_mi)
|
||||
|
||||
@ -642,9 +642,8 @@ def test_ccc_whitelist(whitelist_ok, setup_ccc, enter_enabled_ccc, ccc_ms_setup,
|
||||
|
||||
@pytest.mark.bitcoind
|
||||
@pytest.mark.parametrize("velocity_mi", ['6 blocks (hour)', '48 blocks (8h)'])
|
||||
def test_ccc_velocity(velocity_mi, setup_ccc, enter_enabled_ccc, ccc_ms_setup,
|
||||
bitcoind, settings_set, policy_sign, settings_get,
|
||||
bitcoind_create_watch_only_wallet):
|
||||
def test_ccc_velocity(velocity_mi, setup_ccc, ccc_ms_setup, bitcoind, settings_set,
|
||||
policy_sign, settings_get, bitcoind_create_watch_only_wallet):
|
||||
|
||||
settings_set("ccc", None)
|
||||
settings_set("chain", "XRT")
|
||||
@ -652,8 +651,7 @@ def test_ccc_velocity(velocity_mi, setup_ccc, enter_enabled_ccc, ccc_ms_setup,
|
||||
|
||||
blocks = int(velocity_mi.split()[0])
|
||||
|
||||
words = setup_ccc(vel=velocity_mi)
|
||||
enter_enabled_ccc(words, first_time=True)
|
||||
setup_ccc(vel=velocity_mi)
|
||||
_, target_mi = ccc_ms_setup()
|
||||
|
||||
assert settings_get("ccc")["pol"]["block_h"] == 0
|
||||
@ -728,8 +726,7 @@ def test_ccc_velocity(velocity_mi, setup_ccc, enter_enabled_ccc, ccc_ms_setup,
|
||||
|
||||
|
||||
@pytest.mark.bitcoind
|
||||
def test_ccc_warnings(setup_ccc, enter_enabled_ccc, ccc_ms_setup,
|
||||
bitcoind, settings_set, policy_sign,
|
||||
def test_ccc_warnings(setup_ccc, ccc_ms_setup, bitcoind, settings_set, policy_sign,
|
||||
bitcoind_create_watch_only_wallet, settings_get):
|
||||
|
||||
settings_set("ccc", None)
|
||||
@ -740,8 +737,7 @@ def test_ccc_warnings(setup_ccc, enter_enabled_ccc, ccc_ms_setup,
|
||||
"2Mxp1Dy2MyR4w36J2VaZhrFugNNFgh6LC1j",
|
||||
"mjR14oKxYzRg9RAZdpu3hrw8zXfFgGzLKm"]
|
||||
|
||||
words = setup_ccc(mag=10000000, vel='6 blocks (hour)', whitelist=whitelist,)
|
||||
enter_enabled_ccc(words, first_time=True)
|
||||
setup_ccc(mag=10000000, vel='6 blocks (hour)', whitelist=whitelist,)
|
||||
_, target_mi = ccc_ms_setup()
|
||||
bitcoind_wo = bitcoind_create_watch_only_wallet(target_mi)
|
||||
|
||||
@ -800,15 +796,14 @@ def test_maxed_out(settings_set, setup_ccc, enter_enabled_ccc, ccc_ms_setup, sim
|
||||
|
||||
# C mnemonic is 24 words
|
||||
c_words = "cluster comic depend absent grain circle demand tag pass clock certain strategy lunar bless pulse useful comfort fatigue glove decorate taste allow adult journey".split()
|
||||
words = setup_ccc(c_words=c_words, mag=100000000, vel='4032 blocks (4w)', whitelist=None)
|
||||
enter_enabled_ccc(words, first_time=True)
|
||||
setup_ccc(c_words=c_words, mag=100000000, vel='4032 blocks (4w)', whitelist=None)
|
||||
# B mnemonic is 24 words
|
||||
b_words = "ceiling apology excite illegal accident define boat prosper decrease utility romance try trial dizzy win lawsuit much sustain similar meadow draw oil cousin wagon".split()
|
||||
_, target_mi = ccc_ms_setup(b_words=b_words)
|
||||
bitcoind_wo = bitcoind_create_watch_only_wallet(target_mi)
|
||||
|
||||
# create whitelist with own addresses - only conso to first 25 addrs allowed
|
||||
enter_enabled_ccc(c_words, first_time=False)
|
||||
enter_enabled_ccc(c_words)
|
||||
# pick random internal/external descriptor
|
||||
ms_descriptors = bitcoind_wo.listdescriptors()
|
||||
|
||||
@ -863,8 +858,7 @@ def test_load_and_sign_key_C(settings_set, setup_ccc, enter_enabled_ccc, ccc_ms_
|
||||
settings_set("seedvault", int(seed_vault))
|
||||
settings_set("seeds", [])
|
||||
|
||||
words = setup_ccc(c_words=None)
|
||||
enter_enabled_ccc(words, first_time=True)
|
||||
setup_ccc(c_words=None)
|
||||
_, target_mi = ccc_ms_setup()
|
||||
bitcoind_wo = bitcoind_create_watch_only_wallet(target_mi)
|
||||
|
||||
@ -925,9 +919,10 @@ def test_load_and_sign_key_C(settings_set, setup_ccc, enter_enabled_ccc, ccc_ms_
|
||||
@pytest.mark.parametrize("c_num_words", [None, 12, 24])
|
||||
@pytest.mark.parametrize("acct", [None, 9999])
|
||||
def test_ccc_xpub_export(chain, c_num_words, acct, settings_set, load_export, setup_ccc,
|
||||
enter_enabled_ccc, pick_menu_item, enter_number, press_select,
|
||||
settings_get, cap_menu):
|
||||
pick_menu_item, enter_number, press_select, settings_get, cap_menu,
|
||||
goto_home):
|
||||
# - "export cc xpubs" path
|
||||
goto_home()
|
||||
settings_set("ccc", None)
|
||||
settings_set("chain", chain)
|
||||
settings_set("multisig", [])
|
||||
@ -941,7 +936,6 @@ def test_ccc_xpub_export(chain, c_num_words, acct, settings_set, load_export, se
|
||||
words = words.split()
|
||||
|
||||
setup_ccc(c_words=words)
|
||||
enter_enabled_ccc(words, first_time=True)
|
||||
pick_menu_item("Export CCC XPUBs")
|
||||
if acct is None:
|
||||
press_select() # default zero
|
||||
@ -981,14 +975,14 @@ def test_ccc_xpub_export(chain, c_num_words, acct, settings_set, load_export, se
|
||||
def test_multiple_multisig_wallets(settings_set, setup_ccc, enter_enabled_ccc, ccc_ms_setup,
|
||||
bitcoind_create_watch_only_wallet, cap_story, bitcoind,
|
||||
policy_sign, settings_get, cap_menu, pick_menu_item,
|
||||
press_select, load_export, offer_ms_import):
|
||||
press_select, load_export, offer_ms_import, goto_home):
|
||||
# - 'build 2-of-N' path
|
||||
goto_home()
|
||||
settings_set("ccc", None)
|
||||
settings_set("chain", "XRT")
|
||||
settings_set("multisig", [])
|
||||
|
||||
words = setup_ccc(c_words=None, mag=2, vel='6 blocks (hour)')
|
||||
enter_enabled_ccc(words, first_time=True)
|
||||
b_keys_0, mi = ccc_ms_setup(N=5)
|
||||
assert len(b_keys_0) == 3 # 5 - 2 (C, A) = 3
|
||||
w0 = bitcoind_create_watch_only_wallet(mi)
|
||||
@ -1029,7 +1023,7 @@ def test_multiple_multisig_wallets(settings_set, setup_ccc, enter_enabled_ccc, c
|
||||
init_block_height+1)["psbt"]
|
||||
policy_sign(w, psbt, violation="velocity")
|
||||
|
||||
enter_enabled_ccc(words, first_time=False)
|
||||
enter_enabled_ccc(words)
|
||||
_, ami = ccc_ms_setup(N=8)
|
||||
_, mi = ccc_ms_setup(N=4)
|
||||
time.sleep(.1)
|
||||
@ -1062,20 +1056,19 @@ def test_multiple_multisig_wallets(settings_set, setup_ccc, enter_enabled_ccc, c
|
||||
press_select()
|
||||
time.sleep(.1)
|
||||
|
||||
enter_enabled_ccc(words, first_time=False)
|
||||
enter_enabled_ccc(words)
|
||||
m = cap_menu()
|
||||
assert f"{w_mn} {new_name}" in m
|
||||
|
||||
|
||||
def test_remove_ccc(settings_set, setup_ccc, enter_enabled_ccc, ccc_ms_setup, settings_get,
|
||||
pick_menu_item, cap_story, press_select, need_keypress, policy_sign,
|
||||
bitcoind_create_watch_only_wallet, bitcoind):
|
||||
|
||||
def test_remove_ccc(settings_set, setup_ccc, ccc_ms_setup, settings_get, policy_sign,
|
||||
pick_menu_item, cap_story, press_select, need_keypress,
|
||||
bitcoind_create_watch_only_wallet, bitcoind, goto_home):
|
||||
goto_home()
|
||||
settings_set("ccc", None)
|
||||
settings_set("multisig", [])
|
||||
|
||||
words = setup_ccc(c_words=None, mag=2, vel='6 blocks (hour)')
|
||||
enter_enabled_ccc(words, first_time=True)
|
||||
setup_ccc(c_words=None, mag=2, vel='6 blocks (hour)')
|
||||
_, mi = ccc_ms_setup(N=3)
|
||||
|
||||
w0 = bitcoind_create_watch_only_wallet(mi)
|
||||
@ -1109,8 +1102,62 @@ def test_remove_ccc(settings_set, setup_ccc, enter_enabled_ccc, ccc_ms_setup, se
|
||||
policy_sign(w0, psbt, ccc_disabled=True)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("has_candidates", [True, False])
|
||||
def test_c_key_from_seed_vault(has_candidates, setup_ccc, build_test_seed_vault, settings_set,
|
||||
goto_home, pick_menu_item, press_select, need_keypress, cap_menu,
|
||||
cap_story, press_cancel, enter_enabled_ccc):
|
||||
goto_home()
|
||||
settings_set("ccc", None)
|
||||
settings_set("multisig", [])
|
||||
|
||||
# TODO
|
||||
# - policy-fail reason submenu; check display
|
||||
settings_set("seedvault", True)
|
||||
sv = build_test_seed_vault()
|
||||
if not has_candidates:
|
||||
# last item is XPR - not acceptable
|
||||
sv = sv[-1:]
|
||||
|
||||
# EOF
|
||||
settings_set("seeds", sv)
|
||||
|
||||
goto_home()
|
||||
pick_menu_item("Advanced/Tools")
|
||||
pick_menu_item("Coldcard Co-Signing")
|
||||
press_select()
|
||||
|
||||
time.sleep(.1)
|
||||
title, story = cap_story()
|
||||
assert title == "CCC Key C"
|
||||
assert "(6) to import from Seed Vault" in story
|
||||
need_keypress("6")
|
||||
time.sleep(.1)
|
||||
m = cap_menu()
|
||||
if not has_candidates:
|
||||
assert len(m) == 1
|
||||
assert m[0] == "(none suitable)"
|
||||
# unpickable
|
||||
for _ in range(3):
|
||||
pick_menu_item(m[0])
|
||||
|
||||
# nothing happened
|
||||
m = cap_menu()
|
||||
assert len(m) == 1
|
||||
assert m[0] == "(none suitable)"
|
||||
press_cancel()
|
||||
return
|
||||
|
||||
# build_test_seed_vault has length of 4, but last item is xprv
|
||||
# xprvs not allowed here - so not displayed in SeedVaultChooserMenu
|
||||
assert len(m) == 3
|
||||
m0_xfp = m[0].strip().split(" ", 1)[-1]
|
||||
pick_menu_item(m[0])
|
||||
time.sleep(.1)
|
||||
m = cap_menu()
|
||||
assert m0_xfp in m[0]
|
||||
press_cancel()
|
||||
time.sleep(.1)
|
||||
title, story = cap_story()
|
||||
assert title == "REMINDER"
|
||||
assert "Key C is in your Seed Vault" in story
|
||||
assert "MUST delete" in story
|
||||
press_select()
|
||||
|
||||
# EOF
|
||||
Loading…
Reference in New Issue
Block a user