load C key + seed vault access

This commit is contained in:
scgbckbone 2024-10-23 13:49:36 +02:00
parent b5a6bf5d18
commit 58eec8be8b
2 changed files with 55 additions and 26 deletions

View File

@ -17,7 +17,6 @@ from exceptions import CCCPolicyViolationError
# limit to number of addresses in list
MAX_WHITELIST = const(25)
class CCCFeature:
# we don't show the user the reason for policy fail (by design, so attacker
@ -312,8 +311,8 @@ wallet, proceed to the multisig menu and remove related wallet entry.'''):
if in_seed_vault(enc):
# remind them to clear the seed-vault copy of Key C because it defeats feature
await ux_show_story('''Key C is in your Seed Vault. If you are done with setup, \
you MUST delete it from the Vault!''', title='REMINDER')
await ux_show_story("Key C is in your Seed Vault. If you are done with setup, "
"you MUST delete it from the Vault!", title='REMINDER')
the_ux.pop()
@ -323,7 +322,7 @@ you MUST delete it from the Vault!''', title='REMINDER')
enc = CCCFeature.get_encoded_secret()
from multisig import export_multisig_xpubs
await export_multisig_xpubs(*a, xfp=xfp, alt_secret=enc, skip_prompt=True)
await export_multisig_xpubs(xfp=xfp, alt_secret=enc, skip_prompt=True)
async def build_2ofN(self, *a):
# ask for a key B, assume A and C are defined => export MS config and import into self.
@ -352,7 +351,7 @@ be ready to show it as a QR, before proceeding.'''
# - just a shortcut, since they have the words, and could enter them
# - one-way trip because the CCC feature won't be enabled inside the temp seed settings
if await ux_show_story(
'Loads the CCC controled seed (key C) as a Temporary Seed and allows '
'Loads the CCC controlled seed (key C) as a Temporary Seed and allows '
'easy use of all Coldcard features on that key.\n\nIf you save into Seed Vault, '
'access to CCC Config menu is quick and easy.') != 'y':
return
@ -594,7 +593,7 @@ class CCCPolicyMenu(MenuSystem):
if not val:
msg = "No check for maximum transaction size will be done. "
if self.policy.get('vel', 0):
msg += ' Velocity check also disabled.'
msg += 'Velocity check also disabled. '
args['vel'] = 0
else:
msg += " maximum per-transaction: \n\n %s" % render_mag_value(val)

View File

@ -2,8 +2,10 @@
#
# tests related to CCC feature
#
# run simulator without --eff
#
import pytest, requests, re, time, random, json, glob, os, hashlib, struct, base64, bech32
#
import pytest, pdb, requests, re, time, random, json, glob, os, hashlib, struct, base64
from binascii import a2b_hex
from base64 import urlsafe_b64encode
from onetimepass import get_totp
@ -341,19 +343,25 @@ 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):
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")
time.sleep(.1)
title, story = cap_story()
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)
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)
return doit
@ -852,13 +860,17 @@ def test_maxed_out(settings_set, setup_ccc, enter_enabled_ccc, ccc_ms_setup, sim
restore_main_seed()
def test_sign_with_key_C(settings_set, setup_ccc, enter_enabled_ccc, ccc_ms_setup, cap_menu,
bitcoind_create_watch_only_wallet, pick_menu_item, load_export,
press_cancel, bitcoind, policy_sign, goto_eph_seed_menu, word_menu_entry,
import_multisig, press_select, settings_get, sim_exec, restore_main_seed):
@pytest.mark.parametrize("seed_vault", [True, False])
def test_load_and_sign_key_C(settings_set, setup_ccc, enter_enabled_ccc, ccc_ms_setup, cap_menu,
bitcoind_create_watch_only_wallet, pick_menu_item, load_export, sim_exec,
cap_story, press_cancel, bitcoind, policy_sign, restore_main_seed,
verify_ephemeral_secret_ui, word_menu_entry, import_multisig,
press_select, settings_get, seed_vault, confirm_tmp_seed):
settings_set("ccc", None)
settings_set("chain", "XRT")
settings_set("multisig", [])
settings_set("seedvault", int(seed_vault))
settings_set("seeds", [])
words = setup_ccc(c_words=None)
enter_enabled_ccc(words, first_time=True)
@ -886,18 +898,24 @@ def test_sign_with_key_C(settings_set, setup_ccc, enter_enabled_ccc, ccc_ms_setu
psbt = psbt_resp.get("psbt")
part_psbt, _ = policy_sign(bitcoind_wo, psbt, violation="magnitude") # more than 1 BTC
# get C seed
# get C seed from device as it was TRNG generated
ccc_secret = bytes.fromhex(settings_get("ccc")["secret"])
assert ccc_secret[0] == 128
ccc_entropy = ccc_secret[1:] # marker
c_words = Mnemonic('english').to_mnemonic(ccc_entropy)
# load key B as tmp
goto_eph_seed_menu()
pick_menu_item("Import Words")
pick_menu_item("12 Words")
time.sleep(0.1)
word_menu_entry(c_words.split())
# load key C as tmp
enter_enabled_ccc(c_words.split())
pick_menu_item("Load Key C")
time.sleep(.1)
title, story = cap_story()
assert "Loads the CCC controlled seed (key C) as a Temporary Seed" in story
assert "save into Seed Vault" in story
assert "access to CCC Config menu is quick and easy" in story
press_select()
confirm_tmp_seed(seedvault=seed_vault)
verify_ephemeral_secret_ui(mnemonic=c_words.split(), seed_vault=seed_vault)
import_multisig(data=ms_conf)
press_select() # confirm multisig import
@ -905,7 +923,19 @@ def test_sign_with_key_C(settings_set, setup_ccc, enter_enabled_ccc, ccc_ms_setu
sim_exec('from ccc import CCCFeature; CCCFeature.last_fail_reason=""')
# no violations ccc not in C settings
policy_sign(bitcoind_wo, base64.b64encode(part_psbt).decode())
restore_main_seed()
restore_main_seed(seed_vault=seed_vault)
enter_enabled_ccc(c_words.split(), seed_vault=seed_vault)
press_cancel()
time.sleep(.1)
title, story = cap_story()
if seed_vault:
assert title == "REMINDER"
assert "Key C is in your Seed Vault" in story
assert "you MUST delete it from the Vault!" in story
else:
# if key is not in seed vault there is no reminder
assert not title and not story
@pytest.mark.parametrize("chain", ["BTC", "XTN"])