Exemple #1
0
async def startup():
    print("startup()")

    from actions import accept_terms, validate_passport_hw
    await accept_terms()

    # We will come here again if the device is shutdown, but the validation
    # words have not been confirmed (assuming the terms were accepted).
    await validate_passport_hw()

    # Setup first PIN if it's blank
    from common import pa
    from actions import initial_pin_setup
    if pa.is_blank():
        await initial_pin_setup()

    # Prompt for PIN and then pick appropriate top-level menu,
    # based on contents of secure chip (ie. is there
    # a wallet defined)
    from actions import start_login_sequence
    await start_login_sequence()

    # from actions import test_normal_menu
    # await test_normal_menu()
    from actions import goto_top_menu
    goto_top_menu()

    from common import loop
    loop.create_task(main())
Exemple #2
0
    async def done_apply(self, *a):
        # apply the passphrase.
        # - important to work on empty string here too.
        from stash import bip39_passphrase
        old_pw = str(bip39_passphrase)

        err = set_bip39_passphrase(pp_sofar)
        if err:
            # kinda very late: but if not BIP39 based key, ends up here.
            return await ux_show_story(err, title="Fail")

        from main import settings
        xfp = settings.get('xfp')

        ch = await ux_show_story(
            '''Above is the master key fingerprint of the new wallet.

Press X to abort and keep editing passphrase. OK to use the new wallet.''',
            title="[%s]" % xfp2str(xfp))
        if ch == 'x':
            # go back!
            set_bip39_passphrase(old_pw)
            return

        goto_top_menu()
Exemple #3
0
    async def done_apply(self, *a):
        # apply the passphrase.
        # - important to work on empty string here too.
        from stash import bip39_passphrase
        old_pw = str(bip39_passphrase)

        set_bip39_passphrase(pp_sofar)

        from main import settings
        xfp = settings.get('xfp')

        msg = '''Above is the master key fingerprint of the new wallet.

Press X to abort and keep editing passphrase, OK to use the new wallet and 1 to save to MicroSD'''

        ch = await ux_show_story(msg, title="[%s]" % xfp2str(xfp), escape='1')
        if ch == 'x':
            # go back!
            set_bip39_passphrase(old_pw)
            return

        if ch == '1':
            await PassphraseSaver().append(xfp, pp_sofar)

        goto_top_menu()
Exemple #4
0
    async def done_cancel(self, *a):
        global pp_sofar

        if len(pp_sofar) > 3:
            if not await ux_confirm("What you have entered will be forgotten."):
                return

        goto_top_menu()
Exemple #5
0
    async def all_done(new_words):
        # save the new seed value
        set_seed_value(new_words)
        
        # clear menu stack
        goto_top_menu()

        return None
Exemple #6
0
async def approve_word_list(seed):
    # Force the user to write the seeds words down, give a quiz, then save them.

    # LESSON LEARNED: if the user is writting down the words, as we have
    # vividly instructed, then it's a big deal to lose those words and have to start
    # over. So confirm that action, and don't volunteer it.

    words = tcc.bip39.from_data(seed).split(' ')
    assert len(words) == 24

    while 1:
        # show the seed words
        ch = await show_words(words, escape='46',
                        extra='\n\nPress 4 to add some dice rolls into the mix.')

        if ch == 'x': 
            # user abort, but confirm it!
            if await ux_confirm("Throw away those words and stop this process?"):
                return
            else:
                continue

        if ch == '4':
            # dice roll mode
            count, new_seed = await add_dice_rolls(0, seed, False)
            if count:
                seed = new_seed
                words = tcc.bip39.from_data(seed).split(' ')

            continue

        if ch == '6':
            # wants to skip the quiz (undocumented)
            if await ux_confirm("Skipping the quiz means you might have "
                                        "recorded the seed wrong and will be crying later."):
                break

        # Perform a test, to check they wrote them down
        ch = await word_quiz(words)
        if ch == 'x':
            # user abort quiz
            if await ux_confirm("Throw away those words and stop this process? Press X to see the word list again and restart the quiz."):
                return

            # show the words again, but don't change them
            continue

        # quiz passed
        break

    # Done!
    set_seed_value(words)

    # send them to home menu, now with a wallet enabled
    goto_top_menu()
Exemple #7
0
    async def all_done(new_words):
        # So we have another part, might be done or not.
        global import_xor_parts
        assert len(new_words) == 24
        import_xor_parts.append(new_words)

        XORWordNestMenu.pop_all()

        num_parts = len(import_xor_parts)
        seed = xor32(*(bip39.a2b_words(w) for w in import_xor_parts))

        msg = "You've entered %d parts so far.\n\n" % num_parts
        if num_parts >= 2:
            chk_word = bip39.b2a_words(seed).split(' ')[-1]
            msg += "If you stop now, the 24th word of the XOR-combined seed phrase\nwill be:\n\n"
            msg += "24: %s\n\n" % chk_word

        if all((not x) for x in seed):
            # zero seeds are never right.
            msg += "ZERO WARNING\nProvided seed works out to all zeros "\
                    "right now. You may have doubled a part or made some other mistake.\n\n"

        msg += "Press (1) to enter next list of words, or (2) if done with all words."

        ch = await ux_show_story(msg,
                                 strict_escape=True,
                                 escape='12x',
                                 sensitive=True)

        if ch == 'x':
            # give up
            import_xor_parts.clear()  # concern: we are contaminated w/ secrets
            return None
        elif ch == '1':
            # do another list of words
            nxt = XORWordNestMenu(num_words=24)
            the_ux.push(nxt)
        elif ch == '2':
            # done; import on temp basis, or be the main secret
            from pincodes import pa
            enc = stash.SecretStash.encode(seed_phrase=seed)

            if pa.is_secret_blank():
                # save it since they have no other secret
                set_seed_value(encoded=enc)

                # update menu contents now that wallet defined
                goto_top_menu()
            else:
                pa.tmp_secret(enc)
                await ux_show_story(
                    "New master key in effect until next power down.")

        return None
Exemple #8
0
    def pop_menu(self):
        # drop them back into menu system, but try not to affect
        # menu position.
        self.ux_done = True

        from actions import goto_top_menu
        from ux import the_ux, restore_menu
        if the_ux.top_of_stack() == self:
            empty = the_ux.pop()
            if empty:
                goto_top_menu()

        restore_menu()
Exemple #9
0
        async def doit(menu, idx, item):
            # apply the password immediately and drop them at top menu
            set_bip39_passphrase(data[idx]['pw'])

            from nvstore import settings
            from utils import xfp2str
            xfp = settings.get('xfp')

            # verification step; I don't see any way for this to go wrong
            assert xfp == data[idx]['xfp']

            # feedback that it worked
            await ux_show_story("Passphrase restored.", title="[%s]" % xfp2str(xfp))

            goto_top_menu()
Exemple #10
0
async def mainline():
    # Mainline of program, after startup
    #
    # - Do not add to this function, its vars are
    #   in memory forever; instead, extend more_setup above.
    from actions import goto_top_menu
    from ux import the_ux

    goto_top_menu()

    gc.collect()
    #print("Free mem: %d" % gc.mem_free())

    while 1:
        await the_ux.interact()
Exemple #11
0
 def done(self, redraw=True):
     # drop them back into menu system, but at top.
     self.ux_done = True
     from actions import goto_top_menu
     m = goto_top_menu()
     if redraw:
         m.show()
Exemple #12
0
    async def interact(self):
        import main
        from main import numpad, is_devmode
        from actions import login_now
        from uasyncio import sleep_ms

        # Replace some drawing functions
        orig_fullscreen = main.dis.fullscreen
        orig_progress_bar = main.dis.progress_bar
        orig_progress_bar_show = main.dis.progress_bar_show
        main.dis.fullscreen = self.hack_fullscreen
        main.dis.progress_bar = self.hack_progress_bar
        main.dis.progress_bar_show = self.hack_progress_bar

        # get ready ourselves
        main.dis.set_brightness(0)        # dimest, but still readable
        self.draw_background()

        # Kill time, waiting for user input
        self.digits = ''
        self.test_restart = False
        while not self.test_restart:
            self.show()
            gc.collect()

            try:
                # Poll for an event, no block
                ch = numpad.get_nowait()

                if ch == 'x':
                    self.digits = ''
                elif ch == 'y':
                    if len(self.digits) == LOCAL_PIN_LENGTH:
                        main.hsm_active.local_pin_entered(self.digits)
                        self.digits = ''
                elif ch == numpad.ABORT_KEY:
                    # important to eat these and fully suppress them
                    pass
                elif ch:
                    if len(self.digits) < LOCAL_PIN_LENGTH:
                        # allow only 6 digits
                        self.digits += ch[0]
                    if len(self.digits) == LOCAL_PIN_LENGTH:
                        # send it, even if they didn't press OK yet
                        main.hsm_active.local_pin_entered(self.digits)

                # do immediate screen update
                continue

            except QueueEmpty:
                await sleep_ms(100)
            except BaseException as exc:
                # just in case, keep going
                sys.print_exception(exc)
                continue

            # do the interactions, but don't let user actually press anything
            req = UserAuthorizedAction.active_request
            if req and not req.ux_done:
                try:
                    await req.interact()
                except AbortInteraction:
                    pass

        # This code only reachable on the simulator and modified devices under test,
        # and when the "boot_to_hsm" feature is used and successfully unlock near
        # boottime.
        from actions import goto_top_menu
        main.hsm_active = None
        goto_top_menu()

        # restore normal operation of UX
        from display import Display
        main.dis.fullscreen = orig_fullscreen
        main.dis.progress_bar = orig_progress_bar
        main.dis.progress_bar_show = orig_progress_bar_show

        return
Exemple #13
0
async def make_new_wallet():
    # pick a new random seed, and force them to
    # write it down, then save it.

    from main import dis
    from uasyncio import sleep_ms

    # CONCERN: memory is really contaminated with secrets in this process, much more so
    # than during normal operation. Maybe we should block USB and force a reboot as well?

    # LESSON LEARNED: if the user is writting down the words, as we have
    # vividly instructed, then it's a big deal to lose those words and have to start
    # over. So confirm that action, and don't volunteer it.

    # dramatic pause
    await ux_dramatic_pause('Generating...', 4)

    # always full 24-word (256 bit) entropy
    seed = bytearray(32)
    rng_bytes(seed)

    assert len(set(seed)) > 4, "impossible luck?"

    # hash to mitigate bias in TRNG
    seed = tcc.sha256(seed).digest()

    words = tcc.bip39.from_data(seed).split(' ')
    assert len(words) == 24

    #print('words: ' + ' '.join(words))

    while 1:
        # show the seed words
        ch = await show_words(words, escape='6')

        if ch == 'x':
            # user abort
            if await ux_confirm("Throw away those words and stop this process?"
                                ):
                return
            else:
                continue

        if ch == '6':
            # wants to skip the quiz (undocumented)
            if await ux_confirm(
                    "Skipping the quiz means you might have "
                    "recorded the seed wrong and will be crying later."):
                break

        # Perform a test, to check they wrote them down
        ch = await word_quiz(words)
        if ch == 'x':
            # user abort quiz
            if await ux_confirm(
                    "Throw away those words and stop this process? Press X to see the word list again and restart the quiz."
            ):
                return

            # show the words again, but don't change them
            continue

        # quiz passed
        break

    # Done!
    set_seed_value(words)

    # send them to home menu, now with a wallet enabled
    goto_top_menu()
Exemple #14
0
# (c) Copyright 2020 by Coinkite Inc. This file is covered by license found in COPYING-CC.
#
# quickly main wipe seed; don't install anything new
from main import pa, settings, numpad, dis
from pincodes import AE_SECRET_LEN, PA_IS_BLANK

if not pa.is_secret_blank():
    # clear settings associated with this key, since it will be no more
    settings.blank()

    # save a blank secret (all zeros is a special case, detected by bootloader)
    dis.fullscreen('Wipe Seed!')
    nv = bytes(AE_SECRET_LEN)
    pa.change(new_secret=nv)

    rv = pa.setup(pa.pin)
    pa.login()

    assert pa.is_secret_blank()

# reset top menu and go there
from actions import goto_top_menu
goto_top_menu()

numpad.abort_ux()