diff --git a/matrix/api.py b/matrix/api.py index 221d6b5..ae4539f 100644 --- a/matrix/api.py +++ b/matrix/api.py @@ -224,7 +224,7 @@ class MatrixClient: h = HttpRequest(RequestType.POST, self.host, path, content) return h - def keys_upload(self, user_id, device_id, account, keys=None, + def keys_upload(self, user_id, device_id, olm, keys=None, one_time_keys=None): query_parameters = {"access_token": self.access_token} @@ -235,7 +235,6 @@ class MatrixClient: content = {} - # TODO one time keys if keys: device_keys = { "algorithms": [ @@ -250,12 +249,7 @@ class MatrixClient: } } - signature = account.sign(json.dumps( - device_keys, - ensure_ascii=False, - separators=(',', ':'), - sort_keys=True, - )) + signature = olm.sign_json(device_keys) device_keys["signatures"] = { user_id: { @@ -265,6 +259,24 @@ class MatrixClient: content["device_keys"] = device_keys + if one_time_keys: + one_time_key_dict = {} + + for key_id, key in one_time_keys.items(): + key_dict = {"key": key} + signature = olm.sign_json(key_dict) + + one_time_key_dict["signed_curve25519:" + key_id] = { + "key": key_dict.pop("key"), + "signatures": { + user_id: { + "ed25519:" + device_id: signature + } + } + } + + content["one_time_keys"] = one_time_key_dict + return HttpRequest(RequestType.POST, self.host, path, content) def mxc_to_http(self, mxc): @@ -557,20 +569,22 @@ class MatrixKickMessage(MatrixMessage): class MatrixKeyUploadMessage(MatrixMessage): - def __init__(self, client, user_id, device_id, account, keys=None, + def __init__(self, client, user_id, device_id, olm, keys=None, one_time_keys=None): data = { "device_id": device_id, "user_id": user_id, - "account": account, + "olm": olm, "keys": keys, "one_time_keys": one_time_keys } + self.device_keys = True if keys else False + MatrixMessage.__init__(self, client.keys_upload, data) def decode_body(self, server): object_hook = partial(MatrixEvents.MatrixKeyUploadEvent.from_dict, - server) + server, self.device_keys) return self._decode(server, object_hook) diff --git a/matrix/encryption.py b/matrix/encryption.py index 9f1fecb..aced6ac 100644 --- a/matrix/encryption.py +++ b/matrix/encryption.py @@ -18,6 +18,7 @@ from __future__ import unicode_literals import os +import json # pylint: disable=redefined-builtin from builtins import str @@ -188,3 +189,13 @@ class Olm(): f.write(pickle) except OlmAccountError as error: raise EncryptionError(error) + + def sign_json(self, json_dict): + signature = self.account.sign(json.dumps( + json_dict, + ensure_ascii=False, + separators=(',', ':'), + sort_keys=True, + )) + + return signature diff --git a/matrix/events.py b/matrix/events.py index 7d5f495..fb6932c 100644 --- a/matrix/events.py +++ b/matrix/events.py @@ -69,19 +69,23 @@ class MatrixErrorEvent(MatrixEvent): class MatrixKeyUploadEvent(MatrixEvent): - def __init__(self, server): + def __init__(self, server, device_keys): + self.device_keys = device_keys MatrixEvent.__init__(self, server) def execute(self): - message = "{prefix}matrix: Uploaded olm device keys.".format( + if not self.device_keys: + return + + message = "{prefix}matrix: Uploaded Olm device keys.".format( prefix=W.prefix("network")) W.prnt(self.server.server_buffer, message) @classmethod - def from_dict(cls, server, parsed_dict): + def from_dict(cls, server, device_keys, parsed_dict): try: - return cls(server) + return cls(server, device_keys) except (KeyError, TypeError, ValueError): return MatrixErrorEvent.from_dict(server, "Error uploading device" "keys", False, parsed_dict) @@ -415,10 +419,12 @@ class MatrixBacklogEvent(MatrixEvent): class MatrixSyncEvent(MatrixEvent): - def __init__(self, server, next_batch, room_infos, invited_infos): + def __init__(self, server, next_batch, room_infos, invited_infos, + one_time_key_count): self.next_batch = next_batch self.joined_room_infos = room_infos self.invited_room_infos = invited_infos + self.one_time_key_count = one_time_key_count MatrixEvent.__init__(self, server) @@ -439,12 +445,21 @@ class MatrixSyncEvent(MatrixEvent): def from_dict(cls, server, parsed_dict): try: next_batch = sanitize_id(parsed_dict["next_batch"]) + one_time_key_count = 0 + + if "device_one_time_keys_count" in parsed_dict: + if ("signed_curve25519" in + parsed_dict["device_one_time_keys_count"]): + one_time_key_count = ( + parsed_dict["device_one_time_keys_count"]["signed_curve25519"]) + room_info_dict = parsed_dict["rooms"] join_infos, invite_infos = MatrixSyncEvent._infos_from_dict( room_info_dict) - return cls(server, next_batch, join_infos, invite_infos) + return cls(server, next_batch, join_infos, invite_infos, + one_time_key_count) except (KeyError, ValueError, TypeError): return MatrixErrorEvent.from_dict(server, "Error syncing", False, parsed_dict) @@ -475,5 +490,6 @@ class MatrixSyncEvent(MatrixEvent): self._queue_joined_info() server.next_batch = self.next_batch + server.check_one_time_keys(self.one_time_key_count) server.handle_events() diff --git a/matrix/server.py b/matrix/server.py index e9f0cc5..4d03480 100644 --- a/matrix/server.py +++ b/matrix/server.py @@ -40,7 +40,7 @@ from matrix.api import ( MatrixKeyUploadMessage ) -from matrix.encryption import Olm, EncryptionError +from matrix.encryption import Olm, EncryptionError, encrypt_enabled try: FileNotFoundError @@ -464,12 +464,26 @@ class MatrixServer: def upload_keys(self, device_keys=False, one_time_keys=False): keys = self.olm.account.identity_keys() if device_keys else None - # TODO generate one time keys and upload them as well + one_time_keys = (self.olm.account.one_time_keys()["curve25519"] if + one_time_keys else None) + message = MatrixKeyUploadMessage(self.client, self.user_id, - self.device_id, self.olm.account, - keys, None) + self.device_id, self.olm, + keys, one_time_keys) self.send_queue.append(message) + @encrypt_enabled + def check_one_time_keys(self, key_count): + max_keys = self.olm.account.max_one_time_keys() + + key_count = (max_keys / 2) - key_count + + if key_count <= 0: + return + + self.olm.account.generate_one_time_keys(key_count) + self.upload_keys(device_keys=False, one_time_keys=True) + def login(self): # type: (MatrixServer) -> None message = MatrixLoginMessage(self.client, self.user, self.password,