pull/1228/merge
Richard van der Hoff 2 months ago committed by GitHub
commit 6a289fec8e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,327 @@
# MSC1228: Removing MXIDs from events
## Background
We would like to be able to break the association between a user's ID (such as
`@richvdh:sw1v.org`) and their activity in a room.
The stretch goal is to also remove the association with server names, since for
many users, they are the only user on a server and it is reasonable to be able
to ask for the removal of any history of `sw1v.org`'s involvement with a room.
The general idea presented here is to use a pseudomym in many places where we
currently use user IDs. The current `@user:server` then becomes a user alias;
the mapping between alias and the psuedonumous ID is public but can be removed
in the future.
User IDs currently appear in the following places in a room:
* `sender` of each event
* `state_key` of `m.room.member` events
* `users` list in `m.room.power_levels` events
* `creatorUserId` in the content of `m.widget`
Server names appear in the following places:
* `origin` of each event
* keys in the `signatures` dict of each event
* Room IDs
* Room Aliases
* `state_keys` in `m.room.aliases` events
* `matrix.to` permalinks
## Proposal
[This is v3 of this proposal, which in summary is: do proposal v1, but also
introduce a requirement for a global `user_key` at the same time, which in
future will replace mxids as the user's One True Identity. v1 and v2 are
available for reference at
https://docs.google.com/document/d/1ni4LnC_vafX4h4K4sYNpmccS7QeHEFpAcYcbLS-J21Q#heading=h.y1krynr6itl4.]
* Each user (currently identified by an mxid) will also have a `user_key`. In
time, this will replace the mxid as your One True Identity; however for now
they will live in parallel.
* A `user_key` is represented like `~1:dV3hr3yE9SxhsWEGBJdTho777S8ompkJTh`,
where `1:` is a version (to allow other systems to be used in future) and
the rest is an (unpadded urlsafe-base64ed) ed25519 public key.
* Homeservers are responsible for making up keys for their users.
* For now, each homeserver maintains a one-to-one mapping between
`user_key` and mxid for each of their users. In future, we will look to
break this link to allow portability of accounts.
* Room IDs also become ed25519 public keys.
* They look like: `!Sr_Vj3FIqyQ2WjJ9fWpUXRdz6fX4oFAjKrDmu198PnI`.
* The server which creates the room is responsible for creating the keypair.
* The `m.room.create` event is signed with the room id to stop people making
new rooms which look like old ones. After this point, the private key is
never needed again. <sup id="a1">[1](#f1)</sup>
* Define a `user_room_key`, which is yet another ed25519 public key.
* It looks like `^Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw`.
* Homeservers are responsible for making up user keys for their users. They
can (and should) use a different key in each room for each user.
* This `user_room_key` is used where we currently use an mxid in the DAG:
`sender`, `m.room.member`, `m.room.power_levels`,
`creatorUserId`. `creator` is removed as per
[MSC2175](./2175-remove-creator-field.md).
* Events are **signed by the `user_room_key` of the sender instead of the
server's key**.
* If a user leaves and rejoins a room, they should use the same
`user_room_key` (unless a server admin has manually removed the old
mapping). This makes ban evasion harder. (It's up to server owners to
ensure this rule is followed - servers which don't respect it and allow a
serial abuser to evade bans by issuing different `user_room_keys` are likely
to suffer whole-server bans.)
* Invite and join events include:
* `mxid_mapping`: field which gives the user's `@user:server` mxid and which
must be signed by the server in question, and the signature must be
verified before the mapping is considered valid.
* `user_mapping`: contains the `user_key` giving your True Identity, and
signed by that key. The signature must be verified by receiving
homeservers for it to be considered a valid invite/join event for a vNext
room.
### Examples
`m.room.create`: signed by both the room key and the `user_room_key` of the
sender:
```json
{
"type": "m.room.create",
"state_key": "",
"room_id": "!Sr_Vj3FIqyQ2WjJ9fWpUXRdz6fX4oFAjKrDmu198PnI",
"event_id": "$Riw5upWofaD4MNGM7bbZIj3bf+Th3fW/tklH4+6+VOg",
"sender": "^Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw",
"content": {},
"origin_server_ts": 1459891964497,
"prev_events": [],
"prev_state": [],
"auth_events": [],
"signatures": {
"!Sr_Vj3FIqyQ2WjJ9fWpUXRdz6fX4oFAjKrDmu198PnI": "<...>",
"^Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw": "<...>"
},
"hashes":{"sha256":"3ASU57dV3hr3yE9SxhsWEGBJdTho777S8ompkJTh/Uo"}
}
```
`m.room.member`, showing `mxid_mapping` and `user_mapping`:
```json
{
"type": "m.room.member",
"state_key": "^Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw",
"room_id": "!Sr_Vj3FIqyQ2WjJ9fWpUXRdz6fX4oFAjKrDmu198PnI",
"event_id": "$k21EhS3j8lhwqTi5NMTUH04oFyvR/1ujBGSWbW27aDs",
"sender": "^Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw",
"content": {
"membership": "join",
"avatar_url": "...",
"displayname": "...",
"mxid_mapping": {
"user_room_key": "^Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw",
"user_id": "@richvdh:matrix.org",
"signatures": {
"matrix.org": { "ed25519:a_zrXW": "<...>" }
}
},
"user_mapping": {
"user_key": "~1:dV3hr3yE9SxhsWEGBJdTho777S8ompkJTh",
"user_room_key": "^Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw",
"signatures": {
"~1:dV3hr3yE9SxhsWEGBJdTho777S8ompkJTh": "<...>"
}
}
},
"origin_server_ts": 1489597048772,
"prev_events": [
"$Riw5upWofaD4MNGM7bbZIj3bf+Th3fW/tklH4+6+VOg"
],
"prev_state": [],
"auth_events": [
"$Riw5upWofaD4MNGM7bbZIj3bf+Th3fW/tklH4+6+VOg"
],
"signatures": {
"^Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw": "<...>"
},
"hashes":{"sha256":"KLx4Alfa0QzOihSmUMZ1WQj5QdWnbMHwmqKxmYO8hJE"}
}
```
### Handling the mxid mapping
When a server joins a room, it will be presented with a bunch of
`m.room.member` events which may claim mappings onto mxids. There are three
reasons we need to know whether the mapping is valid:
* For clients, to help track users between rooms and to correlate to presence
* To authorise other servers to do backfill requests, etc.
* For outgoing messages, knowing which servers to send to.
None of these are _particularly_ urgent (they all degrade fairly gracefully in
the case that a mapping is missed). A lot of the slowness in joining rooms
currently comes from having to pull server keys so that event signatures can be
verified during the join process, so it would be nice to be able to consider
the join complete as soon as the signature on the event is verified, and verify
the `mxid_mappings` in the background.
#### Implementation
As a homeserver, we make an attempt to verify the sender before sending events
to our clients.
* When a new invite/join event turns up for a _room you are already in_ (either
via federation push, or because we pulled it via `/event/xxx` due to missing
`auth_events`/`prev_events`):
We make an attempt to verify its `mxid_mapping` before persisting it into
our db. If the sig is wrong, we reject the event at that point.
If we can't get the key (with a shortish timeout), we handle it as normal
(and schedule a retry for later).
Typically we only care about the most recent<sup id="a2">[2](#f2)</sup>
`mxid_mapping` for each `user_room_key`; when we see another we can cancel any
pending verification of any previous mapping<sup id="a3">[3](#f3)</sup>. Any
previously-verified mapping should remain in place until another mapping
becomes available.
* When we _backfill_:
We do the same thing, although in many cases we'll already have active
mappings for the users in question, so we can ignore any received that
way. By only honouring the most recent mapping, we gain the correct
semantics for account portability: authorisation for backfill depends on the
current location of the user, rather than wherever they were in the past.
* When we join a _new room_:
For now we do the same thing (ie, make an attempt to verify the mxid
mappings in the join events we receive, and time them out quickly). In
future we might optimise this so the the mappings are verified lazily.
This should mean that in the majority of cases, we'll have a verified mxid by
the time we send the event to a client.
For each `user_room_key`, we therefore have:
* zero or one verified mxid mappings.
* zero or one incomplete mxid mappings.
We extend the CS API to include a `verified_sender_mxid` field on any events
sent to a client where we know the user's current **verified** mxid.
* This is included in the interests of helping simple clients do the right
thing most of the time - but it is annoying and dangerous because it will
**sometimes** be missing. Still, we don't want to either (a) hold up all
traffic in the room while we wait for a verification which may never
succeed; (b) hold back some events while we do a verification for a sender;
(c) require that all clients always have to wait for an asynchronous
verification and match them up.
We also add a **new** field to the `/sync` response which tells clients about
mxid mappings as they are resolved.
Question: should we remove unverified mxid mappings from join events before
serving them to the clients, to stop client developers relying on it and
breaking everything?
### Sending invites
We have a bootstrapping problem for invites in that, until a user joins a room,
we don't know their `user_room_key`.
Also: invite events are supposed to be signed by the invitee, so that other
members of the room can be sure that they have actually received a copy of the
event.
The current invite dance is:
* inviting server builds a complete invite event, and signs it
* inviting server sends a copy to invited server, along with some (unsigned)
state about the room: name, avatar, inviting user's join event
* invited server checks that request came from server of inviting user
* invited server also signs the event, and tells the user about it
* inviting server adds the (double-signed) event to the DAG and sends it to
the rest of the federation (including the invited server, if it was already
in the room)
We could change this to:
* inviting server builds a partial invite event
* inviting server PUTs to `/_matrix/federation/v3/invite/<event_id>/<target_mxid>` on invited server
* invited server checks that request came from server of inviting user
* invited server adds:
* `user_room_key` in state_key
* mxid attestation
* signature
* invited server tells the user about it, and returns the completed event to inviting server
* inviting server adds its signature to the complete event
* inviting server adds the (double-signed) event to the DAG and sends it to
the rest of the federation (including the invited server, if it was already
in the room)
## Problems
How to handle the state keys in `room_aliases` events?
How to handle server names in `matrix.to` permalinks?
What if somebody does a join with an inappropriate avatar/displayname? If we
redact their join, we'll redact their identity assertion too :/
## Other things that might fall out nicely
* Fixes broken backfill due to changed signing keys (matrix-org/synapse#3121)
* Case sensitive mxid comparison problems?
* Opens a path to killing off perspectives in favour of just asking servers
what their keys are (via TLS, with trust coming from X.509 certificates).
* Helping with the domain reuse problem
* Nicer way of validating redactions by comparing the `user_room_key` of the
message and the redaction which addresses the suboptimal solution introduced
by [MSC1659](./1659-event-id-as-hashes.md)
* …
## Stuff we might want to avoid designing out
* Ability to migrate users between servers by changing their mapping assertions
* Ability to support alternative identity mapping assertions rather than being
strictly mxid->user_key (e.g. 3pid->user-key too). This could help
decentralised identity mappings in general in future, and possibly unify 3pid
invites with normal invites? It could also support discovering servers by
key rather than DNS (e.g. via DHT), which would be useful for p2p in future.
## Key management
If a private user key gets lost, they can just start using a new one and
announce a new mapping; however this may require an update to the
`power_levels` to give rights to the new user.
If a private user key is compromised, then again we start using a new one and
announce a new mapping, so that new events from the old key wouldn't look like
they came from that user. Again it may need `power_level`s updates to remove
power from the old `user_key`, but I think that is fair: you give away the keys
to your privileged account, you have to expect some cleanup. Ideally we would
have a way of revoking the old key properly, but this can be deferred for now.
<a id="f1"/>[1] although we might think about letting its use confer some sort of founder semantics.[↩](#a1)
<a id="f2"/>[2] or more accurately, the one in the current room state.[↩](#a2)
<a id="f3"/>[3] if a user/server spams out mappings so quickly that none of them ever complete, that is their own loss.[↩](#a3)
Loading…
Cancel
Save