Пример #1
0
    def _split_message(self, message):
        """Given a protected message, return the outer message that contains
        all Class I and Class U options (but without payload or Object-Security
        option), and a proto-inner message that contains all Class E options."""

        # not trying to preserve token or mid, they're up to the transport
        outer_message = Message(code=message.code)
        inner_message = message.copy()

        if inner_message.code.is_request():
            protected_uri = inner_message.get_request_uri()

            if protected_uri.count('/') >= 3:
                protected_uri = protected_uri[:protected_uri.index(
                    '/',
                    protected_uri.index('/',
                                        protected_uri.index('/') + 1) + 1)]
            outer_message.set_request_uri(protected_uri)

            if inner_message.opt.proxy_uri:
                # pack them into the separatable fields ... hopefully (FIXME
                # use a set_request_uri that *never* uses Proxy-Uri)
                inner_message.set_request_uri(protected_uri)

            inner_message.opt.uri_host = None
            inner_message.opt.uri_port = None
            inner_message.opt.proxy_uri = None
            inner_message.opt.proxy_scheme = None

        outer_message.opt.observe = inner_message.opt.observe
        if inner_message.code.is_response():
            inner_message.opt.observe = None

        return outer_message, inner_message
Пример #2
0
    def _split_message(self, message):
        """Given a protected message, return the outer message that contains
        all Class I and Class U options (but without payload or Object-Security
        option), and a proto-inner message that contains all Class E options.

        This leaves the messages' remotes unset."""

        if message.code.is_request():
            outer_host = message.opt.uri_host
            proxy_uri = message.opt.proxy_uri

            inner_message = message.copy(
                uri_host=None,
                uri_port=None,
                proxy_uri=None,
                proxy_scheme=None,
            )
            inner_message.remote = None

            if proxy_uri is not None:
                # Use set_request_uri to split up the proxy URI into its
                # components; extract, preserve and clear them.
                inner_message.set_request_uri(proxy_uri, set_uri_host=False)
                if inner_message.opt.proxy_uri is not None:
                    raise ValueError("Can not split Proxy-URI into options")
                outer_uri = inner_message.remote.uri_base
                inner_message.remote = None
                inner_message.opt.proxy_scheme = None

            if message.opt.observe is None:
                outer_code = POST
            else:
                outer_code = FETCH
        else:
            outer_host = None
            proxy_uri = None

            inner_message = message.copy()

            outer_code = CHANGED

        # no max-age because these are always successsful responses
        outer_message = Message(
            code=outer_code,
            uri_host=outer_host,
            observe=None
            if message.code.is_response() else message.opt.observe,
        )
        if proxy_uri is not None:
            outer_message.set_request_uri(outer_uri)

        return outer_message, inner_message
Пример #3
0
    def _split_message(self, message):
        """Given a protected message, return the outer message that contains
        all Class I and Class U options (but without payload or Object-Security
        option), and a proto-inner message that contains all Class E options.

        This leaves the messages' remotes unset."""

        if message.code.is_request():
            outer_host = message.opt.uri_host
            proxy_uri = message.opt.proxy_uri

            inner_message = message.copy(
                    uri_host=None,
                    uri_port=None,
                    proxy_uri=None,
                    proxy_scheme=None,
                    )
            inner_message.remote = None

            if proxy_uri is not None:
                # Use set_request_uri to split up the proxy URI into its
                # components; extract, preserve and clear them.
                inner_message.set_request_uri(proxy_uri, set_uri_host=False)
                if inner_message.opt.proxy_uri is not None:
                    raise ValueError("Can not split Proxy-URI into options")
                outer_uri = inner_message.remote.uri_base
                inner_message.remote = None
                inner_message.opt.proxy_scheme = None

            if message.opt.observe is None:
                outer_code = POST
            else:
                outer_code = FETCH
        else:
            outer_host = None
            proxy_uri = None

            inner_message = message.copy()

            outer_code = CHANGED

        # no max-age because these are always successsful responses
        outer_message = Message(code=outer_code,
                uri_host=outer_host,
                observe=None if message.code.is_response() else message.opt.observe,
                )
        if proxy_uri is not None:
            outer_message.set_request_uri(outer_uri)

        return outer_message, inner_message
Пример #4
0
    def protect(self, message, request_partiv=None):
        # not trying to preserve token or mid, they're up to the transport
        outer_message = Message(code=message.code)
        if message.code.is_request():
            protected_uri = message.get_request_uri()
            if protected_uri.count('/') >= 3:
                protected_uri = protected_uri[:protected_uri.index(
                    '/',
                    protected_uri.index('/',
                                        protected_uri.index('/') + 1) + 1)]
            outer_message.set_request_uri(protected_uri)

        # FIXME any options to move out?
        inner_message = message

        if request_partiv is None:
            assert inner_message.code.is_request(
            ), "Trying to protect a response without request IV (possibly this is an observation; that's not supported in this OSCOAP implementation yet)"

            seqno = self.new_sequence_number()
            partial_iv = binascii.unhexlify(
                ("%%0%dx" % (2 * self.algorithm.iv_bytes)) % seqno)
            partial_iv_short = partial_iv.lstrip(b'\0')
            iv = _xor_bytes(self.sender_iv, partial_iv)

            unprotected = {
                6: partial_iv_short,
                4: self.sender_id,
            }
            request_kid = self.sender_id
        else:
            assert inner_message.code.is_response()

            partial_iv = request_partiv
            partial_iv_short = partial_iv.lstrip(b"\x00")
            iv = _flip_first_bit(_xor_bytes(partial_iv, self.sender_iv))
            unprotected = {}

            # FIXME: better should mirror what was used in request
            request_kid = self.recipient_id

        protected = {}

        assert protected == {}
        protected_serialized = b''  # were it into an empty dict, it'd be the cbor dump
        enc_structure = [
            'Encrypt0', protected_serialized,
            self._extract_external_aad(outer_message, request_kid,
                                       partial_iv_short)
        ]
        aad = cbor.dumps(enc_structure)
        key = self.sender_key

        plaintext = inner_message.opt.encode()
        if inner_message.payload:
            plaintext += bytes([0xFF])
            plaintext += inner_message.payload

        ciphertext, tag = self.algorithm.encrypt(plaintext, aad, key, iv)

        if USE_COMPRESSION:
            if protected:
                raise RuntimeError(
                    "Protection produced a message that has uncompressable fields."
                )
            if sorted(unprotected.keys()) == [4, 6]:
                shortarray = [unprotected[6], unprotected[4]]
                shortarray = cbor.dumps(shortarray)
                # we're using a shortarray shortened by one because that makes
                # it easier to then "exclude [...] the type and length for the
                # ciphertext"; the +1 on shortarray[0] makes it appear like a
                # 3-long array again.
                if (shortarray[0] + 1) & 0b11111000 != 0b10000000 or \
                        shortarray[1] & 0b11000000 != 0b01000000:
                    raise RuntimeError(
                        "Protection produced a message that has uncmpressable lengths"
                    )
                shortarray = bytes(
                    ((((shortarray[0] + 1) & 0b111) << 3) |
                     (shortarray[1] & 0b111), )) + shortarray[2:]
                oscoap_data = shortarray + ciphertext + tag
            elif unprotected == {}:
                oscoap_data = ciphertext + tag
            else:
                raise RuntimeError(
                    "Protection produced a message that has uncompressable fields."
                )
        else:
            cose_encrypt0 = [
                protected_serialized, unprotected, ciphertext + tag
            ]
            oscoap_data = cbor.dumps(cose_encrypt0)

        if inner_message.code.can_have_payload():
            outer_message.opt.object_security = b''
            outer_message.payload = oscoap_data
        else:
            outer_message.opt.object_security = oscoap_data

        # FIXME go through options section
        return outer_message, partial_iv