Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Add option to enable encryption by default for new rooms #7639

Merged
merged 10 commits into from
Jun 10, 2020
1 change: 1 addition & 0 deletions changelog.d/7639.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add an option to enable encryption by default for private rooms.
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
16 changes: 16 additions & 0 deletions docs/sample_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,22 @@ retention:
#
#request_token_inhibit_3pid_errors: true

# Controls whether locally-created rooms should be end-to-end encrypted by
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
# default.
#
# Possible options are "all", "invite", and "off". They are defined as:
#
# * "all": any locally-created room
# * "invite": any locally-created room that requires an invite to join
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
# * "off": this option will take no effect
#
# The default value is "off".
#
# Note that this option will only affect rooms created after it is set. It
# will also not affect rooms created by another, federated server.
#
#encryption_enabled_by_default_for_room_type: private


## TLS ##

Expand Down
5 changes: 5 additions & 0 deletions synapse/api/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,8 @@ class EventContentFields(object):
# Timestamp to delete the event after
# cf https://github.com/matrix-org/matrix-doc/pull/2228
SELF_DESTRUCT_AFTER = "org.matrix.self_destruct_after"


class RoomEncryptionAlgorithms(object):
MEGOLM_V1_AES_SHA2 = "m.megolm.v1.aes-sha2"
DEFAULT = MEGOLM_V1_AES_SHA2
30 changes: 30 additions & 0 deletions synapse/config/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@
--------------------------------------------------------------------------------"""


class RoomDefaultEncryptionTypes(object):
"""Possible values for the encryption_enabled_by_default_for_room_type config option"""

ALL = "all"
INVITE = "invite"
OFF = "off"


class ServerConfig(Config):
section = "server"

Expand Down Expand Up @@ -521,6 +529,12 @@ class LimitRemoteRoomsConfig(object):
"request_token_inhibit_3pid_errors", False,
)

# Whether new, locally-created rooms should have encryption enabled
self.encryption_enabled_by_default_for_room_type = config.get(
"encryption_enabled_by_default_for_room_type",
RoomDefaultEncryptionTypes.OFF,
)
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved

def has_tls_listener(self) -> bool:
return any(listener["tls"] for listener in self.listeners)

Expand Down Expand Up @@ -1013,6 +1027,22 @@ def generate_config_section(
# act as if no error happened and return a fake session ID ('sid') to clients.
#
#request_token_inhibit_3pid_errors: true

# Controls whether locally-created rooms should be end-to-end encrypted by
# default.
#
# Possible options are "all", "invite", and "off". They are defined as:
#
# * "all": any locally-created room
# * "invite": any locally-created room that requires an invite to join
# * "off": this option will take no effect
#
# The default value is "off".
#
# Note that this option will only affect rooms created after it is set. It
# will also not affect rooms created by another, federated server.
#
#encryption_enabled_by_default_for_room_type: private
"""
% locals()
)
Expand Down
12 changes: 10 additions & 2 deletions synapse/handlers/federation.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@
from twisted.internet import defer

from synapse import event_auth
from synapse.api.constants import EventTypes, Membership, RejectedReason
from synapse.api.constants import (
EventTypes,
Membership,
RejectedReason,
RoomEncryptionAlgorithms,
)
from synapse.api.errors import (
AuthError,
CodeMessageException,
Expand Down Expand Up @@ -742,7 +747,10 @@ async def _process_received_pdu(
if device:
keys = device.get("keys", {}).get("keys", {})

if event.content.get("algorithm") == "m.megolm.v1.aes-sha2":
if (
event.content.get("algorithm")
== RoomEncryptionAlgorithms.MEGOLM_V1_AES_SHA2
):
# For this algorithm we expect a curve25519 key.
key_name = "curve25519:%s" % (device_id,)
current_keys = [keys.get(key_name)]
Expand Down
90 changes: 63 additions & 27 deletions synapse/handlers/room.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,15 @@

from six import iteritems, string_types

from synapse.api.constants import EventTypes, JoinRules, RoomCreationPreset
from synapse.api.constants import (
EventTypes,
JoinRules,
RoomCreationPreset,
RoomEncryptionAlgorithms,
)
from synapse.api.errors import AuthError, Codes, NotFoundError, StoreError, SynapseError
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS, RoomVersion
from synapse.config.server import RoomDefaultEncryptionTypes
from synapse.events.utils import copy_power_levels_contents
from synapse.http.endpoint import parse_and_validate_server_name
from synapse.storage.state import StateFilter
Expand Down Expand Up @@ -56,31 +62,6 @@


class RoomCreationHandler(BaseHandler):

PRESETS_DICT = {
RoomCreationPreset.PRIVATE_CHAT: {
"join_rules": JoinRules.INVITE,
"history_visibility": "shared",
"original_invitees_have_ops": False,
"guest_can_join": True,
"power_level_content_override": {"invite": 0},
},
RoomCreationPreset.TRUSTED_PRIVATE_CHAT: {
"join_rules": JoinRules.INVITE,
"history_visibility": "shared",
"original_invitees_have_ops": True,
"guest_can_join": True,
"power_level_content_override": {"invite": 0},
},
RoomCreationPreset.PUBLIC_CHAT: {
"join_rules": JoinRules.PUBLIC,
"history_visibility": "shared",
"original_invitees_have_ops": False,
"guest_can_join": False,
"power_level_content_override": {},
},
}

def __init__(self, hs):
super(RoomCreationHandler, self).__init__(hs)

Expand All @@ -89,6 +70,54 @@ def __init__(self, hs):
self.room_member_handler = hs.get_room_member_handler()
self.config = hs.config

self._presets_dict = {
RoomCreationPreset.PRIVATE_CHAT: {
"join_rules": JoinRules.INVITE,
"history_visibility": "shared",
"original_invitees_have_ops": False,
"guest_can_join": True,
"power_level_content_override": {"invite": 0},
"encryption_algorithm": (
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
RoomEncryptionAlgorithms.DEFAULT
if hs.config.encryption_enabled_by_default_for_room_type
in [
RoomDefaultEncryptionTypes.ALL,
RoomDefaultEncryptionTypes.INVITE,
]
else None
),
},
RoomCreationPreset.TRUSTED_PRIVATE_CHAT: {
"join_rules": JoinRules.INVITE,
"history_visibility": "shared",
"original_invitees_have_ops": True,
"guest_can_join": True,
"power_level_content_override": {"invite": 0},
"encryption_algorithm": (
RoomEncryptionAlgorithms.DEFAULT
if hs.config.encryption_enabled_by_default_for_room_type
in [
RoomDefaultEncryptionTypes.ALL,
RoomDefaultEncryptionTypes.INVITE,
]
else None
),
},
RoomCreationPreset.PUBLIC_CHAT: {
"join_rules": JoinRules.PUBLIC,
"history_visibility": "shared",
"original_invitees_have_ops": False,
"guest_can_join": False,
"power_level_content_override": {},
"encryption_algorithm": (
RoomEncryptionAlgorithms.DEFAULT
if hs.config.encryption_enabled_by_default_for_room_type
in [RoomDefaultEncryptionTypes.ALL]
else None
),
},
}

self._replication = hs.get_replication_data_handler()

# linearizer to stop two upgrades happening at once
Expand Down Expand Up @@ -798,7 +827,7 @@ async def send(etype, content, **kwargs) -> int:
)
return last_stream_id

config = RoomCreationHandler.PRESETS_DICT[preset_config]
config = self._presets_dict[preset_config]

creator_id = creator.user.to_string()

Expand Down Expand Up @@ -888,6 +917,13 @@ async def send(etype, content, **kwargs) -> int:
etype=etype, state_key=state_key, content=content
)

if config["encryption_algorithm"]:
last_sent_stream_id = await send(
etype=EventTypes.RoomEncryption,
state_key="",
content={"algorithm": config["encryption_algorithm"]},
)

return last_sent_stream_id

async def _generate_room_id(
Expand Down
6 changes: 5 additions & 1 deletion tests/federation/test_federation_sender.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

from twisted.internet import defer

from synapse.api.constants import RoomEncryptionAlgorithms
from synapse.rest import admin
from synapse.rest.client.v1 import login
from synapse.types import JsonDict, ReadReceipt
Expand Down Expand Up @@ -536,7 +537,10 @@ def build_device_dict(user_id: str, device_id: str, sk: SigningKey):
return {
"user_id": user_id,
"device_id": device_id,
"algorithms": ["m.olm.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"],
"algorithms": [
"m.olm.curve25519-aes-sha2",
RoomEncryptionAlgorithms.MEGOLM_V1_AES_SHA2,
],
"keys": {
"curve25519:" + device_id: "curve25519+key",
key_id(sk): encode_pubkey(sk),
Expand Down
18 changes: 14 additions & 4 deletions tests/handlers/test_e2e_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import synapse.handlers.e2e_keys
import synapse.storage
from synapse.api import errors
from synapse.api.constants import RoomEncryptionAlgorithms

from tests import unittest, utils

Expand Down Expand Up @@ -222,7 +223,10 @@ def test_reupload_signatures(self):
device_key_1 = {
"user_id": local_user,
"device_id": "abc",
"algorithms": ["m.olm.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"],
"algorithms": [
"m.olm.curve25519-aes-sha2",
RoomEncryptionAlgorithms.MEGOLM_V1_AES_SHA2,
],
"keys": {
"ed25519:abc": "base64+ed25519+key",
"curve25519:abc": "base64+curve25519+key",
Expand All @@ -232,7 +236,10 @@ def test_reupload_signatures(self):
device_key_2 = {
"user_id": local_user,
"device_id": "def",
"algorithms": ["m.olm.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"],
"algorithms": [
"m.olm.curve25519-aes-sha2",
RoomEncryptionAlgorithms.MEGOLM_V1_AES_SHA2,
],
"keys": {
"ed25519:def": "base64+ed25519+key",
"curve25519:def": "base64+curve25519+key",
Expand Down Expand Up @@ -315,7 +322,10 @@ def test_upload_signatures(self):
device_key = {
"user_id": local_user,
"device_id": device_id,
"algorithms": ["m.olm.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"],
"algorithms": [
"m.olm.curve25519-aes-sha2",
RoomEncryptionAlgorithms.MEGOLM_V1_AES_SHA2,
],
"keys": {"curve25519:xyz": "curve25519+key", "ed25519:xyz": device_pubkey},
"signatures": {local_user: {"ed25519:xyz": "something"}},
}
Expand Down Expand Up @@ -392,7 +402,7 @@ def test_upload_signatures(self):
"device_id": device_id,
"algorithms": [
"m.olm.curve25519-aes-sha2",
"m.megolm.v1.aes-sha2",
RoomEncryptionAlgorithms.MEGOLM_V1_AES_SHA2,
],
"keys": {
"curve25519:xyz": "curve25519+key",
Expand Down
Loading