You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
matrix-spec/proposals/1543-qr_code_key_verificati...

14 KiB

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

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:

    • Alice's user ID,
    • Dynabook's device key,
    • what it thinks Alice's master key is, as the other_user_key parameter, 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:
    • Alice's user ID,
    • Osborne2's device key (optional),
    • what it thinks Dynabook's key is, as other_device_key,
    • Alice's master key, both as key_<key_id> and other_user_key parameters, 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).

  • 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 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. In the case where a device does not have a cross-signing key (as in the case where a user logs in to a new device, and is verifying against another device), thin the QR code should contain at least the device's 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.
  • other_device_key: the other device's key, in unpadded base64. This is only needed when a user is verifying their own devices, where the other device has not yet been signed with the cross-signing key.

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 QR code has been scanned.

message contents:

Example:

{
  "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, 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.

Rather than embedding the keys in the QR codes directly, the two clients could perform an exchange similar to MSC1267, 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.

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