Merge e26c97c2bb
into ecf996389f
commit
68ec903fe6
@ -0,0 +1,317 @@
|
||||
# MSC2787: Portable Identities
|
||||
|
||||
## Background
|
||||
|
||||
This is an evolution of [MSC1228](https://github.com/matrix-org/matrix-doc/pull/1228)
|
||||
which aims to make it simpler to implement portable identities and decentralised
|
||||
accounts.
|
||||
|
||||
It is still a work-in-progress—some things that need attention include:
|
||||
|
||||
- How to handle multiple homeservers joining the same room, since they will have
|
||||
their own membership events, and clients may wish to disambiguate these;
|
||||
- How to handle invites, given that you won't know a UDK until after the user has
|
||||
joined the room;
|
||||
- How to adequately disconnect UDKs from UPKs as a part of a data removal request
|
||||
for GDPR compliance;
|
||||
- Whether UPKs should really be a "one true identity" for a user or whether a user
|
||||
may actually have multiple UPKs if they want;
|
||||
- How to handle device list syncing and send-to-device messages;
|
||||
- The extent to which users should be involved in attesting MXID-to-UPK mappings.
|
||||
|
||||
## Goals
|
||||
|
||||
The goals of this proposal are:
|
||||
|
||||
- To enable account portability by breaking the link between a user identity and a
|
||||
specific homeserver;
|
||||
- To allow breaking the link between delegated and permanent user identities at a
|
||||
later date, e.g. as a part of a data deletion request;
|
||||
- To allow a user to grant permission to one or more homeservers to act on behalf of
|
||||
the user in a given room, e.g. allowing them to creating and sign events from a
|
||||
user;
|
||||
- To remove the need for servers to have a single static signing key, as they do
|
||||
today.
|
||||
|
||||
## Proposal
|
||||
|
||||
This proposal includes specifications to:
|
||||
|
||||
- To give a user a single cryptographic User Permanent Key (herein referred to as
|
||||
a "UPK"), which they will use as part of a cryptographic challenge login;
|
||||
- To give a server a set of User Delegated Keys (herein referred to as a "UDK"),
|
||||
which will represent servers acting on behalf of users within rooms;
|
||||
- To allow users to attest one or more UDKs using their UPK;
|
||||
- To remove Matrix IDs (MXIDs) and server names from events, similar to MSC1228;
|
||||
- To allow a user to receive one or more server-provided MXIDs mapped to a UPK.
|
||||
|
||||
### User Permanent Key (UPK)
|
||||
|
||||
The UPK is an ed25519 public key which represents a user entity. The initial
|
||||
intention is that a user will use this UPK to perform a challenge-response login
|
||||
to a homeserver and that it will become their "one true identity".
|
||||
|
||||
Central to this design is that the UPKs are user-owned and therefore the private
|
||||
key portion to each UPK is held by the user, although they could be protected with
|
||||
a passphrase and backed up to key storage on one or more homeservers if needed.
|
||||
|
||||
The UPK private portion must not be decrypted nor used serverside.
|
||||
|
||||
#### UPK Format
|
||||
|
||||
The UDK is prefixed with a version byte, then URL-safe base64-encoded, and then
|
||||
prefixed with the `~` sigil. The version byte for ed25519 is `0x01`.
|
||||
|
||||
### User Delegated Key (UDK)
|
||||
|
||||
Homeservers create a new UDK, which is an ed25519 key, on behalf of each user in
|
||||
each room. These keys are generated and stored by the server when a user joins a
|
||||
room:
|
||||
|
||||
1. The user requests to join a room;
|
||||
2. The server generates a UDK and sends the public key to the client;
|
||||
3. The client signs a UDK attestation using a UPK - this forms the link between
|
||||
the UDK and the UPK;
|
||||
4. The server generates the membership event, including the attestation, and sends
|
||||
it into the room/to federated servers, signed using the new UDK.
|
||||
|
||||
The homeserver should store all active UPK-UDK links.
|
||||
|
||||
#### UDK Format
|
||||
|
||||
The UDK is prefixed with a version byte, then URL-safe base64-encoded, and then
|
||||
prefixed with the `^` sigil. The version byte for ed25519 is `0x01`.
|
||||
|
||||
### Membership UDK Attestation
|
||||
|
||||
An attestation will include the UDK that is being attested to, and an expiry time. The
|
||||
attestation will be valid for events up until the expiry time, at which point a new
|
||||
attestation will be required.
|
||||
|
||||
The attestation `"content"` is built by the client, and then is signed with the UPK
|
||||
before being sent to the server. The attested server will then add its own signature
|
||||
using the UDK.
|
||||
|
||||
The completed attestation will take a format similar to this:
|
||||
|
||||
```
|
||||
"attestation": {
|
||||
"content": {
|
||||
"identity": ~upk_that_is_attesting",
|
||||
"delegate": "^udk_that_is_being_attested",
|
||||
"server_name": "example.com",
|
||||
"expires": 15895491111111
|
||||
},
|
||||
"signatures": {
|
||||
"~upk_that_is_attesting": {
|
||||
"ed25519": "upk_signature"
|
||||
},
|
||||
"~udk_that_is_being_attested": {
|
||||
"ed25519": "udk_signature"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The attestation contains a `"server_name"` field which contains the name of the server
|
||||
that manages the UDK. This is necessary as, without this, other servers in the room
|
||||
will not be able to work out where to route messages for this UDK.
|
||||
|
||||
The attestation `"content"` key will then be canonicalised and signed, once by the UPK
|
||||
and then once by the homeserver that issued the UDK.
|
||||
|
||||
#### Validity
|
||||
|
||||
The attestation will be valid from the point that it is sent (in effect, from the
|
||||
`"origin_server_ts"` timestamp) up until the `"expires"` timestamp.
|
||||
|
||||
Since there may be multiple membership state events with renewals over time, event
|
||||
validity is based on the attestation in the room state at (before) the event. If the attestation has expired in the room state at (before) the event, the attestation is
|
||||
considered invalid - newer attestations must not be considered when determining the
|
||||
validity period.
|
||||
|
||||
#### Authorisation rules
|
||||
|
||||
Events will continue to refer to the membership event as an auth event, with the
|
||||
main difference being that the referred-to membership event will now contain one or
|
||||
more attestations.
|
||||
|
||||
Authorisation rules will be updated to include extra clauses, that events should
|
||||
only be accepted for a specific UDK as long as there is:
|
||||
|
||||
1. A valid attestation for the UDK in the referred membership event;
|
||||
2. The event falls inclusive of the `"origin_server_ts"` and the `"expires"` of
|
||||
the attestation;
|
||||
3. An `m.room.member` event with an `"attestation"` section must contain a signature
|
||||
from the UPK.
|
||||
|
||||
To cover the possibility of an attestation not being renewed, soft-fail rules will
|
||||
be updated to include extra clauses, that events should be soft-failed when received as
|
||||
new events unless there is:
|
||||
|
||||
1. A valid attestation for the UDK in the current room state;
|
||||
2. The event falls before the `"expires"` timestamp of the attestation.
|
||||
|
||||
This prevents servers from continuing to impersonate the user with new events after
|
||||
the attestation has expired - necessary as the server owns and maintains the UDK
|
||||
keypair.
|
||||
|
||||
Some thought needs to be given on how to ban a UPK so that generating new UDKs is not
|
||||
an effective measure for evading bans.
|
||||
|
||||
### Membership event format
|
||||
|
||||
A membership event including an attestation may look something like this:
|
||||
|
||||
```
|
||||
{
|
||||
"auth_events": [ ... ],
|
||||
"prev_events": [ ... ],
|
||||
"content": {
|
||||
"avatar_url": "mxc://here/is/neilalexander.png",
|
||||
"displayname": "neilalexander",
|
||||
"membership": "join",
|
||||
"attestation": {
|
||||
"content": {
|
||||
"identity": ~upk_that_is_attesting",
|
||||
"delegate": "^udk_that_is_being_attested",
|
||||
"server_name": "example.com",
|
||||
"expires": 15895491111111
|
||||
},
|
||||
"signatures": {
|
||||
"~upk_that_is_attesting": {
|
||||
"ed25519": "upk_signature"
|
||||
},
|
||||
"~udk_that_is_being_attested": {
|
||||
"ed25519": "udk_signature"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"origin_server_ts": 1589549295296,
|
||||
"sender": "^udk_that_is_being_attested",
|
||||
"signatures": {
|
||||
"^udk_that_is_being_attested": ...
|
||||
},
|
||||
"hashes": {
|
||||
"sha256": ...,
|
||||
}
|
||||
"state_key": "^udk_that_is_being_attested",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 25,
|
||||
},
|
||||
"event_id": "$eventid",
|
||||
"room_id": "!roomid"
|
||||
}
|
||||
```
|
||||
|
||||
Note that there is no MXID in the `"sender"` and `"state_key"` fields, nor in the
|
||||
`"signatures"` field of the event itself - these are now referencing the UPKs.
|
||||
|
||||
Multiple servers wanting to join on behalf of the same user should send their own
|
||||
membership events, each with an attestation as created and signed by the user.
|
||||
There may be a need for clients to disambiguate users.
|
||||
|
||||
### Timeline event format
|
||||
|
||||
Otherwise, the event format remains unchanged, with only one exception: that the
|
||||
`"signatures"` contains the signature from the UDK, rather than from the server
|
||||
itself as today:
|
||||
|
||||
```
|
||||
{
|
||||
"auth_events": [ ... ],
|
||||
"prev_events": [ ... ],
|
||||
"content": {
|
||||
"body": "Hi!",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"origin_server_ts": 1589549295384,
|
||||
"sender": "^udk_that_is_being_attested",
|
||||
"signatures": {
|
||||
"^udk_that_is_being_attested": ...
|
||||
},
|
||||
"hashes": {
|
||||
"sha256": ...,
|
||||
}
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 26,
|
||||
},
|
||||
"event_id": "$eventid",
|
||||
"room_id": "!roomid"
|
||||
}
|
||||
```
|
||||
|
||||
#### Renewing an attestation
|
||||
|
||||
At any time, the UPK holder can issue new attestations by sending updated membership
|
||||
state events with a new attestation. This can be done either before or after the
|
||||
validity of the previous attestation has expired.
|
||||
|
||||
The previous membership event with the previous attestation must appear in the
|
||||
`auth_events` of the new membership event with the new attestation.
|
||||
|
||||
#### Removing an attestation
|
||||
|
||||
To remove an attestation, the membership event should be replaced with a new
|
||||
membership event that no longer includes an attestation.
|
||||
|
||||
Assuming there is no need to remove evidence of the attestations ever existing,
|
||||
then this will be sufficient. The previously attested servers will no longer be able
|
||||
to send events into the room on behalf of the user.
|
||||
|
||||
#### Redacting an attestation
|
||||
|
||||
To satisfy data deletion requests, or where it may be important to fully remove links
|
||||
between UDKs and UPKs for legal compliance, it should be possible to redact the
|
||||
membership events to remove the `"attestation"` section from them.
|
||||
|
||||
This may need to be done recursively, following the `"auth_events"`, to remove all
|
||||
historical attestations too.
|
||||
|
||||
TODO: Doing this may mean that other servers that try to backfill may not be able to
|
||||
verify that the events were allowed to be sent?
|
||||
|
||||
As the redaction algorithms already have rules for `m.room.member` events which will
|
||||
preserve the `"membership"` key, it should be possible to redact any other personally
|
||||
identifiable information such as the `"attestation"`, the `"display_name"` or the
|
||||
`"avatar_url"` without issue.
|
||||
|
||||
The UDK signature will remain in the event, but without the attestation, it will not
|
||||
be possible to link it to a UPK.
|
||||
|
||||
### Matrix ID to UPK mapping
|
||||
|
||||
Public keys as identifiers may enable some portability but they aren't user-friendly
|
||||
and somewhat difficult to put on a business card. For this, it is necessary to be
|
||||
able to allow users to maintain MXID mappings much as they have today.
|
||||
|
||||
However, a homeserver returning a UPK for an MXID should ideally imply that the server
|
||||
actually has some kind of association with the user and that the user is resident,
|
||||
rather than third-party servers gratuitously providing MXID mappings for users that
|
||||
they may not otherwise be aware of.
|
||||
|
||||
### User signalling
|
||||
|
||||
For things like invites, direct messages etc, it is not possible to know what the UDK
|
||||
will be before a homeserver generates one to join the room. Therefore these endpoints
|
||||
should be updated to use either:
|
||||
|
||||
- A UPK in combination with a previously-known resident server name;
|
||||
- A MXID, from which a response will contain the UPK and a resident server name.
|
||||
|
||||
In these instances, you are addressing "the user" rather than a UDK.
|
||||
|
||||
### Invites
|
||||
|
||||
TODO.
|
||||
|
||||
### Device list syncing
|
||||
|
||||
TODO.
|
||||
|
||||
### Send-to-device
|
||||
|
||||
TODO.
|
Loading…
Reference in New Issue