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
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
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
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