Exemple #1
0
def _try_microsd(bad_fs_ok=False):
    # Power up, mount the SD card, return False if we can't for some reason.
    #
    # If we're about to reformat, we don't need a working filesystem

    sd = pyb.SDCard()

    if not sd.present():
        return False

    if ckcc.is_simulator():
        return True

    try:
        # already mounted and ready?
        st = os.statvfs('/sd')
        return True
    except OSError:
        pass

    try:
        sd.power(1)
        os.mount(sd, '/sd', readonly=0, mkfs=0)
        st = os.statvfs('/sd')

        return True

    except OSError as exc:
        # corrupt or unformated SD card (or something)
        if bad_fs_ok: return True
        #sys.print_exception(exc)
        return False
Exemple #2
0
async def test_ae508a():
    if ckcc.is_simulator(): return

    assert not get_is_bricked(), "AE508a is bricked"

    for ph in range(5):
        gg = get_genuine()

        dis.clear()
        if gg:
            dis.text(-1, 8, "Green ON? -->")
        else:
            dis.text(-1, 50, "Red ON? -->")

        dis.show()
        k = await ux_wait_keyup('xy')
        assert k == 'y', "LED bust"

        if ph and gg:
            # stop once it's on and we've tested both states
            return

        # attempt to switch to other state
        if gg:
            clear_genuine()
        else:
            # very slow!
            dis.text(0, 0, "Wait")
            dis.show()
            set_genuine()
            ux_clear_keys()

        ng = get_genuine()
        assert ng != gg, "Could not invert LED"
Exemple #3
0
    def busy_bar(self, enable, speed_code=5):
        # Render a continuous activity (not progress) bar in lower 8 lines of display
        # - using OLED itself to do the animation, so smooth and CPU free
        # - cannot preserve bottom 8 lines, since we have to destructively write there
        # - assumes normal horz addr mode: 0x20, 0x00
        # - speed_code=>framedelay: 0=5fr, 1=64fr, 2=128, 3=256, 4=3, 5=4, 6=25, 7=2frames
        assert 0 <= speed_code <= 7

        setup = bytes([
            0x21,
            0x00,
            0x7f,  # setup column address range (start, end): 0-127
            0x22,
            7,
            7,  # setup page start/end address: page 7=last 8 lines
        ])
        animate = bytes([
            0x2e,  # stop animations in progress
            0x26,  # scroll leftwards (stock ticker mode)
            0,  # placeholder
            7,  # start 'page' (vertical)
            speed_code,  # scroll speed: 7=fastest, but no order to it
            7,  # end 'page'
            0,
            0xff,  # placeholders
            0x2f  # start
        ])

        cleanup = bytes([
            0x2e,  # stop animation
            0x20,
            0x00,  # horz addr-ing mode
            0x21,
            0x00,
            0x7f,  # setup column address range (start, end): 0-127
            0x22,
            7,
            7,  # setup page start/end address: page 7=last 8 lines
        ])

        if not enable:
            # stop animation, and redraw old (new) screen
            self.write_cmds(cleanup)
            self.show()
        else:

            # a pattern that repeats nicely mod 128
            # - each byte here is a vertical column, 8 pixels tall, MSB at bottom
            data = bytes(0x80 if (x % 4) < 2 else 0x0 for x in range(128))

            if ckcc.is_simulator():
                # just show as static pattern
                t = self.dis.buffer[:-128] + data
                self.dis.write_data(t)
            else:
                self.write_cmds(setup)
                self.dis.write_data(data)
                self.write_cmds(animate)
Exemple #4
0
    def default_values():
        # Please try to avoid defaults here... It's better to put into code
        # where value is used, and treat undefined as the default state.

        if ckcc.is_simulator():
            from sim_settings import sim_defaults
            return dict(sim_defaults)

        return dict(_age=0)
Exemple #5
0
    async def animate_splash(self, loop, done_fcn, func50):
        ns = 32 if not ckcc.is_simulator() else 4
        for i in range(ns):
            self.progress_bar(i / (ns-1))
            self.show()
            if i == ns//2 and func50:
                # do something at 50% mark
                func50()
            await sleep_ms(5)

        if done_fcn:
            # create a new task, so this stack can be freed
            loop.create_task(done_fcn())
Exemple #6
0
async def test_secure_element():

    assert not get_is_bricked()         # bricked already

    # test right chips installed
    is_fat = ckcc.is_stm32l496()
    if is_fat:
        assert version.has_608          # expect 608a
        assert version.hw_label == 'mk3'
    else:
        assert not version.has_608      # expect 508a
        assert version.hw_label != 'mk3'

    if ckcc.is_simulator(): return

    for ph in range(5):
        gg = get_genuine()

        dis.clear()
        if gg:
            dis.text(-1, 8, "Green ON? -->")
        else:
            dis.text(-1,50, "Red ON? -->")

        dis.show()
        k = await ux_wait_keyup('xy')
        assert k == 'y'     # "LED bust"

        if ph and gg:
            # stop once it's on and we've tested both states
            return

        # attempt to switch to other state
        if gg:
            clear_genuine()
        else:
            # very slow!
            dis.text(0,0, "Wait")
            dis.show()
            set_genuine()
            ux_clear_keys()

        ng = get_genuine()
        assert ng != gg     # "Could not invert LED"
Exemple #7
0
    def save(self):
        # Create JSON document for next time.
        simple = [
            'must_log', 'never_log', 'msg_paths', 'share_xpubs', 'share_addrs',
            'notes', 'period', 'allow_sl', 'warnings_ok', 'boot_to_hsm',
            'priv_over_ux'
        ]
        rv = dict()
        for fn in simple:
            rv[fn] = getattr(self, fn, None)

        rv['rules'] = [i.to_json() for i in self.rules]

        # never write this secret into JSON
        if ckcc.is_simulator():
            # .. except simulator case
            rv['set_sl'] = self.set_sl
        else:
            assert 'set_sl' not in rv

        return rv
Exemple #8
0
 def get_sd_root(self):
     # get the path to the SD card
     if ckcc.is_simulator():
         return ckcc.get_sim_root_dirs()[1]
     else:
         return '/sd'
Exemple #9
0
async def test_microsd():
    if ckcc.is_simulator(): return

    async def wait_til_state(want):
        dis.clear()
        dis.text(None, 10, 'MicroSD Card:')
        dis.text(None, 34, 'Remove' if sd.present() else 'Insert', font=FontLarge)
        dis.show()

        while 1:
            if want == sd.present(): return
            await sleep_ms(100)
            if ux_poll_once():
                raise RuntimeError("MicroSD test aborted")

    try:
        import pyb
        sd = pyb.SDCard()
        sd.power(0)

        # test presence switch
        for ph in range(7):
            await wait_til_state(not sd.present())

            if ph >= 2 and sd.present():
                # debounce
                await sleep_ms(100)
                if sd.present(): break
                if ux_poll_once():
                    raise RuntimeError("MicroSD test aborted")

        dis.clear()
        dis.text(None, 10, 'MicroSD Card:')
        dis.text(None, 34, 'Testing', font=FontLarge)
        dis.show()

        # card inserted
        assert sd.present()     #, "SD not present?"

        # power up?
        sd.power(1)
        await sleep_ms(100)

        try:
            blks, bsize, *unused = sd.info()
            assert bsize == 512
        except:
            assert 0        # , "card info"

        # just read it a bit, writing would prove little
        buf = bytearray(512)
        msize = 256*1024
        for addr in range(0, msize, 1024):
            sd.readblocks(addr, buf)
            dis.progress_bar_show(addr/msize)

            if addr == 0:
                assert buf[-2:] == b'\x55\xaa'      # "Bad read"

        # force removal, so cards don't get stuck in finished units
        await wait_til_state(False)

    finally:
        # CRTICAL: power it back down
        sd.power(0)
Exemple #10
0
    async def handle(self, cmd, args):
        # Dispatch incoming message, and provide reply.
        from main import hsm_active, is_devmode

        try:
            cmd = bytes(cmd).decode()
        except:
            raise FramingError('decode')

        if cmd[0].isupper() and (is_simulator() or is_devmode):
            # special hacky commands to support testing w/ the simulator
            try:
                from usb_test_commands import do_usb_command
                return do_usb_command(cmd, args)
            except:
                raise
                pass

        if hsm_active:
            # only a few commands are allowed during HSM mode
            if cmd not in HSM_WHITELIST:
                raise HSMDenied

        if cmd == 'dfu_':
            # only useful in factory, undocumented.
            return self.call_after(callgate.enter_dfu)

        if cmd == 'rebo':
            import machine
            return self.call_after(machine.reset)

        if cmd == 'logo':
            from utils import clean_shutdown
            return self.call_after(clean_shutdown)

        if cmd == 'ping':
            return b'biny' + args

        if cmd == 'upld':
            offset, total_size = unpack_from('<II', args)
            data = memoryview(args)[4 + 4:]

            return await self.handle_upload(offset, total_size, data)

        if cmd == 'dwld':
            offset, length, fileno = unpack_from('<III', args)
            return await self.handle_download(offset, length, fileno)

        if cmd == 'ncry':
            version, his_pubkey = unpack_from('<I64s', args)

            return self.handle_crypto_setup(version, his_pubkey)

        if cmd == 'vers':
            from version import get_mpy_version, hw_label
            from callgate import get_bl_version

            # Returning: date, version(human), bootloader version, full date version
            # BUT: be ready for additions!
            rv = list(get_mpy_version())
            rv.insert(2, get_bl_version()[0])
            rv.append(hw_label)

            return b'asci' + ('\n'.join(rv)).encode()

        if cmd == 'sha2':
            return b'biny' + self.file_checksum.digest()

        if cmd == 'xpub':
            assert self.encrypted_req, 'must encrypt'
            return self.handle_xpub(args)

        if cmd == 'mitm':
            assert self.encrypted_req, 'must encrypt'
            return await self.handle_mitm_check()

        if cmd == 'smsg':
            # sign message
            addr_fmt, len_subpath, len_msg = unpack_from('<III', args)
            subpath = args[12:12 + len_subpath]
            msg = args[12 + len_subpath:]
            assert len(msg) == len_msg, "badlen"

            from auth import sign_msg
            sign_msg(msg, subpath, addr_fmt)
            return None

        if cmd == 'p2sh':
            # show P2SH (probably multisig) address on screen (also provides it back)
            # - must provide redeem script, and list of [xfp+path]
            from auth import start_show_p2sh_address

            if hsm_active and not hsm_active.approve_address_share(
                    is_p2sh=True):
                raise HSMDenied

            # new multsig goodness, needs mapping from xfp->path and M values
            addr_fmt, M, N, script_len = unpack_from('<IBBH', args)

            assert addr_fmt & AFC_SCRIPT
            assert 1 <= M <= N <= 20
            assert 30 <= script_len <= 520

            offset = 8
            witdeem_script = args[offset:offset + script_len]
            offset += script_len

            assert len(witdeem_script) == script_len

            xfp_paths = []
            for i in range(N):
                ln = args[offset]
                assert 1 <= ln <= 16, 'badlen'
                xfp_paths.append(unpack_from('<%dI' % ln, args, offset + 1))
                offset += (ln * 4) + 1

            assert offset == len(args)

            return b'asci' + start_show_p2sh_address(M, N, addr_fmt, xfp_paths,
                                                     witdeem_script)

        if cmd == 'show':
            # simple cases, older code: text subpath
            from auth import start_show_address

            addr_fmt, = unpack_from('<I', args)
            assert not (addr_fmt & AFC_SCRIPT)

            return b'asci' + start_show_address(addr_fmt, subpath=args[4:])

        if cmd == 'enrl':
            # Enroll new xpubkey to be involved in multisigs.
            # - text config file must already be uploaded

            file_len, file_sha = unpack_from('<I32s', args)
            if file_sha != self.file_checksum.digest():
                return b'err_Checksum'
            assert 100 < file_len <= (20 * 200), "badlen"

            # Start an UX interaction, return immediately here
            from auth import maybe_enroll_xpub
            maybe_enroll_xpub(sf_len=file_len, ux_reset=True)

            return None

        if cmd == 'msck':
            # Quick check to test if we have a wallet already installed.
            from multisig import MultisigWallet
            M, N, xfp_xor = unpack_from('<3I', args)

            return int(MultisigWallet.quick_check(M, N, xfp_xor))

        if cmd == 'stxn':
            # sign transaction
            txn_len, flags, txn_sha = unpack_from('<II32s', args)
            if txn_sha != self.file_checksum.digest():
                return b'err_Checksum'

            assert 50 < txn_len <= MAX_TXN_LEN, "bad txn len"

            from auth import sign_transaction
            sign_transaction(txn_len, (flags & STXN_FLAGS_MASK), txn_sha)
            return None

        if cmd == 'stok' or cmd == 'bkok' or cmd == 'smok' or cmd == 'pwok':
            # Have we finished (whatever) the transaction,
            # which needed user approval? If so, provide result.
            from auth import UserAuthorizedAction

            req = UserAuthorizedAction.active_request
            if not req:
                return b'err_No active request'

            if req.refused:
                UserAuthorizedAction.cleanup()
                return b'refu'

            if req.failed:
                rv = b'err_' + req.failed.encode()
                UserAuthorizedAction.cleanup()
                return rv

            if not req.result:
                # STILL waiting on user
                return None

            if cmd == 'pwok':
                # return new root xpub
                xpub = req.result
                UserAuthorizedAction.cleanup()
                return b'asci' + bytes(xpub, 'ascii')
            elif cmd == 'smok':
                # signed message done: just give them the signature
                addr, sig = req.address, req.result
                UserAuthorizedAction.cleanup()
                return pack('<4sI', 'smrx', len(addr)) + addr.encode() + sig
            else:
                # generic file response
                resp_len, sha = req.result
                UserAuthorizedAction.cleanup()
                return pack('<4sI32s', 'strx', resp_len, sha)

        if cmd == 'pass':
            # bip39 passphrase provided, maybe use it if authorized
            assert self.encrypted_req, 'must encrypt'
            from auth import start_bip39_passphrase
            from main import settings

            assert settings.get('words', True), 'no seed'
            assert len(args) < 400, 'too long'
            pw = str(args, 'utf8')
            assert len(pw) < 100, 'too long'

            return start_bip39_passphrase(pw)

        if cmd == 'back':
            # start backup: asks user, takes long time.
            from auth import start_remote_backup
            return start_remote_backup()

        if cmd == 'blkc':
            # report which blockchain we are configured for
            from chains import current_chain
            chain = current_chain()
            return b'asci' + chain.ctype

        if cmd == 'bagi':
            return self.handle_bag_number(args)

        if has_fatram:
            # HSM and user-related  features only larger-memory Mk3

            if cmd == 'hsms':
                # HSM mode "start" -- requires user approval
                if args:
                    file_len, file_sha = unpack_from('<I32s', args)
                    if file_sha != self.file_checksum.digest():
                        return b'err_Checksum'
                    assert 2 <= file_len <= (200 * 1000), "badlen"
                else:
                    file_len = 0

                # Start an UX interaction but return (mostly) immediately here
                from hsm_ux import start_hsm_approval
                await start_hsm_approval(sf_len=file_len, usb_mode=True)

                return None

            if cmd == 'hsts':
                # can always query HSM mode
                from hsm import hsm_status_report
                import ujson
                return b'asci' + ujson.dumps(hsm_status_report())

            if cmd == 'gslr':
                # get the value held in the Storage Locker
                assert hsm_active, 'need hsm'
                return b'biny' + hsm_active.fetch_storage_locker()

            # User Mgmt
            if cmd == 'nwur':  # new user
                from users import Users
                auth_mode, ul, sl = unpack_from('<BBB', args)
                username = bytes(args[3:3 + ul]).decode('ascii')
                secret = bytes(args[3 + ul:3 + ul + sl])

                return b'asci' + Users.create(username, auth_mode,
                                              secret).encode('ascii')

            if cmd == 'rmur':  # delete user
                from users import Users
                ul, = unpack_from('<B', args)
                username = bytes(args[1:1 + ul]).decode('ascii')

                return Users.delete(username)

            if cmd == 'user':  # auth user (HSM mode)
                from users import Users
                totp_time, ul, tl = unpack_from('<IBB', args)
                username = bytes(args[6:6 + ul]).decode('ascii')
                token = bytes(args[6 + ul:6 + ul + tl])

                if hsm_active:
                    # just queues these details, can't be checked until PSBT on-hand
                    hsm_active.usb_auth_user(username, token, totp_time)
                    return None
                else:
                    # dryrun/testing purposes: validate only, doesn't unlock nothing
                    return b'asci' + Users.auth_okay(username, token,
                                                     totp_time).encode('ascii')

        print("USB garbage: %s +[%d]" % (cmd, len(args)))

        return b'err_Unknown cmd'
Exemple #11
0
    async def handle(self, cmd, args):
        # Dispatch incoming message, and provide reply.

        try:
            cmd = bytes(cmd).decode()
        except:
            raise FramingError('decode')

        if cmd == 'dfu_':
            # only useful in factory, undocumented.
            return self.call_after(callgate.enter_dfu)

        if cmd == 'rebo':
            import machine
            return self.call_after(machine.reset)

        if cmd == 'logo':
            return self.call_after(callgate.show_logout)

        if cmd == 'ping':
            return b'biny' + args

        if cmd == 'upld':
            offset, total_size = unpack_from('<II', args)
            data = memoryview(args)[4 + 4:]

            return await self.handle_upload(offset, total_size, data)

        if cmd == 'dwld':
            offset, length, fileno = unpack_from('<III', args)
            return await self.handle_download(offset, length, fileno)

        if cmd == 'ncry':
            version, his_pubkey = unpack_from('<I64s', args)

            return self.handle_crypto_setup(version, his_pubkey)

        if cmd == 'vers':
            from version import get_mpy_version
            from callgate import get_bl_version

            # Returning: date, version(human), bootloader version, full date version
            # BUT: be ready for additions!
            rv = list(get_mpy_version())
            rv.insert(2, get_bl_version()[0])

            return b'asci' + ('\n'.join(rv)).encode()

        if cmd == 'sha2':
            return b'biny' + self.file_checksum.digest()

        if cmd == 'xpub':
            assert self.encrypted_req, 'must encrypt'
            return self.handle_xpub(str(args, 'ascii'))

        if cmd == 'mitm':
            assert self.encrypted_req, 'must encrypt'
            return await self.handle_mitm_check()

        if cmd == 'smsg':
            # sign message
            addr_fmt, len_subpath, len_msg = unpack_from('<III', args)
            subpath = args[12:12 + len_subpath]
            msg = args[12 + len_subpath:]
            assert len(msg) == len_msg, "badlen"

            from auth import sign_msg
            sign_msg(msg, subpath, addr_fmt)
            return None

        if cmd == 'p2sh':
            # show P2SH (probably multisig) address on screen (also provides it back)
            # - must provide redeem script, and list of [xfp+path]
            from auth import start_show_p2sh_address

            # new multsig goodness, needs mapping from xfp->path and M values
            addr_fmt, M, N, script_len = unpack_from('<IBBH', args)

            assert addr_fmt & AFC_SCRIPT
            assert 1 <= M <= N <= 20
            assert 30 <= script_len <= 520

            offset = 8
            witdeem_script = args[offset:offset + script_len]
            offset += script_len

            assert len(witdeem_script) == script_len

            xfp_paths = []
            for i in range(N):
                ln = args[offset]
                assert 2 <= ln <= 16, 'badlen'
                xfp_paths.append(unpack_from('<%dI' % ln, args, offset + 1))
                offset += (ln * 4) + 1

            assert offset == len(args)

            return b'asci' + start_show_p2sh_address(M, N, addr_fmt, xfp_paths,
                                                     witdeem_script)

        if cmd == 'show':
            # simple cases, older code: text subpath
            from auth import start_show_address

            addr_fmt, = unpack_from('<I', args)
            assert not (addr_fmt & AFC_SCRIPT)

            return b'asci' + start_show_address(addr_fmt, subpath=args[4:])

        if cmd == 'enrl':
            # Enroll new xpubkey to be involved in multisigs.
            # - text config file must already be uploaded

            file_len, file_sha = unpack_from('<I32s', args)
            if file_sha != self.file_checksum.digest():
                return b'err_Checksum'
            assert 100 < file_len <= (20 * 200), "badlen"

            # Start an UX interaction, return immediately here
            from auth import maybe_enroll_xpub
            maybe_enroll_xpub(sf_len=file_len, ux_reset=True)
            return None

        if cmd == 'msck':
            # Quick check to test if we have a wallet already installed.
            from multisig import MultisigWallet
            M, N, xfp_xor = unpack_from('<3I', args)

            return int(MultisigWallet.quick_check(M, N, xfp_xor))

        if cmd == 'stxn':
            # sign transaction
            txn_len, finalize, txn_sha = unpack_from('<II32s', args)
            if txn_sha != self.file_checksum.digest():
                return b'err_Checksum'

            assert 50 < txn_len <= MAX_TXN_LEN, "bad txn len"

            from auth import sign_transaction
            sign_transaction(txn_len, bool(finalize))
            return None

        if cmd == 'stok' or cmd == 'bkok' or cmd == 'smok' or cmd == 'pwok' or cmd == 'enok':
            # Have we finished (whatever) the transaction,
            # which needed user approval? If so, provide result.
            from auth import active_request, UserAuthorizedAction

            if not active_request:
                return b'err_No active request'

            if active_request.refused:
                UserAuthorizedAction.cleanup()
                return b'refu'

            if active_request.failed:
                rv = b'err_' + active_request.failed.encode()
                UserAuthorizedAction.cleanup()
                return rv

            if not active_request.result:
                # STILL waiting on user
                return None

            if cmd == 'pwok' or cmd == 'enok':
                # return new root xpub
                xpub = active_request.result
                UserAuthorizedAction.cleanup()
                return b'asci' + bytes(xpub, 'ascii')
            elif cmd == 'smok':
                # signed message done: just give them the signature
                addr, sig = active_request.address, active_request.result
                UserAuthorizedAction.cleanup()
                return pack('<4sI', 'smrx', len(addr)) + addr.encode() + sig
            else:
                # generic file response
                resp_len, sha = active_request.result
                UserAuthorizedAction.cleanup()
                return pack('<4sI32s', 'strx', resp_len, sha)

        if cmd == 'pass':
            # bip39 passphrase provided, maybe use it if authorized
            assert self.encrypted_req, 'must encrypt'
            from auth import start_bip39_passphrase
            from main import settings

            assert settings.get('words', True), 'no seed'
            assert len(args) < 400, 'too long'
            pw = str(args, 'utf8')
            assert len(pw) < 100, 'too long'

            return start_bip39_passphrase(pw)

        if cmd == 'back':
            # start backup: asks user, takes long time.
            from auth import start_remote_backup
            return start_remote_backup()

        if cmd == 'bagi':
            return self.handle_bag_number(args)

        if is_simulator() and cmd[0].isupper():
            # special hacky commands to support testing w/ the simulator
            from sim_usb import do_usb_command
            return do_usb_command(cmd, args)

        print("USB garbage: %s +[%d]" % (cmd, len(args)))

        return b'err_Unknown cmd'
Exemple #12
0
    async def handle(self, cmd, args):
        # Dispatch incoming message, and provide reply.

        cmd = bytes(cmd).decode()

        if cmd == 'dfu_':
            # only useful in factory, undocumented.
            return self.call_after(callgate.enter_dfu)

        if cmd == 'rebo':
            import machine
            return self.call_after(machine.reset)

        if cmd == 'logo':
            return self.call_after(callgate.show_logout)

        if cmd == 'ping':
            return b'biny' + args

        if cmd == 'upld':
            offset, total_size = unpack_from('<II', args)
            data = memoryview(args)[4 + 4:]

            return await self.handle_upload(offset, total_size, data)

        if cmd == 'dwld':
            offset, length, fileno = unpack_from('<III', args)
            return await self.handle_download(offset, length, fileno)

        if cmd == 'ncry':
            version, his_pubkey = unpack_from('<I64s', args)

            return self.handle_crypto_setup(version, his_pubkey)

        if cmd == 'vers':
            from version import get_mpy_version
            from callgate import get_bl_version

            # Returning: date, version(human), bootloader version, full date version
            # BUT: be ready for additions!
            rv = list(get_mpy_version())
            rv.insert(2, get_bl_version()[0])

            return b'asci' + ('\n'.join(rv)).encode()

        if cmd == 'sha2':
            return b'biny' + self.file_checksum.digest()

        if cmd == 'xpub':
            assert self.encrypted_req, 'must encrypt'
            return self.handle_xpub(str(args, 'ascii'))

        if cmd == 'mitm':
            assert self.encrypted_req, 'must encrypt'
            return await self.handle_mitm_check()

        if cmd == 'smsg':
            # sign message
            addr_fmt, len_subpath, len_msg = unpack_from('<III', args)
            subpath = args[12:12 + len_subpath]
            msg = args[12 + len_subpath:]
            assert len(msg) == len_msg, "badlen"

            from auth import sign_msg
            sign_msg(msg, subpath, addr_fmt)
            return None

        if cmd == 'show':
            # show address on screen (also provides it back)
            addr_fmt, = unpack_from('<I', args)
            subpath = args[4:]
            from auth import start_show_address

            return b'asci' + start_show_address(subpath, addr_fmt)

        if cmd == 'stxn':
            # sign transaction
            txn_len, finalize, txn_sha = unpack_from('<II32s', args)
            if txn_sha != self.file_checksum.digest():
                return b'err_Checksum'

            assert 50 < txn_len <= MAX_TXN_LEN, "bad txn len"

            from auth import sign_transaction
            sign_transaction(txn_len, bool(finalize))
            return None

        if cmd == 'stok' or cmd == 'bkok' or cmd == 'smok' or cmd == 'pwok':
            # Have we finished (whatever) the transaction,
            # which needed user approval? If so, provide result.
            from auth import active_request, UserAuthorizedAction

            if not active_request:
                return b'err_No active request'

            if active_request.refused:
                UserAuthorizedAction.cleanup()
                return b'refu'

            if active_request.failed:
                rv = b'err_' + active_request.failed.encode()
                UserAuthorizedAction.cleanup()
                return rv

            if not active_request.result:
                # STILL waiting on user
                return None

            if cmd == 'pwok':
                # return new root xpub
                xpub = active_request.result
                UserAuthorizedAction.cleanup()
                return b'asci' + bytes(xpub, 'ascii')
            elif cmd == 'smok':
                # signed message done: just give them the signature
                addr, sig = active_request.address, active_request.result
                UserAuthorizedAction.cleanup()
                return pack('<4sI', 'smrx', len(addr)) + addr.encode() + sig
            else:
                # generic file response
                resp_len, sha = active_request.result
                UserAuthorizedAction.cleanup()
                return pack('<4sI32s', 'strx', resp_len, sha)

        if cmd == 'pass':
            # bip39 passphrase provided, maybe use it if authorized
            assert self.encrypted_req, 'must encrypt'
            from auth import start_bip39_passphrase

            assert len(args) < 400, 'too long'
            pw = str(args, 'utf8')
            assert len(pw) < 100, 'too long'

            return start_bip39_passphrase(pw)

        if cmd == 'back':
            # start backup: asks user, takes long time.
            from auth import start_remote_backup
            return start_remote_backup()

        if cmd == 'bagi':
            return self.handle_bag_number(args)

        if is_simulator() and cmd[0].isupper():
            # special hacky commands to support testing w/ the simulator
            from sim_usb import do_usb_command
            return do_usb_command(cmd, args)

        print("USB garbage: %s +[%d]" % (cmd, len(args)))

        return b'err_Unknown cmd'
Exemple #13
0
    async def usb_hid_recv(self):
        # blocks and builds up a full-length command packet in memory
        # - calls self.handle() once complete msg on hand
        msg_len = 0

        while 1:
            yield core._io_queue.queue_read(self.blockable)

            try:
                here, is_last, is_encrypted = self.get_packet()

                #print('Rx[%d]' % len(here))
                if here:
                    lh = len(here)
                    if msg_len + lh > MAX_MSG_LEN:
                        raise FramingError('xlong')

                    self.msg[msg_len:msg_len + lh] = here
                    msg_len += lh
                else:
                    # treat zero-length packets as a reset request
                    # do not echo anything back on link.. used to resync connection
                    msg_len = 0
                    continue

                if not is_last:
                    # need more content
                    continue

                if not (4 <= msg_len <= MAX_MSG_LEN):
                    raise FramingError('badsz')

                if is_encrypted:
                    if self.decrypt is None:
                        raise FramingError('no key')

                    self.encrypted_req = True
                    self.decrypt_inplace(msg_len)
                else:
                    self.encrypted_req = False

                # process request
                try:
                    # this saves memory over a simple slice (confirmed)
                    args = memoryview(self.msg)[4:msg_len]
                    resp = await self.handle(self.msg[0:4], args)
                    msg_len = 0
                except CCBusyError:
                    # auth UX is doing something else
                    resp = b'busy'
                    msg_len = 0
                except HSMDenied:
                    resp = b'err_Not allowed in HSM mode'
                    msg_len = 0
                except (ValueError, AssertionError) as exc:
                    # some limited invalid args feedback
                    #print("USB request caused assert: ", end='')
                    #sys.print_exception(exc)
                    msg = str(exc)
                    if not msg:
                        msg = 'Assertion ' + problem_file_line(exc)
                    resp = b'err_' + msg.encode()[0:80]
                    msg_len = 0
                except MemoryError:
                    # prefer to catch at higher layers, but sometimes can't
                    resp = b'err_Out of RAM'
                    msg_len = 0
                except Exception as exc:
                    # catch bugs and fuzzing too
                    if is_simulator() or is_devmode:
                        print("USB request caused this: ", end='')
                        sys.print_exception(exc)
                    resp = b'err_Confused ' + problem_file_line(exc)
                    msg_len = 0

                # aways send a reply if they get this far
                await self.send_response(resp)

            except FramingError as exc:
                reason = exc.args[0]
                #print("Framing: %s" % reason)
                self.framing_error(reason)
                msg_len = 0

            except BaseException as exc:
                # recover from general issues/keep going
                #print("USB!")
                #sys.print_exception(exc)
                msg_len = 0
Exemple #14
0
async def test_microsd():
    if ckcc.is_simulator(): return

    from main import numpad
    numpad.stop()

    try:
        import pyb
        sd = pyb.SDCard()
        sd.power(0)

        # test presence switch
        for ph in range(7):
            want = not sd.present()

            dis.clear()
            dis.text(None, 10, 'MicroSD Card:')
            dis.text(None,
                     34,
                     'Remove' if sd.present() else 'Insert',
                     font=FontLarge)
            dis.show()

            while 1:
                if want == sd.present(): break
                await sleep_ms(100)
                if ux_poll_once():
                    raise RuntimeError("MicroSD test aborted")

            if ph >= 2 and sd.present():
                # debounce
                await sleep_ms(100)
                if sd.present(): break
                if ux_poll_once():
                    raise RuntimeError("MicroSD test aborted")

        dis.clear()
        dis.text(None, 10, 'MicroSD Card:')
        dis.text(None, 34, 'Testing', font=FontLarge)
        dis.show()

        # card inserted
        assert sd.present(), "SD not present?"

        # power up?
        sd.power(1)
        await sleep_ms(100)

        try:
            blks, bsize, ctype = sd.info()
            assert bsize == 512, "wrong block size"
        except:
            assert 0, "unable to get card info"

        # just read it a bit, writing would prove little
        buf = bytearray(512)
        msize = 1024 * 1024
        for addr in range(0, msize, 1024):
            sd.readblocks(addr, buf)
            dis.progress_bar_show(addr / msize)

            if addr == 0:
                assert buf[-2:] == b'\x55\xaa', "Bad read"

    finally:
        # CRTICAL: power it back down
        sd.power(0)
        numpad.start()