# MSC4362: Simplified Encrypted State Events Currently, all state events are unencrypted. This allows the homeserver to read state event content in order to do its job in implementing the Matrix protocol: processing room membership and power levels, and performing state resolution. A side effect of homeservers being able to read state event content is that anyone with access to the homeserver's data (such as an administrator or a successful attacker) can also read these events. The set of events that are actually needed by the homeserver is quite small, so we propose encrypting everything else. This provides a significant reduction in the amount of visible metadata, at the cost of some user inconvenience (because users need decryption keys to see state information like room names). [MSC3414](https://github.com/matrix-org/matrix-spec-proposals/pull/3414) has similar goals to this proposal, but it specifies a concrete mechanism for hiding encrypted event types, and resolving state where it cannot be fully resolved by the server. We think this approach could be problematic, and may effectively require us to implement full state resolution on the client. Here, we simply propose the "easy" part: encrypting state events without hiding their types from the server. The intent is to allow real-world usage of encrypted state, accepting the limitations imposed because state is hidden from users in situations where they might want it, without requiring us to draw conclusions on the trickiest parts (sharing historical state, resolving state the server can't identify, and exposing room names and topics). ## 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. ## Limitations ### Room names and topics are not visible from outside The name and topic of a room with encrypted state will not be visible without access to the keys used to encrypt them. Without additional proposals, this will make it impossible to provide a room directory entry, list the room inside a space, or display room details when invited. ### State sent before joining the room is inaccessible Upon joining a room with encrypted state, new users will not be able to decrypt room state, making the room name, topic and other information (e.g. ongoing whiteboard sessions or call) inaccessible. This limitation does not apply if [MSC4268](https://github.com/matrix-org/matrix-spec-proposals/pull/4268) is available and the room settings allow sharing the relevant events. ## Potential issues 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 | Name | Stable name | Unstable name | | - | - | - | | Property in `m.room.encryption` event | `encrypt_state_events` | `io.element.msc4362.encrypt_state_events` | ## Dependencies This proposal is a more limited alternative to [MSC3414](https://github.com/matrix-org/matrix-spec-propsals/tree/main/proposals/3414-encrypted-state-events.md). The limitations of this proposal are improved somewhat if [MSC4268](https://github.com/matrix-org/matrix-spec-proposals/pull/4268) is available.