Esempio n. 1
0
async def dump_summary(*A):
    # save addresses, and some other public details into a file
    if not await ux_confirm('''\
Saves a text file to MicroSD with a summary of the *public* details \
of your wallet. For example, this gives the XPUB (extended public key) \
that you will need to import other wallet software to track balance.''' +
                            SENSITIVE_NOT_SECRET):
        return

    # pick a semi-random file name, save it.
    with imported('backups') as bk:
        await bk.make_summary_file()
Esempio n. 2
0
async def start_selftest(*args):
    import version

    if len(args) and not version.is_factory_mode():
        # called from inside menu, not directly
        if not await ux_confirm('''Selftest destroys settings on other profiles (not seeds). Requires MicroSD card and might have other consequences. Recommended only for factory.'''):
            return await ux_aborted()

    with imported('selftest') as st:
        await st.start_selftest()

    settings.save()
Esempio n. 3
0
    async def use_dice(self, *a):
        # Use lots of (D6) dice rolls to create privkey entropy.
        privkey = b''
        with imported('seed') as seed:
            count, privkey = await seed.add_dice_rolls(0, privkey, True)
            if count == 0: return

        if privkey >= SECP256K1_ORDER or privkey == bytes(32):
            # lottery won! but not going to waste bytes here preparing to celebrate
            return

        return await self.doit(have_key=privkey)
Esempio n. 4
0
async def electrum_skeleton(*A):
    # save xpub, and some other public details into a file
    if not await ux_confirm('''\
Saves a skeleton wallet file which Electrum can open, on to MicroSD. \
You can then open that file in Electrum without ever connecting the
Coldcard to a computer.''' + SENSITIVE_NOT_SECRET):
        return

    # TODO: pick segwit or classic derivation+such

    # pick a semi-random file name, save it.
    with imported('backups') as bk:
        await bk.make_electrum_wallet(is_segwit=False)
Esempio n. 5
0
async def verify_backup(*A):
    # check most recent backup is "good"
    # read 7z header, and measure checksums

    # save everything, using a password, into single encrypted file, typically on SD
    fn = await file_picker(
        'Select file containing the backup to be verified. No password will be required.',
        suffix='.7z',
        max_size=10000)

    if fn:
        with imported('backups') as bk:
            await bk.verify_backup_file(fn)
Esempio n. 6
0
async def restore_everything(*A):
    from main import pa

    if not pa.is_secret_blank():
        await ux_show_story(EMPTY_RESTORE_MSG)
        return

    # restore everything, using a password, from single encrypted 7z file
    fn = await file_picker('Select file containing the backup to be restored, and '
                            'then enter the password.', suffix='.7z', max_size=10000)

    if fn:
        with imported('backups') as bk:
            await bk.restore_complete(fn)
Esempio n. 7
0
async def verify_backup(*A):
    # check most recent backup is "good"
    # read 7z header, and measure checksums

    with imported('backups') as bk:

        fn = await file_picker(
            'Select file containing the backup to be verified. No password will be required.',
            suffix='.7z',
            max_size=bk.MAX_BACKUP_FILE_SIZE)

        if fn:
            # do a limited CRC-check over encrypted file
            await bk.verify_backup_file(fn)
Esempio n. 8
0
async def restore_complete(fname_or_fd):
    from ux import the_ux

    with imported('seed') as seed:

        async def done(words):
            # remove all pw-picking from menu stack
            seed.WordNestMenu.pop_all()

            await restore_complete_doit(fname_or_fd, words)

        # give them a menu to pick from
        m = seed.WordNestMenu(num_words=num_pw_words, has_checksum=False, done_cb=done)

    the_ux.push(m)
Esempio n. 9
0
async def wasabi_skeleton(*A):
    # save xpub, and some other public details into a file
    # - user has no choice, it's going to be bech32 with  m/84'/0'/0' path
    import chains

    ch = chains.current_chain()

    if await ux_show_story('''\
This saves a skeleton Wasabi wallet file onto the MicroSD card. \
You can then open that file in Wasabi without ever connecting this Coldcard to a computer.\
''' + SENSITIVE_NOT_SECRET) != 'y':
        return

    # no choices to be made, just do it.
    with imported('backups') as bk:
        await bk.make_json_wallet('Wasabi wallet', lambda: bk.generate_wasabi_wallet(), 'new-wasabi.json')
Esempio n. 10
0
async def bitcoin_core_skeleton(*A):
    # save output descriptors into a file
    # - user has no choice, it's going to be bech32 with  m/84'/{coin_type}'/0' path
    import chains

    ch = chains.current_chain()

    if await ux_show_story('''\
This saves a command onto the MicroSD card that includes the public keys.\
You can then run that command in Bitcoin Core without ever connecting this Coldcard to a computer.\
''' + SENSITIVE_NOT_SECRET) != 'y':
        return

    # no choices to be made, just do it.
    with imported('backups') as bk:
        await bk.make_bitcoin_core_wallet()
Esempio n. 11
0
async def restore_everything(*A):
    from main import pa

    if not pa.is_secret_blank():
        await ux_show_story('''\
You must clear the wallet seed before restoring a backup because it replaces \
the seed value and the old seed would be lost.\n\n\
Visit the advanced menu and choose 'Destroy Seed'.''')
        return

    # save everything, using a password, into single encrypted file, typically on SD
    fn = await file_picker('Select file containing the backup to be restored, and '
                            'then enter the password.', suffix='.7z', max_size=10000)

    if fn:
        with imported('backups') as bk:
            await bk.restore_complete(fn)
Esempio n. 12
0
async def restore_everything_cleartext(*A):
    # Asssume no password on backup file; devs and crazy people only
    from main import pa

    if not pa.is_secret_blank():
        await ux_show_story(EMPTY_RESTORE_MSG)
        return

    # restore everything, using NO password, from single text file, like would be wrapped in 7z
    fn = await file_picker('Select the cleartext file containing the backup to be restored.',
                             suffix='.txt', max_size=10000)

    if fn:
        with imported('backups') as bk:
            prob = await bk.restore_complete_doit(fn, [])
            if prob:
                await ux_show_story(prob, title='FAILED')
Esempio n. 13
0
    def render_qr(self, msg):
        # Version 2 would be nice, but can't hold what we need, even at min error correction,
        # so we are forced into version 3 = 29x29 pixels
        # - see <https://www.qrcode.com/en/about/version.html>
        # - to display 29x29 pixels, we have to double them up: 58x58
        # - not really providing enough space around it
        # - inverted QR (black/white swap) still readable by scanners, altho wrong

        from utils import imported

        with imported('uQR') as uqr:
            if self.is_alnum:
                # targeting 'alpha numeric' mode, typical len is 42
                ec = uqr.ERROR_CORRECT_Q
                assert len(msg) <= 47
            else:
                # has to be 'binary' mode, altho shorter msg, typical 34-36
                ec = uqr.ERROR_CORRECT_M
                assert len(msg) <= 42

            q = uqr.QRCode(version=3,
                           box_size=1,
                           border=0,
                           mask_pattern=3,
                           error_correction=ec)
            if self.is_alnum:
                here = uqr.QRData(msg.upper().encode('ascii'),
                                  mode=uqr.MODE_ALPHA_NUM,
                                  check_data=False)
            else:
                here = uqr.QRData(msg.encode('ascii'),
                                  mode=uqr.MODE_8BIT_BYTE,
                                  check_data=False)
            q.add_data(here)
            q.make(fit=False)

            self.qr_data = q.get_matrix()
Esempio n. 14
0
async def backup_everything(*A):
    # save everything, using a password, into single encrypted file, typically on SD
    with imported('backups') as bk:
        await bk.make_complete_backup()
Esempio n. 15
0
async def electrum_skeleton_step2(_1, _2, item):
    # pick a semi-random file name, render and save it.
    with imported('backups') as bk:
        addr_fmt = item.arg
        await bk.make_json_wallet(
            'Electrum wallet', lambda: bk.generate_electrum_wallet(addr_fmt))
Esempio n. 16
0
async def electrum_skeleton_step2(_1, _2, item):
    # pick a semi-random file name, render and save it.
    with imported('backups') as bk:
        await bk.make_electrum_wallet(addr_type=item.arg)
Esempio n. 17
0
    async def doit(self, *a, have_key=None):
        # make the wallet.
        from main import dis

        try:
            from chains import current_chain
            import tcc
            from serializations import hash160
            from stash import blank_object

            if not have_key:
                # get some random bytes
                await ux_dramatic_pause("Picking key...", 2)
                privkey = tcc.secp256k1.generate_secret()
            else:
                # caller must range check this already: 0 < privkey < order
                privkey = have_key

            # calculate corresponding public key value
            pubkey = tcc.secp256k1.publickey(privkey,
                                             True)  # always compressed style

            dis.fullscreen("Rendering...")

            # make payment address
            digest = hash160(pubkey)
            ch = current_chain()
            if self.is_segwit:
                addr = tcc.codecs.bech32_encode(ch.bech32_hrp, 0, digest)
            else:
                addr = tcc.codecs.b58_encode(ch.b58_addr + digest)

            wif = tcc.codecs.b58_encode(ch.b58_privkey + privkey + b'\x01')

            if self.can_do_qr():
                with imported('uqr') as uqr:
                    # make the QR's now, since it's slow
                    is_alnum = self.is_segwit
                    qr_addr = uqr.make(
                        addr if not is_alnum else addr.upper(),
                        min_version=4,
                        max_version=4,
                        encoding=(uqr.Mode_ALPHANUMERIC if is_alnum else 0))

                    qr_wif = uqr.make(wif,
                                      min_version=4,
                                      max_version=4,
                                      encoding=uqr.Mode_BYTE)
            else:
                qr_addr = None
                qr_wif = None

            # Use address as filename. clearly will be unique, but perhaps a bit
            # awkward to work with.
            basename = addr

            dis.fullscreen("Saving...")
            with CardSlot() as card:
                fname, nice_txt = card.pick_filename(
                    basename + ('-note.txt' if self.template_fn else '.txt'))

                with open(fname, 'wt') as fp:
                    self.make_txt(fp, addr, wif, privkey, qr_addr, qr_wif)

                if self.template_fn:
                    fname, nice_pdf = card.pick_filename(basename + '.pdf')

                    with open(fname, 'wb') as fp:
                        self.make_pdf(fp, addr, wif, qr_addr, qr_wif)
                else:
                    nice_pdf = ''

            # Half-hearted attempt to cleanup secrets-contaminated memory
            # - better would be force user to reboot
            # - and yet, we just output the WIF to SDCard anyway
            blank_object(privkey)
            blank_object(wif)
            del qr_wif

        except CardMissingError:
            await needs_microsd()
            return
        except Exception as e:
            await ux_show_story('Failed to write!\n\n\n' + str(e))
            return

        await ux_show_story('Done! Created file(s):\n\n%s\n\n%s' %
                            (nice_txt, nice_pdf))