Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MSC2787: Portable Identities #2787

Draft
wants to merge 8 commits into
base: old_master
Choose a base branch
from
Draft
317 changes: 317 additions & 0 deletions proposals/2787-portable-identities.md
Original file line number Diff line number Diff line change
@@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- How to handle multiple homeservers joining the same room, since they will have
- How to handle the same user on multiple homeservers joining the same room, since they will have

i assume?

How about we have a separate UDK and membership event for every device in the room (whether it's on the same server or not)? That way we can kill device lists.

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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will be required if only for key rotation for future algorithm upgrades.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, this might be more important than it seems. What if your UPK private key is compromised, how can you retrieve back your identity. Currently the homeserver can reset your credentials, so at the very least something similar has to be included.

Some considerations

  • If we detach the mxid from the UPK, then there is an ambiguity when the UPK private key is compromised, i.e. is the old key stolen and the user made a new UPK to link the mxid identity, or did the user give up on that mxid. Which is the true identity of the user, the UPK or the mxid?
  • The identity is better verified via a x509 certificate cross-signed by all authorized servers (in case one of them is compromised). Then if the UPK is compromised, the user can use their 3pid to reauthenticate and create a new certificate and UPK. Cross verification between servers needs more thought though.
  • For an x509 certificate, the main CA should be either the identity server (s) where you registered your 3pid (cross confirming the identity and having at least a common subset of 3pids) or your homeserver(s). For the former it would be more straightforward to reset one's identity even if one of the identity servers is comprised, but for the latter, you should make sure all servers should simultaneously agree with the identity, e.g. if a redaction is requested, it should be announced to the other servers which contact the user and confirm the validity.
  • The UPK should be occasionally checked against other authorized servers to make sure it was not flagged as compromised.
  • Having the UPK have a finite lifetime seems useful, but what if the user did not log in in a long time? How do we know it is not comprised in the meantime?

- How to handle device list syncing and send-to-device messages;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

KILL WITH 🔥

- The extent to which users should be involved in attesting MXID-to-UPK mappings.
Copy link
Member

@ara4n ara4n Sep 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ability for a server admin to unilaterally migrate their users between domains is Very Desirable™️ (so you can rebrand vector.im to riot.im to element.io without the users having to opt into it, or for that matter move users from uk.example.com to us.example.com - or add backup.example.com or cluster2.example.com as a valid home for the users, etc.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be interesting if the user could override this using their keys. For example the server admin can say "also forward messages here" or "stop forwarding messages to me". But the user can sign an attestation saying "I no longer trust this server, do not send messages there and they may no longer redirect my messages".

Of course this simple solution is prone to "fork bomb" tactics. But something like "Servers A and B are my only trusted servers at this point" could be used to be more robust.

I guess what I am saying is that it would make sense to have temporary (but indefinite) delegated consent. The homeservers can then manage these mappings but the user can reset and revoke this at any time.


## 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line 28: undefined concept error: delegated & permanent user identities are not defined concepts

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
Copy link
Member

@ara4n ara4n Sep 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as per above, i'm pretty sure we need servers to be able to move users around without the users consenting to it (at least for the non-p2p world)

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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hum, shouldn't this be the same as their Secret Storage key from 4S? Last thing we need is to maintain separate cryptographic login passphrase from the recovery passphrase. Also, this means this supersedes #1699

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, we'll need to figure out how to rotate this key (i.e. how password reset works)

- 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;
Copy link
Member

@ara4n ara4n Sep 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, do we not get a different UDK per room? avoiding correlation of users between different rooms if they blow away their UPK->UDK mappings feels very desirable (and is an expectation i've set to many VIPs to expect in the future, much to their happiness, based on #1228's precedent)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If all of the UDKs are signed with the same UPK how will this provide anonymity?

Furthermore I am struggling to identify what the point of all this is if you want to be anonymous. It seems that the point of this signing is allowing the server to join the room on behalf of you. If you want to join anonymously why not just let the server join without any signature?

I guess the reason is that if you want to then move the chat somewhere else you need to prove that it is the "same you". In that case we still don't want to link it to the UPK. But you could generate effectively a new UPK for each ID you want to have and join the room with that. Of course managing these is a mess but you could just make it a KDF of your "primary" UPK. Then if you want to prove that it is actually "you" you can sign your new UPK with your primary UPK.

Or to make better terms: UPK user primary key. URK user room key. SRK server room key. To join a room anonymously the server generates a SRK, you generate a URK and sign the server's SRK. That is then logged to the room. In the future you can migrate server by signing the new server's SRK. If you want to de-anonymize yourself you can sign your URK with your UPK to prove that it is you.

But there isn't really a difference between the UPK and URK in this picture. You can view them both as different identities. And by cross-signing you can "join" them.

So to loop around to the start if you want anonymity I think you need a new UPK per room, not a new UDK. Unless I am missing something.

- 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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, we seem to have lost all the room_id fun that MSC1228 provides, which is Really Important because it means we can kill off signing keys (i think?) and thus massively speeds up room joins, and fixes matrix-org/synapse#3121 and stuff. If this MSC completely replaces #1228, then we should merge it in.

- 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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of the UPK representing the whole user's entity, it should be more of a profile. There is a security issue if the UPK is a single point of failure and identity. Instead, the matrix server should continuously attest the validity of the UPK, not just the UDK.

The idea of a profile, makes it possible to have multiple UPK to be associated with specific sets of identities with controllable knowledge of one another, e.g. Alice has an account on her homeserver and Bob's server, and Alice's server knows about the relation between those 2, but Bob's doesn't. So on Alice's server she links 2 UPKs to her account, while on Bob, just one. This makes more sense in a context of work/family/internet profiles each managed by different servers. Similarly the devices can have a subset of profiles that can be accessible.

As for the security, it depends on the model that the user has. If the user has a UPK registered at a single server, than that server should be in change of the sole identification and attestation of that user. If it's distributed, than all servers should agree to the new identity, and individually confirm the new identity, from a registered 3pid etc.

For that reason, I think an additional User Identity Certificate (UIC) should be added and tracked by all relevant servers. The UPK creates and signs the UIC with the servers that co-manage this user/profile, and the servers co-sign the certificate and save a copy. Optionally a global 3pid or an identity server can be included in it, that all servers can reference, otherwise the user has to register specific identification for each server. Then, in the event of a UPK update/rollover, the server can announce this event to all relevant servers if the user cannot access one of the servers when updating.

In the event of requesting a UPK reset or cancellation, the server confirms their identity and forwards the request to all relevant servers to confirm as well, and if all agree the event passes and a new identity is made. If someone else tries to impersonate, than it has to have access to all the identities registered for each server. This should be independent of client stored information, because presumably that is the source of the account being compromised.

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`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, why bury the version byte within the base64 rather than the ~1:..... approach from MSC1228, ooi?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The order of this is awkward because it is reversed. Why not say "The UDK is a tidle sigil then the base64 of both a version byte and the public key".

I also agree that keeping the version byte outside is much more flexible for future changes. How about this:

The UDK is the ~ sigil, followed by the version as a base-10 ascii number. Followed by a : and the URL-safe base64-encoded public key.
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it'd be good to justify we we need an expiry time here (especially if it means that non-NTP-synced hosts are going to get horribly confused)

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": {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"~udk_that_is_being_attested": {
"^udk_that_is_being_attested": {

i think?

"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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hum. can't the server just emit events with an origin_server_ts before the expires ts?


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": {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"~udk_that_is_being_attested": {
"^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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the point of expiry if we can issue new attestations that invalidate the old ones? It seems that this just means that if I go on vacation for a month my sever stops being able to effectively handle my messages.

It seems to me that there is no expiry that is both short enough to be useful and long enough to avoid timing out when someone is offline.

What is the threat model if the latest attestation is trusted forever? As a user I can send new attestations to other servers in the room and they will trust me because it is newer. The only server I can't convince is the malicious one, but even if its attestation expired what difference would that make. Would other users of that server be warned that the attestation is out of date? Assuming that e2ee is enabled the only benefit to doing this should be getting metadata, but the server gets metadata from its users anyways.


#### Removing an attestation

To remove an attestation, the membership event should be replaced with a new
membership event that no longer includes an attestation.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Presumably this needs to be signed by the UPK or it can be spoofed?


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?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I"m not completely convinced that we need all this expiration stuff. We trust the server to do many things on our behalf, but we can rely on e2ee to stop them spoofing messages.


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
Copy link

@jonaharagon jonaharagon Jan 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, would it be strictly necessary to operate a homeserver to map an MXID to a UPK? If a UPK is static (which is my understanding?), I would be interested in a discovery endpoint that could be handled by any webserver, for instance https://example.com/.well-known/matrix/id/jonah for @jonah:example.com could return a response containing a UPK and a resident homeserver of my choosing.

I envision a use-case where it is trivial to create many MXID aliases pointing to a single account, and making it easy for organizations and individuals on budget/shared hosting platforms to use "branded domain" MXIDs. Multiple discovery methods could extend to non-HTTP methods as well, perhaps a number are implemented in some prioritized order:

  1. Query homeserver at server_name for MXID in database
  2. Query .well-known endpoint for MXID mapping
  3. Query DNS TXT record for MXID mapping
  4. ...?

Copy link
Contributor Author

@neilalexander neilalexander Jan 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So far I have worked on the assumption that the user-homeserver attestations would need to remain up-to-date to maintain an alias, and that other homeservers would verify those attestations, otherwise it creates a big social problem in that someone can just buy a domain and start assigning unwanted aliases to users (I'm envisaging things like @unknowing_user:supportsthenazis.net).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that in the next section here, but I don't think I am as convinced this is an issue personally. I don't see what is currently stopping me now from operating that supportsthenazis.net homeserver and creating users named anything I want, just as nothing is stopping me from operating an email server that forwards [email protected] to [email protected].

I don't think there is a societal expectation that such a username is always someone's canonical ID online for this reason. If I've never included a certain username on my websites/business cards/etc., I don't think someone would immediately assume it belongs to me if I just so happened to receive it.

Invitations in Matrix could also display both a "From" and a "To" field, and I could judge invitations based on that.

My other assumption is that in this proposal, MXIDs will only be used to identify a user's UPK, but are not actually a part of the user's canonical profile, so after the initial invite to a room the MXID would be irrelevant anyways.

If you do want to include MXIDs in user profiles, I still don't see why this wouldn't be possible. If Bob wants to verify @alice:example.com belongs to Alice:

  1. Bob's homeserver checks example.com for an MXID mapping
  2. That mapping is followed to Alice's (presumed) actual resident homeserver
  3. Alice's homeserver returns a profile object signed with her user key that includes @alice:example.com as a known alias.

That should function as two-way verification without requiring any special software on example.com itself. In this case, profiles could even have define multiple aliases and set one as a canonical alias if it is desired to have a displayed MXID in user profiles.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could also be solved by signing the UPK with the associated private key. So https://chat.example/.well-known/matrix/id/%40kevincox:chat.example would contain a signed attestation that kevincox:chat.example is a MXID for the signing UPK. This can be trusted beacuse

  • The domain is validated by the https cert.
  • The UPK is validated by the signature.

It should also be possible/required to provide an expiry so that a malicious domain owner can't host that attestation forever and pretend to be you.


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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed - being able to map @matthew:matrix.org to the right UPK is super important for compatibility. Could we put the mxid in the attestation rather than server_name to achieve this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could also be solved via the comment above: https://github.com/matrix-org/matrix-doc/pull/2787/files#r563198856

The client would look up the URL and get the associated UPK and signed proof.


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.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is not an appropriate usage of the UDK/UPK.

When Alice wants to invite, message, etc. Bob, than she needs to know where to find Bob, either through a mxid or 3pid+identity server. She then contact's a server (or all of them) where Bob is registered, Charlie's for example, and sends the requests there to be redirected to Bob's devices. Then Bob responds with whichever identity he prefers to use with Alice and the UPK/UDK for that server.

If for example the server identity at that server is decommissioned, Bob is responsible to give the appropriate redirect to the server where to be found next. If the server is not accessible at all, Bob should have prepared a separate identity before and sent a request to decommission that identity to all the rooms he is in. This could also be sent and tracked by the servers themselves, doing the necessary re-routing and announcing the identity change for future usages as well.


### Invites

TODO.

### Device list syncing

TODO.

### Send-to-device

TODO.