Commit to device ID = identity key and prove ownership of that key.

pull/4129/head
Denis Kasak 2 months ago
parent be6f22f851
commit 84326e6a17

@ -788,8 +788,16 @@ At this point the new device knows that, subject to the user consenting, it shou
3. **New device informs existing device that it wants to use the `device_authorization_grant`**
The new device send the `verification_uri` and, if present, the `verification_uri_complete` over to the existing device and
indicates that want to use protocol `device_authorization_grant` along with the `device_id` that will be used:
At this point, the new device should ensure it has generated its Olm account, so that it has its Curve25519 and Ed25519
device identity keys.
It then sends a `m.login.protocol` message to the existing device, containing:
- An indicator that it wants to use protocol `device_authorization_grant`
- The `verification_uri`
- The `verification_uri_complete`, if present
- The device ID it will be using, which MUST equal the unpadded base64-encoded form of the Curve25519 identity key of
the new device
*New device => Existing device via secure channel*
@ -801,7 +809,7 @@ indicates that want to use protocol `device_authorization_grant` along with the
"verification_uri": "https://auth-oidc.lab.element.dev/link",
"verification_uri_complete": "https://auth-oidc.lab.element.dev/link?code=123456"
},
"device_id": "ABCDEFGH"
"device_id": "3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI"
}
```
@ -848,7 +856,7 @@ sequenceDiagram
N->>+OP: POST /auth/device client_id=xyz&scope=openid+urn:matrix:api:*+urn:matrix:device:ABCDEFGH...
OP->>-N: 200 OK {"user_code": "123456",<br>"verification_uri_complete": "https://id.matrix.org/device/abcde",<br>"expires_in_ms": 120000, "device_code": "XYZ", "interval": 1}
note over N: 3) New device informs existing device of choice of protocol:
N->>Z: SecureSend({"type": "m.login.protocol", "protocol": "device_authorization_grant",<br> "device_authorization_grant":{<br>"verification_uri_complete": "https://id.matrix.org/device/abcde",<br>"verification_uri": ...}, "device_id": "ABCDEFGH"})
N->>Z: SecureSend({"type": "m.login.protocol", "protocol": "device_authorization_grant",<br> "device_authorization_grant":{<br>"verification_uri_complete": "https://id.matrix.org/device/abcde",<br>"verification_uri": ...,<br>"device_id": "3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI"})
deactivate N
end
@ -860,7 +868,7 @@ sequenceDiagram
end
rect rgba(0,255,0, 0.1)
Z->>E: SecureReceive() => {"type": "m.login.protocol", "protocol": "device_authorization_grant",<br> "device_authorization_grant":{<br>"verification_uri_complete": "https://id.matrix.org/device/abcde",<br>"verification_uri": ...}, "device_id": "ABCDEFGH"}
Z->>E: SecureReceive() => {"type": "m.login.protocol", "protocol": "device_authorization_grant",<br> "device_authorization_grant":{<br>"verification_uri_complete": "https://id.matrix.org/device/abcde",<br>"verification_uri": ...},<br>"device_id": "3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI"}
end
rect rgba(255,0,0, 0.1)
@ -918,7 +926,7 @@ sequenceDiagram
N->>+OP: POST /auth/device client_id=xyz&scope=openid+urn:matrix:api:*+urn:matrix:device:ABCDEFGH...
OP->>-N: 200 OK {"user_code": "123456",<br>"verification_uri_complete": "https://id.matrix.org/device/abcde",<br>"expires_in_ms": 120000, "device_code": "XYZ", "interval": 1}
note over N: 3) New device informs existing device of choice of protocol:
N->>Z: SecureSend({"type": "m.login.protocol", "protocol": "device_authorization_grant",<br> "device_authorization_grant":{<br>"verification_uri_complete": "https://id.matrix.org/device/abcde",<br>"verification_uri": ...}, "device_id": "ABCDEFGH"})
N->>Z: SecureSend({"type": "m.login.protocol", "protocol": "device_authorization_grant",<br> "device_authorization_grant":{<br>"verification_uri_complete": "https://id.matrix.org/device/abcde",<br>"verification_uri": ...},<br>"device_id": "3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI"})
deactivate N
end
@ -929,7 +937,7 @@ sequenceDiagram
#end
rect rgba(0,255,0, 0.1)
Z->>E: SecureReceive() => {"type": "m.login.protocol", "protocol": "device_authorization_grant",<br> "device_authorization_grant":{<br>"verification_uri_complete": "https://id.matrix.org/device/abcde",<br>"verification_uri": ...}, "device_id": "ABCDEFGH"}
Z->>E: SecureReceive() => {"type": "m.login.protocol", "protocol": "device_authorization_grant",<br> "device_authorization_grant":{<br>"verification_uri_complete": "https://id.matrix.org/device/abcde",<br>"verification_uri": ...},<br>"device_id": "3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI"}
end
# alt if New device scanned QR code
@ -953,10 +961,10 @@ Then we continue with the actual login:
On receipt of the `m.login.protocol_accepted` message:
- In accordance with [RFC8628](https://datatracker.ietf.org/doc/html/rfc8628#section-3.3.1) the new device must display
the user_code in order that the user can confirm it on the OIDC Provider if required.
the `user_code` in order that the user can confirm it on the OIDC Provider if required.
- The new device then starts to poll the OIDC Provider by making
[Device Access Token Requests](https://datatracker.ietf.org/doc/html/rfc8628#section-3.4) using the interval and bounded
by expires_in.
by `expires_in`.
*New device => OIDC Provider via HTTP*
@ -1028,7 +1036,7 @@ sequenceDiagram
OP-->>N: 400 Bad Request {"error": "authorization_pending"}
else granted
OP-->>N: 200 OK {"access_token": "...", "token_type": "Bearer", ...}
N->>E: SecureSend({ "type": "m.login.success" })
N->>E: SecureSend({ "type": "m.login.success", "proof": proof_of_identity_key_ownership })
Note over N: Device now has an access_token and can start to talk to the homeserver
else denied
OP-->>N: 400 Bad Request {"error": "authorization_declined"}
@ -1051,6 +1059,9 @@ sequenceDiagram
end
```
The reader will note that the `m.login.success` contains a proof that the new device owns the identity key it had
previously committed to, in the `m.login.protocol` step. This is explained in the next section.
#### Secret sharing and device verification
Once the new device has logged in and obtained an access token it will want to obtain the secrets necessary to set up
@ -1067,7 +1078,7 @@ If checked successfully then the existing device sends the following secrets to
This is achieved as following:
1. **Existing device confirms that the new device has indeed logged in successfully**
1. **Existing device confirms that a device with the previously committed-to device ID (device identity key) has indeed logged in successfully**
On receipt of an `m.login.success` message the existing device queries the homeserver to check that the is a device online
with the corresponding device_id (from the `m.login.protocol` message).
@ -1079,9 +1090,40 @@ If the device isn't immediately visible it can repeat the `GET` request for up t
If no device is found then the process should be stopped.
2. **Existing device shares secrets with new device**
2. **Existing device confirms that the new device owns the private part of the committed-to device identity key**
The new device then proves it controls the public key to which it previously committed. It does this by doing an ECDH
between the committed-to identity key and the other device's secure channel ephemeral key to derive a shared secret,
which is used to construct a proof of ownership. Due to the properties of ECDH, the other device knows that the new
device can only do this if it possesses the private part of the committed-to identity key.
The new device does:
```
SH := ECDH(Is, Ep)
EncKey_Proof := HKDF_SHA256(SH, "MATRIX_QR_CODE_LOGIN_ENCKEY_PROOF|" || Ip || "|" || Ep, salt=0, size=32)
NonceBytes := ToLowEndianBytes(0)[..12]
Proof := ChaCha20Poly1305_Encrypt(EncKey_Proof, NonceBytes, "MATRIX_QR_CODE_PROOF_OF_POSSESSION")
```
The existing device sends a `m.login.secrets` message via the secure channel:
And sends the **Proof** to the existing device.
The existing device does the following to verify the proof:
```
SH := ECDH(Es, Ip)
EncKey_Proof := HKDF_SHA256(SH, "MATRIX_QR_CODE_LOGIN_ENCKEY_PROOF|" || Ip || "|" || Ep, salt=0, size=32)
NonceBytes := ToLowEndianBytes(0)[..12]
Plaintext := ChaCha20Poly1305_Decrypt(EncKey_Proof, NonceBytes, Proof)
unless Plaintext == "MATRIX_QR_CODE_PROOF_OF_POSSESSION":
FAIL
```
3. **Existing device shares secrets with new device**
If both previous steps succeeded, the existing device proceeds to send a `m.login.secrets` message via the secure
channel:
```json
{
@ -1122,14 +1164,14 @@ Content-Type: application/json
"m.olm.v1.curve25519-aes-sha2",
"m.megolm.v1.aes-sha2"
],
"device_id": "SGKMSRAGBF",
"device_id": "3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI",
"keys": {
"curve25519:SGKMSRAGBF": "I11VOe5quKuH/YjdOqn5VcW06fvPIJQ9JX8ryj6ario",
"ed25519:SGKMSRAGBF": "b8gROFh+UIHLD/obY0+IlxoWiGtYVhKdqixvw4QHcN8"
"curve25519:3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI": "3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI",
"ed25519:3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI": "b8gROFh+UIHLD/obY0+IlxoWiGtYVhKdqixvw4QHcN8"
},
"signatures": {
"@testing_35:morpheus.localhost": {
"ed25519:SGKMSRAGBF": "ziHEUIsHnrYBH4CqYpN1JC/ex3t4VG3zvo16D8ORqN6yAErpsKsnd/5LDdZERIOB1MGffKGfCL6ny5V7rT9FCQ",
"ed25519:3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI": "ziHEUIsHnrYBH4CqYpN1JC/ex3t4VG3zvo16D8ORqN6yAErpsKsnd/5LDdZERIOB1MGffKGfCL6ny5V7rT9FCQ",
"ed25519:bkYgAVUNqvuyy8b1w09utJNJxBvK3hZB65xxoLPVzFol": "p257k0tfPF98OIDuXnFSJS2DmVlxO4sgTHdF41DTdZBCpTZfPwok6iASo3xMRKdyy3WMEgkQ6lzhEyRKKZBGBQ"
}
},
@ -1276,7 +1318,7 @@ Fields:
|`type`|required `string`|`m.login.failure`|
|`reason`|required `string`| One of: <table> <tr> <td><strong>Value</strong> </td> <td><strong>Description</strong> </td> </tr><tr> <td><code>authorization_expired</code> </td> <td>The Device Authorization Grant expired</td> </tr> <tr> <td><code>device_already_exists</code> </td> <td>The device ID specified by the new client already exists in the Homeserver provided device list</td> </tr><tr><td><code>device_not_found</code></td><td>The new device is not present in the device list as returned by the Homeserver</td></tr><tr><td><code>unexpected_message_received</code></td><td>Sent by either device to indicate that they received a message of a type that they weren't expecting</td></tr><tr><td><code>unsupported_protocol</code></td><td>Sent by a device where no suitable protocol is available or the requested protocol requested is not supported</td></tr><tr><td><code>user_cancelled</code></td><td>Sent by either new or existing device to indicate that the user has cancelled the login</td></tr></table>|
|`homeserver`|`string`| When the existing device is sending this it can include the Base URL of the homeserver so that the new device can at least save the user the hassle of typing it in|
Example:
```json

Loading…
Cancel
Save