Beispiel #1
0
def dispatch(report, open_callback, close_callback, unknown_callback):
    '''
    Dispatches payloads of reports adhering to one of the wire codecs.
    '''

    if codec_v1.detect(report):
        marker, session_id, report_data = codec_v1.parse_report(report)
    else:
        marker, session_id, report_data = codec_v2.parse_report(report)

        if marker == codec_v2.REP_MARKER_OPEN:
            log.debug(__name__, 'request for new session')
            open_callback()
            return
        elif marker == codec_v2.REP_MARKER_CLOSE:
            log.debug(__name__, 'request for closing session %x', session_id)
            close_callback(session_id)
            return

    if session_id not in readers:
        log.warning(__name__, 'report on unknown session %x', session_id)
        unknown_callback(session_id, report_data)
        return

    log.debug(__name__, 'report on session %x', session_id)
    reader = readers[session_id]

    try:
        reader.send(report_data)
    except StopIteration:
        readers.pop(session_id)
    except Exception as e:
        log.exception(__name__, e)
Beispiel #2
0
def msg_authenticate_genkey(app_id: bytes, keyhandle: bytes):

    from apps.common import seed

    # unpack the keypath from the first half of keyhandle
    keybuf = keyhandle[:32]
    keypath = ustruct.unpack('>8L', keybuf)

    # check high bit for hardened keys
    for i in keypath:
        if not i & 0x80000000:
            log.warning(__name__, 'invalid key path')
            return None

    # derive the signing key
    nodepath = [_U2F_KEY_PATH] + list(keypath)
    node = seed.get_root_without_passphrase('nist256p1')
    node.derive_path(nodepath)

    # second half of keyhandle is a hmac of app_id and keypath
    keybase = hmac.Hmac(node.private_key(), app_id, hashlib.sha256)
    keybase.update(keybuf)
    keybase = keybase.digest()

    # verify the hmac
    if keybase != keyhandle[32:]:
        log.warning(__name__, 'invalid key handle')
        return None

    return node
Beispiel #3
0
def msg_authenticate_genkey(app_id: bytes, keyhandle: bytes, pathformat: str):
    from apps.common import seed

    # unpack the keypath from the first half of keyhandle
    keybuf = keyhandle[:32]
    keypath = ustruct.unpack(pathformat, keybuf)

    # check high bit for hardened keys
    for i in keypath:
        if not i & HARDENED:
            if __debug__:
                log.warning(__name__, "invalid key path")
            return None

    # derive the signing key
    nodepath = [_U2F_KEY_PATH] + list(keypath)
    node = seed.derive_node_without_passphrase(nodepath, "nist256p1")

    # second half of keyhandle is a hmac of app_id and keypath
    keybase = hmac.Hmac(node.private_key(), app_id, hashlib.sha256)
    keybase.update(keybuf)
    keybase = keybase.digest()

    # verify the hmac
    if keybase != keyhandle[32:]:
        if __debug__:
            log.warning(__name__, "invalid key handle")
        return None

    return node
Beispiel #4
0
    def _node_from_key_handle(rp_id_hash: bytes, keyhandle: bytes,
                              pathformat: str) -> bip32.HDNode | None:
        # unpack the keypath from the first half of keyhandle
        keypath = keyhandle[:32]
        path = ustruct.unpack(pathformat, keypath)

        # check high bit for hardened keys
        for i in path:
            if not i & HARDENED:
                if __debug__:
                    log.warning(__name__, "invalid key path")
                return None

        # derive the signing key
        nodepath = [_U2F_KEY_PATH] + list(path)
        node = seed.derive_node_without_passphrase(nodepath, "nist256p1")

        # second half of keyhandle is a hmac of rp_id_hash and keypath
        mac = hmac(hmac.SHA256, node.private_key(), rp_id_hash)
        mac.update(keypath)

        # verify the hmac
        if not utils.consteq(mac.digest(), keyhandle[32:]):
            if __debug__:
                log.warning(__name__, "invalid key handle")
            return None

        return node
Beispiel #5
0
    async def dispatch_DebugLinkDecision(
        ctx: wire.Context, msg: DebugLinkDecision
    ) -> None:

        if debuglink_decision_chan.putters:
            log.warning(__name__, "DebugLinkDecision queue is not empty")

        debuglink_decision_chan.publish(msg)
def _finalize_default(task: loop.Task, value: Any) -> None:
    global default_task

    if default_task is task:
        if __debug__:
            log.debug(__name__, "default closed")
        default_task = None
    else:
        if __debug__:
            log.warning(
                __name__,
                "default task does not match: task=%s, default_task=%s",
                task,
                default_task,
            )
Beispiel #7
0
    async def dispatch_DebugLinkDecision(ctx: wire.Context,
                                         msg: DebugLinkDecision) -> None:
        if debuglink_decision_chan.putters:
            log.warning(__name__, "DebugLinkDecision queue is not empty")

        if msg.x is not None:
            evt_down = io.TOUCH_START, msg.x, msg.y
            evt_up = io.TOUCH_END, msg.x, msg.y
            loop.synthetic_events.append((io.TOUCH, evt_down))
            loop.synthetic_events.append((io.TOUCH, evt_up))
        else:
            debuglink_decision_chan.publish(msg)

        if msg.wait:
            loop.schedule(return_layout_change(ctx))
Beispiel #8
0
def _handle_unexpected(session_id, msg_type, data_len):
    log.warning(__name__, 'session %x: skip type %d, len %d', session_id,
                msg_type, data_len)

    # read the message in full
    try:
        while True:
            yield
    except EOFError:
        pass

    # respond with an unknown message error
    from trezor.messages.Failure import Failure
    from trezor.messages.FailureType import UnexpectedMessage
    failure = Failure(code=UnexpectedMessage, message='Unexpected message')
    failure = Failure.dumps(failure)
    sessions.get_codec(session_id).encode(session_id,
                                          Failure.MESSAGE_WIRE_TYPE, failure,
                                          _write_report)
Beispiel #9
0
def msg_register(req: Msg) -> Cmd:
    global _state
    global _lastreq

    from apps.common import storage

    if not storage.is_initialized():
        log.warning(__name__, 'not initialized')
        return msg_error(req.cid, _SW_CONDITIONS_NOT_SATISFIED)

    # check length of input data
    if len(req.data) != 64:
        log.warning(__name__, '_SW_WRONG_LENGTH req.data')
        return msg_error(req.cid, _SW_WRONG_LENGTH)

    # parse challenge and app_id
    chal = req.data[:32]
    app_id = req.data[32:]

    # check equality with last request
    if _lastreq is None or _lastreq.__dict__ != req.__dict__:
        if _state is not None:
            _state.kill()
        _state = None
        _lastreq = req

    # wait for a button or continue
    if _state is not None and utime.ticks_ms() > _state.deadline_ms:
        _state.kill()
        _state = None
    if _state is None:
        _state = ConfirmState(_CONFIRM_REGISTER, app_id)
        _state.fork()
    if _state.confirmed is None:
        log.info(__name__, 'waiting for button')
        return msg_error(req.cid, _SW_CONDITIONS_NOT_SATISFIED)
    _state = None

    buf = msg_register_sign(chal, app_id)

    return Cmd(req.cid, _CMD_MSG, buf)
Beispiel #10
0
    async def dispatch_DebugLinkDecision(ctx: wire.Context,
                                         msg: DebugLinkDecision) -> None:
        from trezor import io

        if debuglink_decision_chan.putters:
            log.warning(__name__, "DebugLinkDecision queue is not empty")

        if msg.x is not None and msg.y is not None:
            evt_down = io.TOUCH_START, msg.x, msg.y
            evt_up = io.TOUCH_END, msg.x, msg.y
            loop.synthetic_events.append((io.TOUCH, evt_down))
            if msg.hold_ms is not None:
                loop.schedule(touch_hold(msg.x, msg.y, msg.hold_ms))
            else:
                loop.synthetic_events.append((io.TOUCH, evt_up))
        else:
            debuglink_decision_chan.publish(msg)

        if msg.wait:
            storage.layout_watcher = LAYOUT_WATCHER_LAYOUT
            loop.schedule(return_layout_change())
Beispiel #11
0
def msg_register(req: Msg, state: ConfirmState) -> Cmd:
    from apps.common import storage

    if not storage.is_initialized():
        if __debug__:
            log.warning(__name__, "not initialized")
        return msg_error(req.cid, _SW_CONDITIONS_NOT_SATISFIED)

    # check length of input data
    if len(req.data) != 64:
        if __debug__:
            log.warning(__name__, "_SW_WRONG_LENGTH req.data")
        return msg_error(req.cid, _SW_WRONG_LENGTH)

    # parse challenge and app_id
    chal = req.data[:32]
    app_id = req.data[32:]

    # check equality with last request
    if not state.compare(_CONFIRM_REGISTER, req.data):
        if not state.setup(_CONFIRM_REGISTER, req.data, app_id):
            return msg_error(req.cid, _SW_CONDITIONS_NOT_SATISFIED)
    state.keepalive()

    # wait for a button or continue
    if not state.confirmed:
        if __debug__:
            log.info(__name__, "waiting for button")
        return msg_error(req.cid, _SW_CONDITIONS_NOT_SATISFIED)

    # sign the registration challenge and return
    if __debug__:
        log.info(__name__, "signing register")
    buf = msg_register_sign(chal, app_id)

    state.reset()

    return Cmd(req.cid, _CMD_MSG, buf)
Beispiel #12
0
async def session_handler(iface: WireInterface, sid: int) -> None:
    reader = None
    ctx = Context(iface, sid)
    while True:
        try:
            # wait for new message, if needed, and find handler
            if not reader:
                reader = ctx.make_reader()
                await reader.aopen()
            try:
                handler, args = workflow_handlers[reader.type]
            except KeyError:
                handler, args = unexpected_msg, ()

            m = utils.unimport_begin()
            w = handler(ctx, reader, *args)
            try:
                workflow.onstart(w)
                await w
            finally:
                workflow.onclose(w)
                utils.unimport_end(m)

        except UnexpectedMessageError as exc:
            # retry with opened reader from the exception
            reader = exc.reader
            continue
        except Error as exc:
            # we log wire.Error as warning, not as exception
            if __debug__:
                log.warning(__name__, "failure: %s", exc.message)
        except Exception as exc:
            # sessions are never closed by raised exceptions
            if __debug__:
                log.exception(__name__, exc)

        # read new message in next iteration
        reader = None
Beispiel #13
0
def dispatch_cmd(req: Cmd, state: ConfirmState) -> Cmd:
    if req.cmd == _CMD_MSG:
        m = req.to_msg()

        if m.cla != 0:
            if __debug__:
                log.warning(__name__, "_SW_CLA_NOT_SUPPORTED")
            return msg_error(req.cid, _SW_CLA_NOT_SUPPORTED)

        if m.lc + _APDU_DATA > len(req.data):
            if __debug__:
                log.warning(__name__, "_SW_WRONG_LENGTH")
            return msg_error(req.cid, _SW_WRONG_LENGTH)

        if m.ins == _MSG_REGISTER:
            if __debug__:
                log.debug(__name__, "_MSG_REGISTER")
            return msg_register(m, state)
        elif m.ins == _MSG_AUTHENTICATE:
            if __debug__:
                log.debug(__name__, "_MSG_AUTHENTICATE")
            return msg_authenticate(m, state)
        elif m.ins == _MSG_VERSION:
            if __debug__:
                log.debug(__name__, "_MSG_VERSION")
            return msg_version(m)
        else:
            if __debug__:
                log.warning(__name__, "_SW_INS_NOT_SUPPORTED: %d", m.ins)
            return msg_error(req.cid, _SW_INS_NOT_SUPPORTED)

    elif req.cmd == _CMD_INIT:
        if __debug__:
            log.debug(__name__, "_CMD_INIT")
        return cmd_init(req)
    elif req.cmd == _CMD_PING:
        if __debug__:
            log.debug(__name__, "_CMD_PING")
        return req
    elif req.cmd == _CMD_WINK:
        if __debug__:
            log.debug(__name__, "_CMD_WINK")
        return req
    else:
        if __debug__:
            log.warning(__name__, "_ERR_INVALID_CMD: %d", req.cmd)
        return cmd_error(req.cid, _ERR_INVALID_CMD)
Beispiel #14
0
async def read_cmd(iface: io.HID) -> Cmd:
    desc_init = frame_init()
    desc_cont = frame_cont()
    read = loop.wait(iface.iface_num() | io.POLL_READ)

    buf = await read

    ifrm = overlay_struct(buf, desc_init)
    bcnt = ifrm.bcnt
    data = ifrm.data
    datalen = len(data)
    seq = 0

    if ifrm.cmd & _TYPE_MASK == _TYPE_CONT:
        # unexpected cont packet, abort current msg
        if __debug__:
            log.warning(__name__, "_TYPE_CONT")
        return None

    if datalen < bcnt:
        databuf = bytearray(bcnt)
        utils.memcpy(databuf, 0, data, 0, bcnt)
        data = databuf
    else:
        data = data[:bcnt]

    while datalen < bcnt:
        buf = await read

        cfrm = overlay_struct(buf, desc_cont)

        if cfrm.seq == _CMD_INIT:
            # _CMD_INIT frame, cancels current channel
            ifrm = overlay_struct(buf, desc_init)
            data = ifrm.data[: ifrm.bcnt]
            break

        if cfrm.cid != ifrm.cid:
            # cont frame for a different channel, reply with BUSY and skip
            if __debug__:
                log.warning(__name__, "_ERR_CHANNEL_BUSY")
            await send_cmd(cmd_error(cfrm.cid, _ERR_CHANNEL_BUSY), iface)
            continue

        if cfrm.seq != seq:
            # cont frame for this channel, but incorrect seq number, abort
            # current msg
            if __debug__:
                log.warning(__name__, "_ERR_INVALID_SEQ")
            await send_cmd(cmd_error(cfrm.cid, _ERR_INVALID_SEQ), iface)
            return None

        datalen += utils.memcpy(data, datalen, cfrm.data, 0, bcnt - datalen)
        seq += 1

    return Cmd(ifrm.cid, ifrm.cmd, data)
Beispiel #15
0
def dispatch_cmd(req: Cmd) -> Cmd:
    if req.cmd == _CMD_MSG:
        m = req.to_msg()

        if m.cla != 0:
            log.warning(__name__, '_SW_CLA_NOT_SUPPORTED')
            return msg_error(req.cid, _SW_CLA_NOT_SUPPORTED)

        if m.lc + _APDU_DATA > len(req.data):
            log.warning(__name__, '_SW_WRONG_LENGTH')
            return msg_error(req.cid, _SW_WRONG_LENGTH)

        if m.ins == _MSG_REGISTER:
            log.debug(__name__, '_MSG_REGISTER')
            return msg_register(m)
        elif m.ins == _MSG_AUTHENTICATE:
            log.debug(__name__, '_MSG_AUTHENTICATE')
            return msg_authenticate(m)
        elif m.ins == _MSG_VERSION:
            log.debug(__name__, '_MSG_VERSION')
            return msg_version(m)
        else:
            log.warning(__name__, '_SW_INS_NOT_SUPPORTED: %d', m.ins)
            return msg_error(req.cid, _SW_INS_NOT_SUPPORTED)

    elif req.cmd == _CMD_INIT:
        log.debug(__name__, '_CMD_INIT')
        return cmd_init(req)
    elif req.cmd == _CMD_PING:
        log.debug(__name__, '_CMD_PING')
        return req
    elif req.cmd == _CMD_WINK:
        log.debug(__name__, '_CMD_WINK')
        return req
    else:
        log.warning(__name__, '_ERR_INVALID_CMD: %d', req.cmd)
        return cmd_error(req.cid, _ERR_INVALID_CMD)
Beispiel #16
0
def _session_unknown(session_id, report_data):
    log.warning(__name__, 'report on unknown session %x', session_id)
Beispiel #17
0
def msg_authenticate(req: Msg, state: ConfirmState) -> Cmd:
    from apps.common import storage

    if not storage.is_initialized():
        if __debug__:
            log.warning(__name__, "not initialized")
        return msg_error(req.cid, _SW_CONDITIONS_NOT_SATISFIED)

    # we need at least keyHandleLen
    if len(req.data) <= _REQ_CMD_AUTHENTICATE_KHLEN:
        if __debug__:
            log.warning(__name__, "_SW_WRONG_LENGTH req.data")
        return msg_error(req.cid, _SW_WRONG_LENGTH)

    # check keyHandleLen
    khlen = req.data[_REQ_CMD_AUTHENTICATE_KHLEN]
    if khlen != 64:
        if __debug__:
            log.warning(__name__, "_SW_WRONG_LENGTH khlen")
        return msg_error(req.cid, _SW_WRONG_LENGTH)

    auth = overlay_struct(req.data, req_cmd_authenticate(khlen))

    # check the keyHandle and generate the signing key
    node = msg_authenticate_genkey(auth.appId, auth.keyHandle, "<8L")
    if node is None:
        # prior to firmware version 2.0.8, keypath was serialized in a
        # big-endian manner, instead of little endian, like in trezor-mcu.
        # try to parse it as big-endian now and check the HMAC.
        node = msg_authenticate_genkey(auth.appId, auth.keyHandle, ">8L")
    if node is None:
        # specific error logged in msg_authenticate_genkey
        return msg_error(req.cid, _SW_WRONG_DATA)

    # if _AUTH_CHECK_ONLY is requested, return, because keyhandle has been checked already
    if req.p1 == _AUTH_CHECK_ONLY:
        if __debug__:
            log.info(__name__, "_AUTH_CHECK_ONLY")
        return msg_error(req.cid, _SW_CONDITIONS_NOT_SATISFIED)

    # from now on, only _AUTH_ENFORCE is supported
    if req.p1 != _AUTH_ENFORCE:
        if __debug__:
            log.info(__name__, "_AUTH_ENFORCE")
        return msg_error(req.cid, _SW_WRONG_DATA)

    # check equality with last request
    if not state.compare(_CONFIRM_AUTHENTICATE, req.data):
        if not state.setup(_CONFIRM_AUTHENTICATE, req.data, auth.appId):
            return msg_error(req.cid, _SW_CONDITIONS_NOT_SATISFIED)
    state.keepalive()

    # wait for a button or continue
    if not state.confirmed:
        if __debug__:
            log.info(__name__, "waiting for button")
        return msg_error(req.cid, _SW_CONDITIONS_NOT_SATISFIED)

    # sign the authentication challenge and return
    if __debug__:
        log.info(__name__, "signing authentication")
    buf = msg_authenticate_sign(auth.chal, auth.appId, node.private_key())

    state.reset()

    return Cmd(req.cid, _CMD_MSG, buf)
Beispiel #18
0
def msg_authenticate(req: Msg) -> Cmd:

    global _state
    global _lastreq

    from apps.common import storage

    if not storage.is_initialized():
        log.warning(__name__, 'not initialized')
        return msg_error(req.cid, _SW_CONDITIONS_NOT_SATISFIED)

    # we need at least keyHandleLen
    if len(req.data) <= _REQ_CMD_AUTHENTICATE_KHLEN:
        log.warning(__name__, '_SW_WRONG_LENGTH req.data')
        return msg_error(req.cid, _SW_WRONG_LENGTH)

    # check keyHandleLen
    khlen = req.data[_REQ_CMD_AUTHENTICATE_KHLEN]
    if khlen != 64:
        log.warning(__name__, '_SW_WRONG_LENGTH khlen')
        return msg_error(req.cid, _SW_WRONG_LENGTH)

    auth = overlay_struct(req.data, req_cmd_authenticate(khlen))

    # check the keyHandle and generate the signing key
    node = msg_authenticate_genkey(auth.appId, auth.keyHandle)
    if node is None:
        # specific error logged in msg_authenticate_genkey
        return msg_error(req.cid, _SW_WRONG_DATA)

    # if _AUTH_CHECK_ONLY is requested, return, because keyhandle has been checked already
    if req.p1 == _AUTH_CHECK_ONLY:
        log.info(__name__, '_AUTH_CHECK_ONLY')
        return msg_error(req.cid, _SW_CONDITIONS_NOT_SATISFIED)

    # from now on, only _AUTH_ENFORCE is supported
    if req.p1 != _AUTH_ENFORCE:
        log.info(__name__, '_AUTH_ENFORCE')
        return msg_error(req.cid, _SW_WRONG_DATA)

    # check equality with last request
    if _lastreq is None or _lastreq.__dict__ != req.__dict__:
        if _state is not None:
            _state.kill()
        _state = None
        _lastreq = req

    # wait for a button or continue
    if _state is not None and utime.ticks_ms() > _state.deadline_ms:
        _state.kill()
        _state = None
    if _state is None:
        _state = ConfirmState(_CONFIRM_AUTHENTICATE, auth.appId)
        _state.fork()
    if _state.confirmed is None:
        log.info(__name__, 'waiting for button')
        return msg_error(req.cid, _SW_CONDITIONS_NOT_SATISFIED)
    _state = None

    buf = msg_authenticate_sign(auth.chal, auth.appId, node.private_key())

    return Cmd(req.cid, _CMD_MSG, buf)
Beispiel #19
0
def msg_authenticate(req: Msg, state: ConfirmState) -> Cmd:
    from apps.common import storage

    if not storage.is_initialized():
        if __debug__:
            log.warning(__name__, 'not initialized')
        return msg_error(req.cid, _SW_CONDITIONS_NOT_SATISFIED)

    # we need at least keyHandleLen
    if len(req.data) <= _REQ_CMD_AUTHENTICATE_KHLEN:
        if __debug__:
            log.warning(__name__, '_SW_WRONG_LENGTH req.data')
        return msg_error(req.cid, _SW_WRONG_LENGTH)

    # check keyHandleLen
    khlen = req.data[_REQ_CMD_AUTHENTICATE_KHLEN]
    if khlen != 64:
        if __debug__:
            log.warning(__name__, '_SW_WRONG_LENGTH khlen')
        return msg_error(req.cid, _SW_WRONG_LENGTH)

    auth = overlay_struct(req.data, req_cmd_authenticate(khlen))

    # check the keyHandle and generate the signing key
    node = msg_authenticate_genkey(auth.appId, auth.keyHandle)
    if node is None:
        # specific error logged in msg_authenticate_genkey
        return msg_error(req.cid, _SW_WRONG_DATA)

    # if _AUTH_CHECK_ONLY is requested, return, because keyhandle has been checked already
    if req.p1 == _AUTH_CHECK_ONLY:
        if __debug__:
            log.info(__name__, '_AUTH_CHECK_ONLY')
        return msg_error(req.cid, _SW_CONDITIONS_NOT_SATISFIED)

    # from now on, only _AUTH_ENFORCE is supported
    if req.p1 != _AUTH_ENFORCE:
        if __debug__:
            log.info(__name__, '_AUTH_ENFORCE')
        return msg_error(req.cid, _SW_WRONG_DATA)

    # check equality with last request
    if not state.compare(_CONFIRM_AUTHENTICATE, req.data):
        if not state.setup(_CONFIRM_AUTHENTICATE, req.data, auth.appId):
            return msg_error(req.cid, _SW_CONDITIONS_NOT_SATISFIED)
    state.keepalive()

    # wait for a button or continue
    if not state.confirmed:
        if __debug__:
            log.info(__name__, 'waiting for button')
        return msg_error(req.cid, _SW_CONDITIONS_NOT_SATISFIED)

    # sign the authentication challenge and return
    if __debug__:
        log.info(__name__, 'signing authentication')
    buf = msg_authenticate_sign(auth.chal, auth.appId, node.private_key())

    state.reset()

    return Cmd(req.cid, _CMD_MSG, buf)