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

MSC2241: Key verification in DMs #2241

Merged
merged 19 commits into from
Apr 18, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
314 changes: 314 additions & 0 deletions proposals/2241-e2e-verification-in-dms.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,314 @@
# Key verification in DMs

Currently, key verification is done using `to_device` messages. However, since
`to_device` messages are not part of a timeline, there is no user-visible
record of the key verification.

As well, the current key verification framework does not provide any feedback
when interacting with clients that do not support it; if a client does not
support the key verification framework, there is no way for users to discover
this other than waiting for a while and noticing that nothing is happening.

This proposal will solve both problems.

## Proposal
uhoreg marked this conversation as resolved.
Show resolved Hide resolved

The current [key verification
framework](https://matrix.org/docs/spec/client_server/r0.5.0#key-verification-framework)
will be replaced by a new framework that uses room messages rather than
`to_device` messages. Key verification messages will be sent in a [Direct
Messaging](https://matrix.org/docs/spec/client_server/r0.5.0#id185) room. If
there is no Direct Messaging room between the two users involved, the client
that initiates the key verification will create one.

In this proposal, we use "Alice" to denote the user who initiates the key
verification, and "Bob" to denote the other user involved in the key
verification.

### General framework

#### Requesting a key verification

To request a key verification, Alice will send an `m.room.message` event with 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 feels a bit sad that we're using m.room.message here. I guess it's for the benefit of clients which don't support this method, but maybe an alternative would be to send two events, one m.room.message for the fallback, which supporting clients can ignore, and another m.key.verification.request which actually starts the process

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, it's so that clients that don't support it will show something sensible. Sending two messages seems redundant, and you'd need some markup on the m.room.message so that supporting clients will know to ignore it, so it doesn't seem like much of a difference from just sticking the verification request payload in the m.room.message.

following properties in its contents:

- `body`: a fallback message to alert users that their client does not support
the key verification framework, and that they should use a different method
to verify keys. For example, "Alice is requesting to verify keys with you.
However, your client does not support this method, so you will need to use
the legacy method of key verification."

Clients that do support the key verification framework should hide the body
and instead present the user with an interface to accept or reject the key
verification.

The event may also contain `format` and `formatted_body` properties as
described in the [m.room.message
msgtypes](https://matrix.org/docs/spec/client_server/r0.5.0#m-room-message-msgtypes)
section of the spec. Clients that support the key verification should
similarly hide these from the user.
- `msgtype`: `m.key.verification.request`
- `methods`: the verification methods supported by Alice's client
- `to`: Bob's Matrix ID. Users should only respond to verification requests if
uhoreg marked this conversation as resolved.
Show resolved Hide resolved
they are named in this field. Users who are not named in this field and who
did not send this event should ignore all other events that have a
`m.reference` relationship with this event.
- `from_device`: Alice's device ID. This is required since some verification
methods may use the device IDs as part of the verification process.

Key verifications will be identified by the event ID of the key verification
request event.

Clients should ignore verification requests that have been accepted or
cancelled, or if they do not belong to the sending or target users.

The way that clients display this event can depend on which user and device the
client belongs to, and what state the verification is in. For example:

- If the verification has been completed (there is an `m.key.verification.done`
or `m.key.verification.cancel` event), the client can indicate that the
verification was successful or had an error.
- If the verification has been accepted (there is an `m.key.verification.start`
event) but has not been completed, the two devices involved can indicate that
the verification is in progress and can use this event as a place in the
room's timeline to display progress of the key verification and to interact
with the user as necessary. Other devices can indicate that the verification
is in progress on other devices.
- If the verification has not been accepted, clients for the target user can
indicate that a verification has been requested and allow the user to accept
the verification on that device. The sending client can indicate that it is
waiting for the request to be accepted, and the sending user's other clients
can indicate the that a request was initiated on a different device.

Clients may choose to display or not to display events of any other type that
reference the original request event; but it must not have any effect on the
verification itself.

#### Accepting a key verification

To accept a key verification, Bob will send an `m.key.verification.ready` event
with the following properties in its contents:

- `m.relates_to`: an object with the properties:
- `rel_type`: `m.reference`
- `event_id`: the event ID of the key verification request that is being
accepted
richvdh marked this conversation as resolved.
Show resolved Hide resolved
- `methods`: an array of verification methods that the device supports
- `from_device`: Bob's device ID. This is required since some verification
methods may use the device IDs as part of the verification process.

(Note: the form of the `m.relates_to` property is based on the current state of
[MSC2674](https://github.com/matrix-org/matrix-doc/pull/2674), but is
independent from it since this MSC does not rely on any aggregations features.)

Clients should ignore `m.key.verification.ready` events that correspond to
verification requests that they did not send.

After this, either Alice or Bob may start the verification by sending an
`m.key.verification.start` event with the following properties in its contents:

- `m.relates_to`: an object with the properties:
- `rel_type`: `m.reference`
- `event_id`: the event ID of the key verification request that is being
started
- `method`: the key verification method that is being used. This should be a
method that both Alice's and Bob's devices support.
- `from_device`: The user's device ID.
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need a three-way handshake? Once Alice starts that verification why does Bob need a m.key.verification.ready? Can he not just jump to m.key.verification.start?

Copy link
Contributor

Choose a reason for hiding this comment

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

It allows negotiating a common set of methods and then picking your preferred one. A client needs to send all methods it supports in start, but it may prefer a specific one, if supported. Then the other client can reply with the ones it prefers in "ready" and then the first client can choose its preferred method. If you reduce that to 2 steps, Either the first client sends it preferred methods instead of all of them, which may lead to no matching methods or it the second client picks one that is suboptimal for the first clients screen. Having 3 steps avoids that pretty much.

Copy link
Contributor

Choose a reason for hiding this comment

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

I see. Would it make sense to make that a bit more clear? It may be helpful to prove a high-level overview of the flow somewhere. Maybe the following can be inserted:

At a high level a successful verification looks like this:

  1. Alice sends a m.room.message event to the room specifying the target and includes supported methods and a fallback message for clients lacking support.
  2. Bob accepts the verification request with a m.key.verification.ready event and includes their supported methods.
  3. Either Alice or Bob selects a method and initiates the verification with a m.key.verification.start event.
  4. Verification proceeds according to the selected method.
  5. Both Alice and Bob send a m.key.verification.done event.

I think this gives a quick overview and can help speeding up the rest of the concrete details.

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 reasoning for m.key.verification.ready is given in #2366 so I don't think we need to repeat it here.


If both Alice and Bob send an `m.key.verification.start` message, and they both
specify the same verification method, then the event sent by the user whose
user ID is the smallest is used, and the other event is ignored. If they both
send an `m.key.verification.start` message and the method is different, then
the verification should be cancelled with a `code` of `m.unexpected_message`.

After the `m.key.verification.start` event is sent, the devices may exchange
messages (if any) according to the verification method in use.

#### Rejecting a key verification

To reject a key verification, Alice or Bob will send an
`m.key.verification.cancel` event with the following properties in its
contents:

- `m.relates_to`: an object with the properties:
- `rel_type`: `m.reference`
- `event_id`: the event ID of the key verification that is being cancelled
- `reason`: A human readable description of the `code`. The client should only
rely on this string if it does not understand the `code`.
- `code`: The error code for why the process/request was cancelled by the
user. The contents are the same as the `code` property of the currently
defined [`m.key.verification.cancel` to-device
event](https://matrix.org/docs/spec/client_server/r0.5.0#m-key-verification-cancel),
or as defined for specific key verification methods.

This message may be sent at any point in the key verification process. Any
subsequent key verification messages relating to the same request are ignored.
However, this does not undo any verifications that have already been done.

#### Concluding a key verification

When the other user's key is verified and no more messages are expected, each
party will send an `m.key.verification.done` event with the following
properties in its contents:

- `m.relates_to`: an object with the properties:
- `rel_type`: `m.reference`
- `event_id`: the event ID of the key verification that is being concluded

This provides a record within the room of the result of the verification.

Any subsequent key verification messages relating to the same request are
ignored.

Although a client may have successfully completed its side of the verification,
it may wait until receiving an `m.key.verification.done` (or
`m.key.verification.cancel`) event from the other device before informing the
user that the verification was successful or unsuccessful.

#### Other events

Key verification methods may define their own event types, or extensions to the
above event types. All events sent as part of a key verification process
should have an `m.relates_to` property as defined for
`m.key.verification.accept` or `m.key.verification.cancel` events.

Clients should ignore events with an `m.relates_to` that have a `rel_type` of
`m.reference` that refer to a verification where it is neither the requester
nor the accepter.
uhoreg marked this conversation as resolved.
Show resolved Hide resolved

Clients should not redact or edit verification messages. A client may ignore
redactions or edits of key verification messages, or may cancel the
verification with a `code` of `m.unexpected_message` when it receives a
redaction or edit.

### SAS verification

The messages used in SAS verification are the same as those currently defined,
except that instead of the `transaction_id` property, an `m.relates_to`
property, as defined above, is used instead.
Comment on lines +186 to +188
Copy link
Contributor

@jplatte jplatte Jul 24, 2020

Choose a reason for hiding this comment

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

As noted by @poljar, this makes it very complicated to implement this MSC in Ruma, since previously we could just have a single content struct definition for every event type. With this, we'd have to use different definitions per event "kind" (ephemeral / message / state / to-device / ...).

Is there any chance of unified content objects between the to-device and message events?

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess this is no longer relevant. This has been implemented, it turned out manageable. I can't mark it as resolved myself though.


If the key verification messages are encrypted, the hash commitment sent in the
`m.key.verification.accept` message MUST be based on the decrypted
`m.key.verification.start` message contents, and include the `m.relates_to`
field, even if the decrypted message contents do not include that field. For
example, if Alice sends a message to start the SAS verification:

```json
{
"content": {
"algorithm": "m.megolm.v1.aes-sha2",
"ciphertext": "ABCDEFG...",
"device_id": "Dynabook",
"sender_key": "alice+sender+key",
"session_id": "session+id",
"m.relates_to": {
"rel_type": "m.reference",
"event_id": "$verification_request_event"
}
},
"event_id": "$event_id",
"origin_server_ts": 1234567890,
"sender": "@alice:example.org",
"type": "m.room.encrypted",
"room_id": "!room_id:example.org"
}
```

which, when decrypted, yields:

```json
{
"room_id": "!room_id:example.org",
"type": "m.key.verification.start",
"content": {
"from_device": "Dynabook",
"hashes": [
"sha256"
],
"key_agreement_protocols": [
"curve25519"
],
"message_authentication_codes": [
"hkdf-hmac-sha256"
],
"method": "m.sas.v1",
"short_authentication_string": [
"decimal",
"emoji"
]
}
}
```

then the hash commitment will be based on the message contents:

```json
{
"from_device": "Dynabook",
"hashes": [
"sha256"
],
"key_agreement_protocols": [
"curve25519"
],
"message_authentication_codes": [
"hkdf-hmac-sha256"
],
"method": "m.sas.v1",
"short_authentication_string": [
"decimal",
"emoji"
],
"m.relates_to": {
"rel_type": "m.reference",
"event_id": "$verification_request_event"
}
}
```

## Alternatives

Messages sent by the verification methods, after the initial key verification
request message, could be sent as to-device messages. The
`m.key.verification.ready`, `m.key.verification.cancel`, and
`m.key.verification.done` messages must be still be sent in the room, as the
`m.key.verification.ready` notifies the sender's other devices that the request
has been acknowledged, and the `m.key.verification.cancel` and
`m.key.verification.done` provide a record of the status of the key
verification.

However, it seems more natural to have all messages sent via the same
mechanism.

## Potential issues
uhoreg marked this conversation as resolved.
Show resolved Hide resolved
turt2live marked this conversation as resolved.
Show resolved Hide resolved

If a user wants to verify their own device, this will require the creation of a
Direct Messaging room with themselves. Instead, clients may use the current
`to_device` messages for verifying the user's other devices.

Direct Messaging rooms could have end-to-end encryption enabled, and some
clients can be configured to only send decryption keys to verified devices.
Key verification messages should be granted an exception to this (so that
decryption keys are sent to all of the target user's devices), or should be
sent unencrypted, so that unverified devices will be able to be verified.

Users might have multiple Direct Messaging rooms with other users. In this
case, clients could need to prompt the user to select the room in which they
want to perform the verification, or could select a room.

## Security considerations

Key verification is subject to the room's visibility settings, and may be
visible to other users in the room. However, key verification does not rely on
secrecy, so this will no affect the security of the key verification. This may
reveal to others in the room that Alice and Bob know each other, but this is
already revealed by the fact that they share a Direct Messaging room.
Copy link
Member

Choose a reason for hiding this comment

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

Does this mean that clients have to be careful to ignore key verification messages not "directed" at them? Is there a situation where the DMness of a room is inconsistent between servers? E.g. you are in a 3-way room that the one of the other participants thinks is a DM room, but you don't, what happens?

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 think that clients should be able to respond to requests in non-DM rooms. So if clients don't agree on which rooms are DM, it should still work. If you try to do a verification in Matrix HQ, you'll just annoy everyone and someone will tell you to do it elsewhere.


This framework allows users to see what key verifications they have performed
in the past. However, since key verification messages are not secured, this
should not be considered as authoritative.

## Conclusion

By using room messages to perform key verification rather than `to_device`
messages, the user experience of key verification can be improved.