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

[WIP] MSC2697: Device dehydration #2697

Closed
wants to merge 9 commits into from
115 changes: 115 additions & 0 deletions proposals/2697-device-dehydration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# MSC2697: Device Dehydration

End-to-end encryption in Matrix relies on the sending device being able to send
megolm sessions to the recipients' devices using to-device messages. When a
uhoreg marked this conversation as resolved.
Show resolved Hide resolved
user logs into a new device, they can obtain the megolm sessions using key
backup or key sharing if another of their devices had previously received the
session. However, when a user has no logged-in devices when a message is sent,
they are unable to receive incoming megolm sessions.

One solution to this is have a dehydrated device stored (encrypted)
server-side, which may be rehydrated and used when the user creates a new
login rather than creating a new device from scratch. The new login will
receive any to-device messages that were sent to the dehydrated device.

## Proposal

### Rehydrating a device

A new parameter, `restore_device` is added to `POST /login`, indicating that the
client can restore a previously stored device. If the parameter is not
present, it defaults to `false`. If the server has a stored device that can be
used, it will respond with:

```json
{
"user_id": "full MXID",
"home_server": "home server address",
"device_data": "base64+encoded+device+data",
"device_id": "ID of dehydrated device",
Copy link
Member

Choose a reason for hiding this comment

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

What happens in the case of them providing restore_device and device_id? Is device ID ignored?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll clarify this in the text, but basically, if they specify a device_id, then it will be used in the event that the dehydrated device is not used.

"dehydration_token": "request+token"
uhoreg marked this conversation as resolved.
Show resolved Hide resolved
}
```

If the server does not have a stored device, or does not understand device
dehydration, then it will respond as if a normal login request were made.

The client will try to decrypt the device data (see below for encryption). The
client will then make a `POST /restore_device` request, with the
`dehydration_token` body parameter set to the token received from the server.
If it was successful and it wishes to use the device, then it will set the
`rehydrate` body parameter set to `true`. Otherwise, it will set `rehydrate`
to `false`. The server will return an object with properties:

- `user_id`: the user's full MXID
- `access_token`: the access token the client will use
- `home_server`: the home server's address
uhoreg marked this conversation as resolved.
Show resolved Hide resolved
- `device_id`: the device ID for the client to use.

The client will use the device ID given to determine if it should use the
dehydrated device, or if it should use a new device. Even if the client was
able to successfully decrypt the device data, it may not able to allowed to use
it. For example, two clients may race in trying to dehydrate the device; only
uhoreg marked this conversation as resolved.
Show resolved Hide resolved
one client should use the dehydrated device.

### Dehydrating a device

To upload a new dehydrated device, a client will use `POST /device/dehydrate`.
Each user has at most one dehydrated device; uploading a new dehydrated device
will remove any previously-set dehydrated device.

```json
{
"device_data": "base64+encoded+device+data",
"initial_device_name": "foo bar",
uhoreg marked this conversation as resolved.
Show resolved Hide resolved
}
```

Result:

```json
{
"device_id": "deviceid"
}
```

After the dehydrated device is uploaded, the client will upload the encryption
keys using `POST /keys/upload/{device_id}`, where the `device_id` parameter is
the device ID given in the response to `/device/dehydrate`.

FIXME: synapse already supports `POST /keys/upload/{device_id}`, but requires
that the given device ID matches the device ID of the client that made the
call. We need to (re-)add the endpoint, and allow uploading keys for the
dehydrated device.

### Device dehydration format
Copy link
Member

Choose a reason for hiding this comment

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

is it possible to break out full examples on this? Creating types for this isn't the easiest thing at the moment (hard to see what it replies with):

export interface IDehydratedDevice {
    device_id: string;
    device_data: {
        algorithm: string;

        // TODO: Fill in from MSC2697
        // https://github.com/matrix-org/matrix-doc/pull/2697
        [key: string]: any;
    }
}


FIXME: should we just reuse libolm's pickle format?

## Potential issues

FIXME:
uhoreg marked this conversation as resolved.
Show resolved Hide resolved

Copy link
Member

Choose a reason for hiding this comment

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

There is I think some side effects to expect with verifications and device list management.
A dehydrated device will appear as a regular device in keys/query right?

  1. Should we display them as a regular device in the device list manager? Wouldn't it look suspicious for user with only one login to see 2 devices?

  2. Also what happen when you request a verification to such devices? Does it impacts the self verification process (where we check for existing devices?

Copy link
Member Author

Choose a reason for hiding this comment

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

The client could tell which device is the dehydrated device by calling GET /dehydrated_device and comparing device IDs. Alternatively, (and maybe better) we could make the server throw a flag in the unsigned portion of the device key object, maybe a "dehydrated": true.

Either way, it would be up to the client to handle the issues that you listed, e.g. by displaying them differently from regular devices, omitting them from verifications, etc.

## Alternatives

Rather than uploading a dehydrated device to the server, we could instead have
the sender resend the megolm session in the case where a user had no active
devices at the time that a message was sent. However this does not solve the
issue for users who happen to never be online at the same time. But this could
be done in addition to the solution proposed here.

The sender could also send the megolm session to a the user using a public key
using some per-user mechanism.

## Security considerations

If the dehydrated device is encrypted using a weak password or key, an attacker
could access it and read the user's encrypted messages.

## Unstable prefix

While this MSC is in development, the `POST /restore_device` endpoint will be
reached at `POST /unstable/org.matrix.msc2697/restore_device`, and the `POST
/device/dehydrate` endpoint will be reached at `POST
/unstable/org.matrix.msc2697/device/dehydrate`. The `restore_device` parameter
for `POST /login` will be called `org.matrix.msc2697.restore_device`.