|
|
|
Key verification using QR codes
|
|
|
|
===============================
|
|
|
|
|
|
|
|
Problem/Background
|
|
|
|
------------------
|
|
|
|
|
|
|
|
Key verification is essential in ensuring that end-to-end encrypted messages
|
|
|
|
cannot be read by unauthorized parties. Traditionally, key verification is
|
|
|
|
done by comparing long strings. To save users from the tedium of reading out
|
|
|
|
long strings, some systems allow one party to verify the other party by
|
|
|
|
scanning a QR code; by doing this twice, both parties can verify each other.
|
|
|
|
In this proposal, we present a method for both parties to verify each other by
|
|
|
|
only scanning one QR code.
|
|
|
|
|
|
|
|
Proposal
|
|
|
|
--------
|
|
|
|
|
|
|
|
When Alice and Bob meet in person to verify keys, Alice will scan a QR code
|
|
|
|
generated by Bob's device. The QR code will encode both Bob's key as well as what Bob
|
|
|
|
thinks Alice's key is. When Alice scans the QR code, she will ensure that the
|
|
|
|
keys match what is expected, in which case, she relays this information to Bob,
|
|
|
|
who can then tell his device that the keys match.
|
|
|
|
|
|
|
|
Example flow:
|
|
|
|
|
|
|
|
1. Alice and Bob meet in person, and want to verify each other's keys.
|
|
|
|
2. Alice requests a key verification through her device by sending an
|
|
|
|
`m.key.verification.request` message (see
|
|
|
|
[MSC2241](https://github.com/matrix-org/matrix-doc/pull/2241)), with
|
|
|
|
`m.qr_code.show.v1`, `m.qr_code.scan.v1`, and `m.reciprocate.v1` listed in
|
|
|
|
`methods`.
|
|
|
|
3. Alice's client displays a QR code that Bob is able to scan, and an option to
|
|
|
|
scan Bob's QR code.
|
|
|
|
4. Bob's client prompts Bob to verify Alice's key. The prompt includes a QR
|
|
|
|
code that Alice can scan (if the `m.key.verification.request` message listed
|
|
|
|
`m.qr_code.scan.v1`), and an option to scan Alice's QR code (if the
|
|
|
|
`m.key.verification.request` message listed `m.qr_code.show.v1`). The QR
|
|
|
|
code encodes:
|
|
|
|
- Bob's Matrix user ID,
|
|
|
|
- Bob's keys that he wants Alice to verify (should contain at least his
|
|
|
|
master cross-signing key),
|
|
|
|
- what Bob thinks Alice's master cross-signing key is,
|
|
|
|
- a random shared secret.
|
|
|
|
5. Alice scans Bob's QR code.
|
|
|
|
6. Alice's device ensures that:
|
|
|
|
- the user ID in the QR code is the same as the expected user ID (which it
|
|
|
|
knows because it is the recipient of her `m.key.verification.request`
|
|
|
|
message),
|
|
|
|
- Bob's keys encoded in the QR code match the keys that she already has for
|
|
|
|
Bob, and
|
|
|
|
- Alice's cross-signing key matches the cross-signing key encoded in the QR
|
|
|
|
code.
|
|
|
|
|
|
|
|
If any of these checks fail, Alice's device displays an error message
|
|
|
|
indicating that the code is incorrect, and sends a
|
|
|
|
`m.key.verification.cancel` message to Bob's device.
|
|
|
|
|
|
|
|
Otherwise, at this point, Alice's device has now verified Bob's key, and her
|
|
|
|
device will display a message saying that all is well.
|
|
|
|
7. Alice's device sends a `m.key.verification.start` message with `method` set
|
|
|
|
to `m.reciprocate.v1` to Bob (see below). The message includes the shared
|
|
|
|
secret from the QR code.
|
|
|
|
8. Upon receipt of the `m.key.verification.start` message, Bob's device ensures
|
|
|
|
that the shared secret matches, and if so, presents a button for him to press
|
|
|
|
/after/ he has checked that Alice's device says that things match, and a
|
|
|
|
button for him to press if Alice's device indicates that the QR code is
|
|
|
|
invalid or if Alice has not yet scanned. If the shared secret does not
|
|
|
|
match, it should display an error message indicating that an attack was
|
|
|
|
attempted. (This does not affect Alice's verification of Bob's keys.)
|
|
|
|
9. Bob sees Alice's device confirm that the key matches, and presses the button
|
|
|
|
on his device to indicate that Alice's key is verified.
|
|
|
|
10. Both devices send an `m.key.verification.done` message.
|
|
|
|
|
|
|
|
### Verification methods
|
|
|
|
|
|
|
|
This proposal defines three verification methods that can be used in
|
|
|
|
`m.key.verification.request` messages (see
|
|
|
|
[MSC2241](https://github.com/matrix-org/matrix-doc/pull/2241)).
|
|
|
|
|
|
|
|
- `m.qr_code.show.v1`: means that the sender of the
|
|
|
|
`m.key.verification.request` message can show a QR code that the recipient
|
|
|
|
can scan. If the recipient can scan the QR code, it should allow the user to
|
|
|
|
do so. This method is never sent as part of a `m.key.verification.start`
|
|
|
|
message.
|
|
|
|
- `m.qr_code.scan.v1`: means that the sender of the
|
|
|
|
`m.key.verification.request` message can scan a QR code displayed by the
|
|
|
|
recipient. If the recipient can display a QR code, it should allow the user
|
|
|
|
to display it so that the sender can scan it. This method is never sent as
|
|
|
|
part of a `m.key.verification.start` message.
|
|
|
|
- `m.reciprocate.v1`: means that the sender can participate in a reciprocal
|
|
|
|
verification, either as initiator or responder, as described in the [Message
|
|
|
|
types](#message-types) section below.
|
|
|
|
|
|
|
|
### QR code format
|
|
|
|
|
|
|
|
The QR codes to be displayed and scanned using this format will encode URLs of
|
|
|
|
the form:
|
|
|
|
`https://matrix.to/#/<user-id>?request=<event-id>&action=verify&key_<keyid>=<key-in-base64>...&secret=<shared-secret>&other_user_key=<master-key-in-base64>`
|
|
|
|
(when `matrix:` URLs are specced, this will be used instead).
|
|
|
|
|
|
|
|
- `request`: is the event ID of the associated verification request event.
|
|
|
|
- `key_<key_id>`: each key that the user wants verified will have an entry of
|
|
|
|
this form, where the value is the key in unpadded base64. The QR code should
|
|
|
|
contain at least the user's master cross-signing key.
|
|
|
|
- `secret`: is a random single-use shared secret in unpadded base64. It must be
|
|
|
|
at least 256-bits long (43 characters when base64-encoded).
|
|
|
|
- `other_user_key`: the other user's master cross-signing key, in unpadded
|
|
|
|
base64. In other words, if Alice is displaying the QR code, this would be
|
|
|
|
the copy of Bob's master cross-signing key that Alice has.
|
|
|
|
|
|
|
|
The QR codes to be displayed and scanned, which are not a part of an in-person
|
|
|
|
verification (for example, for printing on business cards), will encode URLs of
|
|
|
|
the form:
|
|
|
|
`https://matrix.to/#/<user-id>?action=verify&key_<keyid>=<key-in-base64>...`
|
|
|
|
In this case, only the user scanning the QR code will verify the key of the
|
|
|
|
user whose QR code was scanned; bi-directional verification is not possible.
|
|
|
|
|
|
|
|
### Message types
|
|
|
|
|
|
|
|
#### `m.key.verification.start`
|
|
|
|
|
|
|
|
Alice's device tells Bob's device that the keys are verified. The request is
|
|
|
|
MAC'ed using the verification algorithm and verification key from the QR code.
|
|
|
|
|
|
|
|
message contents:
|
|
|
|
|
|
|
|
- `method`: `m.reciprocate.v1`
|
|
|
|
- `m.relates_to`: as per [key verification framework](https://github.com/matrix-org/matrix-doc/pull/2241)
|
|
|
|
- `secret`: the shared secret from the QR code
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
|
|
|
```json
|
|
|
|
{
|
|
|
|
"method": "m.reciprocate.v1",
|
|
|
|
"m.relates_to": {
|
|
|
|
"rel_type": "m.reference",
|
|
|
|
"event_id": "!event_id_of_verification_request"
|
|
|
|
},
|
|
|
|
"secret": "shared+secret"
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Note that this message could be sent by either the sender or the recipient of
|
|
|
|
the `m.key.verification.request` message, depending on which user scanned the
|
|
|
|
QR code.
|
|
|
|
|
|
|
|
### Cancellation
|
|
|
|
|
|
|
|
In addition to the cancellation codes specified in [the spec for
|
|
|
|
`m.key.verification.cancel`](https://matrix.org/docs/spec/client_server/r0.5.0#m-key-verification-cancel),
|
|
|
|
the following cancellation codes may be used:
|
|
|
|
|
|
|
|
- `m.qr_code.invalid`: The QR code is invalid (e.g. it is not a URL of the
|
|
|
|
required form)
|
|
|
|
|
|
|
|
The verification can also be cancelled with the error codes:
|
|
|
|
|
|
|
|
- `m.key_mismatch`: if the QR code has keys that do not match the expected
|
|
|
|
value
|
|
|
|
- `m.user_mismatch`: if the QR code is for a different user from what was expected
|
|
|
|
|
|
|
|
Tradeoffs/Alternatives
|
|
|
|
----------------------
|
|
|
|
|
|
|
|
Other methods of verifying keys, which do not require scanning QR codes, are
|
|
|
|
needed for devices that are unable to scan QR codes. One such method is
|
|
|
|
[MSC1267](https://github.com/matrix-org/matrix-doc/issues/1267).
|
|
|
|
|
|
|
|
Security Considerations
|
|
|
|
-----------------------
|
|
|
|
|
|
|
|
The first check in Step 6 in the example flow is to ensure that Bob does not
|
|
|
|
present a QR code claiming to be Carol's key. Without this check, Bob will be
|
|
|
|
able to trick Alice into verifying a key under his control, and evesdropping on
|
|
|
|
Alice's communications with Carol.
|
|
|
|
|
|
|
|
The security of verifying Alice's key depends on Bob not hitting the "Verified"
|
|
|
|
button (step 9 in the example flow) until after Alice's device indicates
|
|
|
|
success or failure. Users have a tendency to click on buttons without reading
|
|
|
|
what the screen says, but this is partially mitigated by the fact that it is
|
|
|
|
unlikely that Bob will be interacting with the device while Alice is scanning
|
|
|
|
and Alice's device will display the verification results immediately upon
|
|
|
|
scanning. Also, Bob's device will not display the button until it receives the
|
|
|
|
`m.key.verification.start` message that contains the shared secret from the QR
|
|
|
|
code, which means that an attacker would need to be physically present while
|
|
|
|
Alice and Bob verify. This issue can also be addressed by allowing Bob to
|
|
|
|
easily undo the verification if Alice's device displays an error.
|