Пример #1
0
def sign_transaction(psbt_len, flags=0x0, psbt_sha=None):
    # transaction (binary) loaded into sflash already, checksum checked
    UserAuthorizedAction.check_busy(ApproveTransaction)
    UserAuthorizedAction.active_request = ApproveTransaction(psbt_len, flags, psbt_sha=psbt_sha)

    # kill any menu stack, and put our thing at the top
    abort_and_goto(UserAuthorizedAction.active_request)
Пример #2
0
def start_show_p2sh_address(M, N, addr_format, xfp_paths, witdeem_script):
    # Show P2SH address to user, also returns it.
    # - first need to find appropriate multisig wallet associated
    # - they must provide full redeem script, and we will re-verify it and check pubkeys inside it

    import ustruct
    from multisig import MultisigWallet, MultisigOutOfSpace

    try:
        assert addr_format in SUPPORTED_ADDR_FORMATS
        assert addr_format & AFC_SCRIPT
    except:
        raise AssertionError('Unknown/unsupported addr format')

    # Search for matching multisig wallet that we must already know about
    xfps = [i[0] for i in xfp_paths]

    idx = MultisigWallet.find_match(M, N, xfps)
    assert idx >= 0, 'Multisig wallet with those fingerprints not found'

    ms = MultisigWallet.get_by_idx(idx)
    assert ms
    assert ms.M == M
    assert ms.N == N

    global active_request
    UserAuthorizedAction.check_busy(ShowAddressBase)

    active_request = ShowP2SHAddress(ms, addr_format, xfp_paths, witdeem_script)

    # kill any menu stack, and put our thing at the top
    abort_and_goto(active_request)

    # provide the value back to attached desktop
    return active_request.address
Пример #3
0
def maybe_enroll_xpub(sf_len=None, config=None, name=None, ux_reset=False):
    # Offer to import (enroll) a new multisig wallet. Allow reject by user.
    global active_request
    from multisig import MultisigWallet

    UserAuthorizedAction.cleanup()

    if sf_len:
        with SFFile(TXN_INPUT_OFFSET, length=sf_len) as fd:
            config = fd.read(sf_len).decode()

    # this call will raise on parsing errors, so let them rise up
    # and be shown on screen/over usb
    ms = MultisigWallet.from_file(config, name=name)

    active_request = NewEnrollRequest(ms)

    if ux_reset:
        # for USB case, and import from PSBT
        # kill any menu stack, and put our thing at the top
        abort_and_goto(active_request)
    else:
        # menu item case: add to stack
        from ux import the_ux
        the_ux.push(active_request)
Пример #4
0
def start_bip39_passphrase(pw):
    # new passphrase has come in via USB. offer to switch to it.

    UserAuthorizedAction.cleanup()
    UserAuthorizedAction.active_request = NewPassphrase(pw)

    # kill any menu stack, and put our thing at the top
    abort_and_goto(UserAuthorizedAction.active_request)
Пример #5
0
def sign_transaction(psbt_len, do_finalize=False):
    # transaction (binary) loaded into sflash already, checksum checked
    global active_request
    UserAuthorizedAction.check_busy(ApproveTransaction)
    active_request = ApproveTransaction(psbt_len, do_finalize)

    # kill any menu stack, and put our thing at the top
    abort_and_goto(active_request)
Пример #6
0
def start_remote_backup():
    # tell the local user the secret words, and then save to SPI flash
    # USB caller has to come back and download encrypted contents.

    UserAuthorizedAction.cleanup()
    UserAuthorizedAction.active_request = RemoteBackup()

    # kill any menu stack, and put our thing at the top
    abort_and_goto(UserAuthorizedAction.active_request)
Пример #7
0
def authorize_upgrade(hdr, length):
    # final USB write has come in, get buy-in

    # Do some verification before we even show to the local user
    UserAuthorizedAction.check_busy()
    UserAuthorizedAction.active_request = FirmwareUpgradeRequest(hdr, length)

    # kill any menu stack, and put our thing at the top
    abort_and_goto(UserAuthorizedAction.active_request)
Пример #8
0
def start_bip39_passphrase(pw):
    # tell the local user the secret words, and then save to SPI flash
    # USB caller has to come back and download encrypted contents.
    global active_request

    UserAuthorizedAction.cleanup()

    active_request = NewPassphrase(pw)

    # kill any menu stack, and put our thing at the top
    abort_and_goto(active_request)
Пример #9
0
async def sign_psbt_buf_OLD(psbt_buf):
    # sign a PSBT string
    from common import dis
    from sram4 import tmp_buf
    from utils import HexStreamer, Base64Streamer, HexWriter, Base64Writer

    UserAuthorizedAction.cleanup()

    # Determine encoding used
    psbt_len = len(psbt_buf)
    taste = psbt_buf[0:10]
    print('sign_psbt_buf: 1')
    if taste[0:5] == b'psbt\xff':
        print('sign_psbt_buf: 2')
        print("sign 1")
        decoder = None
        def output_encoder(x): return x
    elif taste[0:10] == b'70736274ff':
        print("sign 2")
        decoder = HexStreamer()
        output_encoder = HexWriter
        psbt_len //= 2
    elif taste[0:6] == b'cHNidP':
        print("sign 3")
        decoder = Base64Streamer()
        output_encoder = Base64Writer
        psbt_len = (psbt_len * 3 // 4) + 10

    print('sign_psbt_buf: 3')

    async def done(psbt):
        if psbt.is_complete():
            psbt.finalize(fd)
        else:
            psbt.serialize(fd)

        ch = await ux_show_signed_transaction()

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

        UserAuthorizedAction.cleanup()

    print('sign_psbt_buf: 4')
    UserAuthorizedAction.active_request = ApproveTransaction(
        psbt_len, approved_cb=done, psbt_buf=psbt_buf)
    print('sign_psbt_buf: 5')

    # Kill any menu stack, and put our thing at the top
    abort_and_goto(UserAuthorizedAction.active_request)
    print('sign_psbt_buf: 6')
Пример #10
0
def start_show_address(addr_format, subpath):
    try:
        assert addr_format in SUPPORTED_ADDR_FORMATS
        assert not (addr_format & AFC_SCRIPT)
    except:
        raise AssertionError('Unknown/unsupported addr format')

    # require a path to a key
    subpath = cleanup_deriv_path(subpath)

    # serAuthorizedAction.check_busy(ShowAddressBase)
    UserAuthorizedAction.active_request = ShowPKHAddress(addr_format, subpath)

    # kill any menu stack, and put our thing at the top
    abort_and_goto(UserAuthorizedAction.active_request)

    # provide the value back to attached desktop
    return UserAuthorizedAction.active_request.address
Пример #11
0
def start_show_address(subpath, addr_format):
    # Show address to user, also returns it.

    try:
        subpath = str(subpath, 'ascii')
    except UnicodeError:
        raise AssertionError('must be ascii')

    try:
        assert addr_format in SUPPORTED_ADDR_FORMATS
    except:
        raise AssertionError('Unknown/unsupported addr format')

    global active_request
    UserAuthorizedAction.check_busy(ShowAddress)
    active_request = ShowAddress(subpath, addr_format)

    # kill any menu stack, and put our thing at the top
    abort_and_goto(active_request)

    # provide the value back to attached desktop too!
    return active_request.address
Пример #12
0
def sign_msg(text, subpath, addr_fmt):
    # Convert to strings
    try:
        text = str(text,'ascii')
        subpath = str(subpath, 'ascii')
    except UnicodeError:
        raise AssertionError('must be ascii')

    try:
        assert addr_fmt in SUPPORTED_ADDR_FORMATS
        assert not (addr_fmt & AFC_SCRIPT)
    except:
        raise AssertionError('Unknown/unsupported addr format')

    # Do some verification before we even show to the local user
    ApproveMessageSign.validate(text)

    global active_request
    UserAuthorizedAction.check_busy()
    active_request = ApproveMessageSign(text, subpath, addr_fmt)

    # kill any menu stack, and put our thing at the top
    abort_and_goto(active_request)
Пример #13
0
def start_show_address(addr_format, subpath):
    try:
        assert addr_format in SUPPORTED_ADDR_FORMATS
        assert not (addr_format & AFC_SCRIPT)
    except:
        raise AssertionError('Unknown/unsupported addr format')

    # text path expected
    try:
        subpath = str(subpath, 'ascii')
        ms = None
        subpaths = None
    except UnicodeError:
        raise AssertionError('must be ascii')

    global active_request
    UserAuthorizedAction.check_busy(ShowAddressBase)
    active_request = ShowPKHAddress(addr_format, subpath)

    # kill any menu stack, and put our thing at the top
    abort_and_goto(active_request)

    # provide the value back to attached desktop
    return active_request.address
Пример #14
0
def sign_psbt_file(filename):
    # sign a PSBT file found on a MicroSD card
    from files import CardSlot, CardMissingError
    from main import dis
    from sram2 import tmp_buf
    global active_request

    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
    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)

            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):
                        out.write(tmp_buf)
                    else:
                        out.write(memoryview(tmp_buf)[0:n])

                    total += n
                    dis.progress_bar_show(total / psbt_len)

            assert total == psbt_len, repr([total, psbt_len])

    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 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':
                await ux_aborted()
                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()

    active_request = ApproveTransaction(psbt_len, approved_cb=done)

    # kill any menu stack, and put our thing at the top
    abort_and_goto(active_request)
Пример #15
0
async def start_hsm_approval(sf_len=0, usb_mode=False, startup_mode=False):
    # Show details of the proposed HSM policy (or saved one)
    # If approved, go into HSM mode and never come back to normal.

    UserAuthorizedAction.cleanup()

    is_new = True

    if sf_len:
        with SFFile(0, length=sf_len) as fd:
            json = fd.read(sf_len).decode()
    else:
        try:
            json = open(POLICY_FNAME, 'rt').read()
        except:
            raise ValueError("No existing policy")

        is_new = False

    # parse as JSON
    cant_fail = False
    try:
        try:
            js_policy = ujson.loads(json)
        except:
            raise ValueError("JSON parse fail")

        cant_fail = bool(js_policy.get('boot_to_hsm', False))

        # parse the policy
        policy = HSMPolicy()
        policy.load(js_policy)
    except BaseException as exc:
        err = "HSM Policy invalid: %s: %s" % (problem_file_line(exc), str(exc))
        if usb_mode:
            raise ValueError(err)

        # What to do in a menu case? Shouldn't happen anyway, but
        # maybe they upgraded the firmware, and so old policy file
        # isn't suitable anymore.
        # - or maybe the settings have been f-ed with.
        print(err)

        if startup_mode and cant_fail:
            # die as a brick here, not safe to proceed w/o HSM active
            import callgate, ux
            ux.show_fatal_error(err.replace(': ', ':\n '))
            callgate.show_logout(1)     # die w/ it visible
            # not reached

        await ux_show_story("Cannot start HSM.\n\n%s" % err)
        return

    # Boot-to-HSM feature: don't ask, just start policy immediately
    if startup_mode and policy.boot_to_hsm:
        msg = uio.StringIO()
        policy.explain(msg)
        policy.activate(False)
        the_ux.reset(hsm_ux_obj)
        return None
        

    ar = ApproveHSMPolicy(policy, is_new)
    UserAuthorizedAction.active_request = ar

    if startup_mode:
        return ar

    if usb_mode:
        # for USB case, kill any menu stack, and put our thing at the top
        abort_and_goto(UserAuthorizedAction.active_request)
    else:
        # menu item case: add to stack, so we can still back out
        from ux import the_ux
        the_ux.push(UserAuthorizedAction.active_request)

    return ar
Пример #16
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')
Пример #17
0
def sign_psbt_file(filename):
    # sign a PSBT file found on a MicroSD card
    from files import CardSlot, CardMissingError, securely_blank_file
    from main import dis
    from sram2 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
                output_encoder = lambda x: 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
        txid = None

        from main import settings
        import os
        del_after = settings.get('del', 0)

        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:
                        if is_comp and del_after:
                            # don't write signed PSBT if we'd just delete it anyway
                            out_fn = None
                        else:
                            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' if not del_after else 'tmp.txn', out_path)

                            if out2_full:
                                with HexWriter(open(out2_full, 'w+t')) as fd:
                                    # save transaction, in hex
                                    txid = psbt.finalize(fd)

                                if del_after:
                                    # rename it now that we know the txid
                                    after_full, out2_fn = card.pick_filename(
                                                            txid+'.txn', out_path, overwrite=True)
                                    os.rename(out2_full, after_full)

                    if del_after:
                        # this can do nothing if they swapped SDCard between steps, which is ok,
                        # but if the original file is still there, this blows it away.
                        # - if not yet final, the foo-part.psbt file stays
                        try:
                            securely_blank_file(filename)
                        except: pass

                    # 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':
                await ux_aborted()
                return

        # done.
        if out_fn:
            msg = "Updated PSBT is:\n\n%s" % out_fn
            if out2_fn:
                msg += '\n\n'
        else:
            # del_after is probably set
            msg = ''

        if out2_fn:
            msg += 'Finalized transaction (ready for broadcast):\n\n%s' % out2_fn
            if txid and not del_after:
                msg += '\n\nFinal TXID:\n'+txid

        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)