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

MSC1544: Key verification using QR codes #1544

Merged
merged 26 commits into from
Nov 25, 2020
Merged
Changes from 23 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
3aba9b1
initial dump of proposal
uhoreg Aug 20, 2018
3734471
mention option of having the QR code be a URL
uhoreg Aug 20, 2018
acd9a5d
add note about Bob mashing the "Verify" button prematurely
uhoreg Aug 20, 2018
95280d8
switch QR code to URL, add introductory text, add clarifications, reo…
uhoreg Sep 5, 2018
517754b
use common key verification message types as per MSC1717
uhoreg Jan 8, 2019
ba39779
add a conclusion
uhoreg Jan 8, 2019
3b0073a
allow for multiple keys to be verified, for cross-signing
uhoreg Jan 8, 2019
38689a8
add information about interacting with key requests and define cancel…
uhoreg Jan 8, 2019
10b6fd6
add more crypto magic, clarify things, add examples
uhoreg Sep 18, 2019
332b560
simplify protocol by embedding Alice's key in Bob's QR code
uhoreg Sep 19, 2019
4f83bd3
Update proposals/1543-qr_code_key_verification.md
uhoreg Jan 22, 2020
379bb79
Update proposals/1543-qr_code_key_verification.md
uhoreg Jan 22, 2020
a8c7fda
add some clarifications
uhoreg Jan 24, 2020
be9c37e
more clarifications, add comparison with SAS
uhoreg Jan 27, 2020
0b4411e
more words
uhoreg Jan 28, 2020
fcfd5d9
add examples for self-verification
uhoreg Jan 29, 2020
21ddf85
make other_user_key optional for one flow of self-verification
uhoreg Jan 29, 2020
7f93084
request can be transaction_id
uhoreg Jan 30, 2020
78b8133
use binary encoding of keys to reduce size of QR code
uhoreg Feb 6, 2020
405ac1e
add base32 option
uhoreg Feb 19, 2020
c77d04c
add base64 option
uhoreg Feb 19, 2020
ea0abe9
remove incorrect stuff
uhoreg Feb 19, 2020
a7279d9
Update proposals/1543-qr_code_key_verification.md
uhoreg Feb 27, 2020
7b3c98c
we're using the binary format
uhoreg Oct 17, 2020
0b97ac5
event IDs use $
uhoreg Nov 19, 2020
9db8cc9
Update proposals/1543-qr_code_key_verification.md
uhoreg Nov 21, 2020
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
375 changes: 375 additions & 0 deletions proposals/1543-qr_code_key_verification.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,375 @@
Bi-directional Key verification using QR codes
Copy link
Contributor

Choose a reason for hiding this comment

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

Unfortunately soru sees several issues with how this proposal is currently layed out:

1st: It does not seem to be in-line with the current spec. Sending m.key.verification.start is supposed to start the verification process, here QR codes are displayed (and thus the verification process started) before that is sent.
2nd: If both sides of the verification advertise as being able to be able to both scan and display a QR code, we get a problem: As there is no additional message exchanged on what was agreed to verify with, it could happen that both get a QR code displayed or both get a camera to scan the others code. While a button could be introduced in the UI to switch between scan and display, this scenario will likely leave many people baffled and "meh, this is broken" and then not verify each other
3rd: There is no real way to do SAS instead of QR code verification: As QR codes are displayed / scanned after receiving m.key.verification.ready, it is impossible to pick e.g. SAS instead of QR code verification. While a button could be introduced for "Oh no, I actually want to do SAS" this is bad UX.
4th: This is not future-proof: Let's say in the future an even more seemless verification method is introduced (background bluetooth/NFC or something between two phones?), how should this be started? Also before sending m.key.verification.start? What if both can do the new method and QR code? What if one displays the QR code and the other one expects to do the new method? The user is likely to think that the verification process is broken, even if there is a button to switch between different methods.
5th: This diminishes a big advantage of #2366 : One point of #2366 was to introduce m.key.verification.ready so that both parties can pick which verification method to use, if e.g. one of them is more tech savvy and uses a more tech savvy client. As verification starts prior sending m.key.verification.start this becomes impossible, diminishing its affects.

All these points seem to be fixed by not displaying / scanning QR codes until after an m.key.verification.start is sent, with the method (m.reciprocate.v2 (v2 as v1 is already implemented in many and this is a breaking change to this proposal)) and what the other person should do, based on their capabilities (m.qr_code.show.v1, m.qr_code.scan.v1). Then, after scanning a new message is sent (m.key.verification.reciprocate?) which contains the information currently in m.key.verification.start. With that, all the mentioned issues are solved.

A client can still automatically pick QR code over SAS, for example, but, as the clients agree on what to verify with prior to verifying we diminish the situation that, e.g. in the future, if there is let's say bluetooth verification, and both clients can do QR, bluetooth and SAS, one will pick to do QR and one will pick to do bluetooth. Explicitly stating what is to be done in m.key.verification.start is appropriate here.

Copy link
Contributor

Choose a reason for hiding this comment

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

Re-visiting this a bit later, soru would propose the following flow to decide which key verification method to use:

  1. A request and ready is exchanged like before
  2. Clients can then auto-pick a method (sas, reciprocate, etc.), sending a start. If a client does this automatically, it should only do so if it was also the one initiating the request.
  3. Any other client can also send a start.
  4. start gets a new, optional, attribute, priority. This is an integer. Higher integer means higher priority. A missing priority is assumed to be 0.
  5. The start with the highest priority is picked. If two have the same priority, then the current methods for determening whos start to use are being used
  6. It does not matter if the start's pick different methods.

This way it is always clear which verification method to display, in addition to allowing the flexibility a pro-user might want.

In most cases, clients would just auto-pick a method, without sending a priority at all. For the QR code scanning, the "Use emojis instead" would then send a start with a priority of 1.

A pro-user could just override whatever method the person they are trying to verify with automatically picked.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sorry for not responding earlier. The intended UX for this is that, once verification is accepted, the client displays a screen that shows the QR code and offers to scan a QR code or verify by emoji instead. This is similar to (and probably inspired by) Signal's "Verify safety numbers" screen which shows a QR code, allows the user to scan a QR code, and displays the safety numbers for users to read out. I think that by immediately showing the QR code, this makes it easier for people to verify each other.

1st: It does not seem to be in-line with the current spec. Sending m.key.verification.start is supposed to start the verification process, here QR codes are displayed (and thus the verification process started) before that is sent.

The wording in the spec is maybe slightly inaccurate. m.key.verification.start is the first message sent in a key verification. QR code scanning doesn't need messages to be exchanged, so it doesn't send an m.key.verification.start. I can add a note in this MSC about this. But if a verification method doesn't need Matrix messages to be sent, then we should avoid sending unnecessary messages.

2nd: If both sides of the verification advertise as being able to be able to both scan and display a QR code, we get a problem: As there is no additional message exchanged on what was agreed to verify with, it could happen that both get a QR code displayed or both get a camera to scan the others code.

The idea is that both sides will automatically show the QR code, and offer to scan instead.

3rd: There is no real way to do SAS instead of QR code verification: As QR codes are displayed / scanned after receiving m.key.verification.ready, it is impossible to pick e.g. SAS instead of QR code verification. While a button could be introduced for "Oh no, I actually want to do SAS" this is bad UX.

I disagree that "Oh no, I actually want to do SAS" is bad UX with the way this is intended to work. If you prompt the user for what method to use before doing anything, then you display three things: a button saying "show QR code", a button saying "scan QR code", and a button saying "verify by emoji". If you immediately show the QR code without waiting for interaction, you display three things: the QR code, a button saying "scan QR code", and a button saying "verify by emoji". So the only difference is that you show a QR code instead of a button, which means that the QR code is immediately accessible, and the user only has two buttons to read instead of three.

4th: This is not future-proof: Let's say in the future an even more seemless verification method is introduced (background bluetooth/NFC or something between two phones?), how should this be started?

It would depend on the exact verification method. If it's some sort of wireless thing, the devices could immediately start broadcasting without exchanging Matrix messages. Devices should be capable of broadcasting a wireless signal, displaying a QR code, and showing a couple buttons at the same time.

One thing to note is that even if a client displays a QR code, it is not committed to using the QR code to verify. All the other methods are still available until the m.key.verification.start is actually sent.

Copy link
Contributor

@Sorunome Sorunome Oct 20, 2020

Choose a reason for hiding this comment

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

I disagree that "Oh no, I actually want to do SAS" is bad UX with the way this is intended to work. If you prompt the user for what method to use before doing anything, then you display three things: a button saying "show QR code", a button saying "scan QR code", and a button saying "verify by emoji". If you immediately show the QR code without waiting for interaction, you display three things: the QR code, a button saying "scan QR code", and a button saying "verify by emoji". So the only difference is that you show a QR code instead of a button, which means that the QR code is immediately accessible, and the user only has two buttons to read instead of three.

The problem is that with QR code (and possible future methods) the other party does not know what the first party selected, due to no data being transmitted. Clients will inevitebly just show a QR code with a button "ohno, i want to use emojis instead", such as....element-android already does.

What about sorus suggestion of 14 days ago which solves all these issues by transmitting packages of what is actually happening?

As it stands, without advertising to display / scan QR codes we are blocking any future method which could just be automatically started in the future. We will always have to show a QR code by default.

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 problem is that with QR code (and possible future methods) the other party does not know what the first party selected, due to no data being transmitted.

The point is that the first party did not "select" anything. It is just presenting a QR code that is available to be scanned, but does not need to be.

Clients will inevitebly just show a QR code with a button "ohno, i want to use emojis instead", such as....element-android already does.

That's basically how it's intended to operate. Except that it isn't really an "ohno, I want to use emojis instead". It's "you have a choice between having the other person scan this QR code that's already displayed, or scanning their QR code, or starting an emoji-based verification." Though I suppose it's just a matter of opinion how it's viewed.

What about sorus suggestion of 14 days ago which solves all these issues by transmitting packages of what is actually happening?

We don't want to automatically "pick" anything. And sending messages when they're not needed will just increase latency.

As it stands, without advertising to display / scan QR codes we are blocking any future method which could just be automatically started in the future. We will always have to show a QR code by default.

I think that it's unlikely that we're going to add many more verification methods. Maybe one or two more, and it's unlikely that it's something that would collide with showing a QR code. I think having one method (emoji/decimal) for low-tech or not-in-person verifications, one method (QR) for more capable devices is sufficient. Maybe we might add some fancy wireless thing (NFC) but we're getting into diminishing returns territory as I'm not sure what practical benefit that would have over QR codes.

Copy link
Member

Choose a reason for hiding this comment

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

This spec is future proof - the clients can come up with UX that suits them. If we were to add another method that gets displayed before the start event like QR codes, clients could show their favourite option and have a button for "show QR code instead". Clients could also stop advertising support for QR codes in their methods, thus indicating that they really don't want a QR code.

The signaling of which client is supposed to scan vs show is largely a UX problem for the clients themselves. The verification flows do not care which person scans, so one of the clients is failing to represent its UX properly.

Additional signaling feels more like piling onto a problem than solving one, unfortunately. Clients will end up implementing it wrong or be overly confused as to why they have to stack rank two sets of capabilities.

Copy link
Contributor

Choose a reason for hiding this comment

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

This spec is future proof

Not with this MSC. As soon as another verification method is added that can be displayed etc. before any signaling, there will be some clients that disagree if they should display a qr code or the new method. For that you need explicit signaling. And, with explicit signaling under the hood, you can have still exactly the same UX for clients, only with the added benifit that you actually know what is going on. The client will reply to the ready with a start already, with whatever it picked as default. Both sides can still display "use method xyz" instead. the UX is the same, only that you are actually future-proof then.

Copy link
Member

Choose a reason for hiding this comment

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

It doesn't sound like you're reading the comments we're posting or trying to understand why the system is proposed as it is, and instead repeating the same argument over and over which has been responded to over and over. As such, I'm personally not convinced that this thread is in a state to block FCP on.

Copy link
Contributor

Choose a reason for hiding this comment

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

To soru it sounds exactly the same way, as if you aren't reading or trying to understand her comments.

So it sounds like there is some bad misscommunication here. If it helps, soru'd be willing to hop in a voicecall or something to explain better.

Copy link
Member

Choose a reason for hiding this comment

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

We've discussed this extensively in #matrix-spec:matrix.org and are largely concluding this thread as it currently stands. There's far too many points to cover in a summary comment like this, but the short version is that this MSC is going ahead regardless of this thread and this thread will continue through manifestation of a different MSC which revamps the verification process as a whole. Currently that discussion is being held on an issue until someone has a chance to convert the ideas to an MSC: https://github.com/matrix-org/matrix-doc/issues/2879

==============================================

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
uhoreg marked this conversation as resolved.
Show resolved Hide resolved
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`, and Bob responds with a `m.key.verification.ready` message.
3. Alice's client displays a QR code that Bob is able to scan, and an option to
uhoreg marked this conversation as resolved.
Show resolved Hide resolved
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 master cross-signing public key,
- what Bob thinks Alice's master cross-signing public key is,
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 it's important to keep around a copy of this key in memory for when to time comes to cross-sign it, and not just take whatever key we have stored for the user at the time we're ready to sign, otherwise a malicious homeserver admin could, with a well-timed attack, forge a cross-signing identity reset for Alice between the .ready and .start event and have Bob sign the new keys instead of the keys that were in the QR code. Am I correct in thinking this could be a possible attack?

I'm adding this as I just noticed Riot web wasn't doing it and was wondering if it's a sufficient pitfall to mention it here.

Copy link
Member

Choose a reason for hiding this comment

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

yeah, I think this may be a good point actually, in that we have to be careful not to just get the key out of the device list again when it comes to signing it.

- a random shared secret.
5. Alice scans Bob's QR code.
6. Alice's device ensures that:
- Bob's key encoded in the QR code match the key that she already has for
uhoreg marked this conversation as resolved.
Show resolved Hide resolved
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
- Alice's device knows that Bob has the correct key for her.

Thus for Bob to verify Alice's key, Alice needs to tell Bob that he has the
right key.
7. Alice's device displays a message saying that all is well. This message
tells Alice that she has the right key for Bob, and tells Bob that he has
the right key for Alice.
8. 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. This signals to Bob's device that Alice has
scanned Bob's QR code.

This message is merely a signal for Bob's device to proceed to the next
step, and is not used for verification purposes.
9. Upon receipt of the `m.key.verification.start` message, Bob's device ensures
bwindels marked this conversation as resolved.
Show resolved Hide resolved
that the shared secret matches.

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.)

If the shared secret does match, it asks Bob to confirm that Alice
has scanned the QR code.
10. Bob sees Alice's device confirm that the key matches, and presses the button
Copy link
Contributor

Choose a reason for hiding this comment

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

The Riot UX shows a "Yes" / "No" button to confirm Alice scanned the QR code. What cancellation code should be used for the .cancel event sent when "No" is pressed?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good question. If we want to use an existing error code, I think m.user would make the most sense. Or maybe m.unexpected_message.

Copy link
Contributor

Choose a reason for hiding this comment

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

Going with m.user 👍

on his device to indicate that Alice's key is verified.

Bob's verification of Alice's key hinges on Alice telling Bob the result of
her scan. Since the QR code includes what Bob thinks Alice's key is,
Alice's device can check whether Bob has the right key for her. Alice has
no motivation to lie about the result, as getting Bob to trust an incorrect
key would only affect communications between herself and Bob. Thus Alice
telling Bob that the code was scanned successfully is sufficient for Bob to
trust Alice's key, under the assumption that this communication is done
over a trusted medium (such as in-person).
11. Both devices send an `m.key.verification.done` message.

This flow allows Alice to verify Bob's key, and Bob to verify Alice's key.
Alice verifies Bob's key because she can trust the QR code that Bob displays
for her, as this is done over a trusted medium. Bob verifies Alice's key
because Alice can trust the QR code that Bob displays, and Bob can trust Alice
to tell him the result of the verification.

#### Self-verification

QR codes can also be used by a user to verify their own devices. These examples
shows Alice verifying two devices, one of them (Osborne2) having cross-signing
already set up, and the other one (Dynabook) having just logged in.
Copy link
Contributor

Choose a reason for hiding this comment

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

How do we know which device just logged in and which one not?

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 one that has not yet been signed by the cross-signing key is the one that just logged in.


In the first example, Osborne2 scans Dynabook:

1. Alice logs into her new Dynabook and wants other users to be able to trust
it via cross-signing, and to trust other devices via cross-signing.
2. Dynabook retrieves Alice's public cross-signing key from the server, and
displays a QR code that encodes:
- Dynabook's device key,
- what it thinks Alice's master key is, and
- a random shared secret.

Note that in this case, the QR code does not include Alice's master key in a
`key_<key_id>` parameter, since Dynabook does not know whether it is trusted
or not.
3. Osborne2 scans the QR code displayed by Dynabook. At this point, Osborne2
knows Dynabook's device key and can sign it with the self-signing key and
upload the signature, and can trust Dynabook for sending secrets via SSSS.
It also knows that Dynabook has the correct cross-signing key.
4. Osborne2 tells Alice that the scan was successful, and sends the
`reciprocate` message containing the shared secret.
5. Upon receipt of the `reciprocate` message, Dynabook (after checking the
shared secret) confirms with Alice that she successfully scanned the QR
code.
6. Alice confirms.
7. Dynabook now knows that it can trust Alice's cross-signing keys that it
fetched from the server.

In the second example, Dynabook scans Osborne2:

1. Alice logs into her new Dynabook and wants other users to be able to trust
it via cross-signing, and to trust other devices via cross-signing.
2. Osborne2 notices that Dynabook is a new device. Osborne2 fetches Dynabook's
identity key and displays a QR code that encodes:
- what it thinks Dynabook's key is,
- Alice's master key, and
- a random shared secret.
3. Dynabook scans the QR code shown by Osborne2. At this point, Dynabook knows
Alice's cross-signing key, and so it can trust it to sign other devices. It
also knows that Osborne2 as the correct key for it.
4. Dynabook tells Alice that the scan is successful, and sends the
`reciprocate` message containing the shared secret.
5. Upon receipt of the `reciprocate` message, Osborne2 (after checking the
shared secret) confirms with Alice that she successfully scanned the QR
code.
6. Alice confirms.
7. Osborne2 now knows that it has the correct device key for Dynabook, and can
sign it with the self-signing key and upload the signature. Osborne2 can
also trust Dynabook for sending secrets via SSSS.

### 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.v2`: 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.v2`: 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

Note: only one of the following will be supported. They are all being
uhoreg marked this conversation as resolved.
Show resolved Hide resolved
documented here while we determine how well different formats are supported.

#### Binary format
uhoreg marked this conversation as resolved.
Show resolved Hide resolved

The QR codes to be displayed and scanned using this format will encode binary
strings in the general form:

- the ASCII string "MATRIX"
- one byte indicating the QR code version (must be `0x02`)
- one byte indicating the QR code verification mode. May be one of the
Copy link
Member

Choose a reason for hiding this comment

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

Do we need the mode? What purpose does it serve?

Copy link
Member Author

Choose a reason for hiding this comment

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

It's mostly a sanity check to make sure that the scanner and the scannee agree on what mode they're doing. So that, for example, if you scan your own device, and neither of them have trusted the cross-signing key yet, it can display something sensible.

following values:
- `0x00` verifying another user with cross-signing
- `0x01` self-verifying in which the current device does trust the master key
- `0x02` self-verifying in which the current device does not yet trust the
master key
- the event ID or `transaction_id` of the associated verification
request event, encoded as:
- two bytes in network byte order (big-endian) indicating the length in
bytes of the ID as a UTF-8 string
- the ID as a UTF-8 string
- the first key, as 32 bytes. The key to use depends on the mode field:
- if `0x00` or `0x01`, then the current user's own master cross-signing public key
- if `0x02`, then the current device's device key
- the second key, as 32 bytes. The key to use depends on the mode field:
- if `0x00`, then what the device thinks the other user's master
cross-signing key is
- if `0x01`, then what the device thinks the other device's device key is
- if `0x02`, then what the device thinks the user's master cross-signing key
is
- a random shared secret, as a byte string. It is suggested to use a secret
uhoreg marked this conversation as resolved.
Show resolved Hide resolved
that is about 8 bytes long. Note: as we do not share the length of the
secret, and it is not a fixed size, clients will just use the remainder of
binary string as the shared secret.

For example, if Alice displays a QR code encoding the following binary string:

```
"MATRIX" |ver|mode| len | event ID
4D 41 54 52 49 58 02 00 00 2D 21 41 42 43 44 ...
| user's cross-signing key | other user's cross-signing key | shared secret
00 01 02 03 04 05 06 07 ... 10 11 12 13 14 15 16 17 ... 20 21 22 23 24 25 26 27
```

this indicates that Alice is verifying another user (say Bob), in response to
the request from event "!ABCD...", her cross-signing key is
uhoreg marked this conversation as resolved.
Show resolved Hide resolved
`0001020304050607...` (which is "AAECAwQFBg..." in base64), she thinks that
Bob's cross-signing key is `1011121314151617...` (which is "EBESExQVFh..." in
base64), and the shared secret is `2021222324252627` (which is "ICEiIyQlJic" in
base64).

#### Base32 format

The QR codes to be displayed and scanned using this format will encode alphanumeric
strings generated as follows:

1. generate a binary string by concatenating:
- the event ID or `transaction_id` of the associated verification
request event, encoded as:
- two bytes in network byte order (big-endian) indicating the length in
bytes of the ID as a UTF-8 string
- the ID as a UTF-8 string
- the first key, as 32 bytes. The key to use depends on the mode field as
described in step 3:
- if "0" or "1", then the user's own master cross-signing public key
- if "2", then the current device's device key
- the second key, as 32 bytes. The key to use depends on the mode field:
- if "0", then what the device thinks the other user's master
cross-signing key is
- if "1", then what the device thinks the other device's device key is
- if "2", then what the device thinks the user's master cross-signing key
is
- a random shared secret, as a byte string. It is suggested to use a secret
that is about 8 bytes long. Note: as we do not share the length of the
secret, and it is not a fixed size, clients will just use the remainder of
binary string as the shared secret.
2. encode the above string using unpadded base32 as defined in RFC4648
3. prepend the resulting string with
- the string "MATRIX"
- one character indicating the version (must by "2")
- one character indicating the QR code verification mode. May be one of the
following values:
- "0" verifying another user with cross-signing
- "1" self-verifying in which the current device does trust the master key
- "2" self-verifying in which the current device does not yet trust the
master key

The QR code is then generated using alphanumeric encoding mode.

#### Base64 format

The QR codes to be displayed and scanned using this format will be a string of
the following form:

- the string "MATRIX"
- one character indicating the version (must by "2")
- one character indicating the QR code verification mode. May be one of the
following values:
- "0" verifying another user with cross-signing
- "1" self-verifying in which the current device does trust the master key
- "2" self-verifying in which the current device does not yet trust the
master key
- the event ID or `transaction_id` of the associated verification
request event, encoded as:
- two bytes in network byte order (big-endian), encoded in unpadded base64 (3
characters), indicating the length in bytes of the ID as a UTF-8 string
- the ID as a UTF-8 string
- the first key as unpadded base64 (43 characters). The key to use depends on
the mode field:
- if "0" or "1", then the user's own master cross-signing public key
- if "2", then the current device's device key
- the second key as unpadded base64 (43 characters). The key to use depends on
the mode field:
- if "0", then what the device thinks the other user's master
cross-signing key is
- if "1", then what the device thinks the other device's device key is
- if "2", then what the device thinks the user's master cross-signing key
is
- a random shared secret, as an ASCII string. It is suggested to use a secret
that is about 11 bytes long. (This is approximately the length of 8 bytes as
a base64 string.) Note: as we do not share the length of the secret, and it
is not a fixed size, clients will just use the remainder of binary string as
the shared secret.

### Message types

#### `m.key.verification.start`

Alice's device tells Bob's device that the QR code has been scanned.

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, encoded using unpadded base64

Example:

```json
{
"method": "m.reciprocate.v1",
"m.relates_to": {
"rel_type": "m.reference",
"event_id": "!event_id_of_verification_request"
uhoreg marked this conversation as resolved.
Show resolved Hide resolved
},
"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). Since the key
verification framework allows for multiple methods to be supported, clients can
allow users to use different methods depending on their capability.

Rather than embedding the keys in the QR codes directly, the two clients could
perform an exchange similar to
[MSC1267](https://github.com/matrix-org/matrix-doc/issues/1267), and encoding
the Short Authentication String code in the QR code. However, this means that
the clients must exchange several messages before they can verify each other,
which would delay showing the QR codes. This proposal is also simpler to
implement.

This proposal does not support the case of asynchronous verification, such as
printing a QR code on a business card for others to scan. That may be address
in a separate MSC.

Security Considerations
-----------------------

The security of verifying Alice's key depends on Bob not hitting the "Verified"
button (step 10 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.