encryption: Initial encrypted message sending support.
This commit is contained in:
parent
8a3bda9797
commit
642e518464
5 changed files with 228 additions and 3 deletions
|
@ -94,6 +94,19 @@ class MatrixClient:
|
||||||
|
|
||||||
return HttpRequest(RequestType.GET, self.host, path)
|
return HttpRequest(RequestType.GET, self.host, path)
|
||||||
|
|
||||||
|
def room_encrypted_message(self, room_id, content):
|
||||||
|
# type: (str, Dict[Any, Any]) -> HttpRequest
|
||||||
|
query_parameters = {"access_token": self.access_token}
|
||||||
|
|
||||||
|
path = ("{api}/rooms/{room}/send/m.room.encrypted/"
|
||||||
|
"{tx_id}?{query_parameters}").format(
|
||||||
|
api=MATRIX_API_PATH,
|
||||||
|
room=quote(room_id),
|
||||||
|
tx_id=quote(str(self._get_txn_id())),
|
||||||
|
query_parameters=urlencode(query_parameters))
|
||||||
|
|
||||||
|
return HttpRequest(RequestType.PUT, self.host, path, content)
|
||||||
|
|
||||||
def room_send_message(self,
|
def room_send_message(self,
|
||||||
room_id,
|
room_id,
|
||||||
message_type,
|
message_type,
|
||||||
|
@ -307,6 +320,18 @@ class MatrixClient:
|
||||||
|
|
||||||
return HttpRequest(RequestType.POST, self.host, path, content)
|
return HttpRequest(RequestType.POST, self.host, path, content)
|
||||||
|
|
||||||
|
def to_device(self, event_type, content):
|
||||||
|
query_parameters = {"access_token": self.access_token}
|
||||||
|
|
||||||
|
path = ("{api}/sendToDevice/{event_type}/{tx_id}?"
|
||||||
|
"{query_parameters}").format(
|
||||||
|
api=MATRIX_API_PATH,
|
||||||
|
event_type=event_type,
|
||||||
|
tx_id=quote(str(self._get_txn_id())),
|
||||||
|
query_parameters=urlencode(query_parameters))
|
||||||
|
|
||||||
|
return HttpRequest(RequestType.PUT, self.host, path, content)
|
||||||
|
|
||||||
def mxc_to_http(self, mxc):
|
def mxc_to_http(self, mxc):
|
||||||
# type: (str) -> str
|
# type: (str) -> str
|
||||||
url = urlparse(mxc)
|
url = urlparse(mxc)
|
||||||
|
@ -648,3 +673,47 @@ class MatrixKeyClaimMessage(MatrixMessage):
|
||||||
server)
|
server)
|
||||||
|
|
||||||
return self._decode(server, object_hook)
|
return self._decode(server, object_hook)
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixToDeviceMessage(MatrixMessage):
|
||||||
|
def __init__(self, client, to_device_dict):
|
||||||
|
data = {
|
||||||
|
"content": to_device_dict,
|
||||||
|
"event_type": "m.room.encrypted"
|
||||||
|
}
|
||||||
|
|
||||||
|
MatrixMessage.__init__(self, client.to_device, data)
|
||||||
|
|
||||||
|
def decode_body(self, server):
|
||||||
|
object_hook = partial(MatrixEvents.MatrixToDeviceEvent.from_dict,
|
||||||
|
server)
|
||||||
|
|
||||||
|
return self._decode(server, object_hook)
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixEncryptedMessage(MatrixMessage):
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
client,
|
||||||
|
room_id,
|
||||||
|
formatted_message,
|
||||||
|
content):
|
||||||
|
self.room_id = room_id
|
||||||
|
self.formatted_message = formatted_message
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"room_id": self.room_id,
|
||||||
|
"content": content
|
||||||
|
}
|
||||||
|
|
||||||
|
MatrixMessage.__init__(self, client.room_encrypted_message, data)
|
||||||
|
|
||||||
|
def decode_body(self, server):
|
||||||
|
object_hook = partial(
|
||||||
|
MatrixEvents.MatrixSendEvent.from_dict,
|
||||||
|
server,
|
||||||
|
self.room_id,
|
||||||
|
self.formatted_message,
|
||||||
|
)
|
||||||
|
|
||||||
|
return self._decode(server, object_hook)
|
||||||
|
|
|
@ -34,7 +34,7 @@ import matrix.globals
|
||||||
try:
|
try:
|
||||||
from olm.account import Account, OlmAccountError
|
from olm.account import Account, OlmAccountError
|
||||||
from olm.session import (Session, InboundSession, OlmSessionError,
|
from olm.session import (Session, InboundSession, OlmSessionError,
|
||||||
OlmPreKeyMessage)
|
OlmMessage, OlmPreKeyMessage)
|
||||||
from olm.group_session import (
|
from olm.group_session import (
|
||||||
InboundGroupSession,
|
InboundGroupSession,
|
||||||
OutboundGroupSession,
|
OutboundGroupSession,
|
||||||
|
@ -313,6 +313,29 @@ class Olm():
|
||||||
except OlmSessionError:
|
except OlmSessionError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def group_encrypt(self, room_id, plaintext_dict):
|
||||||
|
# type: (str, Dict[str, str]) -> Dict[str, str], Bool
|
||||||
|
is_new = False
|
||||||
|
plaintext_dict["room_id"] = room_id
|
||||||
|
|
||||||
|
if not room_id in self.outbound_group_sessions:
|
||||||
|
self.create_outbound_group_session(room_id)
|
||||||
|
is_new = True
|
||||||
|
|
||||||
|
session = self.outbound_group_sessions[room_id]
|
||||||
|
|
||||||
|
ciphertext = session.encrypt(Olm._to_json(plaintext_dict))
|
||||||
|
|
||||||
|
payload_dict = {
|
||||||
|
"algorithm": "m.megolm.v1.aes-sha2",
|
||||||
|
"sender_key": self.account.identity_keys()["curve25519"],
|
||||||
|
"ciphertext": ciphertext,
|
||||||
|
"session_id": session.id,
|
||||||
|
"device_id": self.device_id
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload_dict, is_new
|
||||||
|
|
||||||
@encrypt_enabled
|
@encrypt_enabled
|
||||||
def group_decrypt(self, room_id, session_id, ciphertext):
|
def group_decrypt(self, room_id, session_id, ciphertext):
|
||||||
if session_id not in self.inbound_group_sessions[room_id]:
|
if session_id not in self.inbound_group_sessions[room_id]:
|
||||||
|
@ -326,6 +349,69 @@ class Olm():
|
||||||
|
|
||||||
return plaintext
|
return plaintext
|
||||||
|
|
||||||
|
def share_group_session(self, room_id, own_id, users):
|
||||||
|
group_session = self.outbound_group_sessions[room_id]
|
||||||
|
|
||||||
|
key_content = {
|
||||||
|
"algorithm": "m.megolm.v1.aes-sha2",
|
||||||
|
"room_id": room_id,
|
||||||
|
"session_id": group_session.id,
|
||||||
|
"session_key": group_session.session_key,
|
||||||
|
"chain_index": group_session.message_index
|
||||||
|
}
|
||||||
|
|
||||||
|
payload_dict = {
|
||||||
|
"type": "m.room_key",
|
||||||
|
"content": key_content,
|
||||||
|
# TODO we don't have the user_id in the Olm class
|
||||||
|
"sender": own_id,
|
||||||
|
"sender_device": self.device_id,
|
||||||
|
"keys": {
|
||||||
|
"ed25519": self.account.identity_keys()["ed25519"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
to_device_dict = {
|
||||||
|
"messages": {}
|
||||||
|
}
|
||||||
|
|
||||||
|
for user in users:
|
||||||
|
|
||||||
|
for key in self.device_keys[user]:
|
||||||
|
if key.device_id == self.device_id:
|
||||||
|
continue
|
||||||
|
|
||||||
|
device_payload_dict = payload_dict.copy()
|
||||||
|
# TODO sort the sessions
|
||||||
|
session = self.sessions[user][key.device_id][0]
|
||||||
|
device_payload_dict["recipient"] = user
|
||||||
|
device_payload_dict["recipient_keys"] = {
|
||||||
|
"ed25519": key.keys["ed25519"]
|
||||||
|
}
|
||||||
|
|
||||||
|
W.prnt("", pprint.pformat(device_payload_dict))
|
||||||
|
|
||||||
|
olm_message = session.encrypt(Olm._to_json(device_payload_dict))
|
||||||
|
|
||||||
|
olm_dict = {
|
||||||
|
"algorithm": "m.olm.v1.curve25519-aes-sha2",
|
||||||
|
"sender_key": self.account.identity_keys()["curve25519"],
|
||||||
|
"ciphertext": {
|
||||||
|
key.keys["curve25519"]: {
|
||||||
|
"type": (0 if isinstance(olm_message,
|
||||||
|
OlmPreKeyMessage) else 1),
|
||||||
|
"body": olm_message.ciphertext
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
to_device_dict["messages"][user] = {
|
||||||
|
key.device_id: olm_dict
|
||||||
|
}
|
||||||
|
|
||||||
|
return to_device_dict
|
||||||
|
# return {}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@encrypt_enabled
|
@encrypt_enabled
|
||||||
def from_session_dir(cls, user, device_id, session_path):
|
def from_session_dir(cls, user, device_id, session_path):
|
||||||
|
@ -474,6 +560,16 @@ class Olm():
|
||||||
|
|
||||||
return signature
|
return signature
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _to_json(json_dict):
|
||||||
|
# type: (Dict[Any, Any]) -> str
|
||||||
|
return json.dumps(
|
||||||
|
json_dict,
|
||||||
|
ensure_ascii=False,
|
||||||
|
separators=(",", ":"),
|
||||||
|
sort_keys=True
|
||||||
|
)
|
||||||
|
|
||||||
@encrypt_enabled
|
@encrypt_enabled
|
||||||
def mark_keys_as_published(self):
|
def mark_keys_as_published(self):
|
||||||
self.account.mark_keys_as_published()
|
self.account.mark_keys_as_published()
|
||||||
|
|
|
@ -367,6 +367,23 @@ class MatrixKeyClaimEvent(MatrixEvent):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixToDeviceEvent(MatrixEvent):
|
||||||
|
|
||||||
|
def __init__(self, server):
|
||||||
|
MatrixEvent.__init__(self, server)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, server, parsed_dict):
|
||||||
|
try:
|
||||||
|
if parsed_dict == {}:
|
||||||
|
return cls(server)
|
||||||
|
|
||||||
|
raise KeyError
|
||||||
|
except KeyError:
|
||||||
|
return MatrixErrorEvent.from_dict(server, ("Error sending to "
|
||||||
|
"device message"),
|
||||||
|
False, parsed_dict)
|
||||||
|
|
||||||
class MatrixBacklogEvent(MatrixEvent):
|
class MatrixBacklogEvent(MatrixEvent):
|
||||||
|
|
||||||
def __init__(self, server, room_id, end_token, events):
|
def __init__(self, server, room_id, end_token, events):
|
||||||
|
|
|
@ -652,6 +652,11 @@ class RoomMemberJoin(RoomEvent):
|
||||||
|
|
||||||
# calculate room display name and set it as the buffer list name
|
# calculate room display name and set it as the buffer list name
|
||||||
room_name = room.display_name(server.user_id)
|
room_name = room.display_name(server.user_id)
|
||||||
|
|
||||||
|
# A user has joined an encrypted room, we need to check for new devices
|
||||||
|
if room.encrypted:
|
||||||
|
server.device_check_timestamp = None
|
||||||
|
|
||||||
W.buffer_set(buff, "short_name", room_name)
|
W.buffer_set(buff, "short_name", room_name)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,9 @@ from matrix.api import (
|
||||||
MatrixSyncMessage,
|
MatrixSyncMessage,
|
||||||
MatrixLoginMessage,
|
MatrixLoginMessage,
|
||||||
MatrixKeyUploadMessage,
|
MatrixKeyUploadMessage,
|
||||||
MatrixKeyQueryMessage
|
MatrixKeyQueryMessage,
|
||||||
|
MatrixToDeviceMessage,
|
||||||
|
MatrixEncryptedMessage
|
||||||
)
|
)
|
||||||
|
|
||||||
from matrix.encryption import Olm, EncryptionError, encrypt_enabled
|
from matrix.encryption import Olm, EncryptionError, encrypt_enabled
|
||||||
|
@ -483,6 +485,7 @@ class MatrixServer:
|
||||||
if not room.encrypted:
|
if not room.encrypted:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# TODO don't send messages unless all the devices are verified
|
||||||
missing = self.olm.get_missing_sessions(room.users.keys())
|
missing = self.olm.get_missing_sessions(room.users.keys())
|
||||||
|
|
||||||
if missing:
|
if missing:
|
||||||
|
@ -494,7 +497,42 @@ class MatrixServer:
|
||||||
# TODO claim keys for the missing user/device combinations
|
# TODO claim keys for the missing user/device combinations
|
||||||
return
|
return
|
||||||
|
|
||||||
# self.send_queue.append(message)
|
body = {"msgtype": "m.text", "body": formatted_data.to_plain()}
|
||||||
|
|
||||||
|
if formatted_data.is_formatted():
|
||||||
|
body["format"] = "org.matrix.custom.html"
|
||||||
|
body["formatted_body"] = formatted_data.to_html()
|
||||||
|
|
||||||
|
plaintext_dict = {
|
||||||
|
"type": "m.room.message",
|
||||||
|
"content": body
|
||||||
|
}
|
||||||
|
|
||||||
|
W.prnt("", "matrix: Encrypting message")
|
||||||
|
|
||||||
|
payload_dict, session_is_new = self.olm.group_encrypt(
|
||||||
|
room_id,
|
||||||
|
plaintext_dict
|
||||||
|
)
|
||||||
|
|
||||||
|
if session_is_new:
|
||||||
|
to_device_dict = self.olm.share_group_session(
|
||||||
|
room_id,
|
||||||
|
self.user_id,
|
||||||
|
room.users.keys()
|
||||||
|
)
|
||||||
|
message = MatrixToDeviceMessage(self.client, to_device_dict)
|
||||||
|
W.prnt("", "matrix: Megolm session missing for room.")
|
||||||
|
self.send_queue.append(message)
|
||||||
|
|
||||||
|
message = MatrixEncryptedMessage(
|
||||||
|
self.client,
|
||||||
|
room_id,
|
||||||
|
formatted_data,
|
||||||
|
payload_dict
|
||||||
|
)
|
||||||
|
|
||||||
|
self.send_queue.append(message)
|
||||||
|
|
||||||
@encrypt_enabled
|
@encrypt_enabled
|
||||||
def upload_keys(self, device_keys=False, one_time_keys=False):
|
def upload_keys(self, device_keys=False, one_time_keys=False):
|
||||||
|
|
Loading…
Reference in a new issue