|
| 1 | +Bi-directional Key verification using QR codes |
| 2 | +============================================== |
| 3 | + |
| 4 | +Problem/Background |
| 5 | +------------------ |
| 6 | + |
| 7 | +Key verification is essential in ensuring that end-to-end encrypted messages |
| 8 | +cannot be read by unauthorized parties. Traditionally, key verification is |
| 9 | +done by comparing long strings. To save users from the tedium of reading out |
| 10 | +long strings, some systems allow one party to verify the other party by |
| 11 | +scanning a QR code; by doing this twice, both parties can verify each other. |
| 12 | +In this proposal, we present a method for both parties to verify each other by |
| 13 | +only scanning one QR code. |
| 14 | + |
| 15 | +Proposal |
| 16 | +-------- |
| 17 | + |
| 18 | +When Alice and Bob meet in person to verify keys, Alice will scan a QR code |
| 19 | +generated by Bob's device. The QR code will encode both Bob's key as well as what Bob |
| 20 | +thinks Alice's key is. When Alice scans the QR code, she will ensure that the |
| 21 | +keys match what is expected, in which case, she relays this information to Bob, |
| 22 | +who can then tell his device that the keys match. |
| 23 | + |
| 24 | +### Example flow |
| 25 | + |
| 26 | +1. Alice and Bob meet in person, and want to verify each other's keys. |
| 27 | +2. Alice requests a key verification through her device by sending an |
| 28 | + `m.key.verification.request` message (see |
| 29 | + [MSC2241](https://github.com/matrix-org/matrix-doc/pull/2241)), with |
| 30 | + `m.qr_code.show.v1`, `m.qr_code.scan.v1`, and `m.reciprocate.v1` listed in |
| 31 | + `methods`, and Bob responds with a `m.key.verification.ready` message. |
| 32 | +3. Alice's client displays a QR code that Bob is able to scan, and an option to |
| 33 | + scan Bob's QR code. |
| 34 | +4. Bob's client prompts Bob to verify Alice's key. The prompt includes a QR |
| 35 | + code that Alice can scan (if the `m.key.verification.request` message listed |
| 36 | + `m.qr_code.scan.v1`), and an option to scan Alice's QR code (if the |
| 37 | + `m.key.verification.request` message listed `m.qr_code.show.v1`). The QR |
| 38 | + code encodes: |
| 39 | + - Bob's master cross-signing public key, |
| 40 | + - what Bob thinks Alice's master cross-signing public key is, |
| 41 | + - a random shared secret. |
| 42 | +5. Alice scans Bob's QR code. |
| 43 | +6. Alice's device ensures that: |
| 44 | + - Bob's key encoded in the QR code matches the key that she already has for |
| 45 | + Bob, and |
| 46 | + - Alice's cross-signing key matches the cross-signing key encoded in the QR |
| 47 | + code. |
| 48 | + |
| 49 | + If any of these checks fail, Alice's device displays an error message |
| 50 | + indicating that the code is incorrect, and sends a |
| 51 | + `m.key.verification.cancel` message to Bob's device. |
| 52 | + |
| 53 | + Otherwise, at this point: |
| 54 | + - Alice's device has now verified Bob's key, and |
| 55 | + - Alice's device knows that Bob has the correct key for her. |
| 56 | + |
| 57 | + Thus for Bob to verify Alice's key, Alice needs to tell Bob that he has the |
| 58 | + right key. |
| 59 | +7. Alice's device displays a message saying that all is well. This message |
| 60 | + tells Alice that she has the right key for Bob, and tells Bob that he has |
| 61 | + the right key for Alice. |
| 62 | +8. Alice's device sends a `m.key.verification.start` message with `method` set |
| 63 | + to `m.reciprocate.v1` to Bob (see below). The message includes the shared |
| 64 | + secret from the QR code. This signals to Bob's device that Alice has |
| 65 | + scanned Bob's QR code. |
| 66 | + |
| 67 | + This message is merely a signal for Bob's device to proceed to the next |
| 68 | + step, and is not used for verification purposes. |
| 69 | +9. Upon receipt of the `m.key.verification.start` message, Bob's device ensures |
| 70 | + that the shared secret matches. |
| 71 | + |
| 72 | + If the shared secret does not match, it should display an error message |
| 73 | + indicating that an attack was attempted. (This does not affect Alice's |
| 74 | + verification of Bob's keys.) |
| 75 | + |
| 76 | + If the shared secret does match, it asks Bob to confirm that Alice |
| 77 | + has scanned the QR code. |
| 78 | +10. Bob sees Alice's device confirm that the key matches, and presses the button |
| 79 | + on his device to indicate that Alice's key is verified. |
| 80 | + |
| 81 | + Bob's verification of Alice's key hinges on Alice telling Bob the result of |
| 82 | + her scan. Since the QR code includes what Bob thinks Alice's key is, |
| 83 | + Alice's device can check whether Bob has the right key for her. Alice has |
| 84 | + no motivation to lie about the result, as getting Bob to trust an incorrect |
| 85 | + key would only affect communications between herself and Bob. Thus Alice |
| 86 | + telling Bob that the code was scanned successfully is sufficient for Bob to |
| 87 | + trust Alice's key, under the assumption that this communication is done |
| 88 | + over a trusted medium (such as in-person). |
| 89 | +11. Both devices send an `m.key.verification.done` message. |
| 90 | + |
| 91 | +This flow allows Alice to verify Bob's key, and Bob to verify Alice's key. |
| 92 | +Alice verifies Bob's key because she can trust the QR code that Bob displays |
| 93 | +for her, as this is done over a trusted medium. Bob verifies Alice's key |
| 94 | +because Alice can trust the QR code that Bob displays, and Bob can trust Alice |
| 95 | +to tell him the result of the verification. |
| 96 | + |
| 97 | +#### Self-verification |
| 98 | + |
| 99 | +QR codes can also be used by a user to verify their own devices. These examples |
| 100 | +shows Alice verifying two devices, one of them (Osborne2) having cross-signing |
| 101 | +already set up, and the other one (Dynabook) having just logged in. |
| 102 | + |
| 103 | +In the first example, Osborne2 scans Dynabook: |
| 104 | + |
| 105 | +1. Alice logs into her new Dynabook and wants other users to be able to trust |
| 106 | + it via cross-signing, and to trust other devices via cross-signing. |
| 107 | +2. Dynabook retrieves Alice's public cross-signing key from the server, and |
| 108 | + displays a QR code that encodes: |
| 109 | + - Dynabook's device key, |
| 110 | + - what it thinks Alice's master key is, and |
| 111 | + - a random shared secret. |
| 112 | + |
| 113 | + Note that in this case, the QR code does not include Alice's master key in a |
| 114 | + `key_<key_id>` parameter, since Dynabook does not know whether it is trusted |
| 115 | + or not. |
| 116 | +3. Osborne2 scans the QR code displayed by Dynabook. At this point, Osborne2 |
| 117 | + knows Dynabook's device key and can sign it with the self-signing key and |
| 118 | + upload the signature, and can trust Dynabook for sending secrets via SSSS. |
| 119 | + It also knows that Dynabook has the correct cross-signing key. |
| 120 | +4. Osborne2 tells Alice that the scan was successful, and sends the |
| 121 | + `reciprocate` message containing the shared secret. |
| 122 | +5. Upon receipt of the `reciprocate` message, Dynabook (after checking the |
| 123 | + shared secret) confirms with Alice that she successfully scanned the QR |
| 124 | + code. |
| 125 | +6. Alice confirms. |
| 126 | +7. Dynabook now knows that it can trust Alice's cross-signing keys that it |
| 127 | + fetched from the server. |
| 128 | + |
| 129 | +In the second example, Dynabook scans Osborne2: |
| 130 | + |
| 131 | +1. Alice logs into her new Dynabook and wants other users to be able to trust |
| 132 | + it via cross-signing, and to trust other devices via cross-signing. |
| 133 | +2. Osborne2 notices that Dynabook is a new device. Osborne2 fetches Dynabook's |
| 134 | + identity key and displays a QR code that encodes: |
| 135 | + - what it thinks Dynabook's key is, |
| 136 | + - Alice's master key, and |
| 137 | + - a random shared secret. |
| 138 | +3. Dynabook scans the QR code shown by Osborne2. At this point, Dynabook knows |
| 139 | + Alice's cross-signing key, and so it can trust it to sign other devices. It |
| 140 | + also knows that Osborne2 as the correct key for it. |
| 141 | +4. Dynabook tells Alice that the scan is successful, and sends the |
| 142 | + `reciprocate` message containing the shared secret. |
| 143 | +5. Upon receipt of the `reciprocate` message, Osborne2 (after checking the |
| 144 | + shared secret) confirms with Alice that she successfully scanned the QR |
| 145 | + code. |
| 146 | +6. Alice confirms. |
| 147 | +7. Osborne2 now knows that it has the correct device key for Dynabook, and can |
| 148 | + sign it with the self-signing key and upload the signature. Osborne2 can |
| 149 | + also trust Dynabook for sending secrets via SSSS. |
| 150 | + |
| 151 | +### Verification methods |
| 152 | + |
| 153 | +This proposal defines three verification methods that can be used in |
| 154 | +`m.key.verification.request` messages (see |
| 155 | +[MSC2241](https://github.com/matrix-org/matrix-doc/pull/2241)). |
| 156 | + |
| 157 | +- `m.qr_code.show.v2`: means that the sender of the |
| 158 | + `m.key.verification.request` message can show a QR code that the recipient |
| 159 | + can scan. If the recipient can scan the QR code, it should allow the user to |
| 160 | + do so. This method is never sent as part of a `m.key.verification.start` |
| 161 | + message. |
| 162 | +- `m.qr_code.scan.v2`: means that the sender of the |
| 163 | + `m.key.verification.request` message can scan a QR code displayed by the |
| 164 | + recipient. If the recipient can display a QR code, it should allow the user |
| 165 | + to display it so that the sender can scan it. This method is never sent as |
| 166 | + part of a `m.key.verification.start` message. |
| 167 | +- `m.reciprocate.v1`: means that the sender can participate in a reciprocal |
| 168 | + verification, either as initiator or responder, as described in the [Message |
| 169 | + types](#message-types) section below. |
| 170 | + |
| 171 | +### QR code format |
| 172 | + |
| 173 | +The QR codes to be displayed and scanned using this format will encode binary |
| 174 | +strings in the general form: |
| 175 | + |
| 176 | +- the ASCII string "MATRIX" |
| 177 | +- one byte indicating the QR code version (must be `0x02`) |
| 178 | +- one byte indicating the QR code verification mode. May be one of the |
| 179 | + following values: |
| 180 | + - `0x00` verifying another user with cross-signing |
| 181 | + - `0x01` self-verifying in which the current device does trust the master key |
| 182 | + - `0x02` self-verifying in which the current device does not yet trust the |
| 183 | + master key |
| 184 | +- the event ID or `transaction_id` of the associated verification |
| 185 | + request event, encoded as: |
| 186 | + - two bytes in network byte order (big-endian) indicating the length in |
| 187 | + bytes of the ID as a UTF-8 string |
| 188 | + - the ID as a UTF-8 string |
| 189 | +- the first key, as 32 bytes. The key to use depends on the mode field: |
| 190 | + - if `0x00` or `0x01`, then the current user's own master cross-signing public key |
| 191 | + - if `0x02`, then the current device's device key |
| 192 | +- the second key, as 32 bytes. The key to use depends on the mode field: |
| 193 | + - if `0x00`, then what the device thinks the other user's master |
| 194 | + cross-signing key is |
| 195 | + - if `0x01`, then what the device thinks the other device's device key is |
| 196 | + - if `0x02`, then what the device thinks the user's master cross-signing key |
| 197 | + is |
| 198 | +- a random shared secret, as a byte string. It is suggested to use a secret |
| 199 | + that is about 8 bytes long. Note: as we do not share the length of the |
| 200 | + secret, and it is not a fixed size, clients will just use the remainder of |
| 201 | + binary string as the shared secret. |
| 202 | + |
| 203 | +For example, if Alice displays a QR code encoding the following binary string: |
| 204 | + |
| 205 | +``` |
| 206 | + "MATRIX" |ver|mode| len | event ID |
| 207 | + 4D 41 54 52 49 58 02 00 00 2D 21 41 42 43 44 ... |
| 208 | +| user's cross-signing key | other user's cross-signing key | shared secret |
| 209 | + 00 01 02 03 04 05 06 07 ... 10 11 12 13 14 15 16 17 ... 20 21 22 23 24 25 26 27 |
| 210 | +``` |
| 211 | + |
| 212 | +this indicates that Alice is verifying another user (say Bob), in response to |
| 213 | +the request from event "$ABCD...", her cross-signing key is |
| 214 | +`0001020304050607...` (which is "AAECAwQFBg..." in base64), she thinks that |
| 215 | +Bob's cross-signing key is `1011121314151617...` (which is "EBESExQVFh..." in |
| 216 | +base64), and the shared secret is `2021222324252627` (which is "ICEiIyQlJic" in |
| 217 | +base64). |
| 218 | + |
| 219 | +### Message types |
| 220 | + |
| 221 | +#### `m.key.verification.start` |
| 222 | + |
| 223 | +Alice's device tells Bob's device that the QR code has been scanned. |
| 224 | + |
| 225 | +message contents: |
| 226 | + |
| 227 | +- `method`: `m.reciprocate.v1` |
| 228 | +- `m.relates_to`: as per [key verification framework](https://github.com/matrix-org/matrix-doc/pull/2241) |
| 229 | +- `secret`: the shared secret from the QR code, encoded using unpadded base64 |
| 230 | + |
| 231 | +Example: |
| 232 | + |
| 233 | +```json |
| 234 | +{ |
| 235 | + "method": "m.reciprocate.v1", |
| 236 | + "m.relates_to": { |
| 237 | + "rel_type": "m.reference", |
| 238 | + "event_id": "$event_id_of_verification_request" |
| 239 | + }, |
| 240 | + "secret": "shared+secret" |
| 241 | +} |
| 242 | +``` |
| 243 | + |
| 244 | +Note that this message could be sent by either the sender or the recipient of |
| 245 | +the `m.key.verification.request` message, depending on which user scanned the |
| 246 | +QR code. |
| 247 | + |
| 248 | +### Cancellation |
| 249 | + |
| 250 | +In addition to the cancellation codes specified in [the spec for |
| 251 | +`m.key.verification.cancel`](https://matrix.org/docs/spec/client_server/r0.5.0#m-key-verification-cancel), |
| 252 | +the following cancellation codes may be used: |
| 253 | + |
| 254 | +- `m.qr_code.invalid`: The QR code is invalid (e.g. it is not a URL of the |
| 255 | + required form) |
| 256 | + |
| 257 | +The verification can also be cancelled with the error codes: |
| 258 | + |
| 259 | +- `m.key_mismatch`: if the QR code has keys that do not match the expected |
| 260 | + value |
| 261 | +- `m.user_mismatch`: if the QR code is for a different user from what was expected |
| 262 | + |
| 263 | +Tradeoffs/Alternatives |
| 264 | +---------------------- |
| 265 | + |
| 266 | +Other methods of verifying keys, which do not require scanning QR codes, are |
| 267 | +needed for devices that are unable to scan QR codes. One such method is |
| 268 | +[MSC1267](https://github.com/matrix-org/matrix-doc/issues/1267). Since the key |
| 269 | +verification framework allows for multiple methods to be supported, clients can |
| 270 | +allow users to use different methods depending on their capability. |
| 271 | + |
| 272 | +Rather than embedding the keys in the QR codes directly, the two clients could |
| 273 | +perform an exchange similar to |
| 274 | +[MSC1267](https://github.com/matrix-org/matrix-doc/issues/1267), and encoding |
| 275 | +the Short Authentication String code in the QR code. However, this means that |
| 276 | +the clients must exchange several messages before they can verify each other, |
| 277 | +which would delay showing the QR codes. This proposal is also simpler to |
| 278 | +implement. |
| 279 | + |
| 280 | +This proposal does not support the case of asynchronous verification, such as |
| 281 | +printing a QR code on a business card for others to scan. That may be address |
| 282 | +in a separate MSC. |
| 283 | + |
| 284 | +Security Considerations |
| 285 | +----------------------- |
| 286 | + |
| 287 | +The security of verifying Alice's key depends on Bob not hitting the "Verified" |
| 288 | +button (step 10 in the example flow) until after Alice's device indicates |
| 289 | +success or failure. Users have a tendency to click on buttons without reading |
| 290 | +what the screen says, but this is partially mitigated by the fact that it is |
| 291 | +unlikely that Bob will be interacting with the device while Alice is scanning |
| 292 | +and Alice's device will display the verification results immediately upon |
| 293 | +scanning. Also, Bob's device will not display the button until it receives the |
| 294 | +`m.key.verification.start` message that contains the shared secret from the QR |
| 295 | +code, which means that an attacker would need to be physically present while |
| 296 | +Alice and Bob verify. This issue can also be addressed by allowing Bob to |
| 297 | +easily undo the verification if Alice's device displays an error. |
0 commit comments