diff --git a/proposals/4080-cryptographic-identities.md b/proposals/4080-cryptographic-identities.md new file mode 100644 index 00000000000..8ab48f4d734 --- /dev/null +++ b/proposals/4080-cryptographic-identities.md @@ -0,0 +1,584 @@ +# MSC4080: Cryptographic Identities (Client-Owned Identities) +**THIS MSC IS IN PROGRESS AND WILL CHANGE AS IMPLEMENTATIONS LAND** + +Today’s Matrix does not allow users to move their account between homeservers. It would be beneficial to be able +to move a user account from one homeserver to another while allowing that user to maintain their existing room +memberships, power levels in those rooms, message history, account data and end-to-end encrypted sessions. + +With Pseudonymous Identities (pseudoIDs) we have decoupled a user’s mxid from the identity used to track room +membership. The new pseudoIDs (also known as user_room_keys/senderids) are both created and managed +entirely by the homeserver. PseudoIDs was the first step towards having portable accounts in Matrix. + +With Cryptographic Identities we aim to take portable accounts one step further by moving the Pseudonymous +Identities off the server and onto the client in order for the client to have full ownership over their identity. +Clients are then responsible for performing full event signing on a per-event basis. This step brings us closer to +portable accounts primarily in two ways. + +1. Users now fully own their identity in the Matrix ecosystem and have the control to move their identity. +2. It improves the security of the Matrix ecosystem by making it more difficult for a homeserver to act maliciously +on a client’s behalf. This is particularly important once accounts are portable in order to prevent a homeserver from +being able to continue operating on a user’s behalf after that user has moved their account off the homeserver. + +Cryptographic Identities does not get us all the way to having account portability. Further MSC/s will be required to +create the appropriate endpoints and other changes in order to successfully port a user’s account from one homeserver +to another. + +## Proposal + +CryptoIDs are generated and stored by the client. When joining a room for the first time, a cryptoID should be +generated for that room. All events are signed by the client using their cryptoID and are no longer signed by the +user’s homeserver with the exception of the mxid_mapping in the m.room.member event. + +### Event Signing + +Events are required to be signed by the cryptoID. In order for this to work with client-owned keys, clients need to +obtain the full version of events before they can be signed. This proposal introduces a few changes to the C-S API +endpoints used to send events between the client and the server. Any C-S API endpoint which previously was used to +send events, now returns the fully formed version of those event/s to the client (minus the signatures block). The +event/s are no longer processed by the server while handling these endpoints. The client then signs the event/s and +forwards them to the server via a new `/send_pdus` endpoint. When handling events sent to this new endpoint the server +should process the event/s like normal by adding them to their respective rooms. + +A homeserver should avoid processing room events from the client until they have been sent via the `/send_pdus` +endpoint to ensure the client actually signs the event so it can be successfully sent into the room. + +### Endpoint Additions + +##### POST /_matrix/client/v1/send_pdus/{txnId} + +**Rate-limited**: Yes +**Requires authentication**: Yes + +Fully formed PDUs are sent to this endpoint to be committed to a room DAG. Clients are expected to have signed the +events sent to this endpoint. Homeservers should reject any event which isn’t properly signed by the client. + +Events sent to this endpoint are processed in the order they are received. A homeserver should check the validity of +each event before sending it to the room. This includes verifying the signature of the event matches the cryptoID +found in the `sender` field of the event. If the event is for a `remote` invite or join, the relevant `/send_invite` +or `/send_join` over federation should be performed prior to adding the event to the room. + +If any event is invalid all events are rejected by the homeserver. Invalid events include those that are not correctly +signed, whose event fields are invalid (such as a state event missing a `state_key` field), or the homeserver deems +the event invalid for some other reason. This approach is taken because this failure mode is most likely due to a +programming error. Failures of this nature result in a HTTP status 400. A [standard error response](https://spec.matrix.org/v1.8/client-server-api/#standard-error-response) +will be returned. As well as the normal common error codes, other reasons for rejection include: + +- M_DUPLICATE_ANNOTATION: The request is an attempt to send a [duplicate annotation](https://spec.matrix.org/v1.8/client-server-api/#avoiding-duplicate-annotations). + +A homeserver should also protect against clients who modify events sent by the homeserver before signing them. If a +client modifies an event, such as changing `prev_events` to force costly state resolution, then we should reject that +event. A homeserver can do this by storing the hash of the proto event in a database, and then on `/send_pdus`, remove +the `signatures` key and check if the hash exists in the DB (i.e the homeserver sent the client this exact proto event). +The homeserver can also then expire the proto event in a timely manner which helps alleviate issues of costly state +resolution due to the likelihood of `prev_events` changing as time passes. Any kind of client event signing is going to +add latency to creating events, which is going to increase the chance of increasing the number of forward extremities. + +A `txn_id` is added to the request parameters. Clients should generate an ID unique across requests with the same +access token; it will be used by the server to ensure idempotency of requests. + +Request: +``` +{ + pdus: [ + PDUInfo + ] +} +``` + +PDUInfo: +``` +{ + room_version: string, + via_server: string, // optional + pdu: PDU // signed PDU +} +``` + +### Endpoint Changes + +Effected endpoint versions all need to be bumped since the underlying behaviour is changed with this proposal. When +hitting any of these endpoints the resulting events are no longer immediately added to the room. Instead the client +is required to send the returned event/s to the `/send_pdus` endpoint after signing them in order for the event/s to +be added to the room DAG. + +##### POST /_matrix/client/v4/createRoom + +Room creation adds a new `cryptoid` field to the request body. The `cryptoid` must be valid [Unpadded Base64](https://spec.matrix.org/v1.8/appendices/#unpadded-base64) +and 32 bytes in size in order to be a valid ed25519 public key. This field is used for the homeserver to be able to +fully create all the necessary room creation events on behalf of the client. Since this is a new room the homeserver +needs to be told which cryptoID to correlate to this room for this user. + +The response includes the new fields: `room_version` and `pdus`. + +Request: +``` +{ + ..., + cryptoid: string +} +``` + +200 OK Response: +``` +{ + room_id: string, + room_version: string, + pdus: [ PDU ] +} +``` + +##### POST /_matrix/client/v4/rooms/{roomId}/invite + +Inviting users to a room has a number of changes in order to make it work. First, since the cryptoID for a given user +and room needs to be created by the client, we cannot rely on the existing invite sequence which relies on the invited +user’s homeserver to fully populate the invite event. Instead we need a way for the invited user to be part of the +loop and provide a cryptoID in order to finalize the event. It would not be acceptable to require the invited client +to be available at all times in order to respond to an invite request in real time. Matrix does not currently have a +requirement that client communications be synchronous and this proposal seeks to preserve asynchronous communications +when participants are unreachable. Instead, this proposal introduces the concept of one-time cryptoIDs. + +One-time cryptoIDs are uploaded to the user’s homeserver so that they can be claimed and used whenever that user +receives a room invite. In order for a user to be available for invite, one-time cryptoIDs should be created and +uploaded to a user’s current homeserver. This should take the same shape as one-time keys for encryption do today. +The one-time cryptoIDs should be signed by the device’s ed25519 key to verify they were created by that device. + +When a client wants to invite a new user to a room for the first time, they need to query the invited user’s +homeserver for one of the invited user’s one-time cryptoIDs. They can then use that cryptoID to create an invite +event for the user. + +The invite response includes a new `pdu` field. + +Two new endpoints are also added to the S-S API: `/make_invite` & `/send_invite`. These endpoints are required in +order to split out generating an invite event, and having the inviting client sign that event, from actually sending +the event to the invited user’s homeserver. + +**TODO**: document /make_invite & /send_invite endpoints + +```mermaid +sequenceDiagram +participant Alice +participant Alice HS +participant Bob HS +participant Bob +Bob ->> Bob HS: /keys_upload (one-time cryptoID) +Note over Bob, Bob HS: Occurs separately, when one-time
cryptoID count is low. +Alice ->> Alice HS: /invite:@bob:bob_hs +Alice HS ->> Bob HS: /make_invite +Bob HS ->> Bob HS: Claim one-time cryptoID +Note right of Bob HS: A valid one-time cryptoID is required
to create the full invite event. +Bob HS ->> Alice HS: (proto pdu) +Alice HS ->> Alice: (proto pdu) +Alice ->> Alice: Sign PDU +Alice ->> Alice HS: /send_pdus +Alice HS ->> Bob HS: /send_invite +``` + +200 OK Response: +``` +{ + pdu: PDU +} +``` + +##### POST /_matrix/client/v4/join/{roomIdOrAlias} && POST /_matrix/client/v4/rooms/{roomId}/join + +A number of fields are added to the response of the `/join` endpoints: `room_version`, `via_server`, and `pdu`. +These are added to help the client when sending the join event to the `/send_pdus` endpoint. The `via_server` is the +server chosen by the homeserver to perform the join via. The `via_server` should be passed along to the `/send_pdus` +endpoint with the fully signed version of this event. + +Room joining adds a new `cryptoid` field to the request body. The `cryptoid` must be valid [Unpadded Base64](https://spec.matrix.org/v1.8/appendices/#unpadded-base64) +and 32 bytes in size in order to be a valid ed25519 public key. This field is used for the homeserver to be able to +create the join event on behalf of the client and for the homeserver to validate the user is joining with the +correct cryptoID if the join follows an invite event. If this is a join without a matching invite, the homeserver +needs to be told which cryptoID to correlate to this room for this user. + +Request: +``` +{ + ..., + cryptoid: string +} +``` + +200 OK Response: +``` +{ + room_id: string, + room_version: string, + via_server: string, + pdu: PDU +} +``` + +##### POST /_matrix/client/v4/rooms/{roomId}/leave + +The leave endpoint is extended to return a `pdu` for the client to sign. + +200 OK Response: +``` +{ + pdu: PDU +} +``` + +##### PUT /_matrix/client/v4/rooms/{roomId}/send/{eventType}/{txnId} && PUT /_matrix/client/v4/rooms/{roomId}/state/{eventType}/{stateKey} + +The `/send` & `/state` endpoints are extended to return the `pdu` in the response for the client to sign. + +200 OK Response: +``` +{ + event_id: string, + pdu: PDU +} +``` + +##### POST /_matrix/client/v4/keys/upload + +A `one_time_cryptoids` field is added to the `/keys/upload` endpoint in order to upload new `one_time_cryptoids` for +the purposes of inviting the user to new rooms. + +Request: +``` +{ + ..., + one_time_cryptoids: map[string]OneTimeCryptoID +} +``` + +200 OK Response: +``` +{ + ..., + one_time_cryptoid_counts: map[string]int +} +``` + +OneTimeCryptoID: +``` +“algorithm:KeyID”: { + “key”: ”base64_bytes” +} +``` + +##### GET /_matrix/client/v4/sync + +The `/sync` endpoint will need to be extended to report the one-time cryptoID count. In the response, a +`one_time_cryptoids_count` field is added. This is a mapping of cryptoID algorithm (ie. ed25519) to the count of +`one_time_cryptoids` for that algorithm. + +200 OK Response: +``` +{ + ..., + one_time_cryptoids_count: map[string]int +} +``` + +The `/sync` endpoint also requires an extension of the `InvitedRoom` parameter to include a `one_time_cryptoid` field +which is the cryptoID that was selected by the user’s homeserver when creating the invite event. This field is +necessary in order to inform the client which cryptoID was used to create the invite event since homeservers translate +all cryptoIDs to regular mxids when sending events to the client. Then the client can track this association +internally in order to correctly sign future events sent to the room. + +200 OK Response (InvitedRoom JSON Object): +``` +{ + invite_state: InviteState, + one_time_cryptoid: string +} +``` + +##### POST /_matrix/client/v4/rooms/{roomId}/kick + +The kick endpoint is extended to return a `pdu` for the client to sign. + +200 OK Response: +``` +{ + pdu: PDU +} +``` + +**TODO: How to handle external users** + +##### POST /_matrix/client/v4/rooms/{roomId}/ban + +The ban endpoint is extended to return a `pdu` for the client to sign. + +200 OK Response: +``` +{ + pdu: PDU +} +``` + +**TODO: How to handle external users** + +##### POST /_matrix/client/v4/rooms/{roomId}/unban + +The unban endpoint is extended to return a `pdu` for the client to sign. + +200 OK Response: +``` +{ + pdu: PDU +} +``` + +##### PUT /_matrix/client/v4/rooms/{roomId}/redact/{eventId}/{txnId} + +**TODO** + +##### POST /_matrix/client/v4/rooms/{roomId}/upgrade + +**TODO** + + + +**TODO**: look into the following: +- Room directory +- Peek & unpeek +- sendToDevice +- What to do with EDUs? + - Read_markers + - Presence + - VOIP stuff + - Typing + - Locations? EDU/State? + + +### Auth Rules + +A new room version will be required to account for the modifications to the auth rules. + +Invite events no longer require a signature from the invited user’s homeserver. This signature requirement does not +appear to have an obvious benefit and would make invite events overly onerous with the new room invite process. + +### Redaction Rules + +A new room version will be required to account for the modifications to the redaction rules. + +The `m.room.member` event content object allows the `mxid_mapping` key. + +### User Attestation (Optional) + +To attest that a cryptoID belongs to a specific user, the client `master_signing_key` could sign the join event +containing their generated cryptoID, verifying they are that identity, to prevent a server from spoofing a user +joining a new room by having the malicious server generate a cryptoID themselves to create & sign events with. + +Linking the cryptoID with the `master_signing_key` will remove the deniability aspect of messages since you are now +cryptographically linking your `master_signing_key` which is synonymous with a user’s identity, with each cryptoID. + +This extension is effectively what is proposed in [MSC3917 - Cryptographic Room Memberships](https://github.com/matrix-org/matrix-spec-proposals/pull/3917). + +An alternative to using the `master_signing_key` would be to use some other client generated key & include that in +the attestation of the cryptoID. A client could choose whether to use different room signing keys per room (the +benefit of doing this would be to ensure that knowing a user’s identity in one room did not lead to knowing that +same user’s identity in another room), or use the same room signing key for all rooms. Then at a later time clients +could use some out of band attestation mechanism to “cross-sign” in order to verify the user/s are who they say they +are. This has the additional benefit of not needing to enter the user’s recovery passphrase to provide the attestation +as clients could store these room signing keys. + +### Identity Sharing Between Devices + +The cryptoIDs of a user are shared between devices using secret storage similar to the way encryption keys are shared. +This leverages server-side key backups for key recovery. + +#### Server-Side Key Backups + +**TODO**: detail this section + +## Potential Issues + +### Recovery Passphrase Entry + +Requiring the `master_signing_key` to sign a join event in order to attest a user is who they claim to be would +typically require the user to enter their recovery passphrase every time they join a room. This is because clients +do not usually store this key. This would lead to a large burden on users and would be best to avoid if at all +possible. + +### Additional Attack Vectors + +Clients can modify events prior to signing them and sending them to the server for processing. This can lead to +issues if the client were to change something such as the `prev_events` which could lead to further problems. +In order to mitigate this, a server should perform validation of each event being received from the `/send_pdus` +endpoint. A homeserver could do this by storing the hash of an event prior to sending it to a client, then ensure +any event received by the `/send_pdus` endpoint has a matching hash to one stored previously. + +A homeserver can run out of one-time cryptoIDs used during invites. Homeservers should protect against this by +attempting to detect malicious activity which seeks to deplete the one-time cryptoID reserves for a user. An +alternative would be to have a fallback one-time cryptoID. The issue with relying on this mitigation is that it +could quickly become the case that a client ends up with the same cryptoID in many rooms. This is not necessarily an +issue unless that user wants to keep their cryptoIDs separate in order to maintain the pseudonymity they provide. + +### Identity/Key Migration + +This MSC currently does not account for the possibility of either changing a cryptoID key, or of changing the cryptoID +key algorithm. This would potentially involve some manner of distinguishing the cryptoID algorithm in use and of being +able to change a user's associated cryptoID key in a room. Both use cases are important in their own right and need +further consideration before this MSC can be considered for acceptance. + +## Alternatives + +### Clients delegate event signing on a per-event basis + +In this alternative, all events would add a field to event `content` specifying the event signing delegate (such as the +user's homeserver). All events would be expected to be signed by this delegate. + +**Advantages**: This has the advantage of avoiding a second round trip for normal messaging. + +**Disadvantages**: This has the disadvantage of the added complexity of trying to protect event content such that +only a client is allowed to specify a signing delegate. This ends up leading to a number of issues where homeservers +could be able to replay events on a client's behalf, thus minimizing the benefits of cryptographic identities. + +This also increases the size of every single event due to the addition of required `content` fields. + +#### Details + +##### Event Signing + +In order to ensure the `allowed_signing_keys` was actually specified by the client, clients now sign the `content` +section of all events. Events are modified to add `nonce`, `allowed_signing_keys`, `hash` and `signatures` fields. These +fields are used to prevent malicious homeservers from spoofing events on a client’s behalf. The signature is created +by signing the combination of `type`, `state_key` and redacted `content`. Signing the `room_id` is not required since +cryptoIDs are already unique per room but could be added if necessary. The `nonce` value should be generated as a +random 128-bit integer (or UUIDv4?), encoded as unpadded base64 (or hex for UUID?). It is especially important for +the `nonce` to be unique for each `type` and `state_key` pairing in order to ensure events cannot be replayed by a +malicious homeserver. The hash is the sha-256 hash, encoded as unpadded base64, of the `content` fields before +`signatures` have been added (including the new `nonce`, and `allowed_signing_keys` fields). The `hash` is required in +order to be able to verify the event `signature` if the event `content` is ever redacted. + +All events are now required to possess the above mentioned fields inside of `content`. + +An example event would look like: + +```json +{ + "msgtype": "m.text", + "body": "hello world!", + "m.client_signatures": { + "nonce": "random_number", + "allowed_signing_keys": { + "ed25519":"some+server+key" + }, + "hash": "hash_of_content_without_signatures", + "signatures": { + "ed25519:base64+cryptoid": "signature+of+cryptoid" + } + } +} +``` + +##### Auth Rules + +Both clients and servers should now check the `content` signatures to validate whether the `type` + `state_key` + +redacted `content` was signed by the `sender`. Only the redacted `content` needs to be signed since that will contain +a `hash` of the full contents that can be used to verify the full event. Signing the redacted `content` instead +of just the `hash` allows for further field validation (ie. fields from `m.room.member`, `m.room.join_rules`, and +`m.room.power_levels` to name a few). If a server detects the `signature` is wrong it should reject the event. If a +client detects the `signature` is wrong it should alert the user who can then decide what further action to take, if +any. A client detecting an invalid `signature` means their homeserver either didn’t check the `signature` or did +check the `signature` and didn’t reject the event. + +Servers should also check that the full event was signed by one of the keys present in the `allowed_signing_keys` +field, or by the cryptoID itself. If the event was not signed by one of these keys, the server should reject the +event. Allowing events to be signed by the cryptoID keeps the possibility of clients to perform state resolution +and generate full events if they should choose to do so. + +To further validate the event, new rules need to be added to verify the `nonce` field. Homeservers should ensure +that the `nonce` is unique for events from that user in this room. It is most important to ensure that the `nonce` +value is unique per `type` and `state_key` pairing. Duplicate `nonce` values that are used for different rooms or +`type`/`state_key` pairings aren’t an issue since the event signatures protect against replay attacks where these +values have been modified. If a homeserver receives an event with a `nonce` that is identical to another event with +the same `type` and `state_key`, that event should be rejected. Clients have the option to also perform `nonce` +validation in this way if the possibility of colluding homeservers is suspected. When sending an event via the C-S +API, a homeserver should verify that the `nonce` of the new event is unique or reject the event from the client. +If the event is rejected in this way, the homeserver should return a response status of 400 with an errcode of +`M_DUPLICATE_NONCE`. + +**Problem**: How can a client generate a usable nonce? + +**Problem**: How could a homeserver validate a nonce as being unique without requiring them to know the entire room DAG? + +##### Redaction Rules + +The following fields should be preserved during redaction for all event types: +- `nonce` +- `allowed_signing_key` +- `hash` +- `signature` + +##### Replay Attacks + +Clients are now responsible for signing the `content` field of events but they don’t sign the full event. This means +that a malicious homeserver could take the contents of an existing room event and replace everything else in the +event without anyone knowing. This could include fields such as `type`, `state_key`, `prev_events`, and `room_id`. +In order to minimize the effects of a replay attack, the client should sign the combination of `type`, `state_key`, +and `content`. Signing `type` prevents reusing the contents in an event of another event `type`. Signing `state_key` +prevents attacks such as changing the `membership` state of another user. Signing `content` prevents a malicious +homeserver from generating arbitrary `content` on behalf of a client. + +Even with the above mitigation, a malicious homeserver could still replay an event in the same room, with the same +`content`, `type` and `state_key` at a different location in the DAG. That is to say, a homeserver can replay the +same event with different values for `prev_events` and `auth_events`. An example of this could take the form of +changing the power levels earlier or later in time. + +A further mitigation could be to use the make/send event, double round trip, approach where a client first requests +the full event from their homeserver, then signs the full event before sending that into the room. This could be +done only for state events since the effects of replay attacks on state events is much more devastating to a room +and state events occur infrequently. This would add an additional level of security while keeping the normal event +sending flow fast since non-state events wouldn’t have the additional client-server round trip. + +### Clients sign full events via room extremity tracking + +In this model the client would be responsible for creating a full event (including `prev_events` and `auth_events`) +by tracking and resolving the room’s state. + +**Advantages**: This has the advantage of events being fully signed by the cryptoID and avoiding a second round trip. + +**Disadvantages**: This has the disadvantage of requiring clients to do state resolution which cannot reasonably be +done by clients due to `/sync` not returning information in such a way that forward extremities can be properly +tracked. + +### Clients delegate event signing in their m.room.member event + +In this model the client would add a `allowed_signing_keys` field to their `m.room.member event` in order to delegate +event signing to another party. Homeservers still have full authority over a client’s events in this scenario since +the client doesn’t sign any part of each event to verify they are the sender. + +**Advantages**: This has the advantage of not adding additional size to each event. + +**Disadvantages**: This has the disadvantage of giving over full event control to the delegated homeserver. It also has +the disadvantage of trying to resolve `allowed_signing_keys` if a client wants to remove authority from a homeserver +or there are conflicts in the room DAG. Revocation of a delegated key is known to be extremely problematic. + +## Unstable prefix + +While this proposal is not considered stable, the `org.matrix.msc4080` unstable prefix should be used on +all new or changed endpoints. + +| Stable | Unstable | +|-|-| +| `POST /_matrix/client/v1/send_pdus/{txnId}` | `POST /_matrix/client/unstable/org.matrix.msc4080/send_pdus/{txnId}` | +| `POST /_matrix/client/v4/createRoom` | `POST /_matrix/client/unstable/org.matrix.msc4080/createRoom` | +| `POST /_matrix/client/v4/rooms/{roomId}/invite` | `POST /_matrix/client/unstable/org.matrix.msc4080/rooms/{roomId}/invite` | +| `POST /_matrix/client/v4/join/{roomIdOrAlias}` | `POST /_matrix/client/unstable/org.matrix.msc4080/join/{roomIdOrAlias}` | +| `POST /_matrix/client/v4/rooms/{roomId}/join` | `POST /_matrix/client/unstable/org.matrix.msc4080/rooms/{roomId}/join` | +| `POST /_matrix/client/v4/rooms/{roomId}/leave` | `POST /_matrix/client/unstable/org.matrix.msc4080/rooms/{roomId}/leave` | +| `PUT /_matrix/client/v4/rooms/{roomId}/send/{eventType}/{txnId}` | `PUT /_matrix/client/unstable/org.matrix.msc4080/rooms/{roomId}/send/{eventType}/{txnId}` | +| `PUT /_matrix/client/v4/rooms/{roomId}/state/{eventType}/{stateKey}` | `PUT /_matrix/client/unstable/org.matrix.msc4080/rooms/{roomId}/state/{eventType}/{stateKey}` | +| `POST /_matrix/client/v4/keys/upload` | `POST /_matrix/client/unstable/org.matrix.msc4080/keys/upload` | +| `GET /_matrix/client/v4/sync` | `GET /_matrix/client/unstable/org.matrix.msc4080/sync` | +| `POST /_matrix/client/v4/rooms/{roomId}/kick` | `POST /_matrix/client/unstable/org.matrix.msc4080/rooms/{roomId}/kick` | +| `POST /_matrix/client/v4/rooms/{roomId}/ban` | `POST /_matrix/client/unstable/org.matrix.msc4080/rooms/{roomId}/ban` | +| `POST /_matrix/client/v4/rooms/{roomId}/unban` | `POST /_matrix/client/unstable/org.matrix.msc4080/rooms/{roomId}/unban` | +| `PUT /_matrix/client/v4/rooms/{roomId}/redact/{eventId}/{txnId}` | `PUT /_matrix/client/unstable/org.matrix.msc4080/rooms/{roomId}/redact/{eventId}/{txnId}` | +| `POST /_matrix/client/v4/rooms/{roomId}/upgrade` | `POST /_matrix/client/unstable/org.matrix.msc4080/rooms/{roomId}/upgrade` | + + +## Dependencies + +[MSC4014 - Pseudonymous Identities](https://github.com/matrix-org/matrix-spec-proposals/pull/4014) +