Exemplo n.º 1
0
    async def render_post(self, request):
        query = query_split(request)

        # this is not deduplicated with update_params in full because that code
        # path is triggered later when the response was already sent

        if 'ep' not in query:
            raise error.BadRequest("ep argument missing")

        if 'lt' in query:
            try:
                _ = int(query['lt'])
            except ValueError:
                raise error.BadRequest("lt must be numeric")

        if 'con' not in query:
            try:
                con = request.remote.uri
            except error.UnparsableMessage:
                raise error.BadRequest("explicit con required")

        asyncio.Task(
            self.process_request(
                network_remote=request.remote,
                registration_parameters=query,
            ))

        return aiocoap.Message(code=aiocoap.CHANGED)
Exemplo n.º 2
0
Arquivo: rd.py Projeto: ltn22/aiocoap
    async def render_post(self, request):
        query = query_split(request)

        # this is not deduplicated with update_params in full because that code
        # path is triggered later when the response was already sent

        if 'ep' not in query:
            raise error.BadRequest("ep argument missing")

        if 'lt' in query:
            try:
                # just trying out whether it can be constructed to err out
                # early and meaningfully
                int(query['lt'])
            except ValueError:
                raise error.BadRequest("lt must be numeric")

        if 'base' in query:
            raise error.BadRequest("base is not allowed in simple registrations")

        try:
            # just trying out whether it can be constructed to err out in
            # time (under the current model of "respond early, ask later")
            request.remote.uri
        except error.UnparsableMessage:
            raise error.BadRequest("explicit base required")

        asyncio.Task(self.process_request(
                network_remote=request.remote,
                registration_parameters=query,
            ))

        return aiocoap.Message(code=aiocoap.CHANGED)
Exemplo n.º 3
0
Arquivo: rd.py Projeto: ltn22/aiocoap
        def update_params(self, network_remote, registration_parameters, is_initial=False):
            """Set the registration_parameters from the parsed query arguments,
            update any effects of them, and and trigger any observation
            observation updates if requried (the typical ones don't because
            their registration_parameters are {} and all it does is restart the
            lifetime counter)"""

            if (is_initial or not self.base_is_explicit) and 'base' not in \
                    registration_parameters:
                # check early for validity to avoid side effects of requests
                # answered with 4.xx
                try:
                    network_base = network_remote.uri
                except error.AnonymousHost:
                    raise error.BadRequest("explicit base required")

            if is_initial:
                self.registration_parameters = registration_parameters
                self.lt = 90000
                if 'base' not in registration_parameters:
                    self.base = network_base
                    self.base_is_explicit = False

                # technically might be a re-registration, but we can't catch that at this point
                actual_change = True
            else:
                if 'd' in registration_parameters or 'ep' in registration_parameters:
                    raise error.BadRequest("Parameters 'd' and 'ep' can not be updated")

                actual_change = any(v != self.registration_parameters[k] for (k, v) in registration_parameters.items())

                self.registration_parameters = dict(self.registration_parameters, **registration_parameters)

            if 'lt' in registration_parameters:
                try:
                    self.lt = int(registration_parameters['lt'])
                except ValueError:
                    raise error.BadRequest("lt must be numeric")

            if 'base' in registration_parameters:
                self.base = registration_parameters['base']
                self.base_is_explicit = True

            if not self.base_is_explicit and self.base != network_base:
                self.base = network_base
                actual_change = True

            if is_initial:
                self._set_timeout()
            else:
                self.refresh_timeout()

            if actual_change:
                self._update_cb()
Exemplo n.º 4
0
        def update_params(self,
                          network_con,
                          registration_parameters,
                          is_initial=False):
            """Set the registration_parameters from the parsed query arguments,
            update any effects of them, and and trigger any observation
            observation updates if requried (the typical ones don't because
            their registration_parameters are {} and all it does is restart the
            lifetime counter)"""

            if is_initial:
                self.registration_parameters = registration_parameters
                self.lt = 86400
                self.con_is_explicit = False
                self.con = network_con

                # technically might be a re-registration, but we can't catch that at this point
                actual_change = True
            else:
                if 'd' in registration_parameters or 'ep' in registration_parameters:
                    raise error.BadRequest(
                        "Parameters 'd' and 'ep' can not be updated")

                actual_change = any(v != self.registration_parameters[k]
                                    for (k,
                                         v) in registration_parameters.items())

                self.registration_parameters = dict(
                    self.registration_parameters, **registration_parameters)

            if 'lt' in registration_parameters:
                try:
                    self.lt = int(registration_parameters['lt'])
                except ValueError:
                    raise error.BadRequest("lt must be numeric")

            if 'con' in registration_parameters:
                self.con = registration_parameters['con']
                self.con_is_explicit = True

            if not self.con_is_explicit and self.con != network_con:
                self.con = network_con
                actual_change = True

            if is_initial:
                self._set_timeout()
            else:
                self.refresh_timeout()

            if actual_change:
                self._update_cb()
Exemplo n.º 5
0
    def _basic_trust_model(self, payload: StereotypeRequest):
        if payload.tags.device_class.is_iot():
            # Don't expect IoT to be good at responding to or executing tasks
            task_submission = [0, 1]
            task_result = [0, 1]

        else:
            # All classes of nodes are expected to be good at acknowledging tasks
            task_submission = [20, 1]

            # More capable devices are expected to be better a delivering a result
            if payload.tags.device_class == DeviceClass.RASPBERRY_PI:
                task_result = [8, 1]

            elif payload.tags.device_class == DeviceClass.PHONE:
                task_result = [12, 1]

            elif payload.tags.device_class == DeviceClass.LAPTOP:
                task_result = [16, 1]

            elif payload.tags.device_class == DeviceClass.SERVER:
                task_result = [20, 1]

            else:
                raise error.BadRequest(
                    f"Unknown device class {payload.tags.device_class}")

        return [task_submission, task_result]
Exemplo n.º 6
0
Arquivo: rd.py Projeto: ltn22/aiocoap
    def initialize_endpoint(self, network_remote, registration_parameters):
        try:
            ep = registration_parameters['ep']
        except KeyError:
            raise error.BadRequest("ep argument missing")
        d = registration_parameters.get('d', None)

        key = (ep, d)

        try:
            oldreg = self._endpoint_registrations_by_key[key]
        except KeyError:
            path = self._new_pathtail()
        else:
            path = oldreg.path[len(self.entity_prefix):]
            oldreg.delete()

        # this was the brutal way towards idempotency (delete and re-create).
        # if any actions based on that are implemented here, they have yet to
        # decide wheter they'll treat idempotent recreations like deletions or
        # just ignore them unless something otherwise unchangeable (ep, d)
        # changes.

        def delete():
            del self._entities_by_pathtail[path]
            del self._endpoint_registrations_by_key[key]

        reg = self.Registration(self.entity_prefix + path, network_remote, delete,
                self._updated_state, registration_parameters)

        self._endpoint_registrations_by_key[key] = reg
        self._entities_by_pathtail[path] = reg

        return reg
Exemplo n.º 7
0
    async def process_request(self, network_remote, registration_parameters):
        if 'proxy' not in registration_parameters:
            try:
                network_base = network_remote.uri
            except error.AnonymousHost:
                raise error.BadRequest("explicit base required")

            fetch_address = (network_base + '/.well-known/core')
            get = aiocoap.Message(uri=fetch_address)
        else:
            # ignoring that there might be a based present, that will err later
            get = aiocoap.Message(uri_path=['.well-known', 'core'])
            get.remote = network_remote

        get.code = aiocoap.GET
        get.opt.accept = media_types_rev['application/link-format']

        # not trying to catch anything here -- the errors are most likely well renderable into the final response
        response = await self.context.request(get).response_raising
        links = link_format_from_message(response)

        if self.registration_warning:
            # Conveniently placed so it could be changed to something setting
            # additional registration_parameters instead
            logging.warning("Warning from registration: %s",
                            self.registration_warning)
        registration = self.common_rd.initialize_endpoint(
            network_remote, registration_parameters)
        registration.links = links
Exemplo n.º 8
0
Arquivo: rd.py Projeto: ltn22/aiocoap
    async def render_post(self, request):
        self._update_params(request)

        if request.opt.content_format is not None or request.payload:
            raise error.BadRequest("Registration update with body not specified")

        return aiocoap.Message(code=aiocoap.CHANGED)
Exemplo n.º 9
0
    async def render_get(self, request):
        """Return stereotype information for the requested Edge server"""
        if request.opt.content_format != media_types_rev['application/cbor']:
            raise error.UnsupportedContentFormat()

        payload = StereotypeRequest.decode(request.payload)

        if payload.model == TrustModel.No:
            result = self._no_trust_model(payload)

        elif payload.model == TrustModel.Basic:
            result = self._basic_trust_model(payload)

        elif payload.model == TrustModel.Continuous:
            result = self._continuous_trust_model(payload)

        elif payload.model == TrustModel.ChallengeResponse:
            result = self._challenge_response_trust_model(payload)

        else:
            raise error.BadRequest(f"Unknown trust model {payload.model}")

        result = StereotypeResponse(payload.model, payload.tags, result)
        result_payload = cbor2.dumps(result.encode())

        return aiocoap.Message(
            payload=result_payload,
            code=codes.CONTENT,
            content_format=media_types_rev['application/cbor'])
Exemplo n.º 10
0
def link_format_from_message(message):
    try:
        if message.opt.content_format == aiocoap.numbers.media_types_rev['application/link-format']:
            return link_header.parse(message.payload.decode('utf8'))
        # FIXME this should support json/cbor too
        else:
            raise error.UnsupportedMediaType()
    except (UnicodeDecodeError, link_header.ParseException):
        raise error.BadRequest()
Exemplo n.º 11
0
def pop_single_arg(query, name):
    """Out of query which is the output of query_split, pick the single value
    at the key name, raise a suitable BadRequest on error, or return None if
    nothing is there. The value is removed from the query dictionary."""

    if name not in query:
        return None
    if len(query[name]) > 1:
        raise error.BadRequest("Multiple values for %r" % name)
    return query.pop(name)[0]
Exemplo n.º 12
0
Arquivo: rd.py Projeto: ltn22/aiocoap
def _paginate(candidates, query):
    try:
        candidates = list(candidates)
        if 'page' in query:
            candidates = candidates[int(query['page']) * int(query['count']):]
        if 'count' in query:
            candidates = candidates[:int(query['count'])]
    except (KeyError, ValueError):
        raise error.BadRequest("page requires count, and both must be ints")

    return candidates
Exemplo n.º 13
0
Arquivo: rd.py Projeto: ltn22/aiocoap
def link_format_from_message(message):
    try:
        if message.opt.content_format == media_types_rev['application/link-format']:
            return parse(message.payload.decode('utf8'))
        elif message.opt.content_format == media_types_rev['application/link-format+json']:
            return LinkFormat.from_json_string(message.payload.decode('utf8'))
        elif message.opt.content_format == media_types_rev['application/link-format+cbor']:
            return LinkFormat.from_cbor_bytes(message.payload)
        else:
            raise error.UnsupportedMediaType()
    except (UnicodeDecodeError, link_header.ParseException):
        raise error.BadRequest()
Exemplo n.º 14
0
    async def render_post(self, request):
        query = query_split(request)

        if 'base' in query:
            raise error.BadRequest(
                "base is not allowed in simple registrations")

        await self.process_request(
            network_remote=request.remote,
            registration_parameters=query,
        )

        return aiocoap.Message(code=aiocoap.CHANGED)
Exemplo n.º 15
0
def _paginate(candidates, query):
    page = pop_single_arg(query, 'page')
    count = pop_single_arg(query, 'count')

    try:
        candidates = list(candidates)
        if page is not None:
            candidates = candidates[int(page) * int(count):]
        if count is not None:
            candidates = candidates[:int(count)]
    except (KeyError, ValueError):
        raise error.BadRequest("page requires count, and both must be ints")

    return candidates
Exemplo n.º 16
0
def link_format_from_message(message):
    """Convert a response message into a LinkFormat object

    This expects an explicit media type set on the response (or was explicitly requested)
    """
    certain_format = message.opt.content_format
    if certain_format is None:
        certain_format = message.request.opt.accept
    try:
        if certain_format == ContentFormat.LINKFORMAT:
            return parse(message.payload.decode('utf8'))
        else:
            raise error.UnsupportedMediaType()
    except (UnicodeDecodeError, link_header.ParseException):
        raise error.BadRequest()
Exemplo n.º 17
0
def link_format_from_message(message):
    """Convert a response message into a LinkFormat object

    This expects an explicit media type set on the response (or was explicitly requested)
    """
    certain_format = message.opt.content_format
    if certain_format is None:
        certain_format = message.request.opt.accept
    try:
        if certain_format == media_types_rev['application/link-format']:
            return parse(message.payload.decode('utf8'))
        elif certain_format == media_types_rev['application/link-format+json']:
            return LinkFormat.from_json_string(message.payload.decode('utf8'))
        elif certain_format == media_types_rev['application/link-format+cbor']:
            return LinkFormat.from_cbor_bytes(message.payload)
        else:
            raise error.UnsupportedMediaType()
    except (UnicodeDecodeError, link_header.ParseException):
        raise error.BadRequest()
Exemplo n.º 18
0
    def initialize_endpoint(self, network_remote, registration_parameters):
        # copying around for later use in static, but not checking again
        # because reading them from the original will already have screamed by
        # the time this is used
        ep_and_d = {
            k: v
            for (k, v) in registration_parameters.items() if k in ('ep', 'd')
        }

        ep = pop_single_arg(registration_parameters, 'ep')
        if ep is None:
            raise error.BadRequest("ep argument missing")
        d = pop_single_arg(registration_parameters, 'd')

        key = (ep, d)

        try:
            oldreg = self._by_key[key]
        except KeyError:
            path = self._new_pathtail()
        else:
            path = oldreg.path[len(self.entity_prefix):]
            oldreg.delete()

        # this was the brutal way towards idempotency (delete and re-create).
        # if any actions based on that are implemented here, they have yet to
        # decide wheter they'll treat idempotent recreations like deletions or
        # just ignore them unless something otherwise unchangeable (ep, d)
        # changes.

        def delete():
            del self._by_path[path]
            del self._by_key[key]

        reg = self.Registration(ep_and_d, self.entity_prefix + path,
                                network_remote, delete, self._updated_state,
                                registration_parameters)

        self._by_key[key] = reg
        self._by_path[path] = reg

        return reg
Exemplo n.º 19
0
    async def process_request(self, network_remote, registration_parameters):
        if 'proxy' not in registration_parameters:
            try:
                network_base = network_remote.uri
            except error.AnonymousHost:
                raise error.BadRequest("explicit base required")

            fetch_address = (network_base + '/.well-known/core')
            get = aiocoap.Message(code=aiocoap.GET, uri=fetch_address)
        else:
            # ignoring that there might be a based present, that will err later
            get = aiocoap.Message(code=aiocoap.GET,
                                  uri_path=['.well-known', 'core'])
            get.remote = network_remote

        # not trying to catch anything here -- the errors are most likely well renderable into the final response
        response = await self.context.request(get).response_raising
        links = link_format_from_message(response)

        registration = self.common_rd.initialize_endpoint(
            network_remote, registration_parameters)
        registration.links = links
Exemplo n.º 20
0
        def update_params(self,
                          network_remote,
                          registration_parameters,
                          is_initial=False):
            """Set the registration_parameters from the parsed query arguments,
            update any effects of them, and and trigger any observation
            observation updates if requried (the typical ones don't because
            their registration_parameters are {} and all it does is restart the
            lifetime counter)"""

            if any(k in ('ep', 'd') for k in registration_parameters.keys()):
                # The ep and d of initial registrations are already popped out
                raise error.BadRequest(
                    "Parameters 'd' and 'ep' can not be updated")

            # Not in use class "R" or otherwise conflict with common parameters
            if any(k in ('page', 'count', 'rt', 'href', 'anchor')
                   for k in registration_parameters.keys()):
                raise error.BadRequest("Unsuitable parameter for registration")

            if (is_initial or not self.base_is_explicit) and 'base' not in \
                    registration_parameters:
                # check early for validity to avoid side effects of requests
                # answered with 4.xx
                try:
                    network_base = network_remote.uri
                except error.AnonymousHost:
                    raise error.BadRequest("explicit base required")

            if is_initial:
                # technically might be a re-registration, but we can't catch that at this point
                actual_change = True
            else:
                actual_change = False

            # Don't act while still checking
            set_lt = None
            set_base = None

            if 'lt' in registration_parameters:
                try:
                    set_lt = int(pop_single_arg(registration_parameters, 'lt'))
                except ValueError:
                    raise error.BadRequest("lt must be numeric")

            if 'base' in registration_parameters:
                set_base = pop_single_arg(registration_parameters, 'base')

            if set_lt is not None and self.lt != set_lt:
                actual_change = True
                self.lt = set_lt
            if set_base is not None and (is_initial or self.base != set_base):
                actual_change = True
                self.base = set_base
                self.base_is_explicit = True

            if not self.base_is_explicit and (is_initial
                                              or self.base != network_base):
                self.base = network_base
                actual_change = True

            if any(v != self.registration_parameters.get(k)
                   for (k, v) in registration_parameters.items()):
                self.registration_parameters.update(registration_parameters)
                actual_change = True

            if is_initial:
                self._set_timeout()
            else:
                self.refresh_timeout()

            if actual_change:
                self._update_cb()
Exemplo n.º 21
0
    async def render_to_pipe(self, pipe):
        request = pipe.request

        try:
            unprotected = oscore.verify_start(request)
        except oscore.NotAProtectedMessage:
            # ie. if no object_seccurity present
            await self._inner_site.render_to_pipe(pipe)
            return

        if request.code not in (FETCH, POST):
            raise error.MethodNotAllowed

        try:
            sc = self.server_credentials.find_oscore(unprotected)
        except KeyError:
            if request.mtype == aiocoap.CON:
                raise error.Unauthorized("Security context not found")
            else:
                return

        try:
            unprotected, seqno = sc.unprotect(request)
        except error.RenderableError as e:
            # Note that this is flying out of the unprotection (ie. the
            # security context), which is trusted to not leak unintended
            # information in unencrypted responses. (By comparison, a
            # renderable exception flying out of a user
            # render_to_pipe could only be be rendered to a
            # protected message, and we'd need to be weary of rendering errors
            # during to_message as well).
            #
            # Note that this clause is not a no-op: it protects the 4.01 Echo
            # recovery exception (which is also a ReplayError) from being
            # treated as such.
            raise e
        # The other errors could be ported thee but would need some better NoResponse handling.
        except oscore.ReplayError:
            if request.mtype == aiocoap.CON:
                pipe.add_response(aiocoap.Message(code=aiocoap.UNAUTHORIZED,
                                                  max_age=0,
                                                  payload=b"Replay detected"),
                                  is_last=True)
            return
        except oscore.DecodeError:
            if request.mtype == aiocoap.CON:
                raise error.BadOption("Failed to decode COSE")
            else:
                return
        except oscore.ProtectionInvalid:
            if request.mtype == aiocoap.CON:
                raise error.BadRequest("Decryption failed")
            else:
                return

        unprotected.remote = OSCOREAddress(sc, request.remote)

        self.log.debug("Request %r was unprotected into %r", request,
                       unprotected)

        sc = sc.context_for_response()

        inner_pipe = aiocoap.pipe.IterablePipe(unprotected)
        pr_that_can_take_errors = aiocoap.pipe.error_to_message(
            inner_pipe, self.log)
        # FIXME: do not create a task but run this in here (can this become a
        # feature of the aiterable PR?)
        aiocoap.pipe.run_driving_pipe(
            pr_that_can_take_errors,
            self._inner_site.render_to_pipe(inner_pipe),
            name="OSCORE response rendering for %r" % unprotected,
        )

        async for event in inner_pipe:
            if event.exception is not None:
                # These are expected to be rare in handlers
                #
                # FIXME should we try to render them? (See also
                # run_driving_pipe). Just raising them
                # would definitely be bad, as they might be renderable and
                # then would hit the outer message.
                self.log.warn(
                    "Turning error raised from renderer into nondescript protected error %r",
                    event.exception)
                message = aiocoap.Message(code=aiocoap.INTERNAL_SERVER_ERROR)
                is_last = True
            else:
                message = event.message
                is_last = event.is_last

            # FIXME: Around several places in the use of pipe (and
            # now even here), non-final events are hard-coded as observations.
            # This should shift toward the source telling, or the stream being
            # annotated as "eventually consistent resource states".
            if not is_last:
                message.opt.observe = 0

            protected_response, _ = sc.protect(message, seqno)
            if message.opt.observe is not None:
                # FIXME: should be done in protect, or by something else that
                # generally handles obs numbers better (sending the
                # oscore-reconstructed number is nice because it's consistent
                # with a proxy that doesn't want to keep a counter when it
                # knows it's OSCORE already), but starting this per obs with
                # zero (unless it was done on that token recently) would be
                # most efficient
                protected_response.opt.observe = sc.sender_sequence_number & 0xffffffff
            self.log.debug("Response %r was encrypted into %r", message,
                           protected_response)

            pipe.add_response(protected_response, is_last=is_last)
            if event.is_last:
                break
Exemplo n.º 22
0
    def initialize_endpoint(self, network_remote, registration_parameters):
        # copying around for later use in static, but not checking again
        # because reading them from the original will already have screamed by
        # the time this is used
        static_registration_parameters = {
            k: v
            for (k, v) in registration_parameters.items()
            if k in IMMUTABLE_PARAMETERS
        }

        ep = pop_single_arg(registration_parameters, 'ep')
        if ep is None:
            raise error.BadRequest("ep argument missing")
        d = pop_single_arg(registration_parameters, 'd')

        proxy = pop_single_arg(registration_parameters, 'proxy')

        if proxy is not None and proxy != 'on':
            raise error.BadRequest("Unsupported proxy value")

        key = (ep, d)

        if static_registration_parameters.pop('proxy', None):
            # FIXME: 'ondemand' is done unconditionally

            if not self.proxy_domain:
                raise error.BadRequest("Proxying not enabled")

            def is_usable(s):
                # Host names per RFC1123 (which is stricter than what RFC3986 would allow).
                #
                # Only supporting lowercase names as to avoid ambiguities due
                # to hostname capitalizatio normalization (otherwise it'd need
                # to be first-registered-first-served)
                return s and all(
                    x in string.ascii_lowercase + string.digits + '-'
                    for x in s)

            if not is_usable(ep) or (d is not None and not is_usable(d)):
                raise error.BadRequest(
                    "Proxying only supported for limited ep and d set (lowercase, digits, dash)"
                )

            proxy_host = ep
            if d is not None:
                proxy_host += '.' + d
            proxy_host = proxy_host + '.' + self.proxy_domain
        else:
            proxy_host = None

        # No more errors should fly out from below here, as side effects start now

        try:
            oldreg = self._by_key[key]
        except KeyError:
            path = self._new_pathtail()
        else:
            path = oldreg.path[len(self.entity_prefix):]
            oldreg.delete()

        # this was the brutal way towards idempotency (delete and re-create).
        # if any actions based on that are implemented here, they have yet to
        # decide wheter they'll treat idempotent recreations like deletions or
        # just ignore them unless something otherwise unchangeable (ep, d)
        # changes.

        def delete():
            del self._by_path[path]
            del self._by_key[key]
            self.proxy_active.pop(proxy_host)

        def setproxyremote(remote):
            self.proxy_active[proxy_host] = remote

        reg = self.Registration(static_registration_parameters,
                                self.entity_prefix + path, network_remote,
                                delete, self._updated_state,
                                registration_parameters, proxy_host,
                                setproxyremote)

        self._by_key[key] = reg
        self._by_path[path] = reg

        return reg
Exemplo n.º 23
0
    async def render(self, request):
        try:
            recipient_id, id_context = oscore.verify_start(request)
        except oscore.NotAProtectedMessage:
            # ie. if no object_seccurity present
            return await self._inner_site.render(request)

        try:
            sc = self.server_credentials.find_oscore(recipient_id, id_context)
        except KeyError:
            if request.mtype == aiocoap.CON:
                raise error.Unauthorized("Security context not found")
            else:
                return aiocoap.message.NoResponse

        try:
            unprotected, seqno = sc.unprotect(request)
        except error.RenderableError as e:
            # Primarily used for the Echo recovery 4.01 reply; the below could
            # be migrated there, but the behavior (at least as currently
            # encoded) is not exactly the one a no_response=26 would show, as
            # we want full responses to CONs but no responses to NONs, wheras
            # no_response=26 only flushes out an empty ACK and nothing more
            return e.to_message()
        except oscore.ReplayError:
            if request.mtype == aiocoap.CON:
                return aiocoap.Message(code=aiocoap.UNAUTHORIZED,
                                       max_age=0,
                                       payload=b"Replay detected")
            else:
                return aiocoap.message.NoResponse
        except oscore.DecodeError:
            if request.mtype == aiocoap.CON:
                raise error.BadOption("Failed to decode COSE")
            else:
                return aiocoap.message.NoResponse
        except oscore.ProtectionInvalid as e:
            if request.mtype == aiocoap.CON:
                raise error.BadRequest("Decryption failed")
            else:
                return aiocoap.message.NoResponse

        unprotected.remote = OSCOREAddress(sc, request.remote)

        self.log.debug("Request %r was unprotected into %r", request,
                       unprotected)

        try:
            response = await self._inner_site.render(unprotected)
        except error.RenderableError as err:
            response = err.to_message()
        except Exception as err:
            response = aiocoap.Message(code=aiocoap.INTERNAL_SERVER_ERROR)
            self.log.error(
                "An exception occurred while rendering a protected resource: %r",
                err,
                exc_info=err)

        protected_response, _ = sc.protect(response, seqno)

        self.log.debug("Response %r was encrypted into %r", response,
                       protected_response)

        return protected_response