Ejemplo n.º 1
0
async def initial_pin_setup(*a):
    # First time they select a PIN of any type.
    from login import LoginUX
    lll = LoginUX()
    pin = await lll.get_new_pin(
        'Choose PIN', '''\
Pick the main wallet's PIN code now. Be more clever, but an example:

123-4567

It has two parts: prefix (123-) and suffix (-4567). \
Each part must between 2 to 6 digits long. Total length \
can be as long as 12 digits.

The prefix part determines the anti-phishing words you will \
see each time you login.

Your new PIN protects access to \
this Coldcard device and is not a factor in the wallet's \
seed words or private keys.

THERE IS ABSOLUTELY NO WAY TO RECOVER A FORGOTTEN PIN! Write it down.
''')
    del lll

    if pin is None: return

    # A new pin is to be set!
    from main import pa, dis, settings, loop
    dis.fullscreen("Saving...")

    try:
        assert pa.is_blank()

        pa.change(new_pin=pin)

        # check it? kinda, but also get object into normal "logged in" state
        pa.setup(pin)
        ok = pa.login()
        assert ok

        # must re-read settings after login, because they are encrypted
        # with a key derived from the main secret.
        settings.set_key()
        settings.load()
    except Exception as e:
        print("Exception: %s" % e)

    # Allow USB protocol, now that we are auth'ed
    from usb import enable_usb
    enable_usb(loop, False)

    from menu import MenuSystem
    from flow import EmptyWallet
    return MenuSystem(EmptyWallet)
Ejemplo n.º 2
0
async def block_until_login(*a):
    #
    # Force user to enter a valid PIN.
    #
    from login import LoginUX
    from main import pa, loop, settings
    from ux import AbortInteraction

    while not pa.is_successful():
        lll = LoginUX()

        try:
            await lll.try_login()
        except AbortInteraction:
            # not allowed!
            pass
Ejemplo n.º 3
0
async def pin_changer(_1, _2, item):
    # Help them to change pins with appropriate warnings.
    # - forcing them to drill-down to get warning about secondary is on purpose
    # - the bootloader maybe lying to us about weather we are main vs. duress
    # - there is a duress wallet for both main/sec pins, and you need to know main pin for that
    # - what may look like just policy here, is in fact enforced by the bootrom code
    #
    from main import pa, dis
    from login import LoginUX
    from pincodes import BootloaderError, EPIN_OLD_AUTH_FAIL

    mode = item.arg

    warn = {
        'main':
        ('Main PIN',
         'You will be changing the main PIN used to unlock your Coldcard. '
         "It's the one you just used a moment ago to get in here."),
        'duress': ('Duress PIN',
                   'This PIN leads to a bogus wallet. Funds are recoverable '
                   'from main seed backup, but not as easily.'),
        'secondary':
        ('Second PIN',
         'This PIN protects the "secondary" wallet that can be used to '
         'segregate funds or other banking purposes. This other wallet is '
         'completely independant of the primary.'),
        'brickme':
        ('Brickme PIN',
         'Use of this special PIN code at any prompt will destroy the '
         'Coldcard completely. It cannot be reused or salvaged, and '
         'the secrets it held are destroyed forever.\n\nDO NOT TEST THIS!'),
    }

    if pa.is_secondary:
        # secondary wallet user can only change their own password, and the secondary
        # duress pin...
        # NOTE: now excluded from menu, but keep
        if mode == 'main' or mode == 'brickme':
            await needs_primary()
            return

    if mode == 'duress' and pa.is_secret_blank():
        await ux_show_story(
            "Please set wallet seed before creating duress wallet.")
        return

    # are we changing the pin used to login?
    is_login_pin = (mode == 'main') or (mode == 'secondary'
                                        and pa.is_secondary)

    lll = LoginUX()
    lll.offer_second = False
    title, msg = warn[mode]

    async def incorrect_pin():
        await ux_show_story(
            'You provided an incorrect value for the existing %s.' % title,
            title='Wrong PIN')
        return

    # standard threats for all PIN's
    msg += '''\n\n\
THERE IS ABSOLUTELY NO WAY TO RECOVER A FORGOTTEN PIN! Write it down.

We strongly recommend all PIN codes used be unique between each other.
'''
    if not is_login_pin:
        msg += '''\nUse 999999-999999 to clear existing PIN.'''

    ch = await ux_show_story(msg, title=title)
    if ch != 'y': return

    args = {}

    need_old_pin = True

    if is_login_pin:
        # Challenge them for old password; certainly had it, because
        # we wouldn't be here otherwise.
        need_old_pin = True
    else:
        # There may be no existing PIN, and we need to learn that
        if mode == 'secondary':
            args['is_secondary'] = True
        elif mode == 'duress':
            args['is_duress'] = True
        elif mode == 'brickme':
            args['is_brickme'] = True

        old_pin = None
        try:
            dis.fullscreen("Check...")
            pa.change(old_pin=b'', new_pin=b'', **args)
            need_old_pin = False
            old_pin = ''  # converts to bytes below
        except BootloaderError as exc:
            #print("(not-error) old pin was NOT blank: %s" % exc)
            need_old_pin = True

    was_blank = not need_old_pin

    if need_old_pin:
        # We need the existing pin, so prompt for that.
        lll.subtitle = 'Old ' + title

        old_pin = await lll.prompt_pin()
        if old_pin is None:
            return await ux_aborted()

    args['old_pin'] = old_pin.encode()

    # we can verify the main pin right away here. Be nice.
    if is_login_pin and args['old_pin'] != pa.pin:
        return await incorrect_pin()

    while 1:
        lll.reset()
        lll.subtitle = "New " + title
        pin = await lll.get_new_pin(title, allow_clear=True)

        if pin is None:
            return await ux_aborted()

        is_clear = (pin == '999999-999999')

        args['new_pin'] = pin.encode() if not is_clear else b''

        if args['new_pin'] == pa.pin and not is_login_pin:
            await ux_show_story(
                "Your new PIN matches the existing PIN used to get here. "
                "It would be a bad idea to use it for another purpose.",
                title="Try Again")
            continue

        break

    # install it.
    dis.fullscreen("Saving...")

    try:
        pa.change(**args)
    except Exception as exc:
        code = exc.args[1]

        if code == EPIN_OLD_AUTH_FAIL:
            # likely: wrong old pin, on anything but main PIN
            return await incorrect_pin()
        else:
            return await ux_show_story("Unexpected low-level error: %s" %
                                       exc.args[0],
                                       title='Error')

    # Main pin is changed, and we use it lots, so update pa
    # - also to get pa.has_duress_pin() and has_brickme_pin() to be correct, need this
    dis.fullscreen("Verify...")
    pa.setup(args['new_pin'] if is_login_pin else pa.pin, pa.is_secondary)

    if not pa.is_successful():
        # typical: do need login, but if we just cleared the main PIN,
        # we cannot/need not login again
        pa.login()

    if mode == 'duress':
        # program the duress secret now... it's derived from real wallet
        from stash import SensitiveValues, SecretStash, AE_SECRET_LEN

        if is_clear:
            # clear secret, using the new pin, which is empty string
            pa.change(is_duress=True,
                      new_secret=b'\0' * AE_SECRET_LEN,
                      old_pin=b'',
                      new_pin=b'')
        else:
            with SensitiveValues() as sv:
                # derive required key
                node = sv.duress_root()
                d_secret = SecretStash.encode(xprv=node)
                sv.register(d_secret)

                # write it out.
                pa.change(is_duress=True,
                          new_secret=d_secret,
                          old_pin=args['new_pin'])
Ejemplo n.º 4
0
async def initial_pin_setup(*a):
    # First time they select a PIN of any type.
    from login import LoginUX
    lll = LoginUX()
    title = 'Choose PIN'

    ch = await ux_show_story('''\
Pick the main wallet's PIN code now. Be more clever, but an example:

123-4567

It has two parts: prefix (123-) and suffix (-4567). \
Each part must between 2 to 6 digits long. Total length \
can be as long as 12 digits.

The prefix part determines the anti-phishing words you will \
see each time you login.

Your new PIN protects access to \
this Coldcard device and is not a factor in the wallet's \
seed words or private keys.

THERE IS ABSOLUTELY NO WAY TO RECOVER A FORGOTTEN PIN! Write it down.
''',
                             title=title)
    if ch != 'y': return

    while 1:
        ch = await ux_show_story('''\
There is ABSOLUTELY NO WAY to 'reset the PIN' or 'factory reset' the Coldcard if you forget the PIN.

DO NOT FORGET THE PIN CODE.
 
Press 6 to prove you read to the end of this message.''',
                                 title='WARNING',
                                 escape='6')

        if ch == 'x': return
        if ch == '6': break

    # do the actual picking
    pin = await lll.get_new_pin(title)
    del lll

    if pin is None: return

    # A new pin is to be set!
    from main import pa, dis, settings, loop
    dis.fullscreen("Saving...")

    try:
        dis.busy_bar(True)
        assert pa.is_blank()

        pa.change(new_pin=pin)

        # check it? kinda, but also get object into normal "logged in" state
        pa.setup(pin)
        ok = pa.login()
        assert ok

        # must re-read settings after login, because they are encrypted
        # with a key derived from the main secret.
        settings.set_key()
        settings.load()
    except Exception as e:
        print("Exception: %s" % e)
    finally:
        dis.busy_bar(False)

    # Allow USB protocol, now that we are auth'ed
    from usb import enable_usb
    enable_usb(loop, False)

    from menu import MenuSystem
    from flow import EmptyWallet
    return MenuSystem(EmptyWallet)