diff --git a/proposals/4362-simplified-encrypted-state.md b/proposals/4362-simplified-encrypted-state.md new file mode 100644 index 00000000000..b7a83cec693 --- /dev/null +++ b/proposals/4362-simplified-encrypted-state.md @@ -0,0 +1,173 @@ +# MSC4362: Simplified Encrypted State Events + + + +This proposal builds upon the earlier MSC3414, aiming to provide a simplified approach to encrypted +state events in Matrix. Currently, all room state is unencrypted and accessible to everyone in the +room, and occasionally people outside the room (such as via the public room directory, invite state, +or peekable rooms). The server also has access to these state events in order to perform state +resolution, and so is visible to server administrators. Most events in room state could be encrypted +to provide confidentiality, which is what this MSC seeks to achieve more straightforwardly. Some +parts, however, cannot be encrypted to maintain a functioning protocol. + +## Proposal + + + +Under this proposal, all room state events can be encrypted, except events critical to maintain the +protocol. Those critical events are: + +- `m.room.create` +- `m.room.member` +- `m.room.join_rules` +- `m.room.power_levels` +- `m.room.third_party_invite` +- `m.room.history_visibility` +- `m.room.guest_access` +- `m.room.encryption` + +An encrypted state event looks very similar to a regular encrypted room message: the `type` becomes +`m.room.encrypted` and the `content` is the same shape as a regular `m.room.encrypted` event. The +`state_key` for encrypted state events is constructed from the plaintext `type` and `state_key` +fields, formatted as `{type}:{state_key}`, preserving the uniqueness of the `type`-`state_key` +mapping required for the server to perform state resolution. + +To track whether a room has state encryption enabled, and to preserve compatibility with older +clients that cannot work with encrypted state events, a new boolean field `encrypt_state_events` is +introduced to the content of `m.room.encryption`, which determines if clients should send state +encrypted events. + +Clients are expected to decrypt all room state on reception and validate the packed state key +matches the decrypted type and state key. This ensures malicious clients cannot send state events +that masquerade as message events and vice versa. + +This MSC relies on the room key sharing mechanism outlined in +[MSC4268](https://github.com/matrix-org/matrix-spec-proposals/pull/4268), which enables clients to +decrypt historical state events. + +## Potential issues + + + +At present, MSC4268 +[does not require invitees to download the key bundle upon receiving an invite](https://github.com/matrix-org/matrix-spec-proposals/blob/rav/proposal/encrypted_history_sharing/proposals/4268-encrypted-history-sharing.md#actions-as-a-receiving-client); +instead, the key bundle is only fetched when the user joins the room, which could lead to problems +displaying the room name, topic, and avatar to invitees. One way to address this is to always +download the room key bundle on invite, but as MSC4268 notes, this introduces a potential +denial-of-service (DoS) attack vector. + +If the client does not receive the keys needed to decrypt state events, the room may become +unusable, as information such as the room's name, topic, avatar, and other metadata will be +inaccessible. Additionally, if there are state events sent both before and after state encryption is +enabled, existing clients might display the unencrypted, outdated state. + +Encrypting certain state events would prevent servers from displaying meaningful information about +rooms, as the room directory relies on being able to read these events. Rooms with encrypted +metadata could either appear as blank, generic, or broken entries in the public room list, or could +be omitted entirely, impeding room discovery. A similar issue arises with the space room list: if +room metadata is encrypted, clients and servers will be unable to display meaningful information +about child rooms within a space. It may be necessary to introduce an unencrypted state event, +`m.space.child_info`, that stores plaintext copies of a child room's avatar, name, and topic, which +can then be used over the encrypted metadata. + +The `:` delimiter may not be suitable in all cases. Additionally, string packing introduces size +limitations, as the combined length of the packed string cannot exceed the 255-byte maximum for a +state key. This effectively reduces the available space for both event types and state keys. + +## Alternatives + + + +A number of alternatives to string-packing the plaintext `type` and `state_key` are possible: + +- Preserving the values of `type` and `state_key`; +- Introducing an adjacent `true_type` field; +- Hashing `type` and `state_key` with HMAC. + +### Preserved Fields + +Rather than string-packing the `type` and `state_key` together, we could preserve these values on +the encrypted event, but still encrypt the event content. This provides the same (lack of) +confidentiality as the approach laid out in this MSC while avoiding string packing. However, this +approach would introduce a difference between the encryption of message events and state events, +which may be undesirable. + +### Adjacent Type Field + +In a similar manner to preserved fields, we could introduce a new `true_type` field to the events +`content`, which holds the plaintext type of the state event. This would require modifying the +server to utilise this field over the value of the `type` field, which may be undesirable. + +### HMAC-hashed `state_key`s + +This is the _ideal solution_, as it hides the state key and type from the server entirely; however, +there are some considerable downsides. We have two choices: + +- Use a static key generated on room creation to encrypt all state events for the duration of the + room's existence; +- Rotate the key periodically, perhaps deriving it from the current Megolm session key. + +The former case lacks post-compromise confidentiality (PCS), which, although quite hard to pull off +as an attacker, makes this approach undesirable. This approach is also vulnerable to frequency +analysis through comparison between the distribution of state key hashes and a known distribution of +public `type`-`state_key` pairs. + +The latter option has issues too: rotating the key breaks the server's ability to track room state, +since two events with identical state keys will produce encrypted events with different hashed state +keys when using different (HMAC) keys. The server will treat each as unique and send both to +clients. This would require clients to perform state resolution locally (to decide which of two +clashing events to accept), which in turn would require them to consume and understand the room DAG. +This approach may also be vulnerable to frequency analysis, but, based on some naive calculations, +the probability a malicious server is able to infer the hash to `type`-`state_key` mapping correctly +becomes increasingly unlikely as the number of state events encrypted by any given key decreases. + +## Security considerations + +This proposal relies on the security of the Olm/Megolm primitives, and an attack against them could +be a viable method to derive partial or complete knowledge of the encrypted content. + +Confidential information **should not** be stored in the `type` and `state_key` fields, since both +are present in plaintext. + +## Unstable prefix + + + +The current implementation uses an `io.element` vendor prefix for the `encrypt_state_events` flag +(i.e. `io.element.msc3414.encrypt_state_events`) for compatibility. + +## Dependencies + +This MSC builds on +[MSC3414](https://github.com/matrix-org/matrix-spec-propsals/tree/main/proposals/3414-encrypted-state-events.md) +and depends on [MSC4268](https://github.com/matrix-org/matrix-spec-proposals/pull/4268), neither of +which have been accepted into the spec at the time of writing.