def make_msg(start):
        msg = ''
        if start == 0:
            msg = "Press 1 to save to MicroSD."
            msg += '\n\n'
        msg += "Addresses %d..%d:\n\n" % (start, start + n - 1)

        addrs = []
        chain = chains.current_chain()

        dis.fullscreen('Loading...')

        with stash.SensitiveValues() as sv:

            for idx in range(start, start + n):
                subpath = path.format(account=0, change=0, idx=idx)
                node = sv.derive_path(subpath, register=False)
                addr = chain.address(node, addr_fmt)
                addr1 = addr[:16]
                addr2 = addr[16:]
                addrs.append(addr)

                msg += "%s =>\n  %s\n  %s\n\n" % (subpath, addr1, addr2)

                dis.progress_bar_show(idx / n)

            stash.blank_object(node)

        msg += "Press 9 to see next group.\nPress 7 to see prev. group."

        return msg, addrs
Esempio n. 2
0
async def write_text_file(fname_pattern, body, title, total_parts=72):
    # - total_parts does need not be precise
    from common import dis, pa, settings
    from files import CardSlot, CardMissingError
    from actions import needs_microsd

    # choose a filename
    try:
        with CardSlot() as card:
            fname, nice = card.pick_filename(fname_pattern)

            # do actual write
            with open(fname, 'wb') as fd:
                for idx, part in enumerate(body):
                    dis.progress_bar_show(idx / total_parts)
                    fd.write(part.encode())

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

    msg = '''%s file written:\n\n%s''' % (title, nice)
    await ux_show_story(msg)
async def choose_first_address(*a):
    # Choose from a truncated list of index 0 common addresses, remember
    # the last address the user selected and use it as the default
    from common import settings, dis
    chain = chains.current_chain()

    dis.fullscreen('Loading...')

    with stash.SensitiveValues() as sv:

        def truncate_address(addr):
            # Truncates address to width of screen, replacing middle chars
            middle = "-"
            leftover = SCREEN_CHAR_WIDTH - len(middle)
            start = addr[0:(leftover + 1) // 2]
            end = addr[len(addr) - (leftover // 2):]
            return start + middle + end

        # Create list of choices (address_index_0, path, addr_fmt)
        choices = []
        for name, path, addr_fmt in chains.CommonDerivations:
            if '{coin_type}' in path:
                path = path.replace('{coin_type}', str(chain.b44_cointype))
            subpath = path.format(account=0, change=0, idx=0)
            node = sv.derive_path(subpath, register=False)
            address = chain.address(node, addr_fmt)
            choices.append((truncate_address(address), path, addr_fmt))

            dis.progress_bar_show(len(choices) / len(chains.CommonDerivations))

        stash.blank_object(node)

    picked = None

    async def clicked(_1, _2, item):
        if picked is None:
            picked = item.arg
        the_ux.pop()

    items = [
        MenuItem(address, f=clicked, arg=i)
        for i, (address, path, addr_fmt) in enumerate(choices)
    ]
    menu = MenuSystem(items, title='Address List')
    menu.goto_idx(settings.get('axi', 0))
    the_ux.push(menu)

    await menu.interact()

    if picked is None:
        return None

    # update last clicked address
    settings.set('axi', picked)
    address, path, addr_fmt = choices[picked]

    return (path, addr_fmt)
Esempio n. 4
0
    def __init__(self, text, subpath, addr_fmt, approved_cb=None):
        super().__init__()
        self.text = text
        self.subpath = subpath
        self.approved_cb = approved_cb

        from common import dis
        dis.fullscreen('Wait...')

        with stash.SensitiveValues() as sv:
            node = sv.derive_path(subpath)
            self.address = sv.chain.address(node, addr_fmt)

        dis.progress_bar_show(1)
Esempio n. 5
0
    def wipe_most(self):
        # erase everything except settings: takes 5 seconds at least
        from nvstore import SLOTS
        end = SLOTS[0]

        from common import dis
        dis.fullscreen("Cleanup...")

        for addr in range(0, end, self.BLOCK_SIZE):
            self.block_erase(addr)
            dis.progress_bar_show(addr / end)

            while self.is_busy():
                pass
Esempio n. 6
0
    async def erase(self):
        # must be used by caller before writing any bytes
        assert not self.readonly
        assert self.length == 0  # 'already wrote?'

        for i in range(0, self.max_size, blksize):
            self.sf.block_erase(self.start + i)

            if i and self.message:
                from common import dis
                dis.progress_bar_show(i / self.max_size)

            # expect block erase to take up to 2 seconds
            while self.sf.is_busy():
                await sleep_ms(50)
Esempio n. 7
0
def wipe_microsd_card():
    # Erase and re-format SD card. Not secure erase, because that is too slow.
    import callgate
    import pyb
    from common import dis

    try:
        os.umount('/sd')
    except:
        pass

    sd = pyb.SDCard()
    assert sd

    if not sd.present():
        return

    # power cycle so card details (like size) are re-read from current card
    sd.power(0)
    sd.power(1)

    dis.fullscreen('Part Erase...')
    cutoff = 1024  # arbitrary
    blk = bytearray(512)

    for bnum in range(cutoff):
        callgate.fill_random(blk)
        sd.writeblocks(bnum, blk)
        dis.progress_bar_show(bnum / cutoff)

    dis.fullscreen('Formating...')

    # remount, with newfs option
    os.mount(sd, '/sd', readonly=0, mkfs=1)

    # done, cleanup
    os.umount('/sd')

    # important: turn off power
    sd = pyb.SDCard()
    sd.power(0)
async def make_address_summary_file(path,
                                    addr_fmt,
                                    fname_pattern='addresses.txt'):
    # write addresses into a text file on the microSD
    from common import dis
    from files import CardSlot, CardMissingError
    from actions import needs_microsd

    # simple: always set number of addresses.
    # - takes 60 seconds, to write 250 addresses on actual hardware
    count = 250

    dis.fullscreen('Saving 0-%d' % count)

    # generator function
    body = generate_address_csv(path, addr_fmt, count)

    # pick filename and write
    try:
        with CardSlot() as card:
            fname, nice = card.pick_filename(fname_pattern)

            # do actual write
            with open(fname, 'wb') as fd:
                for idx, part in enumerate(body):
                    fd.write(part.encode())

                    if idx % 5 == 0:
                        dis.progress_bar_show(idx / count)

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

    msg = '''Address summary file written:\n\n%s''' % nice
    await ux_show_story(msg)
Esempio n. 9
0
    def read(self, ll=None):
        if ll == 0:
            return b''
        elif ll is None:
            ll = self.length - self.pos
        else:
            ll = min(ll, self.length - self.pos)

        if ll <= 0:
            # at EOF
            return b''

        rv = bytearray(ll)
        self.sf.read(self.start + self.pos, rv)

        self.pos += ll

        if self.message and ll > 1:
            from common import dis
            dis.progress_bar_show(self.pos / self.length)

        # altho tempting to return a bytearray (which we already have) many
        # callers expect return to be bytes and have those methods, like "find"
        return bytes(rv)
Esempio n. 10
0
def sign_message_digest(digest, subpath, prompt):
    # do the signature itself!
    from common import dis

    if prompt:
        dis.fullscreen(prompt, percent=.25)

    with stash.SensitiveValues() as sv:
        dis.progress_bar_show(.50)

        node = sv.derive_path(subpath)
        pk = node.private_key()
        sv.register(pk)

        dis.progress_bar_show(.75)
        rv = trezorcrypto.secp256k1.sign(pk, digest)

    dis.progress_bar_show(1)

    return rv
Esempio n. 11
0
async def restore_from_dict(vals):
    # Restore from a dict of values. Already JSON decoded.
    # Reboot on success, return string on failure
    from common import pa, dis, settings
    from pincodes import SE_SECRET_LEN

    #print("Restoring from: %r" % vals)

    # step1: the private key
    # - prefer raw_secret over other values
    # - TODO: fail back to other values
    try:
        chain = chains.get_chain(vals.get('chain', 'BTC'))

        assert 'raw_secret' in vals
        raw = bytearray(SE_SECRET_LEN)
        rs = vals.pop('raw_secret')
        if len(rs) % 2:
            rs += '0'
        x = a2b_hex(rs)
        raw[0:len(x)] = x

        # check we can decode this right (might be different firmare)
        opmode, bits, node = stash.SecretStash.decode(raw)
        assert node

        # verify against xprv value (if we have it)
        if 'xprv' in vals:
            check_xprv = chain.serialize_private(node)
            assert check_xprv == vals['xprv'], 'xprv mismatch'

    except Exception as e:
        return ('Unable to decode raw_secret and '
                'restore the seed value!\n\n\n' + str(e))

    ls = None
    if 'long_secret' in vals:
        try:
            ls = a2b_hex(vals.pop('long_secret'))
        except Exception as exc:
            sys.print_exception(exc)
            # but keep going.

    dis.fullscreen("Saving...")
    dis.progress_bar_show(.25)

    # clear (in-memory) settings and change also nvram key
    # - also captures xfp, xpub at this point
    pa.change(new_secret=raw)

    # force the right chain
    pa.new_main_secret(raw, chain)  # updates xfp/xpub

    # NOTE: don't fail after this point... they can muddle thru w/ just right seed

    if ls is not None:
        try:
            pa.ls_change(ls)
        except Exception as exc:
            sys.print_exception(exc)
            # but keep going

    # restore settings from backup file

    for idx, k in enumerate(vals):
        dis.progress_bar_show(idx / len(vals))
        if not k.startswith('setting.'):
            continue

        if k == 'xfp' or k == 'xpub':
            continue

        settings.set(k[8:], vals[k])

    # write out
    settings.save()

    if ('hsm_policy' in vals):
        import hsm
        hsm.restore_backup(vals['hsm_policy'])

    await ux_show_story(
        'Everything has been successfully restored. '
        'We must now reboot to install the '
        'updated settings and/or seed.',
        title='Success!')

    from machine import reset
    reset()
Esempio n. 12
0
def sign_psbt_buf(psbt_buf):
    # sign a PSBT file found on a microSD card
    from uio import BytesIO
    from common import dis
    from sram4 import tmp_buf
    from utils import HexStreamer, Base64Streamer, HexWriter, Base64Writer

    UserAuthorizedAction.cleanup()

    # copy buffer into SPI Flash
    # - accepts hex or base64 encoding, but binary prefered
    with BytesIO(psbt_buf) as fd:
        dis.fullscreen('Reading...')

        # see how long it is
        psbt_len = fd.seek(0, 2)
        fd.seek(0)

        # determine encoding used, altho we prefer binary
        taste = fd.read(10)
        fd.seek(0)

        if taste[0:5] == b'psbt\xff':
            print('tastes like text PSBT')
            decoder = None
            def output_encoder(x): return x
        elif taste[0:10] == b'70736274ff':
            print('tastes like binary PSBT')
            decoder = HexStreamer()
            output_encoder = HexWriter
            psbt_len //= 2
        elif taste[0:6] == b'cHNidP':
            print('tastes like Base64 PSBT')
            decoder = Base64Streamer()
            output_encoder = Base64Writer
            psbt_len = (psbt_len * 3 // 4) + 10
        else:
            return

        total = 0
        with SFFile(TXN_INPUT_OFFSET, max_size=psbt_len) as out:
            print('sign 1')
            # blank flash
            await out.erase()
            print('sign 2')

            while 1:
                n = fd.readinto(tmp_buf)
                print('sign copy to SPI flash 1: n={}'.format(n))
                if not n:
                    break

                if n == len(tmp_buf):
                    abuf = tmp_buf
                else:
                    abuf = memoryview(tmp_buf)[0:n]

                if not decoder:
                    out.write(abuf)
                    total += n
                else:
                    for here in decoder.more(abuf):
                        out.write(here)
                        total += len(here)

                print('sign copy to SPI flash 2: {}/{} = {}'.format(total, psbt_len, total/psbt_len))
                dis.progress_bar_show(total / psbt_len)

            print('sign 3')

        # might have been whitespace inflating initial estimate of PSBT size
        assert total <= psbt_len
        psbt_len = total
        print('sign 4')

    # Create a new BytesIO() to hold the result
    async def done(psbt):
        print('sign 5: done')
        signed_bytes = None
        with BytesIO() as bfd:
            with output_encoder(bfd) as fd:
                print('sign 6: done')
                if psbt.is_complete():
                    print('sign 7: done')
                    psbt.finalize(fd)
                    print('sign 8: done')
                else:
                    print('sign 9: done')
                    psbt.serialize(fd)
                    print('sign 10: done')

                bfd.seek(0)
                signed_bytes = bfd.read()
                print('signed_bytes={}'.format(signed_bytes))

        print('sign 11: done')

        gc.collect()

        from ur1.encode_ur import encode_ur
        from ubinascii import hexlify
        signed_str = hexlify(signed_bytes)
        print('signed_str={}'.format(signed_str))

        from ux import DisplayURCode
        o = DisplayURCode('Signed Txn', 'Scan to Wallet', signed_str)
        await o.interact_bare()

        UserAuthorizedAction.cleanup()

    print('sign 12: done')
    UserAuthorizedAction.active_request = ApproveTransaction(psbt_len, approved_cb=done)
    print('sign 13: done')

    # kill any menu stack, and put our thing at the top
    abort_and_goto(UserAuthorizedAction.active_request)
    print('sign 14: done')
Esempio n. 13
0
def sign_psbt_file(filename):
    # sign a PSBT file found on a microSD card
    from files import CardSlot, CardMissingError
    from common import dis
    from sram4 import tmp_buf
    from utils import HexStreamer, Base64Streamer, HexWriter, Base64Writer

    UserAuthorizedAction.cleanup()

    #print("sign: %s" % filename)

    # copy file into our spiflash
    # - can't work in-place on the card because we want to support writing out to different card
    # - accepts hex or base64 encoding, but binary prefered
    with CardSlot() as card:
        with open(filename, 'rb') as fd:
            dis.fullscreen('Reading...')

            # see how long it is
            psbt_len = fd.seek(0, 2)
            fd.seek(0)

            # determine encoding used, altho we prefer binary
            taste = fd.read(10)
            fd.seek(0)

            if taste[0:5] == b'psbt\xff':
                decoder = None
                def output_encoder(x): return x
            elif taste[0:10] == b'70736274ff':
                decoder = HexStreamer()
                output_encoder = HexWriter
                psbt_len //= 2
            elif taste[0:6] == b'cHNidP':
                decoder = Base64Streamer()
                output_encoder = Base64Writer
                psbt_len = (psbt_len * 3 // 4) + 10

            total = 0
            with SFFile(TXN_INPUT_OFFSET, max_size=psbt_len) as out:
                # blank flash
                await out.erase()

                while 1:
                    n = fd.readinto(tmp_buf)
                    if not n:
                        break

                    if n == len(tmp_buf):
                        abuf = tmp_buf
                    else:
                        abuf = memoryview(tmp_buf)[0:n]

                    if not decoder:
                        out.write(abuf)
                        total += n
                    else:
                        for here in decoder.more(abuf):
                            out.write(here)
                            total += len(here)

                    dis.progress_bar_show(total / psbt_len)

            # might have been whitespace inflating initial estimate of PSBT size
            assert total <= psbt_len
            psbt_len = total

    async def done(psbt):
        orig_path, basename = filename.rsplit('/', 1)
        orig_path += '/'
        base = basename.rsplit('.', 1)[0]
        out2_fn = None
        out_fn = None

        while 1:
            # try to put back into same spot, but also do top-of-card
            is_comp = psbt.is_complete()
            if not is_comp:
                # keep the filename under control during multiple passes
                target_fname = base.replace('-part', '')+'-part.psbt'
            else:
                # add -signed to end. We won't offer to sign again.
                target_fname = base+'-signed.psbt'

            for path in [orig_path, None]:
                try:
                    with CardSlot() as card:
                        out_full, out_fn = card.pick_filename(
                            target_fname, path)
                        out_path = path
                        if out_full:
                            break
                except CardMissingError:
                    prob = 'Missing card.\n\n'
                    out_fn = None

            if not out_fn:
                # need them to insert a card
                prob = ''
            else:
                # attempt write-out
                try:
                    with CardSlot() as card:
                        with output_encoder(open(out_full, 'wb')) as fd:
                            # save as updated PSBT
                            psbt.serialize(fd)

                        if is_comp:
                            # write out as hex too, if it's final
                            out2_full, out2_fn = card.pick_filename(
                                base+'-final.txn', out_path)
                            if out2_full:
                                with HexWriter(open(out2_full, 'wt')) as fd:
                                    # save transaction, in hex
                                    psbt.finalize(fd)

                    # success and done!
                    break

                except OSError as exc:
                    prob = 'Failed to write!\n\n%s\n\n' % exc
                    sys.print_exception(exc)
                    # fall thru to try again

            # prompt them to input another card?
            ch = await ux_show_story(prob+"Please insert an SDCard to receive signed transaction, "
                                     "and press OK.", title="Need Card")
            if ch == 'x':
                return

        # done.
        msg = "Updated PSBT is:\n\n%s" % out_fn
        if out2_fn:
            msg += '\n\nFinalized transaction (ready for broadcast):\n\n%s' % out2_fn

        await ux_show_story(msg, title='PSBT Signed')

        UserAuthorizedAction.cleanup()

    UserAuthorizedAction.active_request = ApproveTransaction(
        psbt_len, approved_cb=done)

    # kill any menu stack, and put our thing at the top
    abort_and_goto(UserAuthorizedAction.active_request)
Esempio n. 14
0
    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.message:
            from common import dis
            dis.progress_bar_show(1)

        return False