diff --git a/matrix/api.py b/matrix/api.py index 69b416c..0bbf4e6 100644 --- a/matrix/api.py +++ b/matrix/api.py @@ -306,7 +306,7 @@ class MatrixClient: return HttpRequest(RequestType.POST, self.host, path, content) - def key_claim(self, key_dict): + def keys_claim(self, key_dict): query_parameters = {"access_token": self.access_token} path = ("{api}/keys/claim?" @@ -315,7 +315,8 @@ class MatrixClient: query_parameters=urlencode(query_parameters)) content = { - "one_time_keys": {key_dict} + "one_time_keys": key_dict, + "timeout": 10000 } return HttpRequest(RequestType.POST, self.host, path, content) @@ -661,16 +662,17 @@ class MatrixKeyQueryMessage(MatrixMessage): class MatrixKeyClaimMessage(MatrixMessage): - def __init__(self, client, key_dict): + def __init__(self, client, room_id, key_dict): + self.room_id = room_id data = { "key_dict": key_dict, } - MatrixMessage.__init__(self, client.keys_query, data) + MatrixMessage.__init__(self, client.keys_claim, data) def decode_body(self, server): object_hook = partial(MatrixEvents.MatrixKeyClaimEvent.from_dict, - server) + server, self.room_id) return self._decode(server, object_hook) diff --git a/matrix/encryption.py b/matrix/encryption.py index d5f6e08..70f460d 100644 --- a/matrix/encryption.py +++ b/matrix/encryption.py @@ -33,8 +33,8 @@ import matrix.globals try: from olm.account import Account, OlmAccountError - from olm.session import (Session, InboundSession, OlmSessionError, - OlmPreKeyMessage) + from olm.session import (Session, InboundSession, OutboundSession, + OlmSessionError, OlmPreKeyMessage) from olm.group_session import ( InboundGroupSession, OutboundGroupSession, @@ -193,6 +193,14 @@ class OlmDeviceKey(): self.keys = key_dict +class OneTimeKey(): + def __init__(self, user_id, device_id, key): + # type: (str, str, str) -> None + self.user_id = user_id + self.device_id = device_id + self.key = key + + class Olm(): @encrypt_enabled @@ -245,6 +253,28 @@ class Olm(): return session + def create_session(self, user_id, device_id, one_time_key): + W.prnt("", "matrix: Creating session for {}".format(user_id)) + id_key = None + + for user, keys in self.device_keys.items(): + if user != user_id: + continue + + for key in keys: + if key.device_id == device_id: + id_key = key.keys["curve25519"] + break + + if not id_key: + W.prnt("", "ERRR not found ID key") + W.prnt("", "Found id key {}".format(id_key)) + session = OutboundSession(self.account, id_key, one_time_key) + self._update_acc_in_db() + self.sessions[user_id][device_id].append(session) + self._store_session(user_id, device_id, session) + W.prnt("", "matrix: Created session for {}".format(user_id)) + def create_group_session(self, room_id, session_id, session_key): W.prnt("", "matrix: Creating group session for {}".format(room_id)) session = InboundGroupSession(session_key) @@ -270,10 +300,12 @@ class Olm(): continue if not self.sessions[user][key.device_id]: + W.prnt("", "Missing session for device {}".format(key.device_id)) devices.append(key.device_id) if devices: - missing[user] = {device: "ed25519" for device in devices} + missing[user] = {device: "signed_curve25519" for + device in devices} return missing @@ -376,11 +408,16 @@ class Olm(): } for user in users: + if user not in self.device_keys: + continue for key in self.device_keys[user]: if key.device_id == self.device_id: continue + if not self.sessions[user][key.device_id]: + continue + device_payload_dict = payload_dict.copy() # TODO sort the sessions session = self.sessions[user][key.device_id][0] @@ -389,8 +426,6 @@ class Olm(): "ed25519": key.keys["ed25519"] } - W.prnt("", pprint.pformat(device_payload_dict)) - olm_message = session.encrypt( Olm._to_json(device_payload_dict) ) @@ -414,6 +449,7 @@ class Olm(): to_device_dict["messages"][user][key.device_id] = olm_dict + # W.prnt("", pprint.pformat(to_device_dict)) return to_device_dict @classmethod diff --git a/matrix/events.py b/matrix/events.py index 36773a7..56cea83 100644 --- a/matrix/events.py +++ b/matrix/events.py @@ -31,7 +31,7 @@ from matrix.rooms import (matrix_create_room_buffer, RoomInfo, RoomMessageText, RoomMessageEvent, RoomRedactedMessageEvent, RoomMessageEmote) -from matrix.encryption import OlmDeviceKey +from matrix.encryption import OlmDeviceKey, OneTimeKey try: from olm.session import OlmMessage, OlmPreKeyMessage @@ -355,16 +355,38 @@ class MatrixKeyQueryEvent(MatrixEvent): class MatrixKeyClaimEvent(MatrixEvent): - def __init__(self, server, keys): + def __init__(self, server, room_id, keys): self.keys = keys + self.room_id = room_id MatrixEvent.__init__(self, server) @classmethod - def from_dict(cls, server, parsed_dict): - raise NotImplementedError + def from_dict(cls, server, room_id, parsed_dict): + W.prnt("", pprint.pformat(parsed_dict)) + keys = [] + try: + for user_id, user_dict in parsed_dict["one_time_keys"].items(): + for device_id, device_dict in user_dict.items(): + for key_dict in device_dict.values(): + # TODO check the signature of the key + key = OneTimeKey(user_id, device_id, key_dict["key"]) + keys.append(key) + + return cls(server, room_id, keys) + except KeyError: + return MatrixErrorEvent.from_dict( + server, ("Error claiming onetime keys."), False, parsed_dict) def execute(self): - pass + server = self.server + olm = server.olm + + for key in self.keys: + olm.create_session(key.user_id, key.device_id, key.key) + + while server.encryption_queue[self.room_id]: + formatted_message = server.encryption_queue[self.room_id].popleft() + server.send_room_message(self.room_id, formatted_message, True) class MatrixToDeviceEvent(MatrixEvent): @@ -384,6 +406,7 @@ class MatrixToDeviceEvent(MatrixEvent): "device message"), False, parsed_dict) + class MatrixBacklogEvent(MatrixEvent): def __init__(self, server, room_id, end_token, events): diff --git a/matrix/server.py b/matrix/server.py index 897e89f..b84bc0a 100644 --- a/matrix/server.py +++ b/matrix/server.py @@ -24,7 +24,7 @@ import time import datetime import pprint -from collections import deque +from collections import deque, defaultdict from http_parser.pyparser import HttpParser from matrix.plugin_options import Option, DebugType @@ -40,7 +40,8 @@ from matrix.api import ( MatrixKeyUploadMessage, MatrixKeyQueryMessage, MatrixToDeviceMessage, - MatrixEncryptedMessage + MatrixEncryptedMessage, + MatrixKeyClaimMessage ) from matrix.encryption import Olm, EncryptionError, encrypt_enabled @@ -63,7 +64,9 @@ class MatrixServer: self.options = dict() # type: Dict[str, weechat.config] self.device_name = "Weechat Matrix" # type: str self.device_id = "" # type: str + self.olm = None # type: Olm + self.encryption_queue = defaultdict(deque) self.user = "" # type: str self.password = "" # type: str @@ -478,7 +481,12 @@ class MatrixServer: message = MatrixSyncMessage(self.client, self.next_batch, limit) self.send_queue.append(message) - def send_room_message(self, room_id, formatted_data): + def send_room_message( + self, + room_id, + formatted_data, + already_claimed=False + ): # type: (str, Formatted) -> None room = self.rooms[room_id] @@ -488,13 +496,13 @@ class MatrixServer: # TODO don't send messages unless all the devices are verified missing = self.olm.get_missing_sessions(room.users.keys()) - if missing: + if missing and not already_claimed: W.prnt("", "{prefix}matrix: Olm session missing for room, can't" " encrypt message.") W.prnt("", pprint.pformat(missing)) - # message = MatrixKeyClaimMessage(self.client, missing) - # self.send_or_queue(message) - # TODO claim keys for the missing user/device combinations + self.encryption_queue[room_id].append(formatted_data) + message = MatrixKeyClaimMessage(self.client, room_id, missing) + self.send_or_queue(message) return body = {"msgtype": "m.text", "body": formatted_data.to_plain()}