restore backup via USB; new CLI commnad "restore"; add backup restore to "upload" cmd
This commit is contained in:
parent
4f4d08c73f
commit
f1ce63812c
33
README.md
33
README.md
@ -159,3 +159,36 @@ Commands:
|
||||
get Get registered miniscript wallet by name.
|
||||
ls List registered miniscript wallet names.
|
||||
```
|
||||
|
||||
## Backup/Restore
|
||||
|
||||
```
|
||||
Usage: ckcc backup [OPTIONS]
|
||||
|
||||
Creates 7z encrypted backup file after prompting user to remember a massive
|
||||
passphrase. Downloads the AES-encrypted data backup and by default, saves
|
||||
into current directory using a filename based on today's date.
|
||||
|
||||
Options:
|
||||
-d, --outdir DIRECTORY Save into indicated directory (auto filename)
|
||||
-o, --outfile filename.7z Name for backup file
|
||||
--help Show this message and exit.
|
||||
```
|
||||
|
||||
```
|
||||
Usage: ckcc restore [OPTIONS] backup.7z
|
||||
|
||||
Uploads 7z encrypted backup file & starts backup restore process. User needs
|
||||
to specify what kind of backup is being uploaded. Default is 7z encrypted
|
||||
file with word-based password. Use -p/--password flag if your backup has
|
||||
custom not word-based password. User is prompted to enter backup password on
|
||||
the device.
|
||||
|
||||
Options:
|
||||
-c, --plaintext Force plaintext restore. No need to use if file has proper
|
||||
'.txt' suffix
|
||||
-p, --password This backup has custom password. Not words.
|
||||
-t, --tmp Force restoring backup as temporary seed. Only works for
|
||||
seedless Coldcard.
|
||||
--help Show this message and exit.
|
||||
```
|
||||
44
ckcc/cli.py
44
ckcc/cli.py
@ -282,11 +282,14 @@ def real_file_upload(fd, dev, blksize=MAX_BLK_LEN, do_upgrade=False, do_reboot=T
|
||||
help='Attempt multisig enroll using file')
|
||||
@click.option('--miniscript', is_flag=True,
|
||||
help='Attempt miniscript enroll using file')
|
||||
def file_upload(filename, blksize, multisig, miniscript):
|
||||
@click.option('--backup', is_flag=True,
|
||||
help='Upload encrypted backup')
|
||||
def file_upload(filename, blksize, multisig, miniscript, backup):
|
||||
"""Send file to Coldcard (PSBT transaction or firmware)"""
|
||||
|
||||
if multisig and miniscript:
|
||||
click.echo("Failed: Only one can be specified from miniscript/multisig")
|
||||
if sum([multisig, miniscript, backup]) > 1:
|
||||
# only 1 or None can be True
|
||||
click.echo("Failed: Only one can be specified from miniscript/multisig/backup")
|
||||
sys.exit(1)
|
||||
|
||||
# NOTE: mostly for debug/dev usage.
|
||||
@ -298,6 +301,9 @@ def file_upload(filename, blksize, multisig, miniscript):
|
||||
dev.send_recv(CCProtocolPacker.multisig_enroll(file_len, sha))
|
||||
elif miniscript:
|
||||
dev.send_recv(CCProtocolPacker.miniscript_enroll(file_len, sha), timeout=None)
|
||||
elif backup:
|
||||
dev.send_recv(CCProtocolPacker.restore_backup(file_len, sha), timeout=None)
|
||||
|
||||
|
||||
|
||||
@main.command('upgrade')
|
||||
@ -614,6 +620,38 @@ def start_backup(outdir, outfile):
|
||||
click.echo("Wrote %d bytes into: %s\nSHA256: %s" % (len(result), fn, str(b2a_hex(chk), 'ascii')))
|
||||
|
||||
|
||||
@main.command('restore')
|
||||
@click.argument('filename', type=click.File('rb'), metavar="backup.7z")
|
||||
@click.option('-c', '--plaintext', is_flag=True,
|
||||
help="Force plaintext restore. No need to use if file has proper '.txt' suffix")
|
||||
@click.option('-p', '--password', is_flag=True,
|
||||
help="This backup has custom password. Not words.")
|
||||
@click.option('-t', '--tmp', is_flag=True,
|
||||
help="Force restoring backup as temporary seed. Only works for seedless Coldcard.")
|
||||
@display_errors
|
||||
def restore_backup(filename, plaintext, password, tmp):
|
||||
"""
|
||||
Uploads 7z encrypted backup file & starts backup restore process. User needs to specify
|
||||
what kind of backup is being uploaded. Default is 7z encrypted file with word-based password.
|
||||
Use -p/--password flag if your backup has custom not word-based password.
|
||||
User is prompted to enter backup password on the device.
|
||||
"""
|
||||
|
||||
if not plaintext and filename.name.lower().endswith(".txt"):
|
||||
plaintext = True
|
||||
|
||||
if plaintext and password:
|
||||
# only 1 or None can be True
|
||||
click.echo("Failed: Plaintext backup cannot have custom password.")
|
||||
sys.exit(1)
|
||||
|
||||
with get_device() as dev:
|
||||
file_len, sha = real_file_upload(filename, dev)
|
||||
dev.send_recv(
|
||||
CCProtocolPacker.restore_backup(file_len, sha, password, plaintext, tmp),
|
||||
timeout=None)
|
||||
|
||||
|
||||
@main.command('addr')
|
||||
@click.argument('path', default=None, metavar='[m/1/2/3]', required=False)
|
||||
@click.option('--segwit', '-s', is_flag=True, help='Show in segwit native (p2wpkh, bech32)')
|
||||
|
||||
@ -67,6 +67,26 @@ class CCProtocolPacker:
|
||||
# prompts user with password for encrypted backup
|
||||
return b'back'
|
||||
|
||||
@staticmethod
|
||||
def restore_backup(length, file_sha, custom_pwd=False, plaintext=False, tmp=False):
|
||||
# backup file has to be already uploaded
|
||||
# custom_pwd: (bool) .7z encrypted with custom password
|
||||
# plaintext: (bool) clear-text (dev)
|
||||
# tmp (bool) force load as tmp, effective only on seed-less CC
|
||||
assert len(file_sha) == 32
|
||||
assert not (custom_pwd and plaintext)
|
||||
|
||||
bf = 0
|
||||
if custom_pwd:
|
||||
bf |= 1
|
||||
if plaintext:
|
||||
bf |= 2
|
||||
if tmp:
|
||||
bf |= 4
|
||||
|
||||
return pack('<4sI32sB', b'rest', length, file_sha, bf)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def encrypt_start(device_pubkey, version=USB_NCRY_V1):
|
||||
supported_versions = [USB_NCRY_V1, USB_NCRY_V2]
|
||||
|
||||
Loading…
Reference in New Issue
Block a user