Merge branch 'master' of github.com:Coldcard/firmware

This commit is contained in:
Peter D. Gray 2023-12-18 10:35:08 -05:00
commit 2b5a99ed59
No known key found for this signature in database
GPG Key ID: A2DCD558C2BE5D7C
5 changed files with 139 additions and 13 deletions

View File

@ -264,16 +264,16 @@ def seconds2human_readable(s):
hours = s % (3600 * 24) // 3600
minutes = (s % 3600) // 60
seconds = (s % 3600) % 60
msg = ""
msg = []
if days:
msg += "%dd" % days
msg.append("%dd" % days)
if hours:
msg += " %dh" % hours
msg.append("%dh" % hours)
if minutes:
msg += " %dm" % minutes
msg.append("%dm" % minutes)
if seconds:
msg += " %ds" % seconds
msg.append("%ds" % seconds)
return msg
return " ".join(msg)
# EOF

View File

@ -49,7 +49,7 @@ def backup_system(settings_set, settings_remove, goto_home, pick_menu_item,
assert "ignores passphrases and produces backup of main seed" in body
assert "(2) to back-up BIP39 passphrase wallet" in body
if st == "eph":
assert "An temporary seed is in effect" in body
assert "A temporary seed is in effect" in body
assert "so backup will be of that seed" in body
need_keypress("y")
@ -238,7 +238,7 @@ def test_backup_ephemeral_wallet(stype, pick_menu_item, need_keypress, goto_home
pick_menu_item("Backup System")
time.sleep(.1)
title, story = cap_story()
assert "An temporary seed is in effect" in story
assert "A temporary seed is in effect" in story
assert "so backup will be of that seed" in story
need_keypress("y")
time.sleep(.1)
@ -540,6 +540,7 @@ def test_clone_start(reset_seed_words, pick_menu_item, cap_story, goto_home):
num_7z = len([i for i in os.listdir(sd_dir) if i.endswith(".7z")])
fname = "ccbk-start.json"
reset_seed_words()
goto_home()
shutil.copy(f"data/{fname}", sd_dir)
pick_menu_item("Advanced/Tools")
pick_menu_item("Backup")

View File

@ -226,7 +226,8 @@ def test_bip_vectors(mode, index, entropy, expect, cap_story, need_keypress,
])
@pytest.mark.parametrize('index', [0, 1, 10, 100, 1000, 9999])
def test_path_index(mode, pattern, index, need_keypress, cap_screen_qr,
derive_bip85_secret):
derive_bip85_secret, reset_seed_words):
reset_seed_words()
# Uses any key on Simulator; just checking for operation + entropy level
_, story = derive_bip85_secret(mode, index)
@ -279,6 +280,8 @@ def test_path_index(mode, pattern, index, need_keypress, cap_screen_qr,
elif 'WIF' in mode:
assert qr == got
need_keypress("x")
def test_type_passwords(dev, cap_menu, pick_menu_item,
goto_home, cap_story, need_keypress, cap_screen

View File

@ -119,11 +119,11 @@ def get_seed_value_ux(goto_home, pick_menu_item, need_keypress, cap_story, nfc_r
if nfc:
need_keypress("1") # show QR code
time.sleep(.1)
time.sleep(.2)
need_keypress("3") # any QR can be exported via NFC
time.sleep(.1)
time.sleep(.2)
str_words = nfc_read_text()
time.sleep(.1)
time.sleep(.5)
need_keypress("y") # exit NFC animation
return str_words.split(" ") # always truncated

View File

@ -23,7 +23,7 @@ from onetimepass import get_hotp
from objstruct import ObjectStruct as DICT
from txn import render_address, fake_txn
from psbt import ser_prop_key
from helpers import sign_msg, prandom
from helpers import sign_msg, prandom, xfp2str
from ckcc_protocol.constants import *
from ckcc_protocol.protocol import CCProtocolPacker
from ckcc_protocol.protocol import CCUserRefused, CCProtoError
@ -776,6 +776,128 @@ def test_multiple_signings(dev, quick_start_hsm, is_simulator,
attempt_psbt(psbt)
@pytest.mark.veryslow
@pytest.mark.parametrize("cc_first", [True, False])
@pytest.mark.parametrize("M_N", [(2,3), (3,5), (15,15)])
def test_multiple_signings_multisig(cc_first, M_N, dev, quick_start_hsm,
is_simulator, attempt_psbt, fake_txn,
load_hsm_users, auth_user, bitcoind,
request):
# signs 400 different PSBTs in loop beaing one leg of multisig
# CC must be on regtest if testing with real thing
af = "bech32"
M, N = M_N
bitcoind.delete_wallet_files(pattern="bitcoind--signer")
bitcoind.delete_wallet_files(pattern="watch_only_")
# create multiple bitcoin wallets (N-1) as one signer is CC
bitcoind_signers = [
bitcoind.create_wallet(wallet_name=f"bitcoind--signer{i}", disable_private_keys=False, blank=False,
passphrase=None, avoid_reuse=False, descriptors=True)
for i in range(N - 1)
]
for signer in bitcoind_signers:
signer.keypoolrefill(405)
# watch only wallet where multisig descriptor will be imported
bitcoind_watch_only = bitcoind.create_wallet(
wallet_name=f"watch_only_{af}_{M}of{N}", disable_private_keys=True,
blank=True, passphrase=None, avoid_reuse=False, descriptors=True
)
# get keys from bitcoind signers
bitcoind_signers_xpubs = []
for signer in bitcoind_signers:
target_desc = ""
bitcoind_descriptors = signer.listdescriptors()["descriptors"]
for desc in bitcoind_descriptors:
if desc["desc"].startswith("pkh(") and desc["internal"] is False:
target_desc = desc["desc"]
core_desc, checksum = target_desc.split("#")
# remove pkh(....)
core_key = core_desc[4:-1]
bitcoind_signers_xpubs.append(core_key)
cc_deriv = "m/9999h/1h/0h"
cc_xpub = dev.send_recv(CCProtocolPacker.get_xpub(cc_deriv), timeout=None)
xfp_str = xfp2str(dev.master_fingerprint).lower()
cc_key_ext = f"[{xfp_str}/{cc_deriv.replace('m/','')}]{cc_xpub}/0/*"
cc_key_int = f"[{xfp_str}/{cc_deriv.replace('m/','')}]{cc_xpub}/1/*"
assert cc_xpub[1:4] == 'pub'
all_external = bitcoind_signers_xpubs + [cc_key_ext]
bitcoind_signers_xpubs_int = [i.replace("/0/*", "/1/*") for i in bitcoind_signers_xpubs]
all_internal = bitcoind_signers_xpubs_int + [cc_key_int]
template_ext = f"wsh(sortedmulti({M},{','.join(all_external)}))"
template_int = f"wsh(sortedmulti({M},{','.join(all_internal)}))"
desc_info_ext = bitcoind_watch_only.getdescriptorinfo(template_ext)
desc_info_int = bitcoind_watch_only.getdescriptorinfo(template_int)
desc_ext = desc_info_ext["descriptor"] # external with checksum
desc_int = desc_info_int["descriptor"] # internal with checksum
desc_obj_ext = {
"desc": desc_ext,
"active": True,
"timestamp": "now",
"internal": False,
"range": [0, 405],
}
desc_obj_int = {
"desc": desc_int,
"active": True,
"timestamp": "now",
"internal": True,
"range": [0, 405],
}
# import multisig wallet to bitcoin core watch only wallet
res = bitcoind_watch_only.importdescriptors([desc_obj_int, desc_obj_ext])
for obj in res:
assert obj["success"], obj
# uploading only external to CC
file_len, sha = dev.upload_file(desc_ext.encode('ascii'))
open('debug/last-config.txt', 'wt').write(desc_ext)
dev.send_recv(CCProtocolPacker.multisig_enroll(file_len, sha), timeout=30000)
time.sleep(.2)
if dev.is_simulator:
need_keypress = request.getfixturevalue('need_keypress')
need_keypress("y")
else:
import pdb;pdb.set_trace() # user interaction required on real CC
multi_addr = bitcoind_watch_only.getnewaddress("", af)
# create spendable segwit utxo in multi wallet
bitcoind.supply_wallet.generatetoaddress(5, bitcoind.supply_wallet.getnewaddress())
bitcoind.supply_wallet.sendtoaddress(address=multi_addr, amount=250)
bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress())
policy = DICT(warnings_ok=True, must_log=1, rules=[dict(users=['pw'])])
load_hsm_users()
quick_start_hsm(policy)
for count in range(400):
# do a peel chain
dest_a = bitcoind.supply_wallet.getnewaddress("", af)
psbt_resp = bitcoind_watch_only.walletcreatefundedpsbt(
[], [{dest_a: 0.3}], 0, {"fee_rate": 10, "change_type": af}
)
psbt_str = psbt_resp.get("psbt")
if not cc_first:
signed = 0
for signer in bitcoind_signers:
resp = signer.walletprocesspsbt(psbt_str, True)
psbt_str = resp.get("psbt")
signed +=1
# do not want to finalize this
if signed == M - 1:
break
psbt = base64.b64decode(psbt_str)
auth_user.psbt_hash = sha256(psbt).digest()
auth_user("pw")
attempt_psbt(psbt)
def test_sign_msg_good(quick_start_hsm, change_hsm, attempt_msg_sign, addr_fmt=AF_CLASSIC):
# message signing, but only at certain derivations
permit = ['m/73', "m/*'", 'm/1p/3h/4/5/6/7' ]